为什么这个简单的语法有转移/减少冲突?

时间:2012-03-15 09:23:29

标签: grammar bison yacc shift-reduce-conflict

%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吗

4 个答案:

答案 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非终结符不会继承任何此类内容,即使它生成PLUSMINUS。关联性和初始化本地化为规则,并围绕终端符号。

我们不能%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并找到PLUSMINUS时发现了冲突在它之后。在这种情况下,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;