%{ #include /* For printf, etc. */ #include /* For pow, used in the grammar. */ #include "calc.h" /* Contains definition of 'symrec'. */ int yylex (void); void yyerror (char const *); %} %define api.value.type union /* Generate YYSTYPE from these types: */ %token NUM /* Simple double precision number. */ %token VAR FNCT /* Symbol table pointer: variable and function. */ %type exp %precedence '=' %left '-' '+' %left '*' '/' %precedence NEG /* negation--unary minus */ %right '^' /* exponentiation */ /* Generate the parser description file. */ %verbose /* Enable run-time traces (yydebug). */ %define parse.trace /* Formatting semantic values. */ %printer { fprintf (yyoutput, "%s", $$->name); } VAR; %printer { fprintf (yyoutput, "%s()", $$->name); } FNCT; %printer { fprintf (yyoutput, "%g", $$); } ; %% /* The grammar follows. */ input: %empty | input line ; line: '\n' | exp '\n' { printf ("%.10g\n", $1); } | error '\n' { yyerrok; } ; exp: NUM { $$ = $1; } | VAR { $$ = $1->value.var; } | VAR '=' exp { $$ = $3; $1->value.var = $3; } | FNCT '(' exp ')' { $$ = (*($1->value.fnctptr))($3); } | exp '+' exp { $$ = $1 + $3; } | exp '-' exp { $$ = $1 - $3; } | exp '*' exp { $$ = $1 * $3; } | exp '/' exp { $$ = $1 / $3; } | '-' exp %prec NEG { $$ = -$2; } | exp '^' exp { $$ = pow ($1, $3); } | '(' exp ')' { $$ = $2; } ; /* End of grammar. */ %% struct init { char const *fname; double (*fnct) (double); }; struct init const arith_fncts[] = { { "atan", atan }, { "cos", cos }, { "exp", exp }, { "ln", log }, { "sin", sin }, { "sqrt", sqrt }, { 0, 0 }, }; /* The symbol table: a chain of 'struct symrec'. */ symrec *sym_table; /* Put arithmetic functions in table. */ static void init_table (void) { int i; for (i = 0; arith_fncts[i].fname != 0; i++) { symrec *ptr = putsym (arith_fncts[i].fname, FNCT); ptr->value.fnctptr = arith_fncts[i].fnct; } } #include /* malloc. */ #include /* strlen. */ symrec * putsym (char const *sym_name, int sym_type) { symrec *ptr = (symrec *) malloc (sizeof (symrec)); ptr->name = (char *) malloc (strlen (sym_name) + 1); strcpy (ptr->name,sym_name); ptr->type = sym_type; ptr->value.var = 0; /* Set value to 0 even if fctn. */ ptr->next = (struct symrec *)sym_table; sym_table = ptr; return ptr; } symrec * getsym (char const *sym_name) { symrec *ptr; for (ptr = sym_table; ptr != (symrec *) 0; ptr = (symrec *)ptr->next) if (strcmp (ptr->name, sym_name) == 0) return ptr; return 0; } #include int yylex (void) { int c; /* Ignore white space, get first nonwhite character. */ while ((c = getchar ()) == ' ' || c == '\t') continue; if (c == EOF) return 0; /* Char starts a number => parse the number. */ if (c == '.' || isdigit (c)) { ungetc (c, stdin); scanf ("%lf", &yylval.NUM); return NUM; } /* Char starts an identifier => read the name. */ if (isalpha (c)) { /* Initially make the buffer long enough for a 40-character symbol name. */ static size_t length = 40; static char *symbuf = 0; symrec *s; int i; if (!symbuf) symbuf = (char *) malloc (length + 1); i = 0; do { /* If buffer is full, make it bigger. */ if (i == length) { length *= 2; symbuf = (char *) realloc (symbuf, length + 1); } /* Add this character to the buffer. */ symbuf[i++] = c; /* Get another character. */ c = getchar (); } while (isalnum (c)); ungetc (c, stdin); symbuf[i] = '\0'; s = getsym (symbuf); if (s == 0) s = putsym (symbuf, VAR); *((symrec**) &yylval) = s; return s->type; } /* Any other character is a token by itself. */ return c; } /* Called by yyparse on error. */ void yyerror (char const *s) { fprintf (stderr, "%s\n", s); } int main (int argc, char const* argv[]) { int i; /* Enable parse traces on option -p. */ for (i = 1; i < argc; ++i) if (!strcmp(argv[i], "-p")) yydebug = 1; init_table (); return yyparse (); }