快乐:减少/减少冲突

时间:2012-05-26 10:05:37

标签: haskell happy reduce-reduce-conflict

为什么这会引发关于减少/减少冲突的警告

root : set1 'X'     
     | set2 'X' 'X' 

set1 : 'A'          
     | 'B'             

set2 : 'B'          
     | 'C'  

但接下来还可以吗?

root : 'A' 'X'     
     | 'B' 'X'     
     | 'B' 'X' 'X'  
     | 'C' 'X' 'X' 

1 个答案:

答案 0 :(得分:8)

不同之处在于,在第一种情况下,解析器必须先选择减少量,然后才能看到一个'X'或者两个{...}。

在第二种情况下,解析器可以使用相同的状态,让它称之为BX,当它看到BX时 - 两者都移位 - 然后取决于下一个令牌,可以移位(如果是X),然后缩小'B' 'X' 'X'规则,或者立即减少'B' 'X'

请注意,如果他们在 -e.g之后没有相同的令牌。你有set1 'X'但是set2 'Y' - 然后就没有问题了,因为前瞻可以启动并选择要减少的数量。

以下是bison -v输出中揭示此问题的相关部分:

案例一

state 0

$accept: . root $end

'A'  shift, and go to state 1
'B'  shift, and go to state 2
'C'  shift, and go to state 3

root  go to state 4
set1  go to state 5
set2  go to state 6

假设我们得到'B',我们转到州2:

state 2

set1: 'B' .
set2: 'B' .

'X'       reduce using rule 4 (set1)
'X'       [reduce using rule 5 (set2)]
$default  reduce using rule 4 (set1)

请注意,我们可以进行两种缩减:set1set2,两者都具有相同的输入标记。因此减少/减少;我们只有一个前瞻标记,并且使用这个语法,唯一的标记可能是'X' - 在任何一种情况下都可以!

案例二

state 0

$accept: . root $end

'A'  shift, and go to state 1
'B'  shift, and go to state 2
'C'  shift, and go to state 3

root  go to state 4

假设我们得到'B',我们转到州2:

state 2

root: 'B' . 'X'
    | 'B' . 'X' 'X'

'X'  shift, and go to state 6

虽然我们只有一个前瞻标记,但由于输入的容纳结构,解析器生成器可以生成一个看到'B' 'X'的状态。因此,我们在任何情况下都会进入状态6(否则会出错; - )):

州6

root: 'B' 'X' .
    | 'B' 'X' . 'X'

'X'  shift, and go to state 9

$default  reduce using rule 2 (root)

这就是魔术发生的地方:如果我们看到'X',我们会转移并转到状态9(我们减少),否则我们立即减少'B' 'X'

为了完整性,这里是状态9:

state 9

root: 'B' 'X' 'X' .

$default  reduce using rule 3 (root)

如果我们有一个先行的标记来消除歧义

使用此示例语法:

root: set1 'X'
    | set2 'Y'

set1: 'A'          
    | 'B'             

set2: 'B'          
    | 'C'

然后,我们开始:

state 0

$accept: . root $end

'A'  shift, and go to state 1
'B'  shift, and go to state 2
'C'  shift, and go to state 3

root  go to state 4
set1  go to state 5
set2  go to state 6

我们转移'B'并转到州2:

state 2

set1: 'B' .
set2: 'B' .

'Y'       reduce using rule 5 (set2)
$default  reduce using rule 4 (set1)

因此,set1set2的规则都达到了这种状态,我们在堆栈上有一个'B'标记。在这种情况下,如果我们接下来看到'Y',我们会缩减为set2 - 或者在任何其他情况下,缩减为set1

set1选为“默认”缩减这一事实可能会对错误处理产生影响。

GLR的附录

Happy(和bisonyacc)默认生成LALR(1)解析器,但您可以在--glr%glr-parser中生成一个GLR解析器{1}}声明文件)。这可以通过同时尝试两种“可能性”来解决歧义;在任何一种情况下,解析器都会分叉并查看它到底有多远。

这可能是不明智的,除非你真的需要它,知道你需要它,并且知道如果出现问题会发生什么。我不确定如果两个叉子成功终止会发生什么;通过我的非科学测试,似乎总是选择更长的解析。

Lexer hacks

如果您不想使用GLR,但又不想显着重构解析器,可以考虑使用词法分析器来解决此问题。

现在,你有这个:

bison

您可以为单个root : set1 'X' | set2 'X' 'X' 字符发出令牌,为两个字符发出不同的令牌:

'X'

这解决了单个令牌中的歧义,并且因此是一个明确的LALR(1)语法。