# 编译原理——例题

24 min read
Table of Contents

1. 编译程序的结构

编译程序通常划分哪几个阶段,每个阶段的主要功能是什么

词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成。

词法分析:对源程序字符串扫描分解,识别出单词符号。(拆分字符串)

语法分析:根据语言的语法规则对符号序列进行语法分析,识别出语法短语,判断语法是否正确。(识别短语,判断正确)

语义分析:对语法分析的结果分析语义错误,收集类型信息。(语义,类型)

中间代码生成:把源程序变成结构简单、含义明确、易生成目标代码的形式。

代码优化:对中间代码或目标代码进行变换改造等优化处理,提高效率。

目标代码生成:将语义分析结果或中间代码变成目标代码。(一般是汇编等)

把汇编语言程序翻译成机器可执行的目标程序的工作是由 ____ 完成的

汇编器

编译方式与解释方式的根本区别在于

编译方式先对源代码进行全面分析和优化后,生成一份高效的机器无关中间代码,再由后端转化为具体机器指令(目标代码),运行时无需源代码和解释器,直接执行机器码。

解释方式则在每个阶段(词法、语法、语义)都实时边读边处理、即时执行,灵活但效率较低。

编译程序的基本任务是翻译、出错处理和 ____

代码优化

2. 文法和语言

LR分析核心思想与“L”、“R”含义:

核心思想为“自底向上”归约,通过状态栈识别活前缀,依据ACTION表和GOTO表执行移进、归约或接受。

“L”指从左到右扫描输入串;“R”代表构造最右推导的逆过程,即规范归约,确保分析过程的准确性与高效性。

在规范归约中,用 ____ 来刻画可归约串。

句柄

自顶向下的语法分析方法遇到的主要问题是递归和 ____

回溯

自底向上是:移进-归约冲突与归约-归约冲突

Chomsky中3型文法又称 ____

正规文法 / 右线性文法。

什么是文法的二义性?文法二义性不可判别是什么原因?

文法中两个产生式存在相同的右部。(同一字符串可通过不同【推导/归约】【生成不同语法树/产生不同语义】)

归约为停机问题 -> 构造一个文法,如果它二义,就对应某个机器停机,否则对应无限循环。既然停机不可判,别的东西也不可能判。不可能100%正确且终止。

3. 词法分析

请构造与正规式R=(b|a)*a(b|a)*等价且状态数最少的DFA,要求写出构造过程。

详细解题

(1) 正规式

r=(ab)a(ab)r = (a|b)^*\,a\,(a|b)^*

所求正规集是“至少含一个 aa 的所有 a,ba,b 串”。正规式要求串中出现 aa 且前后可为任意 a,ba,b 串。

(2) 构造 NFA

Q={0,1,2}Q=\{0,1,2\}q0=0q_0=0F={2}F=\{2\}δ\delta 如下:

  • δ(0,a)={0,1}\delta(0,a)=\{0,1\}δ(0,b)={0}\delta(0,b)=\{0\}
  • δ(1,a)={2}\delta(1,a)=\{2\}δ(1,b)={2}\delta(1,b)=\{2\}
  • δ(2,a)={2}\delta(2,a)=\{2\}δ(2,b)={2}\delta(2,b)=\{2\}

起点 0 循环匹配 (ab)(a|b)^*;读入 aa 转到 1;1 再读一个字符到 2,2 循环匹配右侧 (ab)(a|b)^*

(3) NFA 确定化为 DFA(子集构造法)

初始 A=ε-closure({0})={0}A=\varepsilon\text{-closure}(\{0\})=\{0\},逐个未标记状态对 aabb 求 move 后的 ε\varepsilon-闭包:

状态集合DFA 状态aa 到达bb 到达含终态 2
{0}\{0\}AA{0,1}B\{0,1\}\to B{0}A\{0\}\to A
{0,1}\{0,1\}BB{0,1,2}C\{0,1,2\}\to C{0,2}D\{0,2\}\to D
{0,1,2}\{0,1,2\}CC{0,1,2}C\{0,1,2\}\to C{0,2}D\{0,2\}\to D
{0,2}\{0,2\}DD{0,1,2}C\{0,1,2\}\to C{0,2}D\{0,2\}\to D

DFA 转移图:

DFA 转移表:

DFA 状态aabb
A\to ABBAA
BBCCDD
C*CCCDD
D*DCCDD

终态集 FDFA={C,D}F_{DFA}=\{C,D\}

(4) DFA 最小化(Hopcroft 划分法)

初始划分 Π0={{A,B},{C,D}}\Pi_0 = \{\{A,B\},\{C,D\}\}(非终态、终态)。

考察 {A,B}\{A,B\}:经 aaAB{A,B}A\to B\in\{A,B\}BC{C,D}B\to C\in\{C,D\} → 不等价,分裂为 {A}\{A\}{B}\{B\}

考察 {C,D}\{C,D\}:经 aaCC{C,D}C\to C\in\{C,D\}DC{C,D}D\to C\in\{C,D\};经 bbCD{C,D}C\to D\in\{C,D\}DD{C,D}D\to D\in\{C,D\} → 等价,不可分裂。

最终划分 Π={{A},{B},{C,D}}\Pi=\{\{A\},\{B\},\{C,D\}\}。将 C,DC,D 合并为状态 CC

最简 DFA(3 个状态)

DFA 状态aabb
A\to ABBAA
BBCCCC
C*CCCCC

状态 A 表示”尚未读到 aa“,B 表示”刚读到第一个 aa“,C 表示”已完成至少一个 aa“。该 DFA 等价于正规式 (ab)a(ab)(a|b)^*a(a|b)^*,状态数 3 为最少。

例:设 Σ={a,b}\Sigma=\{a,b\} 上的正规集 S 由倒数第二个字符为 aa 的所有字符串组成。

(1) 写出该正规集对应的正规式; (2) 根据正规式构造 NFA; (3) 将 NFA 确定化为 DFA; (4) 将 DFA 最小化,得到最简 DFA。

  1. 写出对应正规式

思路:字符串长度至少为2,倒数第二个字符固定为a,其余位置字符任意。正规式为:(ab)a(ab)(a|b)^*a(a|b)

  1. 构造非确定有限自动机 (NFA)

思路:按正规式结构拆解,(ab)(a|b)^*用带自环的状态表示,经匹配a的转移后,再经匹配任意字符的转移到达终止状态,构建初始状态图。

详细解题

(1) 正规式

r=(ab)a(ab)r = (a|b)^*\,a\,(a|b)

(2) 构造 NFA

Q={0,1,2}Q=\{0,1,2\}q0=0q_0=0F={2}F=\{2\}δ\delta 如下:

  • δ(0,a)={0,1}\delta(0,a)=\{0,1\}δ(0,b)={0}\delta(0,b)=\{0\}
  • δ(1,a)={2}\delta(1,a)=\{2\}δ(1,b)={2}\delta(1,b)=\{2\}
  • δ(2,)=\delta(2,\cdot)=\emptyset

起点 0 可任意循环匹配 (ab)(a|b)^*;一旦读入 aa 转到 1(这一位即”倒数第二个字符为 a”),再读一个字符到终态 2。

(3) NFA 确定化为 DFA(子集构造法)

初始 A=ε-closure({0})={0}A=\varepsilon\text{-closure}(\{0\})=\{0\},逐个未标记状态对 aabb 求 move 后的 ε\varepsilon-闭包:

状态集合DFA 状态aa 到达bb 到达含终态 2
{0}\{0\}AA{0,1}\{0,1\}\toBB{0}A\{0\}\to A
{0,1}\{0,1\}BB{0,1,2}\{0,1,2\}\toCC{0,2}\{0,2\}\toDD
{0,1,2}\{0,1,2\}CC{0,1,2}C\{0,1,2\}\to C{0,2}D\{0,2\}\to D
{0,2}\{0,2\}DD{0,1,2}C\{0,1,2\}\to C{0}A\{0\}\to A

DFA 转移图:

DFA 转移表:

DFA 状态aabb
A\to ABBAA
BBCCDD
C*CCCDD
D*DCCAA

终态集 FDFA={C,D}F_{DFA}=\{C,D\}

(4) DFA 最小化(Hopcroft 划分法)

初始划分 Π0={{A,B},{C,D}}\Pi_0 = \{\{A,B\},\{C,D\}\}(非终态、终态)。

考察 {A,B}\{A,B\}:经 aaAB{A,B}A\to B\in\{A,B\}BC{C,D}B\to C\in\{C,D\} → 不等价,分裂为 {A}\{A\}{B}\{B\}

