calc.y
上传用户:qaz666999
上传日期:2022-08-06
资源大小:2570k
文件大小:11k
- %{
- /* A simple integer desk calculator using yacc and gmp.
- Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
- This file is part of the GNU MP Library.
- This program is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3 of the License, or (at your option) any later
- version.
- This program is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/. */
- /* This is a simple program, meant only to show one way to use GMP for this
- sort of thing. There's few features, and error checking is minimal.
- Standard input is read, calc_help() below shows the inputs accepted.
- Expressions are evaluated as they're read. If user defined functions
- were wanted it'd be necessary to build a parse tree like pexpr.c does, or
- a list of operations for a stack based evaluator. That would also make
- it possible to detect and optimize evaluations "mod m" like pexpr.c does.
- A stack is used for intermediate values in the expression evaluation,
- separate from the yacc parser stack. This is simple, makes error
- recovery easy, minimizes the junk around mpz calls in the rules, and
- saves initializing or clearing "mpz_t"s during a calculation. A
- disadvantage though is that variables must be copied to the stack to be
- worked on. A more sophisticated calculator or language system might be
- able to avoid that when executing a compiled or semi-compiled form.
- Avoiding repeated initializing and clearing of "mpz_t"s is important. In
- this program the time spent parsing is obviously much greater than any
- possible saving from this, but a proper calculator or language should
- take some trouble over it. Don't be surprised if an init/clear takes 3
- or more times as long as a 10 limb addition, depending on the system (see
- the mpz_init_realloc_clear example in tune/README). */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "gmp.h"
- #define NO_CALC_H /* because it conflicts with normal calc.c stuff */
- #include "calc-common.h"
- #define numberof(x) (sizeof (x) / sizeof ((x)[0]))
- void
- calc_help (void)
- {
- printf ("Examples:n");
- printf (" 2+3*4 expressions are evaluatedn");
- printf (" x=5^6 variables a to z can be set and usedn");
- printf ("Operators:n");
- printf (" + - * arithmeticn");
- printf (" / %% division and remainder (rounding towards negative infinity)n");
- printf (" ^ exponentiationn");
- printf (" ! factorialn");
- printf (" << >> left and right shiftsn");
- printf (" <= >= > \ comparisons, giving 1 if true, 0 if falsen");
- printf (" == != < /n");
- printf (" && || logical and/or, giving 1 if true, 0 if falsen");
- printf ("Functions:n");
- printf (" abs(n) absolute valuen");
- printf (" bin(n,m) binomial coefficientn");
- printf (" fib(n) fibonacci numbern");
- printf (" gcd(a,b,..) greatest common divisorn");
- printf (" kron(a,b) kronecker symboln");
- printf (" lcm(a,b,..) least common multiplen");
- printf (" lucnum(n) lucas numbern");
- printf (" nextprime(n) next prime after nn");
- printf (" powm(b,e,m) modulo powering, b^e%%mn");
- printf (" root(n,r) r-th rootn");
- printf (" sqrt(n) square rootn");
- printf ("Other:n");
- printf (" hex \ set hex or decimal for input and outputn");
- printf (" decimal / ("0x" can be used for hex too)n");
- printf (" quit exit program (EOF works too)n");
- printf (" ; statements are separated with a ; or newlinen");
- printf (" \ continue expressions with \ before newlinen");
- printf (" # xxx comments are # though to newlinen");
- printf ("Hex numbers must be entered in upper case, to distinguish them from then");
- printf ("variables a to f (like in bc).n");
- }
- int ibase = 0;
- int obase = 10;
- /* The stack is a fixed size, which means there's a limit on the nesting
- allowed in expressions. A more sophisticated program could let it grow
- dynamically. */
- mpz_t stack[100];
- mpz_ptr sp = stack[0];
- #define CHECK_OVERFLOW()
- if (sp >= stack[numberof(stack)]) /* FIXME */
- {
- fprintf (stderr,
- "Value stack overflow, too much nesting in expressionn");
- YYERROR;
- }
- #define CHECK_EMPTY()
- if (sp != stack[0])
- {
- fprintf (stderr, "Oops, expected the value stack to be emptyn");
- sp = stack[0];
- }
- mpz_t variable[26];
- #define CHECK_VARIABLE(var)
- if ((var) < 0 || (var) >= numberof (variable))
- {
- fprintf (stderr, "Oops, bad variable somehow: %dn", var);
- YYERROR;
- }
- #define CHECK_UI(name,z)
- if (! mpz_fits_ulong_p (z))
- {
- fprintf (stderr, "%s too bign", name);
- YYERROR;
- }
- %}
- %union {
- char *str;
- int var;
- }
- %token EOS BAD
- %token HELP HEX DECIMAL QUIT
- %token ABS BIN FIB GCD KRON LCM LUCNUM NEXTPRIME POWM ROOT SQRT
- %token <str> NUMBER
- %token <var> VARIABLE
- /* operators, increasing precedence */
- %left LOR
- %left LAND
- %nonassoc '<' '>' EQ NE LE GE
- %left LSHIFT RSHIFT
- %left '+' '-'
- %left '*' '/' '%'
- %nonassoc UMINUS
- %right '^'
- %nonassoc '!'
- %%
- top:
- statement
- | statements statement;
- statements:
- statement EOS
- | statements statement EOS
- | error EOS { sp = stack[0]; yyerrok; };
- statement:
- /* empty */
- | e {
- mpz_out_str (stdout, obase, sp); putchar ('n');
- sp--;
- CHECK_EMPTY ();
- }
- | VARIABLE '=' e {
- CHECK_VARIABLE ($1);
- mpz_swap (variable[$1], sp);
- sp--;
- CHECK_EMPTY ();
- }
- | HELP { calc_help (); }
- | HEX { ibase = 16; obase = -16; }
- | DECIMAL { ibase = 0; obase = 10; }
- | QUIT { exit (0); };
- /* "e" leaves it's value on the top of the mpz stack. A rule like "e '+' e"
- will have done a reduction for the first "e" first and the second "e"
- second, so the code receives the values in that order on the stack. */
- e:
- '(' e ')' /* value on stack */
- | e '+' e { sp--; mpz_add (sp, sp, sp+1); }
- | e '-' e { sp--; mpz_sub (sp, sp, sp+1); }
- | e '*' e { sp--; mpz_mul (sp, sp, sp+1); }
- | e '/' e { sp--; mpz_fdiv_q (sp, sp, sp+1); }
- | e '%' e { sp--; mpz_fdiv_r (sp, sp, sp+1); }
- | e '^' e { CHECK_UI ("Exponent", sp);
- sp--; mpz_pow_ui (sp, sp, mpz_get_ui (sp+1)); }
- | e LSHIFT e { CHECK_UI ("Shift count", sp);
- sp--; mpz_mul_2exp (sp, sp, mpz_get_ui (sp+1)); }
- | e RSHIFT e { CHECK_UI ("Shift count", sp);
- sp--; mpz_fdiv_q_2exp (sp, sp, mpz_get_ui (sp+1)); }
- | e '!' { CHECK_UI ("Factorial", sp);
- mpz_fac_ui (sp, mpz_get_ui (sp)); }
- | '-' e %prec UMINUS { mpz_neg (sp, sp); }
- | e '<' e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) < 0); }
- | e LE e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) <= 0); }
- | e EQ e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) == 0); }
- | e NE e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) != 0); }
- | e GE e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) >= 0); }
- | e '>' e { sp--; mpz_set_ui (sp, mpz_cmp (sp, sp+1) > 0); }
- | e LAND e { sp--; mpz_set_ui (sp, mpz_sgn (sp) && mpz_sgn (sp+1)); }
- | e LOR e { sp--; mpz_set_ui (sp, mpz_sgn (sp) || mpz_sgn (sp+1)); }
- | ABS '(' e ')' { mpz_abs (sp, sp); }
- | BIN '(' e ',' e ')' { sp--; CHECK_UI ("Binomial base", sp+1);
- mpz_bin_ui (sp, sp, mpz_get_ui (sp+1)); }
- | FIB '(' e ')' { CHECK_UI ("Fibonacci", sp);
- mpz_fib_ui (sp, mpz_get_ui (sp)); }
- | GCD '(' gcdlist ')' /* value on stack */
- | KRON '(' e ',' e ')' { sp--; mpz_set_si (sp,
- mpz_kronecker (sp, sp+1)); }
- | LCM '(' lcmlist ')' /* value on stack */
- | LUCNUM '(' e ')' { CHECK_UI ("Lucas number", sp);
- mpz_lucnum_ui (sp, mpz_get_ui (sp)); }
- | NEXTPRIME '(' e ')' { mpz_nextprime (sp, sp); }
- | POWM '(' e ',' e ',' e ')' { sp -= 2; mpz_powm (sp, sp, sp+1, sp+2); }
- | ROOT '(' e ',' e ')' { sp--; CHECK_UI ("Nth-root", sp+1);
- mpz_root (sp, sp, mpz_get_ui (sp+1)); }
- | SQRT '(' e ')' { mpz_sqrt (sp, sp); }
- | VARIABLE {
- sp++;
- CHECK_OVERFLOW ();
- CHECK_VARIABLE ($1);
- mpz_set (sp, variable[$1]);
- }
- | NUMBER {
- sp++;
- CHECK_OVERFLOW ();
- if (mpz_set_str (sp, $1, ibase) != 0)
- {
- fprintf (stderr, "Invalid number: %sn", $1);
- YYERROR;
- }
- };
- gcdlist:
- e /* value on stack */
- | gcdlist ',' e { sp--; mpz_gcd (sp, sp, sp+1); };
- lcmlist:
- e /* value on stack */
- | lcmlist ',' e { sp--; mpz_lcm (sp, sp, sp+1); };
- %%
- yyerror (char *s)
- {
- fprintf (stderr, "%sn", s);
- }
- int calc_option_readline = -1;
- int
- main (int argc, char *argv[])
- {
- int i;
- for (i = 1; i < argc; i++)
- {
- if (strcmp (argv[i], "--readline") == 0)
- calc_option_readline = 1;
- else if (strcmp (argv[i], "--noreadline") == 0)
- calc_option_readline = 0;
- else if (strcmp (argv[i], "--help") == 0)
- {
- printf ("Usage: calc [--option]...n");
- printf (" --readline use readlinen");
- printf (" --noreadline don't use readlinen");
- printf (" --help this messagen");
- printf ("Readline is only available when compiled in,n");
- printf ("and in that case it's the default on a tty.n");
- exit (0);
- }
- else
- {
- fprintf (stderr, "Unrecognised option: %sn", argv[i]);
- exit (1);
- }
- }
- #if WITH_READLINE
- calc_init_readline ();
- #else
- if (calc_option_readline == 1)
- {
- fprintf (stderr, "Readline support not availablen");
- exit (1);
- }
- #endif
- for (i = 0; i < numberof (variable); i++)
- mpz_init (variable[i]);
- for (i = 0; i < numberof (stack); i++)
- mpz_init (stack[i]);
- return yyparse ();
- }