Create a gist now

Instantly share code, notes, and snippets.

@gabocze /calc.c
Last 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 <stdlib.h> /* malloc. */
#include <string.h> /* 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 <ctype.h>
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 <stdio.h> /* For printf, etc. */
#include <math.h> /* 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 <double> NUM /* Simple double precision number. */
%token <symrec*> VAR FNCT /* Symbol table pointer: variable and function.*/
%type <double> exp
%type <arglst_t *> 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. */
%%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment