Lex/Bison返回字符a提示无效令牌的问题排查
原本开发的Lex/Bison解析器使用命名令牌规则(如Lex中a {return Tok_A;},Bison中声明%token Tok_A)时运行正常,正确输入可被接受。尝试改为直接返回字母ASCII值实现更通用的解析器,但返回字符"a"时,Bison提示无效令牌。
代码示例
parser.l
%{ #include "parser4.tab.h" %} %% [a-h] {return *yytext;} \n {return 0;} /* EOF */ %%
parser.y
%{ extern void yyerror(char *); extern int yylex(void); #define YYDEBUG 1 %} %token a %% S : a {printf("S->a");} %% int main(void) { #if YYDEBUG yydebug = 1; #endif if(!yyparse()) printf("End of input reached\n"); return 0; } void yyerror (char *s) { /* fprintf (stderr, "%s\n", s); */ printf("Incorrect derivation!\n"); }
报错输出
输入"a"后得到:
Starting parse Entering state 0 Stack now 0 Reading a token a Next token is token "invalid token" () Incorrect derivation! Cleanup: discarding lookahead token "invalid token" () Stack now 0
用户推测问题出在Lex的return *yytext;规则上,已知Lex和Bison通过parser.tab.h通信,令牌从257开始编号,0-255对应ASCII字符,但疑惑为何直接返回"a"字符Bison无法识别。
核心原因
Bison中%token a声明的不是ASCII字符'a',而是一个用户自定义命名令牌,Bison会为其分配一个≥257的编号;而Lex中return *yytext;返回的是字符'a'的ASCII值(97),属于0-255的ASCII令牌范围。两者编号不匹配,导致Bison将Lex返回的ASCII值识别为无效令牌。
解决方法
有两种可行方案,根据需求选择:
方案1:直接使用ASCII字符字面量(推荐,符合通用解析需求)
去掉Bison中的%token a声明,在语法规则中用单引号包裹字符字面量,让Bison直接识别ASCII令牌:
修改后的parser.y核心部分:
%{ extern void yyerror(char *); extern int yylex(void); #define YYDEBUG 1 %} // 移除%token a声明 %% S : 'a' {printf("S->a");} // 用单引号包裹字符字面量 %%
此时Lex返回的ASCII值97(即字符'a')会被Bison正确匹配到规则中的'a',解析正常。
方案2:让Lex返回命名令牌的编号(适合需保留命名令牌的场景)
如果必须使用命名令牌,需确保Lex返回的编号与Bison定义的一致。比如在Bison中声明%token TOK_A,然后在Lex中对应返回该令牌:
- parser.y中:
%token TOK_A - parser.l中:
a {return TOK_A;}
但这种方式不符合"通用解析器"的需求,因此方案1更合适。
补充说明
Bison的令牌编号规则:
- 0:代表EOF(Lex返回0时触发)
- 1-255:对应ASCII字符,可直接在语法规则中用
'字符'引用 - 257及以上:用户通过
%token声明的自定义命名令牌,编号由Bison自动分配
内容的提问来源于stack exchange,提问作者Mimoa

