%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 ;
WHY吗
答案 0 :(得分:4)
如果您希望优先规则解决歧义,则需要为exp binop exp
规则指定优先级:
exp : exp binaryop exp %prec PLUS;
通过此更改,可以解决所有冲突。
修改强>
这些评论似乎表明对yacc / bison中的优先规则有些混淆。
优先规则是一种半自动解决语法中的移位/减少冲突的方法。它们只是半自动的,因为在指定优先级时你必须知道自己在做什么。
基本上,每当要移位的令牌和要减少的规则之间存在移位/减少冲突时,yacc会比较要移位的令牌的优先级和要减少的规则,并且 - 只要两者都有已分配优先级 - 以较高优先级为准。如果令牌或规则未分配优先级,则会向用户报告冲突。
当令牌和规则具有SAME优先权时, %left
/ %right
/ %nonassoc
进入图片。在这种情况下,%left
表示执行reduce,%right
表示执行shift,而%nonassoc
表示不执行此操作,如果解析器遇到此情况,则会在运行时导致语法错误。
优先级本身会分配给包含%left
/ %right
/ %nonassoc
的令牌和带有%prec
的规则。唯一奇怪的是,RHS上没有%prec
且至少有一个终端的规则获得了RHS上最后一个终端的优先级。这有时会最终为您确实不希望具有优先权的规则分配优先级,这有时会导致由于错误地解决它们而导致隐藏冲突。您可以通过在相关规则中添加额外级别的间接来避免这些问题 - 将RHS上有问题的终端更改为扩展到该终端的新非终端。
答案 1 :(得分:3)
这是因为第二个实际上是模棱两可的。第一个语法也是如此,但您通过添加%left
解决了歧义。
此%left
在第二个语法中不起作用,因为关联性和优先级不是从规则继承的。即binaryop
非终结符不会继承任何此类内容,即使它生成PLUS
和MINUS
。关联性和初始化本地化为规则,并围绕终端符号。
我们不能%left binaryop
,但我们可以稍微重构一下语法:
exp : exp binaryop term
exp : term;
term : INT;
binaryop: PLUS | MINUS ;
现在没有冲突,因为它是隐式左关联的。即更长和更长的表达式的生成只能发生在binaryop
的左侧,因为右侧是term
,它只生成一个INT。
答案 2 :(得分:0)
我认为这属于Bison手册所称的“Mysterious Conflicts”。你可以用以下内容复制它:
exp: exp plus exp;
exp: exp minus exp;
exp: INT;
plus: PLUS;
minus: MINUS;
给我四个S / R冲突。
答案 3 :(得分:0)
描述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
以下是有关'State 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;