Compiler -- Intro


编译器

编译器是一个将一个程序由一种源语言翻译到目标语言的程序。

通常来讲,一个编程语言有如下三种工作方式:

  • 编译:将程序通过编译器编译为机器语言的目标程序,直接将数据输入至目标程序由机器执行,如 C 语言。
  • 解释:将程序与数据一起输入解释器,由解释器代为执行,如 Python 语言。
  • 混合:由编译器将程序编译为某种中间语言的程序,再使用虚拟机解释执行该程序,如 Java 语言与 JVM 。

实际项目中往往会将程序分为各个片段,因此除了编译器外还需要预处理器收集符号信息等,汇编器与链接器将不同片段编译生成的汇编程序链接起来;同时有些语言还需要预处理器事先调用宏来替换源代码,以及从程序中收集编译器参数等。

编译器结构

一个编译器通常由以下五个部分组成,该结构最早来自于 FORTRAN I 的编译器:

  1. 词法分析(Lexical Analysis, scan):从字符流中识别出词素(lexeme),将程序由字节流转化为带有词汇信息的 token 流;
  2. 语法分析(Syntax Analysis, parse):从 token 流中识别出语法结构,从而构建出包含语法信息的结构,通常是 AST(Abstract Syntax Tree)
  3. 语义分析(Semantic Analysis):对 AST 进行进一步的检查,如类型作用域检查;生成相关的语义信息,如表达式类型等;
  4. 中间代码生成(Intermediate Code Generation):生成介于源语言与汇编之间的中间语言表示(Intermediate Representation, IR),用于代码优化;
  5. 代码优化(Code Optimization):使用生成的中间代码将程序改写为作用相同但更优的程序(运行时间、内存使用、耗电等方面),有时也会对 AST 或者汇编代码进行优化;
  6. 代码生成(Code Generation):将中间代码或 AST 翻译为目标语言,通常是汇编语言,供后续程序处理或运行。

通常将前三个步骤称为编译器的前端,后三个步骤称为编译器后端,区别在于前端会报告源程序中的错误,而通过了编译器前端的程序在后端通常不会报错。