为什么这种简单的语法有移位/减少冲突


%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非终端即使产生PLUSMINUS也不会继承任何这样的东西。关联性和优先性定位于规则,并围绕终端符号旋转。

我们不能做%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并在它之后找到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;

相关内容

  • 没有找到相关文章

最新更新