Instantly share code, notes, and snippets.

# gabocze/calc.cLast active May 21, 2017

What would you like to do?
Exercice 1 from the Bison Manual for the version 3.0.4, Chapter 2, § 6: Add some new functions from math.h to the initialization list. https://www.gnu.org/software/bison/manual/html_node/index.html
 #include "calc.h" /* Contains definition of ’symrec’. */ /* The symbol table: a chain of ’struct symrec’. */ symrec *sym_table; struct init { char const *fname; void *fnct; char const *proto; }; struct init const arith_fncts[] = { { "atan", atan, "%lf" }, { "cos", cos, "%lf" }, { "exp", exp, "%lf" }, { "ln", log, "%lf" }, { "sin", sin, "%lf" }, { "sqrt", sqrt, "%lf" }, { "hypot", hypot, "%lf,%lf" }, { 0, 0, 0 }, }; /* 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.fnct.fnctptr = arith_fncts[i].fnct; ptr->value.fnct.proto = (char *) malloc( strlen(arith_fncts[i].proto)+1 ); strcpy(ptr->value.fnct.proto, arith_fncts[i].proto); } } #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; } arglst_t *create_arglst(double arg) { arglst_t * arglst = (arglst_t *)malloc(sizeof(arglst_t)); arglst->arg = arg; arglst->next = 0; return arglst; } arglst_t *append_arg(arglst_t *arglst, double arg) { arglst_t *ret = arglst; while(arglst->next!=0) { arglst = arglst->next; } arglst->next = create_arglst(arg); return ret; } void discard_arglst(arglst_t *arglst) { while(arglst!=0) { arglst_t *prev = arglst; arglst = arglst->next; free(prev); } } double fnctcall(symrec *fnctrec, arglst_t *arglst) { static char const * ERR_FNCTARG = "Bad arguments in call to function"; switch(check_arglst(fnctrec, arglst)) { case 1: return (((double)(*)(double))fnctrec->value.fnct.fnctptr)(arglst->arg); case 2: return (((double)(*)(double, double))fnctrec->value.fnct.fnctptr)(arglst->arg, arglst->next->arg); } char * s = (char *) malloc(strlen(ERR_FNCTARG)+strlen(fnctrec->name)+2); strcpy(s, ERR_FNCTARG); strcat(s, " "); strcat(s, fnctrec->name); error(s); discard_arglst(arglst); YYERROR; return 0; } int check_arglst(symrec *fnctrec, arglst_t *arglst) { int nargs=0; char *s = fnctrec->value.fnct.proto; size_t length=strcspn(s, ","); while(length==3 && strncmp(s, "%lf", length)==0 && arglst!=0 ) { nargs++; s += length+1; length = strcspn(s, ","); arglst = arglst->next; } if(length!=0 || arglst!=0) return -1; return nargs; } #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 (); }
 /* Function type. */ typedef void *func_t; /* Data type for links in the chain of symbols. */ struct symrec { char *name; /* name of symbol */ int type; /* type of symbol: either VAR or FNCT */ union { double var; /* value of a VAR */ struct { func_t fnctptr; /* value of a FNCT */ char *proto; /* prototype of a FNCT */ } fnct; } value; struct symrec *next; /* link field */ }; typedef struct symrec symrec; /* The symbol table: a chain of ’struct symrec’. */ extern symrec *sym_table; symrec *putsym (char const *, int); symrec *getsym (char const *); /* Data type for argument list in function calls */ struct arglst_s { double arg; struct arglst_s *next; }; typedef struct arglst_s arglst_t; arglst_t *create_arglst(double arg); arglst_t *append_arg(arglst_t *arglst, double arg); discard_arglst(arglst_t *arglst); double fnctcall(symrec *fnctrec, arglst_t *arglst); int check_arglst(symrec *fnctrec, arglst_t *arglst);
 %{ #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 %type arglst %precedence ’=’ %left ’-’ ’+’ %left ’*’ ’/’ %precedence NEG /* negation--unary minus */ %right ’^’ /* exponentiation */ %destructor {discard_arglst(\$\$);} arglst // Notice that if "exp" is erroneous inside the rule "arglst: arglst ',' exp", right-hand-side "arglst" will be grabbed by this destructor. %% /* The grammar follows. */ input: %empty | input line ; line: ’\n’ | exp ’\n’ { printf ("%.10g\n", \$1); } | error ’\n’ { yyerrok; } // TODO semantic value of symbols with symrec * type popped during error recovery should be freed. More important so of the terminals discarded thru the end of the recovery, for the lexer'll allocate space for them unnecessary. ; exp: NUM { \$\$ = \$1; } | VAR { \$\$ = \$1->value.var; } | VAR ’=’ exp { \$\$ = \$3; \$1->value.var = \$3; } | FNCT ’(’ arglst ’)’ { \$\$ = fnctcall(\$1, \$3); discard_arglst(\$3); } // errors from fnctcall() handled by up-sitting rule. | 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; } ; arglst: exp {\$\$ = create_arglst(\$1);} | arglst ',' exp {\$\$ = append_arg(\$1, \$3);} ; /* End of grammar. */ %%