考察 {C,D}\{C,D\}:经 bbCD{C,D}C\to D\in\{C,D\}DA{A}D\to A\in\{A\} → 不等价,分裂为 {C}\{C\}{D}\{D\}

最终划分 Π={{A},{B},{C},{D}}\Pi=\{\{A\},\{B\},\{C\},\{D\}\},不可再分,DFA 已是最简(4 个状态,无冗余)。

4. 语法分析-自顶向下语法分析(LL(1))

请描述自顶向下的语法分析方法的基本思想

从文法开始符号(S)出发,逐个产生式展开直到匹配输入串的终结符符号(LL(1)文法通过FIRST/FOLLOW集预先预测,消去左递归和公因子后避免回溯)。

例:已知文法 G[S]SaSBca;BBbdG[S]:S→aSBc | a; B→Bb | d

(1)改写文法使其适合自顶向下分析,写出改写结果; (2)分别计算改写后文法的右部文法符号串的 FIRST 集和 非终结符的FOLLOW集; (3)判断该文法是否为LL(1),若是,构造预测分析表;若不是,说明原因。

总结:LL(1)分析的关键在于预处理文法(消左递归、提公因子),并确保任意非终结符的SELECT集互不相交。

详细解题

(1) 改写文法

提取左公因子SaSBcS\to aSBcSaS\to a 公因子为 aa):

SaSSSBcεS \to aS'\qquad S' \to SBc \mid \varepsilon

消除直接左递归BBbdB\to Bb\mid d,引入 BB'):

BdBBbBεB \to dB'\qquad B' \to bB' \mid \varepsilon

改写后文法 GG'

(1) SaS(2) SSBc(3) Sε(4) BdB(5) BbB(6) Bε\begin{aligned} (1)&\ S \to aS' \\ (2)&\ S' \to SBc \\ (3)&\ S' \to \varepsilon \\ (4)&\ B \to dB' \\ (5)&\ B' \to bB' \\ (6)&\ B' \to \varepsilon \end{aligned}

(2) 计算 FIRST 与 FOLLOW 集

FIRST 集(自底向上):

符号FIRST依据
BB'{b,ε}\{b,\varepsilon\}BbBB'\to bB'bbBεB'\to\varepsilon
BB{d}\{d\}BdBB\to dB'dd 为首终结符
SS{a}\{a\}SaSS\to aS'
SS'{a,ε}\{a,\varepsilon\}SSBcS'\to SBcFIRST(S)={a}FIRST(S)=\{a\}SεS'\to\varepsilon

右部串的 FIRST:

产生式右部FIRSTFIRST
aSaS'{a}\{a\}
SBcSBc{a}\{a\}
ε\varepsilon{ε}\{\varepsilon\}
dBdB'{d}\{d\}
bBbB'{b}\{b\}

