【问题标题】:Bison: strange shift-reduce conflict野牛:奇怪的移位减少冲突
【发布时间】:2018-06-18 22:55:22
【问题描述】:

我尝试在自定义语法中实现函数调用(加上类似的数组访问运算符):

expression
    :   ....OTHER EXPRESSION RULES....

    | expression PARENTHESIS_OPEN expressions PARENTHESIS_CLOSE   {  }     %prec DOT
    | expression SQUARE_OPEN expressions SQUARE_CLOSE      {  }          %prec DOT
    ;

这是我所有的运算符优先级:

%right ASSIGN ASSIGN_MOD ASSIGN_XOR ASSIGN_AND ASSIGN_STAR ASSIGN_MINUS ASSIGN_PLUS ASSIGN_OR ASSIGN_DIV ASSIGN_LSHIFT ASSIGN_RSHIFT
%right QUESTION COLON
%left OR
%left AND
%left BIN_OR
%left XOR
%left BIN_AND
%left NOT_EQUALS NOT_SAME EQUALS SAME
%left LESS LESS_EQUALS MORE MORE_EQUALS
%left LSHIFT RSHIFT
%left PLUS MINUS
%left PERCENT STAR SLASH
%right TILDE NOT DECREASE INCREASE
%left DOT

请注意,DOT 的优先级最高。因此,我尝试将此赋予我的函数调用规则。尽管如此,我还是收到了 74 个 shift/reduce 警告,它们都遵循这种模式:

State 25
15 expression: expression . PLUS expression
16           | expression . MINUS expression
17           | expression . NOT_EQUALS expression
18           | expression . NOT_SAME expression
19           | expression . PERCENT expression
20           | expression . ASSIGN_MOD expression
21           | expression . XOR expression
22           | expression . ASSIGN_XOR expression
23           | expression . BIN_AND expression
24           | expression . AND expression
25           | expression . ASSIGN_AND expression
26           | expression . STAR expression
27           | expression . ASSIGN_STAR expression
28           | expression . ASSIGN_MINUS expression
29           | expression . ASSIGN expression
30           | expression . EQUALS expression
31           | expression . SAME expression
32           | expression . ASSIGN_PLUS expression
33           | expression . BIN_OR expression
34           | expression . OR expression
35           | expression . ASSIGN_OR expression
36           | expression . SLASH expression
37           | expression . ASSIGN_DIV expression
38           | expression . DOT expression
39           | expression . LESS expression
40           | expression . LESS_EQUALS expression
41           | expression . LSHIFT expression
42           | expression . ASSIGN_LSHIFT expression
43           | expression . MORE expression
44           | expression . MORE_EQUALS expression
45           | expression . RSHIFT expression
46           | expression . ASSIGN_RSHIFT expression
48           | expression . PARENTHESIS_OPEN expressions PARENTHESIS_CLOSE
49           | expression . SQUARE_OPEN expressions SQUARE_CLOSE
53           | DECREASE expression .
55           | expression . DECREASE
56           | expression . INCREASE

PARENTHESIS_OPEN  shift, and go to state 46
DECREASE          shift, and go to state 47
INCREASE          shift, and go to state 52
SQUARE_OPEN       shift, and go to state 54
DOT               shift, and go to state 61

PARENTHESIS_OPEN  [reduce using rule 53 (expression)]
SQUARE_OPEN       [reduce using rule 53 (expression)]
$default          reduce using rule 53 (expression)

状态 46,即冲突转移所表明的内容如下:

State 46

48 expression: expression PARENTHESIS_OPEN . expressions PARENTHESIS_CLOSE

MINUS             shift, and go to state 5
TILDE             shift, and go to state 6
NOT               shift, and go to state 7
PARENTHESIS_OPEN  shift, and go to state 8
DECREASE          shift, and go to state 9
INCREASE          shift, and go to state 10
INT               shift, and go to state 11
FLOAT             shift, and go to state 12
STRING            shift, and go to state 13
CHAR              shift, and go to state 14
ID                shift, and go to state 15

$default  reduce using rule 59 (expressions)

expression   go to state 87
expressions  go to state 88

我真的不明白为什么野牛选择减少。因为我给了函数调用规则尽可能高的优先级,所以野牛应该尝试移动,直到它匹配那个。尽管如此,前缀 DECREASE 运算符看起来像是野牛的选择,即使它的优先级较低。

为什么野牛会这样做?如何向 bison 明确地说函数调用规则应该具有更高的优先级,从而避免冲突?

