有没有办法让这个语法LALR(1)?

时间:2013-05-01 16:35:19

标签: parsing grammar yacc lalr lr

我有一个像这样的规则的语法

A -> pq
B -> pr

Block -> { Astar Bstar }

Astar -> Astar A
       | epsilon

Bstar -> Bstar B
       | epsilon

有没有办法把这个语法变成LALR(1)?根据我的结论,如果解析器在块内看到p,则会发生转换/引出冲突。

2 个答案:

答案 0 :(得分:3)

您的语言是常规语言,相当于正则表达式\{(pq)*(pr)*\}。问题是,任何简单的语法转换都需要两个字符的前瞻,才能看到q之后是r还是p

现在您已将此标记为yaccparsing,因此不清楚您是否正在寻找“如何使用yacc处理此问题”或“理论答案”的实际答案“是否存在LALR(1)这种语言的语法。“

对于实际答案,解决方案就是你试图 - 将AB的识别移动到词法分析器中,在那里识别字符序列pq和{{1 }}作为标记prA。由于lex / flex使用DFA并回溯到最长匹配的令牌,因此这里的任意前瞻都没有问题。

对于理论答案,您需要转换语法以消除前瞻性的需要。有问题的构造是B(大括号无关紧要),相当于(pq)*(pr)*,它表示如下语法:

p(qp)*(rp)*r | p(qp)*q | epsilon

另一种方法是重构Block -> { p Astar Bstar r } | { p Astar q } | { } A -> q p B -> r p Astar -> Astar A | epsilon Bstar -> Bstar B | epsilon 规则,使它们与空字符串不匹配:

star

为同一种语言提供两种非常不同的LALR(1)语法。

答案 1 :(得分:2)

让我们首先看看你为什么会遇到LALR(1)冲突,然后看看我们是否可以重写语法使其成为LALR(1)。

要了解为什么语法不是LALR(1),让我们从计算语法的LR(1)配置集开始:

(1)
S'     -> .Block [$]
Block  -> .{ Astar Bstar } [$]

(2)
S'     -> Block. [$]

(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]

(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A     -> .pq [}p]
Bstar -> .epsilon [ }p ]
Bstar -> . Bstar B [ }p ]

此时,我们可以停止,因为我们在符号p上的状态(4)中有一个移位/缩小的混淆:你是否移动A -> .pq [ {p ]的p,还是减少BStar -> .epsilon [ }p ]?由于LR(1)语法中存在移位/减少冲突,因此语法根本不是LR(1),这意味着它不可能是LALR(1)(因为每个LALR(1)语法也是LR (1)语法)。

从根本上说,问题是当解析器看到p时,它无法判断它是否正在查看A的开头(意味着它需要移动它)或者是否存在不再是A了,它正在查看B的开头(意味着它需要减少Bstar -> epsilon)。

要解决这个问题,让我们看看如果我们进行一些小调整会发生什么。我们遇到的问题是解析器需要在看到p是否要移位或减少时立即确定。如果我们通过查看p然后跟进后续角色给予时间推迟决定,该怎么办?要做到这一点,让我们通过重写

稍微改变你的语法
Bstar -> epsilon
Bstar -> Bstar B

作为

Bstar -> epsilon
Bstar -> B Bstar

现在,解析器在决定做什么之前会查看更多令牌。如果它正在查看pq,它知道它没有查看与B相关的任何内容。如果它看到pr,它就知道它在看B,因此可以开始进行第二种类型的制作。如果我们这样做,让我们看看LR(1)州会发生什么:

(1)
S'     -> .Block [$]
Block  -> .{ Astar Bstar } [$]

(2)
S'     -> Block. [$]

(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]

(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A     -> .pq [}p]
Bstar -> .epsilon [ } ]
Bstar -> . B Bstar [ } ]
B     -> .pr [}]

(5)
Block -> { Astar Bstar . } [$]

(6)
Block -> { Astar Bstar } . [$]

(7)
A     -> p.q [}p]
B     -> p.r [}]

(8)
A     -> .pq [}p]

(9)
B     -> pr. [}]

(10)
Bstar -> B . Bstar [ } ]
Bstar -> . B Bstar [ } ]
B     -> .pr [}]

(11)
B     -> p.r [}]

请注意,我们原来的shift / reduce冲突已经消失,这个新语法根本不再有任何转移/减少冲突。此外,由于没有任何具有相同核心的状态对,上述状态集也是我们在LALR(1)表中将具有的状态集。因此,上面的语法确实是LALR(1),我们根本没有改变语法的含义。

希望这有帮助!