FOLLOW 集(初始 #FOLLOW(S)\#\in FOLLOW(S),反复传播至稳定):

  1. SSBcS'\to SBcFIRST(Bc)={d}FIRST(Bc)=\{d\}BB 非空),故 dFOLLOW(S)d\in FOLLOW(S)BB 后跟 cccFOLLOW(B)c\in FOLLOW(B)
  2. SaSS\to aS'SS' 在末尾,FOLLOW(S)FOLLOW(S)FOLLOW(S)\subseteq FOLLOW(S')
  3. BdBB\to dB'BB' 在末尾,FOLLOW(B)FOLLOW(B)FOLLOW(B)\subseteq FOLLOW(B')
  4. BbBB'\to bB'BB' 在末尾,自反无新信息。

迭代后稳定:

非终结符FOLLOW
SS{#,d}\{\#,d\}
SS'{#,d}\{\#,d\}
BB{c}\{c\}
BB'{c}\{c\}

(3) SELECT 集与 LL(1) 判别、预测分析表

SELECT 集

产生式FIRSTFIRSTε\varepsilonSELECT
SaSS\to aS'{a}\{a\}
SSBcS'\to SBc{a}\{a\}
SεS'\to\varepsilonFOLLOW(S)={#,d}FOLLOW(S')=\{\#,d\}
BdBB\to dB'{d}\{d\}
BbBB'\to bB'{b}\{b\}
BεB'\to\varepsilonFOLLOW(B)={c}FOLLOW(B')=\{c\}

LL(1) 判别(同左部 SELECT 集相交):

  • SS'{a}{#,d}=\{a\}\cap\{\#,d\}=\emptyset
  • BB'{b}{c}=\{b\}\cap\{c\}=\emptyset
  • SSBB 各一个产生式,无冲突 ✓

该文法是 LL(1) 文法。

预测分析表 M[A,a]M[A,a]VT={a,b,c,d,#}V_T=\{a,b,c,d,\#\}):

非终结符aabbccdd#\#
SSSaSS\to aS'
SS'SSBcS'\to SBcSεS'\to\varepsilonSεS'\to\varepsilonSεS'\to\varepsilon

说明:SεS'\to\varepsilon 的 SELECT 集为 {#,d}\{\#,d\},故仅在 dd#\# 列填;cc 列为空,修正表如下。

非终结符aabbccdd#\#
SSSaSS\to aS'
SS'SSBcS'\to SBcSεS'\to\varepsilonSεS'\to\varepsilon
BBBdBB\to dB'
BB'BbBB'\to bB'BεB'\to\varepsilon

分析过程流图(以输入 adbcbc#adbcbc\# 为例,展示归约式分析过程):

5. 语法分析-算符优先文法分析

名词解释:句柄、素短语

句柄:最左直接短语。

素短语:句型中具有句法独立性且不可再分的最小短语结构。(去短语中找,并且要满足两条规则:1)包含终结符 2)除了他自身不能包含素短语)

例.给定文法 G[E]G[E]

EE+TT;TTFF;FFPP;P(E)IE → E+T|T; T → T*F|F; F → F↑P|P; P → (E) | I

(1)给出句型 FP+T(E+T)F↑P+T*(E+T) 的短语、直接短语、句柄、素短语、最左素短语。 (2)计算每个非终结符的 FIRSTVT 和 LASTVT 集,并据此构造算符优先关系表。 (3)对输入串 ii+iii↑i+i* i 执行算符优先分析,写出分析过程(包括栈、输入串、动作)。

详细解题

(1) 句型 FP+T(E+T)F↑P+T*(E+T) 的短语、直接短语、句柄、素短语、最左素短语

语法树(最右推导 EE+TE+TFE+T(E)E+T(E+T)FP+T(E+T)E\Rightarrow E+T\Rightarrow E+T*F\Rightarrow E+T*(E)\Rightarrow E+T*(E+T)\Rightarrow F↑P+T*(E+T) 的逆过程):

叶子自左向右:F, , P, +, T, , (, E, +, T, )F,\ \uparrow,\ P,\ +,\ T,\ *,\ (,\ E,\ +,\ T,\ )

短语集合(每棵子树叶节点串):

短语来自子树
FF子叶 FF(父 FFPF\to F↑P 的左子)
PP子叶 PP 右子)
FPF↑PFFPF\to F↑P
第一个 TTTT 自身
括号内 EETT子叶
E+TE+TEE+TE\to E+T(括号内)
(E+T)(E+T)P(E)P\to(E)
T(E+T)T*(E+T)TTFT\to T*F
FP+T(E+T)F↑P+T*(E+T)EE 整棵树

直接短语(子树高度为 1,父节点一步推出该叶子串):

直接短语单步归约依据
FFFF,其父直接派生
PPPP
E+TE+TEE+TE\to E+T 单步
(E+T)(E+T)P(E)P\to(E) 单步

注:FPF↑PFFPF\to F↑P 后叶子 PP 再由 PiP\to i 派生,高度 > 1,不是直接短语;T(E+T)T*(E+T) 同理。

句柄 = 最左直接短语:最左叶子是 FF,对应的直接短语即 FF

素短语(含至少一个终结符,且不含其他素短语的短语):

  • FPF↑P:含 \uparrow,内部 FFPP 不含终结符 → 素短语
  • E+TE+T:含 ++,内部 EETT 不含终结符 → 素短语
  • (E+T)(E+T):含子短语 E+TE+T(已含终结符)→ 不素
  • T(E+T)T*(E+T)、整句:含子素短语 → 不素

素短语集合:{FP, E+T}\{F↑P,\ E+T\}

最左素短语:最左的素短语 → FPF↑P(位置 1–3)。

结果汇总

结果
短语F, P, FP, T, E+T, (E+T), T(E+T), FP+T(E+T)F,\ P,\ F↑P,\ T,\ E+T,\ (E+T),\ T*(E+T),\ F↑P+T*(E+T)
直接短语F, P, E+T, (E+T)F,\ P,\ E+T,\ (E+T)
句柄FF
素短语FP, E+TF↑P,\ E+T
最左素短语FPF↑P

(2) FIRSTVT、LASTVT 集与算符优先关系表

FIRSTVT 集(规则:AaA\to a\cdotsABaA\to Ba\cdotsaaABA\to B\cdots 继承 FIRSTVT(B)FIRSTVT(B)):

非终结符FIRSTVT依据
PP{(, i}\{(,\ i\}P(P\to(\cdotsPiP\to i
FF{, (, i}\{\uparrow,\ (,\ i\}FFPF\to F↑P\uparrowFPF\to P 继承 FIRSTVT(P)FIRSTVT(P)
TT{, , (, i}\{*,\ \uparrow,\ (,\ i\}TTFT\to T*F*TFT\to F 继承 FIRSTVT(F)FIRSTVT(F)
EE{+, , , (, i}\{+,\ *,\ \uparrow,\ (,\ i\}EE+TE\to E+T++ETE\to T 继承 FIRSTVT(T)FIRSTVT(T)

LASTVT 集(规则:AaA\to\cdots aAaBA\to\cdots aBaaABA\to\cdots B 继承 LASTVT(B)LASTVT(B)):

非终结符LASTVT依据
PP{), i}\{),\ i\}P()P\to(\cdots)))PiP\to i
FF{, ), i}\{\uparrow,\ ),\ i\}FFPF\to F↑P\uparrowFPF\to P 继承 LASTVT(P)LASTVT(P)
TT{, , ), i}\{*,\ \uparrow,\ ),\ i\}TTFT\to T*F*TFT\to F 继承 LASTVT(F)LASTVT(F)
EE{+, , , ), i}\{+,\ *,\ \uparrow,\ ),\ i\}EE+TE\to E+T++ETE\to T 继承 LASTVT(T)LASTVT(T)

\doteq 关系(产生式右部相邻终结符):

  • P(E)P\to(E)()( \doteq )

\lessdot 关系AaBA\to\cdots aB\cdotsbFIRSTVT(B)b\in FIRSTVT(B)aba\lessdot b):

产生式aaBBbFIRSTVT(B)b\in FIRSTVT(B)关系
EE+TE\to E+T++TT{,,(,i}\{*,\uparrow,(,i\}++\lessdot *, \uparrow, ((, ii
TTFT\to T*F*FF{,(,i}\{\uparrow,(,i\}*\lessdot \uparrow, ((, ii
FFPF\to F↑P\uparrowPP{(,i}\{(,i\}(\uparrow\lessdot (, ii
P(E)P\to(E)((EE{+, ,,(,i}\{+,\ *,\uparrow,(,i\}(+, ,,(,i(\lessdot+,\ *,\uparrow,(,i
句括号#\#EE{+, ,,(,i}\{+,\ *,\uparrow,(,i\}#+, ,,(,i\#\lessdot+,\ *,\uparrow,(,i

\gtrdot 关系ABbA\to\cdots Bb\cdotsaLASTVT(B)a\in LASTVT(B)aba\gtrdot b):

产生式BBbbaLASTVT(B)a\in LASTVT(B)关系
EE+TE\to E+TEE++{+, ,,),i}\{+,\ *,\uparrow,),i\}+, ,,),i++,\ *,\uparrow,),i\gtrdot +
TTFT\to T*FTT*{, ,),i}\{*,\ \uparrow,),i\}, ,),i*,\ \uparrow,),i\gtrdot *
FFPF\to F↑PFF\uparrow{, ),i}\{\uparrow,\ ),i\},),i\uparrow,),i\gtrdot\uparrow\uparrow\gtrdot\uparrow 表右结合)
P(E)P\to(E)EE)){+, ,,),i}\{+,\ *,\uparrow,),i\}+, ,,),i)+,\ *,\uparrow,),i\gtrdot)
句括号EE#\#{+, ,,),i}\{+,\ *,\uparrow,),i\}+, ,,),i#+,\ *,\uparrow,),i\gtrdot\#

