使用Flex/Bison开发自定义编译器,如何让Bison使用自定义令牌数值代码?
我之前刚好折腾过类似的需求,这就给你一步步讲清楚怎么让Bison乖乖用你自定义的令牌数值~
核心思路
Bison允许你在令牌声明时直接指定自定义数值,只要先让它能识别你在TokenTypes.h里定义的宏就行。整个过程分三步:关联头文件、声明令牌对应关系、确保Flex也用相同数值返回令牌。
步骤1:在Bison文件中引入自定义头文件
首先在你的Bison源文件(比如parser.y)的最顶部,用%{ ... %}块包含TokenTypes.h,这样Bison的预处理器就能识别你定义的TokenType_INT、TokenType_PLUS这些宏了:
%{ #include "TokenTypes.h" // 这里还可以加其他需要的头文件,比如stdio.h之类的 %}
步骤2:在Bison中声明令牌与自定义数值的对应关系
接下来用Bison的%token语法,给每个令牌绑定你自定义的数值。有两种写法,选你觉得方便的就行:
写法一:逐个明确指定(推荐,清晰不易错)
直接给每个令牌名称赋值对应的宏:
%token INT = TokenType_INT %token PLUS = TokenType_PLUS %token NUMBER = TokenType_NUMBER %token EOF = TokenType_EOF // 注意:Bison默认EOF是0,所以如果你的宏也是0就对应上了
写法二:利用预处理器批量处理(适合令牌多的场景)
如果你的令牌名称和宏名有固定规律(比如宏是TokenType_XXX,令牌是XXX),可以在TokenTypes.h里加个辅助宏,然后在Bison里展开:
在TokenTypes.h中新增:
#define BIND_TOKEN(name) #name = TokenType_##name
然后在Bison文件里:
%token BIND_TOKEN(INT) %token BIND_TOKEN(PLUS)
这种方式能少写点重复代码,但要注意预处理器的语法细节,避免出错。
步骤3:让Flex词法分析器返回相同的数值
这一步很关键,不然Bison收到的令牌数值和你定义的对不上,肯定会出错。在Flex源文件(比如lexer.l)里也要包含TokenTypes.h,然后在匹配模式时返回对应的宏:
%{ #include "TokenTypes.h" // 如果用Bison的-d选项生成了parser.tab.h,也可以包含它,但确保你的自定义宏优先级更高 %} %% "int" { return TokenType_INT; } "+" { return TokenType_PLUS; } [0-9]+ { return TokenType_NUMBER; } [ \t\n] { /* 忽略空白字符,啥也不用返回 */ } . { yyerror("遇到未知字符啦"); } %% int yywrap() { return 1; // 表示没有更多输入了 }
注意事项
- 避免数值冲突:确保你自定义的所有令牌数值都是唯一的,而且不要和Bison默认分配的数值重叠(Bison会从256开始给未指定数值的令牌分配,所以如果你自定义的数值都小于256,基本不会冲突)。
- 不要依赖Bison自动生成的令牌头文件:如果你用
bison -d生成了parser.tab.h,里面也会包含令牌数值定义,但因为你已经自己指定了数值,所以两者会保持一致,不用担心冲突。 - EOF令牌的特殊处理:Bison默认把EOF当作0,所以建议你的
TokenType_EOF也定义为0,这样不用额外处理就能正常工作。
简单示例验证
假设你的TokenTypes.h是这样的:
#define TokenType_INT 1 #define TokenType_PLUS 2 #define TokenType_NUMBER 3 #define TokenType_EOF 0
按照上面的步骤写完parser.y和lexer.l后,用下面的命令编译:
flex lexer.l bison -d parser.y gcc lex.yy.c parser.tab.c -o mycompiler
运行./mycompiler后输入int 123 + 456,就能看到Bison正确识别你的自定义令牌啦~
内容的提问来源于stack exchange,提问作者Alkis Mavridis