【问题讨论】:

    标签: bison operator-precedence


    【解决方案1】:

    以下引自this answer

    回想一下,在产生式和终端之间定义了优先关系。它不涉及两个终端或两个产生式(因此不能用于解决归约-归约冲突)。可以减少的生产的优先级与前瞻终端之间的比较确定是否会发生减少或转移。

    %prec 声明(重新)定义了它所属的 reduction 的优先级。在你的情况下,

    | expression PARENTHESIS_OPEN expressions PARENTHESIS_CLOSE   {  }     %prec DOT
    | expression SQUARE_OPEN expressions SQUARE_CLOSE      {  }          %prec DOT
    

    声明这两个缩减的优先级为DOT,而不是PARENTHESIS_CLOSESQUARE_CLOSE [注1]。由于后两个标记没有出现在%left / %right 声明中,这实际上是对优先级的定义,但出于两个原因,它是不必要的:

    1. 您可以将PARENTHESIS_CLOSESQUARE_CLOSE 添加到适当的优先级,但更重要的是

    2. 这两个归约不参与任何移位/归约冲突。

    您应该尝试理解(并希望同意)我在第 (2) 项中的主张。作为一个起点,请考虑您在问题中包含的状态 25。在状态 25 中,唯一可能的减少是通过规则 53 (expression: DECREASE expression)。您可以看到这一点,因为这是该州中唯一在右侧边缘具有 . 的项目。只有右边有点的项目才能被减少(因为右边的点表示该项目对应的生产可能在这种状态下完成。)而且,确实,你可以看到针对此状态报告的移位/减少冲突:

    PARENTHESIS_OPEN  shift, and go to state 46
    PARENTHESIS_OPEN  [reduce using rule 53 (expression)]
    
    SQUARE_OPEN       shift, and go to state 54
    SQUARE_OPEN       [reduce using rule 53 (expression)]
    

    这两个冲突都涉及使用规则 53 的可能减少。

    所以在状态 25 中,如果 ( 是前瞻字符,则语法将允许任一

    • ( 的变化,导致项目expression: expression PARENTHESIS_OPEN . expressions PARENTHESIS_CLOSE 的状态(注意点如何移动到PARENTHESIS_OPEN 标记上)。

      李>
    • 或减少规则expression: DECREASE expression

    Bison 通过比较 reduction (DECREASE) 的优先级与前瞻令牌 (PARENTHESIS_OPEN) 的优先级来解决此冲突。 PARENTHESIS_OPEN 没有出现在任何优先级中,因此 Bison 使用其默认值,即更喜欢移位。

    显然,更改归约的优先级 expression: expression PARENTHESIS_OPEN expressions PARENTHESIS_CLOSE 不会影响此冲突的解决,因为该归约与此冲突无关。

    现在,我的主张是,这种减少与语法中的任何冲突无关。这可能看起来有点古怪,因为我看不到很多语法,而且确实我可能是错的。理论上,表格中可能存在包含该项目的其他状态:

    expression: expression PARENTHESIS_OPEN expressions PARENTHESIS_CLOSE .
    

    还包括了一些可以转换的项目,例如

    some_non_terminal: expression PARENTHESIS_OPEN expressions PARENTHESIS_CLOSE . something
    

    这对我来说似乎不太可能。

    通常,后缀运算符归约(以及函数调用和数组索引在概念上是后缀运算符)从不参与移位归约冲突,因为实际上在后缀运算符之后永远不会发生移位。如果有这样的转变,运算符将是中缀,而不是后缀。您可以想象一个语法,其中运算符符号可以是中缀和后缀运算符,类似于 - 之类的运算符,它可以是中缀或前缀。但事实证明,由于超出此答案范围的原因,情况不对称。 [注2]

    回到最初的问题:我们已经看到,在 reduction expression: DECREASE expression(在这种情况下)和 终端 之间发生了 shift/reduce 冲突PARENTHESIS_OPENSQUARE_OPEN,因为 PARENTHESIS_OPENSQUARE_OPEN 未列在您的优先级中,所以无法解决。所以解决方案是列出它们:

    /* ... */
    %left PERCENT STAR SLASH
    %precedence TILDE NOT DECREASE INCREASE
    %precedence PARENTHESIS_OPEN SQUARE_OPEN
    

    请注意,我将最后一个 %left%right 更改为 %precedence,这是一个野牛扩展,允许您为关联性无意义的运算符定义优先级。我这样做是因为我认为它更清楚。 [注3]

    注意事项

    1. 使用PARENTHESIS_OPEN 而不是更简单、更易读的'(',真的没什么好说的。 Yacc 和 bison 允许像这样精确地对单个字符标记进行单引号,以提高

      的可读性
      expression:  expression '(' expressions ')'
      expression:  expression '[' expressions ']'
      

      这也简化了您的 (f)lex 扫描器,因为单个后备规则可以处理所有四个标记以及所有其他单个字符标记,包括您尚未添加到语法中的标记:

            /* Put this rule at the end of your ruleset */ 
      .    { return *yytext;}
      
    2. 例如,假设 ! 可以是后缀或中缀,并考虑表达式 a!-b*4。此处的歧义((a!)-ba!(-b))由于二进制 bang、减号和乘法运算符也具有有效的优先规则这一事实而更加复杂。

    3. 一元运算符,无论是前缀还是后缀,都没有关联性,因为关联性仅适用于二元运算符。关联性就是为什么a + b + c(a + b) + ca = b = ca = (b = c)。相反,只有一种方法可以解析--a!!a(或它们的组合)。 (如果您考虑优先关系如何影响 shift-reduce 冲突解决,这一点也很清楚。需要将一元 reduction ('!' expression .) 与 ! 前瞻进行比较的冲突是什么符号?对于像'-'这样的双重目的运算符,我们需要将归约的优先级更改为伪终端(%prec UMINUS),之后显然不能应用关联性,因为UMINUS不可能是前瞻符号。

    【讨论】:

    • 这真的很有帮助!谢谢!
    • @rici 'Shift/resolve conflict':您的意思是“shift/reduce conflict”吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    • 1970-01-01
    • 2021-12-24
    • 2013-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多