home *** CD-ROM | disk | FTP | other *** search
- \ CALCT -- A simple four-function calculator grammar
- \ which allows variable assignments.
- \ Uses QTREES to produce an abstract syntax tree
- \ and later evaluate it with a tree walker
- \ MUST be compiled with QTREES defined
-
- \ symtab_union will be placed in the symtabtype struct, under the
- \ union classes.
- #begin symtab_union
- double value; /* we need a slot to carry a double value associated
- with an <identifer> */
- #end symtab_union
-
- \ eval_functions will appear among the global functions in eval.c.
- #begin eval_functions
-
- /* ............. */
- static double eval(root)
- semrectype *root;
- /* function "eval" expects an expression tree in "root", and walks down
- through it. The return value will always be the computed arithmetic
- value of the expression tree, as should be apparent from the
- structure of this function. This can be generalized to return a
- struct pointer for something more complex. Or the tree walk can
- simply be with the purpose of assigning an attribute to an identifier
- in the symbol table.
- We recommend writing a large "eval" function as a separately
- compiled file, rather than in the grammar as here, in order to avoid
- regenerating the parse tables, expanding skeleton files, etc. required
- by the "make" logic. */
- {
- switch (root->semt) {
- case PLUS: return eval(root->LEFT) + eval(root->RIGHT);
- case MINUS: return eval(root->LEFT) - eval(root->RIGHT);
- case MPY: return eval(root->LEFT) * eval(root->RIGHT);
- case DIVIDE: {
- double denominator= eval(root->RIGHT);
-
- if (denominator == 0.0) {
- printf(" ***divide by 0\n");
- return 0.0;
- }
- else return eval(root->LEFT) / denominator;
- }
- case UMINUS: return -eval(root->CHILD);
- case PARENS:
- case INTVAL:
- case REALVAL:
- case VARIABLE: return eval(root->CHILD);
- case IDENT: return root->usem.symp->usym.value;
- case FIXED: return (double) root->usem.numval;
- case FLOAT: return root->usem.rval;
- case QUIT: ;
- default:
- printf(" *** eval case %s (%d) not supported\n",
- flags[root->semt], root->semt);
- return 0.0;
- }
- }
-
- #end eval_functions
-
- \ apply_locals will appear just after the local variable declarations
- \ in function apply in eval.c, but before any executables
- #begin apply_locals
- double evalue;
- #end apply_locals
-
- \ GRAMMAR STARTS HERE
- \ Each tagged production will get built into a syntax tree for
- \ evaluation by function "eval", defined above
- \ Untagged single productions are bypassed in the tree and can
- \ therefore be ignored. Terminal tokens, e.g. "+", "/", "(", ")",
- \ are NOT assigned a position in the AST.
-
- Goal -> Stmts QUIT <eol> #QUIT
- Stmts -> Stmts Stmt
- -> <empty>
- Stmt -> Expr <eol> #PRTVAL
- { /* CHILD refers to the "Expr"; <eol> is not put in the tree */
- evalue= eval(tsemp->CHILD); /* evaluate the expression tree */
- printf(" %lf\n", evalue);
- tsemp= disp_sem(tsemp);
- }
- -> <identifier> := Expr <eol> #ASSIGN1
- {
- /* RIGHT refers to "Expr", LEFT to "<identifier>". ":=" and
- "<eol>" are not put in the tree */
- evalue= eval(tsemp->RIGHT); /* evaluate Expr tree */
- tsemp->LEFT->usem.symp->usym.value= evalue; /* the assignment */
- tsemp->LEFT->usem.symp->symt= REALVAR; /* defined */
- printf(" %lf\n", evalue);
- tsemp= disp_sem(tsemp);
- }
- -> <eol> \ allow an empty line
- Expr -> Expr + Term #PLUS
- -> Expr - Term #MINUS
- -> Term
- Term -> Term * Fact #MPY
- -> Term / Fact #DIVIDE
- -> Fact
- Fact -> Primary
- -> - Primary #UMINUS
- Primary -> ( Expr ) #PARENS
- -> <identifier> #VARIABLE
- { /* CHILD refers to the "<identifier>" */
- if (tsemp->CHILD->usem.symp->symt != REALVAR) {
- /* name is undefined */
- printf(" %s undefined\n", tsemp->CHILD->usem.symp->sym);
- tsemp->CHILD->usem.symp->symt= REALVAR; /* define it anyway */
- tsemp->CHILD->usem.symp->usym.value= 0.0;
- }
- }
- -> <real> #REALVAL
- -> <integer> #INTVAL