符号表
上一篇的计算器程序中的变量名只支持单个字符(a-z), 很少有用户满意单字符变量名,所以现在要增加使用较长变量名的能力。这意味着需要一个符号表——一种用来跟踪使用中的名字的结构。每次记法分析程序读取输入中的名字时,它都在符号表中查找这个名字,并且得到一个对应符号表条目的指针。在程序的其它地方,使用符号表指针而不是名字串,因为每次需要时指针比查找名字更快速更容易。
因为符号表要求语法分析程序和词法分析程序共享的数据结构,所以我们创建一个头文件ch3hdr.h:
$ vi ch3hdr.h
#define NSYMS 20 /* maximum number of symbols */
struct symtab {
char *name;
double value;
}symtab[NSYMS];
struct symtab *symlook(char *s);
这个符号表是一个结构体数组,每个结构都包含变量的名字和它的数值。我们还声明一个符号表查找程序symlook(),它以文本字符串形式的名字为参数,顺序地搜索字符表来寻找与传入参数名字对应的条目。如果某条目的name与symlook正在搜索的字符串匹配,就返回该条目的指针,因为名字已进被放进了表中。如果name字段为空,并且已经寻找了符号表中的所有条目且所有找到匹配的条目,那么我们就把名字输入至今为止还是空的符号表条目中。
具有符号表的语法分析程序ch3-4.y
%{
#include "stdio.h"
#include "ch3hdr.h"
#include <string.h>
%}
%union {
double dval;
struct symtab *symp;
}
%token <symp> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression
%%
statement_list: statement '\n'
| statement_list statement '\n'
statement: NAME '=' expression { $1->value=$3; }
| expression { printf("=%g\n",$1); }
;
expression: expression '+' expression { $$=$1+$3; }
| expression '-' expression { $$=$1-$3; }
| expression '*' expression { $$=$1*$3; }
| expression '/' expression
{ if($3==0.0)
yyerror("divided by zero.\n");
else
$$=$1 / $3;
}
| '-' expression %prec UMINUS { $$ = -$2; }
| '(' expression ')' { $$ = $2; }
| NUMBER { $$ = $1; }
| NAME { $$ = $1->value; }
%%
int main(){
yyparse();
return 0;
}
void yyerror(char *s){
fprintf(stderr,"%s\n",s);
}
具有符号表的词法分析程序ch3-4.l
%{
#include "y.tab.h"
#include "ch3hdr.h"
#include <math.h>
#undef yywrap
%}
%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
yylval.dval = atof(yytext);
return NUMBER;
}
[ \t] ;
[A-Za-z][A-Za-z0-9]* {
yylval.symp=symlook(yytext);
return NAME;
}
"$" { return 0; }
\n |
. return yytext[0];
%%
int yywrap()
{
return 1;
}
struct symtab *
symlook(char *s){
char *p;
struct symtab *sp;
for(sp=symtab;sp<&symtab[NSYMS];sp++){
if(sp->name && !strcmp(sp->name,s))
return sp;
if(!sp->name){
sp->name=strdup(s);
return sp;
}
}
yyerror("Too many symbols.");
exit(1);
}
编译和运行:
[postgre@host132 ch3]$ yacc -d ch3-4.y
[postgre@host132 ch3]$ lex ch3-4.l
[postgre@host132 ch3]$ cc -o test lex.yy.c y.tab.c
[postgre@host132 ch3]$ ./test
foo=23
foo/5
=4.6
abc=34
3*abc
=102
$
[postgre@host132 ch3]$