LR,SLR和LALR解析器有什么区别?

时间:2010-04-20 14:55:53

标签: algorithm parsing compiler-construction grammar

LR,SLR和LALR解析器之间的实际区别是什么?我知道SLR和LALR是LR解析器的类型,但就解析表而言,实际差异是什么?

如何显示语法是LR,SLR还是LALR?对于LL语法,我们只需要显示解析表的任何单元格都不应包含多个生产规则。 LALR,SLR和LR的任何类似规则?

例如,我们如何才能显示语法

S --> Aa | bAc | dc | bda
A --> d

是LALR(1)但不是SLR(1)?


编辑(ybungalobill):我对LALR和LR之间的差异没有得到满意的答复。因此LALR的表格较小,但它只能识别LR语法的一个子集。有人可以详细说明LALR和LR之间的区别吗? LALR(1)和LR(1)足以应答。它们都使用1个令牌前瞻,两个都是表驱动的!它们有何不同?

8 个答案:

答案 0 :(得分:56)

答案 1 :(得分:18)

LALR解析器在LR语法中合并类似的状态,以生成与等效SLR语法完全相同的解析器状态表,这些表通常比纯LR解析表小一个数量级。但是,对于过于复杂而不能成为LALR的LR语法,这些合并状态会导致解析器冲突,或者生成一个不能完全识别原始LR语法的解析器。

BTW,我在我的MLR(k)解析表算法here中提到了一些相关的事情。

<强>附录

简短的回答是LALR解析表较小,但解析器机制是相同的。如果生成所有LR状态,则给定的LALR语法将产生更大的解析表,具有许多冗余(接近相同)状态。

LALR表较小,因为相似(冗余)状态被合并在一起,有效地丢弃了单独状态编码的上下文/先行信息。优点是你可以为相同的语法获得更小的解析表。

缺点是并非所有LR语法都可以编码为LALR表,因为更复杂的语法具有更复杂的前瞻,导致两个或更多状态而不是单个合并状态。

主要区别在于,生成LR表的算法在状态到状态的转换之间传递更多信息,而LALR算法则没有。因此,LALR算法无法判断给定的合并状态是否真的应该保留为两个或多个单独的状态。

答案 2 :(得分:12)

又一个答案(YAA)。

SLR(1),LALR(1)和LR(1)的解析算法完全像Ira Baxter所说的那样,
但是,由于解析器生成算法,解析器表可能不同。

SLR解析器生成器创建LR(0)状态机并根据语法(FIRST和FOLLOW集)计算预测。这是一种简化的方法,可能会报告LR(0)状态机中实际不存在的冲突。

LALR解析器生成器创建LR(0)状态机并计算LR(0)状态机的预测(通过终端转换)。这是一种正确的方法,但偶尔会报告LR(1)状态机中不存在的冲突。

Canonical LR解析器生成器计算LR(1)状态机,并且前​​瞻已经是LR(1)状态机的一部分。这些解析器表可能非常大。

最小LR解析器生成器计算LR(1)状态机,但在该过程中合并兼容状态,然后计算来自最小LR(1)状态机的预测。这些解析器表与LALR解析器表的大小相同或略大,从而提供最佳解决方案。

LRSTAR 10.0 可以在C ++中生成LALR(1),LR(1),CLR(1)或LR(*)解析器,无论语法需要什么。请参阅 this diagram ,其中显示了LR解析器之间的差异。

[完全披露:LRSTAR是我的产品]

答案 3 :(得分:5)

假设没有前瞻的解析器很乐意为你的语法解析字符串。

使用您给出的示例,它会遇到一个字符串dc,它会做什么?它是否将它减少到S,因为dc是由该语法产生的有效字符串?或者也许我们试图解析bdc,因为即使这是一个可接受的字符串?

作为人类,我们知道答案很简单,我们只需要记住我们是否刚刚解析了b。但计算机是愚蠢的:)

由于SLR(1)解析器具有超过LR(0)的额外功率来执行前瞻,我们知道任何数量的前瞻都无法告诉我们在这种情况下该做什么;相反,我们需要回顾过去。因此,规范的LR解析器来拯救。它记得过去的背景。

