我已经编写了以下bison语法文件:
%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冲突使用%prec进行合成,但我也不理解作者的解释。
有人能澄清为什么这个语法模棱两可,以及如何解决冲突吗?
编辑:我所说的NUMBER NUMBER
是指NUMBER * NUMBER
,即这两个数字的乘积。
这里的问题是令牌NUMBER
没有优先级。因此,当有一个状态可以改变NUMBER
或减少规则(无论该规则是否具有优先级)时,它无法决定该做什么
现在,您可以通过为NUMBER
添加优先级(使其与*
相同)来修复此语法,但如果您为以NUMBER
以外的任何标记开头的表达式添加任何规则,则会返回此语法——例如,如果您添加expr: '(' expr ')'
,则会在'('
上获得shift/reduce冲突。
一个更大的问题是,如果添加一元前缀运算符,例如expr: '-' expr
。在这种情况下,您不会得到冲突,因为"-"已经有优先级,但像NUMBER - NUMBER
这样的输入将被解析为NUMBER ( - NUMBER )
,这可能根本不是您想要的。没有什么好办法用优先规则来处理这个问题。
造成这种混乱的根本原因是,bison的优先级规则并没有像你在使用"优先级"一词时天真地期望的那样,通过比较两个规则来解决优先级问题。相反,他们的工作方式是将要减少的规则的"优先级"与要转移的令牌的优先级进行比较,并以此为基础做出转移或减少的决定。在发生这种情况的解析中,第二条规则尚未被识别;相反,bison只是根据代币猜测它可能是什么。
部分答案是仔细查看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个移位/减少冲突。这些是不那么严重的冲突类型;如果您确信语法所做的是正确的,那么您可以声明您希望它们在语法中使用%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
也是如此!