编译器结构
编译器由两个部分组成:分析 (analysis)部分和综合 (synthesis)部分。
- 分析部分也称前端 (front end),它把源程序分解成为多个组成要素,并在这些要素之上加上语法结构,然后使用这个结构来创建该源程序的一个中间表示。
- 分析部分还会收集有关源程序的信息,放在一个称为符号表 (symbol table)的数据结构中。符号表将和中间表示形式一起传送给综合部分。
- 综合部分也称后端 (back end),根据中间表示和符号表中的信息来构造用户期待的目标程序。
编译步骤
- 词法分析
- 词法分析器读入组成源程序的字符流,将他们切分组织成有意义的词法单元 (token)的序列作为输出。
- 语法分析
- 语法分析器根据各个词法单元,创建树形中间表示,常用语法树 (syntax tree)。语法分析只判断源程序在结构上是否正确。
- 语义分析
- 使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致。语义分析是判断结构正确的源程序所表达的意义是否正确,其中一个重要部分是类型检查 (type checking)。
- 中间代码生成
- 编译器根据语法树和符号表,生成一个明确的低级的或类机器语言的中间表示 (intermediate representation)。三地址代码 (three-address code)是一种常见的中间表示形式。
- 代码优化(机器无关)
- 机器无关的代码优化步骤试图改进中间代码,以便生成更好的目标代码。
- 代码生成器
- 代码生成器以源程序的中间表示形式作为输入,并把它映射到目标语言。如果目标语言是机器代码,就必须为程序使用的每个变量选择寄存器或内存位置。然后,中间指令被翻译成为能够完成相同任务的机器指令序列
- 代码优化(机器相关)
- 优化目标机器语言
查看编译步骤
创建文件 compileText.c
|
|
使用clnag/llvm,输入下面命令查看编译步骤:
|
|
输出得到编译步骤:
符号表
符号表记录源程序中使用的变量的名字,并收集和每个名字的各种属性有关的信息。如一个名字的存储分配、类型、作用域、参数数量和类型、返回类型等。
符号表为每个变量名字创建一个记录条目。编译器可以向记录中快速存放和获取数据。
Example
词法分析
创建文件 compileText.c
|
|
输入以下命令:
|
|
输出token序列:
|
|
语法分析
输入以下命令:
|
|
输出如下:
|
|
中间代码生成
输入命令:
|
|
得到compileTest.ll文件:
|
|
生成汇编
输入命令:
|
|
得到compileTest.s:
|
|
生成目标文件
输入命令:
|
|
输出文件compileTest.o:
|
|
生成可执行文件
输入命令:
|
|
输出可执行文件compileTest,执行如下:
|
|
|
|
Example传送门
https://github.com/blackteachinese/compile_principle_introduce/tree/master
编译器 (compiler)
编译器是一个程序,可以阅读以某一种语言(源语言)编写的程序,并把该程序翻译成为一个等价的、用另一种语言(目标语言)编写的程序。
解释器 (interpreter)
解释器不通过翻译的方式生成目标程序,直接利用用户提供的输入执行源程序中指定的操作。
编译器和解析器的区别
编译型语言在编译过程中生成目标平台的指令,解释型语言在运行过程中才生成目标平台的指令。
虚拟机的任务是在运行过程中将中间代码翻译成目标平台的指令。
语言处理流程
预处理器 (preprocessor)
编译器 (compiler)
汇编器 (assembler)
链接器 (linker)/加载器 (loader)
相关名词
- 词法单元 (token)
- 抽象语法树 (AST - abstract syntax tree)
- 类型检查 (type checking)
- 三地址代码 (three-address code)
- 中间表示形式 (IR - intermediate representation)
- 中间语言 (IL - intermediate language)
- 字节码 (bytecode)