(bison.info)Mfcalc Symtab

Prev: Mfcalc Rules Up: Multi-function Calc

The `mfcalc' Symbol Table

   The multi-function calculator requires a symbol table to keep track
of the names and meanings of variables and functions.  This doesn't
affect the grammar rules (except for the actions) or the Bison
declarations, but it requires some additional C functions for support.

   The symbol table itself consists of a linked list of records.  Its
definition, which is kept in the header `calc.h', is as follows.  It
provides for either functions or variables to be placed in the table.

     /* 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          */
         double (*fnctptr)();  /* value of a 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 ();
     symrec *getsym ();

   The new version of `main' includes a call to `init_table', a
function that initializes the symbol table.  Here it is, and
`init_table' as well:

     #include <stdio.h>
     main ()
       init_table ();
       yyparse ();

     yyerror (s)  /* Called by yyparse on error */
          char *s;
       printf ("%s\n", s);
     struct init
       char *fname;
       double (*fnct)();

     struct init arith_fncts[]
       = {
           "sin", sin,
           "cos", cos,
           "atan", atan,
           "ln", log,
           "exp", exp,
           "sqrt", sqrt,
           0, 0
     /* The symbol table: a chain of `struct symrec'.  */
     symrec *sym_table = (symrec *)0;

     init_table ()  /* puts arithmetic functions in table. */
       int i;
       symrec *ptr;
       for (i = 0; arith_fncts[i].fname != 0; i++)
           ptr = putsym (arith_fncts[i].fname, FNCT);
           ptr->value.fnctptr = arith_fncts[i].fnct;

   By simply editing the initialization list and adding the necessary
include files, you can add additional functions to the calculator.

   Two important functions allow look-up and installation of symbols in
the symbol table.  The function `putsym' is passed a name and the type
(`VAR' or `FNCT') of the object to be installed.  The object is linked
to the front of the list, and a pointer to the object is returned.  The
function `getsym' is passed the name of the symbol to look up.  If
found, a pointer to that symbol is returned; otherwise zero is returned.

     symrec *
     putsym (sym_name,sym_type)
          char *sym_name;
          int sym_type;
       symrec *ptr;
       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 (sym_name)
          char *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;

   The function `yylex' must now recognize variables, numeric values,
and the single-character arithmetic operators.  Strings of alphanumeric
characters with a leading nondigit are recognized as either variables or
functions depending on what the symbol table says about them.

   The string is passed to `getsym' for look up in the symbol table.  If
the name appears in the table, a pointer to its location and its type
(`VAR' or `FNCT') is returned to `yyparse'.  If it is not already in
the table, then it is installed as a `VAR' using `putsym'.  Again, a
pointer and its type (which must be `VAR') is returned to `yyparse'.

   No change is needed in the handling of numeric values and arithmetic
operators in `yylex'.

     #include <ctype.h>
     yylex ()
       int c;
       /* Ignore whitespace, get first nonwhite character.  */
       while ((c = getchar ()) == ' ' || c == '\t');
       if (c == EOF)
         return 0;

     /* Char starts a number => parse the number.         */
       if (c == '.' || isdigit (c))
           ungetc (c, stdin);
           scanf ("%lf", &yylval.val);
           return NUM;

     /* Char starts an identifier => read the name.       */
       if (isalpha (c))
           symrec *s;
           static char *symbuf = 0;
           static int length = 0;
           int i;

     /* Initially make the buffer long enough
              for a 40-character symbol name.  */
           if (length == 0)
             length = 40, symbuf = (char *)malloc (length + 1);
           i = 0;

               /* 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 (c != EOF && isalnum (c));
           ungetc (c, stdin);
           symbuf[i] = '\0';

     s = getsym (symbuf);
           if (s == 0)
             s = putsym (symbuf, VAR);
           yylval.tptr = s;
           return s->type;
       /* Any other character is a token by itself.        */
       return c;

   This program is both powerful and flexible. You may easily add new
functions, and it is a simple job to modify this code to install
predefined variables such as `pi' or `e' as well.

automatically generated by info2www