2018-09-07
2018-09-07 本文已影响0人
Maymomo
编译原理 Ch1
概念
编译程序
本质上是一个翻译程序,将一门源语言(高级语言)翻译成功能等价的低级语言(汇编语言,机器语言等)的程序。
编译程序由八部分组成:
- 词法分析程序
- 语法分析程序
- 语义分析程序
- 中间代码生成程序
- 代码优化程序
- 目标代码生成程序
- 表格管理程序
- 出错处理程序
词法分析
顺序读入源程序文件,解析出一个个的单词.
我的理解是将语言的保留字,标识符,运算符和数值等提取出来。
如下简单的C代码(假设不经历预处理器处理), 经过词法分析后,
提取出“int”, "main", "(", "void", ")", ”{", ... , "\*", "d"等单词。
int main(void) {
int a = 0;
int b = 1;
int c = a + b;
int *d = &c;
return 0;
}
语法分析
语法分析是在词法分析的基础之上,根据语言语法规则将单词序列分解成程序,语句,表达式等。如下语句经过语法分析后,形成如下语法树。
int a = 0;
赋值语句
/ = \
a 0
语法分析和词法分析都是对源程序的结构进行分析。二者功能相似,但侧重点不同;
词法分析主要是提取单词序列。
语法分析是将单词序列分解。
语义分析
语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。
我的理解是根据提取的语法树分析,判断各变量和常量之间进行运算时类型是否匹配和记录它们的类型信息。
举个栗子, c++实现虚函数时,需要知道哪些是虚函数和如何调用虚函数。
哪些是虚函数: 在类定义时,有virtual关键字的为虚函数。
如何调用虚函数: 在虚函数调用之前,编译器通过调用自动生成的虚函数查找函数,根据虚表,将实际函数的地址移至%rax(开启-m32时为%eax),之后执行callq *%rax, 调用实际函数。
中间代码生成
中间代码:一种简单,含义明确的记号系统,这种记号系统可以设计为多种多样的形式。
设计原则:
1. 容易生成
2. 容易将它翻译成目标代码。很多编译程序使用了一种近似“三地址指令”的“四元式”的中间代码: (运算符, 运算对象1, 运算对象2, 结果)
目标代码生成
把中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编代码。
表格处理程序
以表格的方式构造和管理编译过程中源程序的各种信息。
出错处理程序
报告编译过程中的警告信息和错误信息,并且将错误所造成的影响限制在尽可能小的范围内,使得源程序的其余部分可以被继续编译下去。有的编译器甚至可以矫正部分错误。
编译器的前端和后端
前端包括:词法分析, 语法分析,语义分析和中间代码生成阶段。前端的工作依赖于源语言的语法规则。
后端包括: 目标代码优化和生成。这一阶段是将前端生成的中间代码翻译成特定机器的汇编或者机器代码。
前端和后端的划分的意义:
- 实现一个前端和实现多个平台的后端,即可达到前端过程的复用。
- 不同语言实现不同的前端,但中间代码的形式保持一致,这样可以复用不同平台的后端。
前后端分离大大减少了编写编译器的工作量,同时提供了更多的灵活性。
据我所知,很多较新的语言实现自己的前端,然后利用llvm作为后端, 比如rust。