如何在 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 彼此不通信,也看不到彼此的源文件。但是,扫描器#includebison生成的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 文件中声明 yylexyyerror。即使你使用 %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 文件中较早,在 YYSTYPEYYLTYPE 声明之前。如果您使用纯解析器,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 声明避免了对全局变量 yylvalyylloc 的需要。但是,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,您可以向 yyparseyylex 添加两个参数,或者您可以在其中包含您的上下文 object Flex 手册中描述的 yyscan_t object section on Extra Data.

最后,如果你使用bison纯解析器API,那么你需要改变你编写flex动作的方式。您需要通过作为参数传递的指针进行分配,而不是分配给全局 yylval(例如 yylval.integer = atol(yytext);):yylvalp->integer = atol(yytext);。 (参数的名称由您决定;我在这里使用的是我在上面指定的名称。)


备注

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

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

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