您的%union指令看起来……嗯,就您所展示的内容而言,“几乎可以”,但是缺少一个右括号。我不能说你省略的部分,但这int int是一个语法错误,所以我必须假设这也不是那里的内容。
大括号中的代码(flex 和 bison 部分)与联合中显示的片段不匹配。
这是一些正确的语法(我添加了更多名称以供讨论,并添加了一些其他项以使输出可编译gcc -O -Wall -c):
%{
#include <stdio.h>
extern int yylex(void);
extern int yyerror(const char *);
%}
%union {
struct named_for_discussion_below {
char *pair_sval;
int pair_ival;
} pair;
int single_ival;
}
%token <pair> TOKEN
%token <single_ival> INTEGER
%%
prog: exprlist;
exprlist: exprlist expr
| /*empty*/
;
expr : TOKEN { printf("got: %s %d\n", $1.pair_sval, $1.pair_ival); }
| INTEGER { printf("got: %d\n", $1); }
;
请注意,由于两个%token指令中提供的类型,bison 假设$1是 的实例struct named_for_discussion_below,包含pair_sval和pair_ival,当标记是 时,但当标记是 时TOKEN,这$1只是一个简单的single_ival值INTEGER。访问值时必须选择结构成员 ( .pair_svaland ) ,但必须省略单词。访问时,您也省略了该词;并且由于没有子名称,因此 . 之后没有其他内容。.pair_ivalpairpairsingle_ivalsingle_ival.field$1
扩展讨论
至少如果您了解生成的解析器如何工作的基础知识,在此注意解析堆栈的每个元素都是一个union类型,这可能会有所帮助。(嗯,是在使用之后%union,否则只是一个普通的int。)
该%union指令提供此类型的内容。它的内部名称是union YYSTYPE,它有一个 typedef-alias spelled YYSTYPE,这是您(或 flex)在为每个标记设置辅助值时应该使用的。每次调用都yylex()必须返回一个普通int值,即令牌编号(0 表示 EOF,1 到 255 表示普通char,令牌值从 256 或以上开始表示令牌)。(Byacc 使用#defines 从 257 开始,而现代野牛使用 anenum并从 258 开始。)每次调用还设置yylval和值 inyylval与令牌一起被推送(移动)到其解析堆栈中。(bison 和 byacc 都使用两个并行堆栈,一个用于解析器状态,一个用于值,但这是您不需要关心的实现细节。除了“Bob Corbett 编写了两者的第一个版本”之外,我不确定为什么它们都是在这里以同样的方式工作。)
当 bison(或 byacc)发出代码时,它使用分配的或假定的类型 from %token、%type或尖括号提供的名称,根据需要添加联合元素名称。例如,假设 yacc 值堆栈被命名S(不是只是假设),并且假设$1实际上是S[1]、$2beingS[2]等。没有%union指令也没有显式类型,$n直接转换为S[n]. 但是,当您引入%union时,它会转换为S[n].field,其中field名称来自隐含或提供的类型。
因此,在上面,当处理INTEGER只产生 a的 an 时single_ival,bison/byacc 会生成您需要的内容,而无需您进行额外的工作。但是,在处理TOKEN产生 a 的 a时pair,S[1].pair选择 的一个元素是不够的struct。添加.pair_sval选择 的char *元素struct。
结构类型的名称struct named_for_discussion_below永远不会出现在任何自动生成的代码中。如果您想将结构类型的副本或指向它的实例的指针传递给某个例程——例如,alter(&$1)当$1扩展为时S[1].pair——<em>您将需要使用结构类型的名称。如果您从不这样做,您可以完全省略该名称。