【问题标题】:Bison Shift/Reduce Conflict for a programming language grammar编程语言语法的 Bison Shift/Reduce Conflict
【发布时间】:2019-02-14 18:16:11
【问题描述】:

我正在编写一个编程语言解析器,但我陷入了这种 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 规则很简单

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

如果您需要有关其他任何内容的更多详细信息,请在 cmets 中告诉我,我会相应地编辑问题。

【问题讨论】:

  • 欢迎来到 Stck Overflow。请尽快阅读 How to AskAbout 页面,但更紧急的是,阅读有关如何创建 MCVE (minimal reproducible example) 的信息。您提供了语法的片段,但不是完整的、最小的语法来重现您遇到的问题。我们必须努力工作,看看发生了什么;我们不能只是从您的问题中复制粘贴。
  • 抱歉,刚刚编辑包含完整代码
  • 完整代码不是我们想要的——MCVE 中的Minimal 也很重要。我会看看它。我对您语法的最小版本的第一次猜测没有重现您的 SR 冲突(它有一个 SR 冲突,但不是那个)。
  • @pollen:完整代码与“在某个时间点包含代码的 pastebin 的链接”不同。您应该包括整个语法以及相关声明(优先级和标记声明;语义声明仅在与您的问题相关时)。就个人而言,我很欣赏不使用太多垂直空白的样式,因为我经常使用小屏幕设备查看问题。但这只是我;你不需要考虑它。重要的是,包含的代码无需修改即可使用,并且存在问题。

标签: c parsing bison yacc compiler-compiler


【解决方案1】:

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

如果您对解析器总是将其解释为函数调用感到高兴(这是它的默认行为,因为它更喜欢移位),那么您只需添加几个优先级声明,以便调用具有优先级高于归约:

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

这只是掩盖了歧义,可能会导致令人惊讶的解析。但唯一的其他解决方案是使语言明确。

【讨论】:

  • 啊,终于得到了一个很好的答案,谢谢你的清晰解释,语句终止符是计划好的,但我想我会从简单的开始暂时没有考虑到这些问题。
【解决方案2】:

问题在于,当解析器转移了一个 TIDENT 标记并向前看一个 TLPAREN 标记时,语法允许两种选择:

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

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

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

call:
       ident TLPAREN args TRPAREN
       ;

使用该规则,在不先将TIDENT 减少为ident 的情况下,将不再是转移TLPAREN 的选项。

或者,您可以考虑完全删除 ident 非终端,而是在您现在使用 ident 的任何地方直接使用 TIDENT。也可能有其他选择。哪个最适合您可能取决于您尝试对语义操作执行的操作。我无法对此做出更具体的评论,因为您选择将语义操作排除在我们的考虑范围之外。

【讨论】:

  • 在任何地方直接使用 TIDENT 都行不通,我尝试像您的第一个建议一样使用 ident 非终端作为呼叫规则,但这反而增加了额外的移位/减少冲突。
  • 好的忽略关于额外冲突的部分,这是另一个错误,但我仍然从 ident 得到一个冲突
  • 查看您的完整语法@PoLLeN,使用TIDENT 不起作用,因为令牌类型不匹配在您的问题中并不明显。至于在call 的生产中切换到使用ident,您可能仍然会遇到冲突,甚至是类似的冲突,但您不会遇到相同的冲突,因为其中涉及的状态之一原来的不存在修改后的语法。
  • 总的来说,SO 不是扩展调试协作的场所。我已经回答了提出的问题:“为什么语法有移位/减少冲突,你如何解决它?”看起来您需要为expr 重构您的生产的下一个冲突已经过去了。此外,您可能会发现您尝试解析的语言不是 LALR(1),因此您编写的任何野牛语法都必然包含一个或多个冲突。如果它正确地解决了歧义,那么这可能是一个放手的问题。
  • Bison 默认选择转换,这是我想要的,但由于某种原因,我对那里的警告感到不舒服,有没有办法告诉 bison 你确实想要默认操作以免发出警告?
【解决方案3】:

Bison 默认生成一个 LR 解析器,这是一个自下而上的解析器,它可以决定每个状态是移动还是减少它。

冲突真的很简单,输出本身很好地解释了(我想知道什么不清楚),它告诉你:

如果我找到 IDENTIFIER,我应该通过规则 24 将其减少为 ident 非终端,还是应该按照 call 规则将其转移?

这是因为一旦减少就不能转移,反之亦然,这确实造成了冲突。

要解决冲突,您需要将该选择移动到解析器的相同状态,以便它能够根据上下文做出决定。

由于 ident 仅具有终端 IDENT 规则并且同样适用于呼叫,您可以轻松地将所有内容移动到同一级别以使其始终移动:

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

或为callexpr 本身使用相同的ident 非终端,这样它总是会减少它。

【讨论】:

  • 我遇到了冲突,但不知道如何解决它,我尝试了将 ident 和 call 规则放入 expr 而不是单独的非终端规则的解决方案,但这仍然没有解决冲突。
  • @PoLLeN:你当然需要从expr非终端中删除callident
  • 我做到了,这里的代码再次以防我想念理解hasteb.in/gacehava.shell
  • 哦,那是因为您在其他规则中使用了ident 非终端,我的回答适用于您的特定规则。通常 Bison 语法中没有解决冲突的灵丹妙药,您需要了解为什么会发生这种情况,尝试使用 ident non terminal EVERYWHERE 而不是 IDENT
  • 嗯,我现在真的不明白,到处使用 ident 非终端?你不是说直接使用 TIDENT 令牌吗,目前没有其他规则使用 ident 非终端,我只是把它放在那里以防我以后需要它。
猜你喜欢
  • 2013-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多