LR、SLR与LALR的详细分析
2. SLR 解析
形式表达
- 定义: SLR(1)(Simple LR)是 LR(1) 的简化版本,仅使用一个前瞻符号,且在构造规约动作时,使用文法的FOLLOW 集来决定是否规约,而非 LR(1) 的完整前瞻集。
- 核心机制:
- SLR 分析表: 与 LR 类似,但规约动作的判定更简单。
- 对于项目
[A → α·, a]
,如果 a ∈ FOLLOW(A),则在状态中添加规约动作。
- 构造方法:
- 构建 LR(0) 项目集规范族(忽略前瞻符号)。
- 在规约时,检查当前状态的终结符是否在产生式左部非终结符的 FOLLOW 集中。
- 数学表达:
- 给定文法 G,SLR 使用 LR(0) 项目集,但规约条件为:若项目
[A → α·]
在状态 s,且当前输入符号 a ∈ FOLLOW(A),则执行规约 A → α。
- 复杂度: 分析表构造比 LR 简单,状态数与 LR(0) 相同,解析时间仍为 O(n)。
形象描述
- 类比: SLR 像一个稍微偷懒的乐高机器人。它还是按照蓝图拼装,但它不看完整的零件上下文(只用 LR(0) 项目集),只在需要决定是否把零件组合成模块时,简单地参考一个“常用零件列表”(FOLLOW 集)。这让它更快、更节省内存,但有时候会因为信息不足而拼错(shift/reduce 冲突)。
特点
- 优点: 分析表更小,构造更简单,适合简单文法。
- 缺点: 由于只用 FOLLOW 集,可能导致冲突(shift/reduce 或 reduce/reduce),无法处理某些复杂文法。
- 适用场景: 文法简单且 FOLLOW 集足以区分动作的场景。
3. LALR 解析
形式表达
- 定义: LALR(1)(Lookahead LR)是 LR(1) 的折衷版本,通过合并 LR(1) 项目集中的状态来减少空间占用,同时保留部分前瞻符号信息。
- 核心机制:
- LALR 分析表: 从 LR(1) 项目集开始,将具有相同核心项目(忽略前瞻符号)的状态合并,保留合并后的前瞻符号。
- 构造方法:
- 构建 LR(1) 项目集规范族。
- 合并核心相同的项目集,保留所有前瞻符号的并集。
- 生成分析表,动作基于合并后的状态和前瞻符号。
- 数学表达:
- 对于 LR(1) 项目
[A → α·β, a]
和 [A → α·β, b]
,若核心相同(A → α·β),合并为 [A → α·β, {a, b}]
。
- 规约动作基于合并后的前瞻符号集。
- 复杂度: 分析表大小接近 LR(0),构造时间比 LR(1) 低,解析时间仍为 O(n)。
形象描述
- 类比: LALR 像一个聪明但节省资源的乐高机器人。它有 LR(1) 机器人的蓝图,但为了节省空间,它把相似的拼装步骤(状态)合并在一起,只保留关键的零件提示(前瞻符号)。这让它比 SLR 更聪明,能处理更复杂的文法,但偶尔因为合并过度也会出错(reduce/reduce 冲突)。
特点
- 优点: 分析表大小接近 SLR,能处理比 SLR 更复杂的文法,兼顾效率和能力。
- 缺点: 状态合并可能引入 reduce/reduce 冲突,导致无法解析某些 LR(1) 可接受的文法。
- 适用场景: 现代编译器(如 Yacc、Bison)广泛使用 LALR,因为它平衡了性能和表达能力。
三者对比
特性 | LR(1) | SLR(1) | LALR(1) |
---|
项目集 | LR(1) 项目,包含前瞻符号 | LR(0) 项目,结合 FOLLOW 集 | LR(1) 项目,合并核心相同状态 |
分析表大小 | 最大(状态数多) | 最小(状态数少) | 中等(状态数接近 LR(0)) |
文法覆盖 | 几乎所有 CFG | 简单文法,易产生冲突 | 大部分 CFG,较少冲突 |
冲突处理 | 无冲突(最精确) | 可能有 shift/reduce 冲突 | 可能有 reduce/reduce 冲突 |
计算复杂度 | 构造复杂,空间占用高 | 构造简单,空间占用低 | 构造中等,空间占用低 |
形象比喻 | 超级聪明的机器人,精确但耗资源 | 偷懒的机器人,简单但易出错 | 聪明且节省的机器人,平衡性能 |
形象总结
- LR: 像一个全能的“超级工程师”,拿着详细的蓝图,能完美拼装任何复杂的乐高模型,但需要大量时间和空间来准备。
- SLR: 像一个“实习生”,只看简化的蓝图和常用零件列表,拼装简单模型很快,但复杂模型容易出错。
- LALR: 像一个“老练的技工”,在超级工程师的蓝图上做优化,合并相似步骤,效率高且能拼装大部分模型,但偶尔会因为简化过度而出错。
补充说明
- 实际应用:
- LR(1): 很少直接使用,因为分析表太大,构造复杂。
- SLR(1): 适合教学或小型文法,但实际编译器中较少使用。
- LALR(1): 是 Yacc、Bison 等工具的核心,广泛用于编程语言解析,因为它在能力和效率间取得了平衡。
- 冲突问题:
- SLR 的冲突源于 FOLLOW 集的粗糙判定。
- LALR 的冲突源于状态合并导致的前瞻符号混淆。
- LR(1) 几乎无冲突,但实现成本高。