home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Snippets / Expression Parser / expr_parser.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-04  |  4.2 KB  |  260 lines  |  [TEXT/MMCC]

  1. /*
  2.  * expr_parser.c - a recursive-descent parser for simple mathematical expressions
  3.  *
  4.  * The grammar is:
  5.  *
  6.  *   expr -> expr '+' term | expr '-' term | term
  7.  *   term -> term '*' factor | term '/' factor | factor
  8.  *   factor -> '(' expr ')' | '-' factor | NUMBER
  9.  *
  10.  * or, equivalently (to eliminate left recursion):
  11.  *
  12.  *   expr  -> term expr'
  13.  *   expr' -> '+' term expr' | '-' term expr' | e
  14.  *   term  -> factor term'
  15.  *   term' -> '*' factor term' | '/' factor term' | e
  16.  *   factor -> '(' expr ')' | '-' factor | NUMBER
  17.  */ 
  18.  
  19. /** includes **/
  20.  
  21. #include <stdio.h>
  22.  
  23. /** type definitions **/
  24.  
  25. typedef enum {
  26.     NUMBER = 1,
  27.     PLUS,
  28.     MINUS,
  29.     TIMES,
  30.     DIVIDED_BY,
  31.     LPAREN,
  32.     RPAREN
  33. } TOKEN_TYPE;
  34.  
  35. typedef struct {
  36.     TOKEN_TYPE    type;
  37.     float        value;
  38. } TOKEN;
  39.  
  40. /** global variables **/
  41.  
  42. Boolean        eoln;            /* TRUE if the end of line has been reached */
  43. Boolean        err;            /* TRUE if an error was encounted during parsing */
  44. Boolean        unget;            /* TRUE if getc() should return the last char */
  45. char        *buf;            /* the character buffer */
  46. char        last;            /* last character fetched */
  47.  
  48. /** prototypes **/
  49.  
  50. float parse_expr(char *s);
  51.  
  52. static float expr(void);
  53. static float expr2(void);
  54. static float term(void);
  55. static float term2(void);
  56. static float factor(void);
  57. static void get_token(TOKEN *t);
  58. static char get_char(void);
  59. static void unget_char(void);
  60.  
  61. /* test routine for expression parser */
  62.  
  63. int main(int argc, char *argv[])
  64. {
  65.     float    result;
  66.     char    s[100];
  67.     
  68.     for (;;) {
  69.         printf("enter an expression: ");
  70.         scanf("%s", s);
  71.                 
  72.         result = parse_expr(s);    
  73.         if (!err)
  74.             printf("result = %g\n\n", result);
  75.         else
  76.             printf("bad expression\n\n");
  77.     }
  78. }
  79.  
  80. /* parse_expr() is the main routine for the parser */
  81. /* it expects a C string (null-terminated) as its argument */
  82.  
  83. float parse_expr(char *s)
  84. {
  85.     eoln = err = unget = FALSE;
  86.     buf = s;
  87.  
  88.     return (expr());
  89. }
  90.  
  91.  
  92. static float expr(void)
  93. {
  94.     return (term() + expr2());
  95. }
  96.  
  97.  
  98. static float expr2(void)
  99. {
  100.     TOKEN    t;
  101.     
  102.     get_token(&t);
  103.     
  104.     if (t.type == PLUS)
  105.         return (term() + expr2());
  106.     else if (t.type == MINUS)
  107.         return (-(term() + expr2()));
  108.     else {
  109.         unget_char();
  110.         return (0);
  111.     }
  112. }
  113.  
  114.  
  115. static float term(void)
  116. {
  117.     return (factor() * term2());
  118. }
  119.  
  120.  
  121. static float term2(void)
  122. {
  123.     TOKEN    t;
  124.     
  125.     get_token(&t);
  126.     
  127.     if (t.type == TIMES)
  128.         return (factor() * term2());
  129.     else if (t.type == DIVIDED_BY)
  130.         return (1 / (factor() / term2()));
  131.     else {
  132.         unget_char();
  133.         return (1);
  134.     }
  135. }
  136.  
  137.  
  138. static float factor(void)
  139. {
  140.     float    e;
  141.     TOKEN    t;
  142.     
  143.     get_token(&t);
  144.     
  145.     if (eoln)
  146.         err = TRUE;
  147.     else if (t.type == LPAREN) {
  148.         e = expr();
  149.         
  150.         get_token(&t);
  151.         if (t.type == RPAREN)
  152.             return (e);
  153.         else {
  154.             err = TRUE;
  155.             return (0);
  156.         }
  157.     }
  158.     else if (t.type == MINUS)
  159.         return (-factor());
  160.     else if (t.type == NUMBER)
  161.         return (t.value);
  162.     else {
  163.         err = TRUE;
  164.         return (0);
  165.     }
  166. }
  167.  
  168. /* get_token() is the lexical analyzer */
  169. /* it returns the next token in the input stream */
  170. /* it sets the eoln flag if the end of the line is reached */
  171.  
  172. static void get_token(TOKEN *t)
  173. {
  174.     register char    c;
  175.     
  176.     while ((c = get_char()) == ' ')    /* ignore spaces */
  177.         ;
  178.     
  179.     switch (c) {
  180.         case '\0':
  181.             eoln = TRUE;            /* we've reached the end of the line */
  182.             break;
  183.         case '+':
  184.             t->type = PLUS;
  185.             break;
  186.         case '-':
  187.             t->type = MINUS;
  188.             break;
  189.         case '*':
  190.             t->type = TIMES;
  191.             break;
  192.         case '/':
  193.             t->type = DIVIDED_BY;
  194.             break;
  195.         case '(':
  196.             t->type = LPAREN;
  197.             break;
  198.         case ')':
  199.             t->type = RPAREN;
  200.             break;
  201.         default:
  202.             if (c >= '0' && c <= '9' || c == '.') {
  203.                 float    value;
  204.                 float    dec;
  205.                 
  206.                 if (c == '.') {
  207.                     value = 0;
  208.                     dec = 10;
  209.                 }
  210.                 else {
  211.                     value = c - '0';
  212.                     dec = 0;
  213.                 }
  214.                 
  215.                 while ((c = get_char()) >= '0' && c <= '9' || c == '.') {
  216.                     if (c == '.') {
  217.                         if (dec == 0)
  218.                             dec = 10;
  219.                         else {
  220.                             err = TRUE;
  221.                             break;
  222.                         }
  223.                     }
  224.                     else if (dec == 0) {
  225.                         value *= 10;
  226.                         value += (c - '0');
  227.                     }
  228.                     else {
  229.                         value += (c - '0') / dec;
  230.                         dec *= 10;
  231.                     }
  232.                 }
  233.                 
  234.                 unget_char();                /* unget the last character read */
  235.                 
  236.                 t->value = value;
  237.                 t->type = NUMBER;
  238.             }
  239.             else
  240.                 err = TRUE;                    /* bad character in input */
  241.             break;
  242.     }
  243. }
  244.  
  245.  
  246. static char get_char(void)
  247. {
  248.     if (unget)
  249.         unget = FALSE;
  250.     else
  251.         last = *buf++;
  252.     return (last);
  253. }
  254.  
  255.  
  256. static void unget_char(void)
  257. {
  258.     unget = TRUE;
  259. }
  260.