1430 字
7 分钟
编译原理——LR(0)/SLR(1)文法
LR(0)/SLR(1)文法学习笔记
一个项目(Item) 的形式是:A → α . β
- 点
.表示当前分析进度。 α是已经看到/规约的部分。β是期望接下来看到的部分。
LR(0)文法与分析表构建
仅根据项目本身来决定动作,不看向前看符号。
第1步:文法拓广
- 目的:使构造的DFA有唯一的初态和唯一的接受状态。
- 方法:增加一个新的开始符号
S',并添加产生式S' → S(其中S是原文法的开始符号)。
第2步:计算LR(0)项目集的闭包(Closure)
- 规则:如果项目集 I 中包含
A → α . B β,那么对于所有B → .γ,也要加入到 I 中。 - 通俗理解:看到一个非终结符
B,就要把B所有可能的产生式(点在最左边的项目)都考虑进来。
第3步:计算状态转换(Goto)
- 规则:对于项目集 I 和文法符号 X(终结符或非终结符),
Goto(I, X) = Closure( { A → α X . β | A → α . X β 属于 I } )。 - 通俗理解:从状态 I “吃掉”一个符号 X 后,我们能到达哪些新的项目集?计算这个新集合的闭包,就是一个新状态。
第4步:构造LR(0)项目集规范族(即DFA的所有状态)
- 从拓广文法的初始项目集
I₀ = Closure( { [S' → .S] } )开始。 - 不断对每个状态 I 和每个文法符号 X 应用
Goto(I, X)函数,直到不再产生新的状态为止。 - 每个
Goto(I, X)都是一条从状态 I 到新状态的标记为 X 的边。
第5步:填充LR(0)分析表(ACTION表 和 GOTO表) 对于每个状态 Iᵢ:
- 移进动作(Shift):
- 如果项目
A → α . a β在 Iᵢ 中,且a是终结符,同时Goto(Iᵢ, a) = Iⱼ。 - 动作:在 ACTION表的
[i, a]格填sⱼ(表示移进a,并转移到状态j)。
- 如果项目
- 规约动作(Reduce):
- 如果项目
A → α .在 Iᵢ 中(点在最右端,称为规约项目),并且它不是S' → S .(接受项目)。 - 动作:对于所有的终结符
a(包括结束符$),在 ACTION表的[i, a]格填rₖ(k是产生式A → α的编号)。
- 如果项目
- 接受动作(Accept):
- 如果项目
S' → S .在 Iᵢ 中。 - 动作:在 ACTION表的
[i, $]格填acc。
- 如果项目
- GOTO表:
- 如果
Goto(Iᵢ, A) = Iⱼ,且 A 是非终结符。 - 动作:在 GOTO表的
[i, A]格填j。
- 如果
第6步:检查冲突
- 如果一个状态(项目集)中同时出现:
- 移进-规约冲突:既有
A → α . a β,又有B → γ .。 - 规约-规约冲突:既有
A → α .,又有B → β .。
- 移进-规约冲突:既有
- 如果存在任何冲突,则该文法不是LR(0)文法。
SLR(1)文法与分析表构建
SLR(1)是对LR(0)的简单改进。核心思想是:在规约时,使用Follow集来解决LR(0)中不必要的冲突。
SLR(1) 做题步骤
第1步 ~ 第4步:与LR(0)完全相同。
- 拓广文法。
- 构造LR(0)项目集规范族(DFA)。
第5步:计算所有非终结符的Follow集
第6步:填充SLR(1)分析表(与LR(0)的关键区别) 对于每个状态 Iᵢ:
- 移进动作(Shift):与LR(0)完全相同。
- 如果
A → α . a β在 Iᵢ 中,且a是终结符,Goto(Iᵢ, a) = Iⱼ。 - ACTION
[i, a]=sⱼ。
- 如果
- 规约动作(Reduce):【这是关键区别!】
- 如果项目
A → α .在 Iᵢ 中,并且它不是S' → S .。 - 动作:对于所有属于
Follow(A)的终结符a,在 ACTION表的[i, a]格填rₖ。 - 注意:不再是像LR(0)那样对所有终结符都规约!
- 如果项目
- 接受动作(Accept):与LR(0)完全相同。
- 如果
S' → S .在 Iᵢ 中。 - ACTION
[i, $]=acc。
- 如果
- GOTO表:与LR(0)完全相同。
第7步:检查冲突
- 检查分析表的每个格子:
- 如果同一个格子被定义了多个动作(例如,既有
sⱼ又有rₖ,或者有多个rₖ),则存在冲突。 - 如果存在移进-规约或规约-规约冲突,则该文法不是SLR(1)文法。
- 如果同一个格子被定义了多个动作(例如,既有
第三部分:解题思路总结与对比
| 特性 | LR(0) | SLR(1) |
|---|---|---|
| 能力 | 最弱 | 比LR(0)强,比LR(1)弱 |
| 核心区别 | 只看状态,不看符号 | 规约时看Follow集 |
| 规约动作 | 状态中有 A → α .,就对所有输入符号规约 | 状态中有 A → α .,只对 Follow(A) 中的输入符号规约 |
| 冲突解决 | 无法解决任何冲突,有冲突就不是LR(0) | 能用Follow集解决部分LR(0)的移进-规约和规约-规约冲突 |
| 构造步骤 | 1. 拓广 2. 构造DFA 3. 填表(规约动作填满一行) | 1. 拓广 2. 构造DFA 3. 求Follow集 4. 填表(规约动作只填Follow集列) |
通用做题流程
- 判断题目要求:是判断文法类型,还是构造分析表?
- 无脑开始构造:
- 拓广文法。
- 从
I₀开始,逐步计算闭包和Goto,画出完整的LR(0)项目集规范族(DFA图)。这一步是基础,LR(0)和SLR(1)共用。
- 如果是LR(0):
- 尝试填LR(0)表。
- 检查:在构造DFA的过程中,如果发现任何一个状态同时包含移进项目和规约项目,或者包含多个规约项目,直接判定不是LR(0)文法。
- 如果是SLR(1):
- 计算所有非终结符的 Follow集。
- 基于第2步的DFA和Follow集来填SLR(1)表。
- 检查:填表时,如果发现同一个ACTION格子需要填入两种不同的动作(如
s和r,或两个r),则判定不是SLR(1)文法。如果没有冲突,就是SLR(1)文法。
依旧网课
编译原理——LR(0)/SLR(1)文法
https://blog.tonks.top/posts/bianyiyuanli1/ 部分信息可能已经过时