赞
踩
编译原理-陈意云-第三版
哈工大编译原理 MOOC
词法分析 → 语法分析 → 语义分析 → 中间代码生成 → 代码优化 → 目标代码生成 词法分析\to语法分析\to语义分析\to中间代码生成\to代码优化\to目标代码生成 词法分析→语法分析→语义分析→中间代码生成→代码优化→目标代码生成
术语 | 含义 |
---|---|
字母表 | 符号的有限集合 |
串 | 符号的有穷序列 |
空串 | 长度为0的串 |
语言 | 字母表上的串集 |
句子、字 | 属于该语言的串 |
连接 | x和y都是串,将y加到x后面形成的串,称为x和y的连接 |
r
表示一个语言 L(r)
ε
)是正规式,表示 {ε}
Finite Automaton
,有限自动机,简称 FA
有限自动机由以下几个要素组成:
示例
直接看概念过于抽象,这里提供一个简单的示例
假设有一个自动机:能够判断输入的2
位数字,是否每一位都大于5,那么这个自动机应该长这样
start
是开始状态,是、不是
是结束状态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 |
aab
Deterministic Finite Automaton
,确定性有限自动机,简称 DFA
FA
中,存在某个状态,输入符号后,转移后的状态只有一个,那么这个 FA
就是 DFA
epsilon-closure(T)
T
,经过有限次的epsilon
(ε),可以到达的状态集合状态 | 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 |
move(T, a)
状态 | 输入符号 | 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;
}
}
move(A, a)
,其中只有状态 2、7 可以接收 amove(A, b)
move(B,a)
move(B,b)
move(C, a)
move(C,b)
move(D,a)
move(D,b)
move(E, a)
和 move(E, b)
不会再产生新的集合0
,而 E 中包含结束状态10
接收状态子集Ⅰ
非接收状态子集Ⅱ
// 将 Ⅰ、Ⅱ 的并集称为 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}
由于 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
识别
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 后面不会再有其他符号
产生式
A
→
a
b
A \to ab
A→ab
文法
文法由一个或多个推导构成
E -> A
A -> ab
A -> ε
非终结符
考虑推导
A
→
a
b
A \to ab
A→ab,符号
A
A
A 可以推导出其他字符,那么
A
A
A 被称为非终结符
在文法中,出现在产生式左侧的,都是非终结符
expression
的缩写终结符
在文法中,没有在产生式左侧出现过的符号,都是终结符
顾名思义,终结:到这个符号结束了,不能再继续推导了
E -> A
A -> ab
上下文无关文法
用 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
E -> (E)
E -> -E
E -> E + E
E -> id
E -> (E)
E -> E + E
E -> E * E
E -> id
E -> E * E
E -> id * E
E -> id * E + E
E -> id * id + E
E -> id * id + id
E -> E + E
E -> E * E + E
E -> id * E + E
E -> id * id + E
E -> id * id + id
直接左递归
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
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 -> Ba | b
B -> Ab
First(A)
考虑文法
E -> TE'
E' -> +TE' | epsilon
T -> FT'
T' -> *FT' | epsilon
F -> (E) | id
求解 First(E)
E -> TE'
First(T)
T -> FT'
First(F)
F -> (E) | id
(
加入
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}
注意到,求解 First 集合是一个递归的过程
该文法各终结符的 First 集合
非终结符 | First集合 |
---|---|
E | (,id |
E’ | +,epsilon |
T | (,id |
T’ | +,epsilon |
F | (,id |
Follow(A)
考虑文法
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)
Follow
集合),B 的后面是 C,而 C 可以转化为 a 或 d
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) 对输入串进行分析,是根据预测分析表进行的
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
栈 | 输入 | 动作 |
---|---|---|
$ | 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
归约这种不确定性可能会导致某种选择分析失败
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 的非递归预测过程如下图
拓广文法
设文法的开始符号为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
,都没有状态转换,那么结束
最终的项目集规范族
根据前面提到的,预测分析表应该如图
根据构建的项目集规范族,可以画出一个状态转换图
项目集 I i I_i Ii
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 的编号
状态转换图中,没有指向其他状态的状态,都是归约状态
最终的预测分析表
考虑文法
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
集合的子集考虑文法
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
没用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称为继承的后继符,否则叫自生的后继符
考虑文法
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
考虑文法
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
找到所有的同心集
对同心集进行合并
状态转换图中,没有指向其他状态的状态,都是归约状态
构建预测分析表
冲突
移进-归约
冲突归约-归约
冲突编译的几个阶段
语义分析
和中间代码生成
可以同时进行,称为语义翻译语法分析
、语义分析
、中间代码生成
可以同时进行,称为语法制导翻译句子:属于该语言的串
语法分析树
考虑文法
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
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
先计算出来
分析树上属性的计算有依赖关系,可以用依赖图(有向图)来表示
依赖图本质上是一个拓扑图
综合属性写在符号的右边
继承属性写在符号的左边
属性的编号,从小到达,表示先后顺序
顺序并不是固定的
例如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 属性定义
仅使用综合属性的 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
的全部属性不能在依赖图中形成环
每个 S 属性定义都是 L 属性定义
综合属性定义,产生式左侧的依赖产生式右侧的属性,也就是依赖图的边从左到右
语法制导翻译SDT
(Semantic Directed Translation)
翻译方案中,将语义规则改了个名字,叫语义动作
将语义动作写在{}
内,并插入到产生式右侧
对于综合属性,语义动作插入到产生式最右侧
对于继承属性,语义动作插入到该符号的左侧
B.ps
是继承属性,因此要写在 B 的左侧,而 S.ht
是综合属性,要写在产生式的最右侧语法制导翻译中,不仅要分析输入串,分析过程中,还要执行相应的语义动作。语义动作中,一般会设计属性的计算,因此属性也需要保存在栈中
示例
形如
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
翻译代码段
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 栈
result
入栈考虑 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 |
若 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
若 E 是形如 ( E 1 ) (E_1) (E1)的表达式,那么后缀表达式是 E 1 E_1 E1 的后缀表达式
示例
assgn
就是等号语法分析树是针对分析过程的,语法树是针对串的
DAG
(D
irected A
cyclic G
raph)t1 = y * z
t2 = x + t1
t1 = -b
t2 = c * d
t3 = t1 + t2
t4 = c * d
t5 = t3 + t4
a = t5
t1 = -b
t2 = c * d
t3 = t1 + t2
t4 = t3 + t2
a = t4
op
:操作码source
:源地址(from)target
:目标地址(to)MOV a b
:将 a 地址的内容,放到 b 地址中ADD a b
:将 a 地址的内容加到 b 地址中SUB a b
:将 b 地址的内容减去 a 地址的内容c
表示常数
contents(a)
:a 地址中的内容
间接寄存器:寄存器中存的内容,是真正内容的地址
直接量:也叫立即数,就是常数
1(每条指令的代价) + 源地址(source)模式的附加代价 + 目的地址(target)模式的附加代价
示例
MOV R0 R1
R0
的内容写到寄存器 R1
,由于寄存器的附加代价是0,并且目标地址和源地址都是寄存器,指令本身代价为 1,因此这条指令的代价是:1MOV R5 M
R5
的内容写到 M
,即内存中(Memory),其中源地址是寄存器,附加代价为 0,而目的地址为内存(绝对地址),附加代价为 1,指令本身代价为 1,因此这条指令的代价是:2ADD #1 R3
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
都存在内存中MOV b R0
ADD c R0
MOV R0 a
MOV b a
ADD c a
R0, R1, R2
分别存放 a, b, c
的地址MOV *R1 *R0
ADD *R2 *R0
R1, R2
存放 b, c
的值ADD R2 R1
MOV R1 a
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。