编译器相关

编译器基本原理

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
复制代码

1.词法分析

将源文件的字符串,进行过滤,去除空格,注释等,而后将其分割成一个个的词(记号、token) 好比 int a = b + sum(1,2); 会拆分红12个token后端

int     类型标识符
a       标识符
=       赋值运算符
b       标识符
+       加号
sum     标识符
(       左括号
1       整数
,       逗号
2       整数
)       右括号
;       分号
复制代码

2.语法分析

词法分析以后,字符流已经被转化为token流了 int<int> ID<a> '=' ID<value> '+' ID<sum> '(' Num<1> ',' Num<2> ')' ';' 上面的int表示一个标识符类型的token。内容为'int'bash

接下来,解析这个token流,首先这是一个语句,咱们主要有用到4种语句,赋值语句,函数调用语句,if和while语句。很明显,这是一个赋值语句。架构

语法结构树.pic.jpg

赋值表达式用变量名、赋值符号= 和表达式构成。编程语言

将语法结构应用到token流上,把等号两边的内容放到对应的节点上,生成语法树以下:

14972724543701.jpg

接下来对expression<b+sum(1,2)>进行解析 表达式有不少种,变量表达式,数字表达式,加减法表达式,通过对比,只有加法表达式结构才能匹配,因而将加法表达式的语法结构应用到其中。如图

14972725257900.jpg

进一步解析语法树的<b><sum(1,2)>,发现只有变量表达式和函数调用表示式才能匹配成功,获得

14972730581069.jpg

接下来进行语法优化,有些节点是多余的,好比在复制表达式中 '='和';',对语法树进行浓缩获得最终的抽象语法树(AST)

14972734592756.jpg

由此看出,语法分析就是不断将语法规则用于源程序,将源程序解析成一颗抽象语法树。以后的语义分析,中间码生成和代码优化都是基于对这棵树进行遍历,检查,修改进行的。

3.语义分析

语义分析阶段,会对语法进行屡次的遍历,进行语法检查,包括类型和声明的检查,OClint就是基于语义分析进行静态代码分析的。 仍是以上面的赋值语句做为例子, 须要检查 1.b, sum是否已经声明过 2.sum函数的参数数量和类型是否和传入的参数数量和类型匹配。 3.加法运算的两个操做数的类型是否匹配,这里也就是检查函数的返回值类型了 4.赋值运算的两个操做数类型是否匹配

通常在遍历语法树过程当中,遇到的变量声明和函数声明时,会将变量名-类型,函数名-返回类型-参数数量-参数类型保存到符号表里,当遇到使用变量和函数调用时,根据名称在符号表猴子那个查找,检查是否声明过,类型是否匹配。所以,对于java这种函数声明能够放在使用位置后面的语言,至少须要遍历两遍语法树。

语义检查时,也会对语法树进行优化,好比将常量的表达式先计算如: a = 1 + 2 * 3; 会被优化成 a = 7;

语义分析完成后,编译期错误被排除,全部使用过的变量名和函数名被绑定到声明的地址,就能够进行代码生成和优化了。

中间码生成

通常的编译器都不会直接生成目标代码,而是先生成中间码再生成目标代码。 中间码的做用: 计算机直接生成的代码比人手写的汇编要庞大、重复不少,计算机科学家们对一些具备固定格式的中间代码(最典型的是三地址中间码)的进行大量的研究工做,提出了不少普遍应用的、效率很是高的优化算法,能够对中间代码进行优化,比直接对目标代码进行优化的效果要好不少。 经过中间代码实现先后级分离,在多系统、多语言开发时,可大幅提升总体开发效率,减小开发成本缩短开发周期。 为了增长编译器的模块化和可移植化、可扩展性。中间代码既独立于任何高级语言,又独立于机器架构,所以能够经过编写m+n个编译模块二得到m *n种编译器。

相关文章
相关标签/搜索