算符优先关系表

++*\uparrow(())ii#\#
++\gtrdot\lessdot\lessdot\lessdot\gtrdot\lessdot\gtrdot
*\gtrdot\gtrdot\lessdot\lessdot\gtrdot\lessdot\gtrdot
\uparrow\gtrdot\gtrdot\gtrdot\lessdot\gtrdot\lessdot\gtrdot
((\lessdot\lessdot\lessdot\lessdot\doteq\lessdot
))\gtrdot\gtrdot\gtrdot\gtrdot\gtrdot
ii\gtrdot\gtrdot\gtrdot\gtrdot\gtrdot
#\#\lessdot\lessdot\lessdot\lessdot\lessdot\doteq

说明:\uparrow\gtrdot\uparrow 体现幂运算右结合。

(3) 对 ii+iii↑i+i*i 的算符优先分析过程

栈底 #\#,输入 ii+ii#i↑i+i*i\#。比较栈顶终结符 aa 与当前输入 bb 的优先关系:\gtrdot 触发归约(找最左素短语),\lessdot\doteq 移进。归约结果统一记为非终结符 NN(不关心具体名)。

步骤符号栈当前输入串关系(栈顶终结符 vs 输入)动作
1#\#ii+ii#i↑i+i*i\##i\#\lessdot i移进 ii
2#i\#ii+ii#↑i+i*i\#ii\gtrdot ↑归约 iNi\to N
3#N\#Ni+ii#↑i+i*i\##\#\lessdot ↑移进
4#N\#N↑i+ii#i+i*i\#i↑\lessdot i移进 ii
5#Ni\#N↑i+ii#+i*i\#i+i\gtrdot +归约 iNi\to N
6#NN\#N↑N+ii#+i*i\#+↑\gtrdot +归约 NNNN↑N\to N
7#N\#N+ii#+i*i\##+\#\lessdot +移进 ++
8#N+\#N+ii#i*i\#+i+\lessdot i移进 ii
9#N+i\#N+ii#*i\#ii\gtrdot *归约 iNi\to N
10#N+N\#N+Ni#*i\#++\lessdot *移进 *
11#N+N\#N+N*i#i\#i*\lessdot i移进 ii
12#N+Ni\#N+N*i#\#i#i\gtrdot \#归约 iNi\to N
13#N+NN\#N+N*N#\##*\gtrdot \#归约 NNNN*N\to N
14#N+N\#N+N#\#+#+\gtrdot \#归约 N+NNN+N\to N
15#N\#N#\###\#\doteq \#接受

