野牛转移/减少A :: = AA规则中的冲突

时间:2012-02-11 18:49:02

标签: bison yacc operator-precedence shift-reduce-conflict

我写过以下野牛语法文件:

%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 conflict using %prec for composition,但我也不理解作者的解释。

有人可以澄清为什么这个语法含糊不清,以及如何解决冲突?

编辑:NUMBER NUMBER我的意思是NUMBER * NUMBER,即这两个数字的乘积。

2 个答案:

答案 0 :(得分:3)

这里的问题是令牌NUMBER没有优先权。因此,当某个状态可以移动NUMBER或缩小规则时(无论该规则是否具有优先权),它都无法决定做什么。

现在你可以通过添加NUMBER的优先级(使其与*相同)来修复此语法,但是如果你为任何以任何开头的表达式添加任何规则,它将会返回除NUMBER以外的令牌 - 例如,如果您添加expr: '(' expr ')',您将在'('上获得转移/减少冲突。

更大的问题是如果添加一元前缀运算符,例如expr: '-' expr。在这种情况下,您不会收到冲突,因为' - '已经有优先权,但像NUMBER - NUMBER这样的输入将被解析为NUMBER ( - NUMBER ),这可能根本不是你想要的。使用优先规则处理这个问题没有好办法。

这种混淆的根本原因在于,野牛的优先规则不会通过比较两个规则来解决优先级问题,因为您可能会根据使用“优先级”这个词进行天真的预期。相反,他们通过将要减少的规则的“优先级”与要转移的令牌的“优先级”进行比较来进行工作,并基于此来决定转移或减少。在发生这种情况的解析中,第二条规则尚未被识别;相反,野牛只是根据令牌猜测它可能是什么。

答案 1 :(得分:2)

部分答案是仔细查看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个shift / reduce冲突。这些是较不严重的冲突类型;如果你确信语法正在做的是正确的话,你可以说你期望语法中有%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也是如此!