如何在 flex 规则中引用 lex 或解析参数?
How to reference lex or parse parameters in flex rules?
我知道我可以在 .y
文件中声明 %parse-param {struct my_st *arg}
。所以 yyparse()
变成了 yyparse(struct my_st *arg)
。但是我如何在 flex 规则中引用参数呢?例如:
[0-9]+ { do_work(arg); return NUMBER; }
我想做一个可重入的解析器,所以我需要这样做。请帮助我,谢谢!
您需要将参数传递给 yylex
。这需要修改 bison 解析器描述,以便解析器使用所需参数调用 yylex
,以及 flex 扫描器描述,以便扫描器生成具有所需参数的 yylex
。
Bison 和 flex 彼此不通信,也看不到彼此的源文件。但是,扫描器#include
bison生成的header文件是正常的,bison允许直接在这个header文件中插入代码的可能性。这样就可以将整个配置放入 bison 文件中。
在 bison 中,您可以使用 %lex-param
directive. But you need to be aware of the additional argument(s) which would also be added to the call if you %define api.pure
.
为 yylex
指定附加参数
如果你使用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-generated header 文件,然后 #include
将其放入扫描仪。但是,如果定义了 YY_DECL
macro,那么 flex-generated 扫描器将不会 forward-declare yylex
,它会在 [=] 的定义中使用 YY_DECL
宏14=]。您可以使用此功能将 yylex
的声明放入 bison 描述中,以便将其传递给 flex 扫描器。
请注意,这不与%bison-bridge
的使用兼容,它通常用于修改yylex
的原型以使用可重入解析器。 %bison-bridge
当然有用,但它不允许向 yylex
调用添加更多参数。如果您想使用 %bison-bridge
,您唯一的选择是将附加参数添加到 "extra data" object in the yyscan_t
object.
在 bison 中,您可以 add declarations to the generated header 使用 %code requires
部分或 %code provides
部分。区别在于 requires
段在 header 文件中较早,在 YYSTYPE
和 YYLTYPE
声明之前。如果您使用纯解析器,yylex
原型通常会引用 YYSTYPE
(和 YYLTYPE
,如果您使用位置),因此它需要进入 %code provides
部分.为了优雅地与 flex 交互,您可以使用 YY_DECL
宏来生成 yylex
声明。
所以你可能会得到类似下面的结果:[注 3]
文件: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);
}
然后您只需将生成的 header 以正常方式插入到您的 flex 文件中:
文件:mylanguage.l
%{
/* System library includes */
#include "mylanguage.tab.h"
%}
bison 中的 %define api.pure full
声明避免了对全局变量 yylval
和 yylloc
的需要。但是,flex-generated 扫描器还使用了许多其他内部全局变量;为了使扫描仪真正 re-entrant,您需要将 %option reentrant
添加到您的 flex 文件中。使用该选项,yylex
应包含参数 yyscan_t yyscanner
(与 flex 定义的所有其他 lexer-related 函数一样)。您需要管理 yyscanner
值,因此需要像上面那样通过 yyparse
传递到 yylex
。您还需要初始化和销毁它,如 flex manual 中所述。 (如果您使用 YY_DECL
生成 yylex
原型,它在 yylex
中的名称必须恰好是 yyscanner
。)
如果您还想传递自己的上下文 object,您可以向 yyparse
和 yylex
添加两个参数,或者您可以在其中包含您的上下文 object Flex 手册中描述的 yyscan_t
object section on Extra Data.
最后,如果你使用bison纯解析器API,那么你需要改变你编写flex动作的方式。您需要通过作为参数传递的指针进行分配,而不是分配给全局 yylval
(例如 yylval.integer = atol(yytext);
):yylvalp->integer = atol(yytext);
。 (参数的名称由您决定;我在这里使用的是我在上面指定的名称。)
备注
较旧的实现允许您通过定义宏 YYLEX_PARAM
来指定 yylex
的参数。自 bison 3.0 起不再支持此功能,因此您不应使用它。
如果使用%parse-param
,附加参数也会被添加到yyerror
。如果您 %define api-pure full
,yyerror
也会收到一个位置 object。您的 yyerror
声明需要保持一致。
%locations
指令强制 bison 生成存储每个令牌位置信息的代码。我在这里使用它是因为它使原型可预测。如果没有它,只有当您实际引用语义操作中某处的某个位置时,原型才会包含 YYLTYPE
个参数。如果您不打算使用标记位置,您可能更愿意删除 %locations
指令和所有YYLTYPE
个参数。但通常位置信息很有用。
我知道我可以在 .y
文件中声明 %parse-param {struct my_st *arg}
。所以 yyparse()
变成了 yyparse(struct my_st *arg)
。但是我如何在 flex 规则中引用参数呢?例如:
[0-9]+ { do_work(arg); return NUMBER; }
我想做一个可重入的解析器,所以我需要这样做。请帮助我,谢谢!
您需要将参数传递给 yylex
。这需要修改 bison 解析器描述,以便解析器使用所需参数调用 yylex
,以及 flex 扫描器描述,以便扫描器生成具有所需参数的 yylex
。
Bison 和 flex 彼此不通信,也看不到彼此的源文件。但是,扫描器#include
bison生成的header文件是正常的,bison允许直接在这个header文件中插入代码的可能性。这样就可以将整个配置放入 bison 文件中。
在 bison 中,您可以使用 %lex-param
directive. But you need to be aware of the additional argument(s) which would also be added to the call if you %define api.pure
.
yylex
指定附加参数
如果你使用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-generated header 文件,然后 #include
将其放入扫描仪。但是,如果定义了 YY_DECL
macro,那么 flex-generated 扫描器将不会 forward-declare yylex
,它会在 [=] 的定义中使用 YY_DECL
宏14=]。您可以使用此功能将 yylex
的声明放入 bison 描述中,以便将其传递给 flex 扫描器。
请注意,这不与%bison-bridge
的使用兼容,它通常用于修改yylex
的原型以使用可重入解析器。 %bison-bridge
当然有用,但它不允许向 yylex
调用添加更多参数。如果您想使用 %bison-bridge
,您唯一的选择是将附加参数添加到 "extra data" object in the yyscan_t
object.
在 bison 中,您可以 add declarations to the generated header 使用 %code requires
部分或 %code provides
部分。区别在于 requires
段在 header 文件中较早,在 YYSTYPE
和 YYLTYPE
声明之前。如果您使用纯解析器,yylex
原型通常会引用 YYSTYPE
(和 YYLTYPE
,如果您使用位置),因此它需要进入 %code provides
部分.为了优雅地与 flex 交互,您可以使用 YY_DECL
宏来生成 yylex
声明。
所以你可能会得到类似下面的结果:[注 3]
文件: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);
}
然后您只需将生成的 header 以正常方式插入到您的 flex 文件中:
文件:mylanguage.l
%{
/* System library includes */
#include "mylanguage.tab.h"
%}
bison 中的 %define api.pure full
声明避免了对全局变量 yylval
和 yylloc
的需要。但是,flex-generated 扫描器还使用了许多其他内部全局变量;为了使扫描仪真正 re-entrant,您需要将 %option reentrant
添加到您的 flex 文件中。使用该选项,yylex
应包含参数 yyscan_t yyscanner
(与 flex 定义的所有其他 lexer-related 函数一样)。您需要管理 yyscanner
值,因此需要像上面那样通过 yyparse
传递到 yylex
。您还需要初始化和销毁它,如 flex manual 中所述。 (如果您使用 YY_DECL
生成 yylex
原型,它在 yylex
中的名称必须恰好是 yyscanner
。)
如果您还想传递自己的上下文 object,您可以向 yyparse
和 yylex
添加两个参数,或者您可以在其中包含您的上下文 object Flex 手册中描述的 yyscan_t
object section on Extra Data.
最后,如果你使用bison纯解析器API,那么你需要改变你编写flex动作的方式。您需要通过作为参数传递的指针进行分配,而不是分配给全局 yylval
(例如 yylval.integer = atol(yytext);
):yylvalp->integer = atol(yytext);
。 (参数的名称由您决定;我在这里使用的是我在上面指定的名称。)
备注
较旧的实现允许您通过定义宏
YYLEX_PARAM
来指定yylex
的参数。自 bison 3.0 起不再支持此功能,因此您不应使用它。如果使用
%parse-param
,附加参数也会被添加到yyerror
。如果您%define api-pure full
,yyerror
也会收到一个位置 object。您的yyerror
声明需要保持一致。%locations
指令强制 bison 生成存储每个令牌位置信息的代码。我在这里使用它是因为它使原型可预测。如果没有它,只有当您实际引用语义操作中某处的某个位置时,原型才会包含YYLTYPE
个参数。如果您不打算使用标记位置,您可能更愿意删除%locations
指令和所有YYLTYPE
个参数。但通常位置信息很有用。