%token <token> PLUS MINUS INT
%left PLUS MINUS
这有效:
exp : exp PLUS exp;
exp : exp MINUS exp;
exp : INT;
这有 2 个班次/减少冲突:
exp : exp binaryop exp;
exp : INT;
binaryop: PLUS | MINUS ;
为什么?
这是因为第二个实际上是模棱两可的。第一种语法也是如此,但您通过添加 %left
解决了歧义。
此%left
在第二种语法中不起作用,因为关联性和优先级不是从一个规则继承到另一个规则的。 即binaryop
非终端即使产生PLUS
和MINUS
也不会继承任何这样的东西。关联性和优先性定位于规则,并围绕终端符号旋转。
我们不能做%left binaryop
,但我们可以稍微重构语法:
exp : exp binaryop term
exp : term;
term : INT;
binaryop: PLUS | MINUS ;
这在现在没有冲突,因为它是隐含的左关联。 也就是说,越来越长的表达式的产生只能发生在binaryop
的左侧,因为右侧是一个只产生INT的term
。
如果您希望优先级规则解决歧义,则需要为exp binop exp
规则指定优先级:
exp : exp binaryop exp %prec PLUS;
通过该更改,所有冲突都得到解决。
编辑
这些评论似乎表明对yacc/bison中的优先规则的作用有些混淆。
优先规则是一种半自动解决语法中的移位/减少冲突的方法。 它们只是半自动的,因为当您指定优先级时,您必须知道自己在做什么。
基本上,每当要转移的令牌和要减少的规则之间存在移位/减少冲突时,yacc 都会比较要转移的令牌和要减少的规则的优先级,并且 - 只要两者都分配了优先级 - 以优先级较高者为准。 如果未分配令牌或规则的优先级,则会向用户报告冲突。
当令牌和规则具有相同的优先级时,%left
/%right
/%nonassoc
会出现。 在这种情况下,%left
表示执行reduce,%right
表示执行移位,%nonassoc
表示两者都不执行,如果解析器遇到这种情况,则会导致运行时语法错误。
优先级级别本身分配给具有 %left
/%right
/%nonassoc
的令牌和具有 %prec
的规则。 唯一奇怪的是,没有%prec
的规则,并且 RHS 上至少有一个终端获得 RHS 上最后一个终端的优先级。 这有时最终会为您确实不希望具有优先级的规则分配优先级,这有时会导致由于错误地解决冲突而导致隐藏冲突。 您可以通过在相关规则中添加额外的间接寻址级别来避免这些问题 - 将 RHS 上有问题的终端更改为扩展到该终端的新非终端。
我认为这属于野牛手册所说的"神秘冲突"。 您可以通过以下方式复制它:
exp: exp plus exp;
exp: exp minus exp;
exp: INT;
plus: PLUS;
minus: MINUS;
这为我提供了四个 S/R 冲突。
描述 Bison (版本 2.3( 在 Linux 上生成的冲突语法的输出文件如下。 顶部的关键信息是"状态 7 存在冲突"。
State 7 conflicts: 2 shift/reduce
Grammar
0 $accept: exp $end
1 exp: exp binaryop exp
2 | INT
3 binaryop: PLUS
4 | MINUS
Terminals, with rules where they appear
$end (0) 0
error (256)
PLUS (258) 3
MINUS (259) 4
INT (260) 2
Nonterminals, with rules where they appear
$accept (6)
on left: 0
exp (7)
on left: 1 2, on right: 0 1
binaryop (8)
on left: 3 4, on right: 1
state 0
0 $accept: . exp $end
INT shift, and go to state 1
exp go to state 2
state 1
2 exp: INT .
$default reduce using rule 2 (exp)
state 2
0 $accept: exp . $end
1 exp: exp . binaryop exp
$end shift, and go to state 3
PLUS shift, and go to state 4
MINUS shift, and go to state 5
binaryop go to state 6
state 3
0 $accept: exp $end .
$default accept
state 4
3 binaryop: PLUS .
$default reduce using rule 3 (binaryop)
state 5
4 binaryop: MINUS .
$default reduce using rule 4 (binaryop)
state 6
1 exp: exp binaryop . exp
INT shift, and go to state 1
exp go to state 7
以下是有关"状态 7"的信息:
state 7
1 exp: exp . binaryop exp
1 | exp binaryop exp .
PLUS shift, and go to state 4
MINUS shift, and go to state 5
PLUS [reduce using rule 1 (exp)]
MINUS [reduce using rule 1 (exp)]
$default reduce using rule 1 (exp)
binaryop go to state 6
问题由标记为 1
的行中的.
标记描述。 出于某种原因,%left
并没有像您期望的那样"生效",因此 Bison 在读取exp PLUS exp
并在它之后找到PLUS
或MINUS
时识别出冲突。 在这种情况下,Bison(和Yacc(会进行转换而不是减少。 在这种情况下,在我看来,这似乎等于赋予规则正确的优先权。
将%left
更改为%right
并省略它不会更改结果(就冲突警告而言(。 我也在Solaris上尝试了Yacc,它产生了基本上相同的冲突。
那么,为什么第一个语法有效? 下面是输出:
Grammar
0 $accept: exp $end
1 exp: exp PLUS exp
2 | exp MINUS exp
3 | INT
Terminals, with rules where they appear
$end (0) 0
error (256)
PLUS (258) 1
MINUS (259) 2
INT (260) 3
Nonterminals, with rules where they appear
$accept (6)
on left: 0
exp (7)
on left: 1 2 3, on right: 0 1 2
state 0
0 $accept: . exp $end
INT shift, and go to state 1
exp go to state 2
state 1
3 exp: INT .
$default reduce using rule 3 (exp)
state 2
0 $accept: exp . $end
1 exp: exp . PLUS exp
2 | exp . MINUS exp
$end shift, and go to state 3
PLUS shift, and go to state 4
MINUS shift, and go to state 5
state 3
0 $accept: exp $end .
$default accept
state 4
1 exp: exp PLUS . exp
INT shift, and go to state 1
exp go to state 6
state 5
2 exp: exp MINUS . exp
INT shift, and go to state 1
exp go to state 7
state 6
1 exp: exp . PLUS exp
1 | exp PLUS exp .
2 | exp . MINUS exp
$default reduce using rule 1 (exp)
state 7
1 exp: exp . PLUS exp
2 | exp . MINUS exp
2 | exp MINUS exp .
$default reduce using rule 2 (exp)
区别似乎在于,在状态 6 和 7 中,它能够根据接下来发生的事情来区分该做什么。
解决问题的一种方法是:
%token <token> PLUS MINUS INT
%left PLUS MINUS
%%
exp : exp binaryop term;
exp : term;
term : INT;
binaryop: PLUS | MINUS;