calc.y
上传用户:qaz666999
上传日期:2022-08-06
资源大小:2570k
文件大小:11k
源码类别:

数学计算

开发平台:

Unix_Linux

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