1.是什么(编译器是什么) 2.为何 (为何须要编译器) 3.怎么作 (编译器如何工做)前端
在编译器工做以前须要进行预处理,包括宏的替换,头文件的导入,以及相似#if的处理 编译器是一种把源程序语音翻译成目标程序语言的计算机程序。通常来讲,源程序是高级语言好比Java,Objective-C等。 目标程序语言通常就是汇编语言或者二进制码。java
编译器通常由前端和后端组成。objective-c
前端主要进行和源语言相关,和目标语言无关的工做,包括词法分析,语法分析,语义分析,中间码生成。 后端主要进行和源语言无关,可是和目标语言有关的工做,好比中间码优化,将中间码转化成目标码,对目标代码优化生成目标程序。算法
端 | 编译器阶段 | 生成产物 | 功能 | 用途 |
---|---|---|---|---|
前端 | 词法分析 | 单词流 | 语法高亮 | 语法高亮 |
前端 | 语法分析 | AST(抽象语法树) | 语法高亮,代码格式化,语法检查 | OCLint |
前端 | 中间码生成 | 中间码 |
天然语言最容易表述人们的要求,当用户用天然语言表述了须要的功能后,编译器将高级编程语言转换成汇编、由汇编到机器码,提升软件开发的效率express
在命令行输入clang -ccc-print-phases main.m编程
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, imag
复制代码
将源文件的字符串,进行过滤,去除空格,注释等,而后将其分割成一个个的词(记号、token) 好比 int a = b + sum(1,2);
会拆分红12个token后端
int 类型标识符
a 标识符
= 赋值运算符
b 标识符
+ 加号
sum 标识符
( 左括号
1 整数
, 逗号
2 整数
) 右括号
; 分号
复制代码
词法分析以后,字符流已经被转化为token流了 int<int> ID<a> '=' ID<value> '+' ID<sum> '(' Num<1> ',' Num<2> ')' ';'
上面的int表示一个标识符类型的token。内容为'int'bash
接下来,解析这个token流,首先这是一个语句,咱们主要有用到4种语句,赋值语句,函数调用语句,if和while语句。很明显,这是一个赋值语句。架构
赋值表达式用变量名、赋值符号= 和表达式构成。编程语言
将语法结构应用到token流上,把等号两边的内容放到对应的节点上,生成语法树以下:
接下来对expression<b+sum(1,2)>
进行解析 表达式有不少种,变量表达式,数字表达式,加减法表达式,通过对比,只有加法表达式结构才能匹配,因而将加法表达式的语法结构应用到其中。如图
进一步解析语法树的<b>
和<sum(1,2)>
,发现只有变量表达式和函数调用表示式才能匹配成功,获得
接下来进行语法优化,有些节点是多余的,好比在复制表达式中 '='和';',对语法树进行浓缩获得最终的抽象语法树(AST)
由此看出,语法分析就是不断将语法规则用于源程序,将源程序解析成一颗抽象语法树。以后的语义分析,中间码生成和代码优化都是基于对这棵树进行遍历,检查,修改进行的。
语义分析阶段,会对语法进行屡次的遍历,进行语法检查,包括类型和声明的检查,OClint就是基于语义分析进行静态代码分析的。 仍是以上面的赋值语句做为例子, 须要检查 1.b, sum是否已经声明过 2.sum函数的参数数量和类型是否和传入的参数数量和类型匹配。 3.加法运算的两个操做数的类型是否匹配,这里也就是检查函数的返回值类型了 4.赋值运算的两个操做数类型是否匹配
通常在遍历语法树过程当中,遇到的变量声明和函数声明时,会将变量名-类型,函数名-返回类型-参数数量-参数类型保存到符号表里,当遇到使用变量和函数调用时,根据名称在符号表猴子那个查找,检查是否声明过,类型是否匹配。所以,对于java这种函数声明能够放在使用位置后面的语言,至少须要遍历两遍语法树。
语义检查时,也会对语法树进行优化,好比将常量的表达式先计算如: a = 1 + 2 * 3;
会被优化成 a = 7;
语义分析完成后,编译期错误被排除,全部使用过的变量名和函数名被绑定到声明的地址,就能够进行代码生成和优化了。
通常的编译器都不会直接生成目标代码,而是先生成中间码再生成目标代码。 中间码的做用: 计算机直接生成的代码比人手写的汇编要庞大、重复不少,计算机科学家们对一些具备固定格式的中间代码(最典型的是三地址中间码)的进行大量的研究工做,提出了不少普遍应用的、效率很是高的优化算法,能够对中间代码进行优化,比直接对目标代码进行优化的效果要好不少。 经过中间代码实现先后级分离,在多系统、多语言开发时,可大幅提升总体开发效率,减小开发成本缩短开发周期。 为了增长编译器的模块化和可移植化、可扩展性。中间代码既独立于任何高级语言,又独立于机器架构,所以能够经过编写m+n个编译模块二得到m *n种编译器。