编译原理学习总结——编译与解释过程概览

1,505 阅读5分钟

前言

最近在了解编译原理相关的知识,本文是对Programming Language Pragmatics一书第一章主要内容的总结,包含Compilation(编译)和 Interpretation(解释)的区别、流程,及各个流程主要干了什么。

Compilation(编译)与 Interpretation(解释)

借用书中的图,对代码有一定经验的人很快就能了解编译与解释有啥区别:

Compilation(编译)

Interpretation(解释)

编译与解释的区别非常明显,然而大多数语言是两者混合的,比如Java:

以上仅是编译与解释的区别,具体实现更为复杂,比如有的语言会有Preprocessor(预处理程序),现代Java的执行有了JIT compiler等等。

具体不展开,有兴趣的朋友自行搜索。

编译的大致流程

书中给出了编译过程的图示:

接下来解释各个阶段的主要任务。

Scanner (lexical analysis,即词法分析)

Scanner阶段主要读取源程序的字符串,把他们分组为token,这里的token是指计算机程序中最小有意义单位。这个阶段也被叫做lexical analysis(词法分析)。

比如将求最大公约数的C语言源程序进行词法分析:

这是源代码:

进行词法分析后拆解出token:

注:
token不单单只是切割后的字符串,一般token由token name和token value(可选)组成。
比如:x = a + b * 2; 的解析后的token为:
[(identifier, x), (operator, =), (identifier, a), (operator, +), (identifier, b), (operator, *), (literal, 2), (separator, ;)]
以上例子摘自——Lexical analysis(token)——Wikipedia

这个阶段的主要目的是简化下一阶段Parser(语法分析器)的工作,主要通过以下几个方法:

  • 减少输入Parser的体积(。。这里好难翻译,原文:reducing the size of the input )
  • 去除一些不相干的字符串,比如空白字符串
  • 去除注释
  • 给token标记出现在源代码中的第几行第几列

等等

Parser(syntax analysis,即句法分析)

Parser阶段做的主要是syntax analysis(句法分析),将词法分析得到的token组织为parse tree,这棵树展示了如何把token组装在一起去生成一个程序

上方的最大公约数程序的parse tree如下:

其中A、B两部分如下:

整棵树的构建主要依赖于一种可递归的规则,被称之为context-free grammar。比如对于循环会有如下规则:

其中的statement可继续递归规则:

A树中的循环与此对应:

parse tree也常被称为concrete syntax tree,因为它具体完整地包含了特地序列的token可以如何从context-free grammar得到。

Semantic Analysis and Intermediate Code Generation(语义分析和中间代码生成)

语义分析是一个逻辑阶段,主要目的是分析程序的含义,除此之外,还有找出多次出现的、指向程序实体的标识符(identifier)并保证其为常量。在多数语言中,语义分析器还会追踪标识符和表达式的类型。

为了达到目的,语义分析器会构建并维护**symbol table(符号表)**这一数据结构。符号表通常包含标识符的信息,比如有类型、内部结构、作用域等等。

在符号表的帮助下,语义分析器能够执行大量context-free grammar和concrete syntax tree中没有的规则。在C语言中,比如可以保证:

  • 每一个标识符使用前需要先定义
  • 标识符必须在其作用域才能使用
  • 子程序调用需要提供正确的参数个数
  • switch上的case是不同的常量 等等

当然,不是所有的语义分析都能在编译期间进行的,能够在编译期间进行的语义分析称之为static semantics,必须在run time检查的称之为dynamic semantics

在语义分析阶段,语义分析器还会将concrete syntax tree的冗余信息去除,转化为abstract syntax tree(也就是我们常说的AST),并给剩下的节点注释上有用的信息,比如标识符指向符号表的指针。

如图是最大公约数的符号表与AST:

Target Code Generation(目标代码生成)

在代码生成阶段,编译器将中间代码转换到目标语言。为了生成汇编语言或者机器语言,代码生成器会遍历符号表分配变量地址,然后遍历程序的中间代码,生成加载和存储变量的引用等等。

上文中的最大公约数例子能够生成汇编语言(截图中的注释不是代码生成器生成的,是手动标注的):

Code Improvement(代码改良)

Code Improvement也被称作optimization(优化),是整个编译过程的可选阶段,主要目的是使得生成的程序更加高效——更快或者使用更少的内存。

一些优化是machine independent(独立于机器)的,这种优化主要是在语义分析之后,对中间代码进行优化;其他的优化则需要了解目标机器的情况,这种优化主要是对生成的目标程序进行。因此,在我们上文的流程中,Code Improvement出现了两次,也均是可选项。

如下图是上文最大公约数汇编语言的优化版,可以看到短了许多:

现代处理器有着非常复杂的内部行为,编译器生成的汇编程序经常比人类编写的更为高效。

解释的大致流程

解释的大致流程如下图:

可以看到解释front end部分和编译的是完全相同的。本书第一章并没有详细讲解Tree-walk routines,这里也不详细展开。

总结

在看完本书第一章后,对编译的大致流程与目的有了较为清晰的了解。

参考资料

语义分析——百度百科
Lexical analysis(Token)——Wikipedia