%union声明
%union声明标识出了符号值可能拥有的所有C类型。
声明格式如下:
%union {... 域声明 ...
}
域声明将被原封不动的拷贝到输出文件中类型为YYSTYPE的C的union声明里。bison并不检查%union中的内容是否是有效的C代码。
如果你有多个%union声明,它们的内容将会合并成一个C或者C++的联合声明。
如果不存在%union声明,bison将把YYSTYPE定义为int,这样所有的符号值都为整数。
如果你可以通过%type来把%union中声明的类型与特定的符号关联起来。
bison把生成的C的联合声明放在生成的C文件和可选的头文件(除非你另行指定,否则被命名为name.tab.h)中,所以你可以在其它
源文件中通过包含该头文件来使用YYSTYPE。反过来说,你也可以把你自己声明的YYSTYPE放在一个头文件中,然后在定义部分通过
#include来引用它。在这种情况下,你必须至少有一个%type或者其它符号类型声明来告诉bison你在使用显示声明的符号类型。
%token声明
token,或者说终结符,是词法分析器传递给语法分析器的符号。当bison语法分析器需要新的标记时,它调用yylex(),这将从输入
中返回下一个标记。在输入结束时,yylex()返回0。
标记可以是通过%token定义的符号或者是单引号中的各个字符。所有被用来作为标记的符号必须在定义部分显式声明,例如:
%token UP DOWN LEFT RIGHT
标记也可以通过%left,%right,%nonassoc来声明,它们都具有和%token一样的语法选项。
标记编号
在词法分析和语法分析器中,标记通过小型整数来唯一标识。一个文字标记的记号编号就是它在本地字符集(通常是ASCII)中的数值,
而且也与被引起字符的C语言的值一致。
符号标记通常由bison来负责编号,该编号要大于任何可能的字符编码,所以它们不会和文字标记发生冲突。你也可以在%token的标记
名字后直接加上要赋值的编号:
%token UP 50 DOWN 60 LEFT 17 RIGHT 25
给两个标记赋予相同的编号是错误的行为。在大多数情况下,让bison来选择每个标记的编号最简单易懂。词法分析器需要知道标记编号,
以便于能够返回合适的数值给语法分析器。对于文字标记而言,它使用对应的C字符常量。对于符号标记,你可以通过-d命令行标志来让bison
创建一个C的头文件,里面包含所有标记编号的定义。如果你在你的词法分析器中#include这个头文件,你就可以直接在C代码中使用符号记号,
例如,UP,DOWN,LEFT和RIGHT。如果你的源文件是xxx.y的话,你的头文件通常会生成为xxx.tab.h,你也可以通过%defines声明或者
--defines=filename命令行选项来更改它。
%defines "xxxsyms.h"
标记值
bison中的每个符号都可以有关联值。由于标记可以有值,你需要在词法分析器返回标记给语法分析器时来设置值。标记值总是保存在变量
yylval中。在最简单的语法分析器里,yylval就是简单的int变量,你可以在flex词法分析器中做如下设置:
[0-9]+ {yylval = atoi(yytext);return NUMBER;}
不过,在大多数情况下,不同的符号会有不同的值类型。
在语法分析器中,你必须定义所有拥有值的标记的值类型。你只需要把相应的联合类型中的标记名字用尖括号括起,然后放到%token或者优
先级声明中。你可以如下定义你的值类型:
%union {enum optype opval;double dval;
}%nonassoc <opval> RELOP
%token <dval> REAL%union {char *sval;
}...
%token <sval> STRING
在这个例子中,RELOP是一个关系操作符,比如==或者>,而标记值表明具体的操作符。
当你返回标记时,你需要设置yylval中相应的域。本例中,你可以在词法分析器中如下进行设置:
%{#include "parser.tab.h"%}...[0-9]+\.[0-9]* {yylval.dval = atof(yytext);return REAL;}\"[^"]*\" {yylval.sval = strdup(yytext);return STRING;}"==" {yylval.opval = OPEQUAL;return RELOP;}
REAL的值是一个double类型,所以它被放在yylval.dval中,而STRING的值是char *类型,所以它被放在yylval.sval中。