我知道我可以在%parse-param {struct my_st *arg}
文件中声明.y
。因此yyparse()
更改为yyparse(struct my_st *arg)
。但是我如何引用flex规则中的参数?例如:
[0-9]+ { do_work(arg); return NUMBER; }
我想制作一个可重入的解析器,所以我需要这样做。请帮助我,谢谢!
答案 0 :(得分:8)
您需要将参数传递给yylex
。这需要修改bison解析器描述,以便解析器使用所需的参数调用yylex
和flex扫描器描述,以便扫描程序生成具有所需参数的yylex
。
Bison和flex不会相互通信,也不会看到彼此的源文件。但是,扫描程序通常#include
由bison生成的头文件是正常的,并且bison允许将代码直接插入此头文件中。这样就可以将整个配置放入bison文件中。
在野外,您可以使用%lex-param
directive为yylex
指定其他参数。但是如果你%define api.pure
,你需要知道附加的参数,这些参数也会添加到调用中。
如果你最近使用bison 3.0或更多,你可以使用
%param { struct my_st *arg }
是
的缩写%lex-param { struct my_st *arg }
%parse-param { struct my_st *arg }
使用单个指令是有意义的(如果你的bison足够新),因为无法在yyparse
函数中插入局部变量声明。因此,唯一可以传递给yylex
的变量是yyparse
的全局变量和参数。 [注1]
请记住,您有责任在bison文件中声明yylex
和yyerror
。即使您使用%lex-param
,bison也不会自动为yylex
生成声明。 [注2]
Flex通常会为yylex
生成声明,因此您不能简单地将声明放入bison生成的头文件中,然后将#include
放入扫描程序中。但是,如果定义了YY_DECL
macro,则flex生成的扫描程序将不会转发声明yylex
,并且会在YY_DECL
的定义中使用yylex
宏。您可以使用此功能将yylex
的声明放入野牛描述中,并将其传递给灵活扫描器。
在野外,您可以使用%code requires
部分或%code provides
部分add declarations to the generated header。不同之处在于requires
段在头文件中较早,在YYSTYPE
和YYLTYPE
被声明之前。如果您使用纯解析器,yylex
原型通常会引用YYSTYPE
(和YYLTYPE
,如果您使用位置),所以它需要进入%code provides
部分。为了优雅地与flex接口,您可以使用YY_DECL
宏来生成yylex
声明。
所以你最终可能得到以下内容:[注3]
file:mylanguage.y
%code requires {
#include <stdio.h>
typedef struct Context { ... } Context;
/* structs used in the %union declaration would go here */
}
%define api.pure full
%locations
%parse-param { Context* context }
%lex-param { Context* context }
%code provides {
#define YY_DECL \
int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, Context* context)
YY_DECL;
int yyerror(YYLTYPE* yyllocp, Context* context, const char* message);
}
然后,您只需以正常方式将生成的标头插入Flex文件:
file:mylanguage.l
%{
/* System library includes */
#include "mylanguage.tab.h"
%}
野牛中的%define api.pure full
声明避免了对全局变量yylval
和yylloc
的需求。但是,flex生成的扫描程序使用了许多其他内部全局变量;为了使扫描仪真正可重入,您需要将%option reentrant
添加到Flex文件中。使用该选项,yylex
应包含参数yyscan_t yyscanner
(与flex定义的所有其他词法分析器相关的函数一样)。您需要管理yyscanner
值,因此需要将yyparse
传递给yylex
,如上所述。您还需要初始化并销毁它,如flex manual中所述。 (如果您使用yylex
生成yyscanner
原型,则yylex
中的名称必须精确YY_DECL
。)
如果您还想传递自己的上下文对象,可以向yyparse
和yylex
添加两个参数,或者可以在yyscan_t
对象中包含上下文对象,如Flex手册section on Extra Data。
最后,如果您使用bison pure parser API,那么您需要更改编写flex操作的方式。您需要通过作为参数传递的指针来分配:yylval
,而不是分配给全局yylval.integer = atol(yytext);
(例如yylvalp->integer = atol(yytext);
)。 (参数的名称取决于你;我在这里使用的是我上面指定的那个。)
较早的实现允许您通过定义宏yylex
来指定YYLEX_PARAM
的参数。 bison 3.0不再支持此功能,因此您不应该使用它。
如果您使用%parse-param
,则其他参数也会添加到yyerror
。如果您%define api-pure full
,yyerror
也会收到位置对象。您对yyerror
的声明需要保持一致。
%locations
指令强制bison生成存储每个令牌的位置信息的代码。我在这里使用它是因为它使原型可预测。如果没有它,原型只有在您实际引用语义操作中的某个位置时才会包含YYLTYPE
个参数。如果您不打算使用令牌位置,则可能更愿意删除%locations
指令和所有YYLTYPE
参数。但通常位置信息很有用。