Annotation of OpenXM_contrib/gmp/demos/calc/calc.y, Revision 1.1
1.1 ! ohara 1: %{
! 2: /* A simple integer desk calculator using yacc and gmp.
! 3:
! 4: Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
! 5:
! 6: This file is part of the GNU MP Library.
! 7:
! 8: This program is free software; you can redistribute it and/or modify it under
! 9: the terms of the GNU General Public License as published by the Free Software
! 10: Foundation; either version 2 of the License, or (at your option) any later
! 11: version.
! 12:
! 13: This program is distributed in the hope that it will be useful, but WITHOUT ANY
! 14: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
! 15: PARTICULAR PURPOSE. See the GNU General Public License for more details.
! 16:
! 17: You should have received a copy of the GNU General Public License along with
! 18: this program; if not, write to the Free Software Foundation, Inc., 59 Temple
! 19: Place - Suite 330, Boston, MA 02111-1307, USA. */
! 20:
! 21:
! 22: /* This is a simple program, meant only to show one way to use GMP for this
! 23: sort of thing. There's few features, and error checking is minimal.
! 24: Standard input is read, calc_help() below shows the inputs accepted.
! 25:
! 26: Expressions are evaluated as they're read. If user defined functions
! 27: were wanted it'd be necessary to build a parse tree like pexpr.c does, or
! 28: a list of operations for a stack based evaluator. That would also make
! 29: it possible to detect and optimize evaluations "mod m" like pexpr.c does.
! 30:
! 31: A stack is used for intermediate values in the expression evaluation,
! 32: separate from the yacc parser stack. This is simple, makes error
! 33: recovery easy, minimizes the junk around mpz calls in the rules, and
! 34: saves initializing or clearing "mpz_t"s during a calculation. A
! 35: disadvantage though is that variables must be copied to the stack to be
! 36: worked on. A more sophisticated calculator or language system might be
! 37: able to avoid that when executing a compiled or semi-compiled form.
! 38:
! 39: Avoiding repeated initializing and clearing of "mpz_t"s is important. In
! 40: this program the time spent parsing is obviously much greater than any
! 41: possible saving from this, but a proper calculator or language should
! 42: take some trouble over it. Don't be surprised if an init/clear takes 3
! 43: or more times as long as a 10 limb addition, depending on the system (see
! 44: the mpz_init_realloc_clear example in tune/README). */
! 45:
! 46:
! 47: #include <stdio.h>
! 48: #include <stdlib.h>
! 49: #include <string.h>
! 50: #include "gmp.h"
! 51: #define NO_CALC_H /* because it conflicts with normal calc.c stuff */
! 52: #include "calc-common.h"
! 53:
! 54:
! 55: #define numberof(x) (sizeof (x) / sizeof ((x)[0]))
! 56:
! 57:
! 58: void
! 59: calc_help (void)
! 60: {
! 61: printf ("Examples:\n");
! 62: printf (" 2+3*4 expressions are evaluated\n");
! 63: printf (" x=5^6 variables a to z can be set and used\n");
! 64: printf ("Operators:\n");
! 65: printf (" + - * arithmetic\n");
! 66: printf (" / %% division and remainder (rounding towards negative infinity)\n");
! 67: printf (" ^ exponentiation\n");
! 68: printf (" ! factorial\n");
! 69: printf (" << >> left and right shifts\n");
! 70: printf (" <= >= > \\ comparisons, giving 1 if true, 0 if false\n");
! 71: printf (" == != < /\n");
! 72: printf (" && || logical and/or, giving 1 if true, 0 if false\n");
! 73: printf ("Functions:\n");
! 74: printf (" abs(n) absolute value\n");
! 75: printf (" bin(n,m) binomial coefficient\n");
! 76: printf (" fib(n) fibonacci number\n");
! 77: printf (" gcd(a,b,..) greatest common divisor\n");
! 78: printf (" kron(a,b) kronecker symbol\n");
! 79: printf (" lcm(a,b,..) least common multiple\n");
! 80: printf (" lucnum(n) lucas number\n");
! 81: printf (" nextprime(n) next prime after n\n");
! 82: printf (" powm(b,e,m) modulo powering, b^e%%m\n");
! 83: printf (" root(n,r) r-th root\n");
! 84: printf (" sqrt(n) square root\n");
! 85: printf ("Other:\n");
! 86: printf (" hex \\ set hex or decimal for input and output\n");
! 87: printf (" decimal / (\"0x\" can be used for hex too)\n");
! 88: printf (" quit exit program (EOF works too)\n");
! 89: printf (" ; statements are separated with a ; or newline\n");
! 90: printf (" \\ continue expressions with \\ before newline\n");
! 91: printf (" # xxx comments are # though to newline\n");
! 92: printf ("Hex numbers must be entered in upper case, to distinguish them from the\n");
! 93: printf ("variables a to f (like in bc).\n");
! 94: }
! 95:
! 96:
! 97: int ibase = 0;
! 98: int obase = 10;
! 99:
! 100:
! 101: /* The stack is a fixed size, which means there's a limit on the nesting
! 102: allowed in expressions. A more sophisticated program could let it grow
! 103: dynamically. */
! 104:
! 105: mpz_t stack[100];
! 106: mpz_ptr sp = stack[0];
! 107:
! 108: #define CHECK_OVERFLOW() \
! 109: if (sp >= stack[numberof(stack)]) \
! 110: { \
! 111: fprintf (stderr, \
! 112: "Value stack overflow, too much nesting in expression\n"); \
! 113: YYERROR; \
! 114: }
! 115:
! 116: #define CHECK_EMPTY() \
! 117: if (sp != stack[0]) \
! 118: { \
! 119: fprintf (stderr, "Oops, expected the value stack to be empty\n"); \
! 120: sp = stack[0]; \
! 121: }
! 122:
! 123:
! 124: mpz_t variable[26];
! 125:
! 126: #define CHECK_VARIABLE(var) \
! 127: if ((var) < 0 || (var) >= numberof (variable)) \
! 128: { \
! 129: fprintf (stderr, "Oops, bad variable somehow: %d\n", var); \
! 130: YYERROR; \
! 131: }
! 132:
! 133:
! 134: #define CHECK_UI(name,z) \
! 135: if (! mpz_fits_ulong_p (z)) \
! 136: { \
! 137: fprintf (stderr, "%s too big\n", name); \
! 138: YYERROR; \
! 139: }
! 140:
! 141: %}
! 142:
! 143: %union {
! 144: char *str;
! 145: int var;
! 146: }
! 147:
! 148: %token EOS BAD
! 149: %token HELP HEX DECIMAL QUIT
! 150: %token ABS BIN FIB GCD KRON LCM LUCNUM NEXTPRIME POWM ROOT SQRT
! 151: %token <str> NUMBER
! 152: %token <var> VARIABLE
! 153:
! 154: /* operators, increasing precedence */
! 155: %left LOR
! 156: %left LAND
! 157: %nonassoc '<' '>' EQ NE LE GE
! 158: %left LSHIFT RSHIFT
! 159: %left '+' '-'
! 160: %left '*' '/' '%'
! 161: %nonassoc UMINUS
! 162: %right '^'
! 163: %nonassoc '!'
! 164:
! 165: %%
! 166:
! 167: top:
! 168: statement
! 169: | statements statement;
! 170:
! 171: statements:
! 172: statement EOS
! 173: | statements statement EOS
! 174: | error EOS { sp = stack[0]; yyerrok; };
! 175:
! 176: statement:
! 177: /* empty */
! 178: | e {
! 179: mpz_out_str (stdout, obase, sp); putchar ('\n');
! 180: sp--;
! 181: CHECK_EMPTY ();
! 182: }
! 183: | VARIABLE '=' e {
! 184: CHECK_VARIABLE ($1);
! 185: mpz_swap (variable[$1], sp);
! 186: sp--;
! 187: CHECK_EMPTY ();
! 188: }
! 189: | HELP { calc_help (); }
! 190: | HEX { ibase = 16; obase = -16; }
! 191: | DECIMAL { ibase = 0; obase = 10; }
! 192: | QUIT { exit (0); };
! 193:
! 194: /* "e" leaves it's value on the top of the mpz stack. A rule like "e '+' e"
! 195: will have done a reduction for the first "e" first and the second "e"
! 196: second, so the code receives the values in that order on the stack. */
! 197: e:
! 198: '(' e ')' /* value on stack */
! 199: | e '+' e { sp--; mpz_add (sp, sp, sp+1); }
! 200: | e '-' e { sp--; mpz_sub (sp, sp, sp+1); }
! 201: | e '*' e { sp--; mpz_mul (sp, sp, sp+1); }
! 202: | e '/' e { sp--; mpz_fdiv_q (sp, sp, sp+1); }
! 203: | e '%' e { sp--; mpz_fdiv_r (sp, sp, sp+1); }
! 204: | e '^' e { CHECK_UI ("Exponent", sp);
! 205: sp--; mpz_pow_ui (sp, sp, mpz_get_ui (sp+1)); }
! 206: | e LSHIFT e { CHECK_UI ("Shift count", sp);
! 207: sp--; mpz_mul_2exp (sp, sp, mpz_get_ui (sp+1)); }
! 208: | e RSHIFT e { CHECK_UI ("Shift count", sp);
! 209: sp--; mpz_fdiv_q_2exp (sp, sp, mpz_get_ui (sp+1)); }
! 210: | e '!' { CHECK_UI ("Factorial", sp);
! 211: mpz_fac_ui (sp, mpz_get_ui (sp)); }
! 212: | '-' e %prec UMINUS { mpz_neg (sp, sp); }
! 213:
! 214: | e '<' e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) < 0); }
! 215: | e LE e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) <= 0); }
! 216: | e EQ e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) == 0); }
! 217: | e NE e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) != 0); }
! 218: | e GE e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) >= 0); }
! 219: | e '>' e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) > 0); }
! 220:
! 221: | e LAND e { sp--; mpz_set_ui (sp, mpz_sgn (sp) && mpz_sgn (sp+1)); }
! 222: | e LOR e { sp--; mpz_set_ui (sp, mpz_sgn (sp) || mpz_sgn (sp+1)); }
! 223:
! 224: | ABS '(' e ')' { mpz_abs (sp, sp); }
! 225: | BIN '(' e ',' e ')' { sp--; CHECK_UI ("Binomial base", sp+1);
! 226: mpz_bin_ui (sp, sp, mpz_get_ui (sp+1)); }
! 227: | FIB '(' e ')' { CHECK_UI ("Fibonacci", sp);
! 228: mpz_fib_ui (sp, mpz_get_ui (sp)); }
! 229: | GCD '(' gcdlist ')' /* value on stack */
! 230: | KRON '(' e ',' e ')' { sp--; mpz_set_si (sp,
! 231: mpz_kronecker (sp, sp+1)); }
! 232: | LCM '(' lcmlist ')' /* value on stack */
! 233: | LUCNUM '(' e ')' { CHECK_UI ("Lucas number", sp);
! 234: mpz_lucnum_ui (sp, mpz_get_ui (sp)); }
! 235: | NEXTPRIME '(' e ')' { mpz_nextprime (sp, sp); }
! 236: | POWM '(' e ',' e ',' e ')' { sp -= 2; mpz_powm (sp, sp, sp+1, sp+2); }
! 237: | ROOT '(' e ',' e ')' { sp--; CHECK_UI ("Nth-root", sp+1);
! 238: mpz_root (sp, sp, mpz_get_ui (sp+1)); }
! 239: | SQRT '(' e ')' { mpz_sqrt (sp, sp); }
! 240:
! 241: | VARIABLE {
! 242: sp++;
! 243: CHECK_OVERFLOW ();
! 244: CHECK_VARIABLE ($1);
! 245: mpz_set (sp, variable[$1]);
! 246: }
! 247: | NUMBER {
! 248: sp++;
! 249: CHECK_OVERFLOW ();
! 250: if (mpz_set_str (sp, $1, ibase) != 0)
! 251: {
! 252: fprintf (stderr, "Invalid number: %s\n", $1);
! 253: YYERROR;
! 254: }
! 255: };
! 256:
! 257: gcdlist:
! 258: e /* value on stack */
! 259: | gcdlist ',' e { sp--; mpz_gcd (sp, sp, sp+1); };
! 260:
! 261: lcmlist:
! 262: e /* value on stack */
! 263: | lcmlist ',' e { sp--; mpz_lcm (sp, sp, sp+1); };
! 264:
! 265: %%
! 266:
! 267: yyerror (char *s)
! 268: {
! 269: fprintf (stderr, "%s\n", s);
! 270: }
! 271:
! 272: int calc_option_readline = -1;
! 273:
! 274: int
! 275: main (int argc, char *argv[])
! 276: {
! 277: int i;
! 278:
! 279: for (i = 1; i < argc; i++)
! 280: {
! 281: if (strcmp (argv[i], "--readline") == 0)
! 282: calc_option_readline = 1;
! 283: else if (strcmp (argv[i], "--noreadline") == 0)
! 284: calc_option_readline = 0;
! 285: else if (strcmp (argv[i], "--help") == 0)
! 286: {
! 287: printf ("Usage: calc [--option]...\n");
! 288: printf (" --readline use readline\n");
! 289: printf (" --noreadline don't use readline\n");
! 290: printf (" --help this message\n");
! 291: printf ("Readline is only available when compiled in,\n");
! 292: printf ("and in that case it's the default on a tty.\n");
! 293: exit (0);
! 294: }
! 295: else
! 296: {
! 297: fprintf (stderr, "Unrecognised option: %s\n", argv[i]);
! 298: exit (1);
! 299: }
! 300: }
! 301:
! 302: #if WITH_READLINE
! 303: calc_init_readline ();
! 304: #else
! 305: if (calc_option_readline == 1)
! 306: {
! 307: fprintf (stderr, "Readline support not available\n");
! 308: exit (1);
! 309: }
! 310: #endif
! 311:
! 312: for (i = 0; i < numberof (variable); i++)
! 313: mpz_init (variable[i]);
! 314:
! 315: for (i = 0; i < numberof (stack); i++)
! 316: mpz_init (stack[i]);
! 317:
! 318: return yyparse ();
! 319: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>