如何将flex与我自己的解析器一起使用

How to use flex with my own parser?

本文关键字:一起 自己的 flex 我自己      更新时间:2023-10-16

我想把词法分析留给lex,但自己开发解析器。

我做了一个token.h头,其中包含令牌类型的枚举和简单的类层次结构,

对于 lex 规则:

[0-9]+ {yylval = new NumToken(std::stoi(yytext));return NUM;}

如何从解析器代码中获取 NumToken 指针?假设我只想打印出令牌。

while(true)
{
    auto t = yylex();
    //std::cout <<yylval.data<<std::endl; // What goes here ?
}

我可以用 yacc/bison 做到这一点,但找不到任何有关如何手动执行此操作的文档或示例。

在传统的 bison/flex 解析器中,yylval 是在 bison 生成的解析器中定义的全局变量,并在 bison 生成的头文件中声明(应该 #include 到生成的扫描器中(。所以一个简单的解决方案就是复制它:在token.h中声明yylval(作为全局(,并在解析器中的某个地方定义它。

但是现代编程风格已经从使用全局变量(有充分的理由(转移,事实上,如果需要,即使是flex也会生成不依赖于全局状态的扫描程序。要请求此类扫描仪,请指定

%option reentrant

在扫描仪定义中。默认情况下,这会将yylex的原型更改为:

int yylex(yyscan_t yyscanner);

其中yyscan_t是不透明的指针。(这是 C,所以这意味着它是一个void*。您可以在 Flex 手册中阅读详细信息;最重要的一点是,您可以要求 Flex 也生成一个头文件(带有 %option header-file (,以便其他翻译单元可以引用用于创建、销毁和操作yyscan_t的各种函数,并且您需要最少创建一个,以便yylex有地方存储其状态。(理想情况下,您也会销毁它。[注1]。

使用来自bison的可重入扫描程序的预期方法是启用%option bison-bridge(如果词法分析器为每个令牌生成源位置信息,则%option bison-location(。这将向yylex原型添加一个附加参数:

int yylex(YYSTYPE *yylval_param, yyscan_t scanner);

使用"%选项野牛位置",将添加两个参数:

int yylex(YYSTYPE *yylval_param,
          YYLTYPE *yylloc_param,
          yyscan_t scanner);

语义类型YYSTYPE和位置类型YYLTYPE不是由 Flex 生成的代码声明的。它们必须显示在您 #include 到扫描仪中的token.h标题中。

bison-bridge 参数的目的是提供一种机制,将语义值yylval返回给调用方(即解析器(。由于yylval实际上与参数yylval_param[注释2]相同,因此它将是指向实际语义值的指针,因此您需要在flex操作中编写(例如(yylval->data = ...

所以这是一种方法。

bison-bridge的一个可能更简单的替代方案是提供您自己的yylex原型,您可以使用宏YY_DECL执行此操作。例如,你可以做这样的事情(如果YYSTYPE很简单(:

#define YY_DECL std::pair<int, YYSTYPE> yylex(yyscan_t yyscanner)

然后规则可以只返回该对:

[0-9]+ {return std::make_pair(NUM, new NumToken(std::stoi(yytext));}

显然,这个主题有很多变体。

<小时 />

笔记

  1. 不幸的是,生成的标头包含了很多不必要的包袱,包括一堆标准"全局"的宏定义,这些定义不起作用,因为在可重入扫描程序中,这些变量只能在 flex 操作中使用。

  2. 使用 bison-bridge 生成的扫描程序将yylval定义为引用不透明状态结构中的字段的宏,并将yylval_param存储到该字段中。 提供了yyget_lvalyyset_lval函数,以便从yylex外部获取或设置此字段。我不知道为什么;这似乎介于不必要和危险之间,因为状态将包含指向值的指针,如调用 yylex 中所述,一旦调用返回,它很可能是一个悬而未决的指针。