Bison中触发yyerror后未跳过错误语句仍执行规约的问题
Bison 语义错误后的语句跳过处理方案
问题核心根源
你遇到的“报错后仍继续完成错误语句规约”的现象,本质原因是:yyerror() 只是一个由用户自定义的报错打印函数,本身不会打断Bison的正常规约流程。只要当前规约动作没有主动让解析器进入错误状态,Bison就会默认该动作执行成功,正常完成当前非终结符的规约,并继续向上层非终结符逐层规约,和你日志里的表现完全吻合。
现有调整方案的可行性评估
你尝试的两处改动,一部分是正确的核心操作,另一部分是冗余且易引发冲突的写法:
- 报错分支中增加
YYERROR宏调用:这部分是完全正确的核心改动。YYERROR是Bison内置的控制宏,调用后会主动终止当前的正常规约流程,触发内置的错误恢复逻辑,让解析器停止处理当前错误语句,开始执行残留token的丢弃流程。 - 在
PROGRAM规则中新增PROGRAM error PARENTSIS_RIGHT STATMENT产生式:这部分是冗余的错误写法。Bison内置的错误恢复机制本身就会自动出栈、丢弃错误语句残留的所有token(包括已移入栈的右括号),不需要为某一个特定的残留token专门编写错误产生式。这类为特定token定制的错误规则,不仅会让语法状态表冗余,还可能引发额外的移进/归约冲突。
标准处理流程
按照Bison的错误恢复设计,正确的实现逻辑如下:
- 保留原有通用错误恢复框架:
PROGRAM: PROGRAM error STATMENT,这个规则已经可以覆盖绝大多数错误场景下的语句跳转需求。 - 在所有需要做语义校验的规约动作中,严格遵循“先打印错误,再调用
YYERROR”的逻辑,一旦校验不通过,主动打断当前规约。正确的写法示例:DEFINE_VECTOR: RESEV_VAR PARENTSIS_LEFT STRING COMMA ELEMENTS PARENTSIS_RIGHT { if ($5 <= 1) { yyerror("Vector too small"); YYERROR; // 立即终止当前规约,进入错误恢复 } if (check($3) != 0) { yyerror("Variable already exists"); YYERROR; // 立即终止当前规约,进入错误恢复 } createVar($3, 2, $5); } - 触发
YYERROR后,Bison会自动完成后续的错误恢复动作:从栈顶开始弹出状态,直到找到可以匹配error非终结符的栈位置,再逐个丢弃后续输入流中的token,直到找到可以继续正常解析的下一条语句起点,完全不需要手动编写针对特定残留token的匹配规则。
额外注意:你语法中ELEMENTS产生式的分支标识符(ELMENTINT、ELMENTFLOAT)存在拼写偏差,需要和词法分析部分的token定义保持一致,避免后续出现解析逻辑不匹配的问题。
内容的提问来源于stack exchange,提问作者RGO
相关产品推荐
相关产品推荐

