参考资料
编译原理-陈意云-第三版
哈工大编译原理 MOOC
前言
- 本文主要介绍词法分析、语法分析、语法制导翻译,这三个章节的内容
- 对于本文中出现的错误,欢迎在评论区指出,我会及时修改
- 语法分析和语法制导翻译,部分内容(如 LR 分析器),使用 Java 代码实现,并将执行结果进行截图,感兴趣的可以访问 gitee 链接
大纲
1. 引论
编译的各个阶段
词法分析 → 语法分析 → 语义分析 → 中间代码生成 → 代码优化 → 目标代码生成 词法分析\to语法分析\to语义分析\to中间代码生成\to代码优化\to目标代码生成 词法分析→语法分析→语义分析→中间代码生成→代码优化→目标代码生成
编译器和解释器的区别
- 编译器是把源程序的每一条语句都编译成机器语言,并生成一个可执行(二进制)文件,可以在计算机上直接运行
- 解释器将代码逐行或逐句的解释成机器语言并执行,不会生成可执行文件
- 编译器对源程序的词法分析、语法分析、语义分析只要进行一次,而解释器对每个语句都要进行一次,所以编译器生成的可执行文件,比解释器更快
2. 词法分析
概念
术语 | 含义 |
---|---|
字母表 | 符号的有限集合 |
串 | 符号的有穷序列 |
空串 | 长度为0的串 |
语言 | 字母表上的串集 |
句子、字 | 属于该语言的串 |
连接 | x和y都是串,将y加到x后面形成的串,称为x和y的连接 |
正规式
定义
- 正规式又叫正则表达式
- 每个正规式
r
表示一个语言L(r)
- 空串(
ε
)是正规式,表示{ε}
示例
- 考虑字母表 Σ = { a , b } \Sigma = \{ a, b \} Σ={a,b}
- a ∣ b a | b a∣b 表示 { a , b } \{a, b\} {a,b},即 ∣ | ∣ 表示或
- ( a , b ) ∣ ( a , b ) (a, b) | (a,b) (a,b)∣(a,b) 表示 { a a , a b , b a , b b } \{aa, ab, ba, bb\} {aa,ab,ba,bb}, ( ) () () 相当于限制了了 ∣ | ∣ 的范围
-
a
∗
a^*
a∗ 表示由任意个
a
a
a 构成的所有串的集合
- 任意个,包括 0 个,即空串
- 为什么是所有串的集合?
因为正规式表示一个语言,语言是字母表上的串集
正规定义
- 可以对正规式进行命名: 名称 → 正规式 名称 \to 正规式 名称→正规式
- 例如
d
i
g
i
t
=
0
∣
1
∣
2
∣
3
∣
4
∣
5
∣
6
∣
7
∣
8
∣
9
digit = 0|1|2|3|4|5|6|7|8|9
digit=0∣1∣2∣3∣4∣5∣6∣7∣8∣9
表示 0 ~ 9 之间的某个符号
FA
-
Finite Automaton
,有限自动机,简称 FA -
有限自动机由以下几个要素组成:
- 状态集合:包含有限个状态的集合,每个状态表示自动机在某个特定时间点的内部状态。
- 输入字母表:包含自动机接受的输入符号的集合。
- 转移函数:描述了自动机在给定输入符号下从一个状态转移到另一个状态的规则。
- 初始状态:表示自动机的初始状态,即开始状态。
- 终止状态集合:表示自动机可接受的最终状态集合,也称为接受态或终止态。
-
示例
直接看概念过于抽象,这里提供一个简单的示例
假设有一个自动机:能够判断输入的2
位数字,是否每一位都大于5,那么这个自动机应该长这样- 其中,每个小圆圈就是状态,而
start
是开始状态,是、不是
是结束状态 - 每个自动机都有唯一的开始状态,有一个或多个结束状态
- 其中,每个小圆圈就是状态,而
NFA
-
Non-deterministic Finite Automaton
,不确定的有限自动机,简称NFA
-
若一个
FA
中,存在某个状态,输入符号后,转移后的状态有多个,那么这个FA
就是NFA
-
示例
正规式 ( a ∣ b ) a b (a|b)ab (a∣b)ab 的NFA
状态转换表状态 a b 0 0, 1 0 1 2 2 - 状态 0 输入 a 可以转移到状态 0 或状态 1,因此是 NFA
- NFA 中的某条路径,就是句子(属于该语言的串),例如
aab
DFA
Deterministic Finite Automaton
,确定性有限自动机,简称DFA
- 若一个
FA
中,存在某个状态,输入符号后,转移后的状态只有一个,那么这个FA
就是DFA
NFA 与 DFA 的对比
- NFA 就像是在走迷宫,而DFA就像在过独木桥
当NFA发现路走不通时,必须回头换条路再走,而DFA只需要一直往前就行 - 显然 DFA 的效率更高,因此需要将 NFA 转化成 DFA
NFA 转 DFA
epsilon-closure
epsilon-closure(T)
- 状态集合 T 的 epsilon 闭包
- 对于状态集合
T
,经过有限次的epsilon
(ε),可以到达的状态集合 - 有限次,包括 0 次
- 示例
- 只考虑:每个状态集合仅有一个状态的情况
状态 epsilon闭包 1 1,2,4 3 1,2,3,4,6,7 5 1,2,4,5,6,7 6 1,2,4,6,7 - 若要考虑状态集合有 n n n 个状态,只需要分别求 epsilon 闭包 c 1 , c 2 , . . . , c n c_1,c_2,...,c_n c1,c2,...,cn,对 c 1 , c 2 , . . . , c n c_1,c_2,...,c_n c1,c2,...,cn 求并集即可。
- 只考虑:每个状态集合仅有一个状态的情况
move
move(T, a)
状态集合 T 输入符号 a 之后,转移后的状态集合,并对集合内的元素求 epsilon 闭包- 示例
状态 输入符号 move 2 a m o v e ( { 2 } ) = e p s i l o n − c l o s u r e ( { 3 } ) = { 1 , 2 , 3 , 4 , 6 , 7 } move(\{2\})=epsilon - closure(\{3\}) = \{1, 2, 3, 4, 6, 7\} move({2})=epsilon−closure({3})={1,2,3,4,6,7}
子集构造法
算法
确认NFA 的输入符号;
求开始状态的 epsilon 闭包 A;
将 A 存入最终结果 res;
while (res 中有尚未标记的状态 T) {
标记状态 T;
for (每一个输入符号 a) {
U = epsilon-clousre(move(T,a));
if(U未标记过) {
将 U 加入 res;
}
状态转换表 T 行 a 列 = U;
}
}
示例
- 考虑 NFA
- 确认NFA的输入符号
Σ = { a , b } \Sigma = \{a, b\} Σ={a,b} - 求开始状态的 epslion 闭包
ϵ − c l o s u r e ( 0 ) = { 0 , 1 , 2 , 4 , 7 } = A \epsilon-closure(0)=\{ 0,1,2,4,7 \} = A ϵ−closure(0)={0,1,2,4,7}=A - 对 A 计算
move(A, a)
,其中只有状态 2、7 可以接收 a
A → a = { 3 , 8 } A\to a = \{ 3,8 \} A→a={3,8}
ϵ − c l o s u r e ( m o v e ( A , a ) ) = { 3 , 8 } + { 1 , 2 , 4 , 6 , 7 } = { 1 , 2 , 3 , 4 , 6 , 7 , 8 } = B \epsilon-closure(move(A,a)) = \{ 3,8 \} + \{1,2,4,6,7\} = \{1,2,3,4,6,7,8\} = B ϵ−closure(move(A,a))={3,8}+{1,2,4,6,7}={1,2,3,4,6,7,8}=B - 计算
move(A, b)
A → b = { 5 } A \to b =\{ 5 \} A→b={5}
ϵ − c l o s u r e ( m o v e ( A , b ) ) = { 5 } + { 1 , 2 , 4 , 6 , 7 } = { 1 , 2 , 4 , 5 , 6 , 7 } = C \epsilon-closure(move(A,b))=\{ 5 \} + \{ 1,2,4,6,7 \} = \{ 1,2,4,5,6,7 \} = C ϵ−closure(move(A,b))={5}+{1,2,4,6,7}={1,2,4,5,6,7}=C - A 已经是标记过的状态集合了,开始计算状态 B
- 计算
move(B,a)
B → a = A → a B \to a = A \to a B→a=A→a
m o v e ( B , a ) = m o v e ( A , a ) move(B,a) = move(A,a) move(B,a)=move(A,a) - 计算
move(B,b)
B → b = { 5 , 9 } B \to b = \{5, 9\} B→b={5,9}
ϵ − c l o s u r e ( m o v e ( B , b ) ) = { 5 , 9 } + { 1 , 2 , 4 , 6 , 7 } = { 1 , 2 , 4 , 5 , 6 , 7 , 9 } = D \epsilon-closure(move(B,b))=\{ 5,9 \} +\{ 1,2,4,6,7 \} = \{ 1,2,4,5,6,7,9 \} = D ϵ−closure(move(B,b))={5,9}+{1,2,4,6,7}={1,2,4,5,6,7,9}=D - B 已经是标记过的状态集合了,开始计算状态 C
- 计算
move(C, a)
m o v e ( C , a ) = m o v e ( A , a ) move(C,a) = move(A,a) move(C,a)=move(A,a) - 计算
move(C,b)
m o v e ( C , b ) = m o v e ( A , b ) move(C,b) = move(A,b) move(C,b)=move(A,b) - C 已经是标记过的状态集合了,开始计算状态 D
- 计算
move(D,a)
m o v e ( D , a ) = m o v e ( A , a ) move(D,a) = move(A,a) move(D,a)=move(A,a) - 计算
move(D,b)
ϵ − c l o s u r e ( m o v e ( D , b ) ) = { 5 , 10 } + { 1 , 2 , 4 , 6 , 7 } = { 1 , 2 , 4 , 5 , 6 , 7 , 10 } = E \epsilon-closure(move(D,b)) = \{ 5,10 \} + \{ 1,2,4,6,7 \} =\{1,2,4,5,6,7,10\}=E ϵ−closure(move(D,b))={5,10}+{1,2,4,6,7}={1,2,4,5,6,7,10}=E move(E, a)
和move(E, b)
不会再产生新的集合- 最终结果
注意到,A 是开始状态,而 E 是结束状态
这是因为 A 中包含开始状态0
,而 E 中包含结束状态10
开始状态有一个,结束状态有一个或多个
DFA 的化简
思想
- 化简 DFA 的主要思想是:DFA 中,是否有两个代价是等价的
- 例如 DFA 中的一部分,若去掉 A,直接指向 C,DFA 的处理结果不变,也就是 A 多余了,可以化简
算法
接收状态子集Ⅰ
非接收状态子集Ⅱ
// 将 Ⅰ、Ⅱ 的并集称为 U
for (在 U 中的集合 G) {
对于 G 中任意两个状态 s, t
当输入 a 时,s,t 转移后的状态为 S1, S2
if(S1 和 S2 在 U 中不同的子集) {
将 s, t 划分成两个子集
} else {
将 s, t 划分到同一个子集
}
划分后的 U' 作为新的 U
}
示例
-
考虑 DFA
-
确认输入符号集合 Σ = { a , b } \Sigma = \{a, b\} Σ={a,b}
-
将状态划分为:接收状态子集、非接收状态子集
接收状态子集就是包含接收状态的集合,非接受状态子集是其余的集合
接收状态子集Ⅰ = { D } 接收状态子集 Ⅰ= \{ D \} 接收状态子集Ⅰ={D}
非接受状态子集Ⅱ = { A , B , C } 非接受状态子集Ⅱ = \{ A,B,C \} 非接受状态子集Ⅱ={A,B,C} -
化简,意味着某些集合是等价的
而 { D } \{D\} {D}显然不可再分,那么考虑化简 { A , B , C } \{ A,B,C \} {A,B,C}- m o v e ( A , a ) = m o v e ( B , a ) = m o v e ( C , a ) = B ∈ { A , B , C } move(A,a)=move(B,a)=move(C,a)=B \in \{A,B,C\} move(A,a)=move(B,a)=move(C,a)=B∈{A,B,C}
- m o v e ( A , b ) = m o v e ( C , b ) = C ∈ { A , B , C } move(A,b) = move(C,b) = C \in \{A,B,C\} move(A,b)=move(C,b)=C∈{A,B,C}
- m o v e ( B , b ) = D ∉ { A , B , C } move(B,b) = D \notin \{A,B,C\} move(B,b)=D∈/{A,B,C}
由于 AC 和 B 对于不同的符号,转移到的状态子集不同(Ⅰ、Ⅱ),因此将 { A , B , C } \{A,B,C\} {A,B,C} 划分为 2 个子集: { A , C } \{A,C\} {A,C}、 { B } \{B\} {B}
-
目前只有 { A , C } \{A,C\} {A,C}可以再划分,考虑 { A , C } \{A,C\} {A,C}
m o v e ( A , a ) = m o v e ( C , a ) = B ∈ { B } move(A,a) = move(C,a) = B \in \{B\} move(A,a)=move(C,a)=B∈{B}
m o v e ( A , b ) = m o v e ( C , b ) = C ∈ { A , C } move(A,b)=move(C,b)=C \in \{A,C\} move(A,b)=move(C,b)=C∈{A,C}
A 和 C 对于不同的符号,转移到的子集相同,因此不能再划分(AC 是等价的)
用 A A A代表 { A , C } \{ A,C \} {A,C} -
最终的状态转换表
化简后的DFA
正规式构造 NFA
规则
-
识别 r = ϵ r = \epsilon r=ϵ 的 NFA
-
识别 r = a r = a r=a 的 NFA
-
识别 r = a ∣ b r = a | b r=a∣b 的 NFA
-
识别 r = a b r = ab r=ab 的 NFA
-
识别 r = ? ∗ r = ?^* r=?∗ 的 NFA
示例
-
构造正规式 r = ( a ∣ b ) ∗ a b r = (a|b)^*ab r=(a∣b)∗ab 的 NFA
-
首先构造 r = a ∣ b r = a|b r=a∣b
-
再构造 ( a ∣ b ) ∗ (a|b)^* (a∣b)∗
-
注意到, ( a ∣ b ) ∗ (a|b)^* (a∣b)∗ 前面不会再有其他符号,因此可以加上
开始
-
( a ∣ b ) ∗ (a|b)^* (a∣b)∗ 后面是 a b ab ab,并且 a b ab ab 后面不会再有其他符号
3. 语法分析
概念
-
产生式
A → a b A \to ab A→ab- A A A 可以推出 a b ab ab
- 这类似数学中的方程, 但是方向是单向的,即 A = a b A = ab A=ab, a b ≠ A ab \neq A ab=A
- 形如 A → B A \to B A→B 的式子,称为产生式
-
文法
文法由一个或多个推导构成- 例如
E -> A A -> ab A -> ε
- 例如
-
非终结符
考虑推导 A → a b A \to ab A→ab,符号 A A A 可以推导出其他字符,那么 A A A 被称为非终结符
在文法中,出现在产生式左侧的,都是非终结符- 非终结符一般用大写字母表示
- S 一般表示开始符号
- 非终结符有时也用小写单词表示,例如
e
x
p
r
→
(
e
x
p
r
)
expr \to (expr)
expr→(expr),其中
e
x
p
r
expr
expr 是
expression
的缩写
-
终结符
在文法中,没有在产生式左侧出现过的符号,都是终结符
顾名思义,终结:到这个符号结束了,不能再继续推导了- 考虑文法
其中 E E E, A A A 都是非终结符, a a a 和 b b b 是终结符E -> A A -> ab
- 终结符一般用小写字母表示
- 数字、标点符号、运算符号( + − × ÷ +-\times \div +−×÷)等,一般都是终结符
- 考虑文法
-
上下文无关文法
用G
表示 上下文无关文法
那么 G = ( V T V N , S , P ) G = (V_TV_N,S,P) G=(VTVN,S,P),其中:-
V T V_T VT 表示终结符(Terminal)
-
V N V_N VN表示非终结符(Non Terminal)
-
S S S 表示开始符号(Start)
-
P P P表示产生式(Production)
-
-
句型:从文法的开始符号出发,能够推导出的一切字符串(有限长度)
- 句子是没有非终结符号的句型
推导
概念
- 文法由一个或多个产生式构成
- 由于产生式可以推导出符号,那么一个文法就对应一个语言(字母表上的串集)
- 推导就是根据文法中的产生式,将非终结符进行替换的过程
- 每次推导,只能使用一个产生式,不能跳步
- 示例
- 考虑文法
E -> (E) E -> -E E -> E + E E -> id
- 可以根据文法推导出
−
(
i
d
+
i
d
)
-(id+id)
−(id+id)
- 从 E 出发,根据 E → − E E \to -E E→−E,得到 E → − E E \to -E E→−E
- 根据 E → ( E ) E \to (E) E→(E),得到 E → − ( E ) E \to -(E) E→−(E)
- 根据 E → E + E E \to E + E E→E+E,得到 E → − ( E + E ) E \to -(E+E) E→−(E+E)
- 根据 E → i d E \to id E→id,得到 E → − ( i d + E ) E \to -(id + E) E→−(id+E)
- 根据 E → i d E \to id E→id,得到 E → − ( i d + i d ) E \to -(id + id) E→−(id+id)
- 考虑文法
分析树
- 分析树是将推导过程,以树的形式,可视化展现
- 在上一节的示例中,由文法推导出
−
(
i
d
+
i
d
)
-(id+id)
−(id+id)的分析树如图
E -> (E) E -> -E E -> E + E E -> id
注意到,分析树的叶子节点,就是推导出的串
最左推导与最右推导
- 在上一节中,我们可能发现推导过程不唯一,例如分析树处于
E
→
E
+
E
E \to E + E
E→E+E
此时,两个 E E E都可以使用产生式 E → i d E\to id E→id,并且不会导致推导失败 - 最左推导:每次推导,都替换产生式右侧中,最左侧的非终结符
E → E 1 + E 2 E\to E_1+E_2 E→E1+E2( E 1 , E 2 E_1,E_2 E1,E2相同,只是用编号区分),选择替换 E 1 E_1 E1 - 最右推导:每次推导,都替换产生式右侧中,最右侧的非终结符
E → E 1 + E 2 E\to E_1+E_2 E→E1+E2( E 1 , E 2 E_1,E_2 E1,E2相同,只是用编号区分),选择替换 E 2 E_2 E2
二义性
- 二义性指的是分析树不唯一(在最左推导或最右推导的情况下)
- 考虑文法
推导串 i d ∗ i d + i d id * id + id id∗id+idE -> (E) E -> E + E E -> E * E E -> id
- 推导1(最左推导)
E -> E * E E -> id * E E -> id * E + E E -> id * id + E E -> id * id + id
- 推导2(最左推导)
E -> E + E E -> E * E + E E -> id * E + E E -> id * id + E E -> id * id + id
- 推导1(最左推导)
消除左递归
左递归
-
直接左递归
A → A a A \to Aa A→Aa
注意到,产生式右侧的第一个符号,与产生式左侧相同
那么可以进行无限的推导 A → A a → A a a → . . . A \to Aa \to Aaa \to ... A→Aa→Aaa→... -
间接左递归
A → B a A \to Ba A→Ba
B → A b B \to Ab B→Ab
如果将第一个产生式中的 B B B 再进行推导,那么就会得到直接左递归 A → A b a A \to Aba A→Aba -
在最左推导中,左递归会导致无限循环,因为每次替换的都是最左侧的非终结符
消除直接左递归
-
考虑产生式 A → A a ∣ b A \to Aa | b A→Aa∣b(A可以推出 A a Aa Aa或 b b b)
-
如果
A
的推导想要结束,必然是经过了非左递归的推导,即 A → b A\to b A→b
那么我们将 A 的推导重写为 A → b A ′ A \to bA' A→bA′,A'
是新引入的符号 -
接下来分析
A'
到底是什么- 其实
A'
就是递归部分, A → A a A\to Aa A→Aa,每次左递归,都会携带一个 a a a
所以 A ′ → a A ′ A' \to aA' A′→aA′ - 结合
A
→
b
A
′
A \to bA'
A→bA′,有可能
A
A
A 以
A
→
b
A \to b
A→b 就结束推导,因此
A'
还可以推导出ε
所以 A ′ → a A ′ ∣ ε A' \to aA' | ε A′→aA′∣ε
- 其实
-
总结
对于产生式 A → A α 1 ∣ A α 2 ∣ . . . ∣ A α m ∣ β 1 ∣ β 2 ∣ . . . ∣ β n A \to A\alpha_1|A\alpha_2|...|A\alpha_m|\beta_1|\beta_2|...|\beta_n A→Aα1∣Aα2∣...∣Aαm∣β1∣β2∣...∣βn- 消除左递归时,首先将非左递归部分与新符号进行连接
A → β 1 A ′ ∣ β 2 A ′ ∣ . . . ∣ β n A ′ A \to \beta_1A'|\beta_2A'|...|\beta_nA' A→β1A′∣β2A′∣...∣βnA′ - 对于新符号,推导出的为左递归部分中的终结符;最后加上一个空串
A ′ → α 1 A ′ ∣ α 2 A ′ ∣ . . . ∣ α m A ′ ∣ ϵ A' \to \alpha_1A'|\alpha_2A'|...|\alpha_mA'|\epsilon A′→α1A′∣α2A′∣...∣αmA′∣ϵ
- 消除左递归时,首先将非左递归部分与新符号进行连接
消除间接左递归
- 考虑间接左递归
A -> Ba | b B -> Ab
- 将
A
A
A 带入第二个产生式,可以得到直接左递归
B → B a b ∣ b b B \to Bab | bb B→Bab∣bb - 再将直接左递归消去
B → b b B ′ B \to bbB' B→bbB′
B ′ → a b B ′ ∣ ϵ B' \to ab B' |\epsilon B′→abB′∣ϵ
First 与 Follow 集合
First
介绍
First(A)
由 A 可以推导出的所有串中,第一个终结符的集合- 非终结符才有 First 集合
- 空串属于 First 集合
算法
- 假设产生式
A
→
B
A \to B
A→B
- 那么 F i r s t ( A ) = F i r s t ( B ) First(A) = First(B) First(A)=First(B)
- 若有 B → C B \to C B→C,那么 F i r s t ( A ) = F i r s t ( B ) = F i r s t ( C ) First(A) = First(B) = First(C) First(A)=First(B)=First(C)
- 若有 B → c B \to c B→c,那么将 c 加入 F i r s t ( A ) First(A) First(A)
示例
-
考虑文法
E -> TE' E' -> +TE' | epsilon T -> FT' T' -> *FT' | epsilon F -> (E) | id
-
求解
First(E)
- 找出 E 为左侧的产生式
E -> TE'
- 产生式右侧的第一个符号是非终结符
T
T
T,因此
F
i
r
s
t
(
E
)
=
F
i
r
s
t
(
T
)
First(E)=First(T)
First(E)=First(T),现在求解
First(T)
- 找出 T 为左侧的产生式
T -> FT'
- 产生式右侧的第一个符号是非终结符
F
F
F,因此
F
i
r
s
t
(
T
)
=
F
i
r
s
t
(
F
)
First(T)=First(F)
First(T)=First(F),现在求解
First(F)
- 找出 F 为左侧的产生式
F -> (E) | id
- 其中
F
→
(
E
)
F \to (E)
F→(E) 的第一个符号是非终结符,因此将
(
加入 F i r s t ( F ) First(F) First(F); F → i d F\to id F→id 的第一个符号是非终结符,因此将id
加入 F i r s t ( F ) First(F) First(F)
因此 F i r s t ( E ) = F i r s t ( T ) = F i r s t ( F ) = { ( , i d } First(E) = First(T) = First(F) = \{ (, id\} First(E)=First(T)=First(F)={(,id}
- 找出 E 为左侧的产生式
-
注意到,求解 First 集合是一个递归的过程
-
该文法各终结符的 First 集合
非终结符 First集合 E (,id E’ +,epsilon T (,id T’ +,epsilon F (,id
Follow
介绍
- Follow 集合相较于 First 集合更复杂,集合的元素是可以跟在非终结符后的终结符
Follow(A)
可以跟在 A 后的终结符- Follow 集合不包括空串
- Follow 集合的计算,会用到 First 集合
为什么要有 Follow 集合
-
考虑文法
S -> aBC B -> bB B -> epsilon C -> a C -> d
-
分析串
abd
中间句子 产生式 S S -> aBC aBC B -> bB abBC B -> epsilon abC C -> d abd ACCEPT -
其中,第 3 步使用了 B 的 epsilon 产生式( B → e p s i l o n B\to epsilon B→epsilon)
- 能否使用,主要看 B 的后面跟着哪些符号(
Follow
集合),B 的后面是 C,而 C 可以转化为 a 或 d - 因为 ab 已经匹配成功了,B 当前要匹配的符号是 d,属于 B 后面紧跟的字符,因此 B 可 以转化为 epsilon
- 假设当前要匹配的符号是 e,此时不能(没必要)使用 B 的 epsilon 产生式,因为即使转化了 B,后续也无法匹配成功,应该在 B 这一步就拒绝这个句子
- 能否使用,主要看 B 的后面跟着哪些符号(
算法
Follow
集合中,不允许出现epsilon
每一次 Follow 集元素的增加,若跟产生式左侧有关,都是左侧添加到产生式右侧
-
Follow(S)
添加$
-
对于每一条产生式,对于产生式右侧
-
对于产生式右侧的非终结符
B
,若其右侧存在非终结符C
,将First(C)
添加到Follow(B)
A -> BC
-
若
C
可以推出epsilon
,将Follow(A)
添加到Follow(B)
A - > BC
C - > epsilon
-
将
Follow(A)
添加到Follow(C)
A -> C
-
若最右侧存在终结符
α
,且终结符左侧存在非终结符B
,将α
添加到Follow(B)
A -> Bα
-
-
回到步骤
2.
若过程中有
Follow
集发生变化,则继续执行步骤2否则结束
示例
-
考虑文法
E -> TE' E' -> +TE' | epsilon T -> FT' T' -> *FT' | epsilon F -> (E) | id
-
最终结果
符号 First Follow E ( , id $,) E’ +, epsilon $,) T ( , id +,$,) T’ *, epsilon +,$,) F ( , id *,+,$,) -
分析第一个产生式
E -> TE'
-
E 本身是一个句型,Follow集中添加 $
-
T 的Follow集添加 E’ 的 First集:+,epsilon
-
由于 E’ 可以推出 epsilon,所以 E 的Follow集添加到 T 的Follow集
-
E’ 是产生式的最后一个字符,因此可以跟在 E 后的符号,也可以跟在 E’ 后,将 E 的Follow集添加到 E’ 的Follow集
-
对于第二个产生式
E' -> +TE' | epsilon
,TE’ 的位置与第一个产生式相同,跳过 -
分析第三个产生式
T -> FT'
-
T 本身是一个句型,Follow集中添加 $
-
F 的Follow集添加 T’ 的 First集:*
-
由于 T’ 可以推出 epsilon,所以 T 的Follow集添加到 F 的Follow集
-
T’ 是产生式的最后一个字符,因此可以跟在 T 后的符号,也可以跟在 T’ 后,将 T 的Follow集添加到 T’ 的Follow集
-
分析第四个产生式:
T' -> *FT' | epsilon
,FT’ 的位置与第三个参数是相同,跳过 -
分析第五个产生式:
F -> (E) | id
-
F 本身是一个句型,Follow集中添加 $
-
推出的式子中,非终结符 E 的右侧为 ),添加到到 E 的Follow集中
处理Follow集的依赖关系
-
第一个产生式
E -> TE'
-
E’ 依赖 E,因此将 E 的Follow集添加到 E’ 的Follow集
-
E‘ 可以推出 epsilon,T 依赖 E,将 E 的Follow集添加到T的Follow集
-
对于第二个产生式
E' -> +TE' | epsilon
,TE’ 的位置与第一个产生式相同,跳过 -
分析第三个产生式
T -> FT'
-
T’ 依赖 T,将 T 的 Follow集添加到 T’ 的Follow集
-
T’ 可以推出 epsilon,F 依赖 T,将 T 的Follow集添加到 F 的Follow集
-
分析第四个产生式:
T' -> *FT' | epsilon
,FT’ 的位置与第三个参数是相同,跳过 -
分析第五个产生式:
F -> (E) | id
,没有依赖关系
结束
自上而下分析
LL(1)文法
定义
- LL(1)
- 第一个 L 表示:分析时从左到右扫描串
- 第二个 L 表示:最左推导
- 1 表示:每次只向右看一个符号
构建预测分析表
介绍
-
LL(1) 对输入串进行分析,是根据预测分析表进行的
-
select
集合select(A)
:A 可以接收什么样的输入符号- 对于每一个产生式
A -> BC
,select 集合为:- 若
B
是终结符,那么select(A -> BC) = B
- 若
B
是非终结符,那么select(A -> BC) = First(B)
- 若
BC
为epsilon
,那么select(A -> BC) = Follow(A)
- 若
-
预测分析表的组成
- 表头
由非终结符和输入符号构成 - 行列
每一行的每一列对应非终结符(行)接收到不同符号(列)的反应
- 表头
-
一个预测分析表,看起来是这样
算法
- 在每个终结符对应的行,
select
集合对应的列,填上对应的产生式
示例
-
考虑文法
E -> TE' E' -> +TE' | epsilon T -> FT' T' -> *FT' | epsilon F -> (E) | id
-
计算
First
与Follow
集合符号 First Follow E ( , id $,) E’ +, epsilon $,) T ( , id +,$,) T’ *, epsilon +,$,) F ( , id *,+,$,) -
对于文法中的每一个产生式,若有选择,进行分解
例如:
E' -> +TE' | epsilon
E' -> +TE' E' -> epsilon
-
本例中的分析表
分解后的文法
E -> TE' E' -> +TE' E' -> epsilon T -> FT' T' -> *FT' T' -> epsilon F -> (E) F -> id
非终结符 \ 输入符号 id + * ( ) $ E E -> TE’ E -> TE’ E’ E’ -> +TE’ E’ -> epsilon E’ -> epsilon T T -> FT’ T -> FT’ T’ T’ -> epsilon T’ -> *FT’ T’ -> epsilon T’ -> epsilon F F -> id F -> (E) -
从非终结符
E
推导的产生式有:E -> TE'
其中,输入符号为
First(T) = { id, ( }
那么
E
这一行,对应输入符号的列id, (
就填上该产生式非终结符 \ 输入符号 id + * ( ) $ E E -> TE’ E -> TE’ -
从非终结符
E'
推导的产生式有:E' -> +TE' E' -> epsilon
-
E' -> +TE'
,输入符号为+
那么
E'
这一行,对应输入符号的列+
就填上该产生式非终结符 \ 输入符号 id + * ( ) $ E’ E’ -> +TE’ -
E' -> epsilon
,输入符号为Follow(E') = { ), $ }
那么
E'
这一行,对应输入符号的列), $
就填上该产生式非终结符 \ 输入符号 id + * ( ) $ E’ E’ -> epsilon E’ -> epsilon
-
-
其余非终结符的推导同理
-
非递归的预测分析
-
有分析表
非终结符 \ 输入符号 id + * ( ) $ E E -> TE’ E -> TE’ E’ E’ -> +TE’ E’ -> epsilon E’ -> epsilon T T -> FT’ T -> FT’ T’ T’ -> epsilon T’ -> *FT’ T’ -> epsilon T’ -> epsilon F F -> id F -> (E) -
预测分析器接收输入句子:
id * id + id
-
先将
$
与 开始符号E
压入栈,将句子连接$
作为输入栈 输入 输出 $E id * id + id $ -
弹出栈顶元素
E
,输入队列的队头id
,在分析表中对应的产生式为:E -> TE'
,输出为TE'
栈 输入 输出 $ id * id + id $ E -> TE’ 将
TE'
逆序入栈栈 输入 输出 $E’T id * id + id $ -
弹出栈顶元素
T
,输入队列的队头id
,在分析表中对应的产生式为:T -> FT'
,输出为FT'
栈 输入 输出 $E’ id * id + id $ T -> FT’ 将
FT'
逆序入栈栈 输入 输出 $E’T’F id * id + id $ -
弹出栈顶元素
F
,输入队列的队头id
,在分析表中对应的产生式为:F -> id'
,输出为id'
栈 输入 输出 $E’T’ id * id + id $ F -> id 将
id
逆序入栈栈 输入 输出 $E’T’id id * id + id $ 此时栈顶与队头相同,弹出栈顶与队头
栈 输入 输出 $E’T’ * id + id $ -
以此类推,每当栈顶为终结符时,与输入队列队头对比,若相同,弹出栈顶与队头
若栈顶与队头不同,或栈顶无队头对应的输入,则拒绝该语句
-
-
完整的预测分析过程
-
根据输出,可以画出分析树
自下而上分析
概念
-
归约
将产生式右侧的串,替换成产生式左侧的非终结符- 产生式:
S -> ab
若有串ab
,则可以将ab
归约成S
- 产生式:
-
句柄 :句型中,与产生式右侧相等的子串(可以被归约的串),并且该归约是最右推导中逆过程的一步
最右推导:任何一次推导,都是对产生式最右侧的非终结符进行替换
句型:从文法的开始符号出发,能够推导出的一切字符串(有限长度)-
E -> E * E | E + E
对于句型
E * E + E
,E + E
是该句型的句柄-
为什么
E * E
不是?因为
E -> E + E
是句型中最右推导逆过程的一步,若先归约E * E
,则不满足最右推导的要求
-
-
移进-归约分析
算法
- 若栈内没有符号,输入队列队头移入栈
- 若栈顶的串可以被文法的某个产生式归约,则进行归约
- 若栈顶的串无法被文法的某个产生式归约,输入队列队头移入栈
示例
- 考虑文法
E -> E + E E -> E * E E -> (E) E -> id
- 分析过程
输入中 i d i id_i idi 都表示 i d id id,只是用下标进行区分栈 输入 动作 $ id1 * id2 + id3 $ 移进 $ id1 * id2 + id3 $ 按 E -> id 归约 $ E * id2 + id3 $ 移进 $ E * id2 + id3 $ 移进 $ E * id2 + id3 $ 按 E -> id 归约 $ E * E + id3 $ 移进 $ E * E + id3 $ 移进 $ E * E + id3 $ 按 E -> id 归约 $ E * E + E $ 按 E -> E + E $ E * E $ 按 E -> E * E $ E $ ACCEPT
冲突
移进-归约冲突
-
考虑文法
E -> E + E E -> E + E + id E -> id
-
输入串为
id + id + id
栈 输入 动作 $ id + id + id $ 移进 $ id + id + id $ 按 E -> id 归约 $ E + id + id $ 移进 $ E + id + id $ 移进 $ E + id + id $ 按 E -> id 归约 $ E + E + id $ 冲突 对于栈
$ E + E
和输入+ id $
有两种选择,发生冲突- 对于栈顶串
E + E
,可以按E -> E + E
进行归约 - 输入
+ id
移入栈,先按E -> id
归约,再按E -> E + E + id
归约
这种不确定性可能会导致某种选择分析失败
- 对于栈顶串
归约-归约冲突
- 归约冲突的例子不是很好举
- 假设有产生式
E
→
X
E \to X
E→X,
E
→
Y
X
E\to YX
E→YX
当栈顶是 Y X YX YX时,会有归约-归约冲突 - 假设有产生式
E
→
X
E \to X
E→X,
T
→
X
T \to X
T→X
当栈顶是 X X X 时,会有归约-归约冲突
LR 分析器
介绍
-
LR 分析器
- 第一个 L 表示从左到右扫描串
- 第二个 R 表示最右推导
-
LR 分析算法需要借助分析表,分析表的构建方式有多种,但是分析方法是一样的
LR 分析算法
- 考虑文法
E → E + T (1) E \to E + T \tag{1} E→E+T(1)
E → T (2) E \to T \tag{2} E→T(2)
T → T ∗ F (3) T \to T * F\tag{3} T→T∗F(3)
T → F (4) T \to F\tag{4} T→F(4)
F → ( E ) (5) F \to (E)\tag{5} F→(E)(5)
F → i d (6) F \to id\tag{6} F→id(6)
-
分析表
状态 id + * ( ) $ E T F 0 s5 s4 1 2 3 1 s6 acc 2 r2 s7 r2 r2 3 r4 r4 r4 r4 4 s5 s4 8 2 3 5 r6 r6 r6 r6 6 s5 s4 9 3 7 s5 s4 10 8 s6 s11 9 r1 s7 r1 r1 10 r3 r3 r3 r3 11 r5 r5 r5 r5 - 分析表中符号的含义
sj
:将当前符号和状态j
移入栈rj
:将栈顶串按第j
个产生式进行归约acc
:接收- 空白:拒绝输入串
- 分析表中符号的含义
-
对于输入串 i d 1 ∗ i d 2 + i d 3 id_1 * id_2 + id_3 id1∗id2+id3,LR 的非递归预测过程如下图
- a c t i o n [ 0 , i d ] = s 5 action[0, id] = s_5 action[0,id]=s5,即状态 0 (栈顶)对应的行,id (输入队列队头)对应的列的值,表示移进符号 i d id id,同时移入状态 5
活前缀
- 该前缀是某个产生式右部的前缀
- 活前缀是句柄的子集
- 示例
有产生式 A → B C D A \to BCD A→BCD
有串 B B B, B C BC BC, B C D BCD BCD 都是活前缀,而 B C D E BCDE BCDE 不是
构建分析表的前置知识
-
拓广文法
设文法的开始符号为
S
,添加S' -> S
-
为什么要引入拓广文法?
-
考虑文法:
E -> E + E E -> E * E E -> id
-
显然,该文法的开始符号为
E
,若成功分析输入串,则必然由某条产生式归约到E
但是,该文法有
3
条产生式都能够归约到E
,这样接收状态对应的产生式就不确定 -
若引入了
E' -> E
,则接收状态对应的产生式是唯一确定的
-
-
-
LR(0)
项目-
简称项目
-
在产生式的右侧的某个位置加
·
-
考虑产生式:
A -> XYZ
共有
4
个项目A -> ·XYZ A -> X·YZ A -> XY·Z A -> XYZ·
-
-
项目集
由多个项目组成的集合,一般用符号
I
表示 -
后继项目
- 后继指的是
·
的出现位置向右移动一位 - 考虑项目
A -> ·XY
- 该项目的后继项目为:
A -> X·Y
- 后继指的是
-
归约项目
若项目的
·
在产生式的最右侧,则称该项目为归约项目
例如A -> XY·
-
closure(I)
-
项目集
I
的闭包 -
构造规则
-
初始时,
I
中的每一个项目都加入closure(I)
-
若
I
中有形如A -> α·B
的项目,将所有B
的产生式B -> ·C
添加到closure(I)
若
C
为非终结符,则将C -> ·D
添加到closure(I)
有点类似求
FIrst
集合这是一个递归的过程
-
-
-
goto(I, X)
-
I
:项目集 -
X
:文法符号 -
对于项目集
I
中的每一个项目,输入文法符号X
后的产生项目-
能否输入符号
X
:项目中·
右侧的符号是否为X
-
考虑项目:
A -> ·BC
,输入符号C
无法产生任何项目 -
考虑项目:
A -> ·BC
,输入符号B
产生的项目为:B·C
-
-
-
项目集的规范族
- 是一个项目集:核心项目 + 非核心项目
- 核心项目:项目集的初始项目
- 非核心项目:由核心项目经过
colsure
闭包得到的项目 - 项目集的规范族内的所有项目,是等价的
项目集规范族求解
-
考虑文法
E' -> E E -> E + T E -> T T -> T * F T -> F F -> (E) F -> id
-
I0
表示初始项目集-
核心项目:
E' -> ·E
-
添加
closure({E' -> ·E})
E -> ·E + T E -> ·T T -> ·T * F T -> ·F F -> ·(E) F -> ·id
-
I0
最终为:E' -> ·E E -> ·E + T E -> ·T T -> ·T * F T -> ·F F -> ·(E) F -> ·id
-
-
I1
-
I0
可能的输入有:E, T, F, (, id
-
其中
E
尚未作为I0
的输入 -
I1
的核心项目为goto(I0, E)
,有:E' -> E· E -> E· + T
其他的产生式都无法输入
E
,例如E -> ·T
,只能输入T
-
核心项目的闭包为空
E' -> E·
,·
的右侧为空E -> E· + T
,·
的右侧为终结符+
-
I1
最终为:E' -> E· E -> E· + T
-
-
I2
-
I0
可能的输入有:E, T, F, (, id
-
其中
T
尚未作为I0
的输入 -
I2
的核心项目为goto(I0, T)
,有:E -> T· T -> T· * F
-
核心项目的闭包为空
-
I2
最终为:E -> T· T -> T· * F
-
-
I3
-
I0
可能的输入有:E, T, F, (, id
-
其中
F
尚未作为I0
的输入 -
I3
的核心项目为goto(I0, F)
,有:T -> F·
-
核心项目的闭包为空
-
I3
最终为:T -> F·
-
-
I4
-
I0
可能的输入有:E, T, F, (, id
-
其中
(
尚未作为I0
的输入 -
I4
的核心项目为goto(I0, ( )
,有:F -> (·E)
-
核心项目的闭包为:
E -> ·E + T E -> ·T T -> ·T * F T -> ·F F -> ·(E) F -> ·id
-
I4
最终为:F -> (·E) E -> ·E + T E -> ·T T -> ·T * F T -> ·F F -> ·(E) F -> ·id
-
-
I5
-
I0
可能的输入有:E, T, F, (, id
-
其中
id
尚未作为I0
的输入 -
I4
的核心项目为goto(I0, id)
,有:F -> id·
-
核心项目的闭包为空
-
I5
最终为:F -> id·
-
-
I6
-
I1
可能的输入有:+
-
其中
+
尚未作为I1
的输入 -
I6
的核心项目为goto(I1, +)
,有:E -> E +· T
-
核心项目的闭包为:
T -> ·T * F T -> ·F F -> ·(E) F -> ·id
-
-
接下来考虑
I2
可能的输入需要注意的是,若
I7
与求出的I
完全相同,那么视为无状态转换 -
若考虑完所有的
I
,都没有状态转换,那么结束 -
最终的项目集规范族
构建 SLR 预测分析表
-
根据前面提到的,预测分析表应该如图
-
根据构建的项目集规范族,可以画出一个状态转换图
-
项目集 I i I_i Ii
- 若 A → a ⋅ b A \to a·b A→a⋅b 在 I i I_i Ii 中,且 g o t o ( I i , b ) = I j goto(I_i, b) = I_j goto(Ii,b)=Ij,那么 a c t i o n [ i , a ] = s j action[i, a] = s_j action[i,a]=sj
- 若 A → a ⋅ A \to a · A→a⋅ 在 I i I_i Ii 中,那么 F o l l o w ( A ) Follow(A) Follow(A) 中的所有符号 k k k, a c t i o n [ i , k ] = r j action[i, k] = r_j action[i,k]=rj, j j j 是产生式 A → a A\to a A→a 的编号
- 若 S ′ → S S' \to S S′→S 在 I i I_i Ii 中,那么 a c t i o n [ i , $ ] = a c c action[i, \$] = acc action[i,$]=acc
- 若 A → a ⋅ B A \to a · B A→a⋅B 在 I i I_i Ii 中,并且 g o t o ( I i , B ) = j goto(I_i, B) = j goto(Ii,B)=j,那么 g o t o [ i , B ] = j goto[i, B] = j goto[i,B]=j
-
I0
输入 E 到I1
,E 是非终结符,因此I0
行, E 列,就应该填 1(从 0 转移到 1) -
I0
输入(
到I4
,I4
中没有归约状态,因此I0
行(
列,填 s 4 s_4 s4 -
I2
中有 E → T ⋅ E \to T· E→T⋅,因此 F o l l o w ( E ) = { + , ) , $ } Follow(E) = \{+, ), $ \} Follow(E)={+,),$} 对应的元素 k k k, a c t i o n [ 2 , k ] = r 2 action[2,k]=r_2 action[2,k]=r2,2 是 E → T E\to T E→T 的编号 -
状态转换图中,没有指向其他状态的状态,都是归约状态
-
-
最终的预测分析表
构建规范的 LR 分析表
引言
-
考虑文法
S -> V = E S -> E V -> * E V -> id E -> V
-
构建的 SLR 分析表中,
action[2, =] = s6
,且Folllow(E)
包含=
,使得action[2, =] = r5
那么状态
2
遇到=
有2
种动作,这种不确定性可能导致分析失败
-
-
移进归约冲突的原因
对于项目
A -> α·
,SLR 分析会将action[i, Follow(A)]
按照rj
进行归约,i
为当前项目集的编号,j
为A -> α
的编号- 但是观察分析树,第一层的
V = E
,E
只有遇到$
时,才会按照S -> V = E
进行归约 - 而
E
的Follow
集合中还有=
,按照SLR
分析action[i, =] = rj
,这就会导致不应该的归约 - 换言之,归约时后继符号集合只是
Follow
集合的子集
- 但是观察分析树,第一层的
LR(1) 项目
-
考虑文法
S -> V = E S -> E V -> * E V -> id E -> V
-
最右侧的叶子节点
V
归约成E
时,只有遇到$
才会进行E -> V·
归约(自下而上分析)- 将项目
E -> V·
改写为E -> V·,$
- 该项目表示,只有遇到
$
时,才能将栈顶的V
归约成E
- 同理有:
E -> v,=
- 将项目
-
规范 LR(1)项目定义为
A → α ⋅ β , a A \to α·β,a A→α⋅β,a-
a
:展望符(也叫搜索符)- 只有
β = ε
时,遇到a
才能按照A -> α
进行归约 - 当
β ≠ ε
时,a
没用
- 只有
-
等价 LR(1)项目
-
A → α ⋅ B β , a A \to \alpha · B \beta ,a A→α⋅Bβ,a
-
B → ⋅ γ , b B \to ·\gamma,b B→⋅γ,b
-
其中 b 为 B → γ B \to \gamma B→γ 的展望符
-
b = F i r s t ( β ) b = First(\beta ) b=First(β)
-
而 β \beta β 有可能是 ϵ \epsilon ϵ,因此 b = F i r s t ( β a ) b = First(\beta a) b=First(βa)
-
当 β → ϵ \beta \to \epsilon β→ϵ 时, b = a b=a b=a称为继承的后继符,否则叫自生的后继符
LR(1)项目集规范族
-
考虑文法
S ′ → S (0) S' \to S \tag{0} S′→S(0)S → L = R (1) S \to L = R \tag{1} S→L=R(1)
S → R (2) S \to R \tag{2} S→R(2)
L → ∗ R (3) L \to * R \tag{3} L→∗R(3)
L → i d (4) L \to id \tag{4} L→id(4)
R → L (5) R \to L \tag{5} R→L(5)
-
I0
S ′ → ⋅ S , $ S' \to ·S,\$ S′→⋅S,$S 后为空串,那么由 S 推出的产生式中,展望符继承 S ′ → ⋅ S , $ 的展望符 S 后为空串,那么由 S 推出的产生式中,展望符继承 S' \to ·S,\$ 的展望符 S后为空串,那么由S推出的产生式中,展望符继承S′→⋅S,$的展望符
S → ⋅ L = R , $ S \to ·L = R,\$ S→⋅L=R,$
S → ⋅ R , $ S \to ·R,\$ S→⋅R,$
S → ⋅ L = R , $ 中, ⋅ 后为 L ,而 L 后的第一个终结符为 = ,所以由 L 推出的产生式,展望符为 = S \to ·L = R,\$中,·后为L,而L后的第一个终结符为=,所以由 L 推出的产生式,展望符为 = S→⋅L=R,$中,⋅后为L,而L后的第一个终结符为=,所以由L推出的产生式,展望符为=
L → ⋅ ∗ R , = L \to ·*R,= L→⋅∗R,=
L → ⋅ i d , = L \to ·id,= L→⋅id,=
R 后为空串,由 R 推出的产生式中,展望符继承 S → ⋅ R , $ 的展望符 R后为空串,由R推出的产生式中,展望符继承 S \to ·R,\$ 的展望符 R后为空串,由R推出的产生式中,展望符继承S→⋅R,$的展望符
R → ⋅ L , $ R \to ·L,\$ R→⋅L,$
L 后为空串,由 L 推出的产生式中,展望符继承 R → ⋅ L , $ 的展望符 L后为空串,由L推出的产生式中,展望符继承R \to ·L,\$的展望符 L后为空串,由L推出的产生式中,展望符继承R→⋅L,$的展望符
L → ⋅ ∗ R , $ L \to ·*R,\$ L→⋅∗R,$
L → ⋅ i d , $ L \to ·id,\$ L→⋅id,$
-
I1
I0
输入S
S ′ → S ⋅ , $ S' \to S·,\$ S′→S⋅,$ -
I2
I0
输入L
S -> ·L=R,$ R -> ·L,$
⋅ 移动一位称为后继项目,后继项目的展望符继承自原项目 ·移动一位称为后继项目,后继项目的展望符继承自原项目 ⋅移动一位称为后继项目,后继项目的展望符继承自原项目
S → L ⋅ = R , $ S \to L·=R,\$ S→L⋅=R,$
R → L ⋅ , $ R \to L·,\$ R→L⋅,$
-
I3
I0
输入R
S → R ⋅ , $ S \to R·,\$ S→R⋅,$ -
I4
I0
输入*
L → ∗ ⋅ R , = L \to *·R,= L→∗⋅R,=L → ∗ ⋅ R , $ L \to *·R,\$ L→∗⋅R,$
R 后是 $ , R 推出的产生式的展望符继承 L → ∗ ⋅ R , = 的展望符 R后是\$,R推出的产生式的展望符继承L \to *·R,= 的展望符 R后是$,R推出的产生式的展望符继承L→∗⋅R,=的展望符
R → ⋅ L , = R \to ·L,= R→⋅L,=
R 后是 $ , R 推出的产生式的展望符继承 L → ∗ ⋅ R , $ 的展望符 R 后是 \$,R推出的产生式的展望符继承L \to *·R,\$的展望符 R后是$,R推出的产生式的展望符继承L→∗⋅R,$的展望符
R → ⋅ L , $ R \to·L,\$ R→⋅L,$
L 后面是空串, L 推出的产生式继承 R → ⋅ L , = 、 R → ⋅ L , $ 的展望符 L 后面是空串,L 推出的产生式继承 R \to ·L,=、R \to ·L,\$ 的展望符 L后面是空串,L推出的产生式继承R→⋅L,=、R→⋅L,$的展望符
L → ⋅ ∗ R , = L \to ·*R,= L→⋅∗R,=
L → ⋅ i d , = L \to ·id,= L→⋅id,=
L → ⋅ ∗ R , $ L \to ·*R,\$ L→⋅∗R,$
L → ⋅ i d , $ L \to·id,\$ L→⋅id,$
-
I5
I0
输入id
L → ⋅ i d , = L \to ·id,= L→⋅id,=L → ⋅ i d , $ L \to·id,\$ L→⋅id,$
-
I6
I2
输入=
S → L = ⋅ R , $ S \to L=·R,\$ S→L=⋅R,$R 后面是空串, R 推出的产生式继承 S → L = ⋅ R , $ 的展望符 R 后面是空串,R 推出的产生式继承 S \to L=·R,\$ 的展望符 R后面是空串,R推出的产生式继承S→L=⋅R,$的展望符
R → ⋅ L , $ R \to ·L,\$ R→⋅L,$
L 后面是空串, L 推出的产生式继承 R → ⋅ L , $ 的展望符 L后面是空串,L推出的产生式继承R \to ·L,\$的展望符 L后面是空串,L推出的产生式继承R→⋅L,$的展望符
L → ⋅ ∗ R , $ L \to ·*R,\$ L→⋅∗R,$
L → ⋅ i d , $ L \to ·id,\$ L→⋅id,$
-
I7
I4
输入R
L → ∗ R ⋅ , = L \to *R·,= L→∗R⋅,=L → ∗ R ⋅ , $ L \to *R·,\$ L→∗R⋅,$
-
I8
I4
输入L
R → L ⋅ , = R \to L·,= R→L⋅,=R → L ⋅ , $ R \to L·,\$ R→L⋅,$
-
I9
I6
输入R
S → L = R ⋅ , $ S \to L=R·,\$ S→L=R⋅,$ -
I10
I6
输入L
R → L ⋅ , $ R \to L·,\$ R→L⋅,$ -
I11
I6
输入*
L → ∗ ⋅ R , $ L \to *·R,\$ L→∗⋅R,$R → ⋅ L , $ R \to ·L,\$ R→⋅L,$
L → ⋅ ∗ R , $ L \to ·*R,\$ L→⋅∗R,$
L → ⋅ i d , $ L \to ·id,\$ L→⋅id,$
-
I12
I6
输入id
L → i d ⋅ , $ L \to id·,\$ L→id⋅,$ -
I13
I11
输入R
L → ∗ R ⋅ , $ L \to *R·,\$ L→∗R⋅,$
求解项目集规范族(手写)
- 考虑文法
S' -> S S -> BB B -> bB B -> a
构建预测分析表
- 根据项目集规范族,得到状态转换图
- 状态转换图中,没有指向其他状态的状态,都是归约状态
- 预测分析表
构建 LALR 分析表
-
考虑文法
S' -> S S -> BB B -> bB | a
-
规范 LR 的项目集规范族,得到状态转换图
-
考虑两个相似的项目集合
B → a ⋅ , b ∣ a (I4) B \to a·,b|a \tag{I4} B→a⋅,b∣a(I4)B → a ⋅ , $ (I7) B \to a·,\$ \tag{I7} B→a⋅,$(I7)
这两个项目集合除了展望符(搜索符)不同以外,其余都是相同的
- 只有展望符不同的项目称为:同心项目
-
可以将同行项目进行合并,例如合并
I4
和I7
,命名为I47
B → a ⋅ , b ∣ a ∣ $ B \to a·,b|a|\$ B→a⋅,b∣a∣$
转换图中,原先到达I4
或I7
的,现在都转移到I47
-
找到所有的同心集
-
对同心集进行合并
-
状态转换图中,没有指向其他状态的状态,都是归约状态
-
构建预测分析表
-
冲突
- 同心集的合并不会引起新的
移进-归约
冲突 - 同心集的合并有可能产生新的
归约-归约
冲突
- 同心集的合并不会引起新的
4.语法制导翻译
回顾概念
-
编译的几个阶段
- 词法分析
- 语法分析
- 语义分析
- 中间代码生成
- 代码优化
- 目标代码生成
语义分析
和中间代码生成
可以同时进行,称为语义翻译语法分析
、语义分析
、中间代码生成
可以同时进行,称为语法制导翻译
-
句子:属于该语言的串
-
语法分析树
-
考虑文法
E -> E + T E -> id T -> id
-
分析句子 i d + i d id + id id+id
语法分析树是针对分析过程的,语法树是针对串的
-
概念
-
SDD
语法制导定义(
Syntax-Directed Definition
) -
语法制导翻译
仅用语法分析对源代码进行翻译
-
语法制导
带属性和语义规则的上下文无关文法
-
每个文法符号都有一组属性
例如文法符号
S
,S
可能有name
,val
属性,等等。这些属性名(
name
、val
,等等)都是自定义的 -
每个产生式有一组语义规则
例如产生式
E -> E + T
-
这样的产生式只是符号的推导,没有任何意义,并且
+
只是个符号,不一定是数学中的加法 -
但是如果定义一组语义规则,例如
E.val = E.val + T.val
那么这个产生式的语义就是:将 E 的 val 属性和 T 的 val 属性相加,再赋值给 E 的 val 属性
-
-
-
综合属性
-
在语义规则中,产生式左侧的属性是由产生式右侧的属性通过某种计算得到的
-
体现在语法分析树中,就是产生式左侧符号的属性,只能通过子节点或自身经过某种计算得到
E 1 E_1 E1 和 E E E 是同一个东西,只是用 E 1 E_1 E1 来区分计算顺序
-
-
继承属性
- 在语义规则中,产生式右侧的属性是由该符号左侧的符号通过某种计算得到的
- 在语法分析树中,产生式右侧符号的属性,只能通过父节点、兄弟节点、本身 经过某种计算得到
所以in
是L
的继承属性
-
定义终结符的属性为:综合属性
终结符是无法作为产生式左侧符号的,终结符的值由词法分析器提供
-
判断一个属性是继承属性还是综合属性
只要看这个属性来自产生式左侧还是右侧即可,来自左侧是综合,来自右侧是继承
-
副作用
语义规则不是表达式,而是一些其他功能,例如打印
-
属性文法
一个没有副作用的文法称为属性文法
-
注释分析树:每个节点的属性值都标出出来
例如:
-
如果一条语义规则的作用和求值无关,如打印一个值或向符号表中添加记录,则成为产生式左侧非终结符的虚拟综合属性
-
语法树
语法分析树是针对分析过程的,语法树是针对串的
SDD 示例
只有综合属性的文法
- 考虑 SDD
- 分析串
带有继承属性的文法
-
考虑 SDD
-
addtype(param1, param2)
- 创建一个属性
param1
,属性值为param2
- 是
L
(产生式左侧)的虚拟综合属性
- 创建一个属性
-
-
分析串
(4)
是一个继承属性
L
使用到了自身、子节点id
属性依赖图
引入
-
考虑 SDD
-
分析串 i n t i d 1 , i d 2 , i d 3 int \quad id_1, id_2, id_3 intid1,id2,id3
-
如果只看语法分析树,会发现,分析过程是完全自上而下的
-
但是如果考虑属性值,则并非如此
-
自下而上
T -> int
,将int
(integer
) 赋值给了T.type
-
兄弟节点
D -> TL
,T.type
赋值给L.in
,那么就要求T.type
先计算出来 -
自上而下
L -> L1 , id
,将L.in
赋值给L1.in
,那么就要求L1.in
先计算出来
-
-
-
分析树上属性的计算有依赖关系,可以用依赖图(有向图)来表示
-
依赖图本质上是一个拓扑图
示例
- 考虑 SDD
- 注释分析树
- 属性依赖图
-
综合属性写在符号的右边
-
继承属性写在符号的左边
-
属性的编号,从小到达,表示先后顺序
-
顺序并不是固定的
-
例如
1, 2, 3, 4
是没有依赖关系的,因此可以任意替换顺序 -
4, 5
显然不能替换顺序,因为L.in = T.type
,5
是依赖4
的 -
6
(addType(id.entry, L.in)
)是有多个依赖项的前面提到,这种非表达式的属性,称为副作用,也叫虚拟综合属性
addType
这个操作需要id.entry
和L.in
,因此6
是依赖3
和5
的
-
-
S 属性 与 L 属性
-
S 属性定义
-
仅使用综合属性的 SDD
-
如果一个 SDD 是 S 属性的,那么可以根据语法树的任何 自底向上 顺序来计算属性值
-
-
L 属性定义
-
依赖图的边可以从左到右,但是不能从右到左
-
若一个SDD是L属性定义的,那么它的每个属性有 2 种情况
-
是综合属性
-
对于产生式 A → X 1 X 2 . . . X i X i + 1 . . . A \to X_1X_2...X_iX_{i+1}... A→X1X2...XiXi+1...
其中Xi
的继承属性只依赖A
的继承属性或X1 ~ Xi
,并且Xi
的全部属性不能在依赖图中形成环- 若可以依赖 A 的综合属性,那么 A 有可能依赖 Xi 的继承属性,若 Xi 的继承属性依赖 A 的综合属性,就会成环
- 若可以依赖 A 的综合属性,那么 A 有可能依赖 Xi 的继承属性,若 Xi 的继承属性依赖 A 的综合属性,就会成环
-
-
-
每个 S 属性定义都是 L 属性定义
综合属性定义,产生式左侧的依赖产生式右侧的属性,也就是依赖图的边从左到右
翻译方案
介绍
-
语法制导翻译
SDT
(Semantic Directed Translation) -
翻译方案中,将语义规则改了个名字,叫
语义动作
将语义动作写在
{}
内,并插入到产生式右侧 -
对于综合属性,语义动作插入到产生式最右侧
-
对于继承属性,语义动作插入到该符号的左侧
示例
- 考虑 SDD
- 翻译方案
-
S
→
B
S \to B
S→B 对应的翻译方案是
S
→
{
B
.
p
s
=
10
}
B
{
S
.
h
t
=
B
.
h
t
}
S \to \{B.ps=10\}B\{S.ht=B.ht\}
S→{B.ps=10}B{S.ht=B.ht}
B.ps
是继承属性,因此要写在 B 的左侧,而S.ht
是综合属性,要写在产生式的最右侧- 注意到图片中翻译方案有换行,和写道一行是同一个意思
- 翻译方案
自下而上计算
代码段翻译
-
语法制导翻译中,不仅要分析输入串,分析过程中,还要执行相应的语义动作。语义动作中,一般会设计属性的计算,因此属性也需要保存在栈中
-
示例
- 考虑产生式 E = E 1 + T E = E_1 + T E=E1+T,该产生式的语义规则是 E . v a l = E 1 . v a l + T . v a l E.val = E_1.val + T.val E.val=E1.val+T.val
- 假设栈中有
E
1
+
T
E_1 + T
E1+T,那么应该是这样
- 要保存属性,由于属性只有一个 val,可以简化一下,属性栈就是 val 栈
那么,当 E 1 + T E_1 + T E1+T 归约成 E E E 时, E . v a l = s t a c k [ t o p ] . v a l + s t a c k [ t o p − 2 ] . v a l E.val = stack[top].val + stack[top-2].val E.val=stack[top].val+stack[top−2].val
其中, s t a c k [ t o p ] stack[top] stack[top] 就是 T,而 s t a c k [ t o p − 2 ] stack[top-2] stack[top−2] 就是 E 1 E_1 E1
-
形如 E → T E \to T E→T 的产生式,语义规则只要不是副作用,代码段可以省略
因为 E . v a l = T . v a l E.val = T.val E.val=T.val 这种语义规则,翻译成代码段就是 s t a c k [ t o p ] . v a l = s t a c k [ t o p ] . v a l stack[top].val = stack[top].val stack[top].val=stack[top].val,没有改变值,因此可以忽略 -
示例
- 考虑SDD
- 翻译代码段
- 考虑SDD
S属性的自下而上计算
-
考虑SDD
-
翻译代码段
-
S 属性自下而上计算,需要用到 LR 分析器,这里选用 SLR
进行 SLR 分析后,预测表如图
-
与 LR 分析不同的是,S 属性定义的计算中,多出了状态栈
但由于 SDD 中,只有一个 val 属性,因此将状态栈简化为 val 栈
-
分析输入串 d i g i t + d i g i t digit + digit digit+digit,其中词法分析器的输入为 1 , 2 1, 2 1,2,即 1 + 2 1 + 2 1+2
-
对于每一个移进状态,将输入串同时移入 stack 栈和 val 栈
-
对于每一个归约状态
考虑代码段,代码段主要对应 val 栈
- 若是
E
→
T
E \to T
E→T 这类无对应代码段的产生式
- stack 栈弹出右侧符号数量的 2 倍,考虑栈顶状态对产生式左侧符号的 goto,将产生式左侧移入 stack,并将 goto 移入 stack
- 预测分析过程的第二行,根据分析表,状态 1 对应的行, + 对应的列 的条目值是 r 7 r_7 r7,即根据 F → d i g i t F \to digit F→digit 进行归约
- 此时栈中有元素 0 d i g i t 1 0 \quad digit \quad1 0digit1,弹出 F → d i g i t F \to digit F→digit 符号数量的 2 倍( l e n ( d i g i t ) × 2 = 1 × 2 len(digit) \times 2 = 1 \times 2 len(digit)×2=1×2),并且 F F F 入栈,栈中还剩下 0 F 0 \quad F 0F
- g o t o ( 0 , F ) = 4 goto(0, F) = 4 goto(0,F)=4,因此 4 4 4 入栈,栈中剩下 0 F 4 0 \quad F \quad 4 0F4 (分析过程的第三行)
- val stack 则不需要任何操作,因为没有代码段
- stack 栈弹出右侧符号数量的 2 倍,考虑栈顶状态对产生式左侧符号的 goto,将产生式左侧移入 stack,并将 goto 移入 stack
- 若是
E
→
E
+
T
E \to E + T
E→E+T 这类有代码段的产生式
- 解析对应的代码段: v a l [ t o p − 2 ] = v a l [ t o p ] + v a l [ t o p − 2 ] val[top-2] = val[top] + val[top-2] val[top−2]=val[top]+val[top−2], r e s u l t = v a l [ t o p − 2 ] result = val[top-2] result=val[top−2]
- 弹出 val 栈中产生式右侧符号数量的 1 倍,并将
result
入栈
- 若是
E
→
T
E \to T
E→T 这类无对应代码段的产生式
L 属性的自上而下计算
-
考虑 SDD
-
SDD中存在左递归(如 E → E + T E \to E + T E→E+T 出现了左递归),而自上而下分析处理不了左递归,因此先消去左递归
L -> En E -> TR R -> +TR R -> epsilon T -> FM M -> *FM M -> epsilon F -> (E) F -> digit
-
语法制导翻译方案 SDT
L -> En {print(E.val)} E -> T {R.i = T.val} R {E.val = R.s} R -> +T {R1.i = R.i + T.val} R {R.s = R1.i} R -> epsilon {R.s = R.i} T -> F {M.i = F.val} M {T.val = M.s} M -> *F {M1.i = M.i * F.val} M {M.s = M1.i} M -> epsilon {M.s = M.i} F -> (E) {F.val = E.val} F -> digit {F.val = digit.lexval}
- R.i:R之前的表达式的值
- R.s:分析完 R 后的值
- M 同理
-
求解 First, Follow 集合
符号 First Follow L (, digit $ E (, digit $, ) R +, epsilon $, ) T (, digit $, ), + M *, epsilon $, ), + F (, digit $, ), +, * -
构建 LL(1) 分析表(非终结符的
select
集合)( ) + * digit $ L L->E L->E E E->TR E->TR R R->epsilon R->+TR R->epsilon T T->FM T->FM M M->epsilon M->epsilon M->*FM M->epsilon F F->(E) F->digit -
遍历每一条产生式 A → a A \to a A→a
-
a 是 epsilon
Follow(A) 中的每一个符号对应的列,添加产生式
-
a 不是 epsilon
First(a) 中的每一个符号对应的列,添加产生式
-
-
-
分析串 d i g i t + d i g i t digit + digit digit+digit,词法分析器的输入是 1, 2
-
LL(1) 分析,用非递归的栈,过程更清晰
-
由于该文法只有一种属性 val,因此属性栈可以默认为 val 栈
-
$ 的一侧为栈底
-
-
分析过程
栈 输入 动作 L$ digit + digit$ L -> E {print(E.val)} E {print(E.val)} $ digit + digit$ E -> T {R.i = T.val} R {E.val = R.s} T {R.i = T.val} R {E.val = R.s} {print(E.val)} $ digit + digit$ T -> F {M.i = F.val} M {T.val = M.s} F {M.i = F.val} M {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ digit + digit$ F -> digit {F.val = digit.lexval} digit {F.val = digit.lexval} {M.i = F.val} M {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ digit.lexval=1 digit + digit$ 匹配 digit {F.val = digit.lexval} {M.i = F.val} M {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ F.val=1 + digit$ 执行 {F.val = digit.lexval} {M.i = F.val} M {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ M.i=1 + digit$ 执行 {M.i = F.val} M {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ + digit$ M -> epsilon {M.s = M.i} epsilon {M.s = M.i} {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ + digit$ 匹配 epsilon {M.s = M.i} {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ + digit$ 执行 {M.s = M.i} {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ M.s=1 + digit$ 执行 {T.val = M.s} {R.i = T.val} R {E.val = R.s} {print(E.val)} $ T.val=1 + digit$ 执行 {R.i = T.val} R {E.val = R.s} {print(E.val)} $ R.i=1 + digit$ R -> +T {R1.i = R.i + T.val} R {R.s = R1.i} +T {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ + digit$ 匹配 + T {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ digit$ T -> F {M.i = F.val} M {T.val = M.s} F {M.i = F.val} M {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ digit$ F -> digit {F.val = digit.lexval} digit {F.val = digit.lexval} {M.i = F.val} M {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ digit$ 匹配 digit {F.val = digit.lexval} {M.i = F.val} M {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ $ 执行 {F.val = digit.lexval} {M.i = F.val} M {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ F.val=2 $ 执行 {M.i = F.val} M {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ M.i=2 $ M -> epsilon {M.s = M.i} epsilon {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ M.s=2 $ 匹配 epsilon {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ $ 执行 {T.val = M.s} {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ T.val=2 $ 执行 {R1.i = R.i + T.val} R {R.s = R1.i} {E.val = R.s} {print(E.val)} $ R1.i=1+2=3 $ R -> epsilon {R.s = R.i} epsilon {R.s = R.i} {R.s = R1.i} {E.val = R.s} {print(E.val)} $ R.s=3 $ 匹配 esilon {R.s = R.i} {R.s = R1.i} {E.val = R.s} {print(E.val)} $ $ 执行 {R.s = R.i} {R.s = R1.i} {E.val = R.s} {print(E.val)} $ R.s = 3 $ 执行 {R.s = R1.i} {E.val = R.s} {print(E.val)} $ R.s = 3 $ 执行 {E.val = R.s} {print(E.val)} $ E.val=3 $ 执行 {print(E.val)} $ $ 匹配 $ ACC
5. 中间代码生成
中间语言
后缀表示
-
若 E 是变量或常数,那么 E 的后缀表示就是本身
-
若 E 是形如 E 1 o p E 2 E_1 op E_2 E1opE2 的表达式,那么后缀表示是 E 1 ′ E 2 ′ o p E_1' E_2' op E1′E2′op
- o p op op 是二元运算符
- E 1 ′ E_1' E1′ 是 E 1 E_1 E1 的后缀表示
- E 2 ′ E_2' E2′ 是 E 2 E_2 E2 的后缀表示
-
若 E 是形如 ( E 1 ) (E_1) (E1)的表达式,那么后缀表达式是 E 1 E_1 E1 的后缀表达式
-
示例
- 1 1 1 的后缀表示是 1 1 1
- 1 + 2 1 + 2 1+2 的后缀表示是 12 + 12+ 12+
- ( 1 + 2 ) − 3 (1+2)-3 (1+2)−3的后缀表示是 12 + 3 − 12+3- 12+3−
图形表示
- 赋值语句
a
=
(
−
b
+
c
∗
d
)
+
c
∗
d
a = (-b+c*d)+c*d
a=(−b+c∗d)+c∗d 的语法树(不是语法分析树)如图
assgn
就是等号
语法分析树是针对分析过程的,语法树是针对串的
- 还有一种图形表示就是有向无环图
DAG
(D
irectedA
cyclicG
raph)
- DAG 和语法树很相似,区别在于公共子表达式,DAG 会用线直接指向(语法树中会画出整个表达式)
- DAG 和语法树很相似,区别在于公共子表达式,DAG 会用线直接指向(语法树中会画出整个表达式)
三地址代码
介绍
- 三地址代码的一般形式: x = y o p z x = y \quad op \quad z x=yopz
- 之所以叫三地址代码,是因为指令包含两个运算对象的地址和一个结果的地址
- 示例
表达式 x + y ∗ z x +y*z x+y∗z 翻译成三地址指令序列
t 1 , t 2 t_1,t_2 t1,t2 是编译器产生的临时变量(的名字),用于保存中间结果。t1 = y * z t2 = x + t1
图形表示与三地址代码
- 考虑表达式 a = ( − b + c ∗ d ) + c ∗ d a = (-b+c*d)+c*d a=(−b+c∗d)+c∗d
- 语法树
- 语法树的三地址代码
t1 = -b t2 = c * d t3 = t1 + t2 t4 = c * d t5 = t3 + t4 a = t5
- 语法树的三地址代码
- DAG
- DAG的三地址代码
DAG的三地址代码更简洁一些t1 = -b t2 = c * d t3 = t1 + t2 t4 = t3 + t2 a = t4
- DAG的三地址代码
6. 代码生成
指令代价计算
常见指令
- 二地址指令
- 形式:
o
p
s
o
u
r
c
e
t
a
r
g
e
t
op \quad source \quad target
opsourcetarget
op
:操作码source
:源地址(from)target
:目标地址(to)
MOV a b
:将 a 地址的内容,放到 b 地址中
ADD a b
:将 a 地址的内容加到 b 地址中SUB a b
:将 b 地址的内容减去 a 地址的内容
- 形式:
o
p
s
o
u
r
c
e
t
a
r
g
e
t
op \quad source \quad target
opsourcetarget
地址模式和代价
-
c
表示常数 -
contents(a)
:a 地址中的内容 -
间接寄存器:寄存器中存的内容,是真正内容的地址
-
直接量:也叫立即数,就是常数
指令代价计算
-
1(每条指令的代价) + 源地址(source)模式的附加代价 + 目的地址(target)模式的附加代价
-
示例
MOV R0 R1
把寄存器(Register)R0
的内容写到寄存器R1
,由于寄存器的附加代价是0,并且目标地址和源地址都是寄存器,指令本身代价为 1,因此这条指令的代价是:1MOV R5 M
把寄存器R5
的内容写到M
,即内存中(Memory),其中源地址是寄存器,附加代价为 0,而目的地址为内存(绝对地址),附加代价为 1,指令本身代价为 1,因此这条指令的代价是:2ADD #1 R3
把常数 1 加到寄存器R3
中,常数的附加代价是 1,寄存器的附加代价是 0,指令本身的代价是 1,因此这条指令的代价是:2SUB 4(R0) *12(R1)
将contetns( contens( 12 + contens(R1) ) )
减去contents( 4 + contens(R0) )
由于c(R)
和*c(R)
的附加代价都是 1,指令本身代价为 1,因此这条指令的代价是:3
-
相同表达式的不同指令
- 考虑 a = b + c a = b + c a=b+c
a, b, c
都存在内存中- 指令序列 1
代价是 6MOV b R0 ADD c R0 MOV R0 a
- 指令序列 2
代价是 6MOV b a ADD c a
- 指令序列 3
假设R0, R1, R2
分别存放a, b, c
的地址
代价是 2MOV *R1 *R0 ADD *R2 *R0
- 指令序列 4
假设R1, R2
存放b, c
的值
代价是 3ADD R2 R1 MOV R1 a