📄 bison_5.htm
字号:
2.3000000000%</PRE><P>Note that multiple assignment and nested function calls are permitted.</P><H3><A NAME="SEC30" HREF="index.html#SEC30">Declarations for <CODE>mfcalc</CODE></A></H3><P>Here are the C and Bison declarations for the multi-function calculator.</P><PRE>%{#include <math.h> /* For math functions, cos(), sin(), etc. */#include "calc.h" /* Contains definition of `symrec' */%}%union {double val; /* For returning numbers. */symrec *tptr; /* For returning symbol-table pointers */}%token <val> NUM /* Simple double precision number */%token <tptr> VAR FNCT /* Variable and Function */%type <val> exp%right '='%left '-' '+'%left '*' '/'%left NEG /* Negation--unary minus */%right '^' /* Exponentiation *//* Grammar follows */%%</PRE><P>The above grammar introduces only two new features of the Bison language.These features allow semantic values to have various data types(see section <A HREF="bison_6.html#SEC45">More Than One Value Type</A>).</P><P>The <CODE>%union</CODE> declaration specifies the entire list of possible types;this is instead of defining <CODE>YYSTYPE</CODE>. The allowable types are nowdouble-floats (for <CODE>exp</CODE> and <CODE>NUM</CODE>) and pointers to entries inthe symbol table. See section <A HREF="bison_6.html#SEC52">The Collection of Value Types</A>.</P><P>Since values can now have various types, it is necessary to associate atype with each grammar symbol whose semantic value is used. These symbolsare <CODE>NUM</CODE>, <CODE>VAR</CODE>, <CODE>FNCT</CODE>, and <CODE>exp</CODE>. Theirdeclarations are augmented with information about their data type (placedbetween angle brackets).</P><P>The Bison construct <CODE>%type</CODE> is used for declaring nonterminal symbols,just as <CODE>%token</CODE> is used for declaring token types. We have not used<CODE>%type</CODE> before because nonterminal symbols are normally declaredimplicitly by the rules that define them. But <CODE>exp</CODE> must be declaredexplicitly so we can specify its value type. See section <A HREF="bison_6.html#SEC53">Nonterminal Symbols</A>.</P><H3><A NAME="SEC31" HREF="index.html#SEC31">Grammar Rules for <CODE>mfcalc</CODE></A></H3><P>Here are the grammar rules for the multi-function calculator.Most of them are copied directly from <CODE>calc</CODE>; three rules,those which mention <CODE>VAR</CODE> or <CODE>FNCT</CODE>, are new.</P><PRE>input: /* empty */ | input line;line: '\n' | exp '\n' { printf ("\t%.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 */%%</PRE><H3><A NAME="SEC32" HREF="index.html#SEC32">The <CODE>mfcalc</CODE> Symbol Table</A></H3><P><A NAME="IDX48"></A></P><P>The multi-function calculator requires a symbol table to keep track of thenames and meanings of variables and functions. This doesn't affect thegrammar rules (except for the actions) or the Bison declarations, but itrequires some additional C functions for support.</P><P>The symbol table itself consists of a linked list of records. Itsdefinition, which is kept in the header <TT>`calc.h'</TT>, is as follows. Itprovides for either functions or variables to be placed in the table.</P><PRE>/* 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 ();</PRE><P>The new version of <CODE>main</CODE> includes a call to <CODE>init_table</CODE>, afunction that initializes the symbol table. Here it is, and<CODE>init_table</CODE> as well:</P><PRE>#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; }}</PRE><P>By simply editing the initialization list and adding the necessary includefiles, you can add additional functions to the calculator.</P><P>Two important functions allow look-up and installation of symbols in thesymbol table. The function <CODE>putsym</CODE> is passed a name and the type(<CODE>VAR</CODE> or <CODE>FNCT</CODE>) of the object to be installed. The object islinked to the front of the list, and a pointer to the object is returned.The function <CODE>getsym</CODE> is passed the name of the symbol to look up. Iffound, a pointer to that symbol is returned; otherwise zero is returned.</P><PRE>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;}</PRE><P>The function <CODE>yylex</CODE> must now recognize variables, numeric values, andthe single-character arithmetic operators. Strings of alphanumericcharacters with a leading nondigit are recognized as either variables orfunctions depending on what the symbol table says about them.</P><P>The string is passed to <CODE>getsym</CODE> for look up in the symbol table. Ifthe name appears in the table, a pointer to its location and its type(<CODE>VAR</CODE> or <CODE>FNCT</CODE>) is returned to <CODE>yyparse</CODE>. If it is notalready in the table, then it is installed as a <CODE>VAR</CODE> using<CODE>putsym</CODE>. Again, a pointer and its type (which must be <CODE>VAR</CODE>) isreturned to <CODE>yyparse</CODE>.</P><P>No change is needed in the handling of numeric values and arithmeticoperators in <CODE>yylex</CODE>.</P><PRE>#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; 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 (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;}</PRE><P>This program is both powerful and flexible. You may easily add newfunctions, and it is a simple job to modify this code to install predefinedvariables such as <CODE>pi</CODE> or <CODE>e</CODE> as well.</P><H2><A NAME="SEC33" HREF="index.html#SEC33">Exercises</A></H2><P><A NAME="IDX49"></A></P><OL><LI>Add some new functions from <TT>`math.h'</TT> to the initialization list.<LI>Add another array that contains constants and their values. Thenmodify <CODE>init_table</CODE> to add these constants to the symbol table.It will be easiest to give the constants type <CODE>VAR</CODE>.<LI>Make the program report an error if the user refers to anuninitialized variable in any way except to store a value in it.</OL><HR>Go to the <A HREF="bison_1.html">first</A>, <A HREF="bison_4.html">previous</A>, <A HREF="bison_6.html">next</A>, <A HREF="bison_15.html">last</A> section, <A HREF="index.html">table of contents</A>.</BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -