如何让Bison生成的yytname外部可见?有无更优获取标签名表的方法?
你的sed workaround确实能搞定问题,但确实不够优雅——毕竟后期修改生成的代码,搞不好哪天Bison更新个版本,生成的代码结构变了,sed命令就失效了。这里有几个更原生、更可靠的方案:
方案一:通过宏直接控制yytname的可见性
Bison生成代码时,会用YYSTATIC宏来修饰yytname这类内部变量。默认情况下YYSTATIC被定义为static,但你可以在Bison源文件的开头重新定义这个宏,让它为空,这样生成的yytname就不会带有static属性:
%{ // 在Bison的%{ ... %}代码块中添加这个定义 #define YYSTATIC %} // 你的token定义、语法规则等 %token TAG SCORE %% // ... 你的语法规则实现 %%
这样生成的chess.tab.c里,yytname的声明就会变成:
const char *const yytname[] = { "$end", "error", "$undefined", "TAG", "SCORE", ... };
直接可以被其他编译单元访问,完全不需要sed这类后处理步骤。
方案二:封装安全的访问接口(最推荐)
如果你不想暴露全局的yytname(毕竟全局变量容易带来命名冲突或意外修改的风险),可以在Bison生成的代码里封装一个获取token名称的函数,然后在头文件中声明它供外部调用。
在Bison源文件最后一个%%之后的用户代码区添加:
%% // 这里可以放你的yylex、yyerror等实现代码 // 自定义获取token名称的函数 const char* get_token_name(int token_id) { // YYTOKENS是Bison自动生成的宏,表示token的总数量 if (token_id >= 0 && token_id < YYTOKENS) { return yytname[token_id]; } // 处理无效token的情况,返回友好提示 return "<unknown token>"; }
然后在你的头文件(比如用Bison的-d选项生成的chess.tab.h)中添加这个函数的声明:
#ifndef CHESS_TAB_H #define CHESS_TAB_H // 保留Bison自动生成的其他声明(比如YYSTYPE、token常量定义等) extern const char* get_token_name(int token_id); #endif
这样外部代码只需要调用get_token_name()就能拿到对应token的名称,既避免了直接访问内部变量,还能添加参数合法性检查,更安全也更符合代码封装的原则。
方案三:使用Bison的纯解析器模式(适用于多线程场景)
如果你的项目需要多线程支持,可以开启Bison的纯解析器模式(%define api.pure full)。在这种模式下,Bison会把yytname设置为全局可见(因为纯解析器不会依赖静态内部状态),不过这个模式会改变解析器的调用方式,需要你调整yylex等函数的参数传递逻辑。
在Bison源文件开头添加:
%define api.pure full
生成的代码中yytname就会是全局可访问的。不过这个方案更适合本来就需要纯解析器的场景,如果只是为了访问yytname,前面的方案二更合适。
内容的提问来源于stack exchange,提问作者A. P. Damien