分析过程示意:

6. 语法分析 - LR分析

LR分析器与优先关系分析器在识别句柄时的主要异同是什么?

相同点:

两类分析器均通过扫描输入串、维护栈和输入指针(或状态),结合文法规则(或表)逐步推进,依次识别终结符句柄(handle)以规约。

不同点:

  • 驱动方式:LR使用自底向上DFA(LR(0)/LR(1)/LALR等),优先关系使用LL(1)预测表或预测函数,驱动策略不同(LR依赖goto表,优先依赖预测表)。
  • 识别过程:LR识别单个句柄,优先关系可识别多个/单个(LL中先看FIRST集)。
  • 文法支持:LR支持左递归,优先关系不支持。
  • 构造复杂度:LR表构造更复杂,优先关系更简单。

例. 已知文法 G 如下:

  1. SSS' \to S;
  2. SES \to E;
  3. EE+TE \to E+T;
  4. ETE \to T;
  5. TTFT \to T*F;
  6. TFT \to F;
  7. F(E)F \to (E);
  8. FiF \to i;

(1) 画出识别该文法所有活前缀的 LR(1) 项目集规范族 (DFA)。 (2) 构造 LR(1) 分析表(要求给出 ACTION 和 GOTO 表)。 (3) 给出输入串 i+iii+i*i 的分析过程(要求列出栈、输入、动作)。

详细解题

本文法的 LR(1) 项目核心(同心部分)与材料 6.10 节 SLR(1) 的 DFA 结构相同,但每个归约项目携带状态相关的向前看符号,而非全局 FOLLOW 集。为便于作图,下面先给出与 SLR(1) 同心的 LR(0) 骨架 DFA,再在 (2) 的分析表中以 LR(1) 向前看符号决定归约栏。

(1) LR(1) 项目集规范族(DFA)

LR(1) 项目形如 [Aαβ, a][A\to\alpha\cdot\beta,\ a],闭包:若 [AαBβ, a]I[A\to\alpha\cdot B\beta,\ a]\in I,加入 [Bγ, b][B\to\cdot\gamma,\ b]bFIRST(βa)b\in FIRST(\beta a)

核心项目集(圆点结构与 SLR 的 I0I_0I11I_{11} 同心,列出关键状态及向前看符号):

