1. 简介

在本篇文章中,我们将深入探讨两种在工业界和学术界广泛应用的语法解析器:LL 解析器 和 LR 解析器。我们将通过语法理论对比它们的特性,帮助你理解它们的本质差异。

LL 和 LR 是两种主流的语法分析方法,适用于不同的语法规则和解析场景。理解它们之间的区别,有助于在设计语言、构建编译器或解析器时做出更合适的技术选型。

2. 编译流程概述

LL 和 LR 解析器都属于编译过程中的语法分析阶段。一个编译器通常会通过多个抽象层次处理源代码,每个阶段都有不同的输入和输出。最终输出是为特定架构优化的机器码。

经典的编译流程如下图所示:

编译流程图

其中,词法分析器输出的是 token 流,而语法分析器(即解析器)则负责生成语法树。解析器实现方式、语法树构造方式以及所支持的语法规则种类,决定了 LL 和 LR 的根本差异。

3. LL 与 LR 的对比

3.1. 语法树构建方式

LL 解析器采用自顶向下(Top-down)方式构建语法树

  • 从根节点开始,按前序方式逐层展开。
  • 目标是找到输入字符串的最左推导(leftmost derivation)。
  • 例如,对于产生式规则 {S → SS, S → a},输入字符串为 aaaa,一个可能的推导路径是:
    S ⇒ SS ⇒ SSS ⇒ aSS ⇒ aaS ⇒ aaaa
    

LL 解析器在每一步中选择一个非终结符,并尝试匹配其产生式。如果解析器只需向前看 k 个符号即可确定正确的产生式,则称为 LL(k) 解析器,常见值为 LL(1)

LL 的缺点:无法处理左递归语法规则,否则可能导致无限循环。


LR 解析器采用自底向上(Bottom-up)方式构建语法树

  • 从叶子节点开始,逐步向上归约(reduce)直到根节点。

  • 目标是将输入字符串归约为起始符号,或构造一个最右推导的逆过程

  • 例如,对于规则 {E → T, T → F, T → T * F, F → id},输入字符串为 id * id,推导过程如下:

    id * id ⇒ F * id ⇒ T * id ⇒ T * F ⇒ E
    

LR 解析器通过“移进-归约”操作完成解析,归约时识别的子串称为“句柄”(handle)。

LR 的优点:支持左递归语法规则,且能处理的语法规则集比 LL 更广。


3.2. 支持的语法规则类型

LL 和 LR 解析器对语法规则有各自的要求:

LL(1) 语法规则

LL(1) 语法必须满足以下条件:

  • 对于任意非终结符 A 的两个不同产生式 A → α | β
    • FIRST(α) ∩ FIRST(β) = ∅(无冲突)
    • 至多一个产生式可以推导出空串
    • 若 β 推导出空串,则 FIRST(α) ∩ FOLLOW(A) = ∅

这些条件确保在解析过程中不会出现歧义。


LR(1) 语法规则

LR 语法的种类较多,常见包括:

  • **SLR(1)**:最基础的 LR 解析器,能力较弱
  • **LR(1)**:能力最强的 LR 解析器
  • **LALR(1)**:在 LR(1) 基础上合并状态,平衡性能与能力

LR 解析器使用“状态自动机”和“项目集”来识别句柄。其核心要求是:

  • 不存在“移进-归约”冲突(shift-reduce conflict)
  • 不存在“归约-归约”冲突(reduce-reduce conflict)

LR 的优势:能接受的语法规则集比 LL 更大,LL 是 LR 的真子集。


3.3. 各自的局限性

LL 解析器的限制

  • 无法处理左递归语法规则
  • 例如规则 S → SS 会导致无限递归
  • 递归下降解析器虽然能处理部分非 LL 规则,但效率低,最坏情况下可能指数级时间复杂度

LR 解析器的限制

  • 无法处理歧义语法规则
  • 但有一种特殊类型的 LR 解析器 —— 算符优先解析器(Operator-Precedence Parser) 可以处理某些算符优先文法(Operator Grammar)
    • 通过定义操作符优先级解决歧义问题

4. 总结

特性 LL 解析器 LR 解析器
构建方式 自顶向下 自底向上
语法树构造 最左推导 最右推导的逆过程
支持左递归
支持的语法规则 较少(LL 是 LR 的子集) 更多
处理歧义 ✅(仅限特定算符优先文法)
实现难度 相对简单 相对复杂
常见类型 LL(1) SLR(1), LALR(1), LR(1)

LL 适合语法结构简单、可预测性强的语言解析,例如某些配置文件格式、DSL。

LR 更适合复杂语言(如 C、Java)的语法解析,尤其在工业级编译器中广泛使用。

⚠️ 选择建议

  • 如果你正在设计一种新语言,且希望手动编写解析器,LL(1) 是一个不错起点。
  • 如果你追求更强的表达能力,或希望支持左递归、复杂结构,建议使用 LR 系列解析器(如 LALR(1) 或 LR(1))。

📌 术语说明

  • FIRST 集合:表示某个符号串可以推导出的第一个终结符集合
  • FOLLOW 集合:表示某个非终结符后可能紧跟的终结符集合
  • 句柄(Handle):在 LR 解析中,当前可归约的子串
  • 算符优先文法(Operator Grammar):一种特殊文法,用于支持算符优先解析器处理部分歧义情况

如果你正在开发一个编译器、解释器或 DSL,LL 和 LR 是你必须掌握的基础知识。选择合适的解析器类型,将直接影响你实现语言的灵活性和效率。


原始标题:LL vs. LR Parsing