野牛:奇怪的转移 - 减少冲突

时间:2018-06-18 22:55:23

标签: bison operator-precedence

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

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个移位/减少警告,所有这些都遵循这种模式:

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函数调用规则应该具有更高的优先级,从而避免冲突?

1 个答案:

答案 0 :(得分:3)

以下内容来自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. 这两项削减不参与任何转变/减少冲突。

  3. 你应该尝试在第(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
    

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

    正常情况下,后缀运算符缩减(以及函数调用和数组索引是概念上的后缀运算符)从不参与shift-reduce冲突,因为在后缀运算符之后几乎不存在可能的移位。如果有这样的转变,运营商将是中缀,而不是后缀。您可以想象一个语法,其中运算符符号可以是中缀和后缀运算符,类似于 - 之类的运算符,可以是中缀或前缀。但事实证明,由于超出了本答案范围的原因,情况并不对称。 [注2]

    回到最初的问题:我们已经看到转移/减少冲突是在减少 expression: DECREASE expression(在这种情况下)和终端之间 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))因二元爆炸,减号和乘法运算符也具有有效优先级规则而变得更加复杂。

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