编译原理 第一章 绪论

编译原理 第一章 绪论

编译原理系列,是在学习本校 "编译技术" 这门课程时,所作记录,参考教材为 《编译技术基础教程》清华大学出版社和《程序设计语言编译原理》国防工业出版社(陈火旺编著)。

1.1 编译程序简介

计算机系统的语言分为三个层次:

  • 机器语言(Machine Language)
    • 难记忆、难编写、难阅读、难改错
    • 计算机可直接理解
    • 例:C706 0000 0002
  • 汇编语言(Assembly Language)
    • 引入助记符
    • 依赖特定机器
    • 编写效率低
    • 例:MOV X,2
  • 高级语言(High-level Language)
    • 接近人类表达习惯,不依赖特定机器
    • 编写效率高
    • 例:x = 2

为了将高级语言和汇编语言程序转换为对应的机器语言程序,必须寻找一种方法——翻译

翻译:讲义中程序设计语言(源语言)所编写的程序(源程序)转变为之等价的另一种程序设计语言(目标语言)的程序(目标程序)的过程。

若源程序是 FORTRAN、Pascal、C、Java 、Objective 这样的高级语言,目标语言是汇编语言或机器语言之类的低级语言,则这样的翻译程序就是编译程序,这样的语言称为编译型语言。

Figure 1 编译型语言的执行过程
Figure 1 编译型语言的执行过程

编译程序一般分为以下几种:

  • 诊断编译程序(Diagnostic Compiler)

    • 专门用于帮助程序开发和调试的编译程序
  • 优化编译程序(Optimizing Compiler)

    • 着重于提高目标代码运行效率的编译程序
  • 交叉编译程序(Cross Compiler)

    • 运行编译程序的计算机称为宿主机,运行编译程序所产生目标代码的计算机称为目标机

    • 编译程序产生不同于其宿主机的目标代码

  • 可变目标编译程序

    • 不需要重写编译程序中与机器无关的部分就能改变目标机
Figure 2 高级语言程序的处理过程
Figure 2 高级语言程序的处理过程
  • 预处理程序(Preprocessor)
    • 将不同文件中的源程序模块汇集在一起,又将源程序中的宏语句展开为原始语句并加入到源程序中的程序。
  • 汇编程序(Assembler)
    • 把汇编语言程序转换为机器语言程序的程序
  • 装配连接程序(Loader-linker)
    • 把可装配的机器代码进行装配链接得到绝对机器代码的程序

高级语言除了可以编译执行,亦可以解释执行,如 Python。效率较低。

解释:以源程序为输入,执行过程中不产生目标程序,而是边解释边执行。

Figure 3 解释型语言的执行过程
Figure 3 解释型语言的执行过程

既然编译型和解释型各有缺点就会有人想到把两种类型整合起来,取其精华去其糟粕。就出现了半编译型语言。比如 C#,C# 在编译的时候不是直接编译成机器码而是中间码,.NET 平台提供了中间语言运行库运行中间码,中间语言运行库类似于 Java 虚拟机。

Figure 4 混合型语言的执行过程
Figure 4 混合型语言的执行过程

1.2 编译程序的结构及编译过程

Figure 5 编译程序的结构
Figure 5 编译程序的结构

词法分析器(Scanner)

功能:输入源程序,进行词法分析,输出单词符号。

  • 识别出源程序的各个单词符号
  • 删除无用的空白字符和注释
  • 检查拼词错误

单词一般分为 5 种:保留字、标识符、常数、运算符和界符。

如,如下 Pascal 程序片段:

1
2
var x,y,z:read;
x := y + z * 60;

词法分析器从左到右扫描输入的字符流,识别出一个个单词符号,并输出其内部表示形式:

Figure 6 程序中的单词符号
Figure 6 程序中的单词符号

则上述程序片段经过词法分析后,输出为:

1
2
$var id1,id2,id3:$real;
id1 := id2 + id3 * int1

语法分析器(Parser)

功能:以扫描器输出的单词流为输入,判断整个单词符号串是否构成一个语法上正确的程序。

依据:程序设计语言的语法规则

方法:为源程序构造一个逻辑上的语法树

如:单词符号串 id1 := id2 + id3 * int1

