编译原理王者之路
前言
近期,学期末,编译原理期末考试,借此机会,对编译原理
这门课程与技术进行整理汇总,形成体系大纲。编译原理偏向于计算机应用程序的底层
,也是计算机程序员应该了解的相关原理。其次,像现在的机器学习等进行自然语言处理
时,也会运用相关准则与方法。
本博文主要是参考《程序设计语言 编译原理(第3版)》(陈火旺等人著)
一、学习路线
1、课本路线
2、技术路线
编译程序的工作过程
一般可以划分为五
个阶段:词法分析、语法分析、语义分析与中间代码产生、优化、目标代码生成
。主要是对字符串
进行分析处理.
本博文也将按照这五个阶段
依次介绍每个阶段的任务、方法及其实现。
二、基础知识
1、基础概念
1.1 编译程序(翻译程序)
能够把某一种语言程序(称为源语言
程序,如C、Java之类的高级语言
)转换成另一种语言程序(称为目标语言
程序,如汇编语言或者机器语言之类的低级语言
)的程序
,后者和前者在逻辑上
是等价
的。
1.2 交叉编译程序
1.2.1 宿主机
运行编译程序的计算机
1.2.2 目标机
运行编译程序所产生的目标程序的计算机
如果一个编译程序产生不同于其宿主机的机器代码,称其为交叉编译程序。
1.3 编译前端
主要是对编译程序的划分。前端主要由与源语言
有关但与目标机无关的部分组成。如此法分析、语法分析、语义分析与中间代码产生,有的代码优化工作也可以包括在前端。
1.4 编译后端
主要是对编译程序的划分。后端包括编译程序中与目标机
有关的那部分,通常不依赖于源语言
而仅仅依赖于中间语言
。如与目标机有关的代码优化和目标代码生成等。
1.5 程序设计环境
编辑程序、编译程序、连接程序、调试工具
等工具的集成
1.6 程序语言
程序语言主要由语法和语义两方面定义,有描述数据
和对数据的运算
这两大功能。
1.6.1 语法
可以形成或者产生一个合式的程序的一组规则
。
任何语言程序都可以看成是一定字符集(称为字母表
)上的一字符串(有限序列)。
1.6.1.1 词法规则和语法规则
这组规则包含词法规则和语法规则(或产生规则)。
(字母组成单词,单词组成词语,词语组成句子)
1.6.2 语义
可以定义一个程序的意义的一组规则。
三、程序语言的语法描述
1、基础概念
1.1 符号串
由∑(字母表)中的符号所构成的一个有穷序列。
1.2 空字
不包含任何符号的序列
1.3 文法
文法是描述语言的语法结构
的形式规则
(即语法规则
)
1.4 上下文无关文法(重点)
所定义的语法范畴
(或语法单位)是完全独立
于这种范畴可能出现的环境
的一种文法
。
一个上下无关文法包括四个部分,故上下文无关文法是一个四元式
。
- 一组终结符号
终结符号是组成语言的基本符号
,在程序语言中为单词符号
,如基本字、标识符等。 - 一组非终结符号
非终结符号代表语法范畴,是一个类或者集合的记号,每个非终结符代表一定符号串的集合
(由终结符号和非终结符号组成的符号串) - 一个开始符号
一个特殊的非终结符号,通常称为句子
(最大的) - 一组产生式
产生式是定义语法范畴的一种书写规则,左边是非终结符
(大的),中间是→
,读作产生,右边是终结符号
或者与非终结符号
组成的符号串
(小的)。
简单理解就是对其进行就事论事
处理,不必考虑上下文。根据上下文无关文法的组成,简单理解为把一个句子揉碎,分为4个部分。
其中心思想是从开始符号
出发,反复连续使用产生式
,对非终结符
实行替换
与展开
,产生仅由终结符号组成的表达式
。
2、上下文无关文法
2.1 定义
上下文无关文法属于2型文法
。文法分为四种:0型、1型、2型、3型。0型强于1型,以此类推。
2.2 推导过程
2.2.1 最左推导
每次总是选择最左侧的符号
进行替换
2.2.2 最右推导
每次总是选择最右侧的符号
进行替换
2.3 语法分析树
2.3.1 定义
用一棵树表示一个句型的推导
2.3.2 二义性
如果一个文法
存在一个句子
对应两棵不同的语法树
,则称这个文法是二义的。
主要是针对文法
而言的。
四、词法分析
1、任务
从左至右逐个字符
地对源程序
进行扫描,产生一个个的单词符号
,把作为字符串
的源程序改造成为单词符号串
的中间程序
。
简单来说,就是把一整个字符串
划分为一个个单词符号
。
2、过程
2.1 词法分析器的输入输出
2.1.1 输入
源程序
2.1.2 输出
单词符号,有五种
- 关键字
程序语言定义的具有固定意义的标识符
- 标识符
表示各种名字
- 常数
- 运算符
- 界符
程序的关键字、运算符、界符是确定的。
2.2 输入、预处理
第一步是输入源程序文本
,输入串
一般放在一个缓冲区
内,此外,大多数情况下还要对输入串进行预处理
,比如剔除注解、空白符、回车符、换行符等。
2.3 单词符号的识别-超前搜索
- 词法分析器调用预处理子程序处理一串输入字符串
- 放入扫描缓冲区
- 从此缓冲区逐一识别单词符号
- 处理完后调用预处理子程序装入新串
超前搜索:超前
扫描许多个
字符,超前到能够肯定磁性的地方为止。
2.4 状态转换图
一种设计词法分析器的好工具。转换图是一张有限方向图
。
组成
- 结点
代表状态
,用圆圈
表示 - 终态、初态
一张状态转换图有初态(单圈表示)和至少一个终态
(双圈
表示) - 连结
状态
之间用箭弧
进行连接 - 标记
在箭弧上,表示箭弧始结点
状态下可能出现
的输入字符
或字符类
2.5 正规表达式和有限自动机
2.5.1 正规表达式
2.5.1.1 正规集定义
程序设计语言的单词表、词汇集
构成的集合,即是字
的集合。它有一定特殊性,我们称之为正规集
。用来代表程序语言的单词表
。
2.5.1.2 正规式定义
可以说是正规集
的名称
。
- 正规集可以用正规表达式(简称正规式)表示
- 正规表达式是表示正规集一种方法
- 一个字集合是正规集当且仅当它能用正规式表示
2.5.1.3 正规式和正规集递归定义
ε
\color{Red} \varepsilon
ε 和
∅
\color{Red} \varnothing
∅都是
Σ
\Sigma
Σ上的正规式,它们所表示的正规集为
{
ε
}
\color{Blue} \{\varepsilon\}
{ε}和
∅
\color{Blue} \varnothing
∅
任何
α
\color{Green} \alpha
α
ϵ
\epsilon
ϵ
Σ
\Sigma
Σ, 则
α
\color{Red} \alpha
α是
Σ
\Sigma
Σ上的正规式,它所表示的正规集为
{
α
}
\color{Blue} \{\alpha \}
{α}
假定
e
1
e_1
e1和
e
2
e_2
e2都是
Σ
\Sigma
Σ上的正规式,它们所表示的正规集为L(
e
1
e_1
e1)和L(
e
2
e_2
e2),则:
1)(
e
1
e_1
e1|
e
2
e_2
e2)为正规式,它所表示的正规集为L(
e
1
e_1
e1)
∪
\cup
∪ L(
e
2
e_2
e2)
2)( e 1 e_1 e1 ⋅ \cdot ⋅ e 2 e_2 e2)为正规式(做连接),它所表示的正规集为L( e 1 e_1 e1)L( e 2 e_2 e2)(连接)
3)( e 1 e_1 e1) ∗ ^* ∗ 为正规式,它所表示的正规集为(L( e 1 e_1 e1)) ∗ ^* ∗(闭包)
2.5.2 有限自动机
2.5.2.1 确定有限自动机
一个确定有限自动机(DFA)M是一个五元式
M=(S,
Σ
\Sigma
Σ, f,
S
0
S_0
S0 , F),其中:
1.S:有穷状态集,即包含起点重点在内的,各个状态
2.
Σ
\Sigma
Σ:输入字母表(有穷),即状态改变的条件
3.f:状态转换函数,为 S
×
\times
×
Σ
\Sigma
Σ
→
\rightarrow
→ S 的单值部分映射,即由A状态到B状态的改变
f(s,a) = s’ 表示:当现行状态为s ,输入字符为a时,将状态转换到下一状态s’,s’称为s的一个后继状态
4.
S
0
S_0
S0
∈
\in
∈ S:是唯一的一个初态,即起点唯一
5.F
⊆
\subseteq
⊆ S :终态集(可空),即最终状态,不唯一。
对于Σ * 中的任何字
α
\alpha
α,若存在
一条从初态
到某一终态
的道路
,且这条路上所有弧上的标记符
依序连接成的字等于
α
\alpha
α,则称
α
\alpha
α为DFA M 所识别(接收)
2.5.2.2 非确定有限自动机
NFA
定义 略
DFA是NFA的一个特例
对于Σ * 中的任何字
α
\alpha
α,若存在
一条从某一初态
到某一终态
的道路
,且这条路上所有弧上的标记符
依序连接成的字等于
α
\alpha
α,则称
α
\alpha
α为NFA M 所识别(接收)
2.5.2.3 子集法
将NFA确定化为DFA的方法
五、语法分析
1、任务
在词法分析识别出单词符号串的基础上,分析并判定
程序的语法结构
是否符合语法规则
。
即按文法的产生式
,识别输入符号串
是否为一个句子
。
从概念上讲,建立一棵与输入串相匹配
的语法分析树
。
根据语法分析树的建立方法,分为两类,一类是自上而下分析方法
,另一类是自下而上分析方法
。
2、自上而下分析方法
2.1 主旨
对于任何输入串,试图用一切可能的办法,从文法开始符号(根结)
出发,自上而下
为输入串建立一棵语法树。
这种方法是带回溯的,如果其中一个不符合,要回头看是否有别的候选。
2.2 缺点应对方法
2.2.1 消除文法的左递归
含左递归的文法会使自上而下的分析过程陷入无限循环
2.2.2 消除回溯,提取左因子
若关于 A 的语法规则符合如下形式:A → δβ1 | δβ2 | … | δβn | δ | γ1 | γ2 | … | γm
则提取左公因子
A → δA/ | γ1 | γ2 | … | γm
A/ → β1 | β2 | … | βn | ε |
继续检查 A 与 A/ 的候选式是否可以继续提取
反复提取左公共因子(包括新引入的非终结符),就可以使得所有候选首符集两两不相交
2.2.3 LL(1)分析条件
- 文法不含
左递归
- 文法中每一个非终结符 A 的各个产生式的
候选首符集不相交
,即若 A → α1 | α2 | … | αn
FIRST(αi)∩FIRST(αj) = ∅,i ≠ j - 对于文法中的每个非终结符 A,若它的某个
候选首符集包含 ε
,则FIRST(αi)∩FOLLOW(A) = ∅
2.2.4 LL(1)分析
一个文法,先消除左递归,再提取公共左因子,得到的文法 G 若满足以上条件,
则称文法 G 为LL(1)文法,可以进行LL(1)分析
过程如下
假设当前等待匹配的符号为 a,进行匹配的非终结符为A,A 的所有产生式为 A → α1 | α2 | … | αn,
- 若 a ∈ FIRST(αi),则将 A 推导为 αi,a得到匹配,斯巴拉西,继续分析下一个符号 ①
- 若 a ∉ FIRST(αi)
- 若 ε ∈ FIRST(αi),且 a ∈ FOLLOW(A),则将 A 推导为 ε,a 交给 A 后面的串匹配 ②
否则,匹配失败,a 此时在输入串中是语法错误 ③
如果这个文法是LL(1)文法,则每一步都能且只能在①②③中确定,每一步都只有一种情况。这就是LL(1)分析
3、自下而上分析方法
3.1 主旨
从输入串开始,逐步进行归约
,直到归约到文法的开始符号。
从语法分析树角度来看,从语法树的末端开始,步步向上归约,直到根结。
3.2 过程
是一种移进-归约
法,用先进后出的栈,把输入串一个个移到栈内,当栈顶形成某个产生式的候选式时,把栈顶的一部分替换成(归约为)该产生式的左部符号
。
3.3 算符优先分析
定义算符之间的某种优先关系,借助于这种优先关系寻找可归约
串和进行归约。
不是一种规范归约。
3.4 LR分析
3.4.1 定义
在规范归约过程中,一方面记住已移进和归约
出的整个符号串
,即记住历史
,另一方面根据所用的产生式推测
未来可能
用到的输入符号
,对未来进行展望
。
一个LR分析器实质上是一个带先进后出寄存器栈
的确定有限状态自动机
。
3.4.2 操作
- 移进
- 归约
- 接收
- 报错
六、语义分析和中间代码产生
1、任务
语义分析包括静态语义检查和翻译,主要有类型检查、控制流检查、一致性检查、相关名字检查、作用域分析
等工作。
编译程序采用了独立于机器的、复杂性介于源语言和机器语言之间的中间语言。
好处是
- 便于进行与机器无关的代码优化工作
- 是编译程序改变目标机更容易
- 是编译程序的结构在逻辑上更为简单明确,编译前端和后端的接口更为清晰。-
2、语义分析
语义分析的任务就是对结构上正确的源程序进行上下文有关性质的审查,审查源程序是否有无语义错误,为代码生成阶段收集类型信息。
3、中间代码生成
3.1 中间语言形式
主要有抽象语法树、后缀式、三地址代码(三元式、四元式、间接三元式)、DAG图表示
3.1.1 后缀式
把运算量(操作数)写在前面,算符(操作符)写在后面
3.1.2 三地址代码
3.1.2.1 一般形式
x:=y op z
3.1.2.2 四元式
一个带有四个域的记录结构,这四个域分别为op、arg1、arg2、result
arg
表示参数,一般在等号右边
,op代表操作符,result代表结果,一般在:
左边
3.1.2.3 三元式
把四元式中的result去掉
一个带有三个域的记录结构,这四个域分别为op、arg1、arg2
参数含义同四元式
3.1.2.4 间接三元式
用一张间接码表辅以三元式表来表示中间代码
调整运算顺序时,只需重新安排间接码表即可,无需改动三元式表
七、优化
1、任务
对程序进行等价变换,使得从变换后的程序处罚,能发生更有效的目标代码
,通常称这种变换为优化
。
其中包括控制流分析、数据流分析、代码变换过程。
2、原则
2.1 等价原则
经过优化后不应改变程序运行的结果
2.2 有效原则
使优化后所产生的目标代码运行时间较短,占用的存储空间少
2.3 合算原则
应尽可能以较低的代价取得较好的优化效果。
3、方法
3.1 删除公共子表达式
3.2 复写传播
3.3 删除无用代码
3.4 强度削弱
3.5 删除归纳变量
八、目标代码生成
1、任务
以源程序的中间代码为输入,产生等价的目标代码为输出
2、输入输出
代码生成器的输入包括中间代码和符号表中的信息
代码生成器的输出是经变换后的目标代码