Bison移位/减少A::=AA规则中的冲突



我已经编写了以下bison语法文件:

%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冲突使用%prec进行合成,但我也不理解作者的解释。

有人能澄清为什么这个语法模棱两可,以及如何解决冲突吗?

编辑:我所说的NUMBER NUMBER是指NUMBER * NUMBER,即这两个数字的乘积。

这里的问题是令牌NUMBER没有优先级。因此,当有一个状态可以改变NUMBER或减少规则(无论该规则是否具有优先级)时,它无法决定该做什么

现在,您可以通过为NUMBER添加优先级(使其与*相同)来修复此语法,但如果您为以NUMBER以外的任何标记开头的表达式添加任何规则,则会返回此语法——例如,如果您添加expr: '(' expr ')',则会在'('上获得shift/reduce冲突。

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

造成这种混乱的根本原因是,bison的优先级规则并没有像你在使用"优先级"一词时天真地期望的那样,通过比较两个规则来解决优先级问题。相反,他们的工作方式是将要减少的规则的"优先级"与要转移的令牌的优先级进行比较,并以此为基础做出转移或减少的决定。在发生这种情况的解析中,第二条规则尚未被识别;相反,bison只是根据代币猜测它可能是什么。

部分答案是仔细查看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个移位/减少冲突。这些是不那么严重的冲突类型;如果您确信语法所做的是正确的,那么您可以声明您希望它们在语法中使用%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也是如此!

相关内容

  • 没有找到相关文章

最新更新