简单的编译流程

简易编译器流程图:

一个典型的编译器,能够包含为一个前端,一个后端。前端接收源程序产生一个中间表示,后端接收中间表示继续生成一个目标程序。因此,前端处理的是跟源语言有关的属性,后端处理跟目标机器有关的属性。前端

复杂的编译器:java

词法分析器:

1.词法分析器读入源代码,而后对字符流(源代码)作切分红记号流。举个例子:程序员

    这是一个程序员看到的字符流(源代码)后端

2.词法分析器将字符流读入,根据关键字、标识符、标点、字符串、整形数等进行划分,造成记号流(单词):数组

    

 举个例子: 假如源语句if(x>5),则词法分析器返回token{k=IF,lexeme=0};token{k=LPAREN,lexeme=0};token{k=ID,lexeme="X"};……数据结构

词法分析器的任务:字符流到记号流。函数

       字符流:和被编译语言密切相关(ASCII,Unicode,or……)工具

       记号流:编译器内部定义的数据结构,编码所识别出的词法单元性能

语法分析器:

 语法分析器(Parser)一般是做为编译器解释器的组件出现的,它的做用是进行语法检查(检查语法是否符合这个语言的规则)、并构建由输入的单词组成的数据结构(通常是语法分析树抽象语法树等层次化的数据结构)。语法分析器一般使用一个独立的词法分析器从输入字符流中分离出一个个的“单词”,并将单词流做为其输入。实际开发中,语法分析器能够手工编写,也可使用工具(半)自动生成。优化

抽象语法树是对程序语法的抽象表示

例如,当在开发语言时,可能在开始的时候,选择LL(1)文法来描述语言的语法规则,编译器前端生成LL(1)语法树,编译器后端对LL(1)语法树进行处理,生成字节码或者是汇编代码。可是随着工程的开发,在语言中加入了更多的特性,用LL(1)文法描述时,感受限制很大,而且编写文法时很吃力,因此这个时候决定采用LR(1)文法来描述语言的语法规则,把编译器前端改生成LR(1)语法树,但在这个时候,你会发现很糟糕,由于之前编译器后端是对LL(1)语树进行处理,不得不一样时也修改后端的代码。

1.抽象语法树的第一个特色为:不依赖于具体的文法。不管是LL(1)文法,仍是LR(1),或者仍是其它的方法,都要求在语法分析时候,构造出相同的语法树,这样能够给编译器后端提供了清晰,统一的接口。即便是前端采用了不一样的文法,都只须要改变前端代码,而不用连累到后端。即减小了工做量,也提升的编译器的可维护性。

2.抽象语法树的第二个特色为:不依赖于语言的细节。在编译器家族中,大名鼎鼎的gcc算得上是一个老大哥了,它能够编译多种语言,例如c,c++,java,ADA,Object C, FORTRAN, PASCAL,COBOL等等。在前端gcc对不一样的语言进行词法,语法分析和语义分析后,产生抽象语法树造成中间代码做为输出,供后端处理。要作到这一点,就必须在构造语法树时,不依赖于语言的细节,例如在不一样的语言中,相似于if-condition-then这样的语句有不一样的表示方法

语义分析器:

对语法树的合法性进行处理(例如:一个变量在使用以前是否先定义声明,所调用的函数是否有对应的定义),产生相应的中间代码或目标代码.

语义分析编译过程的一个逻辑阶段, 语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。好比语义分析的一个工做是进行类型审查,审查每一个算符是否具备语言规范容许的运算对象,当不符合语言规范时,编译程序应报告错误。若有的编译程序要对实数用做数组下标的状况报告错误。又好比某些程序规定运算对象可被强制,那么当二目运算施于一整型和一实型对象时,编译程序应将整型转换为实型而不能认为是源程序的错误。

通过上面的处理,程序中就没有在包括语言和语义的错误(除非编译器自己就有bug)

中间代码

  中间代码也叫中间语言

(Intermediate code /language)是:源程序的一种内部表示,不依赖目标机的结构,复杂性介于源语言和机器语言之间。

中间代码的优势

一、逻辑结构清楚;

二、利于不一样目标机上实现同一种语言;

三、利于进行与机器无关的优化;

中间代码能够生成例如 三地址代码 SSA 控制流图 等 (中间码的生成也是取决于编译器设计中的考虑,例如需不须要优化,或者追求速度,性能等).

关于语法分析器和中间代码:

LINKhttps://blog.csdn.net/yongchaocsdn/article/details/79056504

代码生成:

中间代码能够被最终的一个代码生成的阶段处理为最后的目标代码(例如机器码,JVM字节码)

符号表

符号表是存储程序编译过程当中重要信息,能够给每一个阶段提供支持

在计算机科学中,符号表是一种用于语言翻译器(例如编译器解释器)中的数据结构。在符号表中,程序源代码中的每一个标识符都和它的声明或使用信息绑定在一块儿,好比其数据类型、做用域以及内存地址

符号表在编译程序工做的过程当中须要不断收集、记录和使用源程序中一些语法符号的类型和特征等相关信息。这些信息通常以表格形式存储于系统中。如常数表、变量名表、数组名表、过程名表、标号表等等,统称为符号表。对于符号表组织、构造和管理方法的好坏会直接影响编译系统的运行效率。

举个例子:

一个加法表达式(sum)在编译中的过程:

例如sum的语法规则是:

1.整数数字 n

2.加法表达式式 n1+n2

根据上面的规则

3

1+2

1+2+3  (加法表达式遵循左结合也就是 1+2 的结果 3+3 这也属于加法表达式的一种)

这上面的三种都遵照了加法表达式的规则  因此都是加法表示式的一种

目标机器: 栈式计算机(stack)

是一个LIFO的一个先入后出存储器,跟它向对应的是FIFO存储器先入先出传统顺序

有两条指令:

1.push n (将指定的参数压入栈中)

2.add (将栈顶的两个索引的数据弹出并进行加法运算,再将运算后的结果压入栈中)

//x和y表示栈顶的数据 pop[]表示弹出栈顶的数据
x = pop[]

y = pop[]

//加法运算
n = x+y

//压栈
push n

例如1+2+3加法运算通过语法分析器翻译抽象语法树(AST):

1+2+3 =语法分析器:      语法树
                         (+)
                     (+)     (3)
                  (1)   (2)

先从左结合开始:

1+2 = 3

3+3 = 6

生成栈式计算机(stack)的代码:

代码生成使用树的后续遍历(从树的左边子节点开始遍历,而后遍历右边子节点最后遍历根节点)

代码生成规则:

1. 遍历树中时若是遇到整数 n 生成代码: push n

2.遍历树中时若是遇到 + 生成代码: add

最后生成的栈式计算机的代码:

push 1

push 2

add

push 3

add
相关文章
相关标签/搜索