编译过程可分为前端和后端,描述一下前端和后端分别由哪些阶段组成?
前端:与源程序有关、与目标机无关的部分。词法分析、语法分析、语义分析与中间代码生成、与机器无关的代码优化
后端:与目标机有关的部分;与机器有关的代码优化、目标代码生成
解释器和编译器有什么区别?
解释器:边解释边执行;不断读取源程序的语句,解释语句,读取此语句需要的数据,根据执行结果读取下一条语句,继续解释执行,直到返回结果;类似于自然语言翻译的同声传译。
编译器:将源程序完整地转换成机器语言程序或汇编语言程序,然后再处理、执行的翻译程序;高级语言程序→汇编/机器语言程序;类似于自然语言翻译的通篇笔译。
给出文法的形式化定义
文法G为一个四元组:
G
=
(
V
,
T
,
S
,
P
)
G=(V,T,S,P)
G=(V,T,S,P)
其中 V 为非终结符集
T 为终结符集,且
V
∩
T
=
∅
V\cap T=\varnothing
V∩T=∅
S为文法开始符号
P 为产生式集合,包含形如
α
→
β
,
α
∈
V
,
β
∈
(
V
∪
T
)
∗
\alpha\rightarrow\beta,\alpha\in V,\beta\in(V\cup T)^*
α→β,α∈V,β∈(V∪T)∗ 的产生式
五种常见的代码优化方法
(机器无关优化)公共子表达式删除、复制传播、无用代码删除、代码外提、强度削弱
(机器相关优化)寄存器的利用、优化体系结构和存储策略、任务划分
代码优化 包括 控制流分析、数据流分析 和 变换三部分
局部性原理的概念
程序中的大部分运行时间都花费在较少的一部分代码中,而且只是涉及到一小部分数据
时间局部性:如果某个程序访问的内存位置有可能在很短的时间内被再次访问
空间局部性:如果被访问过的内存位置的邻近位置有可能在很短的时间内被再次访问
C语言(非嵌套语言) / Pascal(嵌套语言)的活动记录


编译程序总体结构
词法分析、语法分析、语法制导翻译(语义分析)和中间代码生成、代码优化、目标代码生成
① 词法分析:从左到右扫描组成源程序的字符串,并将其转换成单词串;同时查词法错误,进行符号表管理;input:字符串;output:token单词序列。
② 语法分析:将单词序列组成句子,构造分析树,指出语法错误,指导翻译;input:token序列;output:语法成分
③ 语法制导翻译 / 语义分析:分析由语法分析器识别出来的语法成分的语义。包括 获取标识符的属性、语义检查、子程序的静态绑定、子程序的静态绑定
④ 中间代码生成:简单规范、与机器无关、易于优化和转换
⑤ 代码优化:对中间代码进行优化处理,使程序运行能够尽量节省存储空间,更有效地利用机器资源,使得程序的运行速度更快,效率更高
⑥ 目标代码生成:为中间代码中出现的运算对象分配存储单元、寄存器等;将中间代码转换成目标机上的机器指令代码或汇编代码
〇 编译程序的八块腹肌 = 上述6点 + 错误处理 + 符号表管理
编译过程各阶段的错误处理
符号表的作用
① 协助进行语义检查和中间代码生成
② 在目标代码生成阶段,当需要为名字分配地址时,符号表中的信息将是地址分配的主要依据
③ 编译器用符号表来记录、收集和查找出现在源程序中的各种名字及其语义信息
符号表 是以名字为关键字来记录其信息的数据结构
支持的两个最基本操作是添加表项和查找表项,这两个操作必须是高效的
表格管理:管理各种符号表,查、写源程序中出现的符号和编译程序生成的符号,为编译的各个阶段提供信息
多遍扫描 与 单边扫描
多遍扫描:本遍扫描的结果作为下一遍扫描的输入,本遍扫描中得到的信息在下一遍扫描中也有效,容易获得更优化的程序;增加内存访问次数,可能增加外部存储的访问次数
单遍扫描:分析所需的信息可能目前尚未掌握,导致产生的目标程序难以达到最优
文法的分类
0型文法 —— 短语结构文法 PSG
1型文法 —— 上下文有关文法 CSG
2型文法 —— 上下文无关文法 CFG
3型文法 —— 正规文法 RG 包括 左线型文法、右线性文法
其中 0 > 1 > 2 > 3
短语、直接短语、句柄、素短语
短语:设有CFG G=(V,T,P,S),∃α, β, γ∈(V∪T)*,S
⇒
∗
\Rightarrow^*
⇒∗ γAβ,A
⇒
+
\Rightarrow^+
⇒+ α,则称α是句型γαβ的相对于变量A的短语
直接短语:如果有A
⇒
\Rightarrow
⇒α,则称α是句型γαβ的相对于变量A的直接短语
句柄:最左直接短语
素短语:至少包含一个终结符,除自身外不再包括其他含终结符的短语(算符优先分析算法c5-p57)
子树版解释 ↓
短语:一棵子树所有叶子从左到右排列
直接短语::只有父子两代的一棵子树,所有叶子从左到右排列
句柄:最左直接短语


