您的%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_sval
and ) ,但必须省略单词。访问时,您也省略了该词;并且由于没有子名称,因此 . 之后没有其他内容。.pair_ival
pair
pair
single_ival
single_ival
.field
$1
扩展讨论
至少如果您了解生成的解析器如何工作的基础知识,在此注意解析堆栈的每个元素都是一个union
类型,这可能会有所帮助。(嗯,是在使用之后%union
,否则只是一个普通的int
。)
该%union
指令提供此类型的内容。它的内部名称是union YYSTYPE
,它有一个 typedef-alias spelled YYSTYPE
,这是您(或 flex)在为每个标记设置辅助值时应该使用的。每次调用都yylex()
必须返回一个普通int
值,即令牌编号(0 表示 EOF,1 到 255 表示普通char
,令牌值从 256 或以上开始表示令牌)。(Byacc 使用#define
s 从 257 开始,而现代野牛使用 anenum
并从 258 开始。)每次调用还设置yylval
和值 inyylval
与令牌一起被推送(移动)到其解析堆栈中。(bison 和 byacc 都使用两个并行堆栈,一个用于解析器状态,一个用于值,但这是您不需要关心的实现细节。除了“Bob Corbett 编写了两者的第一个版本”之外,我不确定为什么它们都是在这里以同样的方式工作。)
当 bison(或 byacc)发出代码时,它使用分配的或假定的类型 from %token
、%type
或尖括号提供的名称,根据需要添加联合元素名称。例如,假设 yacc 值堆栈被命名S
(不是只是假设),并且假设$1
实际上是S[1]
、$2
beingS[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>您将需要使用结构类型的名称。如果您从不这样做,您可以完全省略该名称。