我知道我可以%parse-param {struct my_st *arg}
在.y
文件中声明.所以yyparse()
改变了yyparse(struct my_st *arg)
.但是我如何引用flex规则中的参数?例如:
[0-9]+ { do_work(arg); return NUMBER; }
我想制作一个可重入的解析器,所以我需要这样做.请帮帮我,谢谢!
你需要将参数传递给yylex
.这需要修改bison解析器描述,以便解析器调用yylex
所需的参数和flex扫描器描述,以便扫描器生成yylex
所需的参数.
Bison和flex不会相互通信,也不会看到彼此的源文件.但是,扫描程序对#include
bison生成的头文件是正常的,而bison允许将代码直接插入此头文件中.这使得将整个配置放入bison文件成为可能.
在野外,您可以指定yylex
使用该%lex-param
指令的其他参数.但是你需要知道额外的参数,如果你这些参数也会添加到调用中%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]
请记住,这是你的责任,声明yylex
和yyerror
在野牛文件.即使你使用%lex-param
,野牛也不会自动生成声明yylex
.[笔记2]
Flex通常会为其生成声明yylex
,因此您不能简单地将声明放入bison生成的头文件中,然后将#include
其放入扫描程序中.但是,如果定义了YY_DECL
宏,那么flex生成的扫描程序将不会向前声明yylex
,并且它将YY_DECL
在定义中使用宏yylex
.您可以使用此功能将声明yylex
放入野牛描述中,以便将其传递给灵活扫描器.
在bison中,您可以使用部分或部分向生成的标头添加声明.区别在于,在声明之前和之前,段在头文件中较早.如果你使用纯解析器,原型通常会引用(如果你使用位置),所以它需要进入一个部分.为了优雅地与flex接口,您可以使用宏来生成声明.%code requires
%code provides
requires
YYSTYPE
YYLTYPE
yylex
YYSTYPE
YYLTYPE
%code provides
YY_DECL
yylex
所以你最终会得到如下内容:[注3]
文件:mylanguage.y
%code requires { #includetypedef 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手册中所述.(如果要使用原型生成原型,则其名称yylex
必须正确.)yyscanner
yylex
YY_DECL
如果您还想传递自己的上下文对象,则可以向yyparse
和添加两个参数yylex
,或者您可以在对象内包含上下文yyscan_t
对象,如"额外数据"的Flex手册部分所述.
最后,如果您使用bison纯解析器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
参数.但通常位置信息很有用.