我写过以下野牛语法文件:
%left '+' '-'
%left '*' '/'
%token NUMBER
%%
expr
: NUMBER
| expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| expr expr %prec '*' /* implicit multiplication */
;
现在bison
报告转移/减少有关expr : expr expr
的冲突。我已将问题提取到以下最小集:
%left OP
%%
expr
: 'a'
| expr expr %prec OP
;
我无法理解为什么bison
仍抱怨转移/减少冲突。我找到了一些旧的邮件存档:Re: bison/yacc: shift/reduce conflict using %prec for composition,但我也不理解作者的解释。
有人可以澄清为什么这个语法含糊不清,以及如何解决冲突?
编辑:NUMBER NUMBER
我的意思是NUMBER * NUMBER
,即这两个数字的乘积。
答案 0 :(得分:3)
这里的问题是令牌NUMBER
没有优先权。因此,当某个状态可以移动NUMBER
或缩小规则时(无论该规则是否具有优先权),它都无法决定做什么。
现在你可以通过添加NUMBER
的优先级(使其与*
相同)来修复此语法,但是如果你为任何以任何开头的表达式添加任何规则,它将会返回除NUMBER
以外的令牌 - 例如,如果您添加expr: '(' expr ')'
,您将在'('
上获得转移/减少冲突。
更大的问题是如果添加一元前缀运算符,例如expr: '-' expr
。在这种情况下,您不会收到冲突,因为' - '已经有优先权,但像NUMBER - NUMBER
这样的输入将被解析为NUMBER ( - NUMBER )
,这可能根本不是你想要的。使用优先规则处理这个问题没有好办法。
这种混淆的根本原因在于,野牛的优先规则不会通过比较两个规则来解决优先级问题,因为您可能会根据使用“优先级”这个词进行天真的预期。相反,他们通过将要减少的规则的“优先级”与要转移的令牌的“优先级”进行比较来进行工作,并基于此来决定转移或减少。在发生这种情况的解析中,第二条规则尚未被识别;相反,野牛只是根据令牌猜测它可能是什么。
答案 1 :(得分:2)
部分答案是仔细查看bison -v
的输出文件。对于你的第一个语法,我得到了这些摘录:
State 8 conflicts: 1 shift/reduce
State 9 conflicts: 1 shift/reduce
State 10 conflicts: 1 shift/reduce
State 11 conflicts: 1 shift/reduce
State 12 conflicts: 1 shift/reduce
Grammar
0 $accept: expr $end
1 expr: NUMBER
2 | expr '+' expr
3 | expr '-' expr
4 | expr '*' expr
5 | expr '/' expr
6 | expr expr
因此语法中有5个shift / reduce冲突。这些是较不严重的冲突类型;如果你确信语法正在做的是正确的话,你可以说你期望语法中有%expect 5
。
state 0
0 $accept: . expr $end
NUMBER shift, and go to state 1
expr go to state 2
state 1
1 expr: NUMBER .
$default reduce using rule 1 (expr)
state 2
0 $accept: expr . $end
2 expr: expr . '+' expr
3 | expr . '-' expr
4 | expr . '*' expr
5 | expr . '/' expr
6 | expr . expr
$end shift, and go to state 3
'+' shift, and go to state 4
'-' shift, and go to state 5
'*' shift, and go to state 6
'/' shift, and go to state 7
NUMBER shift, and go to state 1
expr go to state 8
state 3
0 $accept: expr $end .
$default accept
state 4
2 expr: expr '+' . expr
NUMBER shift, and go to state 1
expr go to state 9
状态5,6,7模仿状态4但是对于其他运营商。州8是第一个有转移/减少冲突的州。请记住,规则中的.
(点)表示解析器到达此状态时的位置。
state 8
2 expr: expr . '+' expr
3 | expr . '-' expr
4 | expr . '*' expr
5 | expr . '/' expr
6 | expr . expr
6 | expr expr .
NUMBER shift, and go to state 1
NUMBER [reduce using rule 6 (expr)]
$default reduce using rule 6 (expr)
expr go to state 8
state 9
2 expr: expr . '+' expr
2 | expr '+' expr .
3 | expr . '-' expr
4 | expr . '*' expr
5 | expr . '/' expr
6 | expr . expr
'*' shift, and go to state 6
'/' shift, and go to state 7
NUMBER shift, and go to state 1
NUMBER [reduce using rule 2 (expr)]
$default reduce using rule 2 (expr)
expr go to state 8
这两种状态之间存在差异和相似之处,但状态10,11,12与状态9匹配,除了不同的歧义点。
麻烦在于语法看到了:
NUMBER OP NUMBER NUMBER
它无法判断是否将其解析为:
(号码号码)号码 expr expr
或作为:
NUMBER OP ( NUMBER NUMBER )
expr OP expr
鉴于在每种情况下都是转移/减少冲突,它选择转移。如果这就是你想要的,那就添加%expect 5
并继续生活。如果这不是你想要的,那么你需要重新思考你的语法。一对相邻的数字表示什么,你确定你不需要一些操作符(可能是逗号或冒号)来分隔它们吗?
我尝试使用以下方法提高缺少运算符的优先级:
%left MISSING
在其他优先级声明之后,然后使用:
expr expr %prec MISSING
这没有改变任何事情。也没有通过在其他运算符之前列出MISSING的优先级来使其非常低。
如果你考虑如何解析这样的表达式,你会发现问题:
NUMBER OP NUMBER NUMBER NUMBER OP NUMBER NUMBER OP NUMBER
每个外观的OP相同。我的大脑受伤了! bison
也是如此!