二义性的定义
如果一个文法的句子存在两棵分析树,那么该句子是二义性的。
如果一个文法包含二义性的句子,则称这个文法是二义性的;否则,该文法是无二义性的
单词的表示
单词 = { 种别 + 属性值}
单词名称 + 类别编码 + 单词值 (c3-page5)
确定的有穷自动机的形式定义
确定有穷自动机M是一个五元组
M
=
(
S
,
∑
,
f
,
S
0
,
F
)
M=(S, \sum, f, S_0, F)
M=(S,∑,f,S0,F)
S
S
S:有穷状态集合
∑
\sum
∑:输入符号字母表(有穷)
f
f
f:状态转换函数,
f
(
s
,
a
)
=
s
′
f(s, a)=s'
f(s,a)=s′表示当前状态
s
s
s,输入字符为
a
a
a,将状态转换到下一状态
s
′
s'
s′
S
0
S_0
S0:
S
0
∈
S
S_0 \in S
S0∈S,是唯一的初态
F
F
F:终止状态集合,
F
⊆
Q
F\subseteq Q
F⊆Q

简单优先文法 与 算符优先文法
简单优先文法:如果各文法符号之间的优先关系互不冲突(即至多存在一种优先关系),则可识别任意句型的句柄
算符优先文法:仅对文法中可能在句型中相邻的终结符定义优先关系,并且各终结符对之间的优先关系互不冲突
最左终结符集 和 最右终结符集
设G =(V,T,P,S)为算符优先文法,则定义
最左终结符集:FIRSTOP(A)={b|A
⇒
+
\Rightarrow ^+
⇒+b…或者A
⇒
+
\Rightarrow ^+
⇒+Bb…, b∈T, B ∈V}
最右终结符集:LASTOP(A)={b|A
⇒
+
\Rightarrow ^+
⇒+…b或者A
⇒
+
\Rightarrow ^+
⇒+…bB, b∈T, B ∈V} (c5-p42)
语义分析器
检查各个语法结构的静态语义,即验证语法正确的程序结构是否真正有意义,也称为静态语义分析或静态检查
类型检查:操作数和操作符的类型是否相容
控制流检查:控制流转向目标地址是否合法
唯一性检查:对象是否被重复定义
关联名检查:同一名字多次特定出现是否一致
语法制导翻译
将静态检查和中间代码生成结合到语法分析中进行的技术
属性分类
综合属性:节点的属性值是通过分析树中该节点或其子节点的属性值计算出来的
继承属性:节点的属性值是由该节点、其兄弟节点或父节点的属性值计算出来的
固有属性:通过词法分析直接得到的属性
S属性 与 L属性
S-属性:只含综合属性的语法制导定义称为S-属性定义;可以按照自下而上的顺序来计算分析树中节点的属性
L-属性:一个语法制导定义被称为L-属性定义,当且仅当它的每个属性是综合属性,或是满足条件的继承属性(产生式A→X1X2…Xi…Xn右部的符号Xi的继承属性 只依赖于 左部A的继承属性 或 右部Xi左侧符号X1~Xi-1的综合属性 或 Xi本身的综合属性或继承属性,但Xi属性不能在依赖图中形成回路)
结构等价 与 名字等价
结构等价:当且仅当下列条件之一成立时,称两个类型T1和T2是结构等价的:
① T1和T2是相同的基本类型
② T1和T2是将同一类型构造符应用于结构等价的类型上形成的
③ T1是表示T2的类型名
名字等价:如果将类型名看作只代表自己,则上述条件中的前两个将导致类型表达式的名字等价
两个类型表达式名字等价 当且仅当 它们完全相同
程序块满足最近嵌套原则
内层程序块中的局部变量只有全部处理完成之后才进入外层块
一旦进入外层程序块,内层块的局部变量就不会再使用了,可以从符号表中将这些符号删除
若用队列实现,符号表中最前面的符号一定是当前正在处理的块中的局部变量
符号表表项中可以不用存放块编号,而是根据符号表表项在符号表中的位置来判断
名字、变量和标识符的区别与联系
名字 和 变量 分别表示 编译时的特定对象和运行时该名字所代表的内存位置。
标识符 是一个字符串,用于指示数据对象、过程、类或对象的入口。
所有标识符都是名字,但并不是所有的名字都是标识符,名字还可以是表达式。
例如,名字x.y表示x所代表结构的域y。
过程定义、过程调用、活动
过程定义 是 把一个标识符和一组语句联系起来。该标识符是过程名,这组语句是过程体。
当过程名出现在可执行语句中时,称相应的过程在该点被调用。过程调用就是执行被调用过程的过程体。
过程体的每次执行 叫做 该过程的一个活动。
调用序列 和 返回序列
过程调用和返回 是 通过在目标代码中生成调用序列和返回序列来实现的
堆空间的分配策略
最佳适应策略
总是将请求的内存分配在满足要求的最小可用孔洞中
倾向于将大孔洞预留起来以便用来满足后续的更大请求,这是令程序产生最少碎片的一种很好的堆分配策略
首次适应策略
将请求的内存分配在第一个满足要求的孔洞中
这种策略在分配空间时所花费的时间较少,但在总体性能上要低于最佳适应策略
桶机制更容易按最佳适应策略找到所需的空闲块:
接合相邻的空闲块:边界标签和双向链接的空闲块列表
目标代码的种类
窥孔优化
窥孔优化是一种简单有效的局部优化方法,它通过检查目标指令中的短序列(窥孔),用更小更短的指令序列进行等价代替,以此来提高目标代码的质量。
典型窥孔优化技术
冗余指令消除、不可达代码消除、强度削弱、特殊机器指令的使用、其他处理
- c d, * b (0), / (1) e, + a (2)prod = 0;
i = 1;
while (i < 20){
prod = prod + a[i] * b[i];
i = i + 1;
}
(假设a[low:up]中low=1, 则addr(a[i]) = A+(i-1)*4 = A-4+4*i)
> 中间代码四元式如下:
100 (=, 0, _, prod)
101 (=, 1, _, i)
102 (j<, i, 20, 104) // while true, goto 104
103 (j, _, _, 114) // false, goto 114
104 (*, 4, i, T1) // 数组元素下标i
105 (-, A, 4, T2) // 数组首地址A ???
106 (=[], T2, T1, T3) // a[i]
107 (*, 4, i, T4)
108 (-, B, 4, T5)
109 (=[], T5, T4, T6) // b[i]
110 (*, T3, T6, T7) // a[i]*b[i]
111 (+, prod, T7, prod)
112 (+, i, 1, i)
113 (j, _, _, 102) // loop over, goto 102
114 // end while


