引论
1、编译器和解释器
CRE:语言处理器包括编译器和解释器。
编译器:一个编译器就是一个程序,它可以阅读某一种语言编写的程序(源程序),并把该程序翻译成一个等价的、用另一种语言编写的程序(目标程序)。如果目标程序是一个可执行的机器语言程序,那么它就可以被用户调用,处理输入并产生输出。
解释器:解释器(interpreter)是另一种常见的语言处理器。它并不通过翻译的方式产生目标程序。从用户的角度看,解释器直接利用用户提供的输入执行源程序中指定的操作。
CRE:Java把源程序编译成中间程序(字节码)。然后用虚拟机来解释执行或者JIT编译执行。C#语言也类似Java。
2、一个编译器的结构
编译器可看作两个部分:分析部分和综合部分。
词法分析(lexical analysis)
编译器第一个步骤称为词法分析或者扫描(scanning)。词法分析器读入组成源程序的字符流,并将它们组织成有意义的词素(lexeme)序列。对于每个词素,词法分析器产生如下形式的词法单元(token)作为输出:<tokenName, attributeName>
。
示例:
position = initial + rate * 60
词素 | 词法单元 |
---|---|
position |
<id, 1> |
= |
<=> |
initial |
<id, 2> |
+ |
<+> |
rate |
<id, 3> |
* |
<*> |
60 |
<60> 或者<number, 4> |
语法分析(syntax analysis)
编译器第二个步骤称为语法分析或者解析(parsing)。语法分析器使用各个词法单元的第一个分量来创建树形的中间表示。
一个常用的表示方式是语法树(syntax tree),树中的每个内部节点表示一个运算,该运算的子节点表示运算的分量。
语义分析(semantic analysis)
语义分析器使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致。它同时也收集类型信息,并把这些信息存放在语法树或者符号表中,以便在随后的中间代码生成过程中使用。
语义分析的一个重要部分是类型检查(type checking)。编译器检查每个运算符是否具有匹配的运算分量。程序设计语言可能允许某些类型转换,称为自动类型转换(coercion)。
中间代码生成
把源程序翻译成目标代码过程中,一个编译器可能构造一个或者多个中间表示,这些中间表示可以有多种形式。语法树就是一种中间表示形式。
考虑使用三地址代码(three-address code)。这种中间表示由一组类似汇编语言的指令组成,每个运算分量都像一个寄存器。有些三地址指令的运算分量少于3个。
代码优化
机器无关的代码优化步骤试图改进中间代码,以便生成更好的目标代码。
代码生成
代码生成器以中间表示形式为输入,并把它映射到目标语言。如果目标语言是机器代码,那么就必须为程序使用的每个变量选择寄存器或者内存位置。代码生成的一个至关重要的方面是合理分配寄存器以存放变量的值。
符号表管理
编译器的重要功能之一是记录程序中使用的变量的名字,并收集和每个名字的各种属性有关的信息。这些属性可以提供一个名字的存储分配、它的类型、作用域等信息。
对于过程名字,这些信息还包括:它的参数数量和类型、每个参数的传递方法(传值或传引用)以及返回类型。
将多个步骤组合成PASS
在一个特定的实现中,多个步骤的活动可以被组合成一趟(pass)。每趟读入一个输入文件产生一个输出文件。
编译器构造工具
常用的编译器构造工具:
- 语法分析器的生成器。
- 扫描器的生成器。
- 语法制导的翻译引擎。
- 代码生成器的生成器。
- 数据流分析引擎。
- 编译器构造工具集。
(END)