使用Bison的D解析器接口时,如何连接Flex与Bison?
Flex+Bison D接口问题解决方案
一、实现Lex.yylex()连接解析器与词法分析器
Bison的D接口要求Lexer类返回Symbol类型,而非传统的int,可以通过手动适配或Flex生成适配代码解决:
手动适配方案
在自定义的Lex类中调用Flex生成的yylex()(返回int),将其转换为Symbol对象,同时同步yylval的值:
class Lex : Lexer { Symbol yylex() { int token = yylex(); // 同步Flex的yylval到当前Lexer的yylval this.yylval.n = yylval.n; if (token == 0) { return new Symbol(SymbolKind.SYM_EOF_); // 处理EOF } // 将int类型token转为SymbolKind枚举并生成Symbol SymbolKind kind = cast(SymbolKind)token; return new Symbol(kind); } void yyerror(string s) { stderr.writef("%s\n", s); } }
Flex自动生成适配代码
在Flex词法文件开头添加以下选项,生成D语言的词法分析器:
%option d %option noyywrap
生成的yylex()默认返回int,直接用上面的手动适配逻辑即可完成对接。
二、不生成yy.tab.h让Flex知晓符号
Bison生成的parser.d中包含SymbolKind枚举,可直接在Flex中引用:
- 先运行Bison生成
parser.d,里面的SymbolKind枚举包含所有终结符(命名终结符对应SYM_XXX_,字面字符对应SYM_ASCII码_)。 - 在Flex文件中引入
parser模块,直接使用枚举值:
%{ import parser; %} %% [0-9]+ { yylval.n = atoi(yytext); return cast(int)SymbolKind.SYM_NUM_; } "+" { return cast(int)SymbolKind.SYM_43_; // 43是'+'的ASCII码 } %%
三、解决字面字符映射为SYM_XX_的不直观问题
方法1:在Bison中为字面字符定义别名
在Bison声明部分给字面字符显式命名,让Bison生成可读性更强的枚举值:
%{ %} %define api.symbol.prefix {SYM_} %union { int n; } %token<n> NUM; %token PLUS '+' // 为'+'定义别名PLUS %% S: NUM | S PLUS NUM {} // 使用别名代替字面字符 %%
此时Bison会生成SYM_PLUS_枚举值,Flex中可直接返回cast(int)SymbolKind.SYM_PLUS_。
方法2:在D代码中封装字符映射函数
如果不想修改Bison规则,可在Lex类中添加辅助函数,将字符转为对应的SymbolKind:
class Lex : Lexer { private SymbolKind charToSymbolKind(char c) { return cast(SymbolKind)("SYM_" ~ cast(string)(cast(ubyte)c) ~ "_").to!SymbolKind; } Symbol yylex() { int token = yylex(); this.yylval.n = yylval.n; if (token == 0) { return new Symbol(SymbolKind.SYM_EOF_); } // 处理可打印ASCII字符的映射 if (token >= 32 && token <= 126) { return new Symbol(charToSymbolKind(cast(char)token)); } return new Symbol(cast(SymbolKind)token); } void yyerror(string s) { stderr.writef("%s\n", s); } }
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

