Yacc Practice, Part II |
In this section we will extend the calculator from the previous section to incorporate some new functionality. New features include arithmetic operators multiply and divide. Parentheses may be used to over-ride operator precedence, and single-character variables may be specified in assignment statements. The following illustrates sample input and calculator output:
user: 3 * (4 + 5) calc: 27 user: x = 3 * (4 + 5) user: y = 5 user: x calc: 27 user: y calc: 5 user: x + 2*y calc: 37
The lexical analyzer returns VARIABLE
and INTEGER
tokens. For
variables yylval
specifies an index to the symbol table sym
. For this
program sym
merely holds the value of the associated variable. When INTEGER
tokens are returned, yylval
contains the number scanned. Here is
the input specification for lex:
%{ #include <stdlib.h> #include "y.tab.h" void yyerror(char *); %} %% /* variables */ [a-z] { yylval = *yytext - 'a'; return VARIABLE; } /* integers */ [0-9]+ { yylval = atoi(yytext); return INTEGER; } /* operators */ [-+()=/*\n] { return *yytext; } /* skip whitespace */ [ \t] ; /* anything else is an error */ . yyerror("invalid character"); %% int yywrap(void) { return 1; }
The input specification for yacc follows. The tokens for INTEGER
and VARIABLE
are utilized by yacc to create #defines
in y.tab.h
for use in lex. This is followed by definitions for the arithmetic operators. We may specify %left
, for left-associative or %right
for right associative. The last
definition listed has the highest precedence. Consequently multiplication and division have higher
precedence than addition and subtraction. All four operators are left-associative. Using this
simple technique we are able to disambiguate our grammar.
%token INTEGER VARIABLE %left '+' '-' %left '*' '/' %{ void yyerror(char *); int yylex(void); int sym[26]; %} %% program: program statement '\n' | ; statement: expr { printf("%d\n", $1); } | VARIABLE '=' expr { sym[$1] = $3; } ; expr: INTEGER | VARIABLE { $$ = sym[$1]; } | expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; } | '(' expr ')' { $$ = $2; } ; %% void yyerror(char *s) { fprintf(stderr, "%s\n", s); } int main(void) { yyparse(); return 0; }