Figure 7 语句 id1 := id2 + id3 * int1 的语法树
Figure 7 语句 id1 := id2 + id3 * int1 的语法树

语义分析和中间代码生成器

功能:按照语义规则对语法分析器识别出来的语法单位进行语义分析并翻译成一定形式的中间代码。

一方面,对各类语法单位进行静态语义检查,如,变量是否定义、类型是否匹配等。另一翻盖你,若语义正确,根据语法单位的类型分类处理。若为说明语句,则将变量的属性填入符号表;若是可执行语句,则翻译为中间代码。

中间代码(Intermediate Code),是一种含义明确、便于处理的记号系统。通常采用四元式:(序号)(op, arg1, arg2, result)

其中 op 是运算符,arg1 是左操作数,arg2 是右操作数,result 是运算结果。

通常语义分析和中间代码生成阶段的工作通常穿插在语法分析中来完成。

Figure 8 语句 id1 := id2 + id3 * int1 的中间代码
Figure 8 语句 id1 := id2 + id3 * int1 的中间代码

代码优化器

功能:对中间代码进行优化处理。

Figure 9 语句 id1 := id2 + id3 * int1 的中间代码的优化
Figure 9 语句 id1 := id2 + id3 * int1 的中间代码的优化

代码优化的主要依据是程序的等价变换原则。常用的优化方法主要包括合并已知量、删除公共子表达式、删除无用代码、代码外提等。

目标代码生成器

功能:将中间代码翻译成目标代码。

Figure 10 语句 id1 := id2 + id3 * int1 的目标代码
Figure 10 语句 id1 := id2 + id3 * int1 的目标代码

表格管理

编译程序在工作过程中需要管理一系列的表格,以登记源程序的各类信息和编译各阶段的进展情况。

当扫描器识别出一个名字(标识符)后,将会把该名字填入到符号表中,但此时并不能够确定名字的全部属性,这些属性要在后续编译的各个阶段才能逐步补充完整。例如,名字的类型等信息要在语义分析时才能确定,而名字的地址信息可能要到目标代码生成时才能确定。

错误处理

通常,源程序中的错误分为语法错误和语义错误两大类。

  • 语法错误
    • 源程序中不符合语法规则或词法规则的错误
    • 可在词法分析或语法分析阶段检测出来
    • 例如,“非法字符”之类的错误可在词法分析时检测出来
    • “括号不 匹配”、“缺少;”之类的错误可在语法分析时检测出来
  • 语义错误
    • 源程序中不符合语义规则的错误
    • 一般可在语义分析阶段检测出来
    • 有的语义错误却要在运行时才能检测出来
    • 例如,说明错误、作用域错误、类型不一致等错误可在语义分析时检测出来。

术语

前端(front end):主要依赖于源语言而与目标机器无关的编译阶段。如:词法分析、语法分析、语义分析、中间代码生成、部分优化工作、与前端有关的出错处理工作和符号表管理工作。

后端(back end):依赖于目标机而一般不依赖于源语言,只与中间代码有关的编译阶段。如:目标代码生成,以及相关出错处理和符号表操作。

遍(Pass):对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程。每一遍扫视可完成上述一个阶段或多个阶段的工作。

编译程序的构造

编辑程序的构造的三个前提:

  • 源语言:编译程序的处理对象
  • 目标语言与目标机:编译长须处理的结果和运行环境
  • 编译方法与工具

五种编译程序的构造方法:

  1. 直接用机器语言或汇编语言编写。常用于编译程序核心代码的编写。
  2. 用高级语言编写编译程序。这是最普遍的方法。
  3. 自编译(自展)方式。先对语言的核心部分构造一个小小的编译程序(可 以用低级语言来实现),再以它为工具构造一个能够编译更多语言成分的较大的编 译程序,如此扩展下去,就像滚雪球一样,越滚越大,最终形成人们所期望的整个 编译程序。
  4. 用编译工具自动生成部分或整个程序。有些工具能用于自动产生扫描器(如 LEX),有些可用于自动产生语法分析器(如 YACC),有些甚至可用来产生整个编 译程序,这些都是以对源程序和目标语言(或机器)的形式描述作为输入来自动产 生编译程序的。
  5. 移植。将某种语言的编译程序从一种类型的机器转移到另一种类型的机器 上并能正常运行的过程。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×