我想将词汇分析留给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执行此操作,但无法找到有关如何手动执行此操作的任何文档或示例。
答案 0 :(得分:1)
在传统的bison / flex解析器中,yylval
是由bison生成的解析器中定义的全局变量,并在bison生成的头文件中声明(应该是#include&#39; d)扫描器)。所以一个简单的解决方案就是复制它:在yylval
中声明token.h
(作为全局)并在解析器中的某处定义它。
但是现代编程风格已经从使用全局变换(有充分理由),甚至flex
甚至会生成不依赖于全局状态的扫描器(如果请求)。要请求此类扫描程序,请指定
%option reentrant
扫描仪定义中的。默认情况下,这会将yylex
的原型更改为:
int yylex(yyscan_t yyscanner);
其中yyscan_t
是一个不透明的指针。 (这是C,所以这意味着它是void*
。)您可以在Flex manual中了解详细信息。最重要的一点是你可以要求flex也生成一个头文件(带有%option header-file
),以便其他翻译单元可以参考创建,销毁和操作yyscan_t
的各种函数,以及你需要最低限度地创建一个,以便yylex
有一个存储其状态的地方。 (理想情况下,你也会破坏它。)[注1]。
从bison
使用可重入扫描程序的预期方法是启用%option bison-bridge
(如果词法分析器为每个令牌生成源位置信息,则为%option bison-location
。这将为yylex
原型添加一个额外的参数:
int yylex(YYSTYPE *yylval_param, yyscan_t scanner);
使用'%选项bison-locations&#39;,添加了两个参数:
int yylex(YYSTYPE *yylval_param,
YYLTYPE *yylloc_param,
yyscan_t scanner);
灵活生成代码声明的语义类型YYSTYPE
和位置类型YYLTYPE
不。它们必须出现在您将#include到扫描仪的token.h
标题中。
bison-bridge参数的目的是提供一种机制,将语义值yylval
返回给调用者(即解析器)。由于yylval
实际上与参数yylval_param
[注释2]相同,因此它将是实际语义值的指针,因此您需要编写(例如)你的弹性动作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));}
显然,这个主题有很多变种。
不幸的是,生成的标题包含了很多不必要的包袱,其中包括一系列标准的宏定义&#34;全局&#34;因为在重入扫描仪中这些变量只能用于弹性动作,所以它不会起作用。
使用bison-bridge
生成的扫描程序将yylval
定义为宏,该宏引用不透明状态结构中的字段,并将yylval_param
存储到此字段中。我们提供了yyget_lval
和yyset_lval
函数,以便从yylex
之外获取或设置此字段。我不知道为什么;它似乎介于不必要和危险之间,因为状态将包含指针到值,如调用yylex
中所提供的,一旦调用返回,它可能是一个悬空指针。