它记住这种情况的方式是它自我约束,每当它遇到b时,它就会开始走上阅读bdc的道路,作为一种可能性。因此,当它看到d时,它知道它是否已经走了一条路。 因此,CLR(1)解析器可以执行SLR(1)解析器无法做到的事情!

但是现在,因为我们必须定义这么多路径,所以机器的状态变得非常大!

因此,我们合并相同的路径,但正如预期的那样,它可能会引起混乱的问题。但是,我们愿意以降低规模为代价承担风险。

这是你的LALR(1)解析器。

现在如何以算法方式进行。

当您绘制上述语言的配置集时,您将在两种状态下看到shift-reduce冲突。要删除它们,您可能需要考虑一个SLR(1),它会根据后续内容做出决定,但您会发现它仍然无法实现。因此,您可以再次绘制配置集,但这次有一个限制,即每当您计算闭包时,添加的其他产品必须具有严格的跟随。请参阅任何教科书,了解应遵循的内容。

答案 4 :(得分:4)

使用SLR与LR生成的解析器表之间的基本区别在于,reduce操作基于SLR表的Follows设置。这可能过于严格,最终导致转移减少冲突。

另一方面,LR解析器仅基于可以实际跟随非终端减少的终端集合来减少决策。这组终端通常是这种非终端的Follows集的适当子集,因此与转换动作冲突的可能性较小。

LR解析器因此而更强大。但是,LR解析表可能非常大。

LALR解析器以构建LR解析表的思想开始,但以一种导致表大小显着减少的方式组合生成的状态。缺点是,对于某些LR表本可以避免的语法,会引入一小部分冲突。

LALR解析器的功能略低于LR解析器,但仍然比SLR解析器更强大。因此,YACC和其他此类解析器生成器倾向于使用LALR。

P.S。为简洁起见,上面的SLR,LALR和LR确实意味着SLR(1),LALR(1)和LR(1),因此暗示了一个令牌前瞻。

答案 5 :(得分:4)

SLR解析器识别LALR(1)解析器可识别的正确语法子集,后者又识别LR(1)解析器可识别的正确语法子集。

每个都构建为一个状态机,每个状态代表一组语法的生产规则(以及每个状态中的位置),因为它解析输入。

不是SLR的LALR(1)语法的Dragon Book示例是这样的:

S → L = R | R
L → * R | id
R → L

以下是此语法的一种状态:

S → L•= R
R → L•

表示解析器在每个可能的制作中的位置。在它到达终点并尝试减少之前,它并不知道它实际上是哪个制作。

此处,解析器可以移动=或缩小R → L

SLR(又名LR(0))解析器将通过检查下一个输入符号是否在R follow set 中来确定它是否可以减少(即,语法中可以跟随R)的所有终端。由于=也在此集合中,因此SLR解析器遇到shift-reduce冲突。

但是,LALR(1)解析器将使用可以跟随此R的特定生成的所有终端的集合,其仅为$(即,输入的结束)。因此,没有冲突。

正如之前的评论者所指出的,LALR(1)解析器与SLR解析器具有相同数量的状态。先行传播算法用于对来自相应LR(1)状态的SLR状态产生进行预测。生成的LALR(1)解析器可能会引入LR(1)解析器中不存在的reduce-reduce冲突,但它不会引入shift-reduce冲突。

在您的示例中,以下LALR(1)状态会导致SLR实施中的移位减少冲突:

S → b d•a / $
A → d• / c

/之后的符号是LALR(1)解析器中每个产生的跟随集。在SLR中,关注( A 包含a,也可以转移。

答案 6 :(得分:0)

一个简单的答案是所有LR(1)语法都是LALR(1)语法。 与LALR(1)相比,LR(1)在相关的有限状态机中具有更多的状态(超过状态的两倍)。这是LALR(1)语法比LR(1)语法需要更多代码来检测语法错误的主要原因。 关于这两个语法的另一个重要的事情是,在LR(1)语法中,我们可能减少/减少冲突。但是在LALR(1)中,减少/减少冲突的可能性更大。

答案 7 :(得分:0)

除上述答案外,该图还演示了不同解析器之间的关系:

enter image description here