Bison 将参数传递给yylex

Bison 将参数传递给yylex,bison,flex-lexer,Bison,Flex Lexer,我正试图修改.l和.y文件,使扫描器和解析器可重入。根据GNU文档等,我将其放在了.y文件中: %define api.pure %lex-param { YYSTYPE *yylval } %parse-param { astNodePtr *programTree } 此外,我还在.l文件中添加了可重入和野牛桥选项 但是,在我使用bison-dv parser.y构建parser.tab.h和parser.tab.c之后,我注意到parser.tab.c包含声明 int yylex(voi

我正试图修改.l和.y文件,使扫描器和解析器可重入。根据GNU文档等,我将其放在了.y文件中:

%define api.pure
%lex-param { YYSTYPE *yylval }
%parse-param { astNodePtr *programTree }
此外,我还在.l文件中添加了
可重入
野牛桥
选项

但是,在我使用
bison-dv parser.y
构建parser.tab.h和parser.tab.c之后,我注意到parser.tab.c包含声明

int yylex(void);
但后来涉及到诸如

yychar = yylex (&yylval, yylval);
此外,试图编译flex创建的.c文件会产生各种各样的错误,这些错误集中在一个名为
yyg
的变量上


是否有其他标志/我需要添加到.l或.y文件中的任何内容?

请记住,flex和bison是两个完全独立的处理器,它们接收两个完全独立的输入文件并生成两个完全独立的程序,然后分别编译。Bison和flex不读取彼此的输入文件(或输出文件),也不读取程序员的想法。因此,生成的程序之间的任何互操作性都是存在的,因为作为程序员,您已经为此做出了安排

对于默认(不可重入)生成的扫描仪和解析器,您可以通过

  • 确保bison生成的标题是
    #在flex生成的扫描仪中包含
    d,以及
  • 在bison生成的解析器中声明
    yylex
    的原型
yylex
的原型通常是

int yylex(void);
您可以让flex使用不同的原型,甚至不同的名称生成
yylex
,但如果这样做,您需要告诉bison如何调用
yylex
(因为它无法知道您在扫描仪中更改了原型)

如果您在
parser.tab.c
文件中找到该行,几乎可以肯定是因为您将其插入了
parser.y
文件的代码块中,因为这是编译不可重入解析器所必需的,并且在修改文件以生成可重入解析器时忘记删除它

默认情况下,解析器和词法分析器使用全局变量
yylval
相互通信,以保存词法标记的语义值。(如果位置也在通信,则使用全局变量
yylloc
)。Bison在生成的解析器中定义这些全局变量,并在生成的头中声明它们,因此只要生成的头是
#将
d包含到生成的扫描程序中,两个程序就可以互操作

但这并不是一种共享数据的好方法,现代编码风格不赞成使用全局变量。通过生成可重入解析器,您可以启用另一种机制,在这种机制中,解析器将扫描指针传递到自己的本地
yylval
(和
yyloc
,如果使用)。这解决了可重入性问题,但现在您需要传达对flex的渴望,这样将生成一个
yylex
,它需要这些参数。方法是将
%bison桥
(可能还有
%bison位置
)插入到flex输入文件中

这样做将调整
yylex
的调用约定,但实际上不会生成可重入扫描程序。它只生成一个扫描器,它不依赖全局变量与解析器通信。扫描器依靠许多其他全局变量来维持其自身状态。如果需要可重入扫描程序,还需要在flex文件中插入
%reentrant
声明,这将导致它生成一个扫描程序,将其“全局”状态保持在不透明类型的上下文对象中
yyscan\t
。该上下文对象必须作为参数传递给
yylex
(位于
yylex
的参数列表末尾)。
%reentrant
flex声明生成了一个
yylex
,它需要该参数,但现在bison已退出循环;再一次,你有责任把这个事实告诉野牛

分配一个可以传递给lexer的
yyscan\u t
对象也是您的责任。但是你不能直接给扫描仪打电话。您调用解析器(
yyparse
),必要时它调用扫描器

Flex允许您将任意代码添加到扫描仪中(通过将其缩进到第一条规则之前)。但不幸的是,野牛没有这样的设施。将新变量注入解析器的唯一方法是将其添加到
yyparse
参数列表中(使用
%parse param
声明)。因此,您需要自己创建
yyscan\t
对象,并将其传递给
yyparse
。然后,您需要告诉bison在调用
yylex
时使用该对象,这是您使用
%lex param
声明所做的

应该相当清楚的是,上面提到的
%parse param
%lex param
声明几乎总是相同的。参数通过
yyparse
传递到
yylex
的唯一方法是,添加了
%parse param
的参数与添加了
%lex param
的参数具有相同的名称。既然如此,bison非常明智地允许您将
%parse param
%lex param
组合成一个
%param
声明

现在你只有一个小问题。需要通过
yyparse
进入
yylex
的参数具有不透明类型
yyscan\u t
。由于该参数是
yyparse
的参数,因此类型
yyscan\t
需要在生成的bison报头中可见。但是,
yylex
被调用(因此必须声明)并带有其他参数,类型为
YYSTYPE*
YYLTYPE*
,这些类型在bi中声明
typedef void* yyscan_t;