语法中的左递归导致冲突

时间:2009-11-08 18:01:45

标签: c grammar yacc bison lalr

在整个Bison语法中,我正在使用正确的递归,并且我已经读过左递归更好,因为它不必首先构建整个堆栈。

然而,当我尝试在其中任何一个上切换到左递归时,我总是会遇到很多冲突,我不明白为什么。

任何人都可以向我展示一个通用示例,说明使用左递归而不是右导致冲突(当正确的递归不会导致冲突时)。那么当切换到左边以纠正这种冲突时需要做什么。我认为一个基本的例子对我的帮助不仅仅是对我自己语法的修正。

修改
但是我想我应该包含一个特定的例子,因为我的理解有点不完整:-)将'list separator命令'改为'命令分隔符列表'可以解决冲突。

State 9 conflicts: 3 shift/reduce


Grammar

    0 $accept: input $end

    1 input: error NEWLINE
    2      | input NEWLINE
    3      | input list NEWLINE
    4      | /* empty */

    5 list: command
    6     | command separator
    7     | list separator command

    8 separator: SEMI
    9          | L_OR
   10          | L_AND

   11 command: variable_assignment
   12        | external_w_redir
   13        | external_w_redir AMP
   14        | pipeline
   15        | pipeline AMP

...

state 9

    5 list: command .
    6     | command . separator

    SEMI   shift, and go to state 18
    L_AND  shift, and go to state 19
    L_OR   shift, and go to state 20

    SEMI      [reduce using rule 5 (list)]
    L_AND     [reduce using rule 5 (list)]
    L_OR      [reduce using rule 5 (list)]
    $default  reduce using rule 5 (list)

    separator  go to state 22

2 个答案:

答案 0 :(得分:4)

编辑:

我必须收回原来的答案。正如我首先想到的那样,你的左递归语法 not 似乎不明确。我认为我对用于使最终分隔符可选的额外规则感到困惑。

以下是原始的,右递归的语法的简化版本:

list: COMMAND
      | COMMAND SEPARATOR
      | COMMAND SEPARATOR list
      ;

这个语法匹配(如果我没有比我想象的更困惑,这当然是可能的)输入C,CS,CSC,CSCS,CSCSC,CSCSCS等。即,一系列的分离器 - 分开的COMMAND,最后有一个可选的SEPARATOR。

这是你的左递归语法的简化版本,它在Bison中提供了一个转移/减少冲突:

list: COMMAND
      | COMMAND SEPARATOR
      | list SEPARATOR COMMAND
      ;

如果我正确地扩展了东西,这个语法匹配输入C,CS,CSC,CSSC,CSCSC,CSSCSC等。它不含糊,但它不等同于你的左递归语法。 COMMAND列表最后不能有SEPARATOR,并且COMMANDS之间的SEPARATOR可以加倍。

我认为转移/减少冲突的原因是Bison在决定是否转移或减少时只查找一个标记,并且使用第二个语法中引入的双分隔符,它有时会混淆。 / p>

如果重要的是最终分隔符是可选的,并且语法必须是左递归的,我建议重写它:

list: separatedlist
      | separatedlist SEPARATOR
      ;

separatedlist: COMMAND
               | separatedlist SEPARATOR COMMAND
               ;

但我不担心左或右,除非你的列表真的长,或者你打算在非常有限的硬件上运行你的解析器。在现代台式电脑上,我认为这并不重要。

答案 1 :(得分:1)

混淆/错误似乎来自列表的制作。你的左递归版是:

list: command
    | command separator
    | list separator command

您正确的递归版本是:

list: command
    | command separator
    | command separator list

这两个规则集实际上是完全不同的,匹配不同的东西。第一个匹配一个或多个命令,其间有分隔符,每个命令后面还有一个可选的额外分隔符 >。第二种类似,只是在 LAST 命令后只允许使用额外的可选分隔符

假设你真正想要的是后者的左递归版本,你想要的是

list_no_trailer: command
               | list_no_trailer separator command

list: list_no_trailer
    | list_no_trailer separator

您的版本中的冲突来自以下事实:在解析'命令'并在查找后看到分隔符时,它不知道是否在命令之后将其作为可选分隔符拉入,或者减少命令假设它是一个列表分隔符,并且在它之后会有另一个命令。这需要2个字符的前瞻,所以你的语法是LR(2),但不是LR(1)