针对编程语言语法的Bison Shift / Reduce冲突

时间:2019-02-14 18:16:11

标签: c parsing bison yacc compiler-compiler

我正在编写一种编程语言解析器,但陷入了这种Shift / Reduce冲突。

这是通过使用-v运行bison获得的parser.output文件中的冲突状态

State 1

   24 ident: TIDENT .
   26 call: TIDENT . TLPAREN args TRPAREN

    TLPAREN  shift, and go to state 24

    TLPAREN   [reduce using rule 24 (ident)]
    $default  reduce using rule 24 (ident)

当我尝试实现呼叫规则时,冲突正在发生,它似乎与正常的ident规则冲突。

这是语法的某些部分,(为简单起见,删除了一些动作,但不需要这样做。而且我不确定规则的定义顺序是否重要,如果我错了,请纠正我)

(大写字母是令牌)

身份规则很简单

ident: TIDENT
          ;

Args,用于通话。

args: /* empty */
        |
        expr
        |
        args TCOMMA expr
        ;

调用调用函数

call:
       TIDENT TLPAREN args TRPAREN
       ;

表达式表达式

expr:
    number
    |
    ternary
    |
    bool
    |
    string
    |
    ident
    |
    call
    |
    TLPAREN expr TRPAREN
    |
    expr TPLUS expr
    |
    expr TMINUS expr
    |
    expr TSLASH expr
    |
    expr TSTAR expr
    |
    expr TGT expr
    |
    expr TGE expr
    | 
    expr TLT expr
    |
    expr TLE expr
    ;

问题:为什么语法会有移位/减少冲突,您如何解决?我见过类似的样式解析器,它们没有真正奇怪的冲突。

如果您需要查看完整的语法以进行重现,请使用hastebin https://hasteb.in/zozifopi.shell

如果您需要其他详细信息,请在评论中让我知道,我将相应地编辑问题。

3 个答案:

答案 0 :(得分:1)

这里的根本问题是语法不明确,因为语句不需要终止(stmts: stmts stmt),并且语句可以是表达式。因此,两个表达式可以一个接一个地出现而没有标点符号。这意味着f(3)可以是一个表达式(一个函数调用),也可以是两个表达式(f(3))。

如果您希望语法分析器始终将其解释为函数调用(这是它的默认行为,因为它倾向于移动),则可以添加几个优先级声明,以便调用具有优先级高于降低优先级:

%precedence TIDENT
//...
%precedence TLPAREN
// ...
%%
expr : ident %prec TIDENT

这只是模糊性的论文,可能会引起令人惊讶的分析。但是,唯一的其他解决方案是使语言明确。

答案 1 :(得分:0)

问题在于,当解析器将TIDENT令牌移至Rails.application.config.before_initialize do # initialization code goes here end 令牌之前,该语法允许两种选择:

  1. TLPAREN减少为TIDENT,或
  2. 移动ident

野牛通常通过选择换档来解决换档/减少冲突,如果在这种情况下正是您想要的,那么您可以简单地忽略警告。

但是,在这种特殊情况下,您应该能够通过更改TLPAREN生产的规则来解决冲突:

call

使用该规则,不再先将call: ident TLPAREN args TRPAREN ; 减小为TLPAREN即可移动TIDENT

或者,您可以考虑完全删除ident非终结符,而直接在现在使用ident的任何地方直接使用TIDENT。也许还有其他选择。最适合您的方法可能取决于您要对语义动作进行的处理。我不能对此发表更具体的评论,因为您已选择将语义动作排除在我们的考虑范围之外。

答案 2 :(得分:0)

默认情况下,Bison会生成一个LR解析器,它是一个自底向上的解析器,它可以决定每种状态是转移令牌还是减少令牌。

冲突真的很简单,并且可以通过输出本身很好地解释(我想知道还不清楚),它告诉您:

  

如果我找到IDENTIFIER,是否应该通过规则24将其减少为ident的非终止符,还是应该像call规则中那样将其移位?

这是因为一旦减少,您将无法转移,反之亦然,这确实造成了冲突。

要解决冲突,您需要将选择项移至解析器的相同状态,以便能够根据上下文进行确定。

由于ident仅具有一条终端IDENT规则,并且通话规则也是如此,因此您可以轻松地将所有内容移到同一级别以使其始终移动:

expr: 
  IDENT | 
  IDENT LPAREN args RPAREN |
  ...

或对identcall本身使用相同的expr非终端,以便始终将其减少。