home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / unix / emx / test / calc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-02  |  12.4 KB  |  539 lines

  1. /* calc.c (emx+gcc) -- Copyright (c) 1992-1993 by Eberhard Mattes */
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <setjmp.h>
  7. #include <math.h>
  8. #include <ctype.h>
  9.  
  10. #define FALSE 0
  11. #define TRUE  1
  12.  
  13. #define T_EOF        0
  14. #define T_NAME          1
  15. #define T_NUMBER    2
  16. #define T_LPAR        3
  17. #define T_RPAR        4
  18. #define T_ASSIGN    5
  19. #define T_OPERATOR    6
  20. #define T_EOL           7
  21. #define T_HELP          8
  22. #define T_QUIT          9
  23.  
  24. struct keyword
  25. {
  26.   char *name;
  27.   int token;
  28.   double (*unary)(double x);
  29.   double (*binary)(double x, double y);
  30.   int u_bp, b_bp;
  31. };
  32.  
  33. static FILE *parse_f;
  34. static char parse_buffer[512];
  35. static char tokstr[512];
  36. static const unsigned char *parse_ptr;
  37. static int parse_line;
  38. static int parse_cont;
  39. static int interactive;
  40. static int token;
  41. static int calc_errno;
  42. static const struct keyword *tokptr;
  43. static double toknum;
  44. static jmp_buf main_loop;
  45.  
  46. static double f_add (double x, double y);
  47. static double f_sub (double x, double y);
  48. static double f_mul (double x, double y);
  49. static double f_div (double x, double y);
  50. static double f_shl (double x, double y);
  51. static double f_shr (double x, double y);
  52. static double f_and (double x, double y);
  53. static double f_or (double x, double y);
  54. static double f_eq (double x, double y);
  55. static double f_ne (double x, double y);
  56. static double f_lt (double x, double y);
  57. static double f_le (double x, double y);
  58. static double f_gt (double x, double y);
  59. static double f_ge (double x, double y);
  60. static double f_neg (double x);
  61. static double f_not (double x);
  62. static void volatile syntax (const char *msg);
  63. static int get_line (void);
  64. static void get_token (int more);
  65. static void check (int t, const char *msg);
  66. static double factor (void);
  67. static double expr (void);
  68. static double expr1 (int min_bp);
  69. static void help (void);
  70.  
  71.  
  72. static struct keyword keywords[] =
  73. {
  74.   {"+",         T_OPERATOR, NULL,  f_add,  0, 10},
  75.   {"++",        T_OPERATOR, NULL,  hypot,  0, 10},
  76.   {"-",         T_OPERATOR, f_neg, f_sub, 12, 10},
  77.   {"*",         T_OPERATOR, NULL,  f_mul,  0, 14},
  78.   {"/",         T_OPERATOR, NULL,  f_div,  0, 14},
  79.   {"%",         T_OPERATOR, NULL,  fmod,   0, 14},
  80.   {"=",         T_OPERATOR, NULL,  f_eq,   0,  8},
  81.   {"<>",        T_OPERATOR, NULL,  f_ne,   0,  8},
  82.   {"!=",        T_OPERATOR, NULL,  f_ne,   0,  8},
  83.   {"<",         T_OPERATOR, NULL,  f_lt,   0,  8},
  84.   {"<=",        T_OPERATOR, NULL,  f_le,   0,  8},
  85.   {">",         T_OPERATOR, NULL,  f_gt,   0,  8},
  86.   {">=",        T_OPERATOR, NULL,  f_ge,   0,  8},
  87.   {"^",         T_OPERATOR, NULL,  pow,    0, 21},
  88.   {"**",        T_OPERATOR, NULL,  pow,    0, 21},
  89.   {"<<",        T_OPERATOR, NULL,  f_shl,  0, 16},
  90.   {">>",        T_OPERATOR, NULL,  f_shr,  0, 16},
  91.   {"&",         T_OPERATOR, NULL,  f_and,  0,  4},
  92.   {"|",         T_OPERATOR, NULL,  f_or,   0,  2},
  93.   {"!",         T_OPERATOR, f_not, NULL,   6,  0},
  94.   {"(",         T_LPAR,     NULL,  NULL,   0,  0},
  95.   {")",         T_RPAR,     NULL,  NULL,   0,  0},
  96.   {"or",        T_OPERATOR, NULL,  f_or,   0,  2},
  97.   {"and",       T_OPERATOR, NULL,  f_and,  0,  4},
  98.   {"not",       T_OPERATOR, f_not, NULL,   6,  0},
  99.   {"exp",       T_OPERATOR, exp,   NULL,  18,  0},
  100.   {"log",       T_OPERATOR, log10, NULL,  18,  0},
  101.   {"ln",        T_OPERATOR, log,   NULL,  18,  0},
  102.   {"cos",       T_OPERATOR, cos,   NULL,  18,  0},
  103.   {"sin",       T_OPERATOR, sin,   NULL,  18,  0},
  104.   {"tan",       T_OPERATOR, tan,   NULL,  18,  0},
  105.   {"cosh",      T_OPERATOR, cosh,  NULL,  18,  0},
  106.   {"sinh",      T_OPERATOR, sinh,  NULL,  18,  0},
  107.   {"tanh",      T_OPERATOR, tanh,  NULL,  18,  0},
  108.   {"acos",      T_OPERATOR, acos,  NULL,  18,  0},
  109.   {"asin",      T_OPERATOR, asin,  NULL,  18,  0},
  110.   {"atan",      T_OPERATOR, atan,  NULL,  18,  0},
  111.   {"cbrt",      T_OPERATOR, cbrt,  NULL,  18,  0},
  112.   {"sqrt",      T_OPERATOR, sqrt,  NULL,  18,  0},
  113.   {"ceil",      T_OPERATOR, ceil,  NULL,  18,  0},
  114.   {"floor",     T_OPERATOR, floor, NULL,  18,  0},
  115.   {"round",     T_OPERATOR, rint,  NULL,  18,  0},
  116.   {"trunc",     T_OPERATOR, trunc, NULL,  18,  0},
  117.   {"abs",       T_OPERATOR, fabs,  NULL,  18,  0},
  118.   {"help",      T_HELP,     NULL,  NULL,   0,  0},
  119.   {"quit",      T_QUIT,     NULL,  NULL,   0,  0},
  120.   {NULL,        0,          NULL,  NULL,   0,  0}
  121. };
  122.  
  123.  
  124. static void volatile syntax (const char *msg)
  125. {
  126.   fprintf (stderr, "%s (Token %s, line %d)\n", msg, tokstr, parse_line);
  127.   if (interactive)
  128.     longjmp (main_loop, 1);
  129.   else
  130.     exit (2);
  131. }
  132.  
  133.  
  134. static int get_line (void)
  135. {
  136.   char *p;
  137.  
  138.   if (interactive != 0)
  139.     {
  140.       if (parse_cont)
  141.         printf (">> ");
  142.       else
  143.         printf ("%d> ", interactive++);
  144.       fflush (stdout);
  145.     }
  146.   if (fgets (parse_buffer, sizeof (parse_buffer), parse_f) == NULL)
  147.     {
  148.       if (!ferror (parse_f))
  149.     return (FALSE);
  150.       perror ("fgets");
  151.       exit (2);
  152.     }
  153.   p = strchr (parse_buffer, '\n');
  154.   if (p != NULL) *p = 0;
  155.   parse_ptr = parse_buffer;
  156.   ++parse_line; parse_cont = TRUE;
  157.   return (TRUE);
  158. }
  159.  
  160.  
  161. static int find_token (const struct keyword *table)
  162. {
  163.   while (table->name != NULL)
  164.     if (strcmp (tokstr, table->name) == 0)
  165.       {
  166.     token = table->token;
  167.     tokptr = table;
  168.     return (TRUE);
  169.       }
  170.     else
  171.       ++table;
  172.   return (FALSE);
  173. }
  174.  
  175.  
  176. static int find_prefix (void)
  177. {
  178.   size_t len;
  179.   int j;
  180.   const struct keyword *table;
  181.   
  182.   len = strlen (tokstr); j = 0;
  183.   for (table = keywords; table->name != NULL; ++table)
  184.     if (strlen (table->name) >= len &&
  185.     memcmp (table->name, tokstr, len) == 0)
  186.       ++j;
  187.   return (j);
  188. }
  189.  
  190.  
  191. static int find_exact (void)
  192. {
  193.   size_t len;
  194.   int j;
  195.   const struct keyword *table;
  196.   
  197.   len = strlen (tokstr); j = 0;
  198.   for (table = keywords; table->name != NULL; ++table)
  199.     if (strlen (table->name) == len && strcmp (table->name, tokstr) == 0)
  200.       {
  201.     token = table->token;
  202.     tokptr = table;
  203.     ++j;
  204.       }
  205.   return (j);
  206. }
  207.  
  208. static void get_token (int more)
  209. {
  210.   const unsigned char *start;
  211.   char *p;
  212.   int i, j;
  213.   
  214.   for (;;)
  215.     {
  216.       if (*parse_ptr == ' ' || *parse_ptr == '\t')
  217.     ++parse_ptr;
  218.       else if (*parse_ptr == 0 || *parse_ptr == '#')
  219.     {
  220.           if (!more)
  221.             {
  222.               strcpy (tokstr, "<<EOL>>");
  223.               token = T_EOL;
  224.               return;
  225.             }
  226.       if (!get_line ())
  227.         {
  228.           strcpy (tokstr, "<<EOF>>");
  229.           token = T_EOF;
  230.           return;
  231.         }
  232.     }
  233.       else
  234.     break;
  235.     }
  236.   if (*parse_ptr >= 0x80 || isalpha (*parse_ptr))
  237.     {
  238.       start = parse_ptr++;
  239.       while (*parse_ptr >= 0x80 || isalnum (*parse_ptr) || *parse_ptr == '_')
  240.     ++parse_ptr;
  241.       i = parse_ptr - start;
  242.       if (i+1 > sizeof (tokstr))
  243.         syntax ("Name too long");
  244.       memcpy (tokstr, start, i);
  245.       tokstr[i] = 0;
  246.       if (!find_token (keywords))
  247.     token = T_NAME;
  248.     }
  249.   else if (isdigit (*parse_ptr))
  250.     {
  251.       errno = 0;
  252.       toknum = strtod (parse_ptr, &p);
  253.       if (errno != 0)
  254.     syntax ("Invalid number");
  255.       token = T_NUMBER;
  256.       i = p - (char *)parse_ptr + 1;
  257.       if (i > sizeof (tokstr))
  258.         i = sizeof (tokstr);
  259.       _strncpy (tokstr, parse_ptr, i);
  260.       parse_ptr = p;
  261.     }
  262.   else
  263.     {
  264.       tokstr[0] = *parse_ptr++;
  265.       i = 1;
  266.       for (;;)
  267.     {
  268.       tokstr[i] = 0;
  269.       j = find_prefix ();
  270.       if (j == 0 || *parse_ptr == 0)
  271.         break;
  272.       tokstr[i++] = *parse_ptr++;
  273.     }
  274.       while (find_exact () != 1)
  275.     {
  276.       if (i == 1)
  277.         syntax ("Invalid character");
  278.       --i; --parse_ptr;
  279.       tokstr[i] = 0;
  280.     }
  281.     }
  282. }
  283.  
  284.  
  285. static void check (int t, const char *msg)
  286. {
  287.   while (token == T_EOL)
  288.     get_token (TRUE);
  289.   if (token != t)
  290.     {
  291.       char buf[100];
  292.  
  293.       sprintf (buf, "`%s' expected", msg);
  294.       syntax (buf);
  295.     }
  296. }
  297.  
  298.  
  299. static double factor (void)
  300. {
  301.   double x;
  302.   const struct keyword *ptr;
  303.  
  304.   switch (token)
  305.     {
  306.     case T_NUMBER:
  307.       x = toknum;
  308.       get_token (FALSE);
  309.       return (x);
  310.     case T_NAME:
  311.       syntax ("Variables not yet implemented");
  312.     case T_LPAR:
  313.       get_token (TRUE);
  314.       x = expr ();
  315.       check (T_RPAR, ")");
  316.       get_token (FALSE);
  317.       return (x);
  318.     case T_OPERATOR:
  319.       if (tokptr->unary == NULL)
  320.         syntax ("Unary operator expected");
  321.       ptr = tokptr;
  322.       get_token (TRUE);
  323.       x = expr1 (ptr->u_bp);
  324.       errno = 0;
  325.       x = ptr->unary (x);
  326.       if (errno != 0)
  327.         calc_errno = errno;
  328.       return (x);
  329.     default:
  330.       syntax ("Operand expected");
  331.     }
  332. }
  333.  
  334.  
  335. static double expr1 (int min_bp)
  336. {
  337.   double x, y;
  338.   const struct keyword *ptr;
  339.  
  340.   min_bp &= ~1;
  341.   x = factor ();
  342.   while (token == T_OPERATOR && tokptr->binary != NULL &&
  343.          tokptr->b_bp > min_bp)
  344.     {
  345.       ptr = tokptr;
  346.       get_token (TRUE);
  347.       y = expr1 (ptr->b_bp);
  348.       errno = 0;
  349.       x = ptr->binary (x, y);
  350.       if (errno != 0)
  351.         calc_errno = errno;
  352.     }
  353.   return (x);
  354. }
  355.  
  356.  
  357. static double expr (void)
  358. {
  359.   return (expr1 (0));
  360. }
  361.  
  362.  
  363. static double f_add (double x, double y)
  364. {
  365.   return (x + y);
  366. }
  367.  
  368.  
  369. static double f_sub (double x, double y)
  370. {
  371.   return (x - y);
  372. }
  373.  
  374.  
  375. static double f_mul (double x, double y)
  376. {
  377.   return (x * y);
  378. }
  379.  
  380.  
  381. static double f_div (double x, double y)
  382. {
  383.   return (x / y);
  384. }
  385.  
  386.  
  387. static double f_shl (double x, double y)
  388. {
  389.   return (ldexp (x, (int)y));
  390. }
  391.  
  392.  
  393. static double f_shr (double x, double y)
  394. {
  395.   return (ldexp (x, (int)(-y)));
  396. }
  397.  
  398.  
  399. static double f_eq (double x, double y)
  400. {
  401.   return (x == y ? 1.0 : 0.0);
  402. }
  403.  
  404.  
  405. static double f_ne (double x, double y)
  406. {
  407.   return (x != y ? 1.0 : 0.0);
  408. }
  409.  
  410.  
  411. static double f_lt (double x, double y)
  412. {
  413.   return (x < y ? 1.0 : 0.0);
  414. }
  415.  
  416.  
  417. static double f_le (double x, double y)
  418. {
  419.   return (x <= y ? 1.0 : 0.0);
  420. }
  421.  
  422.  
  423. static double f_gt (double x, double y)
  424. {
  425.   return (x > y ? 1.0 : 0.0);
  426. }
  427.  
  428.  
  429. static double f_ge (double x, double y)
  430. {
  431.   return (x >= y ? 1.0 : 0.0);
  432. }
  433.  
  434.  
  435. static double f_neg (double x)
  436. {
  437.   return (-x);
  438. }
  439.  
  440.  
  441. static double f_and (double x, double y)
  442. {
  443.   return (x != 0.0 && y != 0.0 ? 1.0 : 0.0);
  444. }
  445.  
  446.  
  447. static double f_or (double x, double y)
  448. {
  449.   return (x != 0.0 || y != 0.0 ? 1.0 : 0.0);
  450. }
  451.  
  452.  
  453. static double f_not (double x)
  454. {
  455.   return (x == 0.0 ? 1.0 : 0.0);
  456. }
  457.  
  458.  
  459. static void help (void)
  460. {
  461.   puts ("This is the emx calculator. "
  462.         "Copyright (c) 1992-1993 by Eberhard Mattes.\n");
  463.   puts ("Commands:");
  464.   puts ("  help           display help");
  465.   puts ("  quit           exit the emx calculator");
  466.   puts ("  <exp>          evaluate the expression <exp> and print it\n");
  467.   puts ("Operators: (ordered by precedence):");
  468.   puts ("  ( )            change order of evaluation");
  469.   puts ("  ^ **           compute power");
  470.   puts ("  sin cos tan sinh cosh tanh asin acos atan exp log ln");
  471.   puts ("      cbrt sqrt abs ceil floor round trunc");
  472.   puts ("  << >>          shift left, shift right");
  473.   puts ("  * / %          multiplication, division, modulus");
  474.   puts ("  -              change sign");
  475.   puts ("  + - ++         addition, subtraction, hypot");
  476.   puts ("  = != < <= > >= compare numbers");
  477.   puts ("  ! not          logical not");
  478.   puts ("  & and          logical and");
  479.   puts ("  | or           logical or");
  480.   puts ("All operators but ^ and ** are left-associative");
  481. }
  482.  
  483.  
  484. int main (int argc, char *argv[])
  485. {
  486.   double x;
  487.  
  488.   if (argc == 1)
  489.     {
  490.       parse_f = stdin;
  491.       interactive = 1;
  492.     }
  493.   else if (argc == 2)
  494.     {
  495.       interactive = 0;
  496.       parse_f = fopen (argv[1], "rt");
  497.       if (parse_f == NULL)
  498.         {
  499.           fprintf (stderr, "Cannot open input file %s\n", argv[1]);
  500.           return (2);
  501.         }
  502.     }
  503.   else
  504.     {
  505.       fprintf (stderr, "Usage: calc [filename]\n");
  506.       return (1);
  507.     }
  508.   parse_line = 0;
  509.   if (interactive)
  510.     printf ("Type `help' for help.\n");
  511.   for (;;)
  512.     {
  513.       setjmp (main_loop);
  514.       if (interactive)
  515.         printf ("\n");
  516.       parse_cont = FALSE;
  517.       parse_buffer[0] = 0; parse_ptr = parse_buffer;
  518.       calc_errno = 0;
  519.       get_token (TRUE);
  520.       if (token == T_EOF || token == T_QUIT)
  521.         break;
  522.       else if (token == T_HELP)
  523.         help ();
  524.       else
  525.         {
  526.           x = expr ();
  527.           if (token != T_EOL)
  528.             syntax ("extra characters at end of line");
  529.           if (interactive)
  530.             printf ("==> ");
  531.           printf ("%g", x);
  532.           if (calc_errno != 0)
  533.             printf (" (%s)", strerror (calc_errno));
  534.           printf ("\n");
  535.         }
  536.     }
  537.   return (0);
  538. }
  539.