【问题标题】:How do merged lookahead sets help LALR parse more grammars than SLR?合并的前瞻集如何帮助 LALR 解析比 SLR 更多的语法?
【发布时间】:2025-12-20 05:10:07
【问题描述】:

到目前为止,从我在 Google 上搜索的内容来看,LALR(1) 和 SLR(1) 之间的唯一区别是 LALR(1) 使用具有合并前瞻集的状态。一位消息人士说,LALR(1) 不会遇到移位减少冲突,因为解析器会“记住”它从哪个状态到达当前状态。但是,解析算法没有区别,所以我很难看到这些合并的前瞻集如何帮助 LALR 解析器解决移位减少冲突。如果解析器记得它是从哪个状态到达当前状态的,为什么它仍然容易受到reduce-reduce冲突的影响?

【问题讨论】:

    标签: parsing


    【解决方案1】:

    它实际上是 SLR,它使用合并的前瞻集。或者,更准确地说,SLR 解析表中每个 产生式 的前瞻集近似为产生式中最后一个符号的 FOLLOW 集,有效地忽略了上下文。

    LALR 解析器已合并 states 相对于规范 LR 解析器。在 LR 和 LALR 中,与 SLR 相比,前瞻集的计算准确,但在 LALR 解析器的情况下,状态被合并,因此它具有与 SLR 解析器相同的状态(但不是相同的前瞻集) .

    Wikipedia 文章中对 SLR 解析、LALR 解析和 Canonical LR 解析的区别进行了合理的讨论。有关详细信息,请参阅这些文章的参考资料。

    无论您使用哪种解析器生成技术,如果语法需要更多前瞻,就会出现冲突。例如,以下语法需要两个 lokahead 符号来决定是移位还是减少 NAME

    prog → λ
    prog → prog defn
    defn → NAME :
    defn → defn NAME
    

    这里,如果名称后面没有冒号,则它是defn 的一部分,因此要决定是否移动名称,您不仅需要作为前瞻标记的名称,还需要 tge跟随令牌,第二个前瞻。


    这是一个非常简单的语法,它不是 SLR(1),它可能会说明使用跟随集的问题。 (它直接来自 Dragon 的书,经常被用作 SLR(1) 语法不足的例子):

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

    这里,R 和 L 的 FOLLOW 集是相同的。 R 和 L 都包含 =,因为 * R = ... 是有效的,并且会减少到 L = ...,并且显然 R 和 L 都可以出现在 S 的末尾,因此两个 FOLLOW 集都包括输入结束标记。

    接下来的问题是决定是否减少R → L。在L = R 中,我们应该将L 保留原样并移动=。但是 FOLLOW 集没有帮助,因为 R 可以跟随 =。所以在一个只使用 FOLLOW 集合的 SLR(1) 语法中,状态中存在移位/归约冲突:

    S → L • = R
    R → L •
    

    在 LALR(1) 语法中不存在此冲突,因为在该状态下 产生式R → L 的前瞻集不包括=,因为产生式是从项目S → • R 中包含的,其前瞻是输入结束标记。

    【讨论】:

    • SLR 有效地将后续集用于生产中的最后一个符号。 LALR 使用基本上结合了前瞻集的合并状态。这是否意味着 SLR 和 LALR 是相同的,唯一的区别是 LALR 明确了它的前瞻集?*只是说 LALR 使用更多的规则来解决冲突,但没有具体说明任何具体的规则差异。
    • @np:不,不是那个意思。在给定状态下,并非 FOLLOW 集合的每个元素都是可行的前瞻符号。这很重要,因为只有当前瞻符号位于要缩减的规则的前瞻集中时,才可能进行缩减。
    • 好的,我想我明白你的意思了。假设对于某个假设语法的产生,动作是移动两个前瞻标记并减少其余标记。 LALR 将能够处理此语法,因为它可以在 action/goto 表的同一行上执行 shift 和 reduce 操作,而 SLR 无法处理它,因为它不能在同一行上同时执行这两个操作。我这样说对吗?
    • @np:不,也不是这样。 LR(0) 不能使用前瞻。 SLR(1) 可以使用前瞻,但它不能准确地计算前瞻集。因此,可以(并且确实)将可移动标记错误地添加到前瞻集中以进行减少。
    • @npCompleteNoob:我添加了非 SLR(1) 语法的标准示例,以及一些解释。应该很容易找到更长的解释;特别是在《龙书》中。