给定如下正则文法,构造正则表达式
S -> +R|-R
R -> 0R|1R|2R|3R|4R|0|1|2|3|4|0D|1D|2D|3D|4D
D -> .B
B -> 0B|1B|2B|3B|4B|0|1|2|3|4
解:记 (X) = (0|1|2|3|4), $为空字,
(X)* 为X的克林闭包, (X)+ 为X的正闭包
有如下推导:
1) S = (+|-)R
2) R = (X)* R
3) R = (X)(D|$)
4) D = .B
5) B = (X)* B
6) B = (X)
由5)6)得 7) B = (X)+
由4)7)得 8) D = .(X)+
由2)3)得 9) R = (X)+ (D|$)
由8)9)得10) R = (X)+ (.(X)+ | $)
由1)10)得 S = (+|-)(X)+ (.(X)+ | $)
即 S = (+|-)(0|1|2|3|4)+ (.(0|1|2|3|4)+ | $)
结果的公式化形式:
S
=
(
+
∣
−
)
(
0
∣
1
∣
2
∣
3
∣
4
)
+
(
.
(
0
∣
1
∣
2
∣
3
∣
4
)
+
∣
ϵ
)
S = (+|-)(0|1|2|3|4)^+ (.(0|1|2|3|4)^+ | \epsilon)
S=(+∣−)(0∣1∣2∣3∣4)+(.(0∣1∣2∣3∣4)+∣ϵ)
=
(
+
∣
−
)
(
(
0
∣
1
∣
2
∣
3
∣
4
)
+
.
(
0
∣
1
∣
2
∣
3
∣
4
)
+
∣
(
0
∣
1
∣
2
∣
3
∣
4
)
+
)
= (+|-)((0|1|2|3|4)^+ .(0|1|2|3|4)^+ | (0|1|2|3|4)^+)
=(+∣−)((0∣1∣2∣3∣4)+.(0∣1∣2∣3∣4)+∣(0∣1∣2∣3∣4)+)
解析:① 对形如A→ a1|a2|…|am的产生式,构造方程式A=a1|a2|…|am。其中可以有形如A→ε的产生式;
② 对形如A→a1A|a2A|…|amA的产生式,构造方程式A=(a1|a2|…|am)*A;
③ 对形如A→a1B|a2B|…|amB的产生式,构造方程式A=(a1|a2|…|am)B,其中B≠A
给定如下正则表达式,构造与之等价的正则文法 (a | b)* a (a | b)
答:由 S → (a | b)* a (a | b) 得,
① S → (a | b) S → aS | bS
② S → a (a | b)
由②得,③ S → aA
④ A → a | b
综上,G[S]:
S → aS(from①)
S → bS(from①)
S → aA(from③)
A → a(from④)
A → b(from④)
解析:


