如何解决Bison语法中表达式列表与if/then/else的冲突?
解决Bison中的Shift/Reduce冲突(不改变接受语言)
首先,让我们拆解这个问题,找到彻底消除冲突的方法——而且完全不会改变你的语法所接受的语言。
你的原始语法
%token if TokenIf then TokenThen else TokenElse true TokenTrue false TokenFalse %left APP %right IF %% Hungry : NoHungry | Hungry NoHungry %prec APP | if Hungry then Hungry else Hungry %prec IF NoHungry : true | false
冲突的核心原因
当解析到if ... else Hungry的末尾时,遇到true或false,Bison面临两个合法选择:
- 移进:把
true/false和else后的Hungry组合成Hungry NoHungry(对应规则2,APP优先级),最终得到if ... else (Hungry NoHungry) - 归约:先把
if ... else Hungry归约成一个独立的Hungry,再和后面的true/false组合成(if ... else Hungry) true(对应规则3,IF优先级)
你预期的是第一种行为(移进),Bison默认也会这么做,但冲突警告依然存在——因为Bison没有明确的优先级规则来判定这个选择,只能依赖默认的移进优先逻辑。
为什么原来的%prec没生效?
你给规则2和规则3设置了优先级,但移进的终结符true/false没有设置优先级。Bison解决shift/reduce冲突时,需要直接比较「归约规则的优先级」和「移进符号的优先级」。由于true/false没有优先级声明,Bison无法明确判定,冲突警告自然无法消除。
两种可行的解决方案(均不改变语言)
方案1:给true/false设置高于IF的优先级
我们可以让true/false的优先级比IF高,这样Bison会明确选择移进(因为移进符号的优先级更高)。只需调整%token的声明顺序(Bison中后声明的终结符优先级更高),或者用%precedence明确声明:
%token if TokenIf then TokenThen else TokenElse %left APP %right IF %token true TokenTrue false TokenFalse # 放在IF之后,优先级更高 %% Hungry : NoHungry | Hungry NoHungry %prec APP | if Hungry then Hungry else Hungry %prec IF NoHungry : true | false
或者用%precedence更清晰地声明:
%token if TokenIf then TokenThen else TokenElse true TokenTrue false TokenFalse %left APP %right IF %precedence true false # 明确声明优先级高于IF %% Hungry : NoHungry | Hungry NoHungry %prec APP | if Hungry then Hungry else Hungry %prec IF NoHungry : true | false
方案2:调整优先级顺序,让APP高于IF
原来的%left APP在%right IF之前,导致APP的优先级低于IF。我们调换顺序,让APP优先级更高,这样规则2(APP优先级)的优先级就会高于规则3(IF优先级),Bison会明确选择移进:
%token if TokenIf then TokenThen else TokenElse true TokenTrue false TokenFalse %right IF %left APP # APP优先级高于IF %% Hungry : NoHungry | Hungry NoHungry %prec APP | if Hungry then Hungry else Hungry %prec IF NoHungry : true | false
效果验证
这两种方案都能彻底消除shift/reduce冲突,同时完全保留你原来的语言——所有合法输入依然会被正确解析,而且解析逻辑和你预期的一致(优先组合else后的Hungry和后续的true/false)。
内容的提问来源于stack exchange,提问作者Sebastian Graf
相关产品推荐
相关产品推荐

