变量和有类型的标记
yacc不仅可以用%token定义标记,而且可以用
%type <type> token
为标记指定类型,其中<type>
需要是在%union中声明的类型,token为已由%token定义的标记名。
下面用带类型的变记扩展之前的计算器来处理具有单个字母名字的变量。因为只有26个字母(目前只关心小写字母),所以我们能在26个条目的数组中(称它为vbltable)存储变量。为了使计算器更有用,也可以扩展它来处理多个表达式(每行一个)和使用浮点值。
具有变量和实值的yacc程序ch3-3.y
注:示例来源于《lex与yacc》第二版,书中此示例代码有缺失,需要补充部分内容。
%{
#include <stdio.h>
double vbltable[26];
%}
%union {
double dval;
int vblno;
}
%token <vblno> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression
%%
statement_list: statement '\n'
| statement_list statement '\n'
statement: NAME '=' expression { vbltable[$1]=$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.");
else
$$ = $1 / $3;
}
| '-' expression %prec UMINUS { $$=-$2; }
| '(' NUMBER ')' { $$=$2; }
| NUMBER { $$=$1; }
| NAME { $$=vbltable[$1]; }
;
%%
int main(){
yyparse();
return 0;
}
void yyerror(char *s){
fprintf(stderr,"%s\n",s);
}
具有变量和实值的计算器词法分析程序ch3-3.l:
%{
#include "y.tab.h"
#include <math.h>
extern double vbltable[26];
#undef yywrap
%}
%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
yylval.dval = atof(yytext);
return NUMBER;
}
[ \t] ;
[a-z] { yylval.vblno=yytext[0]-'a'; printf("lex name=%d\n",yytext[0]-'a'); return NAME; }
"$" { return 0; }
\n |
. return yytext[0];
%%
int yywrap()
{
return 1;
}
编译运行结果:
[postgre@host132 ch3]$ ls
3.y.bak ch3-1.l ch3-1.y ch3-2.y ch3-3.l ch3-3.y ch3-4.y ch3hdr.h lex.yy.c test test.l test.y y.tab.c y.tab.h
[postgre@host132 ch3]$ yacc -d ch3-3.y
[postgre@host132 ch3]$ lex ch3-3.l
[postgre@host132 ch3]$ cc -o test lex.yy.c y.tab.c
[postgre@host132 ch3]$ ./test
2/3
=0.666667
4/7
=0.571429
a=1/3
lex name=0
b=3/4
lex name=1
a+b
lex name=0
lex name=1
=1.08333
$
[postgre@host132 ch3]$