自底向上——LR 分析
含AI生成内容
第六章 自底向上——LR 分析 · 知识点详解
🔴 核心(必背/必算) 🟡 理解(会说清) 🟢 了解(有印象) 本章性质:计算大题(约 20 分),项目集 DFA + 分析表 + 分析过程。
一、LR 分析法概述(🟡 理解)
LR(k) 的含义
- L:从左到右扫描输入符号。
- R:最右推导的逆过程(即规范归约/最左归约)。
- k:向前看 k 个符号,用以确定归约所用规则。(k=0 → LR(0),k=1 → LR(1))
相比算符优先的优点
- 算符优先只看终结符优先关系,适应面窄;发现最左素短语尾后还要回头找头。
- LR 用状态记住分析历史,直接判断句柄是否形成,适应面宽。
LR 分析器组成
分析表(ACTION[s,a] 与 GOTO[s,X])+ 状态栈 + 符号栈 + 总控程序。
sn:移进,把符号和状态 n 压栈。rn:用第 n 个产生式归约。acc:接受;空格:error。
二、活前缀与可归前缀(🟡 理解)
- 拓广文法:加
S′→S,使有唯一接受态(S′ 只在左部出现)。 - 可归前缀:若
S′⇒*ᵣ αAγ ⇒ αβγ,则 αβ 为可归前缀(每步归约前句型的前部分串)。 - 活前缀:可归前缀的前缀。
- 活前缀出现在分析栈中,不会包含句柄之后的任何符号。
- 一旦栈中出现完整的 αβ(句柄形成),就用
A→β归约。
求可归前缀的一般算法:定义
LC(A)={α | S′⇒*αAβ}(记作 [A]),列正规式方程组求解;LR(0)C(A→β)=LC(A)·{β}即含句柄的可归前缀。(了解即可,实际用项目集法)
三、LR(0) 项目与项目集(🔴 核心)
LR(0) 项目
在产生式右部某位置加圆点 ·。圆点左=已识别部分,右=待识别部分。
- 例
S→aAcBe有 6 个项目:S→·aAcBe、S→a·AcBe、…、S→aAcBe·。 - 特别地
A→ε只有一个项目A→·。
项目四分类
| 类型 | 形式 | 含义 |
|---|---|---|
| 移进项目 | A→α·aβ(圆点后是终结符) | 准备移进 a |
| 待约项目 | A→α·Bβ(圆点后是非终结符) | 等待 B 归约 |
| 归约项目 | A→αβ·(圆点在最右) | 可用 A→αβ 归约 |
| 接受项目 | S′→α·(拓广开始符的归约项目) | 接受 |
CLOSURE(闭包)
求项目集 I 的闭包:
- I 中项目都在 CLOSURE(I) 中。
- 若
A→α·Bβ∈CLOSURE(I),则所有B→·γ也加入。 - 重复直到不再增加新项目。
GO 转移函数
GO(I,X) = CLOSURE({ A→αX·β | A→α·Xβ∈I })
—— 把 I 中圆点在 X 前的项目,圆点越过 X,再求闭包。
项目集规范族(构造识别活前缀的 DFA)
C = { CLOSURE({S′→·S}) };while (C 有新项目集) { for (每个 I∈C,每个 X∈V_N∪V_T) if (GO(I,X)≠∅ 且 GO(I,X)∉C) C = C∪{GO(I,X)};}每个项目集是 DFA 的一个状态。
四、LR(0) 分析表构造与冲突(🔴 核心)
构造算法
对项目集 Iᵢ(状态 i):
A→α·aβ∈Iᵢ且GO(Iᵢ,a)=Iⱼ⟹ACTION[i,a]=sjA→α·Bβ∈Iᵢ且GO(Iᵢ,B)=Iⱼ⟹GOTO[i,B]=jA→α·∈Iᵢ(归约项目)⟹ 对所有 a∈V_T∪{#},ACTION[i,a]=rjS′→S·∈Iᵢ⟹ACTION[i,#]=acc- 空格置 error。
冲突与 LR(0) 文法
- 移进-归约冲突:I 中既有移进项目又有归约项目。
- 归约-归约冲突:I 中含≥2 个归约项目。
- I 无冲突称相容。若所有 I 都相容,则 G 是 LR(0) 文法。
LR(0) 分析算法
状态栈顶为 s,输入符 a:
ACTION[s,a]=si:a 入符号栈、i 入状态栈,输入指针后移。ACTION[s,a]=rj(用 A→β):弹出 |β| 个符号/状态,A 入符号栈,GOTO[栈顶状态,A]入状态栈,输出规则。ACTION[s,a]=acc:接受返回。- 否则 error。
五、SLR(1) 分析(🔴 核心)
动机
LR(0) 太弱:一有移进-归约或归约-归约冲突就不能用。
解决方法
对冲突项目集 I={X→α·bβ, A→γ·, B→δ·},面临输入 a:
- 若 a=b → 移进;
- 若 a∈FOLLOW(A) → 用 A→γ· 归约;若 a∈FOLLOW(B) → 用 B→δ· 归约;
- 否则报错。
要求相关 FOLLOW 集互不相交且与移进符不冲突。
构造 SLR(1) 表(与 LR(0) 唯一区别)
归约项目 A→α· 只对 a∈FOLLOW(A) 填 ACTION[i,a]=rj(LR(0) 是对所有终结符填)。
本质:面临的输入符属于欲归约非终结符的 FOLLOW 集时才归约。 SLR(1) 分析表无冲突 → G 是 SLR(1) 文法,能力强于 LR(0)。
六、LR(1) 分析(🔴 核心)
动机
SLR 用整个 FOLLOW 集,信息仍不够精确。LR(1) 在构造状态时就带上后继符(搜索符)。
LR(1) 项目
形如 [A→α·β, a],a 是向前搜索符(FOLLOW 集的子集)。
闭包 CLOSURE(I)(关键区别)
若 [A→α·Bβ, a]∈I,B→η 是产生式,则对每个 b∈FIRST(βa),把 [B→·η, b] 加入。
- 当
β⇒ε时 b=a,称 继承的后继符;否则称 自生的后继符。
GO 转移
GO(I,X)=CLOSURE({ [A→αX·β,a] | [A→α·Xβ,a]∈I })
构造 LR(1) 表
[A→α·aβ,b]∈Iᵢ且 GO(Iᵢ,a)=Iⱼ ⟹ACTION[i,a]=sj[A→α·,a]∈Iᵢ⟹ACTION[i,a]=rj(仅对搜索符 a 归约)- GO(Iᵢ,A)=Iⱼ ⟹
GOTO[i,A]=j [S′→S·,#]∈Iᵢ⟹ACTION[i,#]=acc
LR(1) 表无冲突 → G 是 LR(1) 文法,能力最强、表最大。
七、LALR(1) 分析(🔴 核心)
动机
LR(1) 状态数太多。合并同心集以减少状态。
同心集
- 心:项目的 LR(0) 部分(去掉搜索符)。
- 心相同的 LR(1) 项目集合并为一个,搜索符集取并集。
- 合并后转移函数自动合并,仍是同心集。
- 合并只可能引入归约-归约冲突,不会引入移进-归约冲突;合并会推迟发现错误,但出错位置依然准确。
构造步骤
- 构造 LR(1) 项目集规范族
C={I₀,…,Iₙ}。 - 合并所有同心集,得
C′={J₀,…}。 - 由 C′ 构造 ACTION/GOTO(方法同 LR(1))。
无冲突且合并后无归约-归约冲突 → G 是 LALR(1) 文法。状态数与 LR(0) 相同,是实用主流。
八、四种方法对比(🔴 必背)
| 方法 | 归约时机 | 特点 |
|---|---|---|
| LR(0) | 对所有终结符归约 | 最弱 |
| SLR(1) | 仅对 a∈FOLLOW(A) 归约 | 用 FOLLOW 解冲突 |
| LR(1) | 项目带搜索符,仅对 a=搜索符归约 | 最强、表最大 |
| LALR(1) | 合并同心集(心同、搜索符取并) | 实用主流,状态数同 LR(0) |
能力关系(必背):LR(0) ⊂ SLR(1) ⊂ LALR(1) ⊂ LR(1)。
LALR(1) 与 LR(1) 能力接近,但状态数远少于 LR(1)。
判定文法属于哪类的流程
- 构造 LR(0) 项目集,看冲突:无冲突 → LR(0)。
- 有冲突,用 FOLLOW 能否解决:能 → SLR(1)。
- SLR 仍冲突,构造 LR(1):无冲突 → LR(1)。
- 合并同心集后无归约-归约冲突 → LALR(1)。
🧮 经典例(赋值语句文法,区分 SLR 与 LR(1))
S′→S, S→L=R|R, L→*R|i, R→L:
- I₂={S→L·=R, R→L·}:面临
=时,因=∈FOLLOW(R),SLR 出现移进-归约冲突 → 不是 SLR(1)。 - LR(1) 中 I₂ 里
R→L·的搜索符只有#,面临=只移进不归约,冲突消除 → 是 LR(1) 文法,也是 LALR(1)。
🧮 LR 分析过程示例(SLR,输入 i+i*i#,片段)
| 步 | 状态栈 | 符号栈 | 输入 | ACTION | GOTO |
|---|---|---|---|---|---|
| 0 | 0 | # | i+i*i# | s5 | |
| 1 | 05 | #i | +i*i# | r6 | 3 |
| 2 | 03 | #F | +i*i# | r4 | 2 |
| 3 | 02 | #T | +i*i# | r2 | 1 |
| 4 | 01 | #E | +i*i# | s6 | |
| … | … | … | … | … | … |
| 末 | 01 | #E | # | acc |
⚠️ 易错点汇总
- LR(0) 归约项目对所有终结符填 rj;SLR 只在 FOLLOW 集对应列填。
- LR(1) 构造闭包别漏”继承的后继符”(β⇒ε 时 b=a)。
- LALR 合并同心集只可能引入归约-归约冲突,不会引入移进-归约冲突。
- 拓广文法
S′→S的目的:获得唯一接受态。 - 归约时弹出符号个数 = 右部长度 |β|,然后查 GOTO 压入新状态。
📝 常考题型
- 画识别所有活前缀的 LR(0)/LR(1) DFA(项目集规范族)。
- 构造 LR 分析表(ACTION/GOTO)。
- 写输入串(如
i+i*i#)的分析过程。 - 判断给定文法是 LR(0)/SLR(1)/LALR(1)/LR(1) 中的哪一类。
- 简答:LR 分析器与优先关系分析器识别句柄的异同。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!










