在整个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
答案 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)