您的%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 时,但是当令牌为INTEGER 时,$1 只是一个简单的single_ival 值。访问pair 值时必须选择结构成员(.pair_sval 和.pair_ival),但必须省略单词pair。访问single_ival 时,您也省略了single_ival 一词;并且由于没有.field 子名称,因此$1 之后没有其他内容。
扩展讨论
至少如果您了解生成的解析器如何工作的基础知识,请注意解析堆栈的每个元素都是union 类型,这可能会有所帮助。 (嗯,是在使用%union之后,否则就是一个普通的int。)
%union 指令提供此类型的内容。它的内部名称是union YYSTYPE,它有一个拼写为YYSTYPE 的typedef-alias,这是您(或flex)在为每个令牌设置辅助值时应该使用的。对yylex() 的每次调用都必须返回一个普通的int 值,它是令牌编号(0 表示EOF,1 到255 表示普通char,令牌值从256 或以上开始表示令牌)。 (Byacc 使用从 257 开始的 #defines,而现代野牛使用 enum 并从 258 开始。)每个调用还设置 yylval 并且 yylval 中的值被推送(移位)到它的解析堆栈中,沿着与令牌。 (bison 和 byacc 都使用两个并行堆栈,一个用于解析器状态,一个用于值,但这是您不需要关心的实现细节。除了“Bob Corbett 编写了两者的第一个版本”之外,我不确定为什么它们都是在这里以同样的方式工作。)
当 bison(或 byacc)发出代码时,它使用分配的或假定的类型(来自 %token、%type 或尖括号提供的名称)根据需要添加联合元素名称。例如,假设 yacc 值堆栈命名为 S(不是假设,只是假设),假设 $1 实际上是 S[1],$2 是 S[2],等等。如果没有 %union 指令和显式类型,$n 只会直接转换为 S[n]。但是,当您引入 %union 时,它会转换为 S[n].field,其中 field 名称来自隐含或提供的类型。
因此,在上述情况下,当处理仅生成 single_ival 的 INTEGER 时,bison/byacc 无需您进行额外工作即可生成您需要的内容。但是,在处理产生pair 的TOKEN 时,S[1].pair 不足以选择struct 的一个元素。添加.pair_sval 选择struct 的char * 元素。
结构类型的名称struct named_for_discussion_below 永远不会出现在任何自动生成的代码中。如果您想将结构类型的副本或指向其实例的指针传递给某个例程(例如,alter(&$1),当 $1 扩展为 S[1].pair 时)您将需要使用结构类型的名称。如果您从不这样做,您可以完全省略该名称。