470 likes | 597 Views
语言和翻译: 语言是人类交流思想和信息的工具。如自然语言,世界上存在着许多种语言,各国之间要交流信息 , 就要有各种语言之间的翻译。计算机语言同样是丰富多彩的。 由于计算机硬件的器件特性,决定了计算机本身只能直接接受由 0 和 1 编码的二进制指令和数据,这种二进制形式的指令集合称为该计算机的 机器语言 , 它是计算机惟一能够直接识别并接受的语言。. 第 1 章 编译程序概论. 1.1 什么是编译程序. 返回目录.
E N D
语言和翻译:语言是人类交流思想和信息的工具。如自然语言,世界上存在着许多种语言,各国之间要交流信息,就要有各种语言之间的翻译。计算机语言同样是丰富多彩的。语言和翻译:语言是人类交流思想和信息的工具。如自然语言,世界上存在着许多种语言,各国之间要交流信息,就要有各种语言之间的翻译。计算机语言同样是丰富多彩的。 由于计算机硬件的器件特性,决定了计算机本身只能直接接受由0和1编码的二进制指令和数据,这种二进制形式的指令集合称为该计算机的机器语言,它是计算机惟一能够直接识别并接受的语言。 第1章 编译程序概论 1.1 什么是编译程序 返回目录
用机器语言编写程序很不方便且容易出错,编写出来的程序也难以调试、阅读和交流。为此,出现了用助记符代替机器语言二进制编码的另外一种语言,这就是汇编语言。汇编语言是建立在机器语言之上的,因为它是机器语言的符号化形式,所以较机器语言直观;但是计算机并不能直接识别这种符号化语言,用汇编语言编写的程序必须翻译成机器语言之后才能执行,这种“翻译”是通过专门的软件——汇编程序实现的。用机器语言编写程序很不方便且容易出错,编写出来的程序也难以调试、阅读和交流。为此,出现了用助记符代替机器语言二进制编码的另外一种语言,这就是汇编语言。汇编语言是建立在机器语言之上的,因为它是机器语言的符号化形式,所以较机器语言直观;但是计算机并不能直接识别这种符号化语言,用汇编语言编写的程序必须翻译成机器语言之后才能执行,这种“翻译”是通过专门的软件——汇编程序实现的。
尽管汇编语言与机器语言相比在阅读和理解上有了长足的进步,但其依赖具体机器的特性是无法改变的,这给程序设计增加了难度。随着计算机应用需求的不断增长,出现了更加接近人类自然语言的功能更强、抽象级别更高的面向各种应用的高级语言,如: C 、 FORTRAN、Pascal 、Java、C++等。高级语言已经从具体机器中抽象出来,摆脱了依赖具体机器的问题。用高级语言编制的程序几乎能够在不改动的情况下在不同种类的计算机上运行且不易出错,这是汇编语言难以做到的,但高级语言程序翻译(编译)成最终能够直接执行的机器语言程序的难度却大大增加了。
由于汇编语言和机器语言一样都是面向机器的,故相对于面向用户的高级语言来说,它们都称之为低级语言,而FORTRAN、PASCAL、C、ADA、Java这类面向应用的语言则称之为高级语言。因此,编译程序就是指这样一种程序,通过它能够将用高级语言编写的源程序转换成与之在逻辑上等价的低级语言形式的目标程序,由于汇编语言和机器语言一样都是面向机器的,故相对于面向用户的高级语言来说,它们都称之为低级语言,而FORTRAN、PASCAL、C、ADA、Java这类面向应用的语言则称之为高级语言。因此,编译程序就是指这样一种程序,通过它能够将用高级语言编写的源程序转换成与之在逻辑上等价的低级语言形式的目标程序,
编译程序:从功能上看,一个编译程序就是一个语言翻译程序。它把一种语言(称作源语言)书写的程序翻译成另一种语言(称作目标语言)的等价的程序。编译程序:从功能上看,一个编译程序就是一个语言翻译程序。它把一种语言(称作源语言)书写的程序翻译成另一种语言(称作目标语言)的等价的程序。 源语言通常是一个高级语言,如FORTRAN,C 或Pascal。目标语言通常是一个低级语言,如汇编或机器语言。编译程序的功能下图所示。 编译程序 低级语言程序 (目标程序) 高级语言程序 (源程序)
如果从计算机系统的角度看,什么是编译程序呢?我们说编译程序是一种软件,是系统软件。通常认为系统软件是居于计算机系统中最靠近硬件的一层,其他软件一般都通过系统软件发挥作用。系统软件和具体的应用领域无关,如编译系统和操作系统等。编译程序也是一种语言处理系统,即把软件语言书写的各种程序处理成可在计算机上执行的程序。 一个编译程序的重要性体现在它使得多数计算机用户不必考虑与机器有关的繁琐细节,使程序员和程序设计专家独立于机器,这对于当今机器的数量和种类持续不断地增长的年代尤为重要。
需预处理的源程序 高级语言程序的处理过程 预处理程序 源程序 编译程序 目标汇编程序 汇编程序 可再装配的机器代码 装配/连接编辑程序 绝对机器代码 可再装配目标文件
一个高级语言程序的执行通常分为两个阶段,即编译阶段和运行阶段,如图所示。编译阶段将源程序变换成目标程序;运行阶段则由所生成的目标程序连同运行系统(数据空间分配子程序、标准函数程序等)接受程序的初始数据作为输入,运行后输出计算结果。一个高级语言程序的执行通常分为两个阶段,即编译阶段和运行阶段,如图所示。编译阶段将源程序变换成目标程序;运行阶段则由所生成的目标程序连同运行系统(数据空间分配子程序、标准函数程序等)接受程序的初始数据作为输入,运行后输出计算结果。
源程序 编译程序 目标程序 运行系统 输入数据 计算结果
用高级语言编写的程序也可通过解释程序来执 用高级语言编写的程序也可通过解释程序来执 行。解释程序也是一种翻译程序。 解释:按源语言的定义边解释边执行。 输入数据 解释程序 源程序 计算结果
优 点:交互方便,移植性好。 缺 点:效率低,如循环语句部分要反复解释执行。 共同点:都需要进行词法、语法、语义分析。 应 用:数据库系统中的动态查询语句(交互性) Java(移植性)。 区 别:编译产生目标程序 , 比喻:笔译。 解释不产生目标程序,比喻:口译。 很多语言如BASIC,LISP和PROLOG等等最初都是解释执行的,后来也都有了编译系统。号称最具生命力的Java环境同时需要解释和编译系统的支持。
编译程序的工作过程是指从输入源程序开始到输出目标程序为止的整个过程,此过程是非常复杂的。一般来说,整个编译过程可以划分成五个阶段:词法分析阶段、语法分析阶段、语义分析和中间代码生成阶段、优化阶段和目标代码生成阶段。编译程序的工作过程是指从输入源程序开始到输出目标程序为止的整个过程,此过程是非常复杂的。一般来说,整个编译过程可以划分成五个阶段:词法分析阶段、语法分析阶段、语义分析和中间代码生成阶段、优化阶段和目标代码生成阶段。 1.2 编译过程概述
1.词法分析 • 词法分析的任务从左到右读入源程序的每个字符,对构成源程序的字符流进行扫描和分解,从而识别出一个个单词(也叫单词符号或符号)。将所识别出的单词用统一长度的标准形式(也称内部码)来表示,以便于后继语法工作的进行。因此,词法分析工作是将源程序中字符串变换成单词符号流的过程,词法分析阶段工作遵循的是语言的构词规则 • 单词:逻辑上紧密相连的一组字符,这些字符具有集体含义。如:标识符、保留字(关键字或基本字)、算符、界符等。
逗号 , • 标识符 first • 逗号 , • 标识符 count • 冒号 : • 保留字 real • 分号 ; • 标识符 sum • 赋值号 := • 标识符 first • 加号 + • 标识符 count • 乘号 * • 整数 10 • 保留字 end • 界符 . 例. 某源程序片断如下: begin var sum , first , count : real ; sum := first + count * 10 end. 扫描后得到如下单词序列: 1.保 留字 var 2.标识符 sum 3.保留字 begin
2.语法分析 语法分析的任务是在词法分析的基础上,根据语言的语法规则(文法规则)把单词符号流分解成各类语法单位(语法范畴),如“短语”、“子句”、“句子(语句)”、“程序段”和“程序”。 通过语法分析确定整个输入串是否构成一个语法上正确的程序。
id1:=id2+id3*10 的语法树 赋值语句 标识符 := 表达式 id1 sum 表达式 + 表达式 表达式 标识符 表达式 * 标识符 整数 id2first 10 id3count
:= id1 + id2 * id3 10 id1:=id2+id3*10 的语法树的另一种形式:
程序结构的递归表示 • 表达式的表示: • 任何标识符是表达式。 • 任何常数(整常数、实常数)是表达式。 • 若表达式1和表达式2都是表达式,那么 • 表达式1+表达式2 • 表达式1*表达式2 • (表达式1) • 都是表达式。
语句的表示: 1.标识符:=表达式 是语句。 2.while (表达式) do 语句和if (表达式) then 语句 else 语句都是语句。 3. 语义分析 语义分析阶段的任务是审查源程序有无语义错误。源程序中有些语法成分,按照语法规则去判断,它是正确的,但它不符合语义规则。比如使用了没有声明的变量;或者给一个过程名赋值;或者调用函数时参数类型不合适或者参加运的两个变量类型不匹配等等。
比如下边的程序片段:int arr[2],c;c = arr1 * 10 ; 其中的赋值语句是符合语法规则的,但是因为没有声 明变量arr1,而存在语义错。 一般,语义分析的工作还包括类型审查,类型提升以及为代码生成阶段收集类型信息. 比如审查每个算符是否实施于具有语言规范允许的运算对象,当不符合语言规范时,编译程序应报告错误。又比如对实数用作数组下标的情况报告错误。又比如某些语言规定运算对象可被强制,那么当二目运算施于一个整型量和
和一个实型量时,编译程序应将整型量自动转换成实型量而不能认为是源程序的错误,或者给出警告信息后将整型量自动转换成实型量。假如在赋值语句sum∶=first+count*10中,算符*的两个运算对象分别是count和10,而count是实型变量,10是整型量.语义分析阶段进行类型审查之后,将整型量提升为实型量.在语法分析所得到的分析树上增加一个一目算符结点,这个结点的名称为inttoreal,表示进行将整型量变成实型量的语义处理,那么,前图的树变成下图所示:和一个实型量时,编译程序应将整型量自动转换成实型量而不能认为是源程序的错误,或者给出警告信息后将整型量自动转换成实型量。假如在赋值语句sum∶=first+count*10中,算符*的两个运算对象分别是count和10,而count是实型变量,10是整型量.语义分析阶段进行类型审查之后,将整型量提升为实型量.在语法分析所得到的分析树上增加一个一目算符结点,这个结点的名称为inttoreal,表示进行将整型量变成实型量的语义处理,那么,前图的树变成下图所示:
:= id1 + id2 * id3 inttoreal 10 如:类型检查。 sum := first + count * 10
4.中间代码生成阶段在进行了上述的词法分析,语法分析和语义分析阶段的工作之后,有的编译程序将源程序变成一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。所谓"中间代码"是一种结构简单、含义明确的记号系统,这种记号系统可以设计为多种多样的形式,重要的设计原则为两点:一是容易生成;二是容易将它翻译成目标代码。很多编译程序采用了一种近似"三地址指令"的"四元式"中间代码,这种四元式的形式为:(运算符,运算对象1,运算对象2,结果)。4.中间代码生成阶段在进行了上述的词法分析,语法分析和语义分析阶段的工作之后,有的编译程序将源程序变成一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。所谓"中间代码"是一种结构简单、含义明确的记号系统,这种记号系统可以设计为多种多样的形式,重要的设计原则为两点:一是容易生成;二是容易将它翻译成目标代码。很多编译程序采用了一种近似"三地址指令"的"四元式"中间代码,这种四元式的形式为:(运算符,运算对象1,运算对象2,结果)。
如:源程序 sum := first + count * 10 生成的四元式可以是: ( inttoreal 10 - t1 ) ( * id3 t1 t2 ) ( + id2 t2 t3 ) ( := t3 - id1) 其中ti(i=1,2,3)是编译程序生成的临时名字,用于存放运算结果的。
5.代码优化 优化的任务是对前阶段产生的中间代码进行等价变换或改造,以期获得更为高效(节省时间和空间)的目标代码。常用的优化措施有删除冗余运算、删除无用赋值、合并已知量、循环优化等。例如,其值并不随循环而发生变化的运算可提到进入循环前计算一次,而不必在循环中每次循环都进行计算。优化所遵循的原则是程序的等价变换规则。
如: ( inttoreal 10 - t1 ) ( * id3 t1 t2 ) ( + id2 t2 t3 ) ( := t3 - id1) 优化后代码: ( * id3 10.0 t1 ) ( + id2 t1 id1 )
6.目标代码生成 任务:把中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编指令代码。 特点:与硬件系统结构和指令含义有关,涉及到硬件系统功能部件的运用、机器指令的选择、各种数据类型变量的存储空间分配以及寄存器和后缓寄存器的调度等。
例:sum := first + count * 10 优化代码: ( * id3 10.0 t1 ) ( + id2 t1 id1 ) 汇编代码: MOVF id3, R2 MULF #10.0, R2 MOVF id2, R1 ADDF R1, R2 MOV R1, id1
编译过程中源程序的各种信息被保留在不同的表格里,编译各阶段的工作都涉及到构造、查找或更新有关的表格,编译过程的绝大部分时间是用在造表、查表和更新表格的事务上,因此,编译程序中还应包括一个表格管理程序。编译过程中源程序的各种信息被保留在不同的表格里,编译各阶段的工作都涉及到构造、查找或更新有关的表格,编译过程的绝大部分时间是用在造表、查表和更新表格的事务上,因此,编译程序中还应包括一个表格管理程序。 出错处理与编译的各个阶段都有联系,与前三个阶段的联系尤为密切。出错处理程序应在发现错误后,将错误的有关信息如错误类型、出错地点等向用户报告。此外,为了尽可能多地发现错误,应在发现错误后还能继续编译下去,以便发现更多的错误。
源程序 1.3 编译程序的结构 词法分析程序 表 格 管 理 程 序 出 错 处 理 程 序 语法分析程序 语义分析程序 中间代码生成程序 代码优化程序 目标代码生成程序 目标程序
前端(front end):主要依赖于源语言而与目标机器无关的编译阶段。如:词法分析、语法分析、语义分析、中间代码生成、部分优化工作、与前端有关的出错处理工作和符号表管理工作。 后端(back end):依赖于目标机而一般不依赖于源语言,只与中间代码有关的编译阶段。如:目标代码生成,以及相关出错处理和符号表操作。 遍(趟):对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程。每一遍扫视可完成上述一个阶段或多个阶段的工作 1.4 编译阶段的组合
在实际的编译系统的设计中,编译的几个阶段的工作究竟应该怎样组合,即编译程序究竟分成几遍,参考的因素主要是源语言和机器(目标机)的特征。比如源语言的结构直接影响编译的遍的划分;像PL/1或ALGOL 68 那样的语言,允许名字的说明出现在名字的使用之后,那么在看到名字之前是不便为包含该名字的表达式生成代码的,这种语言的编译程序至少分成两遍才容易生成代码。另外机器的情况,即编译程序工作的环境也影响编译程序的遍数的划分。遍数多一点,整个编译程序的逻辑结构可能清晰些,但遍数多即意味着增加读写中间文件的次数,势必消耗较多时间,一般会比一遍的编译要慢。
虽然从编译器工作效率的角度讲,一遍扫描是最好的。但是,由于各种原因,若干遍扫描也是不可少的。例如,由于中间代码界定了前端和后端,并且两个部分的工作有很大区别,因此,往往至少将前端作为一遍扫描。另外,为了生成高质量的目标代码,需要对中间代码进行优化,而全局性的控制流和数据流分析也应该是对中间代码的一遍扫描。总之,对一个具体的编译器,要确定用几遍扫描来完成,需要综合考虑各种因素,从中取得最佳折中。
编译技术的发展 据说第一个编译程序的出现是在20世纪50年代早期,很难讲出确切的时间,因为当初大量的实验和实现工作是由不同的小组独立完成的,多数早期的编译工作是将算术公式翻译成机器代码。用现在的标准来衡量,当时的编译程序能完成的工作十分初步,如只允许简单的单目运算,数据元素的命名方式有很多限制。然而它们奠定了对高级语言编译系统的研究和开发的基础。20世纪50年代中期出现了FORTRAN等一批高级语言,相应的一批编译系统开发成功。随着编译技术的发展和社会对编译程序需求的不断增长,20世纪50年代末有人开始研究编译程序的自动生成工具,提出并研制编译程序的编译程序。它的功能是以任一语言的词法规则、语法规则和语义解释出发,自动产生该语言的编译程序。 1.4 编译技术的发展和应用
目前很多自动生成工具已广泛使用,如词法分析程序的生成系统LEX,语法分析程序的生成系统YACC等。20世纪60年代起,不断有人使用自展技术来构造编译程序。自展的主要特征是用被编译的语言来书写该语言自身的编译程序。1971年,PASCAL的编译程序用自展技术生成后,其影响就越来越大。随着并行技术和并行语言的发展,处理并行语言的并行编译技术,将串行程序转换成并行程序的自动并行编译技术也正在深入研究之中。 另外嵌入式应用迅速增长的需求,推动了交叉编译技术的发展.还有系统芯片设计方法和关键EDA技术的研究,也带动了专用语言VHDL等及其编译技术的不断深化。
应用: 1.语言的结构化编辑器 2.语言程序的调试工具 3.语言程序测试工具 4.高级语言之间的转换工具
语言的结构化编辑器 结构化编辑器是引导用户在语言的语法制导下编制程序,能自动地提供关键字和与其匹配的关键字,如if后必须有then,begin和end的配对,左右括号的配对等,这样可以减少语法上的错误,可加快对源程序的调试,提高效率和质量。
语言程序的调试工具 调试是软件开发过程中一个重要环节,结构化编辑器只能解决语法错误的问题,而对一个已通过编译的程序来说,需进一步了解的是程序执行的结果与编程人员的意图是否一致,程序的执行是否实现预计的算法和功能。这种对算法的错误或程序没能反应算法的功能等错误就需用调试器来协助解决。调试器的功能愈强,实现愈复杂,但它必须与语法分析、语义处理有紧密联系。
语言程序测试工具 语言程序的测试工具有两种:静态分析器和动态测试器。静态分析器是对源程序进行静态地分析。它对源程序进行语法分析并制定相应表格,检查变量定值与引用的关系。如某变量未被赋值就被引用,或定值后未被引用,或多余的源代码等一些编译程序的语法分析发现不了的错误。动态测试工具是在源程序的适当位置插入某些信息,并用测试用例记录(显示语句或函数)程序运行时的实际路径。将运行结果与期望的结果进行比较分析,帮助编程人员查找问题。这种测试工具在国内已有开发,如FORTRAN语言和C语言的测试工具。
高级语言之间的转换工具 由于计算机硬件的不断更新换代,更新更好的程序设计语言的推出为提高计算机的使用效率提供了良好条件,然而一些已有的非常成熟的软件如何在新机器新语言情况下使用呢?为了减少重新编制程序所耗费的人力和时间,就要解决如何把一种高级语言转换成另一种高级语言,乃至汇编语言转换成高级语言的问题。这种转换工作要对被转换的语言进行词法和语法分析,只不过生成的目标语言是另一种高级语言而已。这与实现一个完整的编译程序相比工作量要少些。在国内已研制出C,PASCAL,FORTRAN到Ada的翻译器和IBM 4700汇编到C的转换器,其效果很好。近年来,由于JAVA语言的发展,国内外也已研制出不少其他语言到JAVA的转换系统,如c到JAVA的转换系统,cobol到JAVA的转换系统等等.
【本章小结】 1.从功能上说编译程序是一个翻译程序,将高级语言的程序翻译成低级语言的程序。 2.以源程序在编译过程中的不同表示形式初步理解编译各阶段的工作。
【教学内容】 第一章 编译程序概述 第二章 PL/0编译程序的实现 第三章 词法分析程序 第四章 文法和语言 第五章 自顶向下语法分析方法 第六章 算符优先分析法 第七章 LR分析法 学习中的几个问题
【学习目标】 1.清楚地理解编译程序是如何工作的。 2.基本掌握程序设计语言的主要特性和实现途径。 3.学习应用一些标准的分析算法和技术。
【学习要求及方法】 本课程是一门理论性和实践性都很强的课程,所以不仅需要做一定量的习题练习,以加深对理论的理解和掌握,同时也需进行必要的上机实践,以加深对理论的理解和提高对技术的应用。
【参考书目】 ALFRED V. AHO, RAVISETHI, JEFFREY D. ULLMAN, Compilers Principles, Techniques and Tools ADDISSON-WESLEY 1986 (本教材主要由此书翻译)。 陈火旺 刘春林等 《程序设计语言编译原理》(第3版) 国防工业出版社