Annotation of OpenXM_contrib/gmp/demos/calc.y, Revision 1.1.1.1
1.1 maekawa 1: /* A simple integer desk calculator using yacc and gmp. */
2:
3: /*
4: Copyright (C) 2000 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:
23: /* This is a simple program, meant only to show one way to use GMP for this
24: sort of thing. There's few features, and error checking is minimal.
25: Standard input is read, there's no command line options.
26:
27: Examples:
28: 2+3*4 expressions are evaluated
29: x=5^6 variables a to z can be set and used
30:
31: Operators:
32: + - * arithmetic
33: / % division and remainder (rounding towards negative infinity)
34: ^ exponentiation
35: ! factorial
36: << >> left and right shifts
37: <= >= > \ comparisons, giving 1 if true, 0 if false
38: == != < /
39: && || logical and/or, giving 1 if true, 0 if false
40:
41: Functions:
42: abs(n) absolute value
43: bin(n,m) binomial coefficient
44: fib(n) fibonacci number
45: gcd(a,b,..) greatest common divisor
46: lcm(a,b,..) least common multiple
47: nextprime(n) next prime after n
48: powm(b,e,m) modulo powering, b^e%m
49: root(n,r) r-th root (rounded down)
50: sqrt(n) square root (rounded down)
51:
52: Other:
53: hex \ set hex or decimal for input and output
54: decimal / ("0x" can be used for hex too)
55: quit exit program (EOF works too)
56: ; statements are separated with a ; or newline
57: \ continue expressions with \ before newline
58: # xxx comments are # though to newline
59:
60: Hex numbers must be entered in upper case, to distinguish them from the
61: variables a to f (like in bc).
62:
63:
64: Expressions are evaluated as they're read. If user defined functions
65: were wanted it'd be necessary to build a parse tree like pexpr.c does, or
66: a list of operations for a stack based evaluator. That would also make
67: it possible to detect and optimize evaluations "mod m" like pexpr.c does.
68:
69: A stack is used for intermediate values in the expression evaluation,
70: separate from the yacc parser stack. This is simple, makes error
71: recovery easy, minimizes the junk around mpz calls in the rules, and
72: saves initializing or clearing "mpz_t"s during a calculation. A
73: disadvantage though is that variables must be copied to the stack to be
74: worked on. A more sophisticated calculator or language system might be
75: able to avoid that when executing a compiled or semi-compiled form.
76:
77: Avoiding repeated initializing and clearing of "mpz_t"s is important. In
78: this program the time spent parsing is obviously much greater than any
79: possible saving from this, but a proper calculator or language should
80: take some trouble over it. Don't be surprised if an init/clear takes 3
81: or more times as long as a 10 limb addition, depending on the system (see
82: the mpz_init_realloc_clear example in tune/README).
83:
84: In a calculator or language using dynamic memory allocation, a good idea
85: would be to keep some "mpz_t"s on a free list. A structure like the
86: following is effectively an "mpz_t" with an extra field tacked on the end
87: for that purpose.
88:
89: struct foo {
90: mpz_t z;
91: struct foo *next;
92: };
93:
94: A pointer "struct foo *p" used as "p->z" will be just a type-safe cast.
95: With care a free list could be made thread safe, and it should never be
96: larger than the deepest nested calculation. */
97:
98:
99: %{
100: #include <stdio.h>
101: #include <stdlib.h>
102:
103: #include "gmp.h"
104:
105: #define numberof(x) (sizeof (x) / sizeof ((x)[0]))
106:
107:
108: int ibase = 0;
109: int obase = 10;
110:
111:
112: /* The stack is a fixed size, which means there's a limit on the nesting
113: allowed in expressions. A more sophisticated program could let it grow
114: dynamically. */
115:
116: mpz_t stack[100];
117: mpz_ptr sp = stack[0];
118:
119: #define CHECK_OVERFLOW() \
120: if (sp >= stack[numberof(stack)]) \
121: { \
122: fprintf (stderr, \
123: "Value stack overflow, too much nesting in expression\n"); \
124: YYERROR; \
125: }
126:
127: #define CHECK_EMPTY() \
128: if (sp != stack[0]) \
129: { \
130: fprintf (stderr, "Oops, expected the value stack to be empty\n"); \
131: sp = stack[0]; \
132: }
133:
134:
135: mpz_t variable[26];
136:
137: #define CHECK_VARIABLE(var) \
138: if ((var) < 0 || (var) >= numberof (variable)) \
139: { \
140: fprintf (stderr, "Oops, bad variable somehow: %d\n", var); \
141: YYERROR; \
142: }
143:
144:
145: #define CHECK_UI(name,z) \
146: if (! mpz_fits_ulong_p (z)) \
147: { \
148: fprintf (stderr, \
149: "Operand must fit in an \"unsigned long\" for %s\n", name); \
150: YYERROR; \
151: }
152:
153: %}
154:
155: %union {
156: char *str;
157: int var;
158: }
159:
160: %token EOS BAD
161: %token HEX DECIMAL QUIT
162: %token ABS BIN FIB GCD LCM NEXTPRIME POWM ROOT SQRT
163: %token <str> NUMBER
164: %token <var> VARIABLE
165:
166: /* operators, increasing precedence */
167: %left LOR
168: %left LAND
169: %nonassoc '<' '>' EQ NE LE GE
170: %left LSHIFT RSHIFT
171: %left '+' '-'
172: %left '*' '/' '%'
173: %nonassoc UMINUS
174: %right '^'
175: %nonassoc '!'
176:
177: %%
178:
179: top:
180: statement
181: | statements statement
182:
183: statements:
184: statement EOS
185: | statements statement EOS
186: | error EOS ={ sp = stack[0]; yyerrok; }
187:
188: statement:
189: /* empty */
190: | e ={
191: mpz_out_str (stdout, obase, sp); putchar ('\n');
192: sp--;
193: CHECK_EMPTY ();
194: }
195: | VARIABLE '=' e ={
196: CHECK_VARIABLE ($1);
197: mpz_swap (variable[$1], sp);
198: sp--;
199: CHECK_EMPTY ();
200: }
201: | HEX ={ ibase = 16; obase = -16; }
202: | DECIMAL ={ ibase = 0; obase = 10; }
203: | QUIT ={ exit (0); }
204:
205: /* "e" leaves it's value on the top of the mpz stack. A rule like "e '+' e"
206: will have done a reduction for the first "e" first and the second "e"
207: second, so the code receives the values in that order on the stack. */
208: e:
209: '(' e ')' /* value on stack */
210: | e '+' e ={ sp--; mpz_add (sp, sp, sp+1); }
211: | e '-' e ={ sp--; mpz_sub (sp, sp, sp+1); }
212: | e '*' e ={ sp--; mpz_mul (sp, sp, sp+1); }
213: | e '/' e ={ sp--; mpz_fdiv_q (sp, sp, sp+1); }
214: | e '%' e ={ sp--; mpz_fdiv_r (sp, sp, sp+1); }
215: | e '^' e ={ CHECK_UI ("exponentiation", sp);
216: sp--; mpz_pow_ui (sp, sp, mpz_get_ui (sp+1)); }
217: | e LSHIFT e ={ CHECK_UI ("lshift", sp);
218: sp--; mpz_mul_2exp (sp, sp, mpz_get_ui (sp+1)); }
219: | e RSHIFT e ={ CHECK_UI ("rshift", sp);
220: sp--; mpz_fdiv_q_2exp (sp, sp, mpz_get_ui (sp+1)); }
221: | e '!' ={ CHECK_UI ("factorial", sp);
222: mpz_fac_ui (sp, mpz_get_ui (sp)); }
223: | '-' e %prec UMINUS ={ mpz_neg (sp, sp); }
224:
225: | e '<' e ={ sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) < 0); }
226: | e LE e ={ sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) <= 0); }
227: | e EQ e ={ sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) == 0); }
228: | e NE e ={ sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) != 0); }
229: | e GE e ={ sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) >= 0); }
230: | e '>' e ={ sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) > 0); }
231:
232: | e LAND e ={ sp--; mpz_set_ui (sp, mpz_sgn (sp) && mpz_sgn (sp+1)); }
233: | e LOR e ={ sp--; mpz_set_ui (sp, mpz_sgn (sp) || mpz_sgn (sp+1)); }
234:
235: | ABS '(' e ')' ={ mpz_abs (sp, sp); }
236: | BIN '(' e ',' e ')' ={ sp--; CHECK_UI ("binomial", sp+1);
237: mpz_bin_ui (sp, sp, mpz_get_ui (sp+1)); }
238: | FIB '(' e ')' ={ CHECK_UI ("fibonacci", sp);
239: mpz_fib_ui (sp, mpz_get_ui (sp)); }
240: | GCD '(' gcdlist ')' /* value on stack */
241: | LCM '(' lcmlist ')' /* value on stack */
242: | NEXTPRIME '(' e ')' ={ mpz_nextprime (sp, sp); }
243: | POWM '(' e ',' e ',' e ')' ={ sp -= 2; mpz_powm (sp, sp, sp+1, sp+2); }
244: | ROOT '(' e ',' e ')' ={ sp--; CHECK_UI ("nth-root", sp+1);
245: mpz_root (sp, sp, mpz_get_ui (sp+1)); }
246: | SQRT '(' e ')' ={ mpz_sqrt (sp, sp); }
247:
248: | VARIABLE ={
249: sp++;
250: CHECK_OVERFLOW ();
251: CHECK_VARIABLE ($1);
252: mpz_set (sp, variable[$1]);
253: }
254: | NUMBER ={
255: sp++;
256: CHECK_OVERFLOW ();
257: if (mpz_set_str (sp, $1, ibase) != 0)
258: {
259: fprintf (stderr, "Invalid number: %s\n", $1);
260: YYERROR;
261: }
262: }
263:
264: gcdlist:
265: e /* value on stack */
266: | gcdlist ',' e ={ sp--; mpz_gcd (sp, sp, sp+1); }
267:
268: lcmlist:
269: e /* value on stack */
270: | lcmlist ',' e ={ sp--; mpz_lcm (sp, sp, sp+1); }
271:
272: %%
273:
274: yyerror (char *s)
275: {
276: fprintf (stderr, "%s\n", s);
277: }
278:
279: int
280: main (void)
281: {
282: int i;
283:
284: for (i = 0; i < numberof (variable); i++)
285: mpz_init (variable[i]);
286:
287: for (i = 0; i < numberof (stack); i++)
288: mpz_init (stack[i]);
289:
290: return yyparse ();
291: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>