如何在flex规则中引用lex或parse参数?

时间:2015-12-22 14:43:15

标签: bison flex-lexer

我知道我可以在%parse-param {struct my_st *arg}文件中声明.y。因此yyparse()更改为yyparse(struct my_st *arg)。但是我如何引用flex规则中的参数?例如:

[0-9]+  { do_work(arg); return NUMBER; }

我想制作一个可重入的解析器,所以我需要这样做。请帮助我,谢谢!

1 个答案:

答案 0 :(得分:8)

您需要将参数传递给yylex。这需要修改bison解析器描述,以便解析器使用所需的参数调用yylex和flex扫描器描述,以便扫描程序生成具有所需参数的yylex

Bison和flex不会相互通信,也不会看到彼此的源文件。但是,扫描程序通常#include由bison生成的头文件是正常的,并且bison允许将代码直接插入此头文件中。这样就可以将整个配置放入bison文件中。

在野外,您可以使用%lex-param directiveyylex指定其他参数。但是如果你%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文件中声明yylexyyerror。即使您使用%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段在头文件中较早,在YYSTYPEYYLTYPE被声明之前。如果您使用纯解析器,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声明避免了对全局变量yylvalyylloc的需求。但是,flex生成的扫描程序使用了许多其他内部全局变量;为了使扫描仪真正可重入,您需要将%option reentrant添加到Flex文件中。使用该选项,yylex应包含参数yyscan_t yyscanner(与flex定义的所有其他词法分析器相关的函数一样)。您需要管理yyscanner值,因此需要将yyparse传递给yylex,如上所述。您还需要初始化并销毁它,如flex manual中所述。 (如果您使用yylex生成yyscanner原型,则yylex中的名称必须精确YY_DECL。)

如果您还想传递自己的上下文对象,可以向yyparseyylex添加两个参数,或者可以在yyscan_t对象中包含上下文对象,如Flex手册section on Extra Data

最后,如果您使用bison pure parser API,那么您需要更改编写flex操作的方式。您需要通过作为参数传递的指针来分配:yylval,而不是分配给全局yylval.integer = atol(yytext);(例如yylvalp->integer = atol(yytext);)。 (参数的名称取决于你;我在这里使用的是我上面指定的那个。)

注释

  1. 较早的实现允许您通过定义宏yylex来指定YYLEX_PARAM的参数。 bison 3.0不再支持此功能,因此您不应该使用它。

  2. 如果您使用%parse-param,则其他参数也会添加到yyerror。如果您%define api-pure fullyyerror也会收到位置对象。您对yyerror的声明需要保持一致。

  3. %locations指令强制bison生成存储每个令牌的位置信息的代码。我在这里使用它是因为它使原型可预测。如果没有它,原型只有在您实际引用语义操作中的某个位置时才会包含YYLTYPE个参数。如果您不打算使用令牌位置,则可能更愿意删除%locations指令和所有YYLTYPE参数。但通常位置信息很有用。