状态LR(1) 项目集(向前看符号标于末尾)
I0I_0[SS,#][S'\to\cdot S,\#][SE,#][S\to\cdot E,\#][EE+T,#][E\to\cdot E+T,\#][EE+T,+][E\to\cdot E+T,+][ET,#][E\to\cdot T,\#][ET,+][E\to\cdot T,+][TTF,#][T\to\cdot T*F,\#][.,+][.,+][.,][.,*][TF,#][T\to\cdot F,\#][.,+][.,+][.,][.,*][F(E),#][F\to\cdot(E),\#][.,+][.,+][.,][.,*][Fi,#][F\to\cdot i,\#][.,+][.,+][.,][.,*]
I1I_1[SS,#][S'\to S\cdot,\#](接受)
I2I_2[SE,#][S\to E\cdot,\#][EE+T,#][E\to E\cdot+T,\#][EE+T,+][E\to E\cdot+T,+]
I3I_3[ET,#][E\to T\cdot,\#][ET,+][E\to T\cdot,+][TTF,#][T\to T\cdot*F,\#][.,+][.,+][.,][.,*]
I4I_4[TF,#][T\to F\cdot,\#][.,+][.,+][.,][.,*]
I5I_5[F(E),#][F\to(\cdot E),\#][.,+][.,+][.,][.,*](闭包后同 I0I_0 结构,但带向前看 {),+,}\{),+,*\}
I6I_6[Fi,#][F\to i\cdot,\#][.,+][.,+][.,][.,*](归约 r7r_7
I7I_7[EE+T,#][E\to E+\cdot T,\#][.,+][.,+](闭包后 T,F,(,iT,F,(,i{#,+,}\{\#,+,*\}
I8I_8[TTF,#][T\to T*\cdot F,\#][.,+][.,+][.,][.,*](闭包后 F,(,iF,(,i{#,+,}\{\#,+,*\}
I9I_9[F(E),#][F\to(E\cdot),\#][.,+][.,+][.,][.,*][EE+T,)][E\to E\cdot+T,)][EE+T,+][E\to E\cdot+T,+]
I10I_{10}[ET,)][E\to T\cdot,)][.,+][.,+][TTF,)][T\to T\cdot*F,)][.,+][.,+][.,][.,*]
I11I_{11}[TF,)][T\to F\cdot,)][.,+][.,+][.,][.,*]

I5,I7,I8I_5,I_7,I_8( , i(\ ,\ i 等 GOTO 后会产生与上述状态 同心 但向前看符号不同的新状态。整体结构与 SLR(1) 的 DFA 同心。

DFA 转移图(同心骨架,标注圆点后读入的符号):

注:I5,I7,I8I_5, I_7, I_8 各自经 ((ii 的 GOTO 指向同心状态(与 I5,I6I_5, I_6 同心但局部向前看符号不同)。上图为同心骨架示意。

(2) LR(1) 分析表

由 LR(1) 项目集族填表:归约仅在该状态存在 [Aα, a][A\to\alpha\cdot,\ a] 且当前输入为 aa 时执行。

状态ii++*(())#\#SSEETTFF
0S6S_6S5S_51234
1acc
2S7S_7r1r_1
3r3r_3S8S_8r3r_3
4r5r_5r5r_5r5r_5
5S6S_6S5S_591011
6r7r_7r7r_7r7r_7
7S6S_6S5S_51011
8S6S_6S5S_511
9S7S_7S11S_{11}'
10r3r_3S8S_8r3r_3
11r5r_5r5r_5r5r_5

说明:

  • 状态 2 含 [SE,#][S\to E\cdot,\#] ⟹ 仅 #\# 列归约 r1r_1;含 [EE+T][E\to E\cdot+T]++ 移进。LR(1) 精确到状态,无冲突。
  • 状态 9 的 )) 项应为归约 r6r_6(由 F(E)F\to(E)\cdot 在括号上下文中以 )) 为向前看符号触发),表示为 r6r_6(修正下表)。
  • 状态 4、11 等归约 r5r_5TFT\to F)。
  • 状态 6 归约 r7r_7FiF\to i)。
  • GOTO 列:状态 0 行 S=1,E=2,T=3,F=4S=1,E=2,T=3,F=4;状态 5 行 E=9,T=10,F=11E=9,T=10,F=11;状态 7 行 T=10,F=11T=10,F=11;状态 8 行 F=11F=11

修正后的 LR(1) 分析表

状态ii++*(())#\#SSEETTFF
0S6S_6S5S_51234
1acc
2S7S_7r1r_1
3r3r_3S8S_8r3r_3
4r5r_5r5r_5r5r_5
5S6S_6S5S_591011
6r7r_7r7r_7r7r_7
7S6S_6S5S_51011
8S6S_6S5S_511
9S7S_7r6r_6
10r3r_3S8S_8r3r_3
11r5r_5r5r_5r5r_5

产生式编号:r1:SEr_1:S\to Er2:EE+Tr_2:E\to E+Tr3:ETr_3:E\to Tr4:TTFr_4:T\to T*Fr5:TFr_5:T\to Fr6:F(E)r_6:F\to(E)r7:Fir_7:F\to i。 状态 14(对应 EE+TE\to E+\cdot TTT 后)含 [EE+T,#][E\to E+T\cdot,\#]#\# 归约 r2r_2* 移进 — 与状态 9 同心但向前看不同。表中状态 9 对应括号内 EE 已归约的场景()) 归约 r6r_6*? 实际 I9I_9 不含 * 移进)。上表已据同心 DFA 简化。

(3) 输入串 i+iii+i*i 的分析过程

利用 LR(1) 分析表(与材料 6.9 节 SLR(1) 分析过程动作相同,因本文法对应部分动作一致):

步骤状态栈符号栈输入串ACTIONGOTO
(1)0#\#i+ii#i+i*i\#S6S_6
(2)06#i\#i+ii#+i*i\#r7r_74
(3)04#F\#F+ii#+i*i\#r5r_53
(4)03#T\#T+ii#+i*i\#r3r_32
(5)02#E\#E+ii#+i*i\#S7S_7
(6)027#E+\#E+ii#i*i\#S6S_6
(7)0276#E+i\#E+ii#*i\#r7r_711
(8)027(11)#E+F\#E+Fi#*i\#r5r_510
(9)027(10)#E+T\#E+Ti#*i\#S8S_8
(10)027(10)8#E+T\#E+T*i#i\#S6S_6
(11)027(10)86#E+Ti\#E+T*i#\#r7r_711
(12)027(10)8(11)#E+TF\#E+T*F#\#r4r_410
(13)027(10)#E+T\#E+T#\#r2r_22
(14)02#E\#E#\#r1r_11
(15)01#S\#S#\#acc

关键步骤解析

  • (2) r7r_7:状态 6 含 [Fi,{+,,#}][F\to i\cdot,\{+,*,\#\}],输入 +{+,,#}+\in\{+,*,\#\} ⟹ 归约 FiF\to i。弹出 1 个状态(6)和 1 个符号(ii),栈顶状态 0,左部 FF 查 GOTO[0,F]=4 ⟹ 压 FF 和 4。
  • (3) r5r_5:状态 4 含 [TF,{#,+,}][T\to F\cdot,\{\#,+,*\}] ⟹ 归约 TFT\to F,GOTO[0,T]=3。
  • (4) r3r_3:状态 3 含 [ET,{#,+}][E\to T\cdot,\{\#,+\}],输入 +{#,+}+\in\{\#,+\} ⟹ 归约 ETE\to T,GOTO[0,E]=2。
  • (5) S7S_7:状态 2 含 [EE+T][E\to E\cdot+T],输入 ++ ⟹ 移进到 I7I_7
  • (13) r2r_2:状态(同心 I9I_9 对应的 E+TE+T 归约态)含 [EE+T,#][E\to E+T\cdot,\#],输入 #\# ⟹ 归约 EE+TE\to E+T(弹出 3 个状态和 +T+T 三符号),栈顶状态 0,GOTO[0,E]=2。
  • (14) r1r_1:状态 2 含 [SE,#][S\to E\cdot,\#],输入 #\# ⟹ 归约 SES\to E,GOTO[0,S]=1。
  • (15) acc:状态 1 含 [SS,#][S'\to S\cdot,\#],输入 #\# ⟹ 接受。

LR(1) 比 SLR(1) 更精细:归约向前看符号是 项目自身携带的状态相关向前看集合 ,而非全局 FOLLOW 集,能区分“括号内归约(向前看含 )))”与“顶层归约(向前看含 #\#)”。输入串 i+iii+i*i 被成功分析,是该 LR(1) 文法的句子。

分析过程流图:

My avatar

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


More Posts

Comments