!!!感谢李啊这老师的殷切指导和谆谆教诲!!!
解:动作 栈 输入缓冲区 打印
1) aacbb
2) 移进 a acbb
3) 移进 aa cbb
4) 移进 aac bb
5) 归约A-c aaA bb 1
6) 移进 aaAb b
7) 归约B-Ab aaB b 2
8) 归约A-aB aA b 0
9) 移进 aAb
10)归约B-Ab aB 2
11)归约A-aB A 0
最后打印结果为: 12020

解: 分析栈 输入 解释
1) # y|Bcw#
2) #y |Bcw# 扫描到c
3) #y| Bcw# 由产生式A->|B和S=>y|Bcw知, c in FOLLOW(A)
4) #y|B bw# 所以此时可以用A->|B进行规约


解析:

给定文法 G[E]:
E → E+T | T
T → T*F | F
F → (E) | i
该文法是 LL(1)文法么?如果是,则给出分析表。如果不是,解释原因,并说明如何将其转换为LL(1)文法,并给出转换后的文法的分析表。要求给出详细分析过程。
答:
该文法不是LL(1)文法,因为有左递归,消除左递归可以得到LL(1)文法
消除左递归,得到新文法G’
E -> TE'
E' -> +TE' | ε
T -> FT'
T' -> *FT' | ε
F -> (E) | i
求 First 集
First(i) = { i }
First(F) = { (, i }
First(T) = First(F) = { (, i }
First(T') = { *, ε }
First(E) = First(T) = { (, i }
First(E') = { +, ε }
求所有非终结符的 Follow 集
# 是开始符号
FOLLOW(E) = { #, ) }
FOLLOW(E') = FOLLOW(E) = { #, ) }
FOLLOW(T) = First(E') + Follow(E') + FOLLOW(E)= { +, #, ) }
FOLLOW(T') = FOLLOW(T) = { +, #, ) }
FOLLOW(F) = First(T') + FOLLOW(T) + FOLLOW(T') = { *, +, #, ) }
根据 LL(1)文法的定义,判断当前文法
① 在步骤2)已消除左递归
② 具有多个产生式右部的非终结符为 { E’, T’, F }。对于E’,FIRST(+TE’)={+},FIRST(ε)={ε},不相交;对于T’,FIRST(*FT’)={*},FIRST(ε)={ε},不相交;对于F,FIRST((E))={(},FIRST(i)={i},不相交。
③ FIRST集中存在ε的非终结符为 { E’, T’ }。对于E’,FIRST(+TE’)
∪
\cup
∪ FIRST(ε) = {+, ε},FOLLOW(E’)={ #, ) },不相交;对于T’,FIRST(*FT’)
∪
\cup
∪ FIRST(ε)={*, ε}, FOLLOW(T’) = { +, #, ) },不相交。
综上,新文法G’是LL(1)文法
分析表如下

解析:
① LL(1)文法的定义如下:
1.文法不含左递归
2.对于文法中每一个非终结符A的各个产生式的候选首符集(FIRST集)两两不相交。即,若
A
→
a
1
∣
a
2
∣
.
.
.
∣
a
n
A→a_1|a_2|...|a_n
A→a1∣a2∣...∣an,则
F
I
R
S
T
(
a
i
)
∩
F
I
R
S
T
(
a
j
)
=
∅
FIRST(a_i)∩FIRST(a _j)=\varnothing
FIRST(ai)∩FIRST(aj)=∅
3.对文法中的每个非终结符A,若它存在某个候选首符集包含ε,则
F
I
R
S
T
(
a
i
)
∩
F
O
L
L
O
W
(
A
)
=
∅
FIRST(a_i)\cap FOLLOW(A)=\varnothing
FIRST(ai)∩FOLLOW(A)=∅,其中i=1,2,…,n
如果—个文法G满足以上条件,则称该文法G为LL(1)文法
A' -> .A,将 .右侧的非终结符在产生式左部的产生式加入当前项目集1. I0:A'->.A .右侧为非终结符A,将产生式左部是A的产生式转为项目并加入
2. I0:A'->.A 现在项目集中.右侧非终结符对应的项目都添加完成
A->.bBa
A->.bBa



综上,分析表中没有冲突,该文法是SLR文法