编译原理与技术教学课件汇总完整版电子教案全书课件

上传人:m**** 文档编号:592021459 上传时间:2024-09-19 格式:PPT 页数:784 大小:15.02MB
返回 下载 相关 举报
编译原理与技术教学课件汇总完整版电子教案全书课件_第1页
第1页 / 共784页
编译原理与技术教学课件汇总完整版电子教案全书课件_第2页
第2页 / 共784页
编译原理与技术教学课件汇总完整版电子教案全书课件_第3页
第3页 / 共784页
编译原理与技术教学课件汇总完整版电子教案全书课件_第4页
第4页 / 共784页
编译原理与技术教学课件汇总完整版电子教案全书课件_第5页
第5页 / 共784页
点击查看更多>>
资源描述

《编译原理与技术教学课件汇总完整版电子教案全书课件》由会员分享,可在线阅读,更多相关《编译原理与技术教学课件汇总完整版电子教案全书课件(784页珍藏版)》请在金锄头文库上搜索。

1、编译原理与技术编译原理与技术第第1章章概论概论编译原理与技术编译原理与技术2主要内容主要内容u为什么学习编译为什么学习编译u什么叫编译程序什么叫编译程序u编译过程概述编译过程概述u编译程序的构成编译程序的构成u与编译有关的概念和技术与编译有关的概念和技术u如何开发编译程序如何开发编译程序u编译系统以及其它相关程序编译系统以及其它相关程序编译原理与技术编译原理与技术31.1为什么学习编译为什么学习编译u编译程序构造的原理和技术一直属于计算编译程序构造的原理和技术一直属于计算机科学必备的专业基础知识。机科学必备的专业基础知识。u是计算机科学中一个非常成功的分支,也是计算机科学中一个非常成功的分支,

2、也是最早获得成功的分支之一。是最早获得成功的分支之一。u它所建立的理论、技术和方法值得深入研它所建立的理论、技术和方法值得深入研究和学习。究和学习。编译构造正确地建立了研究的问题领域和研编译构造正确地建立了研究的问题领域和研究方式。究方式。编译原理与技术编译原理与技术41.1为什么学习编译为什么学习编译针对编译程序构造的某些部分已经开发了标针对编译程序构造的某些部分已经开发了标准的形式化技术,包括有限自动机理论、上准的形式化技术,包括有限自动机理论、上下文无关文法、正规表达式、属性文法、机下文无关文法、正规表达式、属性文法、机器代码描述、数据流分析方程式等。器代码描述、数据流分析方程式等。编译

3、程序包含许多普遍使用的数据结构和算编译程序包含许多普遍使用的数据结构和算法,例如散列法(哈希算法)、栈机制、堆法,例如散列法(哈希算法)、栈机制、堆机制、垃圾收集、集合算法、表驱动算法。机制、垃圾收集、集合算法、表驱动算法。编译程序的许多构造技术已经得到了广泛的编译程序的许多构造技术已经得到了广泛的应用。应用。学习编译原理和技术还有助于我们理解程序学习编译原理和技术还有助于我们理解程序设计语言,编写优秀的软件。设计语言,编写优秀的软件。编译原理与技术编译原理与技术51.2什么叫编译程序什么叫编译程序u概念概念翻译程序或翻译器是把一种语言(翻译程序或翻译器是把一种语言(源语言源语言)转换成等价的

4、另外一种语言(转换成等价的另外一种语言(目标语言目标语言)的)的程序。程序。如果源语言是高级编程语言,目标语言是机如果源语言是高级编程语言,目标语言是机器代码和汇编语言这样的低级语言,这类翻器代码和汇编语言这样的低级语言,这类翻译程序就叫做译程序就叫做编译程序编译程序或或编译器编译器。编译原理与技术编译原理与技术61.2什么叫编译程序什么叫编译程序编译执行方式:把源程序用编译程序翻译成编译执行方式:把源程序用编译程序翻译成机器可以执行的目标程序或目标代码,然后机器可以执行的目标程序或目标代码,然后才能接受输入数据运行。才能接受输入数据运行。编译程序源程序输入数据目标程序计算机系统计算机系统目标

5、程序运行结果编译原理与技术编译原理与技术71.2什么叫编译程序什么叫编译程序解释程序:解释程序不产生源程序的目标代解释程序:解释程序不产生源程序的目标代码,而是对源程序逐条语句的分析,根据每码,而是对源程序逐条语句的分析,根据每个语句的含义执行产生结果。个语句的含义执行产生结果。解释程序解释程序输入数据源程序计算机系统计算机系统运行结果编译原理与技术编译原理与技术81.3编译过程概述编译过程概述u词法分析词法分析词法分析的任务是逐步地扫描和分解构成源词法分析的任务是逐步地扫描和分解构成源程序的字符串,识别出一个一个的单词符号程序的字符串,识别出一个一个的单词符号或符号。或符号。编译程序的词法分

6、析也叫编译程序的词法分析也叫词法扫描词法扫描或线或线性扫性扫描描。计算机高级语言的单词符号通常包括:标识计算机高级语言的单词符号通常包括:标识符、关键字或基本字、标点符号、常数、运符、关键字或基本字、标点符号、常数、运算符、分隔符等类型。算符、分隔符等类型。编译原理与技术编译原理与技术91.3编译过程概述编译过程概述符号符号类型类型while关键字(分隔符i标识符运算符100整常数)分隔符sum标识符=运算符sum标识符i标识符+运算符;分隔符例子例子1.1:while(i100)sum=sum+i词法分析的结果识别出的单词词法分析的结果识别出的单词编译原理与技术编译原理与技术101.3编译过

7、程概述编译过程概述u语法分析语法分析语法分析的任务是在词法分析基础上,根据语法分析的任务是在词法分析基础上,根据语言的语法规则把单词符号串分解成各类语语言的语法规则把单词符号串分解成各类语法单元(语法范畴、语法短语)法单元(语法范畴、语法短语)l例如例如“短语短语”、“子句子句”、“语句语句”、“程序段程序段”、“函数函数”和和“程序程序”等。等。语法分析是把线形序列的单词符号,根据语语法分析是把线形序列的单词符号,根据语言的语法规则,按照层次分解,结果通常表言的语法规则,按照层次分解,结果通常表示成语法示成语法分析树分析树。编译原理与技术编译原理与技术111.3编译过程概述编译过程概述例子例

8、子1.1while(i100)sum=sum+i语法分析树语法分析树while语句分隔符分隔符(分隔符)表达式表达式;表达式循环体语句运算符赋值表达式变量常量100i变量表达式复合赋值运算符+sum+=运算符表达式变量i编译原理与技术编译原理与技术121.3编译过程概述编译过程概述u语义分析和中间代码生成语义分析和中间代码生成语义分析的任务是检查程序语义的正确性,语义分析的任务是检查程序语义的正确性,解释程序结构的含义。解释程序结构的含义。检查变量是否有定义,变量在使用前是否具检查变量是否有定义,变量在使用前是否具有值,数值是否溢出等,其中的一个重要部有值,数值是否溢出等,其中的一个重要部分是

9、进行类型的检查和转换。分是进行类型的检查和转换。语义分析完成之后,编译程序通常就依据语语义分析完成之后,编译程序通常就依据语言的语义规则、利用语法制导技术把源程序言的语义规则、利用语法制导技术把源程序翻译成某种中间代码。翻译成某种中间代码。编译原理与技术编译原理与技术131.3编译过程概述编译过程概述中间代码是一种定义明确、便于处理、独立中间代码是一种定义明确、便于处理、独立于计算机硬件的记号系统,可以认为是一种于计算机硬件的记号系统,可以认为是一种抽象机的程序。其中一类是三地址代码,很抽象机的程序。其中一类是三地址代码,很象机器的汇编语言象机器的汇编语言Lbegin:ifi100gotoLb

10、odygotoLendLbody:t1:=sum+isum:=t1t2:=i+1i:=t2gotoLbeginLend:编译原理与技术编译原理与技术141.3编译过程概述编译过程概述u中间代码优化中间代码优化主要任务是对前一阶段产生的中间代码进行主要任务是对前一阶段产生的中间代码进行等价变换,以便产生速度快、空间小的目标等价变换,以便产生速度快、空间小的目标代码。代码。Lbegin:ifi100gotoLbodygotoLendLbody:sum:=sum+ii:=i+1gotoLbeginLend:编译原理与技术编译原理与技术151.3编译过程概述编译过程概述u目标代码生成目标代码生成目标代

11、码生成的主要任务是把(经过优化处目标代码生成的主要任务是把(经过优化处理的)中间代码翻译成特定的机器指令或汇理的)中间代码翻译成特定的机器指令或汇编程序。编程序。这个阶段的工作依赖于计算机的硬件结构和这个阶段的工作依赖于计算机的硬件结构和指令系统,主要涉及到机器指令的选择、各指令系统,主要涉及到机器指令的选择、各种类型变量存储空间的分配,以及寄存器的种类型变量存储空间的分配,以及寄存器的分配和调度,等等。分配和调度,等等。编译原理与技术编译原理与技术161.3编译过程概述编译过程概述u目标代码生成目标代码生成MOV#100,R0/把常数100存入寄存器R0MOVi,R1/把变量i的值存入寄存器

12、R1MOVsum,R2/把变量m的值存入寄存器R2Lbegin:CMPR1,R0/比较R1和R0的值,结果存入状态寄存器CTJLend/状态寄存器CT1或2,即R1R0,程序转入单元LendADDR1,R2/把寄存器R1加R2,结果送入R2INCR1/寄存器R1的值加1JLbegin/无条件转移到地址LbeginLend:编译原理与技术编译原理与技术171.4编译程序的构成编译程序的构成符号表管理错误处理词法分析器语法分析器语义分析与中间代码生器代码优化器目标代码生成器目标代码单词符号源程序语法单元中间代码中间代码编译原理与技术编译原理与技术181.4编译程序的构成基本功能编译程序的构成基本功

13、能词法分析器,又叫扫描器,对输入的源程序执行词法词法分析器,又叫扫描器,对输入的源程序执行词法分析工作,输出单词符号序列。分析工作,输出单词符号序列。语法分析器,又叫分析器,对单词符号序列进行语法语法分析器,又叫分析器,对单词符号序列进行语法分析,识别出各类语法单元,判断输入的符号串是否分析,识别出各类语法单元,判断输入的符号串是否构成语法正确的构成语法正确的“程序程序”。语义分析与中间代码生成器,对语法正确的各类程序语义分析与中间代码生成器,对语法正确的各类程序单元进行语义分析,并把它们翻译成一定形式的中间单元进行语义分析,并把它们翻译成一定形式的中间代码。代码。代码优化器,执行对中间代码的

14、优化处理,以提高代代码优化器,执行对中间代码的优化处理,以提高代码的执行效率。码的执行效率。目标代码生成器,根据特定的机器把中间代码翻译成目标代码,目标代码生成器,根据特定的机器把中间代码翻译成目标代码,并进行优化处理。并进行优化处理。编译原理与技术编译原理与技术191.4编译程序的构成辅助功能编译程序的构成辅助功能符号表管理:把编译程序中的各种符号合理符号表管理:把编译程序中的各种符号合理地组织和管理,方便符号信息的添加、查询、地组织和管理,方便符号信息的添加、查询、更新和删除。更新和删除。错误诊断和报告错误诊断和报告:有效地识别、诊断、分析:有效地识别、诊断、分析和报告程序中的各种错误。和

15、报告程序中的各种错误。分类:语法错误(词法错误和句法错误)和分类:语法错误(词法错误和句法错误)和语义错误这两类。语义错误这两类。编译原理与技术编译原理与技术201.5其它与编译有关的概念和技术其它与编译有关的概念和技术u遍(趟)遍(趟)在编译的具体实现时,往往根据不同的源语在编译的具体实现时,往往根据不同的源语言、设计要求、使用对象以及编译程序所在言、设计要求、使用对象以及编译程序所在宿主机的内存等硬件条件,将编译过程组织宿主机的内存等硬件条件,将编译过程组织为若干遍(趟)。一个编译程序最终经过几为若干遍(趟)。一个编译程序最终经过几遍完成,就称为几遍编译。遍完成,就称为几遍编译。编译原理与

16、技术编译原理与技术211.5其它与编译有关的概念和技术其它与编译有关的概念和技术u编译的前端和后端编译的前端和后端编译前端只依赖于源程序,独立于目标计算机。编译编译前端只依赖于源程序,独立于目标计算机。编译前端的工作包括词法分析、语法分析、语义分析、中前端的工作包括词法分析、语法分析、语义分析、中间代码生成及其优化,文法错误的处理和符号表的组间代码生成及其优化,文法错误的处理和符号表的组织也在编译前端完成。织也在编译前端完成。编译后端的工作主要是目标代码的生成和优化,独立编译后端的工作主要是目标代码的生成和优化,独立于源程序,完全依赖于目标机器和中间代码。于源程序,完全依赖于目标机器和中间代码

17、。把编译程序分成前端和后端已经成为目前编译程序的把编译程序分成前端和后端已经成为目前编译程序的设计实践,其显著优点是,可以优化配置不同的编译设计实践,其显著优点是,可以优化配置不同的编译程序组合,实现编译的重用,保持语言与机器的独立程序组合,实现编译的重用,保持语言与机器的独立性。性。编译原理与技术编译原理与技术221.5其它与编译有关的概念和技术其它与编译有关的概念和技术u编译程序的分类编译程序的分类诊断型编译程序:专门用于帮助程序的开发诊断型编译程序:专门用于帮助程序的开发和调试,它们系统地分析程序,发现程序中和调试,它们系统地分析程序,发现程序中的错误,智能地校正一些错误。的错误,智能地

18、校正一些错误。优化型编译程序:这类编译程序着重于提高优化型编译程序:这类编译程序着重于提高目标代码的时空效率,使得产生的目标代码目标代码的时空效率,使得产生的目标代码既占用较少的存储空间,又运行的快。既占用较少的存储空间,又运行的快。编译原理与技术编译原理与技术231.5其它与编译有关的概念和技术其它与编译有关的概念和技术交叉型编译程序:运行目标程序的计算机和交叉型编译程序:运行目标程序的计算机和运行编译程序的计算机的型号不相同运行编译程序的计算机的型号不相同。利用编译前端和后端的技术,可以设计与目利用编译前端和后端的技术,可以设计与目标机无关的编译程序,利用编译后端就可以标机无关的编译程序,

19、利用编译后端就可以改变目标计算机,这样编译方便移植,称为改变目标计算机,这样编译方便移植,称为可变目标型编译程序。可变目标型编译程序。编译原理与技术编译原理与技术241.6编译技术和软件工具编译技术和软件工具u语法制导编辑器语法制导编辑器这类工具运用程序语言的语法知识,在用户这类工具运用程序语言的语法知识,在用户编写程序的时候按照词法和语法分析的信息编写程序的时候按照词法和语法分析的信息提供智能的帮助,包括自动地提供关键字及提供智能的帮助,包括自动地提供关键字及其匹配的关键字、左右括号的配对、对象的其匹配的关键字、左右括号的配对、对象的属性和操作,等等。属性和操作,等等。u程序调试工具程序调试

20、工具调试的目的是根据程序的异常,追踪和确定调试的目的是根据程序的异常,追踪和确定错误在程序中的具体位置,并且修改程序,错误在程序中的具体位置,并且修改程序,消除错误。消除错误。编译原理与技术编译原理与技术251.6编译技术和软件工具编译技术和软件工具u程序测试工具程序测试工具程序测试是为了发现错误而执行程序的过程,程序测试是为了发现错误而执行程序的过程,基于编译技术的测试辅助工具可以分为静态基于编译技术的测试辅助工具可以分为静态分析器和动态测试工具。分析器和动态测试工具。u程序理解工具程序理解工具在软件测试、软件维护以及软件的再向工程在软件测试、软件维护以及软件的再向工程和逆向工程等工作中,需

21、要人们理解和分析和逆向工程等工作中,需要人们理解和分析程序,得到需要的软件信息,这类工具称为程序,得到需要的软件信息,这类工具称为程序理解工具。程序理解工具。编译原理与技术编译原理与技术261.7如何开发编译程序如何开发编译程序u手工编写编译程序手工编写编译程序u编译程序的自动生成技术编译程序的自动生成技术u编译程序的自展技术Ln=LL0L1.编译原理与技术编译原理与技术271.7如何开发编译程序如何开发编译程序u编译程序的移植技术编译程序的移植技术用宿主计算机上的高级语言编写一个能在另用宿主计算机上的高级语言编写一个能在另外类型目标机上运行的编译程序外类型目标机上运行的编译程序ABHHKMA

22、BK图1.7把机器H上的编译移植到机器K上编译原理与技术编译原理与技术281.7编译系统以及其它相关程序编译系统以及其它相关程序编译系统预处理器源程序编辑器修改后的源程序汇编程序可重定位的目标程序可执行的目标程序函数库可重定位的目标文件连接器与加载器特性器调试器解释器配置与版本控制器源程序汇编器编译器源程序编译原理与技术编译原理与技术291.7编译系统以及其它相关程序编译系统以及其它相关程序编辑器:编辑器:程序员借助编辑器编写源程序,由程序员借助编辑器编写源程序,由编辑器产生出标准的正文文件(如编辑器产生出标准的正文文件(如ASCII文件)文件)作为编译程序的输入。作为编译程序的输入。预处理器

23、:预处理器:它是在编译程序真正开始翻译源它是在编译程序真正开始翻译源程序之前调用的一个独立的程序,以便加快程序之前调用的一个独立的程序,以便加快和简化翻译工作。和简化翻译工作。汇编器汇编器:汇编器把汇编语言代码翻译成一个汇编器把汇编语言代码翻译成一个特定的机器指令序列。特定的机器指令序列。连接器:连接器:搜集和组织程序所需要的不同代码搜集和组织程序所需要的不同代码和数据,把它们连接成可以执行的目标代码和数据,把它们连接成可以执行的目标代码的工具。的工具。编译原理与技术编译原理与技术301.7编译系统以及其它相关程序编译系统以及其它相关程序装载器:装载器:给程序在内存器中分配一个起始地给程序在内

24、存器中分配一个起始地址,载入目标机器,以便程序中的各个符号址,载入目标机器,以便程序中的各个符号通过相对地址可以真实地访问存储器。通过相对地址可以真实地访问存储器。调试器调试器:用来确定编译过的程序在运行时的用来确定编译过的程序在运行时的错误。错误。特性器:特性器:这是搜集运行程序行为特征的统计这是搜集运行程序行为特征的统计数据的软件工具。数据的软件工具。配置与版本控制器配置与版本控制器:管理和维护每个独立的:管理和维护每个独立的源程序模块、编译模块、数据文件及其每个源程序模块、编译模块、数据文件及其每个文件的修改历史信息包括模块连接、加载的文件的修改历史信息包括模块连接、加载的信息等的工具。

25、信息等的工具。编译原理与技术编译原理与技术第第2章章词法分析词法分析主要内容主要内容u词法分析器的设计词法分析器的设计u词法分析器的一种手工实现词法分析器的一种手工实现u正规表达式正规表达式u有限自动机有限自动机u词法分析的自动生成器词法分析的自动生成器Lex编译原理与技术编译原理与技术32词法分析器在编译中的位置词法分析器在编译中的位置编译原理与技术编译原理与技术33词法分析器语法分析器符号表源程序单词取下一个单词单词记号2.1词法分析器的设计词法分析器的设计u词法分析器的基本功能词法分析器的基本功能按照语言的定义规则,逐个地读入源程序的按照语言的定义规则,逐个地读入源程序的符号,识别出对语

26、言有意义的符号串,即单符号,识别出对语言有意义的符号串,即单词符号;词符号;分析单词记号的属性,并把单词记号及其属分析单词记号的属性,并把单词记号及其属性填写在符号表中;性填写在符号表中;同时把源程序改造成等价的计算机内部表示同时把源程序改造成等价的计算机内部表示单词记号,以便编译的后续阶段使用。单词记号,以便编译的后续阶段使用。还要对源程序进行预处理工作,包括:删除还要对源程序进行预处理工作,包括:删除源程序中的空格、制表符、换行、注释等不源程序中的空格、制表符、换行、注释等不影响程序语法、语义的结构。影响程序语法、语义的结构。编译原理与技术编译原理与技术342.1词法分析器的设计词法分析器

27、的设计u高级程序语言的五种单词记号:高级程序语言的五种单词记号:保留字保留字是程序语言定义的具有固定意义的英文单词,是程序语言定义的具有固定意义的英文单词,有时称为基本字或关键字。例如,在有时称为基本字或关键字。例如,在C+中,中,char、float、extern、friend、switch、new都是关键字。保都是关键字。保留字一般不能另作它用。留字一般不能另作它用。标识符标识符表示各种名字的字符串,如变量名、类型名、表示各种名字的字符串,如变量名、类型名、函数名、对象名等。函数名、对象名等。运算符运算符如如+、 、=、=等。等。常量常量常量的类型一般有整型、实型、布尔型、文字常量的类型一

28、般有整型、实型、布尔型、文字型等。型等。分界符分界符如分号、括号、注释标记如分号、括号、注释标记/*、*/等。等。编译原理与技术编译原理与技术352.1词法分析器的设计词法分析器的设计u词法分析器的输出词法分析器的输出单词记号一般采用形式为单词记号一般采用形式为的二元式。的二元式。单词的种别是语法分析需要的信息,通常用单词的种别是语法分析需要的信息,通常用整数表示;整数表示;单词的属性值则是编译的语义分析和代码生单词的属性值则是编译的语义分析和代码生成等阶段需要的信息。成等阶段需要的信息。编译原理与技术编译原理与技术362.1词法分析器的设计词法分析器的设计3,编译原理与技术编译原理与技术37

29、例例2.1:假如保留字的编码是假如保留字的编码是1,标识符的为,标识符的为2,运算符为,运算符为3,分界符为分界符为4,整型常量为,整型常量为10,实型常量为实型常量为11。那么,对于源。那么,对于源程序代码:程序代码:for(i=1,sum=9.8;i=100;i+)sum+=i 3.14;词法分析器产生的结果是单词词法分析器产生的结果是单词记号序列记号序列2.1词法分析器的设计词法分析器的设计u词法扫描器与符号表词法扫描器与符号表对符号表的操作主要是填表、查询和更新。对符号表的操作主要是填表、查询和更新。每当词法分析器识别了一个单词的时候,第一项工作每当词法分析器识别了一个单词的时候,第一

30、项工作就是查询符号表。对于不同的单词种别,查询的方式就是查询符号表。对于不同的单词种别,查询的方式和随后的处理完全不同。例如,对于关键字、分界符和随后的处理完全不同。例如,对于关键字、分界符和运算符等,只需在各自的符号表中查询,获得并记和运算符等,只需在各自的符号表中查询,获得并记录其它属性值,生成相应的单词记号。录其它属性值,生成相应的单词记号。处理常量,特别是处理标识符要复杂的多,而且仅仅处理常量,特别是处理标识符要复杂的多,而且仅仅在词法分析阶段是无法获得一个标识符的所有信息。在词法分析阶段是无法获得一个标识符的所有信息。当词法扫描器识别了一个标识符的时候,首先查询关当词法扫描器识别了一

31、个标识符的时候,首先查询关键字表,看它是否是关键字;如果不是,还要在标识键字表,看它是否是关键字;如果不是,还要在标识符表中查询,看它是否已经存在,如果不存在就把它符表中查询,看它是否已经存在,如果不存在就把它填入标识符表,并填入种别、类型等信息。填入标识符表,并填入种别、类型等信息。编译原理与技术编译原理与技术382.1词法分析器的设计词法分析器的设计u词法分析器的两种实现模式词法分析器的两种实现模式完全独立模式和相对独立模式完全独立模式和相对独立模式在完全独立模式下,词法分析器作为编译的在完全独立模式下,词法分析器作为编译的子系统单独地运行一趟,扫描整个源程序,子系统单独地运行一趟,扫描整

32、个源程序,把识别的单词序列以机器内码的形式输出在把识别的单词序列以机器内码的形式输出在一个中间文件上,供为语法分析使用。一个中间文件上,供为语法分析使用。编译原理与技术编译原理与技术392.1词法分析器的设计词法分析器的设计u完全独立模式的好处完全独立模式的好处编译程序结构清晰、条理化而且便于高效地实现;编译程序结构清晰、条理化而且便于高效地实现;在设计高级语言时能独立地研究词法与语法两个方面在设计高级语言时能独立地研究词法与语法两个方面的特性;的特性;增强编译程序的可移植性:可以就同一个语言为不同增强编译程序的可移植性:可以就同一个语言为不同的机器写不同的词法分析器,而只编写一个共同的语的机

33、器写不同的词法分析器,而只编写一个共同的语法分析,使用这些词法分析器相同的单词机内表示;法分析,使用这些词法分析器相同的单词机内表示;把同一个词法由于单词记号的语法可以用较简单的文把同一个词法由于单词记号的语法可以用较简单的文法描述,把词法和语法分开,就能为这种文法建立有法描述,把词法和语法分开,就能为这种文法建立有效的特殊方法和自动构造技术。效的特殊方法和自动构造技术。编译原理与技术编译原理与技术402.1词法分析器的设计词法分析器的设计相对独立模式:词法分析器设计成一个子程相对独立模式:词法分析器设计成一个子程序,每当语法分析需要一个单词的时候,就序,每当语法分析需要一个单词的时候,就调用

34、该子程序。调用该子程序。相对独立模式的好处:词法分析器和语法分相对独立模式的好处:词法分析器和语法分析器被设计在同一趟,省去了存放单词的终析器被设计在同一趟,省去了存放单词的终结文件。结文件。编译原理与技术编译原理与技术412.1词法分析器的设计词法分析器的设计u词法错误的处理词法错误的处理在词法分析阶段发现的错误统称为词法错误,在词法分析阶段发现的错误统称为词法错误,它们大多是单词拼写错误,这或者是因为书它们大多是单词拼写错误,这或者是因为书写错误、或者因为键入错误,例如把关键字写错误、或者因为键入错误,例如把关键字拼写错。拼写错。对词法错误校正的常用策略是修补尝试,一对词法错误校正的常用策

35、略是修补尝试,一般包括:般包括:l删除一个多余的字符;删除一个多余的字符;l插入一个遗漏的字符;插入一个遗漏的字符;l用一个正确的字符替换一个不正确的字符;用一个正确的字符替换一个不正确的字符;l交换两个相邻的字符。交换两个相邻的字符。编译原理与技术编译原理与技术422.2词法分析器的一种手工实现词法分析器的一种手工实现u输入的预处理输入的预处理对于许多程序语言来说,空格、制表符、换对于许多程序语言来说,空格、制表符、换行符等编辑性字符除了出现在文字常量中,行符等编辑性字符除了出现在文字常量中,在其它任何地方的出现都没有意义,而注释在其它任何地方的出现都没有意义,而注释作为程序的重要文档几乎可

36、以出现在程序中作为程序的重要文档几乎可以出现在程序中的任何地方。它们的存在可以改善程序的可的任何地方。它们的存在可以改善程序的可读性和易理解性,却不影响程序的语法结构读性和易理解性,却不影响程序的语法结构和执行语义。和执行语义。通常在编译的词法分析阶段被预处理过程删通常在编译的词法分析阶段被预处理过程删除掉。除掉。编译原理与技术编译原理与技术432.2词法分析器的一种手工实现词法分析器的一种手工实现u输入的预处理输入的预处理l扫描器对缓冲区进行扫描时一般使用两个指针:一个指向当前正在识别的单词的起始位置,另一个用于向前搜索以寻找该单词的终点,两个指针之间的符号串就是要识别的单词符号。无论扫描缓

37、冲区设计的多大都不能保证单词符号不会超过其边界。扫描缓冲区一分为二的两段置.编译原理与技术编译原理与技术44.for(sum=0,i=1.搜索指针起点指针.Car.eel.2.2.2词法分析器的一种手工实现词法分析器的一种手工实现u超前搜索和最长匹配超前搜索和最长匹配为了识别一个更有意义的单词符号,在找到了可能是为了识别一个更有意义的单词符号,在找到了可能是单词符号的起点或者构成了单词部分时,扫描器并不单词符号的起点或者构成了单词部分时,扫描器并不满足,还要继续读入输入串,看是否能找到由更多符满足,还要继续读入输入串,看是否能找到由更多符号所组成的单词(即最长匹配),有时可能要扫描到号所组成的

38、单词(即最长匹配),有时可能要扫描到一个可以一个可以“断句断句”的符号(超前搜索),才能决定最的符号(超前搜索),才能决定最后一个扫描的符号不属于之前的符号串所构成的单词。后一个扫描的符号不属于之前的符号串所构成的单词。超前搜索符号通常是最长匹配单词的结束标志,可以超前搜索符号通常是最长匹配单词的结束标志,可以是空格符、回车符、制表符等可以被预处理掉的符号;是空格符、回车符、制表符等可以被预处理掉的符号;也可能是下一个单词记号的起始符。也可能是下一个单词记号的起始符。编译原理与技术编译原理与技术452.2词法分析器的一种手工实现词法分析器的一种手工实现u超前搜索和最长匹配的例子超前搜索和最长匹

39、配的例子在识别在识别“for”的时候,要扫描到左括弧的时候,要扫描到左括弧(时才知道,它不属于标识符的符号;时才知道,它不属于标识符的符号;当读到了当读到了,以便构造出小于等于,以便构造出小于等于“=”或不等于或不等于“”的比较运算符,否则,的比较运算符,否则,就构造小于运算符。就构造小于运算符。编译原理与技术编译原理与技术462.2词法分析器的一种手工实现词法分析器的一种手工实现u状态转换图状态转换图状态转换图是构造词法分析器的一个良好工具,它描状态转换图是构造词法分析器的一个良好工具,它描绘了为得到一个单词记号,词法分析器应该执行的动绘了为得到一个单词记号,词法分析器应该执行的动作。作。状

40、态转换图是一个有向图,结点代表状态,用圆圈表状态转换图是一个有向图,结点代表状态,用圆圈表示,内部用数字表示状态名称;示,内部用数字表示状态名称;状态之间由箭弧连接,箭弧上有符号作为标记,称为状态之间由箭弧连接,箭弧上有符号作为标记,称为从箭弧尾的离开状态读入标记符号以后转换到箭弧头从箭弧尾的离开状态读入标记符号以后转换到箭弧头的进入状态。的进入状态。若离开状态若离开状态s的某个标记为的某个标记为other,则表示离开,则表示离开s的其它的其它箭弧标记以外的任意符号。箭弧标记以外的任意符号。每个状态转换图中的状态数量有限,都有唯一的一个每个状态转换图中的状态数量有限,都有唯一的一个起始状态(本

41、书用一个进入的箭头表示)和至少一个起始状态(本书用一个进入的箭头表示)和至少一个终结状态(用双圈表示)。终结状态(用双圈表示)。编译原理与技术编译原理与技术472.2词法分析器的一种手工实现词法分析器的一种手工实现u状态转换图识别或接受一定的输入符号串状态转换图识别或接受一定的输入符号串从起始状态开始,读进输入符号串的一个符从起始状态开始,读进输入符号串的一个符号号a,沿着状态转换标记为,沿着状态转换标记为a进入下一个状态,进入下一个状态,重复执行直到进入终结状态。重复执行直到进入终结状态。即,如果存在一个从起始状态到终结状态的即,如果存在一个从起始状态到终结状态的路径,路径上的标记用连接运算

42、连接在一起路径,路径上的标记用连接运算连接在一起形成一个符号串,它和输入符号串相同,则形成一个符号串,它和输入符号串相同,则称该输入符号串可以接受。如果不能进入任称该输入符号串可以接受。如果不能进入任何一个终结状态,则称该状态转换图不能识何一个终结状态,则称该状态转换图不能识别或接受这个输入符号串别或接受这个输入符号串编译原理与技术编译原理与技术482.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术49digit01letterletter对于符号串var1,有状态序列,例例2.2:标识符一般定义为字母打头的字母数字序列标识符一般定义为字母打头的字母数字序列.2

43、.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术505other=1=032例例2.3:类似语言中的关系符的状态转换图。在终结状态加了星号*,表示在状态1、2和3都还不能确定它们是否是符合最长匹配准则的单词记号,还需要在读入一个字符才能确定。而为实现最长匹配的一个超前搜索符号“其它”则不属于这个单词,应该推给扫描缓冲区。2.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术51例例2.4:Pascal语言中数的状态转换图语言中数的状态转换图。在这个复杂的例子中,状态在这个复杂的例子中,状态3、5和和8分别表示识别是整数、不带指数部分分

44、别表示识别是整数、不带指数部分的实数以及带有指数部分的实数,但是,只能在超前搜索一个其它符号以的实数以及带有指数部分的实数,但是,只能在超前搜索一个其它符号以后,才能在状态后,才能在状态9确定识别了一个确定识别了一个Pascal的数。读者可以自己验证例子数的数。读者可以自己验证例子数2005,+1998, 81.07,2.003 6,看它们是否能被这个转换图所接受。,看它们是否能被这个转换图所接受。3digitdigitotherdigitdigitdigit9814562+,E7+,digitEdigitotherother*2.2词法分析器的一种手工实现词法分析器的一种手工实现u基于状态转

45、换图的词法分析器的实现基于状态转换图的词法分析器的实现code表示单词记号的种别;表示单词记号的种别;value存放标识符或数在符号表的入口地址;存放标识符或数在符号表的入口地址;过程过程getghar(ch)从扫描缓冲区得到一个搜索符号,从扫描缓冲区得到一个搜索符号,存储在变量存储在变量ch中;中;函数函数isLetter(ch)和和isDigit(ch)分别检查分别检查ch是否是字是否是字母母/数字;数字;函数函数lookup(token,table)在符号表在符号表table中查询是否中查询是否包含单词包含单词token;insert(token,table)在则把单词在则把单词toke

46、n插入符号表插入符号表table中并返回在符号表的地址;中并返回在符号表的地址;函数函数reporterror()报告并简单处理词法错误。报告并简单处理词法错误。编译原理与技术编译原理与技术522.2词法分析器的一种手工实现词法分析器的一种手工实现u根据状态栈图编写词法扫描器的方法一根据状态栈图编写词法扫描器的方法一让状态转移对应一个读入字符的语句或函数,让状态转移对应一个读入字符的语句或函数,然后与转移上的标记比较,如果相等就进入然后与转移上的标记比较,如果相等就进入转移对应的程序段或子程序;否则,调用错转移对应的程序段或子程序;否则,调用错误处理程序。误处理程序。多个转移就对应分支语句;多

47、个转移就对应分支语句;如果转移返回自身,形成一个圈,对应程序如果转移返回自身,形成一个圈,对应程序段的就是循环语句。段的就是循环语句。编译原理与技术编译原理与技术532.2词法分析器的一种手工实现词法分析器的一种手工实现标识符状态转换图的一个实现标识符状态转换图的一个实现编译原理与技术编译原理与技术54intcode,value;chartoken=”;/在开始状态0dotoken=token+ch;/不断读入字母或数字,合并成一个标识符getchar(ch);/保持在状态1while(!isLetter(ch)|!isDigit(ch);/isLetter(ch)和isDigit(ch)分别

48、检查ch是否是字母/数字/进入结束开始状态2code=lookup(token,keywordsTable); /在关键字表中查询token,若它是关键字就返回1if(code=1)return(1,token);/返回关键字的单词记号,假如关键字种别是1elsevalue=insert(token,identifierTable); /把token插入标识符表,返回入口地址return(2,value)/返回标识符的单词记号,假如标识符种别是22.2词法分析器的一种手工实现词法分析器的一种手工实现u根据状态栈图编写词法扫描器的方法二根据状态栈图编写词法扫描器的方法二采用一个变量来记录当前的状

49、态,把状态转采用一个变量来记录当前的状态,把状态转换嵌入到一个循环体内的分支语句中,其中换嵌入到一个循环体内的分支语句中,其中的第一个分支测试当前状态,而嵌入内层的的第一个分支测试当前状态,而嵌入内层的第一个分支语句则对给定的状态测试输入符第一个分支语句则对给定的状态测试输入符号,以决定转移进入的状态。号,以决定转移进入的状态。编译原理与技术编译原理与技术552.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术56例例2.5:下图示意的是识别下图示意的是识别C风格注释,即形式风格注释,即形式/*.*/,的状态转换图。状态,的状态转换图。状态2中的标记中的标记oth

50、er是除是除*之外的其它符号,而从状态之外的其它符号,而从状态3到状态到状态2的标的标记记other是除是除*和和/之外的其它符号。之外的其它符号。4132other*0/other*2.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术57intstate=0;while(state=0,1,2,3)switchstatecase0:getchar(ch);if(ch=/)state=1;getchar(ch);elsereporterror();case1:getchar(ch);if(ch=*)state=2;getchar(ch);elsereporterr

51、or();case2:getchar(ch);if(ch=*)state=3;getchar(ch);elsegetcharch(ch);/还是在状态2case3:getchar(ch);switchchcase/:state=4;getchar(ch);case*:state=3;getchar(ch);default:state=2if(state=4)return;elsereporterror();编译原理与技术编译原理与技术58Id-keywordsnumberotherotherotherEAotherotherotherdigitotherinIDstartLNE=*=other

52、*letter,digitletter23inNum*digitdigit*commentGE=*+*/=+*/digit课本图2.6:intcode,value,;charstate=“start”;chartoken=”;state_sets表示所有状态名的集合2.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术59while(state属于state_sets)switchstatecase“comment”:getchar(ch);if(ch=)state=“start”;getchar(ch);case“start”: getchar(ch);switc

53、hchcaseisletter(ch): state=“inID”;token=ch;getchar(ch);caseisdigit(ch): state=“inNum”;token=ch;getchar(ch);casech=:state=“comment”;getchar(ch);casech=: state=“GE”;token=ch;getchar(ch);casech=+: state=“+”;token=ch;casech=: state=“”;token=ch;casech=*: state=“*”;token=ch;casech=/:state=“/”;token=ch;def

54、ault:getchar(ch);/过滤掉无用的符号2.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术60case“inNum”:while(state属于inNumber,2,3)/处理数switchstatecase“inNum”:switchch/处理整数caseisdigit(ch):token=token+ch;getchar(ch);casech=.:state=“2”;token=token+ch;getchar(ch);default:state=“number”;code=10;/处理实数case“2”:if(isdigit(ch)state=

55、“3”;token=token+ch;getchar(ch);elsereporterror();case“3”:if(isdigit(ch)state=“3”;token=token+ch;getchar(ch);elsecode=11;state=“number”;case“number”:value=insert(token,identifierTable);return(code,value);2.2词法分析器的一种手工实现词法分析器的一种手工实现编译原理与技术编译原理与技术61case“inID”:while(isletter(ch)|isdigit(ch)token=token+c

56、h;getchar(ch);code=lookup(token,keywordsTable);/在关键字表中查询tokenif(code=1)return(1,token);/返回关键字的单词记号elsevalue=insert(token,identifierTable);/把token插入标识符表return(2,value);/返回标识符的单词记号case“LNE”:getchar(ch);if(ch=)getchar(ch);return(3,“”);if(ch=)getchar(ch);return(3,“=”);elsereturn(3,“=”);elsereturn(3,“”);

57、case“+”:getchar(ch);return(3,“+”);case“”:getchar(ch);return(3,“”);case“”:getchar(ch);return(3,“”);case“/”:getchar(ch);return(3,“/”);2.3正规表达式正规表达式u符号、符号串与符号集合符号、符号串与符号集合定义定义2.1:字母表是有限的非空的符号集合,:字母表是有限的非空的符号集合,字母表中的元素称作符号。字母表中的元素称作符号。例如,二进制数语言的字母表是例如,二进制数语言的字母表是0,1;Java语言的字母表可以说是一切可以打印字符组语言的字母表可以说是一切可以

58、打印字符组成的集合。成的集合。编译原理与技术编译原理与技术622.3正规表达式正规表达式定义定义2.2:由字母表中的符号所组成的任何有:由字母表中的符号所组成的任何有限序列称为符号串。一个符号串所包含符号限序列称为符号串。一个符号串所包含符号的个数称为该符号串的长度。的个数称为该符号串的长度。l例如,对于字母表例如,对于字母表 a,b,a、b、aa、ab、ba和和abba都是都是 上的符号串。符号串上的符号串。符号串b、ab和和abba的长度分别是的长度分别是1、2和和4。符号串中符号的排列顺序。符号串中符号的排列顺序十分重要,上面的十分重要,上面的ab和和ba表示不同的符号串。通表示不同的符

59、号串。通常用小写的希腊字母常用小写的希腊字母、等表示符号串。等表示符号串。符号符号的长度表示成的长度表示成|,例如,例如|abba|=4。l允许空符号串,即不包含任何符号的符号串,用允许空符号串,即不包含任何符号的符号串,用希腊字母希腊字母 表示,表示,| |=0。编译原理与技术编译原理与技术632.3正规表达式正规表达式l如果如果x=uv是一个符号串,则称是一个符号串,则称u是是x的头,称的头,称v是是x的尾。当我们堆一个符号串的某些部分感兴趣、的尾。当我们堆一个符号串的某些部分感兴趣、堆其它部分不感兴趣时,通常忽略调不感兴趣的堆其它部分不感兴趣时,通常忽略调不感兴趣的部分,而只保留感兴趣的

60、部分。部分,而只保留感兴趣的部分。l例如,若我们只关心符号串例如,若我们只关心符号串x=t中的符号中的符号t,也,也可以用可以用x=.t.表示;同样,表示;同样,x=t和和x=t.这两种表这两种表示都只关注符号的头时符号示都只关注符号的头时符号t。编译原理与技术编译原理与技术642.3正规表达式正规表达式定义定义2.3:设:设和和是同一字母表上的符号串,把是同一字母表上的符号串,把的各的各个符号相继地写在个符号相继地写在之后所的到的符号串称为之后所的到的符号串称为和和的的连接(并置),连接(并置),记做。显然,记做。显然,|=|+|。例如,字母表例如,字母表 a,b,0,1上的符号串上的符号串

61、=bb11、=a00,是是bb11a00,而,而是是a00bb11,而且,而且|=|=7=|+|=|+|=4+3=7。显然,对于任何符号串显然,对于任何符号串,都有,都有 。定义定义2.4:设:设u是某一字母表上的符号串,把是某一字母表上的符号串,把u自身连自身连接接n次,即次,即=u.u(n个个u),称作符号串,称作符号串u的的n次方幂,次方幂,记做记做=un。例如,例如,u1=u,u2=uu,u4=uuuu。特别地,当。特别地,当n=0时,时,u0= 。显然,。显然,uun-1=un-1u=un。编译原理与技术编译原理与技术652.3正规表达式正规表达式定义定义2.5:若集合:若集合A中的

62、所有元素都是某字母表上的符号串,则中的所有元素都是某字母表上的符号串,则称集合称集合A是该字母表上的符号集合。是该字母表上的符号集合。字母表上的符号集合通常用大写字母字母表上的符号集合通常用大写字母A、B、C等表示。例如,等表示。例如,字母表字母表 a,b,0,1上长度为上长度为2的符号串集合的符号串集合A=|=xy,并且,并且x和和y是是 中的一个符号中的一个符号;字母表;字母表 上单词上单词B=|是是a,b中的符中的符号串号串。定义定义2.6:两个符号串集合:两个符号串集合A和和B的乘积的乘积AB定义为:定义为:AB=uv|u A并且并且v B。例如,设例如,设A=a,b,B=0,1,那么

63、,那么AB=a0,a1,b0,b1,BA=0a,1a,0b,1b,AA=aa,ab,ba,bb。由于对于任何符号。由于对于任何符号串串x都有都有x xx,所以,所以 A=A =A,但是,对于空集,但是,对于空集,却有等式,却有等式A=A=。类似于符号串的方幂,可以定义符号串集合的方幂,特别地,类似于符号串的方幂,可以定义符号串集合的方幂,特别地,定义字母表定义字母表A的方幂为的方幂为A0= ,A1=A,An=An-1A(n0),显然,若显然,若u An,则,则|u|=n。编译原理与技术编译原理与技术662.3正规表达式正规表达式定义定义2.7:字母表:字母表 的闭包的闭包 * 0 1. n.,

64、正闭,正闭包包 + 1 2. n.。例如,对于字母表例如,对于字母表 a,b, +=a,b,aa,bb,ab,ba,aaa,bbb,aab,bba,aba,bab,abb,baa,.。显然,显然, * 0 +, += * =*。 *表示字母表表示字母表 上所有长度的符号串的集合,包括空上所有长度的符号串的集合,包括空符号串;符号串; +表示长度至少为表示长度至少为1的符号串的集合。的符号串的集合。 +实际上就表示了该字母表所构成的语言,句子就是实际上就表示了该字母表所构成的语言,句子就是其中的符号串。其中的符号串。对于对于C语言,可以说,语言,可以说,C语言是其字母表,也即基本语言是其字母表,

65、也即基本符号正闭包的真子集。符号正闭包的真子集。编译原理与技术编译原理与技术672.3正规表达式正规表达式例例2.6:令字母表:令字母表L=A,B,.,Z,a,b,.,z,D=0,1,.,9,那么,那么LD是字母和数字的集合;是字母和数字的集合;LD4表示以字母开头、跟随表示以字母开头、跟随4个数字的串的集个数字的串的集合;合;L(LD)15表示长度为表示长度为16的标识符,即以字母的标识符,即以字母开始的开始的16位的字母和数字串的集合;位的字母和数字串的集合;D*表示不含空的数字串的集合。表示不含空的数字串的集合。编译原理与技术编译原理与技术682.3正规表达式正规表达式u正规式与正规集正

66、规式与正规集字母表字母表 上的正规表达式用来描述一种称为正规集的语言。上的正规表达式用来描述一种称为正规集的语言。定义定义2.8:字母表:字母表 上的正规表达式(简称正规式)按照下列规上的正规表达式(简称正规式)按照下列规则递归地定义:则递归地定义:(1) 是是 上的正规式,它表示的正规集是上的正规式,它表示的正规集是 ;(2)是是 上的正规式,它表示的正规集是上的正规式,它表示的正规集是;(3) 中的任意符号中的任意符号a都是都是 上的正规式,它表示的正规集是上的正规式,它表示的正规集是a(4)若)若r和和t都是正规式,它们所表示的正规集分别是都是正规式,它们所表示的正规集分别是L(r)和和

67、L(t),那么,那么(r)、r|t、rt和和r*都是正规式,表示的正规集分别是都是正规式,表示的正规集分别是L(r)、L(r)L(t)、L(r)L(t)、(L(r)*。根据显然定义有下列等式:根据显然定义有下列等式:L(a)=a,L( )= ,L()=,L(r)=L(r),L(rt)=L(r)L(t),L(r|t)=L(r)L(t),L(r*)=(L(r)*。编译原理与技术编译原理与技术692.3正规表达式正规表达式例例2.7:令字母表:令字母表 =a,b,c,那么,那么(a|b)(a|b)aa,ab,ba,bb;(a|c)*表示所有表示所有a和和c组成的符号串,其中包含空串组成的符号串,其中

68、包含空串 ;(a|c)*b(a|c)*表示只包含一个表示只包含一个b的字母表的字母表 上的所有符上的所有符号串,例如号串,例如b,abc,baaac,caccb,ccbaaa。最多包含一个最多包含一个b的字母表的字母表 上的符号串的集合可以表示上的符号串的集合可以表示成成(a|c)*|(a|c)*b(a|c)*),或者,或者(a|c)*(b| )(a|c)*。(a|c)*b(a|c)*b表示的集合是什么呢?它表示只含两个表示的集合是什么呢?它表示只含两个b的符号串的集合。的符号串的集合。编译原理与技术编译原理与技术702.3正规表达式正规表达式定义定义2.9:如果两个正:如果两个正规式规式r与

69、与t表示的正规表示的正规集相同,则称它们的集相同,则称它们的等价的,记做等价的,记做r=t。正规式等价的例子如正规式等价的例子如a|(ba)*=(ba)*|a,(a|b)=(b|a)。定理解释r|t=t|r|的交换律r|(s|t)=(r|s)|tr(st)=(rs)t结合律r(s|t)=rs|rt(r|s)t=rt|st分配律r=r=rr=r=rr|=r吸收律r*=(r|)*闭包运算和之间的关系编译原理与技术编译原理与技术712.3正规表达式正规表达式u扩展的正规式扩展的正规式(1)一个或多次重复:一元后缀算符)一个或多次重复:一元后缀算符“+”表示一个或多次重复,表示一个或多次重复,即正规式

70、即正规式r+表示一个或多个表示一个或多个r的串的集合。这样,的串的集合。这样,(0|1)+表示所表示所有二进制数字的集合,而有二进制数字的集合,而(0|1)*同时还包含了可串。同时还包含了可串。(2)字符集的范围:对于字母或数字的集合,可以使用)字符集的范围:对于字母或数字的集合,可以使用a|b|.|z或或0|1|.|9。更简洁的方式是用方括弧,用连接线表示范围,这样,。更简洁的方式是用方括弧,用连接线表示范围,这样,上面的字母或数字就可以分别表示成上面的字母或数字就可以分别表示成a-z和和0-9。类似的,。类似的,a|b|c|d可以写成可以写成a-d或者或者abcd。标识符是字母打头的字母数

71、字。标识符是字母打头的字母数字串,可以表示成串,可以表示成A-Za-zA-Za-z0-9*。(3)零个或一个:一元后缀算符)零个或一个:一元后缀算符“?”表示零个或一个,表示零个或一个,r?是是r| 的缩写。带符号的整数可以写成的缩写。带符号的整数可以写成(+| )?1-90-9*编译原理与技术编译原理与技术722.3正规表达式正规表达式如果正规式很长,可以给它命名,使它们可如果正规式很长,可以给它命名,使它们可以像普通的符号一样,在随后的正规式中使以像普通的符号一样,在随后的正规式中使用这些名字来引用相应的正规式,以便得到用这些名字来引用相应的正规式,以便得到简洁的正规式。简洁的正规式。如果

72、如果r如是字母表如是字母表 上的正规式,那么正规定上的正规式,那么正规定义的形式是:义的形式是:namer。这样,正规式。这样,正规式r的名的名字字name就可以像就可以像 中的符号一样,在以后构中的符号一样,在以后构造造 上正规式的时候使用。上正规式的时候使用。编译原理与技术编译原理与技术732.3正规表达式正规表达式例例2.8:Pascal语言的标识符集合是字母开头的语言的标识符集合是字母开头的字母数字串,下面就是这个集合的正规定义:字母数字串,下面就是这个集合的正规定义:letterA-Za-zdigit0-9identifierletter(letter|digit)*编译原理与技术编

73、译原理与技术742.3正规表达式正规表达式例例2.9:Pascal语言的数是语言的数是2005,+1998, 81.07,2.003 6这样的串,即由整数、小数和指数三个部分这样的串,即由整数、小数和指数三个部分组成。小数和指数部分是可选的,其中指数标记组成。小数和指数部分是可选的,其中指数标记E后后面可以有面可以有+或或 ,再跟上一个或多个数字,而小数点之,再跟上一个或多个数字,而小数点之后必须至少有一个数字。下面就是后必须至少有一个数字。下面就是Pascal语言的数的语言的数的集合的正规定义:集合的正规定义:digit0-9digitsdigitdigit*signed+| fractio

74、n(.digits)?exponent(E(signed)?digits)?numbersigned?digitsfractionexponent编译原理与技术编译原理与技术752.3正规表达式正规表达式u正规表达式的实现和应用正规表达式的实现和应用正规表达式实际上是描述和识别一组字符串的模板正规表达式实际上是描述和识别一组字符串的模板(模式),它包含字符、元符号(如表示重复的(模式),它包含字符、元符号(如表示重复的*和选择符和选择符|)和一些具有特殊意义的符号。这个模板)和一些具有特殊意义的符号。这个模板决定什么样的字符串属于某一个集合。决定什么样的字符串属于某一个集合。正规表达式在处理文

75、本方面具有强大的能力,它在正规表达式在处理文本方面具有强大的能力,它在计算机领域的应用不仅仅局限于构造编译器的词法计算机领域的应用不仅仅局限于构造编译器的词法扫描器,其它著名的应用还包括扫描器,其它著名的应用还包括UNIX操作系统的操作系统的命令工具如命令工具如grep,处理复杂文本分析与操作的脚本,处理复杂文本分析与操作的脚本语言语言Perl,Tcl,Python,PHP和和awk以及通用程序以及通用程序开发编辑器开发编辑器emacs。由于正规表达式的重要而广泛的应用,由于正规表达式的重要而广泛的应用,Java语言通语言通过包过包java.util.regex还对正规表达式的处理提供了直还对

76、正规表达式的处理提供了直接支持。接支持。编译原理与技术编译原理与技术762.4有限自动机有限自动机u为什么引入有限自动机为什么引入有限自动机确定的有限自动机和不确定的有限自动机都确定的有限自动机和不确定的有限自动机都能识别正规集,即它们识别的语言正好就是能识别正规集,即它们识别的语言正好就是正规式所能表达的语言,而且在识别语言的正规式所能表达的语言,而且在识别语言的能力上,它们完全等价。能力上,它们完全等价。但是,实现这两类有限状态机的效率不同,但是,实现这两类有限状态机的效率不同,用它们构造的词法分析器在识别语言中单词用它们构造的词法分析器在识别语言中单词记号的效率方面也有显著的差别。记号的

77、效率方面也有显著的差别。编译原理与技术编译原理与技术77正规式不确定的有限自动机确定的有限自动机词法分析器2.4有限自动机有限自动机u确定的有限自动机确定的有限自动机DFA定义定义2.10:一个确定的有限自动机:一个确定的有限自动机DFAM是五元组是五元组,其中:,其中:(1)S是非空的有限的状态集合;是非空的有限的状态集合;(2) 是非空的输入字母表;是非空的输入字母表;(3)T是部分单值映射是部分单值映射SS,又称转移函数;,又称转移函数;T(s1,a)=s2表示输入符号表示输入符号a时,把状态时,把状态s1转换到转换到s2,成,成为当前状态;为当前状态;(4)s0 S,是唯一的起始状态;

78、,是唯一的起始状态;(5)F S,是非空的终结状态。,是非空的终结状态。被被M接受或识别的语言,记做接受或识别的语言,记做L(M),定义为字符串,定义为字符串的集合,其中每个的集合,其中每个ci,并且存在状态序列,并且存在状态序列s1=T(s0,c1),s2=T(s1,c2),.,sn=T(sn-1,cn),sn F。编译原理与技术编译原理与技术782.4有限自动机有限自动机例例2.10:一个有限自动机:一个有限自动机DFAN=,其中,其中T的定义如下的定义如下:T(A,+)=BT(A, )=BT(A,)=CT(A,d)=DT(B,)=DT(B,d)=CT(C,)ET(C,d)=CT(D,d)

79、ET(E,d)E状态状态A是起始状态,是起始状态,E是终结状态。是终结状态。编译原理与技术编译原理与技术792.4有限自动机有限自动机转换函数可以用状态转换函数可以用状态转换矩阵或状态转换转换矩阵或状态转换表来表示,该表的行表来表示,该表的行表示状态,列对应输表示状态,列对应输入的符号,表元素表入的符号,表元素表示状态转移的状态,示状态转移的状态,空白元素对应的二元空白元素对应的二元组组没有没有定义。定义。+dABBCDBDCCECDEEE编译原理与技术编译原理与技术80例2.10有限自动机的状态转换矩阵2.4有限自动机有限自动机一个一个DFA也可以表示成一个确定的状态转换也可以表示成一个确定

80、的状态转换图,状态转换函数图,状态转换函数T(s1,a)=s2对应了连接两个对应了连接两个结点结点s1和和s2,标记为,标记为a的有向弧。的有向弧。编译原理与技术编译原理与技术81+,CEdADBdddd例2.10有限自动机的状态转换图从状态A到B的转换上的标记“+,”表示两条从A到B的弧的标记+和,这是常用的简化方式。2.4有限自动机有限自动机定义定义2.11:对于有限自动机:对于有限自动机M的的 *中的任何一中的任何一个符号串个符号串,若存在一条从起始状态到某一终,若存在一条从起始状态到某一终结状态的通路,且这条通路上所有弧的标记结状态的通路,且这条通路上所有弧的标记符连成的串等于符连成的

81、串等于,则称,则称被被M识别(读出或识别(读出或接受)。若起始状态也是一个终结状态,则接受)。若起始状态也是一个终结状态,则空串空串 可以为可以为M接受。接受。DFAM所能识别的所有所能识别的所有字的集合称为字的集合称为M识别的语言,记做识别的语言,记做L(M)。编译原理与技术编译原理与技术822.4有限自动机有限自动机例如,考虑上述的有限自动机,如果例如,考虑上述的有限自动机,如果d代表任意代表任意一个数字一个数字0,1,.,9,它能否接受符号串,它能否接受符号串30.、+.28和和 813.19?对于它们,可以分别给出?对于它们,可以分别给出从状态从状态A到终结状态到终结状态E的路径:的路

82、径:因而它们都是这个因而它们都是这个DFAN可以接受的数。可以接受的数。编译原理与技术编译原理与技术832.4有限自动机有限自动机定义定义2.12(a):两个有限自动机:两个有限自动机M1和和M2是等价是等价的,当且仅当它们识别相同的语言,即的,当且仅当它们识别相同的语言,即L(M1)=L(M2)。定义定义2.12(b):两个有限自动机:两个有限自动机M1和和M2是等价是等价的,当且仅当对于每个的,当且仅当对于每个M1接受的符号串接受的符号串,M2接受接受。编译原理与技术编译原理与技术842.4有限自动机有限自动机u不确定的有限自动机不确定的有限自动机NFA定义:一个不确定的有限自动机定义:一

83、个不确定的有限自动机NFAM是五元组是五元组,其中:,其中:(1)S是非空的有限的状态集合;是非空的有限的状态集合;(2) 是非空的输入字母表;是非空的输入字母表;(3)T是是S ( )2S(S的幂集),它可以把一个状态映的幂集),它可以把一个状态映射到一组状态射到一组状态;T(s1,a)=s2表示输入符号表示输入符号a时,把状态时,把状态s1转换到转换到s2,成为当前状,成为当前状态;态;(4)s0 S,是唯一的起始状态;,是唯一的起始状态;(5)F S,是非空的终结状态。,是非空的终结状态。被被M接受或识别的语言,记做接受或识别的语言,记做L(M),定义为字符串,定义为字符串的集的集合,其

84、中每个合,其中每个ci,并且存在状态序列,并且存在状态序列s1=T(s0,c1),s2=T(s1,c2),.,sn=T(sn-1,cn),sn F。编译原理与技术编译原理与技术852.4有限自动机有限自动机例例2.11:识别语言:识别语言(a|b)nabb|n 0的一的一个个NFAM=,ab01,2111,212334编译原理与技术编译原理与技术86aa24a031bbbba这个NFA识别aabbabb,即存在一个状态序列,从状态0到状态4,路经上的标记连接是aabbabb:2.4有限自动机有限自动机两个两个NFAN1和和N2称为等价的,当且仅当它们称为等价的,当且仅当它们识别同一个语言,即识

85、别同一个语言,即L(N1)=L(N2)。为什么要引入为什么要引入NFA?l首先,需要有限自动机的主要目的是用来识别正首先,需要有限自动机的主要目的是用来识别正规式所描述语言的单词记号,直接从正规式构造规式所描述语言的单词记号,直接从正规式构造出出DFA的算法比较复杂,而从正规式构造的算法比较复杂,而从正规式构造NFA的的算法则相对简单;然后,再从算法则相对简单;然后,再从NFA构造等价的构造等价的DFA作为记号的识别分析器。作为记号的识别分析器。l其次,我们有时需要多值映射以及所谓的空转移,其次,我们有时需要多值映射以及所谓的空转移,即形式为即形式为N 的产生式,以便简洁地表达多种可的产生式,

86、以便简洁地表达多种可能的选择。而且,使用空转移描述对空串的匹配,能的选择。而且,使用空转移描述对空串的匹配,更加直观和自然。更加直观和自然。编译原理与技术编译原理与技术872.4有限自动机有限自动机u从从NFA到到DFA的等价变换的等价变换定理定理2.1:对于任意一个:对于任意一个NFAN都存在一个都存在一个等价的等价的DFAM,即,即L(N)=L(M)。为了从为了从NFA构造出等价的构造出等价的DFA,需要,需要l消除消除N 形式的产生式,即消除空转移;形式的产生式,即消除空转移;l确定化每个多重转移,即拆分多值函数为单值函确定化每个多重转移,即拆分多值函数为单值函数。数。l一个常用的方法是

87、子集法。一个常用的方法是子集法。编译原理与技术编译原理与技术882.4有限自动机有限自动机定义定义2.13:设:设I是是NFAN=中一中一个状态子集,对个状态子集,对I的的 闭包运算闭包运算 -closure(I)的结的结果是一个状态子集,定义为果是一个状态子集,定义为 -closure(I)=s|s I s|s -closure(I)并且存在并且存在T(s, )=s。直观地说,状态子集直观地说,状态子集I的的 闭包就是从闭包就是从I中的任中的任何一个状态经过任意个何一个状态经过任意个 连线所能达到的所有连线所能达到的所有状态。状态。编译原理与技术编译原理与技术892.4有限自动机有限自动机例

88、例2.12:下图是接受连续:下图是接受连续a或或b的语言的一个的语言的一个NFA,尽管在每个状态,对应字母表上符号的转移是唯一尽管在每个状态,对应字母表上符号的转移是唯一的,但是,由于增加了所谓的空转移的,但是,由于增加了所谓的空转移 转移,所以,转移,所以,它不是它不是DFA。编译原理与技术编译原理与技术90abE5132B6baaa4bb例如,一个子集I=B,按照定义,计算-closure(B)的序列如下:-closure(B)BB,5B,5,12.4有限自动机有限自动机定义定义2.14:设:设I是是NFAN=中一中一个状态子集,个状态子集,a,对,对I移动的闭包运算移动的闭包运算Ia的的

89、结果是一个状态子集,定义为结果是一个状态子集,定义为Ia= -closure(J),其中,其中J=s|s I并且存在并且存在T(s,a)=s。直观上,状态子集直观上,状态子集Ia是从是从I中的任何一个状态中的任何一个状态经过标记为经过标记为a的一次转移之后所能到达的所有的一次转移之后所能到达的所有状态的状态的 闭包。计算闭包。计算Ia的两个步骤:的两个步骤:(1)计算从)计算从I读入符号读入符号a所达到的状态集合所达到的状态集合J;(2)求)求J的的 闭包,即闭包,即 -closure(J)。编译原理与技术编译原理与技术91编译原理与技术编译原理与技术92算法算法2.1从NFA到DFA的等价构

90、造算法输入:输入:NFAN=)输出:输出:等价的DFA假设假设=a1,a2,.,an(1)首先构造DFA的状态转换表MATRIX:这是一个n+1列、行动态增长的表格;第零列的每个元素对应DFA的状态,随着算法的执行,递增地产生新的状态,即增加新行;初始化:row=1;/MATRIX的行sn=row;/DFA的状态个数MATRIXrow,0=-closure(q0);S=MATRIXrow,0;/加入DFA状态集合的第一个状态,它本身是一个集合do/逐行地填表fori=1tondoMATRIXrow,i=Iai;/在每行逐列地填表ifIaiSthenS=SIai;/把Iai加入Ssn=sn+1;

91、MATRIXsn,0=Iai;/状态转换表增加一行,即增加一个新的状态endif;endfor;row=row+1;/计算下一个状态的Iaiwhilesn=row/直到没有新的状态为止(2)矩阵中第一行第零列所表示的状态就是DFA的起始状态;(3)包含了NFA终结状态的新状态就是DFA的终结状态。(4)可选:重新命名DFA的状态,相应地改变状态转换函数中状态名字。2.4有限自动机有限自动机例例2.13:对例:对例2.12表示的表示的NFA构造等价的构造等价的DFA。l首先,把首先,把 -closure(q0)= -closure(B)=B,5,1填入转填入转换矩阵表的第一行第一列。然后对每个换

92、矩阵表的第一行第一列。然后对每个a计算计算Ia并填入表并填入表中,如果中,如果Ia是新的状态集合,则在状态转换矩阵表中新增一是新的状态集合,则在状态转换矩阵表中新增一行,把它加入其中。对于行,把它加入其中。对于a,计算,计算Ia= -closure(B,5,1)=5,1,3,是个新的状态集合,加入表的第二行;对于对于,是个新的状态集合,加入表的第二行;对于对于b,计算,计算Ib= -closure(B,5,1)=5,1,4,也是个新的状,也是个新的状态集合,加入表的第三行。态集合,加入表的第三行。l然后,把然后,把5,1,3看作看作I,对每个,对每个a计算计算Ia并填入表中。并填入表中。Ia=

93、5,1,3,2,6,E是个新的状态集合,加入表中的下一行,是个新的状态集合,加入表中的下一行,而而Ib=5,1,4已经出现过,什么也不做。已经出现过,什么也不做。编译原理与技术编译原理与技术932.4有限自动机有限自动机IaIbB,5,15,1,35,1,45,1,35,1,3,2,6,E5,1,45,1,45,1,35,1,4,2,6,E5,1,3,2,6,E5,1,3,2,6,E5,1,4,6,E5,1,4,2,6,E5,1,3,6,E5,1,4,2,6,E5,1,4,6,E5,1,3,6,E5,1,4,2,6,E5,1,3,6,E5,1,3,2,6,E5,1,4,6,E编译原理与技术编译

94、原理与技术94这样继续下去,最后得到的状态转换矩阵如表这样继续下去,最后得到的状态转换矩阵如表2.5(a)所示。所示。2.4有限自动机有限自动机ab01213221435646435编译原理与技术编译原理与技术95状态子集重新命名,并将Ia和Ib分别视为a与b后得表2.5(b),对应的状态转换图如图2.13所示。ab34a01bbba256baabbaa2.4有限自动机有限自动机例例2.14:对例子:对例子2.11表示的表示的NFA(如下图)构造等价(如下图)构造等价的的DFA。IaIb01,211,21,21,311,211,31,21,41,41,21编译原理与技术编译原理与技术96ab4

95、3ab01bbba2baaa2.4有限自动机有限自动机uDFA的最小化的最小化定理定理2.2:对任意一个:对任意一个DFAN都存在一个等价都存在一个等价的的DFAM,它包含最少的状态数,而且这个,它包含最少的状态数,而且这个最小状态的最小状态的DFAM是唯一的(状态名不同的是唯一的(状态名不同的同构情况除外)。同构情况除外)。关键在于把关键在于把DFA的状态划分成一些不相交的的状态划分成一些不相交的等价子集,使得任何两个不同子集中的状态等价子集,使得任何两个不同子集中的状态都可以区别,而每个子集内的状态都是等价都可以区别,而每个子集内的状态都是等价的。这样,每个子集用一个状态代表,删除的。这样

96、,每个子集用一个状态代表,删除子集中的其它状态,就得到了状态个数最少子集中的其它状态,就得到了状态个数最少的的DFA。编译原理与技术编译原理与技术972.4有限自动机有限自动机定义定义2.15:设:设q1和和q2是是DFAN中的两个不同的状态,中的两个不同的状态,如果从状态如果从状态q1出发能扫描任意串出发能扫描任意串而停于终结状态,而停于终结状态,当且仅当从当且仅当从q2出发也能扫描任意串出发也能扫描任意串而停于终结状态,而停于终结状态,称状态称状态q1和和q2是等价的。是等价的。定义定义2.16:若两个状态:若两个状态q1和和q2不是等价的,则称这两不是等价的,则称这两个状态是可区分的。个

97、状态是可区分的。例如,例例如,例2.14中状态中状态1和和3是可区别的,因为从状态是可区别的,因为从状态1读读入一个入一个b而停在终结状态而停在终结状态3,但是,从状态,但是,从状态3读入一个读入一个b而没有达到终结状态而没有达到终结状态4。又如空串。又如空串 把一切终结状态把一切终结状态与任何非终结状态区别开。与任何非终结状态区别开。编译原理与技术编译原理与技术982.4有限自动机有限自动机uDFA最小化的方法最小化的方法对一个对一个DFAM=的状态划分,不断地尝试对其中的状态的状态划分,不断地尝试对其中的状态子集寻找可区别的状态,进行分裂,直到没有状态子集可以分裂为止,子集寻找可区别的状态

98、,进行分裂,直到没有状态子集可以分裂为止,直至每个子集内的状态是等价的,而每个状态子集是可区分的。直至每个子集内的状态是等价的,而每个状态子集是可区分的。假如在某个时刻,划分假如在某个时刻,划分=I1,I2,.,Im,其中的任何一个都是状态子集。,其中的任何一个都是状态子集。对于任意一个状态子集对于任意一个状态子集Ijq1,q2,.,qk,若存在一个输入符号,若存在一个输入符号a,对于任意两个不等的对于任意两个不等的qi和和qj使得使得(1)T(qi,a)=s和和T(qj,a)=t不在当前划分不在当前划分的任何一个状态子集中,的任何一个状态子集中,(2)或者其中)或者其中T(qi,a)和和T(

99、qj,a)的任何一个没有定义的任何一个没有定义则可以据此把则可以据此把Ij一分为二:其中的一部分一分为二:其中的一部分Ij1是从是从Ij的状态读入的状态读入a之后进之后进入包含入包含s状态子集的状态,状态子集的状态,Ij2=Ij Ij1。这样,在划分。这样,在划分中用中用Ij1和和Ij2取取代代Ij,就形成一个新的划分。,就形成一个新的划分。编译原理与技术编译原理与技术992.4有限自动机有限自动机可以根据可以根据Ij中每个中每个qi的的T(qi,a)来执行来执行K分裂:若分裂:若T(qi,a)落在划分落在划分的的K个不同的状态子集中,则可以把个不同的状态子集中,则可以把Ij划分成划分成K个不

100、相交的组,个不相交的组,使得每组的状态都落入使得每组的状态都落入的同一子集中。的同一子集中。重复这个划分,直到没有可以分裂的状态子集为止,得到最终重复这个划分,直到没有可以分裂的状态子集为止,得到最终的划分的划分。选择每组中的一个状态作为代表,删除其它一切等价的状态,选择每组中的一个状态作为代表,删除其它一切等价的状态,所有这些状态代表就构成了化简了的所有这些状态代表就构成了化简了的DFA状态集合。状态集合。每个状态组之间的连线转移就是每个状态代表之间的转移,同每个状态组之间的连线转移就是每个状态代表之间的转移,同时去掉不可达状态和无用状态。时去掉不可达状态和无用状态。含有原来初态的等价状态组

101、就是化简了的含有原来初态的等价状态组就是化简了的DFA的起始状态。的起始状态。包含了原来终结状态的等价状态组就是化简了的包含了原来终结状态的等价状态组就是化简了的DFA的终结状的终结状态。态。编译原理与技术编译原理与技术1002.4有限自动机有限自动机算法算法2.2 DFA最小化算法最小化算法输入:输入:DFAN=输出:输出:等价的具有最少状态的等价的具有最少状态的DFAM假设假设 =a1,a2,.,an初始化:初始化:=F,S F;标记标记F和和S F没有访问过没有访问过finished=false;编译原理与技术编译原理与技术1012.4有限自动机有限自动机(1)完成划分;)完成划分;wh

102、ilenotfinisheddoif中不存在未访问的子集中不存在未访问的子集Jthenfinished=trueelse 把把J标记标记“访问过访问过”;/Ijq1,q2,.,qkdistinguished=false;i=1;whilei nANDnotdistinguisheddoif J使得使得Jai J可区分的可区分的thendistinguished=true;把把J从从中删除;中删除;设设T(qi,a)落在划分落在划分的的K个不同个不同Ji1,.,Jik;把把J划分成划分成K个不相交的组并加入到个不相交的组并加入到中;中; elsei=i+1;endif; endwhile;end

103、if;/试算试算中的下一个状态子集中的下一个状态子集endwhile;/直到直到的每个状态子集不可划分为止的每个状态子集不可划分为止编译原理与技术编译原理与技术1022.4有限自动机有限自动机(2)取)取的每组中的一个状态作为代表,删除的每组中的一个状态作为代表,删除其它一切等价的状态,所有这些状态代表就其它一切等价的状态,所有这些状态代表就构成了化简了的构成了化简了的DFA状态集合;状态集合;(3)每个状态组之间的连线变成每个状态代表)每个状态组之间的连线变成每个状态代表之间的连线,去掉不可达状态和无用状态;之间的连线,去掉不可达状态和无用状态;(4)含有原来初态的等价状态组就是化简了的)含

104、有原来初态的等价状态组就是化简了的DFA的起始状态;的起始状态;(5)包含了原来终结状态的等价状态组就是化)包含了原来终结状态的等价状态组就是化简了的简了的DFA的终结状态;的终结状态;编译原理与技术编译原理与技术1032.4有限自动机有限自动机例例2.15:图:图2.13所示的所示的DFA的最小化过程如下的最小化过程如下:初始划分初始划分=0,1,2,3,4,5,6,计算,计算0,1,2a1,3,它不包,它不包含在含在的任何一个子集当中,所以,可以把的任何一个子集当中,所以,可以把0,1,2分解分解.根据根据T(1,a)=3,T(0,a)=T(2,a)=1,把,把0,1,2分解成两个状态子集

105、分解成两个状态子集1和和0,2。这时的。这时的=1,0,2,3,4,5,6。继续划分:由于继续划分:由于0,2b=2,5未包含在当前划分的任何一个子集未包含在当前划分的任何一个子集中,所以中,所以0,2一分为二,得到新的划分一分为二,得到新的划分=0,1,2,3,4,5,6。由于由于3,4,5,6a=3,4,6,3,4,5,6b=4,5,6都包含在都包含在3,4,5,6内,内,不可再划分。至此,整个划分结束。不可再划分。至此,整个划分结束。令状态令状态3代表代表3,4,5,6,把原来进入状态,把原来进入状态4、5和和6的弧都导入的弧都导入3,删除状态删除状态4、5和和6,得到最小化的,得到最小

106、化的DFA如图如图2.15。编译原理与技术编译原理与技术1042.4有限自动机有限自动机例例2.15编译原理与技术编译原理与技术105ab34a01bbba256baabbaaab3b01bba2baa最小化最小化图图2.13所示的所示的DFA最小化的最小化的DFA2.4有限自动机有限自动机例例2.16:下图所示的:下图所示的DFA的最小化过程的最小化过程编译原理与技术编译原理与技术106ab43b01bbba2baaa32b1bb0baaaa(1)因为状态仅状态4是终结状态,首先得划分4和0,1,2,3。(2)只需对0,1,2,3继续划分:0,1,2,3在面临字符a时,每个状态都转换到状态1

107、,无法区分;而在面临符号b时,状态3进入状态4,状态0、1和2都转换到0,1,2,3内得状态,所以,可以把0,1,2,3分解成3和0,1,2,从而得到新的划分包括4、3和0,1,2。(3)可能再分解的是0,1,2:在输入b时,T(0,b)2,T(1,b)=3,T(2,b)=2,据此可以把0,1,2分解成1和0,2(4)继续尝试分解0,2:这时的0,2a1,0,2b2,无法区分,划分完毕。(5)最小化的状态包括0,2、1、3和4,重新命名、整理后得到右上图。2.4有限自动机有限自动机u从正规式到有限自动机从正规式到有限自动机基于转换规则的算法,利用基于转换规则的算法,利用 转移把单个的转移把单个

108、的正规式粘接起来,形成更复杂的正规式粘接起来,形成更复杂的、识别正规、识别正规式表示语言的式表示语言的NFA。编译原理与技术编译原理与技术107定理定理2.3:(1)对字母表对字母表 上的任何一个正规表达式上的任何一个正规表达式e都存都存在一个接受在一个接受L(e)的有限自动机的有限自动机M,即,即L(e)L(M)。(2)对字母表对字母表 上的任何一个有限自动机上的任何一个有限自动机M都存都存在一个接受在一个接受L(M)正规表达式正规表达式e,即,即L(M)L(e)。2.4有限自动机有限自动机(1)对应于基本正规式)对应于基本正规式a、 、的的NFA编译原理与技术编译原理与技术108a对应于基

109、本正规式对应于基本正规式a的的NFA是是对应于基本正规式对应于基本正规式 的的NFA是是对应于基本正规式对应于基本正规式的的NFA是是2.4有限自动机有限自动机(2)选择替换规则:)选择替换规则:e1|e2表示成表示成编译原理与技术编译原理与技术109e2e1构造的有限自动机新增加了两个状态,分别表示开始状态和终结状态,然后把新增的起始状态与N(e1)和N(e2)的起始状态用转移连接起来,把N(e1)和N(e2)的终结状态与新增的终结状态用转移连接来,并且更改N(e1)和N(e2)起始状态和终结状态的性质。N(e1)接受的任何串s,等价于s,而它可以被N(e1|e2)接受,即L(e1)L(e1

110、|e2)。同样,L(e2)L(e1|e2)。反之,所有从N(e1|e2)的开始状态,经过N(e1)部分到达终结状态,所接受的语言是L(e1)L(e1),或者经过N(e2)部分到达终结状态,所接受的语言是L(e2)L(e2),即L(e1|e2)L(e1)L(e2)。所以L(e1|e2)L(e1)L(e2)。2.4有限自动机有限自动机(3)连接替换规则:)连接替换规则:e1e2表示成表示成编译原理与技术编译原理与技术110e2e1e1和e2连接成e1e2时,把N(e1)的终结状态改为非终结状态,然后通过转移与N(e2)的起始状态连接起来,N(e1)的起始状态成为N(e1e2)的起始状态,N(e2)

111、的终结状态成为N(e1e2)的终结状态。如果N(e1)接受s1,N(e2)接受s2,那么,构成s1的路径必须经过标记为的唯一的一条边后,才能与构成s2的路径连接起来到达结束状态。这样,合成的有限自动机接受的是e1e2,即e1e2。2.4有限自动机有限自动机(4)重复替换规则:)重复替换规则:e*表示成表示成编译原理与技术编译原理与技术111e对于e的闭包运算e*,新增加了分别表示开始和结束的两个状态,并且更改N(e)起始状态和终结状态的性质;然后新增四条转移连接新增的的起始状态与N(e)的起始状态;连接新增的起始状态和终结状态;连接N(e)的终结状态与新增的终结状态;连接N(e)的终结状态与N

112、(e)的起始状态。新增的边表示e的零次连接,等价于(L(e)0;新增的边和原来N(e)构成了回路,这样,从N(e)的开始状态到结束状态,可以经过N(e)一次或多次。显然这个N(e)识别语言(L(e)*。2.4有限自动机有限自动机u从正规式到有限自动机的例子从正规式到有限自动机的例子例例2.17:对正规式:对正规式(+| | )dd*构造对应的构造对应的NFA。把把(+| | )dd*表示成表示成e1e2,其中,其中e1=+| | ,e2=dd*,这样,简化的构造有三步,过程如,这样,简化的构造有三步,过程如图:图:编译原理与技术编译原理与技术112+dd+dd2.4有限自动机有限自动机编译原理

113、与技术编译原理与技术113例例2.18:对正规式:对正规式ab*(ab|ba)a*构造等价的构造等价的NFA。首先将其改造成形式为首先将其改造成形式为e1e2e3的三个子正规式的连接,其中的三个子正规式的连接,其中e1=ab*,e2=e4|e5,e3=a*,而,而e4=ab,e5=ba。babbabaabbab*a*ab*abbaab|baaaabbaab*(ab|ba)a*ba2.4有限自动机有限自动机u有限自动机在计算机中的表示有限自动机在计算机中的表示矩阵表示方法、表结构表示和程序表示法矩阵表示方法、表结构表示和程序表示法例例2.19:设一个:设一个DFA的状态集合是的状态集合是s0,s

114、1,s2,s3,其中,其中s0是起始状态,是起始状态,s3是接受状态;输入符号是接受状态;输入符号集合是集合是a,b,映射,映射t(s0,a)=s1,t(s0,b)=s2,t(s1,a)=s1,t(s2,b)=s2,t(s1,b)=s3,t(s2,a)=s3,t(s3,a)=s3。我们可以定义一个矩阵我们可以定义一个矩阵M4,2,使得,使得M0,0=1,M0,1=2,M1,0=1,M2,1=2,M1,1=3,M2,0=3,M3,0=3,其中状态就用状态的下表,其中状态就用状态的下表代替,字母代替,字母a和和b分别用分别用0和和1表示。表示。编译原理与技术编译原理与技术1142.4有限自动机有限

115、自动机表结构表示表结构表示编译原理与技术编译原理与技术115例例2.20:例2.19中的DFA可以表示成如图2.19所示的表结构。比如给定一个状态s1和输入符号b,首先找到s1所在的表项,然后在这个表中找到输入符号b,按照b的指针就可以找到s3的表项。s02a.b.s12a.b.s22a.b.s31a.2.5词法分析的自动生成器词法分析的自动生成器LexuLex系统示意图系统示意图编译原理与技术编译原理与技术116Lex编译系统词法分析器Lex源程序词法分析器输入流单词记号Lex源程序是用一种面向问题的语言写成的,这个语言的核心是正规表达式,用它描述输入串的词法结构。在这个语言中用户还可以描述

116、当某一个词形被识别出来时要完成的动作,例如在高级语言的词法分析器中,当识别出一个关键字时,它应该向语法分析器返回该关键字的内部编码。Lex并不是一个完整的语言,它只是某种高级语言(称为Lex的宿主语言)的扩充,因此Lex没有为描述动作设计新的语言,而是借助其宿主语言来描述动作,常用C作为Lex的宿主语言。2.5词法分析的自动生成器词法分析的自动生成器LexuLex的语言与实现的语言与实现Lex源程序的一般格式是:源程序的一般格式是:辅助定义的部分辅助定义的部分%识别规则部分识别规则部分%用户子程序部分用户子程序部分编译原理与技术编译原理与技术1172.5词法分析的自动生成器词法分析的自动生成器

117、Lex辅助定义的部分辅助定义的部分l在在Lex源程序中,用户为方便起见,需要一些辅助定义,如源程序中,用户为方便起见,需要一些辅助定义,如用一个名字代表一个复杂的正规式。辅助定义必须在第一个用一个名字代表一个复杂的正规式。辅助定义必须在第一个之前给出,并且从第一列开始写,辅助定义的语法是:之前给出,并且从第一列开始写,辅助定义的语法是:nametranslationl例如用名字例如用名字IDENT来代表标识符的正规式的辅助定义为来代表标识符的正规式的辅助定义为IDENTa-zA-Za-zA-Z0-9*辅助定义在识别规则中的使用方式是用运算符辅助定义在识别规则中的使用方式是用运算符将将name括

118、起来,括起来,Lex自动地用自动地用translation去替换它,例如上去替换它,例如上述标识符的辅助定义的使用为:述标识符的辅助定义的使用为:IDENT标识符的动作标识符的动作编译原理与技术编译原理与技术1182.5词法分析的自动生成器词法分析的自动生成器Lex下面我们用辅助定义的手段来写一段识别下面我们用辅助定义的手段来写一段识别FORTRAN语言中整数和实数的语言中整数和实数的Lex源程序:源程序:D0 9EDE +?D+D+printf(“integer”);D+”.”D*(E)?|D*”.”D+(E)?|D+Eprintf(“real”);请注意在辅助定义部分中可以使用前面的辅助定

119、义。请注意在辅助定义部分中可以使用前面的辅助定义。例如:定义例如:定义E时使用了时使用了D,但所用的辅助定义必须是,但所用的辅助定义必须是事先已定义过的,不能出现循环定义。事先已定义过的,不能出现循环定义。编译原理与技术编译原理与技术1192.5词法分析的自动生成器词法分析的自动生成器Lex识别规则部分识别规则部分识别规则都分是识别规则都分是Lex源程序的核心,它是一张表:左源程序的核心,它是一张表:左边一列是正规式,右边是相应的动作,形式如下:边一列是正规式,右边是相应的动作,形式如下:R1A1R2A2.RnAn其中的其中的Ri是正规式,称为词形。是正规式,称为词形。Ai是相应的动作,当是相

120、应的动作,当Lex识别出一个词形时,要完成相应的动作。识别出一个词形时,要完成相应的动作。编译原理与技术编译原理与技术1202.5词法分析的自动生成器词法分析的自动生成器LexLex的工作原理的工作原理Lex程序的编译过程程序的编译过程编译原理与技术编译原理与技术121(1)对每条识别规则构造相应的对每条识别规则构造相应的NFAMi;(2)然后引进一个新的起始状态,把它与然后引进一个新的起始状态,把它与每个每个Mi连接成为一个连接成为一个NFAM;(3)把这个把这个NFAM确定化,必要时进行化确定化,必要时进行化简。简。(4)当识别了一个词形,即到达了某个终当识别了一个词形,即到达了某个终结状

121、态,就调用相应的动作。结状态,就调用相应的动作。(1)将源程序中的正规式转换成相应的确将源程序中的正规式转换成相应的确定有限自动机定有限自动机;(2)把相应的动作则插入到词法分析器中把相应的动作则插入到词法分析器中适当的地方,控制流由该确定有限自适当的地方,控制流由该确定有限自动机的解释器掌握。动机的解释器掌握。P1M2M1MnP2Pnlex把识别规则转换成NFA2.5词法分析的自动生成器词法分析的自动生成器Lexu识别规则的例子识别规则的例子滤掉输入串中所有空格、滤掉输入串中所有空格、tab和回车换行符,相应的和回车换行符,相应的识别规则如下:识别规则如下:tn;如果相邻的几条规则的动作都相

122、同,则可以用如果相邻的几条规则的动作都相同,则可以用“|”表示动作部分,它指出该规则的动作与下一条规则的表示动作部分,它指出该规则的动作与下一条规则的动作相同。例如上例也可以写成:动作相同。例如上例也可以写成:“”|“t”|“n”;编译原理与技术编译原理与技术1222.5词法分析的自动生成器词法分析的自动生成器Lexu识别规则的例子识别规则的例子在输入串中寻找词形在输入串中寻找词形“integer”,每当与之,每当与之匹配成功时,就打印出匹配成功时,就打印出“foundkeywordINT”这句话。在识别规则中,正规式与动作这句话。在识别规则中,正规式与动作之间必须用空格分隔开。之间必须用空格

123、分隔开。integerprintf(”foundkeywordINT”);编译原理与技术编译原理与技术123编译原理与技术编译原理与技术124Lex产生一个把所有大写字母转换成小写字母的程序,在产生一个把所有大写字母转换成小写字母的程序,在C风格注释中的除风格注释中的除外,即任何在外,即任何在/*.*/之间的字符除外。之间的字符除外。%#include#ifndefFALSE#defineFALSE0#endif#ifndefTRUE#defineTRUE1#endif%A-Zputchartolower(yytext0);/*yytext0是找到的单个大写字母*/“/*”charc;intd

124、one=FALSE;ECHO;dowhile(c=input()!=*)putchar(c);putchar(c);while(c=input()=*)putchar(c);putchar(c);if(c=/)done=TRUE;while(!done);%voidmain(void)yylex();在这个例子中使用了Lex一些内部过程。其中使用input而不是getchar,是可以确保使用Lex的输入缓冲区。ECHO是Lex的缺省动作,把yytext(和当前动作匹配的串)的内容打印到yyout(Lex的输出文件,缺省时为stdout)。yylex是Lex扫描字程序。编译原理与技术编译原理与技

125、术第第3章章程序语言的语法描述程序语言的语法描述主要内容主要内容u文法和语言文法和语言u文法的分类文法的分类u文法的等价变换文法的等价变换u语法分析概述语法分析概述编译原理与技术编译原理与技术126程序语言的语法描述程序语言的语法描述u程序语言的定义程序语言的定义程序语言通常是一个能完整、准确和规则地程序语言通常是一个能完整、准确和规则地表达人们的意图,并用以指挥或控制计算机工作表达人们的意图,并用以指挥或控制计算机工作的的“符号系统符号系统”。它完整的定义包括语法、语义。它完整的定义包括语法、语义及语用三个方面。及语用三个方面。一个程序语言的语法是指这样一组规则,使一个程序语言的语法是指这样

126、一组规则,使用它可以形成和产生一个合适的程序。这些用它可以形成和产生一个合适的程序。这些规则一部分称为词法规则,另一部分称为语规则一部分称为词法规则,另一部分称为语法规则(或产生规则)。法规则(或产生规则)。编译原理与技术编译原理与技术127程序语言的语法描述程序语言的语法描述一个程序语言的语义是指这样一组规则,使一个程序语言的语义是指这样一组规则,使用它可以定义一个程序的意义。静态语义是用它可以定义一个程序的意义。静态语义是一系列限定规则,确定哪些合乎语法的程序一系列限定规则,确定哪些合乎语法的程序是合适的;动态语义也称作运行语义或执行是合适的;动态语义也称作运行语义或执行语义,表明程序要做

127、些什么,要计算什么。语义,表明程序要做些什么,要计算什么。语用表示语言符号及其使用者之间的关系,语用表示语言符号及其使用者之间的关系,涉及符号的来源、使用和影响。涉及符号的来源、使用和影响。编译原理与技术编译原理与技术1283.1文法和语言文法和语言u文法的形式定义文法的形式定义定义定义3.1: 定义四元组定义四元组G=(VN,VT,P,S)是一个是一个文法。其中文法。其中VN ,VT和和P都是非空有限集合,分别都是非空有限集合,分别称为非终结符号集合、终结符号集合及产生式集称为非终结符号集合、终结符号集合及产生式集合。合。S称作识别符号或开始符号,它是一个非终称作识别符号或开始符号,它是一个

128、非终结符,至少要在一条产生式中作为左部出现。结符,至少要在一条产生式中作为左部出现。VN和和VT不含公共元素,即不含公共元素,即VNVT=。通。通常用常用V表示表示VNVT,称为文法,称为文法G的字母表。的字母表。编译原理与技术编译原理与技术1293.1文法和语言文法和语言例例3.1:文法文法G1=(VN,VT,P,S),其中,其中VN=S,VT=0,1,P=S0S1,S01。该文法只有一个非终结符该文法只有一个非终结符S,有两个终结符,有两个终结符0和和1,有两,有两条产生式,开始符号是条产生式,开始符号是S。很很多多时时候候,不不用用将将文文法法G的的四四元元组组显显式式地地表表示示出出来

129、来,而而只只将将产产生生式式写写出出。一一般般约约定定,第第一一条条产产生生式式的的左左部部是是开开始始符符号号,用用尖尖括括号号括括起起来来的的是是非非终终结结符符号号,不不用用尖尖括括号号括括起起来来的的是是终终结结符符号号,或或者者用用大大写写字字母母表表示示非非终终结结符符号号,小小写写字字母母表表示示终终结结符符号号。另另外外也也有有一一种种习习惯惯写写法法,将将G写写成成GS,其其中中S是是开开始始符符号号。因因此此,例例3.1还还可可以以写成:写成:G:S0S1或或GS:S0S1S01S01编译原理与技术编译原理与技术1303.1文法和语言文法和语言有有时时,为为书书写写简简洁洁

130、,常常把把相相同同左左部部的的产产生生式式,形形如如A1,A2,An,缩缩写写为为:A1|2|n。这这里里的元符号的元符号|读做读做“或或”。如,例如,例3.1的产生式可以写成的产生式可以写成S0S1|01。注意注意元符号元符号和和源符号源符号的区别:的区别:文法中使用的元符号文法中使用的元符号或或 =表示左部由右部定义,表示左部由右部定义,|表表示定义同一个左部的几个可选择的右部。而源符号是指示定义同一个左部的几个可选择的右部。而源符号是指文法定义的语言中的符号,全部由文法的终结符构成。文法定义的语言中的符号,全部由文法的终结符构成。编译原理与技术编译原理与技术1313.1文法和语言文法和语

131、言例例3.2:文法文法G2=(VN,VT,P,S),其中,其中VN=标识符标识符,字母字母,数字数字,VT=a,b,c,x,y,z,0,1,9,P=标识符标识符字母字母|标识符标识符字母字母|标识符标识符数字数字字母字母a|b|z数字数字0|1|9S=标识符标识符,这里使用尖括号,这里使用尖括号和和括起非终结符。括起非终结符。编译原理与技术编译原理与技术1323.1文法和语言文法和语言例例3.3:一个文法的几种写法:一个文法的几种写法:G=(S,A,a,b,P,S)其中其中P=SaAb,Aab,AaAb,AG:SaAbAabAaAbAGS:AabAaAbASaAbGS:Aab|aAb|SaAb

132、编译原理与技术编译原理与技术1333.1文法和语言文法和语言u推导与归约推导与归约定定义义3.2: 设设是是文文法法G=(VN,VT,P,S)的的产产生生式式,和和是是V*中中的的任任意意符符号号。若若有有符符号号串串v,w满满足足:v=,w=,则则说说v直直接接产产生生w,或或者者说说,w是是v的的直直接接推推导导。记记作作vw。也也可可以以说说w直接归约直接归约到到v。归约是推导的逆过程。归约是推导的逆过程。编译原理与技术编译原理与技术1343.1文法和语言文法和语言对例对例3.1文法文法G,可以给出直接推导的一些例子如下:,可以给出直接推导的一些例子如下:v=0S1,w=0011,直接推

133、导:直接推导:0S10011,使用的规则:使用的规则:S01,这里,这里=0,=1。v=S,w=0S1,直接推导:直接推导:S0S1,使用的规则:使用的规则:S0S1,这里,这里=,=。v=0S1,w=00S11,直接推导:直接推导:0S100S11,使用的规则:使用的规则:S0S1,这里,这里=0,=1。编译原理与技术编译原理与技术1353.1文法和语言文法和语言对例对例3.2文法文法G,直接推导的例子有:,直接推导的例子有:v=标识符标识符,w=标识符标识符字母字母,直接推导:直接推导:标识符标识符标识符标识符字母字母,使用的规则:使用的规则:标识符标识符标识符标识符字母字母,这里这里=。

134、v=标识符标识符字母字母数字数字,w=字母字母字母字母数字数字,直接推导:直接推导:标识符标识符字母字母数字数字字母字母字母字母数字数字,使用的规则:使用的规则:标识符标识符字母字母。这里这里=,=字母字母数字数字。v=abc数字数字,w=abc5,直接推导:直接推导:abc数字数字abc5,使用的规则:使用的规则:数字数字5,这里,这里=abc,=。编译原理与技术编译原理与技术1363.1文法和语言文法和语言定定义义3.3: 如如果果存存在在直直接接推推导导的的序序列列:v=w0w1w2wn=w,(n0),则则称称v推推导导出出w,推推导导长长度度为为n。或或者者称称w归归约约到到v。记记作

135、作vw。若若有有vw,或或v=w,则记作,则记作vw。定定义义3.19: 设设GS是是一一文文法法,如如果果符符号号串串x是是从从开开始始符符号号推推导导出出来来的的,即即有有Sx,则则称称x是是文文法法GS的的句句型型。若若x仅仅由由终终结结符符号号组组成成,即即Sx,xVT*,则称,则称x为为GS的的句子句子。编译原理与技术编译原理与技术1373.1文法和语言文法和语言对例对例3.1文法,存在直接推导序列文法,存在直接推导序列v=0S100S11000S11100001111=w,即即0S100001111,也可记作,也可记作0S100001111。对例对例3.2文法,存在直接推导序列文法

136、,存在直接推导序列v=标识符标识符标识符标识符数字数字字母字母数字数字x数字数字x1=w,即即标识符标识符x1,也可记作,也可记作标识符标识符x1。S,0S1,000111都是例都是例3.1的文法的文法G的句型,其中的句型,其中000111是是G的句子。的句子。标识符标识符字母字母,字母字母数字数字,a1等都是例等都是例3.2文法文法G的句型,其中的句型,其中a1是是G的句子。的句子。编译原理与技术编译原理与技术1383.1文法和语言文法和语言例例3.4:终终结结符符号号串串(i*i+i)是是文文法法EE+E|E*E|(E)|i的的一一个个句句子子。因因为为有有从从开开始始符符号号E至至终终结

137、结符符号号串串(i*i+i)的的一个推导:一个推导:E(E)(E+E)(E*E+E)(i*E+E)(i*i+E)(i*i+i)而而E,(E),(E*E+E)等是文法的句型。等是文法的句型。编译原理与技术编译原理与技术1393.1文法和语言文法和语言定义定义3.4:若推导过程中每一步都是替换最左(右)若推导过程中每一步都是替换最左(右)边的非终结符,则称该推导为边的非终结符,则称该推导为最左(右)推导最左(右)推导。句型的最右推导称为句型的最右推导称为规范推导规范推导,其逆过程最左归,其逆过程最左归约称为约称为规范归约规范归约。例例3.5:文法文法GS:SABAA0|1BB0|S1给出句子给出句

138、子101001的最左和最右推导。的最左和最右推导。解:最左推导:SAB1BB10B10S110AB1101BB11010B1101001最右推导:SABAS1AAB1AA01A1B01A10011B1001101001编译原理与技术编译原理与技术1403.1文法和语言文法和语言u文法产生的语言文法产生的语言定定义义3.5: 文文法法G的的全全部部句句子子组组成成的的集集合合称称为为G产产生的语言,记为生的语言,记为L(G),即,即L(G)=x|Sx,其中,其中S为开始符号,且为开始符号,且xVT*例例3.1的文法的文法G1的语言是的语言是L(G)=0n1n|n1。例。例3.2的的文法文法G2的

139、句子是字母打头的、由字母和数字组成的符号的句子是字母打头的、由字母和数字组成的符号串,也就是程序设计语言中用于表示名字的标识符,因串,也就是程序设计语言中用于表示名字的标识符,因此产生的语言就是所有标识符的集合。此产生的语言就是所有标识符的集合。编译原理与技术编译原理与技术1413.1文法和语言文法和语言例例3.6:考虑文法考虑文法G1:SbAAaA|a求它所定义的语言。求它所定义的语言。解:从开始符S出发,可以推出如下句子:SbAba SbAbaAbaa SbAbaAbaaAbaaa因此,文法G1产生以b开头、后面跟一个或多个a的所有符号串,从而L(G1)=ban|n1。编译原理与技术编译原

140、理与技术1423.1文法和语言文法和语言例例3.7:设有文法设有文法G2:SP|aPbPba|bQaQab求语言求语言L(G2)。解:从开始符S出发,可以推出如下句子: SPba SPbQababaSaPbababSaPbabQabababab文法G2共能产生4个句子,因此L(G2)=ba,baba,abab,ababab编译原理与技术编译原理与技术1433.1文法和语言文法和语言例例3.8:设设G3=(VN,VT,P,S),VN=S,B,E,VT=a,b,e,P由下列产生式组成:由下列产生式组成:(1)SaSBE(2)SaBE(3)EBBE(4)aBab(5)bBbb(6)bEbe(7)eE

141、ee求语言求语言L(G3)。解:L(G3)=anbnen|n1。编译原理与技术编译原理与技术1443.1文法和语言文法和语言u语言的验证语言的验证一一般般来来说说,对对“文文法法G产产生生语语言言L”的的证证明明包包括括两部分:两部分:(1)证明由)证明由G产生的每个字符串都在产生的每个字符串都在L中。中。(2)证明)证明L中的每个字符串都能由中的每个字符串都能由G产生。产生。编译原理与技术编译原理与技术1453.1文法和语言文法和语言例例3.9:验验证证文文法法G1:S(S)S| 产产生生语语言言L(G1)=配配对对的的括号串的集合。括号串的集合。证明:(1)按推导步数进行归纳证明:推出的是

142、配对括号串归纳基础:S归纳假设:少于n步的推导都产生配对的括号串归纳步骤:n步的最左推导如下S(S)S(x)S(x)y其中x、y是配对的括号串,从而(x)y也是配对的括号串,即n步的推导也产生配对的括号串。因此,由G1产生的每个字符串都是配对的括号串都在L(G1)中。编译原理与技术编译原理与技术1463.1文法和语言文法和语言(2)按符号串的长度进行归纳证明:配对括号串可由S推出归纳基础:S归纳假设:长度小于2n的配对括号串都可以从S推导出来归纳步骤:考虑长度为2n(n1)的w=(x)y,其中w、x、y均为配对括号串,有S(S)S(x)S(x)y即长度为2n的配对括号串都可以从S推导出来。因此

143、,L(G1)中的每个字符串都能由G1产生。由(1)、(2)可知,文法G:S(S)S|产生语言L(G1)=配对的括号串的集合。编译原理与技术编译原理与技术1473.1文法和语言文法和语言例例3.10:验证文法验证文法G2:EE+aa产生的语言是所有由若产生的语言是所有由若干个干个“+”分隔开的分隔开的a组成的符号串,即组成的符号串,即L(G2)=a,a+a,a+a+a,a+a+a+a,.证明:(1)按a的数目n归纳证明:每个符号串a+a+.+aL(G2)。归纳基础:因为Ea,所以Ea,aL(G2)。即n=1时成立。归纳假设:假设s=a+a+.+aL(G2),且有n-1个a,则存在推导E s。归纳

144、步骤:使用产生式EE+a一次,再利用归纳假设可得推导:E E+as+a。因此,s+aL(G2),其中有n个a。 因此,所有形如a+a+.+a的符号串在L(G2)中。编译原理与技术编译原理与技术1483.1文法和语言文法和语言(2)按推导的长度n归纳证明:sL(G2)必满足格式a+a+.+a。归纳基础:推导的长度为1时,只能为Ea,而a是满足格式。归纳假设:长度为n-1的推导能推出满足格式a+a+.+a。归纳步骤:考虑长度为n1的推导E s。因为n1,因此第一步推导必然使用产生式EE+a,从而E E+a s+a=s,其中推导E s长度为n-1,由归纳假设可知s满足格式。因此,s=s+a也满足格式

145、a+a+.+a。因此,L(G2)中的所有符号串都满足格式a+a+.+a。 由(1)、(2)可知,文法G2:EE+aa产生语言L(G2)=配对的括号串的集合。编译原理与技术编译原理与技术1493.1文法和语言文法和语言u语言的文法表达语言的文法表达由已知语言求其文法描述,实际上就是讨论由已知语言求其文法描述,实际上就是讨论语言中的句子,根据句子的特点利用层次分析的语言中的句子,根据句子的特点利用层次分析的方法,构造相应的文法。构造过程中经常会用到方法,构造相应的文法。构造过程中经常会用到下面形式的产生式。下面形式的产生式。l右递归(右递归(rightrecursive)产生式:产生式:AaA|a

146、产生产生a+,AaA| 产生产生a*l左递归(左递归(leftrecursive)产生式:产生式:AAa|a产生产生a+,AAa| 产生产生a*编译原理与技术编译原理与技术1503.1文法和语言文法和语言例例3.11:试试构构造造语语言言L1=anbnci|n1,i0=ab,aabb,abc,aabbc,abcc,aabbcc,的文法。的文法。解:L1中符号串的特点是:串中含一个或多个a并置,可以看作含有a+形式;串中含一个或多个b并置,可以看作含有b+形式;串中含有0个或多个c,可以看作是c*形式。因此,该语言可以看作是a+、b+与c*的连接。 使用AaA|a可以产生a+,使用BbB|b可以

147、产生b+,使用CcC|可以产生c*。而要表示它们的连接,只需将非终结符A、B、C连接起来即可。因此,满足要求的文法为G(Z):ZABCAaA|aBbB|bCcC|编译原理与技术编译原理与技术1513.1文法和语言文法和语言也可以使用左递归产生式,得到满足要求的另一个文法G(Z):ZABCAAa|aBBb|bCcC|实际上,a+与b+的产生过程完全一致,可以将产生它们的产生式合并为AaAb|ab。则得到满足要求的另一个文法G(Z):ZABAaAb|abBcB| 由此可以看出,已知语言求文法,文法不唯一,可以有不同的表达方法。编译原理与技术编译原理与技术1523.1文法和语言文法和语言例例3.12

148、:已已知知语语言言L2=x|x a,b,c*,且且x是是回回文文字字= ,aa,bb,cc,aba,aca,bab,bcb,cac,cbc,aabaa,,写写出出该语言的文法。该语言的文法。 解:L2中符号串的特点是:串中若包含符号,则以a开头必以a结束,以b开头必以b结束,串中符号对称出现。可以设计文法为 G(Z):ZaZa|bZb|cZc|a|b|c|。编译原理与技术编译原理与技术1533.1文法和语言文法和语言u分析树与语法树分析树与语法树分析树分析树(parsetree)定定义义3.6: 语语法法分分析析树树用用来来描描述述句句型型的的结结构构,是是句句型推导的一种树型表示,简称分析树

149、。型推导的一种树型表示,简称分析树。给给定定文文法法G=(VN,VT,P,S),对对于于G的的任任何何句句型型都都能能构构造相应的分析树,这棵树满足下列条件:造相应的分析树,这棵树满足下列条件:每个结点都有一个标记。根结点的标记是开始符号每个结点都有一个标记。根结点的标记是开始符号S;非叶结点;非叶结点的标记是非终结符;叶结点的标记是终结符或非终结符或的标记是非终结符;叶结点的标记是终结符或非终结符或。如果一个非叶结点如果一个非叶结点A有有n个儿子结点,从左到右标记为个儿子结点,从左到右标记为A1,A2,Ak,则,则AA1A2Ak一定是一定是P中的一个产生式。中的一个产生式。编译原理与技术编译

150、原理与技术1543.1文法和语言文法和语言例例3.13:文法文法GS:SaAS|SASbA|SS|ba其句子其句子aabbaa的语法分析树如图的语法分析树如图3.1所示。所示。编译原理与技术编译原理与技术155图3.1aabbaa的语法分析树3.1文法和语言文法和语言语语法法树树表表示示了了在在推推导导过过程程中中使使用用了了哪哪个个产产生生式式和和用用在在哪哪个非终结符上,它并没有表明施用产生式的顺序。个非终结符上,它并没有表明施用产生式的顺序。比如例比如例3.13中句子中句子aabbaa的推导过程可以列举的推导过程可以列举3个:个:推导过程推导过程1:SaASaAaaSbAaaSbbaaa

151、abbaa推导过程推导过程2:SaASaSbASaabASaabbaSaabbaa推导过程推导过程3:SaASaSbASaSbAaaabAaaabbaa编译原理与技术编译原理与技术1563.1文法和语言文法和语言语法树语法树(syntaxtree)定定义义3.7: 抽抽象象语语法法树树用用来来表表示示程程序序层层次次结结构构,它它把把分分析析树树中中对对语语义义无无关关紧紧要要的的成成分分去去掉掉,是是分分析析树树的的抽抽象象形形式式,简简称称语语法法树树,也也称称语语法法结结构构树树或或结构树。结构树。语法树可看作分析树的浓缩,而分析树可看语法树可看作分析树的浓缩,而分析树可看成具体语法树。

152、成具体语法树。编译原理与技术编译原理与技术1573.1文法和语言文法和语言例例3.14:条条件件语语句句产产生生式式SifB-exprthenS1elseS2的的抽象语法树与语法分析树如图抽象语法树与语法分析树如图3.2所示。所示。(a)语法树(b)分析树图3.2条件语句的语法树与分析树 抽象语法树中,操作符和关键字都不作为叶结点出抽象语法树中,操作符和关键字都不作为叶结点出现,而是作为内部结点。现,而是作为内部结点。 编译原理与技术编译原理与技术1583.1文法和语言文法和语言例例3.15:赋值语句产生式为赋值语句产生式为赋值语句赋值语句i:=E,表达式的产生式为表达式的产生式为EE+E|E

153、*E|-E|id,则则a:=b*-c+b*-c的语法树与分析树如图的语法树与分析树如图3.3所示。所示。(a)语法树(b)分析树图3.3赋值语句的语法树与分析树编译原理与技术编译原理与技术1593.2文法和语言文法和语言u文法的二义性文法的二义性二义性文法二义性文法定定义义3.8: 给给定定一一个个文文法法G,如如果果其其产产生生的的语语言言L(G)中中存存在在存存在在某某个个句句子子对对应应两两棵棵或或两两棵棵以以上上分析树,则称文法分析树,则称文法G是二义性的。是二义性的。或者说,若一个文法中存在某个句子,它有或者说,若一个文法中存在某个句子,它有两个或两个以上不同的最左两个或两个以上不同

154、的最左(最右最右)推导,则该文推导,则该文法是二义的。法是二义的。编译原理与技术编译原理与技术1603.1文法和语言文法和语言例例3.16:对于简单表达式文法对于简单表达式文法GE:EE+E|E-E|E*E|E/E|(E)|i句句子子i*i+i有有如如下下两两个个不不同同的的最最左左推推导导,它它们们所所对对应应的的分分析析树树如如图图3.4所示。所示。因此该文法是二义性文法。因此该文法是二义性文法。推导一:推导一:EE+EE*E+Ei*E+Ei*i+Ei*i+i推导二:推导二:EE*Ei*Ei*E+Ei*i+Ei*i+i编译原理与技术编译原理与技术161(a)推导一的分析树(b)推导二的分析

155、树图3.4句子i*i+i两棵不同的分析树3.1文法和语言文法和语言注意文法二义性和语言二义性的区别:注意文法二义性和语言二义性的区别:这是两个不同的概念,因为可能有两个不同的文法这是两个不同的概念,因为可能有两个不同的文法G和和G,其中,其中G是二义的,但是却有是二义的,但是却有L(G)=L(G),也就是,也就是说,这两个文法所产生的语言是相同的。如果产生上下说,这两个文法所产生的语言是相同的。如果产生上下文无关语言的每一个文法都是二义的,则说该语言是二文无关语言的每一个文法都是二义的,则说该语言是二义的。义的。编译原理与技术编译原理与技术1623.1文法和语言文法和语言文法二义性的消除文法二

156、义性的消除方法一:方法一:设置一个规则,该规则可在每个二义性情况设置一个规则,该规则可在每个二义性情况下指出哪一个分析树(或语法树)是正确的。这样的规下指出哪一个分析树(或语法树)是正确的。这样的规则称作消除二义性规则(则称作消除二义性规则(disambiguatingrule)。)。其优点为:无需修改文法(可能会很复杂)就可消除二义性;其优点为:无需修改文法(可能会很复杂)就可消除二义性;其缺点为:语言的语法结构再也不能由文法单独提供。其缺点为:语言的语法结构再也不能由文法单独提供。方法二:方法二:将文法改变成一个强制正确分析树的构造的将文法改变成一个强制正确分析树的构造的格式,就可以解决二

157、义性。格式,就可以解决二义性。编译原理与技术编译原理与技术1633.1文法和语言文法和语言为为了了消消除除例例题题3.16中中简简单单表表达达式式文文法法中中的的二二义义性性,可可以以设设置置消消除除二二义义性性规规则则,它它建建立立了了3个个运运算算相相互互之之间间的的优优先先关关系系。其其标标准准解解决决办办法法是是给给予予加加法法和和减减法法相相同同的的优优先先权权,而而乘乘法法和和除除法法则则有有高高一一级级的的优优先先权权,并并按按惯惯例例规规定定它它们们都都服服从从左左结结合合。这这样样句句子子i*i+i相相当当于于(i*i)+i,它它有有惟惟一的分析树为图一的分析树为图3.4(a

158、)。编译原理与技术编译原理与技术1643.1文法和语言文法和语言现现在在来来看看重重写写文文法法以以消消除除二二义义性性的的方方法法。为为了了处处理理文文法法中中的的运运算算优优先先权权问问题题,就就必必须须把把具具有有相相同同优优先先权权的的算算符符归纳在一组中,并为每一种优先权规定不同的规则。归纳在一组中,并为每一种优先权规定不同的规则。例如,将例例如,将例3.11中文法分组为中文法分组为EE+E|E-E|TTT*T|T/T|FF(E)|i在在这这个个文文法法中中,加加法法和和减减法法则则被被归归在在E规规则则下下,乘乘法法和和除除法法被被归归在在T规规则则下下。由由于于T只只能能由由E生

159、生成成,这这就就意意味味着着加加法法和和减减法法在在分分析析树树和和语语法法树树中中将将被被表表现现地地“更更高高一一些些”(也也就就是是更更接接近近于于根根结结点点),因因此此它它们们的的优优先先权权就就比比乘乘法法和和除除法法低低一一级级。这这样样将将算算符符放放在在不不同同的的优优先先权权级级别别中中的的办办法法是是在在语语法法说说明明中中使使用用BNF的的一一个个标标准准方方法法。这这种分组称作种分组称作优先级联优先级联(precedencecascade)。)。编译原理与技术编译原理与技术1653.1文法和语言文法和语言分组后的文法未指出算符的结合性而且仍有二义性。分组后的文法未指出

160、算符的结合性而且仍有二义性。原因在于形如原因在于形如EE+E|E-E的产生式,它既是左递归又的产生式,它既是左递归又是右递归,若要得到符号串是右递归,若要得到符号串i+i-i,既可以先使用,既可以先使用E+E再再对第二个对第二个E使用使用E-E,也可以先使用,也可以先使用E-E再对递一个再对递一个E使使用用E+E。解决方法是强制匹配一边的递归,用。解决方法是强制匹配一边的递归,用EE+T|E-T|T代替代替EE+E|E-E|T,使加法和减法都服从左结,使加法和减法都服从左结合。若要使它们服从右结合,则可以改为合。若要使它们服从右结合,则可以改为ET+E|T-E|T。也就是说,左递归规则使得它的

161、算符在左边结合,。也就是说,左递归规则使得它的算符在左边结合,而右递归规则使得它们在右边结合。而右递归规则使得它们在右边结合。这样,若统一规定服从左结合,则例这样,若统一规定服从左结合,则例3.16中文法可以修改为中文法可以修改为EE+T|E-T|TTT*F|T/F|FF(E)|i编译原理与技术编译原理与技术1663.1文法和语言文法和语言可可以以验验证证,该该文文法法是是一一个个无无二二义义文文法法。句句子子i+i*i-i惟惟一一的的分分析树与语法树如图析树与语法树如图3.5所示所示。(a)分析树(b)语法树图3.5句子i+i*i-i的分析树与语法树编译原理与技术编译原理与技术1673.1文

162、法和语言文法和语言下下面面介介绍绍一一种种确确定定符符号号优优先先关关系系的的方方法法,称称简简单单优优先先关关系。它规定文法系。它规定文法G中任意两个符号的优先关系如下:中任意两个符号的优先关系如下:(1)XY,当且仅当有产生式,当且仅当有产生式ABY,且有推导,且有推导BrX及及Ya。其中其中A、B为非终结符,为非终结符,X、Y为待定优先关系的两为待定优先关系的两个任意符号,个任意符号,、和和为由终结符和非终结符组成的为由终结符和非终结符组成的任意符号串,可以是空串。任意符号串,可以是空串。a是终结符。是终结符。编译原理与技术编译原理与技术1683.1文法和语言文法和语言悬挂悬挂elsee

163、lse问题问题考虑下面条件语句的文法:考虑下面条件语句的文法: statement if-stmt |other if-stmt if(exp)statement |if(exp)statement elsestatement exp 0|1该文法是二义的,符号串该文法是二义的,符号串if(0)if(1)otherelseother两棵两棵分析树,如图分析树,如图3.6所示。所示。编译原理与技术编译原理与技术1693.1文法和语言文法和语言编译原理与技术编译原理与技术170图3.6符号串if(0)if(1)otherelseother两棵不同的分析树(一)3.1文法和语言文法和语言编译原理与技

164、术编译原理与技术171图3.6符号串if(0)if(1)otherelseother两棵不同的分析树(二)3.1文法和语言文法和语言第第一一棵棵分分析析树树将将else部部分分与与第第一一个个if语语句句结结合合;第第二二棵棵分分析析树树将将else与与第第2个个if语语句句结结合合。这这种种二二义义性性称称作作悬悬挂挂else问题问题(danglingelseproblem)。一般的程序语言中,条件语句的一般的程序语言中,条件语句的else部分总是与没有部分总是与没有else部分的最近的部分的最近的if语句结合。这个消除二义性的规则被语句结合。这个消除二义性的规则被称为用于悬挂称为用于悬挂e

165、lse问题的问题的最近嵌套规则最近嵌套规则(mostcloselynestedrule)。)。编译原理与技术编译原理与技术1723.1文法和语言文法和语言解解决决悬悬挂挂else二二义义性性比比处处理理前前面面的的二二义义性性困困难难,修修改改后后为:为:statement matched-stmt |unmatched-stmtmatched-stmt if(exp)matched-stmt elsematched-stmt |otherunmatched-stmt if(exp)statement |if(exp)matched-stmt elseunmatched-stmtexp 0|1

166、if语句中只允许有一个语句中只允许有一个matched-stmt出现在出现在else之前,之前,这样就迫使尽可能快地匹配所有的这样就迫使尽可能快地匹配所有的else部分。使用消除部分。使用消除二义性后的文法,符号串二义性后的文法,符号串if(0)if(1)otherelseother的分的分析树如图析树如图3.7所示。所示。编译原理与技术编译原理与技术1733.1文法和语言文法和语言编译原理与技术编译原理与技术174图3.7消除二义性后的分析树3.2文法的分类文法的分类u形式语言的分类形式语言的分类Chomsky对文法中的规则施加不同限制,将对文法中的规则施加不同限制,将文法和语言分为四大类:

167、文法和语言分为四大类:0型文法型文法1型文法型文法2型文法型文法3型文法型文法编译原理与技术编译原理与技术1753.2文法的分类文法的分类0型文法型文法定定义3.9: 文文法法G=(VN,VT,P,S),如如果果它它的的每每个个产产生生式式形形如如,其其中中V*VNV*,V*,则则G是是一一个个0型文法型文法。其中。其中V=VNVT。0型文法也称型文法也称为短短语文法文法(PSG,phrasestructuregrammars)。由定)。由定义可知,可知,0型文法的型文法的产生式左端是生式左端是由由终结符和非符和非终结符符组成的字符串,并且至少含有一成的字符串,并且至少含有一个非个非终结符。符

168、。产生式右端是由生式右端是由终结符和非符和非终结符符组成成的任意字符串。的任意字符串。编译原理与技术编译原理与技术1763.2文法的分类文法的分类例例3.17:文法文法G0S:SACaB CaaaCCBDBCBEaDDa ADACaEEa AE则该文法是一个则该文法是一个0型文法,产生的语言为型文法,产生的语言为L0=|iI+=aa,aaaa,aaaaaaaa,由由0型文法生成的语言称为型文法生成的语言称为0型语言型语言(或递归可枚举(或递归可枚举语言)。它可由语言)。它可由图灵(图灵(Turing)机)机识别。识别。编译原理与技术编译原理与技术1773.2文法的分类文法的分类1型文法型文法对

169、对于于程程序序设设计计语语言言来来,0型型文文法法有有很很大大的的随随意意性性,还还须须加加以以限限制制。对对0型型文文法法产产生生式式的的形形式式进进一一步步加加以以限限制制,可可以得到以得到1型文法。型文法。定定义义3.10:文文法法G=(VN,VT,P,S),如如果果它它是是0型型文文法法,并并且且每每个个产产生生式式形形如如 1A 2 12,其其中中 1, 2 V*,V+,A VN,则,则G是一个是一个1型文法型文法。1型文法也称为型文法也称为上下文有关文法上下文有关文法(CSG,context-sensitivegrammars)。只有当非终结符)。只有当非终结符A的前后分别为的前后

170、分别为 1, 2时,才能将时,才能将A替换为替换为 。即对非终结符进行替换时,。即对非终结符进行替换时,务必考虑上下文,这也是称之为上下文有关文法的原因。务必考虑上下文,这也是称之为上下文有关文法的原因。并且一般不允许替换成空串并且一般不允许替换成空串 。编译原理与技术编译原理与技术1783.2文法的分类文法的分类1型文法还有另一种定义形式:型文法还有另一种定义形式:定定义义3.11: 文文法法G=(VN,VT,P,S),如如果果它它是是0型型文文法法,并并且且每每个个产产生生式式满满足足|(产产生生式式S例例外外,但但S不不得得出出现现在在任任何何产产生生式式的的右右部部),则则G是是一一个

171、个1型型文文法法。其其中中| |和和| |分别为分别为 和和 的长度。的长度。由由该该定定义义可可知知,1型型文文法法除除了了可可能能有有S外外,其其余余产产生生式右端的符号串长度大于等于左端符号串长度。式右端的符号串长度大于等于左端符号串长度。上述两种定义是等价的。即任一种形式定义的文法产上述两种定义是等价的。即任一种形式定义的文法产生的语言都可由另一种形式定义的文法产生。需要注意生的语言都可由另一种形式定义的文法产生。需要注意的是,根据定义,含有的是,根据定义,含有 产生式的文法不是产生式的文法不是1型文法。由型文法。由于实际需要,我们将于实际需要,我们将S作为作为1型文法的特例接受。型文

172、法的特例接受。编译原理与技术编译原理与技术1793.2文法的分类文法的分类例例3.18:文法文法G1S:S |AAaABCAabCCBBCbBbbbCbccCcc则该文法是一个则该文法是一个1型文法,产生的语言为型文法,产生的语言为L1=aibici|i0=,abc,aabbcc,1型文法产生的语言称为型文法产生的语言称为上下文有关语言上下文有关语言CSL,它可,它可由由线性限界自动机线性限界自动机识别。识别。编译原理与技术编译原理与技术1803.2文法的分类文法的分类2型文法型文法对对1型文法的产生式进一步限制,可得型文法的产生式进一步限制,可得2型文法。型文法。定定义3.12: 文文法法G

173、=(VN,VT,P,S),如如果果它它是是1型型文文法法,并并且且每每个个产产生生式式A满满足足AVN,V+,(产产生式生式S例外),则例外),则G是一个是一个2型文法型文法。2型型文文法法也也称称为为上上下下文文无无关关文文法法(CFG,context-freegrammars)。用用取取代代非非终终结结符符A时时,与与A所所在在的上下文无关,因此取名为上下文无关文法。的上下文无关,因此取名为上下文无关文法。由定由定义可知,可知,2型文法所有型文法所有产生式左端的符号都是生式左端的符号都是单个的非个的非终结符。除了可能有符。除了可能有产生式生式S外,其余外,其余产生式右端不能是空串。生式右端

174、不能是空串。编译原理与技术编译原理与技术1813.2文法的分类文法的分类例例3.19:文法文法G2S:SaSbSab则该文法是一个则该文法是一个2型文法,产生的语言为型文法,产生的语言为L2=aibi|i1=ab,aabb,aaabbb,程序设计语言的文法是上下文无关的,如程序设计语言的文法是上下文无关的,如C,Pascal。2型文法产生的语言称为型文法产生的语言称为上下文无关语言上下文无关语言CFL,它可由,它可由下下推自动机推自动机识别。识别。编译原理与技术编译原理与技术1823.2文法的分类文法的分类3型文法型文法对对2型文法的产生式进一步限制,可得型文法的产生式进一步限制,可得3型文法

175、。型文法。定定义3.13: 文文法法G=(VN,VT,P,S),如如果果它它是是2型型文文法法,并并且且仅仅含含有有形形如如AaB或或Aa的的产产生生式式,则则称称G为为右右线线性性文文法法。若若仅仅含含有有形形如如ABa或或Aa的的产产生生式式,则则称称G为为左左线线性性文文法法。其其中中A,B VN,a VT。左左线性文法和右性文法和右线性文法都称性文法都称为3型文法型文法。3型文法也称型文法也称正规文法正规文法(RG,regulargrammars)。)。编译原理与技术编译原理与技术1833.2文法的分类文法的分类例例3.20:文法文法G3S:SlA|l,A0A|0该文法是一个右线性文法

176、,产生的语言为该文法是一个右线性文法,产生的语言为L3=10i|i0=1,10,100,1000,例例3.21:文法文法G4S:SBc|Sc,BAb|Bb,AAa|a该文法是一个左线性文法,产生的语言为该文法是一个左线性文法,产生的语言为L4=aibjck|i,j,k1例例3.22:定义标识符的定义标识符的3型(正规)文法为型(正规)文法为GI:IlT|l,TlT|dT|l|d其中其中l是英文字母是英文字母l,表示,表示az中的任何一英文字母,中的任何一英文字母,d表示表示09中的中的任一数字。任一数字。编译原理与技术编译原理与技术1843.2文法的分类文法的分类3型型文文法法产产生生的的语语

177、言言称称为为3型型正正规规语语言言(或或正正规规语语言言),它可由它可由有限自动机有限自动机识别。正规语言可用正规表达式表示。识别。正规语言可用正规表达式表示。四个文法类的定义是逐渐增加限制的,因此每一种正四个文法类的定义是逐渐增加限制的,因此每一种正规文法都是上下文无关的,每一种上下文无关文法都是规文法都是上下文无关的,每一种上下文无关文法都是上下文有关的,而每一种上下文有关文法都是上下文有关的,而每一种上下文有关文法都是0型文法。型文法。它们之间是逐级它们之间是逐级“包含包含”的关系,由四种文法产生的语的关系,由四种文法产生的语言也是逐级言也是逐级“包含包含”的关系。的关系。编译原理与技术

178、编译原理与技术1853.2文法的分类文法的分类u上下文无关文法上下文无关文法上下文无关文法拥有足够强的表达力来表示上下文无关文法拥有足够强的表达力来表示大多数大多数程序设计语言程序设计语言的语法。的语法。比如描述算术表达比如描述算术表达式,描述各种语句等等。式,描述各种语句等等。实际上,几乎所有实际上,几乎所有程序程序设计语言设计语言都是通过上下文无关文法来定义的。另都是通过上下文无关文法来定义的。另外,上下文无关文法足够简单,可以构造有效的外,上下文无关文法足够简单,可以构造有效的分析算法来检验一个给定符号串是否是由某个上分析算法来检验一个给定符号串是否是由某个上下文无关文法产生。下文无关文

179、法产生。编译原理与技术编译原理与技术1863.2文法的分类文法的分类例例3.23:文法文法G=(E,+,*,i ,(,),P,E),其中,其中P=EE+E|E*E|(E)|i该文法是上下文无关文法。这里的非终结符该文法是上下文无关文法。这里的非终结符E表示一类算术表达式。表示一类算术表达式。i表示程序设计语言中的表示程序设计语言中的“变量变量”,该文法定义了,该文法定义了(描述了描述了)由变量、由变量、+、*、(和和)组成的算术表达式的语法结构,即:变量是算术表达组成的算术表达式的语法结构,即:变量是算术表达式;若式;若E1和和E2是算术表达式,则是算术表达式,则E1+E2、E1*E2和和(E

180、1)也是算术表也是算术表达式。达式。例例3.24:描述语句的产生式:描述语句的产生式:语句语句条件语句条件语句|赋值语句赋值语句|循环语句循环语句简单赋值语句的产生式为:简单赋值语句的产生式为:赋值语句赋值语句i:=E条件语句的产生式:条件语句的产生式:条件语句条件语句if条件条件then语句语句|if条件条件then语句语句else语句语句它们都符合上下文无关文法它们都符合上下文无关文法CFG的产生式要求。的产生式要求。编译原理与技术编译原理与技术1873.2文法的分类文法的分类u正规文法和正规式正规文法和正规式一个正规语言可以由正规文法定义,也可一个正规语言可以由正规文法定义,也可以由正规

181、式定义,对任意一个正规文法,存在一以由正规式定义,对任意一个正规文法,存在一个定义同一个正规语言的正规式;反之,对每个个定义同一个正规语言的正规式;反之,对每个正规式,存在一个生成同一语言的正规文法,有正规式,存在一个生成同一语言的正规文法,有些正规语言很容易用文法定义,有些语言更容易些正规语言很容易用文法定义,有些语言更容易用正规式定义,现在介绍两者间的转换,从结构用正规式定义,现在介绍两者间的转换,从结构上建立它们的等价性。上建立它们的等价性。编译原理与技术编译原理与技术1883.2文法的分类文法的分类将将上的一个正规式转换成正规文法上的一个正规式转换成正规文法G=(VN,VT,P,S)。

182、令令VT=,确定产生式和,确定产生式和VN的元素的方法如下:的元素的方法如下:l对任何正规式对任何正规式r,选择一个非终结符,选择一个非终结符S生成产生式生成产生式Sr,为区别文法中的产生式,把这个产生式叫,为区别文法中的产生式,把这个产生式叫做正规产生式,并将做正规产生式,并将S定为定为G的开始符号。的开始符号。l若若x和和y都是正规式,对形如都是正规式,对形如Axy的正规产生式,的正规产生式,重写成:重写成:AxB,By两个正规产生式,其中两个正规产生式,其中B是新选择的非终结符,即是新选择的非终结符,即B VN。编译原理与技术编译原理与技术1893.2文法的分类文法的分类l对已形成的形如

183、对已形成的形如Ax*y的正规产生式,引入新的的正规产生式,引入新的非终结符非终结符B,重写为:,重写为:AxB|y BxB|y对形如对形如Ax|y的正规产生式,重写为:的正规产生式,重写为:Ax,Ay不断利用上述不断利用上述规则做做变换,直到每个,直到每个产生式都符合生式都符合三型文法的要求。三型文法的要求。编译原理与技术编译原理与技术1903.2文法的分类文法的分类例例3.25:将将R=a(a|d)*转换成相应的正规文法。转换成相应的正规文法。解:令S是文法的开始符号,首先形成Sa(a|d)*,然后形成SaA和A(a|d)*,重写第二条产生式形成SaAA(a|d)BAB(a|d)BB进而变换

184、为正规文法:SaABaBAaBBdBAdBBA编译原理与技术编译原理与技术1913.2文法的分类文法的分类将正规文法转换成正规式。将正规文法转换成正规式。基本上是上述过程的逆过程,转换规则列于表基本上是上述过程的逆过程,转换规则列于表3.1表3.1正规文法到正规式的转换规则.文法产生式正规式规则1规则2规则3AxBByAxA|yAxAyA=xyA=x*yA=x|y编译原理与技术编译原理与技术1923.2文法的分类文法的分类例例3.26:文法文法GS:SaASaAaAAdAAaAd写出其对应的正规式。写出其对应的正规式。解:先有S=aA|aA=(aA|dA)|(a|d),再将A的正规式变换为A=

185、(a|d)A|(a|d),据表中规则2变换为:A=(a|d)*|(a|d),再将A右端代入S的正规式得:S=a(a|d)*|a(a|d)|a,再利用正规式的代数变换可依次得到S=a(a|d)*|(a|d)|),S=a(a|d)*。a(a|d)*即为所求。编译原理与技术编译原理与技术1933.3文法的等价变换文法的等价变换定定义义3.14:若若L(G1)=L(G2),则则称称文文法法G1和和G2是是等价的。等价的。也也就就是是说说,如如果果两两个个文文法法定定义义的的语语言言相相同同,则称这两个文法是等价的。则称这两个文法是等价的。例如:下列两个文法例如:下列两个文法G1A与与G2S等价等价G1

186、A:A0RA01RA1G2S:S0S1S01L(G1)=L(G2)=0n1n|n1。编译原理与技术编译原理与技术1943.3文法的等价变换文法的等价变换定定义义3.15: 对对文文法法进进行行变变换换,使使变变换换后后的的文文法法满满足足某某种种要要求求并并与与原原文文法法等等价价,这这种种变变换换称称为为文文法法的等价变换。的等价变换。不不存存在在一一个个能能判判定定两两个个文文法法是是否否等等价价的的算算法法。但但是是ji我们经常使用下列等价变换:我们经常使用下列等价变换:l增广文法增广文法l消除回溯消除回溯l消除左消除左递归编译原理与技术编译原理与技术1953.3文法的等价变换文法的等价

187、变换u增广文法增广文法对对任任一一文文法法G1均均可可构构造造文文法法G2,使使得得L(G1)=L(G2),并并且且G2的的初初始符号不出现于产生式的右部。始符号不出现于产生式的右部。定义定义3.16: 设文法设文法GS=(VN,VT,P,S),构造文法,构造文法GS=(VNS,VT,P,S),其中,其中P=A |APSS,显然显然L(G)=L(G),称,称G为文法为文法G的的增广文法增广文法。例例3.27:G1Z:ZabZA|a,Ab经过等价变换后可得到增广文法经过等价变换后可得到增广文法G2Z:ZZZabZA|aAb编译原理与技术编译原理与技术1963.3文法的等价变换文法的等价变换u提取

188、左因子提取左因子定义定义3.17: 若文法中有产生式若文法中有产生式P1|2|.|n,则称该文法含有则称该文法含有左因子左因子。其中其中P VN,,1,2,.,n (VNVT)*。假设假设P有产生式有产生式P1|2|.|n|1|2|.|n其中其中i(i=1,2,.,n)不以不以开头。则可将其改写为开头。则可将其改写为PP|1|2|.|nP1|2|.|n编译原理与技术编译原理与技术1973.3文法的等价变换文法的等价变换例例3.28:文法文法GS:SiEtS|iEtSeS|aEb提取左因子后,该文法变为:提取左因子后,该文法变为:GS:SiEtSS|aSeS|Eb反复提取左因子,可以消除某些文法

189、的回溯。相应反复提取左因子,可以消除某些文法的回溯。相应付出的代价是,引进了较多新的非终结符。付出的代价是,引进了较多新的非终结符。编译原理与技术编译原理与技术1983.3文法的等价变换文法的等价变换u消除左递归消除左递归定定义义3.18: 若若文文法法中中存存在在推推导导PP,则则称称该该文文法法含含有有左左递归递归。若存在产生式若存在产生式PP,则称文法含有,则称文法含有直接左递归直接左递归。若若存存在在产产生生式式PP1,P1P2,Pn-1Pn,PnP,则称文法含有,则称文法含有间接左递归间接左递归。其中其中P,P1,.,Pn VN,, (VNVT)*。左递归会导致推导过程中出现大量的死

190、循环,因此左递归会导致推导过程中出现大量的死循环,因此需要消除文法中的左递归。需要消除文法中的左递归。编译原理与技术编译原理与技术1993.3文法的等价变换文法的等价变换直接左递归的消除直接左递归的消除假假设设非非终终结结符符P存存在在产产生生式式PP|,其其中中是是不不以以P开开头头的字符串。的字符串。(1)删除左递归产生式删除左递归产生式PP;(2)引入新的非终结符消除文法中的左递归,得:引入新的非终结符消除文法中的左递归,得:PPPP| 一般地,若非终结符有产生式一般地,若非终结符有产生式PP1|P2|.|Pm|1|2|.|n其其中中i (i=1,2,.,m),i(i=1,2,.,n)不

191、不以以P开开头头。则则消消除除左左递递归归之后为之后为P1P|2P|.|nPP1P|2P|.|mP| 编译原理与技术编译原理与技术2003.3文法的等价变换文法的等价变换例例3.29:文法文法GE:EE+T|TTT*F|FF(E)|i消除左递归之后得到如下文法:GE:ETEE+TE|TFTT*FT|F(E)|i间接左递归的消除间接左递归的消除(1)将间接左递归转化成直接左递归;将间接左递归转化成直接左递归;(2)消除直接左递归;消除直接左递归;(3)化化简文法,文法,删除含有从开始符号无法到达的非除含有从开始符号无法到达的非终结符的符的产生生式。式。编译原理与技术编译原理与技术2013.3文法

192、的等价变换文法的等价变换例例3.30:文法文法GS:SAa|aABb|bBSc|c将BSc|c代入ABb|b得AScb|cb|b,代入SAa|a得SScba|cba|ba|a。得到含直接左递归文法:G1S:SScba|cba|ba|aAScb|cb|bBSc|c消除直接左递归得:G2S:ScbaS|baS|aSScbaS|AScb|cb|bBSc|c其中A、B均不能由开始符号S推出,因此删除无用产生式后得:GS:ScbaS|baS|aSScbaS|选择其他的代入顺序也可以得到消除左递归后的文法,所得的文法都是等价的。编译原理与技术编译原理与技术2023.3文法的等价变换文法的等价变换算法3.1

193、消除左递归输入:无回路且无空产生式的文法GS输出:不含左递归的文法GS以某种顺序将文法非终结符排列P1,P2,Pnfori=1tondoforj=1toi-1doifPj1|2|kthen将PiPj改写为Pi1|2|kendfor;消除Pi的直接左递归endfor;化简得到的文法编译原理与技术编译原理与技术2033.4语法分析概述语法分析概述语法分析是编译过程的核心部分。语法分析是编译过程的核心部分。语法分析的任务语法分析的任务是:是:按照文法,从源程序符号串中识别出各类语法成份,按照文法,从源程序符号串中识别出各类语法成份,同时进行语法检查,为语义分析和代码生成做准备。执同时进行语法检查,为

194、语义分析和代码生成做准备。执行语法分析任务的程序称为分析程序,也称为语法分析行语法分析任务的程序称为分析程序,也称为语法分析器,它是编译程序的主要子程序之一,在编译程序中的器,它是编译程序的主要子程序之一,在编译程序中的地位如图地位如图3.14所示。所示。图3.8语法分析器在编译程序中的地位编译原理与技术编译原理与技术2043.4语法分析概述语法分析概述典型的语法分析方法有三类:典型的语法分析方法有三类:通用方法,能分析任何文法。通用方法,能分析任何文法。但但这类方法在生成方法在生成编译器器时效率太低。效率太低。自自顶向下的分析方法向下的分析方法自底向上的分析方法自底向上的分析方法这两两类方法

195、通常只能方法通常只能处理文法的一些子理文法的一些子类,而而这些子些子类中的某些文法足以用来描述程序中的某些文法足以用来描述程序语言的大部分言的大部分语法法结构,如构,如LL文法和文法和LR文法。文法。编译原理与技术编译原理与技术2053.4语法分析概述语法分析概述u自顶向下的语法分析自顶向下的语法分析自顶向下的语法分析是从文法的开始符号出发,反自顶向下的语法分析是从文法的开始符号出发,反复使用各种产生式,推导出句型,并一个符号一个符号复使用各种产生式,推导出句型,并一个符号一个符号地与给定终结符号串进行匹配,寻找匹配于输入串的推地与给定终结符号串进行匹配,寻找匹配于输入串的推导。如果全部匹配成

196、功,表示开始符号可推导出给定终导。如果全部匹配成功,表示开始符号可推导出给定终结符号串,由此判定给定的终结符号串是文法的句子。结符号串,由此判定给定的终结符号串是文法的句子。编译原理与技术编译原理与技术2063.4语法分析概述语法分析概述例例3.30:文法文法GZ:ZaBb|aDBb|bBDd|bD用用自自顶顶向向下下分分析析法法判判断断终终结结符符号号串串abbd是是否否是是文文法法G的的一个句子。一个句子。解:从文法的开始符号Z出发可以得到如下推导ZaDabDabbDabbd这样就找到了匹配于输入串abbd的推导,所以终结符号串abbd是文法G的一个句子。编译原理与技术编译原理与技术207

197、3.4语法分析概述语法分析概述例例3.31:考虑文法考虑文法GS:ScAdAab|a识别输入串识别输入串w=cabd是否该文法的句子。是否该文法的句子。解:由开始符号S开始,试着为cabd建立一棵分析树,如图3.9(a)所示。在第一步,只有唯一的一个产生式ScAd可施用,可得直接推导ScAd。从S向下画分析树,如图3.9(b)所示。这棵树的最左叶子标记为c,已和w的第一个符号匹配,考虑下一个叶子,标记A,可用A的第一个候选产生式Aab去扩展A,则会得到如图3.9(c)所示的分析树,得到的直接推导为cAdcabd。这时输入符号串w的第二个符号a得到了匹配,第三个输入符号为b,将它与下一叶子标记b

198、相比较,得以匹配,叶子d匹配了第四个输入符号,这时可以宣布识别过程结束。所得到的推导过程为:ScAdcabd。编译原理与技术编译原理与技术2083.4语法分析概述语法分析概述图3.9自顶向下的分析步骤编译原理与技术编译原理与技术2093.4语法分析概述语法分析概述u自底向上的语法分析自底向上的语法分析自自底底向向上上分分析析就就是是从从输输入入串串开开始始,逐逐步步进进行行归归约约,直直至至归归到到文文法法的的开开始始符符号号。或或者者说说,从从语语法法树树的的未未端端开开始始,步步步步向向上上归归约约,直直到到根根结结点点。若若不不能能到到达达开开始始符符号号或根结点,则说明此输入串不是该文

199、法的句子。或根结点,则说明此输入串不是该文法的句子。例例3.32:文法文法GZ:ZaBb|aDBb|bBDd|bD用用自自底底向向上上分分析析法法判判断断终终结结符符号号串串abbd是是否否为为文文法法G的的一个句子。一个句子。编译原理与技术编译原理与技术2103.4语法分析概述语法分析概述解:句型归约规则abbdDdabbDDbDabDDbDaDZaDZ说明:先将句型abbd中的d按照产生式规则归约成D,形成句型abbD,再将新产生的句型abbD中的bD按照产生式规则归约成D,形成句型abD,然后将abD中的bD规约成D,形成句型aD,最后将句型aD归约成Z,即文法的开始符号。因为能归约为文

200、法的开始符号Z,所以终极符串abbd是文法G的一个句子。编译原理与技术编译原理与技术2113.4语法分析概述语法分析概述仍仍使使用用例例3.31中中的的文文法法来来为为输输入入符符号号串串cabd构构造造推推导导或或分分析树,所采用的是自底向上的方法。析树,所采用的是自底向上的方法。首先从输入符号串开始。扫描cabd,从中寻找一个子串,该子串与某一产生式的右端相匹配。子串a和子串ab都是合格的,假若我们选用了ab,用产生式(2)的左端A去替代它,即把ab归约到了A,得到了串cAd。构造了一个直接推导cAdcabd,即从cabd叶子开始向上构造语法树,如图3.10(b)所示。接下去,在得到的串c

201、Ad中又找到了子串cAd与产生式(1)的右端相匹配,则用S替代cAd,或称将cAd归约到S,得到了又一直接推导ScAd,形成了图3.10(c)所示的语法树,符号串cabd的推导序列为:ScAdcabd。编译原理与技术编译原理与技术2123.4语法分析概述语法分析概述图3.10自底向上的分析编译原理与技术编译原理与技术2133.4语法分析概述语法分析概述u语法分析的基本问题语法分析的基本问题(1)如何选择使用哪个产生式进行推导?)如何选择使用哪个产生式进行推导?假定要被替换的最左非终结符号是假定要被替换的最左非终结符号是B,而有,而有n条产生式:条产生式:BA1|A2|An,那么如何确定用哪个右

202、部去替换,那么如何确定用哪个右部去替换B?有一种解决办法是从各种可能的选择中随机挑选一有一种解决办法是从各种可能的选择中随机挑选一种,并希望它是正确的。如果以后发现它是错误的,我种,并希望它是正确的。如果以后发现它是错误的,我们必须退回去,再试另外的选择,这种方式称为回溯。们必须退回去,再试另外的选择,这种方式称为回溯。显然这样做代价极高,效率很低。这个问题我们将在第显然这样做代价极高,效率很低。这个问题我们将在第四章中具体解决。四章中具体解决。编译原理与技术编译原理与技术2143.5语法分析概述语法分析概述(2)如何识别可归约的串?)如何识别可归约的串?在自底向上的分析方法中,在分析程序工作

203、的每一步,在自底向上的分析方法中,在分析程序工作的每一步,都是从当前串中选择一个子串,将它归约到某个非终结都是从当前串中选择一个子串,将它归约到某个非终结符号,该子串称为符号,该子串称为“可归约串可归约串”。问题是,每一步如何。问题是,每一步如何确定这个确定这个“可归约串可归约串”?这是自底向上分析的关键问题,需要精确定义这是自底向上分析的关键问题,需要精确定义“可可归约串归约串”。事实上,存在种种不同的方法刻画。事实上,存在种种不同的方法刻画可归约可归约串串。对这个概念的不同定义形成了不同的自底向上分。对这个概念的不同定义形成了不同的自底向上分析方法。我们将在第五章中详细介绍。析方法。我们将

204、在第五章中详细介绍。编译原理与技术编译原理与技术215编译原理与技术编译原理与技术第第4章章自顶向下的语法分析自顶向下的语法分析主要内容主要内容u自顶向下语法分析概述自顶向下语法分析概述uLL(1)文法文法u递归下降分析技术递归下降分析技术u预测分析技术预测分析技术uLL(1)分析中的错误处理分析中的错误处理编译原理与技术编译原理与技术2174.1自顶向下语法分析的一般方法自顶向下语法分析的一般方法u基本思想:基本思想:对任何输入串,试图用一切可能的办法,从对任何输入串,试图用一切可能的办法,从文法开始符号出发,自上而下,从左到右地文法开始符号出发,自上而下,从左到右地为输入串建立分析树。或者

205、说,为输入串寻为输入串建立分析树。或者说,为输入串寻找最左推导。找最左推导。u特点:特点:本质上是一种试探过程,反复使用不同的产本质上是一种试探过程,反复使用不同的产生式谋求匹配输入串。生式谋求匹配输入串。编译原理与技术编译原理与技术2184.1自顶向下语法分析的一般方法自顶向下语法分析的一般方法例例4.1:设文法设文法GS:ScAd,Aab|a,输入串为,输入串为cad,自,自顶向下进行语法分析,并构造相应语法树。顶向下进行语法分析,并构造相应语法树。编译原理与技术编译原理与技术219先按文法的开始符号产生根结点S,选择S惟一的产生式构造语法树,如图4.1(a)所示。把S的子结点从左到右与输

206、入串中的字符相比较,第一个子结点c匹配输入串第一个符号。第二个子结点是非终结符A,要选择A的产生式进一步构造。而A有两个候选式,先选择Aab构造语法树,如图4.1(b)所示。(a)(b)(c)图4.1输入串cad的语法分析树4.1自顶向下语法分析的一般方法自顶向下语法分析的一般方法编译原理与技术编译原理与技术220此时A的最左子结点a匹配输入串第二个符号。而A的第二个子结点b和输入串第三个符号不一致,说明A的这个产生式不能用来产生该输入串。这就需要回到子结点A,选用另一个产生式Aa来构造语法树,如图4.1(c)所示。这种回头选用其他产生式的过程称为回溯。回溯时,要把由前面产生式Aab得到的子树

207、删除,并重新读入在子结点A处的当时符号a。用新的产生式构造语法树后,A的子结点为a,与当前符号一致,读入最后一个输入符号d。此时结点A完成匹配,它的右边是结点d,与当前符号一致。这样就完成了输入串的匹配,说明输入串cad是该文法的一个句子。上面的分析同时也给出了输入串cad的最左推导过程:ScAdcad。(a)(b)(c)4.1自顶向下语法分析的一般方法自顶向下语法分析的一般方法u这种一般方法存在一些问题:这种一般方法存在一些问题:(1)左递归问题左递归问题自顶向下分析采取最左推导,文法中含有左递归会使自顶向下分析采取最左推导,文法中含有左递归会使自上而下的分析过程陷入无限循环。因此,必须消除

208、文自上而下的分析过程陷入无限循环。因此,必须消除文法的左递归。法的左递归。(2)回溯问题回溯问题反复寻找可正确匹配的产生式时可能需要不断回溯,反复寻找可正确匹配的产生式时可能需要不断回溯,虚假匹配现象需要使用更复杂的回溯技术。这样将会产虚假匹配现象需要使用更复杂的回溯技术。这样将会产生许多额外工作,因此应设法消除回溯。生许多额外工作,因此应设法消除回溯。编译原理与技术编译原理与技术2214.1自顶向下语法分析的一般方法自顶向下语法分析的一般方法(3)出错处理出错处理分析不成功时,要确定出错的具体位置比较困难。分析不成功时,要确定出错的具体位置比较困难。(4)效率问题效率问题这种带回溯的自顶向下

209、方法实际上是一种穷尽一切可这种带回溯的自顶向下方法实际上是一种穷尽一切可能的试探法,因此效率很低,代价较高,从而该方法只能的试探法,因此效率很低,代价较高,从而该方法只有理论意义,在实际应用中价值不大。有理论意义,在实际应用中价值不大。编译原理与技术编译原理与技术2224.2LL(1)文法文法要实现无回溯的自顶向下语法分析,对相应文法要实现无回溯的自顶向下语法分析,对相应文法必须要有一定的限制。首先,文法应该不含左递必须要有一定的限制。首先,文法应该不含左递归,若文法中含有左递归,则需使用文法的等价归,若文法中含有左递归,则需使用文法的等价变换消除左递归。其次,还要消除回溯。变换消除左递归。其

210、次,还要消除回溯。通过提取左因子消除某些文法的回溯,为什么?通过提取左因子消除某些文法的回溯,为什么?没有左递归和左因子的文法是否一定可以进行确没有左递归和左因子的文法是否一定可以进行确定的自顶向下分析?定的自顶向下分析?编译原理与技术编译原理与技术2234.2LL(1)文法文法u首符集首符集FIRST编译原理与技术编译原理与技术224例例4.2:文法文法G1S:SpA|qBAcAd|aBdB|bw1=pccadd自顶向下的推导过程:S pA pcAd pccAdd pccadd语法树:ddSpASpAcA dSpAcA dcA dSpAcAcAa4.2LL(1)文法文法文法文法G1S:SpA

211、|qBAcAd|aBdB|b这个文法的特点:这个文法的特点:每个产生式的右部都由终结每个产生式的右部都由终结符号开始。符号开始。如果两个产生式有相同的左如果两个产生式有相同的左部,那么它们的右部由不同部,那么它们的右部由不同的终结符开始。的终结符开始。 编译原理与技术编译原理与技术225对于这样的文法,分析输入串时,可以跟据输入串的当前符号确定选取产生式。比如w1=pccadd,第一个符号是p,而从开始符号S出发,只有选择产生式SpA推导,才能出现符号p。同样,要出现第二个符号c,必须选择产生式AcAd。这样,虽然具有相同左部的产生式不只一个,但文法的特点决定了每一步推导只能选择惟一的产生式,

212、从而可以避免回溯。4.2LL(1)文法文法编译原理与技术编译原理与技术226例例4.3:文法文法G2S:SAa|BbAa|cABb|dBw2=ccaa自顶向下的推导过程:S Aa cAa ccAa ccaa语法树:SAaSAacASAacAcASAacAcAa4.2LL(1)文法文法文法文法G2S:SAa|BbAa|cABb|dB这个文法的特点:这个文法的特点:每个产生式的右部不全是由每个产生式的右部不全是由终结符号开始。终结符号开始。如果两个产生式有相同的左如果两个产生式有相同的左部,那么它们的右部由不同部,那么它们的右部由不同的终结符或非终结符开始。的终结符或非终结符开始。文法中无空产生式

213、。文法中无空产生式。编译原理与技术编译原理与技术227对于这样的文法,分析输入串时,也可以跟据输入串的当前符号确定地选取产生式。比如推导w2=ccaa时,首先考虑开始符号S,它可以推出以A或B开头的符号串。而由以A和B为左部的产生式可知,A只能推出以a,c开头的符号串,B只能推出以b,d开头的符号串。因此,要得到w2的第一个符号c,只有选择产生式SAa,AcA。同样,要出现第二个符号c,仍需选择产生式AcA,没有别的选择。这样,文法的特点决定了每一步推导只能选择确定的产生式,从而可以避免回溯。4.2LL(1)文法文法由上面两个例子可知,一个文法在推导过程中是否会产由上面两个例子可知,一个文法在

214、推导过程中是否会产生回溯,与文法中具有相同左部的每个产生式右部所能生回溯,与文法中具有相同左部的每个产生式右部所能推出的开头符号有关系。推出的开头符号有关系。定义定义4.1:设设G=(VN,VT,P,S)是上下文无关文法,对于是上下文无关文法,对于V*,V=VNVT,定义首符集,定义首符集FIRST()为为FIRST()=a|,aVT,V*即即FIRST()=a|a,aVT,V*特别地,若特别地,若,则规定,则规定FIRST(),即,即FIRST()是是能推导出的所有在开头位置的终结符或空符。能推导出的所有在开头位置的终结符或空符。编译原理与技术编译原理与技术2284.2LL(1)文法文法编译

215、原理与技术编译原理与技术229由此我们可以看出,如果文法中不含空符产生式,并由此我们可以看出,如果文法中不含空符产生式,并且每个非终结符的所有候选式右部的首符集两两不相交,且每个非终结符的所有候选式右部的首符集两两不相交,则推导中就不会产生回溯。而提取左因子正是为了达到则推导中就不会产生回溯。而提取左因子正是为了达到这个目的,即反复提取左因子后,就能够把每个非终结这个目的,即反复提取左因子后,就能够把每个非终结符(包括新引进的非终结符)的所有候选首符集变成两符(包括新引进的非终结符)的所有候选首符集变成两两不相交。因此,提取左因子可以使得不含空符产生式两不相交。因此,提取左因子可以使得不含空符

216、产生式的文法消除回溯。的文法消除回溯。4.2LL(1)文法文法u后继符集后继符集FOLLOW编译原理与技术编译原理与技术230例例4.4:文法文法G3S:SAa|BbAa|cA|Bb|dB输入串w3=cca自顶向下的推导过程为:SAacAaccAaccaG3与例4.3中文法G2惟一不同之处在于,G3中非终结符A的候选式中含有空符产生式。分析时,对于输入串w3的前两个符号cc,可以确定使用产生式AcA,而要得到第三个符号a,按照a所在的首符集我们应该选择产生式Aa,但是显然这种选择是错误的,因为这样得到的是符号串ccaa而不是cca。实际上,这时正确的选择是产生式A,也就是让A自动匹配到空符,就

217、可以得到与输入串匹配的符号串cca。4.2LL(1)文法文法编译原理与技术编译原理与技术231这说明只要求文法每个非终结符的所有候选首符集两两不相交是不够的,还需要进一步讨论。观察上面例子可以看出,之所以会出现上述问题,是因为文法中含有空符产生式A,并且推导过程中A后面跟的终结符a恰好也是A的一个右部首符集中的符号。也就是说,a既能紧跟在A的后面出现,也能由A推出。这样,如果遇到当前非终结符是A而输入串中相应符号为a时,就不容易确定该用产生式A将A自动匹配空符得到紧跟其后的a,还是用产生式Aa推出a。文文法法G3S:SAa|BbAa|cA|Bb|dB4.2LL(1)文法文法因此,一个文法能否进

218、行确定的自顶向下语法分析,不因此,一个文法能否进行确定的自顶向下语法分析,不仅仅与文法中具有相同左部的产生式右部的仅仅与文法中具有相同左部的产生式右部的FIRST集有集有关系,若有产生式右部可能推出关系,若有产生式右部可能推出,则还与其左部非终结,则还与其左部非终结符的后继符号集合有关。符的后继符号集合有关。定义定义4.2:设设G=(VN,VT,P,S)是上下文无关文法,对于是上下文无关文法,对于PVN,定义后继符集,定义后继符集FOLLOW(P)为为FOLLOW(P)=a|S P 且且aFRIST( ), VT*, V+即即FOLLOW(P)=a|SPa,aVT。特别地,若特别地,若SP,则

219、规定,则规定$FOLLOW(P)。即。即FOLLOW(P)是推导过程中所有可能紧跟在是推导过程中所有可能紧跟在P之后的终结之后的终结符或边界符号符或边界符号$($用来界定输入串,表示为:用来界定输入串,表示为:$输入串输入串$)。编译原理与技术编译原理与技术2324.2LL(1)文法文法编译原理与技术编译原理与技术233可以看出,开始符号S后面不会跟任何符号,但是有SS,因此FOLLOW(S)=$。非终结符A后面可能不跟任何符号,即SA,也可能跟开始符号S,而S推导的符号串只能以a,d开头,即FIRST(S)=a,d,因此FOLLOW(A)=a,d,$。例例4.5:文法文法G4S:SaA|dA

220、bAS|输入串w4=abd的推导过程为:SaAabASabSabd4.2LL(1)文法文法u一般地,文法中含有形如一般地,文法中含有形如P|,PVN,,V*的产的产生式时,若生式时,若,不能同时推导出空符,不妨设不能同时推导出空符,不妨设,,则当,则当FIRST()(FIRST()FOLLOW(P)=时,对于非终结符时,对于非终结符P可以确定地选取产生式。可以确定地选取产生式。编译原理与技术编译原理与技术234比如例4.4中,产生式Aa|cA|,FOLLOW(A)=a,$,FIRST()=FIRST(a|cA)=a,c,FIRST()=FIRST()=,FIRST()(FIRST()FOLLO

221、W(A)=a,ca,$=a。因此不能确定选取产生式。而例4.5中,产生式AbAS|,FOLLOW(A)=a,d,$,FIRST()=FIRST(bAS)=b,FIRST()=FIRST()=,FIRST()(FIRST()FOLLOW(A)=ba,d,$=。因此可以确定选取产生式。4.2LL(1)文法文法u选择集选择集SELECT定义定义4.3:给定不含左递归的上下文无关文法的产生式给定不含左递归的上下文无关文法的产生式P,PVN,V*,定义选择集,定义选择集SELECT(P)为为若若,则,则SELECT(P)=FIRST()若若,则,则SELECT(P)=(FIRST()-)FOLLOW(P

222、)也就是说,当文法中含有形如也就是说,当文法中含有形如P|(PVN,,V*,不同时能推出不同时能推出)的产生式时,能够使用确定)的产生式时,能够使用确定的自顶向下分析必须使文法满足的自顶向下分析必须使文法满足SELECT(P)SELECT(P)=编译原理与技术编译原理与技术2354.2LL(1)文法文法比如,例4.5中SELECT(SaA)=FIRST(aA)=a,SELECT(Sd)=FIRST(d)=dSELECT(AbAS)=FIRST(bAS)=b,SELECT(A)=(FIRST()-)FOLLOW(A)=a,d,$SELECT(SaA)SELECT(Sd)=ad=SELECT(Ab

223、AS)SELECT(A)=ba,d,$=因此,文法G4可以进行确定的自顶向下语法分析。编译原理与技术编译原理与技术236例例4.5:文文法法G4S:SaA|dAbAS|4.2LL(1)文法文法uLL(1)文法文法定义定义4.4:文法文法G是是LL(1)文法,当且仅当每个非终结符文法,当且仅当每个非终结符P的任何两个候选式的任何两个候选式P|(,V*)满足:)满足:不存在终结符不存在终结符a,使得,使得和和推出的符号串都能以推出的符号串都能以a开开头。即头。即FIRST()FIRST()=若若,,则,则所能推出的符号串的开头符号不在所能推出的符号串的开头符号不在FOLLOW(P)中。即中。即FI

224、RST()FOLLOW(P)=编译原理与技术编译原理与技术2374.2LL(1)文法文法由由SELECT集可以得到集可以得到LL(1)文法的另一个等价定义:文法的另一个等价定义:定义定义4.5:一个上下文无关文法是一个上下文无关文法是LL(1)文法,当其仅当对文法,当其仅当对于每个非终结符于每个非终结符P的任何两个候选式的任何两个候选式P|满足满足SELECT(P)SELECT(P)=其中其中PVN,,V*,且,且,不同时推出不同时推出。编译原理与技术编译原理与技术238例例4.6:下面文法是否是下面文法是否是LL(1)文法?文法?(1)G1S:SaA|dAbAS|(2)G2S:SaAS|bA

225、bA|4.2LL(1)文法文法编译原理与技术编译原理与技术239解:用定义4.4判断(1)对于SaA|d,FIRST(aA)FIRST(d)=ad=;对于AbAS|,FIRST(bAS)FIRST()=b=;FIRST(bAS)=b,FOLLOW(A)=a,d,$,FIRST(bAS)FOLLOW(A)=ba,d,$=;因此文法G1满足条件,,由定义4.4知该文法是LL(1)文法。(2)对于SaA|b,FIRST(aA)FIRST(b)=ab=,对于AbAS|,FIRST(bAS)FIRST()=b=,FIRST(bAS)=b,FOLLOW(A)=a,b,$FIRST(bAS)FOLLOW(A

226、)=ba,b,$=b。因此文法G2满足条件,但不满足条件,从而不是LL(1)文法。4.2LL(1)文法文法编译原理与技术编译原理与技术240用定义4.5判断(1)SELECT(SaA)=FIRST(aA)=aSELECT(Sd)=FIRST(d)=dSELECT(AbAS)=FIRST(bAS)=bSELECT(A)=(FIRST()-)FOLLOW(A)=a,d,$所以SELECT(SaA)SELECT(Sd)=ad=SELECT(AbAS)SELECT(A)=ba,d,$=由定义4.5知文法G1是LL(1)文法。(2)SELECT(SaAS)=FIRST(aAS)=aSELECT(Sb)=

227、FIRST(b)=bSELECT(AbA)=FIRST(bA)=bSELECT(A)=(FIRST()-)FOLLOW(A)=a,b,$所以SELECT(SaAS)SELECT(Sb)=ab=SELECT(AbA)SELECT(A)=ba,b,$由定义4.5知文法G2不是LL(1)文法。4.2LL(1)文法文法uLL(1)文法的特点文法的特点l没有左因子没有左因子l不是二义的不是二义的l不含左递归不含左递归编译原理与技术编译原理与技术241 一个文法中若含有左递归和左因子,则它一定不是一个文法中若含有左递归和左因子,则它一定不是LL(1)LL(1)文法,也就不可能用确定的自顶向下分析法。然而,

228、文法,也就不可能用确定的自顶向下分析法。然而,某些含有左递归和左因子的文法可以通过等价变换,消除某些含有左递归和左因子的文法可以通过等价变换,消除左递归和左因子后可能变为左递归和左因子后可能变为LL(1)LL(1)文法,不过仍需要用文法,不过仍需要用LL(1)LL(1)文法的定义加以判别。也就是说,文法中不含左递文法的定义加以判别。也就是说,文法中不含左递归和左因子只是归和左因子只是LL(1)LL(1)文法的必要条件。文法的必要条件。 4.3递归下降分析技术递归下降分析技术u基本思想基本思想为文法中每个非终结符编写一个递归过程,为文法中每个非终结符编写一个递归过程,每个过程的功能是识别由该非终

229、结符推出的每个过程的功能是识别由该非终结符推出的串,当某非终结符的产生式有多个候选式时,串,当某非终结符的产生式有多个候选式时,按按LL(1)形式唯一确定选择哪个候选式进行推形式唯一确定选择哪个候选式进行推导,若遇到某候选式为导,若遇到某候选式为 ,认为其自动匹配。,认为其自动匹配。把这些把这些递归过程组合起来就构成了文法的递递归过程组合起来就构成了文法的递归下降分析程序。归下降分析程序。编译原理与技术编译原理与技术2424.3递归下降分析技术递归下降分析技术u递归下降分析器的设计递归下降分析器的设计设设LL(1)文法文法G=(VN,VT,P,S),VN=X1,X2,Xn。对。对G的每个非终结

230、符号的每个非终结符号Xi,可以按,可以按照下面方法设计子程序照下面方法设计子程序Pi(): (1)对于形如)对于形如Xi1|2|m的产生式,在相应子程的产生式,在相应子程序序Pi()中,应该能够判断当前输入符号中,应该能够判断当前输入符号a属于哪个候选式属于哪个候选式j的的FIRST集,并转入该候选式相应代码段,继续识别。集,并转入该候选式相应代码段,继续识别。对候选式的选择可用对候选式的选择可用if语句或语句或case语句实现。语句实现。编译原理与技术编译原理与技术2434.3递归下降分析技术递归下降分析技术(2)对于形如)对于形如XiY1Y2Yk(YjVNVT)的产生式,)的产生式,相应子

231、程相应子程Pi()是一个依次识别其右部各符号是一个依次识别其右部各符号Yj(j=1,2,k)的过程:如果)的过程:如果YjVT,则判断当前输入符,则判断当前输入符号是否与号是否与Yj匹配;若匹配;若YjVN则应有调用相应于则应有调用相应于Yj的子程的子程序的代码。序的代码。(3)对于形如)对于形如Xi的产生式,在相应的子程序的产生式,在相应的子程序Pi()中,中,应该能够判断当前输入符号应该能够判断当前输入符号a是否属于集合是否属于集合FOLLOW(Xi),从而决定是从,从而决定是从Pi()返回还是报错。返回还是报错。(4)在各个子程序)在各个子程序Pi()中,均应含有进行语法检查的代中,均应

232、含有进行语法检查的代码。码。编译原理与技术编译原理与技术2444.3递归下降分析技术递归下降分析技术例例4.8:下面文法产生下面文法产生Pascal类型的子集,我们用类型的子集,我们用dotdot表表示示“.”,以强调这个字符序列作为一个词法单元。,以强调这个字符序列作为一个词法单元。typesimple| id|arraysimpleoftypesimpleinteger|char|numdotdotnum显然该文法是显然该文法是LL(1)文法,为其构造递归下降分析器。文法,为其构造递归下降分析器。编译原理与技术编译原理与技术2454.3递归下降分析技术递归下降分析技术编译原理与技术编译原理

233、与技术246使用类Pascal语言为非终结符type设计子程序如下:proccduretype;begincaselookahead ofininteger,char,num:simple():begin match ();match (id)endarray:begin match (array);match();simple();match(); match (of);type()endothererror()endcaseend;4.3递归下降分析技术递归下降分析技术在这段代码中,使用变量lookahead来存放向前查看的单词符号,根据该单词符号的不同而选择不同的动作。具体来说,如果lo

234、okaheadFIRST(simple)=integer,char,num,则转入simple子程序;如果当前单词符号为,则调用匹配函数match,检查是否匹配,若匹配则读入下一个单词符号,存放到变量lookahead中,然后继续调用匹配函数match,检查当前符号是否与match函数的参数id匹配,若匹配则意味着可以选取产生式type id;如果当前单词为array,则依次执行以下操作:匹配array,匹配,调用simple子程序,匹配,匹配of,递归调用type子程序;如果lookahead中的单词符号不是上述符号,则调用出错处理函数error。编译原理与技术编译原理与技术2474.3递归

235、下降分析技术递归下降分析技术编译原理与技术编译原理与技术248我们用nexttoken作为读取下一个单词符号的函数,则match函数的设计如下:procedurematch(t:token);beginiflookahead =tthen lookahead :=nexttoken()elseerror()endifend;4.3递归下降分析技术递归下降分析技术编译原理与技术编译原理与技术249类似地,给出非终结符simple的子程序如下:proceduresimple;begincaselookahead ofinteger:match(integer)char:match (char)nu

236、m:begin match (num);match (dotdot);match (num)endothererror()endcaseend;4.3递归下降分析技术递归下降分析技术编译原理与技术编译原理与技术250这样,我们得到了两个非终结符的子程序,递归下降分析的主程序设计就比较简单了。首先需要读入一个单词符号,然后调用开始符号的子程序,让其自动递归下降进行子过程调用。当所有调用结束,最终回到主程序时,判断是否到达输入串末尾,如果到达,则分析成功,否则出错。我们用函数gettoken读入输入串第一个单词符号,则主程序伪代码如下:beginlookahead =gettoken(); typ

237、e();iflookahead =$exit;elseerror()endifend4.3递归下降分析技术递归下降分析技术编译原理与技术编译原理与技术251将上面的各程序组合起来,就得到了该类型定义文法的递归下降分析器。现在使用该分析器分析输入串arrayintegerofchar,首先读入第一个单词array,然后调用开始符号type的子程序,依次进行下面操作:匹配array,读取下一符号;匹配,读取下一单词integer;调用simple的子程序,匹配integer,读取下一符号。返回type过程,继续进行下面操作:匹配,读取下一单词of;匹配of,读取下一单词char;递归调用type过

238、程,因为char在integer,char,num中,所以调用simple过程,匹配char,读取下一符号,即输入串的结束符$。返回最近的type过程,结束操作,返回外层type过程,结束操作,返回主程序。此时变量lookahead中存放的是结束符$,因此该分析过程结束。说明该输入串符合Pascal类型定义。4.3递归下降分析技术递归下降分析技术例例4.9:为下列表达式文法为下列表达式文法GE编写递归下降识别程编写递归下降识别程序。序。EE+T|TTT*F|FF(E)|i解:步骤1:消除左递归:ETEE+TE|TFTT*FT|F(E)|i步骤2:编写递归下降识别子程序,这里使用C语言形式。见下

239、页图。编译原理与技术编译原理与技术252编译原理与技术编译原理与技术2534.3递归下降分析技术递归下降分析技术编译原理与技术编译原理与技术254步骤3:编写递归下降识别主程序main()lookahead=getsymbol();E();iflookahead=$exit;elseerror();4.3递归下降分析技术递归下降分析技术编译原理与技术编译原理与技术255使用该识别程序识别输入串:i*i+i,首先读入符号i,然后调用子程序E,在E中调用子程序T,在T中调用子程序F,匹配i,读入下一符号*,子程序F调用完毕,回到T。在T中继续调用子程序T,匹配*,读入下一符号i。在T中继续调用子程

240、序F,匹配i,读入下一符号+,回到子程序T。在T中递归调用子程序T,不执行任何操作返回T,进而返回T,进而返回E。在E中继续调用子程序E,匹配+,读入下一符号i,调用子程序T。在T中调用子程序F,匹配i,读入下一符号,为输入串结束符$,返回E。在E中递归调用E,不执行任何操作返回E,进而返回E。至此子程序E调用完毕,回到主程序,检查到达输入串末尾,从而识别成功。4.3递归下降分析技术递归下降分析技术u扩充的巴科斯范式扩充的巴科斯范式EBNF例例4.10:例例4.9的表达式文法用扩充的巴科斯范式表示为:的表达式文法用扩充的巴科斯范式表示为:ET+TTF*FF(E)|i与每个非终结符相对应的递归子

241、程序的伪代码如下:与每个非终结符相对应的递归子程序的伪代码如下:编译原理与技术编译原理与技术256用花括号用花括号表示闭包运算表示闭包运算*。用用n0表示表示可任意重复可任意重复0到到n次,次,特别地,特别地,n0=0=。用方括号用方括号表示表示10,即表示即表示可以出现也可以不出现,等价于可以出现也可以不出现,等价于|。编译原理与技术编译原理与技术2574.3递归下降分析技术递归下降分析技术编译原理与技术编译原理与技术258使用该识别程序识别输入串:i*i+i。首先读入符号i,然后调用子程序E,在E中调用T,在T中调用F,匹配i,读入下一符号*,返回T。在T中匹配*,读入下一符号i;调用F,

242、匹配i,读入下一符号+,返回T,返回E。在E中匹配+,读入下一符号i,调用T。在T中调用F,匹配i,读入下一符号,为输入串结束符$,返回T,返回E,返回主程序。检查到达输入串末尾,从而识别成功。显然,使用EBNF编写的递归子程序数量较少,分析过程也比较简单。但是需要注意的是,将BNF转化为EBNF通常不是一件很容易的事情。4.3递归下降分析技术递归下降分析技术u递归下降分析的特点递归下降分析的特点优点优点l实现思想简单明了,程序结构和语法规则有直接实现思想简单明了,程序结构和语法规则有直接的对应关系。的对应关系。l由于每个过程表示一个非终结符号的处理,添加由于每个过程表示一个非终结符号的处理,

243、添加语义加工工作比较方便。语义加工工作比较方便。l递归下降分析需要书写程序的语言支持递归调用,递归下降分析需要书写程序的语言支持递归调用,如果递归调用机制是高效的,那么分析程序也是如果递归调用机制是高效的,那么分析程序也是高效的。高效的。编译原理与技术编译原理与技术2594.3递归下降分析技术递归下降分析技术缺点缺点l递归下降分析对文法要求比较高,必须满足递归下降分析对文法要求比较高,必须满足LL(1)文法。文法。当然在某些语言中个别产生式的推导当不满足当然在某些语言中个别产生式的推导当不满足LL(1)而满足而满足LL(2)时,也可以采用多向前扫描一时,也可以采用多向前扫描一个符号的办法。个符

244、号的办法。l由于递归调用多,所以速度慢,占用空间多。由于递归调用多,所以速度慢,占用空间多。尽管如此,递归下降分析方法仍是许多程序语言尽管如此,递归下降分析方法仍是许多程序语言(例如(例如PASCAL,C等编译系统)常常采用的语法分析等编译系统)常常采用的语法分析方法。方法。编译原理与技术编译原理与技术2604.4预测分析技术预测分析技术u预测分析程序的工作过程预测分析程序的工作过程一个预测分析器由预测分析程序一个预测分析器由预测分析程序(总控程序总控程序)、先进后出栈先进后出栈(STACK)、预测分析表三个部分组、预测分析表三个部分组成,其中只有预测分析表与文法有关,它是一个成,其中只有预测

245、分析表与文法有关,它是一个二维矩阵,存放非终结符二维矩阵,存放非终结符X面临输入符号面临输入符号a时应时应选择的产生式,一般用选择的产生式,一般用MX,a表示,是预测分表示,是预测分析程序分析时的主要依据。析程序分析时的主要依据。编译原理与技术编译原理与技术2614.4预测分析技术预测分析技术编译原理与技术编译原理与技术262图4.2预测分析器模型4.4预测分析技术预测分析技术预测分析程序开始时,将预测分析程序开始时,将$置入置入STACK栈,将开始符栈,将开始符号号S置入置入STACK栈,将第一个输入符号读入栈,将第一个输入符号读入a,将栈,将栈顶符号读入顶符号读入X。总控程序执行下面四种动

246、作之一。总控程序执行下面四种动作之一。(1)推导推导若若X VN,并且,并且MX,a中存有产生式中存有产生式XX1X2Xk,则,则X出栈,出栈,X1,X2,Xk反序置入反序置入STACK栈栈(X时直接将时直接将X出栈即可);出栈即可);(2)匹配)匹配若若X=a$,则,则X出栈,把下一输入符号读入出栈,把下一输入符号读入a。(3)接受)接受若若X=a=$,则分析成功,停止分析过程。,则分析成功,停止分析过程。(4)出错)出错若若X VT,但,但Xa,或者若,或者若X VN,但,但MX,a中存中存放出错标记放出错标记error,则报告出错。,则报告出错。编译原理与技术编译原理与技术2634.4预

247、测分析技术预测分析技术编译原理与技术编译原理与技术264预测分析程序工作示意图4.4预测分析技术预测分析技术算法4.1非递归的预测分析输入:输入串w和文法GS的分析表M输出:如果wL(G),输出w的最左推导,否则报告错误。$S进栈,S在栈顶,w$在输入缓冲区,ip指向w$的第一个符号;/准备工作flag:=true/flag作为控制标记whileflagdo令X等于栈顶符号,a等于ip指向的符号;ifXVNthendoifMX,a=XY1Y2Ykthendo从栈中弹出X;把Yk,Yk1,Y1依次压入栈,Y1在栈顶;输出产生式XY1Y2Ykelseerror()/报告错误elseifX=athe

248、ndoifX=$thenflag:=false/结束循环else把X从栈顶弹出,ip指向下一符号elseerror()endwhile;编译原理与技术编译原理与技术2654.4预测分析技术预测分析技术u预测分析表的构造预测分析表的构造构造构造FIRST集集首先求出文法中每个文法符号的首符集,即为首先求出文法中每个文法符号的首符集,即为XVTVN构造构造FIRST(X)。方法如下:。方法如下:l若若X VT,则,则FIRST(X)=X;l若若X VN,且有产生式,且有产生式Xa,则,则FIRST(X)=FIRST(X) a。特别地,若有产生式特别地,若有产生式X,则,则FIRST(X)=FIRS

249、T(X) ;编译原理与技术编译原理与技术2664.4预测分析技术预测分析技术l若若XVN,且有产生式,且有产生式XY1Y2Yk,(i=1,2,k),则,则若若 1ik,使得使得FIRST(Yj),(j=1,2,i-1),即,即Y1Y2Yi-1,则,则FIRST(X)=FIRST(X)(FIRST(Yj)-);若若 1ik,都有都有FIRST(Yj),则,则FIRST(X)=FIRST(X)l反复利用以上规则,直到反复利用以上规则,直到FIRST(X)不再增大为止。不再增大为止。然后求符号串然后求符号串 的首符集的首符集FIRST( ),算法如下:,算法如下:编译原理与技术编译原理与技术2674

250、.4预测分析技术预测分析技术算法4.2求符号串的首符集FIRST()输入:文法G,符号串=X1X2Xn及FIRST(Xi),其中Xi(VNVT),1in输出:首符集FIRST()FIRST()=,k=0fori=1tondoifFIRST(Xi)then置FIRST()=FIRST()(FIRST(Xi)-)且k=k+1else置FIRST()=FIRST()FIRST(Xi)结束循环endfor;ifk=nthen置FIRST()=FIRST()编译原理与技术编译原理与技术2684.4预测分析技术预测分析技术例例4.11:文法文法GS:SAB|bCAb|BaD|CAD|bDaS|c各非终结符

251、的首符集为:FIRST(D)=a,cFIRST(B)=a,FIRST(A)=b,FIRST(C)=(FIRST(A)-)FIRST(D)b=a,b,cFIRST(S)=(FIRST(A)-)(FIRST(B)-)b=a,b,符号串AB的首符集为:FIRST(AB)=(FIRST(A)-)(FIRST(B)-)=a,b,考虑产生式SAB|bC,因为FIRST(AB)FIRST(bC)=a,b,b,由定义4.6可知,该文法不是LL(1)文法。编译原理与技术编译原理与技术2694.4预测分析技术预测分析技术构造构造FOLLOW集集l若若P是文法开始符号,则是文法开始符号,则FOLLOW(P)=$;l

252、若有产生式若有产生式PQ,则,则FOLLOW(Q)=FOLLOW(Q) (FIRST()- )l若有产生式若有产生式PQ,或者有产生式,或者有产生式PQ而而 ,即,即FIRST(),则,则FOLLOW(Q)=FOLLOW(Q) FOLLOW(P)l反复使用上面规则,直到每个反复使用上面规则,直到每个FOLLOW集不再增大为止。集不再增大为止。编译原理与技术编译原理与技术2704.4预测分析技术预测分析技术算法4.3求非终结符后继集FOLLOW输入:文法GS及FIRST(X),所有X(VNVT)输出:所有非终结符的后继集FOLLOWFOLLOW(S)=$FOLLOW(P)=,所有PVN,PSfo

253、r每一个产生式doif该产生式形如PQthen置FOLLOW(Q)=FOLLOW(Q)(FIRST()-)ifthen置FOLLOW(Q)=FOLLOW(Q)FOLLOW(P)if该产生式形如PQthen置FOLLOW(Q)=FOLLOW(Q)FOLLOW(P)endfor;编译原理与技术编译原理与技术2714.4预测分析技术预测分析技术例例4.12:仍讨论例仍讨论例4.11文法文法GS:SAB|bCAb|BaD|CAD|bDaS|c所有非终结符求FOLLOW集过程如下:FOLLOW(S)=$FOLLOW(D)FOLLOW(A)=aa,cFOLLOW(S)FOLLOW(B)=FOLLOW(S)

254、FOLLOW(C)=FOLLOW(S)FOLLOW(D)=FOLLOW(B)FOLLOW(C)从FOLLOW(S)=$开始,不断循环求解直到所有FOLLOW集不再增大,最后可得:FOLLOW(S)=$FOLLOW(A)=a,c,$FOLLOW(B)=$FOLLOW(C)=$FOLLOW(D)=$编译原理与技术编译原理与技术2724.4预测分析技术预测分析技术构造构造SELECT集集算法4.4求选择集SELECT输入:文法GS及FIRST(X),所有X(VNVT),FOLLOW(P),所有PVN输出:所有产生式的选择集SELECTfor每一个产生式P,PVN,V*doifthen置SELECT(

255、P)=FIRST()ifthen置SELECT(P)=(FIRST()-)FOLLOW(P)endfor;编译原理与技术编译原理与技术2734.4预测分析技术预测分析技术例例4.13:例例4.11中文法中文法GS:SAB|bCAb|BaD|CAD|bDaS|c对所有产生式求SELECT集的过程为:SELECT(SAB)=FIRST(AB)FOLLOW(S)=b,a,$SELECT(SbC)=FIRST(bC)=bSELECT(A)=FIRST()FOLLOW(A)=a,c,$SELECT(Ab)=FIRST(b)=bSELECT(B)=FIRST()FOLLOW(B)=$SELECT(BaD)

256、=FIRST(aD)=aSELECT(CAD)=FIRST(AD)=a,b,cSELECT(Cb)=FIRST(b)=bSELECT(DaS)=FIRST(aS)=aSELECT(Dc)=FIRST(c)=c编译原理与技术编译原理与技术2744.4预测分析技术预测分析技术由以上计算结果可得相同左部产生式的SELECT交集为:SELECT(SAB)SELECT(SbC)=b,a,$b=bSELECT(A)SELECT(Ab)=a,c,$b=SELECT(B)SELECT(BaD)=$a=SELECT(CAD)SELECT(Cb)b,a,cbbSELECT(DaS)SELECT(Dc)=ac由LL

257、(1)文法定义知该文法不是LL(1)文法,因为关于S和C的相同左部其产生式的SELECT集的交集不为空。编译原理与技术编译原理与技术2754.4预测分析技术预测分析技术例例4.14:文法文法GE:ETEE+TE| TFTT*FT| F(E)|i求所有非终结符的求所有非终结符的FIRST集和集和FOLLOW集,以及所有产集,以及所有产生式的生式的SELECT集,并判断是否是集,并判断是否是LL(1)文法。文法。编译原理与技术编译原理与技术2764.4预测分析技术预测分析技术所有非终结符的所有非终结符的FIRST集:集:FIRST(E)=(,iFIRST(E)=+, FIRST(T)=(,iFIR

258、ST(T)=*, FIRST(F)=(,i编译原理与技术编译原理与技术277文法文法GE:ETEE+TE| TFTT*FT| F(E)|i4.4预测分析技术预测分析技术所有非终结符的所有非终结符的FOLLOW集:集:FOLLOW(E)=),$FOLLOW(E)=),$FOLLOW(T)=+,),$FOLLOW(T)=+,),$FOLLOW(F)=*,+,),$编译原理与技术编译原理与技术278文法文法GE:ETEE+TE| TFTT*FT| F(E)|i4.4预测分析技术预测分析技术所有产生式的所有产生式的SELECT集为:集为:SELECT(ETE)=(,iSELECT(E+TE)=+SEL

259、ECT(E)=),$SELECT(TFT)=(,iSELECT(T*FT)=*SELECT(T)=+,),$SELECT(F(E)=(SELECT(Fi)i是是LL(1)LL(1)文法。文法。编译原理与技术编译原理与技术279文法文法GE:ETEE+TE| TFTT*FT| F(E)|i4.4预测分析技术预测分析技术构造预测分析表构造预测分析表M方法一:对每个产生式方法一:对每个产生式P讨论讨论SELECT(P),将,将P放入预测分析表放入预测分析表M中非终结符中非终结符P所在的行,所在的行,SELECT(P)中每个终结符中每个终结符a所在的列。所有产生所在的列。所有产生式都讨论过之后,表中没

260、有产生式的地方放入出错标式都讨论过之后,表中没有产生式的地方放入出错标记。构造算法如下:记。构造算法如下:编译原理与技术编译原理与技术2804.4预测分析技术预测分析技术算法4.5构造预测分析表输入:文法GS所有产生式的SELECT集输出:预测分析表Mfor每一个产生式Pdofor每个终结符aSELECT(P)do把P放入MP,aendfor;endfor;把所有无定义的MP,a标上出错标记。编译原理与技术编译原理与技术2814.4预测分析技术预测分析技术方法二:对每个产生式方法二:对每个产生式P先讨论先讨论FIRST(),将,将P放入预测分析表放入预测分析表M中非终结符中非终结符P所在的行,

261、所在的行,FIRST()中中每个终结符每个终结符a所在的列。然后看所在的列。然后看FIRST()中是否有空符中是否有空符,若有,则讨论,若有,则讨论FOLLOW(P),将,将P放入预测分析表放入预测分析表M中非终结符中非终结符P所在的行、所在的行、FOLLOW(P)中每个终结符中每个终结符b所在的列。所有产生式都讨论过之后,表中没有产生式所在的列。所有产生式都讨论过之后,表中没有产生式的地方放入出错标记。构造算法如下:的地方放入出错标记。构造算法如下:编译原理与技术编译原理与技术2824.4预测分析技术预测分析技术算法4.6构造预测分析表输入:文法GS所有非终结符的FIRST集和FOLLOW集

262、输出:预测分析表Mfor每一个产生式Pdofor每个终结符aFIRST()do把P放入MP,aendfor;ifFIRST()thenfor每个bFOLLOW(P)do把P放入MP,aendfor;endfor;把所有无定义的MP,a标上出错标记error。编译原理与技术编译原理与技术2834.4预测分析技术预测分析技术例例4.12:利用例利用例4.14中所求得的中所求得的FIRST集集FOLLOW集和集和SELECT集,按照算法集,按照算法4.5和算法和算法4.6均可构造出文法均可构造出文法GE的预测分析表,如表的预测分析表,如表4.1所示,其空白处均为出错所示,其空白处均为出错标记标记er

263、ror。编译原理与技术编译原理与技术284i+*()$EETEETEEE+TEEETTFTTFTTTT*FTTTFFiF(E)表4.1:例4.14的预测分析表Mu利用该预测分析表分析输入串i+i*i,其过程为:分析步骤STACK栈剩余输入符号串动作/使用的产生式(1)$Ei+i*i$推导/ETE(2)$ETi+i*i$推导/TFT(3)$ETFi+i*i$推导/Fi(4)$ETii+i*i$匹配(5)$ET+i*i$推导/T(6)$E+i*i$推导/E+TE(7)$ET+i*i$匹配(8)$ETi*i$推导/TFT(9)$ETFi*i$推导/Fi(10)$ETii*i$匹配(11)$ET*i$

264、推导/T*FT(12)$ETF*i$匹配(13)$ETFi$推导/Fi(14)$ETii$匹配(15)$ET$推导/T(16)$E$推导/E(17)$分析成功编译原理与技术编译原理与技术285表4.2:对输入串i+i*i的预测分析过程4.5LL(1)分析中的错误处理分析中的错误处理u分析器对错误处理的基本目标分析器对错误处理的基本目标清楚而准确地报告错误的出现;清楚而准确地报告错误的出现;迅速地从每个错误中恢复过来,以便诊断后迅速地从每个错误中恢复过来,以便诊断后面的错误;面的错误;对正确程序的处理速度不至于降低太多。对正确程序的处理速度不至于降低太多。编译原理与技术编译原理与技术2864.5

265、LL(1)分析中的错误处理分析中的错误处理u预测分析器中的错误处理预测分析器中的错误处理预测分析过程中的两种错误预测分析过程中的两种错误l栈顶栈顶X是终结符,但它与当前输入符号是终结符,但它与当前输入符号a不匹配不匹配;l栈顶栈顶X是非终结符,但是非终结符,但MX,a中没有产生式。中没有产生式。错误处理错误处理l如果栈顶终结符不能被匹配,最简单的办法就是跳过该输入如果栈顶终结符不能被匹配,最简单的办法就是跳过该输入符号,让符号,让a指向下一符号,并给出提示的信息,说明输入中指向下一符号,并给出提示的信息,说明输入中插入了该终结符,然后继续进行分析插入了该终结符,然后继续进行分析;l若是第二种情

266、况,可以为非终结符定义同步符号集,当分析若是第二种情况,可以为非终结符定义同步符号集,当分析程序遇到错误时,让其跳过一些符号,直到遇到同步符号,程序遇到错误时,让其跳过一些符号,直到遇到同步符号,然后继续分析。这种方法可以称为紧急方式的错误恢复然后继续分析。这种方法可以称为紧急方式的错误恢复。编译原理与技术编译原理与技术2874.5LL(1)分析中的错误处理分析中的错误处理同步符号同步符号同步符号一般是定界符,如分号或同步符号一般是定界符,如分号或end,它们在源程序中的作用,它们在源程序中的作用是清楚的。紧急方式错误恢复的效果依赖于同步记号集合的选择。是清楚的。紧急方式错误恢复的效果依赖于同

267、步记号集合的选择。这种集合的选择应该使得分析器能迅速地从实际可能发生的错误中这种集合的选择应该使得分析器能迅速地从实际可能发生的错误中恢复过来。恢复过来。一般地,可以将一般地,可以将FOLLOW(P)中所有符号定义为非终结符中所有符号定义为非终结符P的同的同步符号。当出现错误时,可以跳过一些符号直到出现步符号。当出现错误时,可以跳过一些符号直到出现FOLLOW(P)中的符号,则中的符号,则P出栈,继续分析。但是只用出栈,继续分析。但是只用FOLLOW(P)作为作为P的同的同步符号集是不够的。例如在步符号集是不够的。例如在C语言中,用分号作为语句的结束符,语言中,用分号作为语句的结束符,语句开始

268、的关键字可能不出现在表达式非终结符的语句开始的关键字可能不出现在表达式非终结符的FOLLOW(P)集集中,这样当分号被遗漏时,下一语句的关键字就可能被跳过。中,这样当分号被遗漏时,下一语句的关键字就可能被跳过。编译原理与技术编译原理与技术2884.5LL(1)分析中的错误处理分析中的错误处理语言的结构往往是层次的,如表达式出现在语句中,语句出现在语言的结构往往是层次的,如表达式出现在语句中,语句出现在程序块中等。可以把高层结构的开始符号加到低层结构的同步符号程序块中等。可以把高层结构的开始符号加到低层结构的同步符号集中,例如可以把表示语句开始的关键字加入到表达式非终结符的集中,例如可以把表示语

269、句开始的关键字加入到表达式非终结符的同步符号集。同步符号集。如果把如果把FIRST(P)中的终结符加入非终结符中的终结符加入非终结符P的同步符号集,那么的同步符号集,那么只要只要FIRST(P)中的符号在输入中出现,就可以恢复关于中的符号在输入中出现,就可以恢复关于P的分析。的分析。编译原理与技术编译原理与技术2894.5LL(1)分析中的错误处理分析中的错误处理例例4.13:将例将例4.12中构造的预测分析表加入同步符号后的中构造的预测分析表加入同步符号后的预测分析表如表预测分析表如表4.3所示:(其中所示:(其中synch表示由表示由FOLLOW集得到的同步符号)集得到的同步符号)i+*(

270、)$EETEETEsynchsynchEE+TEEETTFTsynchTFTsynchsynchTTT*FTTTFFisynchsynchF(E)synchsynch编译原理与技术编译原理与技术290表4.3:加入同步符号的预测分析表表4.3的使用:如果分析器查找MP,a发现它是空的,则跳过输入符号a;如果MP,a是synch,则调用同步过程并把栈顶的非终结符弹出,恢复分析;如果栈顶不匹配输入符号,则从栈顶弹出该符号。分析输入串*i*+i的过程如表4.4所示。分析步骤STACK栈剩余输入符号串动作/使用的产生式(1)$E*i*+i$ 出错,跳过(2)$Ei*+i$推导/ETE(3)$ETi*+

271、i$推导/TFT(4)$ETFi*+i$推导/Fi(5)$ETii*+i$匹配(6)$ET*+i$推导/T*FT(7)$ETF*+i$匹配(8)$ETF+i$出错,F出栈(9)$ET+i$推导/T(10)$E+i$推导/E+TE(11)$ET+i$匹配(12)$ETi$推导/TFT(13)$ETFi$推导/Fi(14)$ETii$匹配(15)$ET$推导/T(16)$E$推导/E(17)$停止分析编译原理与技术编译原理与技术291表4.4:对输入串*i*+i的预测分析及错误恢复编译原理与技术编译原理与技术第第5章章自底向上的语法分析自底向上的语法分析主要内容主要内容u自底向上语法分析概述自底向

272、上语法分析概述u算符优先分析方法算符优先分析方法uLR分析方法分析方法uLALR分析器的生成工具分析器的生成工具yacc编译原理与技术编译原理与技术2935.1自底向上语法分析概述自底向上语法分析概述u自底向上语法分析器的体系结构自底向上语法分析器的体系结构本书介绍的自底向上的语法分析器明显地使本书介绍的自底向上的语法分析器明显地使用一个栈结构来执行语法分析用一个栈结构来执行语法分析编译原理与技术编译原理与技术294a1.ai.an$X1.Xm自底向上语法分析控制程序自底向上语法分析表分析栈输入串语法分析输出5.1自底向上语法分析概述自底向上语法分析概述包括四个组成:包括四个组成:l记录语法分

273、析信息和过程的语法分析栈记录语法分析信息和过程的语法分析栈;l被分析的输入符号串被分析的输入符号串;l语法分析控制程序语法分析控制程序l表达文法结构和存储控制信息的语法分析表。表达文法结构和存储控制信息的语法分析表。编译原理与技术编译原理与技术2955.1自底向上语法分析概述自底向上语法分析概述控制程序自左向右地逐个扫描输入字符,对每一个分控制程序自左向右地逐个扫描输入字符,对每一个分析栈元素和输入符号的二元组析栈元素和输入符号的二元组,根据不同的,根据不同的语法分析表,执行下列四个动作之一:语法分析表,执行下列四个动作之一:(1)移进)移进把输入串当前符号把输入串当前符号a读入栈顶,并把指针

274、指向下一读入栈顶,并把指针指向下一个输入符号;个输入符号;(2)规约)规约把自栈顶把自栈顶X向下的若干符号串用某个产生式的左向下的若干符号串用某个产生式的左端非终结符替换;端非终结符替换;(3)接受)接受宣布语法分析成功,此时栈指针指向栈内的维一宣布语法分析成功,此时栈指针指向栈内的维一符号符号文法开始符,输入串指针指向结束符号文法开始符,输入串指针指向结束符号$;(4)出错)出错发现源程序有错,调用出错处理程序。发现源程序有错,调用出错处理程序。由于自底向上语法分析的基本动作是移进输入字符或者由于自底向上语法分析的基本动作是移进输入字符或者归约符号串,有时也称之为移进归约分析。归约符号串,有

275、时也称之为移进归约分析。编译原理与技术编译原理与技术2965.1自底向上语法分析概述自底向上语法分析概述步骤分析栈输入符号串动作/归约使用的产生式(1)$i+i$移进(2)$i+i$归约/Fi(3)$F+i$归约/TF(4)$T+i$归约/ET(5)$E+i$移进(6)$E+i$移进(7)$E+i$归约/Fi(8)$E+F$归约/TF(9)$E+T$归约/EE+T(10)$E$接受编译原理与技术编译原理与技术297例例5.1:对于文法对于文法G5.1EE+T|TTT*F|FF(E)|i按照自底向上的按照自底向上的分析方法分析输分析方法分析输入符号串入符号串i+i,就,就是把输入串是把输入串i+

276、i归归约到文法的开始约到文法的开始符号符号E。5.1自底向上语法分析概述自底向上语法分析概述编译原理与技术编译原理与技术298规范归约过程是最右推导过程的相反顺序,表中的规范归约过程是最右推导过程的相反顺序,表中的6个归约正好对应个归约正好对应了最右推导(规范推导)的了最右推导(规范推导)的6个步骤:个步骤:EE+TE+FE+iT+iF+ii+i特别地,语法分析过程可以用一棵分析树表示出来。特别地,语法分析过程可以用一棵分析树表示出来。FiFiTFiTEFiTEFiFiTEFiTFiTEFiTE5.1自底向上语法分析概述自底向上语法分析概述u有两个基本问题值得探讨:有两个基本问题值得探讨:(1

277、)如何确定是执行移进还是归约操作?)如何确定是执行移进还是归约操作?例如,当前输入符号同样是例如,当前输入符号同样是+,在第(,在第(5)行)行是移入栈顶的操作,而第(是移入栈顶的操作,而第(2)、()、(3)和)和(4)行却要进行归约。)行却要进行归约。(2)什么是可归约串?在什么条件下可以归约,)什么是可归约串?在什么条件下可以归约,哪个具体的产生式左部的非终结符可以替换哪个具体的产生式左部的非终结符可以替换(归约)符号串?(归约)符号串?编译原理与技术编译原理与技术2995.1自底向上语法分析概述自底向上语法分析概述对输入串对输入串i+i的算符优先归约过程的算符优先归约过程步骤分析栈剩余

278、输入符号串动作/归约使用的产生式(1)$i+i$移进(2)$i+i$归约/Fi(3)$F+i$移进(4)$F+i$移进(5)$F+i$归约/Fi(6)$F+F$归约/EE+T(7)$F$接受编译原理与技术编译原理与技术300在归约的时候只需知道把当前的可归约串替换为某个非终结符,而不关心这个非终结符具体是什么,这样就可以加快语法分析的过程。例如,本例子当中,可以只把i归约为F,而不必一致规范归约到T。5.1自底向上语法分析概述自底向上语法分析概述u短语、句柄和最左素短语短语、句柄和最左素短语编译程序结构清晰、条理化而且便于高效地实现;编译程序结构清晰、条理化而且便于高效地实现;在设计高级语言时

279、能独立地研究词法与语法两个方面在设计高级语言时能独立地研究词法与语法两个方面的特性;的特性;增强编译程序的可移植性:可以就同一个语言为不同增强编译程序的可移植性:可以就同一个语言为不同的机器写不同的词法分析器,而只编写一个共同的语的机器写不同的词法分析器,而只编写一个共同的语法分析,使用这些词法分析器相同的单词机内表示;法分析,使用这些词法分析器相同的单词机内表示;把同一个词法由于单词记号的语法可以用较简单的文把同一个词法由于单词记号的语法可以用较简单的文法描述,把词法和语法分开,就能为这种文法建立有法描述,把词法和语法分开,就能为这种文法建立有效的特殊方法和自动构造技术。效的特殊方法和自动构

280、造技术。编译原理与技术编译原理与技术3015.1自底向上语法分析概述自底向上语法分析概述u短语、句柄和最左素短语短语、句柄和最左素短语定义定义5.1:令令S是文法是文法G的开始符号,的开始符号,是文法的一个句是文法的一个句型,如果有型,如果有S*P并且并且P,则称则称是句型是句型相对于非终结符相对于非终结符U的短语,其中的短语,其中、和和是文法的符号串,是文法的符号串,和和可以为空,可以为空,P是非终结符。特是非终结符。特别地,如果有别地,如果有U则称则称是句型是句型相对于非终结符相对于非终结符U的直接短语。的直接短语。定义定义5.2:一个句型的最左直接短语称为该句型的句柄。一个句型的最左直接

281、短语称为该句型的句柄。定义定义5.3:素短语是一个包含至少一个终结符的短语,并素短语是一个包含至少一个终结符的短语,并且除自身之外不再有更小的素短语。且除自身之外不再有更小的素短语。编译原理与技术编译原理与技术3025.1自底向上语法分析概述自底向上语法分析概述u短语、句柄和最左素短语短语、句柄和最左素短语编译原理与技术编译原理与技术303例例5.2:考虑文法:考虑文法G5.2EE+T|TTT*F|FFFP|PP(E)|I句型i1*i2+i3的短语是:i1,i2,i3,i1*i2和i1*i2+i3,其中i1,i2和i3是该句型的直接短语,i1在所有这些直接短语的最左边,即是句柄。尽管有E*i2

282、+i3,由于不存在从开始符号E到i1*E的推导,所以i2+i3不是该句型的一个短语。另一个句型E+T*F+i的短语包括:E+T*F+i,E+T*F,T*F和i,其中T*F和i是直接短语,T*F句柄;另外,T*F和i也是素短语,T*F是最左素短语。5.1自底向上语法分析概述自底向上语法分析概述u概念之间的关系以及对应的分析方法概念之间的关系以及对应的分析方法自底向上分析过程的每一步就是寻找句型的自底向上分析过程的每一步就是寻找句型的最左直接短语(句柄)或者最左素短语,把最左直接短语(句柄)或者最左素短语,把它们作为可归约串进行归约的。它们作为可归约串进行归约的。编译原理与技术编译原理与技术304

283、短语素短语最左素短语直接短语句柄是一个产生式的右部至少包含一个终结符最左LR分析算符优先分析5.1自底向上语法分析概述自底向上语法分析概述u规范规约过程规范规约过程句柄总是在栈顶。句柄总是在栈顶。不断检查栈顶的符号串是不断检查栈顶的符号串是否形成了新的句柄,如果否形成了新的句柄,如果是,就把句柄用相应产生是,就把句柄用相应产生式的左部符号替换;式的左部符号替换;如果栈顶没有形成句柄,如果栈顶没有形成句柄,就把把输入符号压在栈顶,就把把输入符号压在栈顶,直到在栈顶形成句柄;直到在栈顶形成句柄;直到把分析栈的符号替换直到把分析栈的符号替换成文法的开始符号。成文法的开始符号。句型与句柄归约规则i+i

284、FiF+iTFT+iETE+iFiE+FTFE+TEE+TE表5.3对输入串i+i按照句柄的归约过程编译原理与技术编译原理与技术3055.1自底向上语法分析概述自底向上语法分析概述u算符优先归约算符优先归约不断地把栈顶的最左不断地把栈顶的最左素短语规约成相应产素短语规约成相应产生式的左部符号,生式的左部符号,或者把输入符号压入或者把输入符号压入栈顶,直到把分析栈栈顶,直到把分析栈的符号替换成文法的的符号替换成文法的开始符号。开始符号。表表5.4是对例是对例5.1输入串输入串i+i依据最左素短语的依据最左素短语的归约过程,句型中的归约过程,句型中的斜体符号串代表该句斜体符号串代表该句型的最左素短

285、语。型的最左素短语。句型与最左素短语归约规则i+iFiF+iFiF+FEE+TE表5.4对输入串i+i按照最左素短语的归约过程编译原理与技术编译原理与技术3065.2算符优先分析方法算符优先分析方法u算符优先文法算符优先文法算符优先分析是一种分析过程比较迅速的自底向上的分析方法,算符优先分析是一种分析过程比较迅速的自底向上的分析方法,特别有利于表达式的分析,便于手工实现。它不是一种严格的特别有利于表达式的分析,便于手工实现。它不是一种严格的最左归约,即不是一种规范归约方法。在算符优先分析方法中,最左归约,即不是一种规范归约方法。在算符优先分析方法中,可归约串就是最左素短语。可归约串就是最左素短

286、语。所谓算符优先就是借鉴了程序语言中,表达式运算的不同优先所谓算符优先就是借鉴了程序语言中,表达式运算的不同优先顺序,在算符,即非终结符之间定义某种优先归约的关系,从顺序,在算符,即非终结符之间定义某种优先归约的关系,从而在句型中寻找可归约串。由于非终结符关系的定义和普通算而在句型中寻找可归约串。由于非终结符关系的定义和普通算术表达式的算符(如加减乘除)运算的优先关系一致,所以得术表达式的算符(如加减乘除)运算的优先关系一致,所以得名算符优先分析方法。名算符优先分析方法。在例子在例子5.3中,句型中,句型E+T*F+i的最左素短语是的最左素短语是T*F,而,而E+T和和F+i都不是短语(为什么

287、?),表明首先把都不是短语(为什么?),表明首先把T*F归约,然后再归约符归约,然后再归约符号和左右符号组成的符号串。这种分析过程正好表示了算术号和左右符号组成的符号串。这种分析过程正好表示了算术表达式中乘除运算的优先级比加减运算的优先级要高。表达式中乘除运算的优先级比加减运算的优先级要高。编译原理与技术编译原理与技术3075.2算符优先分析方法算符优先分析方法u算符文法算符文法定义定义5.4:如果文法如果文法G中没有形如中没有形如U.PR.的产生式,其中的产生式,其中P和和R都是非终结符,即任何产生式都是非终结符,即任何产生式的右部都不含两个连续的非终结符,则称该文法为算的右部都不含两个连续

288、的非终结符,则称该文法为算符文法。符文法。定理定理5.1:在算符文法中不存在包含两个相邻非终结符在算符文法中不存在包含两个相邻非终结符的句型。的句型。定理定理5.2:如果如果aR(或者(或者Ra)出现在算符文法的句型)出现在算符文法的句型中,其中中,其中a是终结符,是终结符,R是非终结符,则是非终结符,则中任何包含中任何包含a的短语必包含的短语必包含R。定理定理5.3:在算符文法的任何句型中,不存在紧前与紧在算符文法的任何句型中,不存在紧前与紧后是非终结符的短语。后是非终结符的短语。编译原理与技术编译原理与技术3085.2算符优先分析方法算符优先分析方法u算符文法的句型的一般形式显然可写成:算

289、符文法的句型的一般形式显然可写成:N1t1N2t2.NmtmNm+1其中其中ti都是终结符,都是终结符,Ni都是非终结符(都是非终结符(i=1,2,.m1),可能出现也可能不出现。),可能出现也可能不出现。编译原理与技术编译原理与技术3095.2算符优先分析方法算符优先分析方法u算符优先文法算符优先文法定义定义5.5:不含:不含产生式的算符文法产生式的算符文法G中,如果任何两个中,如果任何两个终结符终结符a和和b,最多存在下列三种优先关系之一:,最多存在下列三种优先关系之一:(1)a b当且仅当当且仅当G中含有形如中含有形如P.aRb.或或P.ab.的产生式;的产生式;(2)a b当且仅当当且

290、仅当G中含有形如中含有形如P.aR.的产生式,的产生式,而而R+b.或或R+Qb.;(3)a b当且仅当当且仅当G中含有形如中含有形如P.Rb.的产生式,的产生式,而而R+.a或或R+.aQ。其中其中P、R和和Q都是非终结符,则称都是非终结符,则称G为算符优先文法。为算符优先文法。编译原理与技术编译原理与技术3105.2算符优先分析方法算符优先分析方法其中其中 、 和和 称为算符优先关系,它们表示算符文法称为算符优先关系,它们表示算符文法G中任意两中任意两个终结符之间的关系,与非终结符无关。个终结符之间的关系,与非终结符无关。优先关系优先关系a b表示表示a和和b的归约优先关系相等,它们属于同

291、一个可的归约优先关系相等,它们属于同一个可归约串,同时被一个产生式左部的非终结符替换调。归约串,同时被一个产生式左部的非终结符替换调。优先关系优先关系a b表示表示a的归约优先级比的归约优先级比b的要低,只有的要低,只有b所在的符号所在的符号串归约完之后,才能归约包含串归约完之后,才能归约包含a的符号串。的符号串。优先关系优先关系a b则表示则表示a的归约优先级比的归约优先级比b的要高,在包含的要高,在包含a的符号的符号串归约完之后,才能归约包含串归约完之后,才能归约包含b的符号串。的符号串。这些优先关系是有序的,不满足对称性和传递性,即对于文法这些优先关系是有序的,不满足对称性和传递性,即对

292、于文法G的终结符的终结符a、b和和c,如果,如果a b,不一定有,不一定有b a;如果存在;如果存在a b和和b c,不能由此推出,不能由此推出b a或或a c;同样,如果存在;同样,如果存在a b和和b c,也不,也不能得出能得出a c。编译原理与技术编译原理与技术3115.2算符优先分析方法算符优先分析方法+*i()$+*i()$编译原理与技术编译原理与技术312例例5.3:考虑文法G5.2(1)EE+T(2)ET(3)TT*F(4)TF(5)FFP(6)FP(7)P(E)(8)Pi按照定义,根据规则(7)可以得出()。根据规则EE+T和T+T*F,可以得到+*,由T+T*FFP得到+,再

293、由T+FPPP(E)P得到+(,同样得到+i。类似地,从规则(3)到(8)可以得出*,*(以及*i;从规则(5)到(8)可以得出(和i。由EE+T和E+E+T得出+,再由一系列推导EE+TT+TF+TP+T(E)+T,可以得出+)。5.2算符优先分析方法算符优先分析方法编译原理与技术编译原理与技术313为了统一起见,把分析时刻用作结束标志的特殊符号为了统一起见,把分析时刻用作结束标志的特殊符号$当作终结符,并且定当作终结符,并且定义义$ $;对于开始符号为对于开始符号为S的文法的文法G中的任何终结符中的任何终结符a和非终结符和非终结符P,若,若S+a.或或S+Pa.,则定义,则定义$ a;若;

294、若S+.a或或S+.aP,则定义,则定义a $。例如,由规则(例如,由规则(1)可以得出)可以得出$ +和和+ $,由推导,由推导EE+TT+TT*F+T得得出出$ *和和+ $。文法文法G5.2中没有连续的两个非终结符,是一个算符文法,而且其中每一对终中没有连续的两个非终结符,是一个算符文法,而且其中每一对终结符最多只有一种优先关系,所以它是一个算符优先文法。结符最多只有一种优先关系,所以它是一个算符优先文法。观察表观察表5.5可以发现,算符优先关系与数学方面的算符运算的优先关系一致,可以发现,算符优先关系与数学方面的算符运算的优先关系一致,例如例如 *, +。特别是特别是) (表示如果出现

295、了表示如果出现了.)(.的情形,则应该先把包括前面一对括号的符的情形,则应该先把包括前面一对括号的符号串归约,再归约后面一对括号的符号串,这与数学表达式中括号的作用完号串归约,再归约后面一对括号的符号串,这与数学表达式中括号的作用完全一致。全一致。5.2算符优先分析方法算符优先分析方法例例5.4:说明文法:说明文法G5.3:EE+E|E*E|(E)|i是否是算符优先文法。是否是算符优先文法。由于文法的产生式中没有连续的两个非终结由于文法的产生式中没有连续的两个非终结符符E并列在一起或并列在一起或.EE.出现,所以出现,所以G5.3是算是算符文法。符文法。由由EE+E和和E+E*E可以得出可以得

296、出+ *;又由;又由EE*E和和E+E+E可以得出可以得出+ *。这样,对于终结符这样,对于终结符+和和*存在两种优先关系,存在两种优先关系,按照定义,按照定义,G5.3不是算符优先文法。不是算符优先文法。编译原理与技术编译原理与技术3145.2算符优先分析方法算符优先分析方法算符优先关系的构造算符优先关系的构造l容易构造优先关系容易构造优先关系 ,通过逐个检查文法产生式的,通过逐个检查文法产生式的每个候选式,看是否存在每个候选式,看是否存在ab或或aRb(其中(其中a和和b式式终结符,终结符,R是非终结符)形式的子符号串,就可以是非终结符)形式的子符号串,就可以找出所有满足找出所有满足a b

297、的优先关系。的优先关系。l为了构造优先关系为了构造优先关系 ,必须设计一定的方法,对于,必须设计一定的方法,对于每个非终结符每个非终结符R找出满足条件找出满足条件R+b.或或R+Nb.的终结符的终结符b的集合,然后检查文法产生式的所的集合,然后检查文法产生式的所有候选式,对于每个具有有候选式,对于每个具有aR形式的子符号串,终形式的子符号串,终结符结符a和这个集合中的每个终结符都有关系和这个集合中的每个终结符都有关系 。l优先关系优先关系 的构造思路类似。的构造思路类似。编译原理与技术编译原理与技术3155.2算符优先分析方法算符优先分析方法定义定义5.6:对于算符文法:对于算符文法G,定义下

298、面两个终结符的,定义下面两个终结符的集合集合FIRSTTV(P)=a|P+a.或或P+Ra.,a是终是终结符,结符,R是非终结符是非终结符LASTTV(P)=a|P+.a或或P+.aR,a是终是终结符,结符,R是非终结符是非终结符直观地说,直观地说,FIRSTTV指出每个产生式右部的第一个终结符;指出每个产生式右部的第一个终结符;LASTTV指出每个产生式右部的最后一个终结符。指出每个产生式右部的最后一个终结符。编译原理与技术编译原理与技术3165.2算符优先分析方法算符优先分析方法集合集合FIRSTTV(P)的构造方法:的构造方法:(1)若有产生式)若有产生式Pa.或或PRa.,则把,则把a

299、加入加入到集合到集合FIRSTTV(P),即,即a FIRSTTV(P);(2)若有产生式)若有产生式PR.,则把集合,则把集合FIRSTTV(R)并入到集合并入到集合FIRSTTV(P),即若,即若a FIRSTTV(R),则有则有a FIRSTTV(P);(3)反复运用上述两条规则,直到)反复运用上述两条规则,直到FIRSTTV(P)不不再增大为止。再增大为止。编译原理与技术编译原理与技术3175.2算符优先分析方法算符优先分析方法集合集合LASTTV(P)的构造方法:的构造方法:(1)若有产生式)若有产生式P.a或或P.aR,则把,则把a加入加入到集合到集合LASTTV(P),即,即a

300、LASTTV(P);(2)若有产生式)若有产生式P.R,则把集合,则把集合LASTTV(R)并入到集合并入到集合LASTTV(P),即若,即若a LASTTV(R),则有则有a LASTTV(P);(3)反复运用上述两条规则,直到)反复运用上述两条规则,直到LASTTV(P)不不再增大为止。再增大为止。编译原理与技术编译原理与技术3185.2算符优先分析方法算符优先分析方法完成了集合完成了集合FIRSTTV和和LASTTV的计算的计算以后,就可以系统地找到所有以后,就可以系统地找到所有 和和 的优的优先关系:先关系:(1)对于每个形如)对于每个形如R.aP.的产生式,的产生式,对每个对每个b

301、FIRSTTV(R)都有都有a b。(2)对于每个形如)对于每个形如R.Pa.的产生式,的产生式,对每个对每个b LASTTV(R)都有都有a b。编译原理与技术编译原理与技术3195.2算符优先分析方法算符优先分析方法编译原理与技术编译原理与技术320算法算法5.1算符文法G的优先表的构造算法输入:输入:算符文法G以及每个非终结符P的FIRSTTV(P)和LASTTV(P)输出:输出:算符优先表for每个产生式AA1A2.Andofori=1ton1doifAi和Ai+1VT,then置AiAi+1;if(ig(b);若若a b,则令,则令f(a)g(b)。这样,优先函数就可以用数值来表示。

302、这样,优先函数就可以用数值来表示。编译原理与技术编译原理与技术3295.2算符优先分析方法算符优先分析方法如果已知文法如果已知文法G的优先关系,优先函数的方法:的优先关系,优先函数的方法:(1)对于每个终结符)对于每个终结符a(包括(包括$)令)令f(a)=g(a)=1(也可以是其(也可以是其它整数)。它整数)。(2)如果)如果a b,而,而f(a) g(b),则令,则令f(a)=g(b)1;(3)如果)如果a b,而,而f(a) g(b),则令,则令g(b)=f(a)1;(4)如果)如果a b,而,而f(a) g(b),则令,则令g(a)=f(a)maxf(a),g(b);(5.1)重复上述

303、步骤,直到过程收敛为止,此时得到的)重复上述步骤,直到过程收敛为止,此时得到的f和和g就是所求的优先函数。就是所求的优先函数。(5.2)如果重复的过程中有一个函数值大于如果重复的过程中有一个函数值大于2n,则该过程不会,则该过程不会收敛,表明对应的优先函数不存在。收敛,表明对应的优先函数不存在。“过程收敛过程收敛”的意思是得到的意思是得到f和和g的所有值都符合优先函数的定的所有值都符合优先函数的定义,即优先函数值与优先关系完全一致。义,即优先函数值与优先关系完全一致。编译原理与技术编译原理与技术3305.2算符优先分析方法算符优先分析方法例例5.7:请说明文法请说明文法G5.2是否存在优先函数

304、。是否存在优先函数。迭代次数迭代次数+*i()$0f1111111g11111111f2222121g23333112f3444141g24555113f3556161g24666114f3557171g24666115f同第同第4次迭代次迭代g编译原理与技术编译原理与技术3315.2算符优先分析方法算符优先分析方法不难看出,对优先函数每个元素的值都增加同一个常不难看出,对优先函数每个元素的值都增加同一个常数,优先关系不变。因而,同一个文法的优先关系矩数,优先关系不变。因而,同一个文法的优先关系矩阵对应的优先函数不唯一。阵对应的优先函数不唯一。有了优先函数以后,就可以用它取代优先关系表,大有了

305、优先函数以后,就可以用它取代优先关系表,大大节省存储空间。同时,在优先分析的过程中,不需大节省存储空间。同时,在优先分析的过程中,不需要比较符号之间的关系,只要比较符号的优先函数,要比较符号之间的关系,只要比较符号的优先函数,这通常是整数值的比较,可以加快分析速度。这通常是整数值的比较,可以加快分析速度。但是,这样也伴随着一些缺点,即信息有所丢失。十但是,这样也伴随着一些缺点,即信息有所丢失。十分明显,按优先文法的定义,两个符号间或者存在唯分明显,按优先文法的定义,两个符号间或者存在唯一的一种优先关系,或者不存在优先关系。当分析过一的一种优先关系,或者不存在优先关系。当分析过程中不存在优先关系

306、的两个符号相匹配时,表明输入程中不存在优先关系的两个符号相匹配时,表明输入串不是合法的句子,即程序有错。然而,优先函数的串不是合法的句子,即程序有错。然而,优先函数的引进使得任何一对符号总可以进行比较,即使不存在引进使得任何一对符号总可以进行比较,即使不存在优先关系也是如此,这样就使得错误不能及时察觉。优先关系也是如此,这样就使得错误不能及时察觉。编译原理与技术编译原理与技术3325.2算符优先分析方法算符优先分析方法存在一些优先文法,优先矩阵中的关系唯一,却存在一些优先文法,优先矩阵中的关系唯一,却不存在优先函数。不存在优先函数。表5.8(a)优先关系矩阵表5.8(b)优先函数ababaf4

307、4bg44编译原理与技术编译原理与技术3335.3.1LR分析方法概述分析方法概述LR(k)分析的基本过程是一种移进归约的过程。分析的基本过程是一种移进归约的过程。它的分析也是自左向右地扫描输入串,根据栈顶的符号串以及它的分析也是自左向右地扫描输入串,根据栈顶的符号串以及当前输入符号,至多向前查看当前输入符号,至多向前查看k个输入符号,就能确定当前的动个输入符号,就能确定当前的动作是移进还是归约,而且面临归约动作时还能唯一地选定产生作是移进还是归约,而且面临归约动作时还能唯一地选定产生式去归约已经识别的句柄。式去归约已经识别的句柄。L表示自左向右地扫描输入串,表示自左向右地扫描输入串,R表示构

308、造最右推导(规范推导)表示构造最右推导(规范推导)的逆过程,的逆过程,k表示要预测输入字符的个数才能正确地决定动作。表示要预测输入字符的个数才能正确地决定动作。实际使用的编译程序一般不需要在输入串中预测实际使用的编译程序一般不需要在输入串中预测2个以上的符号。个以上的符号。LR分析的归约是规范推导的逆过程,所以分析的归约是规范推导的逆过程,所以LR分析过程是一种规分析过程是一种规范归约过程。范归约过程。编译原理与技术编译原理与技术3345.3.1LR分析方法概述分析方法概述LR(k)技术包含一组方法:技术包含一组方法:LR(0)、SLR(1)、LR(1)和和LALR(1)。LR(0)无需预测输

309、入符号,实现最简单,局限性最大,基本上无无需预测输入符号,实现最简单,局限性最大,基本上无法实用,但是它是法实用,但是它是LR分析的基础。分析的基础。简单的简单的LR,即,即SLR(1),是一种比较容易实现而且具有使用价值,是一种比较容易实现而且具有使用价值的方法,但是却不能分析一些常见程序语言的结构。的方法,但是却不能分析一些常见程序语言的结构。规范的规范的LR分析,能够适用于很大一类文法,分析能力最强,构分析,能够适用于很大一类文法,分析能力最强,构造方法因而也最复杂。造方法因而也最复杂。对对LR(1)的一种改进叫做向前的一种改进叫做向前LR分析(简称分析(简称LALR(1),其中,其中L

310、R是向前是向前lookahead的缩写),它在分析能力和构造工作方面都介的缩写),它在分析能力和构造工作方面都介于于SLR(1)和和LR(1),即,即LALR(1)的使用范围比的使用范围比SLR(1)广泛,实现广泛,实现的工作量也相应地多。的工作量也相应地多。编译原理与技术编译原理与技术3355.3.1LR分析方法概述分析方法概述uLR分析器的体系结构分析器的体系结构编译原理与技术编译原理与技术336a1.ai.an$s0s1.sm自底向上语法分析控制程序分析栈输入串语法分析输出.$X1.Xm$X1.Xmactiongotoa1.ai.an$X1.Xm自底向上语法分析控制程序自底向上语法分析表

311、分析栈输入串语法分析输出LR方法在分析栈中保存分析过程不同形式的相同信息,此外,分析表也不同于算符优先分析的优先关系表,而是直接存放分析的动作:移进、归约、接受和报错。LR分析器的体系结构本书的自底向上分析器的体系结构5.3.1LR分析方法概述分析方法概述LR的分析栈包括一个符号栈和一个状态栈,的分析栈包括一个符号栈和一个状态栈,开始状态开始状态s0对应结束标志符号对应结束标志符号$,在分析过程,在分析过程中,分析栈中的状态和符号是一一对应的。中,分析栈中的状态和符号是一一对应的。分析表由两个部分组成:一个是分析表由两个部分组成:一个是“动作动作”(action),一个是),一个是“状态转换状

312、态转换”(goto)。)。ACTIONs,a“动作动作”,定义当状态,定义当状态s面临面临输入符号输入符号a时应该采取的动作如移进、归约,时应该采取的动作如移进、归约,GOTOsi,Xsj则表示当状态则表示当状态si面临分析栈定面临分析栈定符号符号X(终结符或非终结符)时应该变换成状(终结符或非终结符)时应该变换成状态态sj。编译原理与技术编译原理与技术3375.3.1LR分析方法概述分析方法概述LR分析表的例子如表分析表的例子如表5.9,其中其中ACTION部分的记号部分的记号的含义如下:的含义如下:si把下一个状态把下一个状态si和当前符号和当前符号a移进栈;移进栈;rj按照第按照第j个产

313、生式个产生式进行归约;进行归约;acc接受输入串;接受输入串;空白空白出错标志,调用出错标志,调用错误处理子程序。错误处理子程序。GOTO部分中的数字表示部分中的数字表示状态,空白表示出错。状态,空白表示出错。状态ACTIONGOTOabcde$SAB0s211acc32s43s6s54r2r2r2r2r2r275s86r3r3r3r3r3r37S98r4r4r4r4r4r49r1r1r1r1r1r1编译原理与技术编译原理与技术338表5.9文法G5.4的LR分析表5.3.1LR分析方法概述分析方法概述uACTIONsi,a的动作的动作在状态栈顶为在状态栈顶为si、面临输入符号、面临输入符号a

314、时可以执行下列四个动作之一时可以执行下列四个动作之一(1)移进)移进把状态把状态sj移入状态栈顶,把符号移入状态栈顶,把符号a读入符号栈顶,并把读入符号栈顶,并把指针指向下一个输入符号;指针指向下一个输入符号;(2)规约)规约当符号栈顶形成句柄当符号栈顶形成句柄时,把它规约成产生式时,把它规约成产生式A的左的左部部A:设句柄:设句柄的长度为的长度为r,归约动作就是,归约动作就是把符号栈顶的把符号栈顶的r个符个符号删除,移进符号号删除,移进符号A;把状态栈顶的把状态栈顶的r个状态删除,移进新的个状态删除,移进新的状态状态sjGOTOsim,A。(3)接受)接受宣布语法分析成功,符号栈只有维一符号

315、宣布语法分析成功,符号栈只有维一符号文法开文法开始符,输入串指针指向结束符号始符,输入串指针指向结束符号$;(4)出错)出错状态栈顶遇到了不该出现的符号,发现源程序有错,状态栈顶遇到了不该出现的符号,发现源程序有错,调用出错处理程序。调用出错处理程序。编译原理与技术编译原理与技术339编译原理与技术编译原理与技术340算法算法5.3LR分析算法输入:输入:待分析的输入符号串,LR分析表输出:输出:是句子或不是句子声明:声明:symbol-stack是以数组实现的符号栈,存放还不能归约的符号串;state-stack是以数组实现的状态栈,每个动作之后和symbol-stack的长度一样;k是整型

316、下标变量,表示符号栈和状态栈的使用深度;a存放当前输入符号。在输入串的后面加一个结束标志符$;k=1;symbol-stackk=$;state-stackk=s0;/初始化doa=getchar();/把输入符号串的下一个符号送入rswitchACTIONstate-stackk,acasesi:k=k+1;symbol-stackk=i;state-stackk=acaserj:找到文法的第j个产生式A;k=klength()1;symbol-stackk=A;s=state-stackk;k=k+1;state-stackk=GOTOs,Acase“acc”:returndefault:

317、error();untilr=$5.3.1LR分析方法概述分析方法概述步步骤状状态栈符号符号栈输入串入串ACTIONGOTO10$abbcde$s2202$abbcde$s43024$abbcde$r234023$aAbcde$s650236$aAbcde$r336023$aAcde$s570235$aAcde$s8802358$aAcde$r47902357$aAcBe$s910023579$aAcBe$r111101$S$acc编译原理与技术编译原理与技术341例例5.9文法文法G5.4(1)SaAcBe(2)Ab(3)AAb(4)BdLR分析表如表分析表如表5.9所示,表所示,表5.10

318、给出了输入串给出了输入串abbcde按照算法按照算法5.3的分析过程的分析过程5.3.1LR分析方法概述分析方法概述LR方法的实际分析过程并不是去直接分析文法符号栈中的符号方法的实际分析过程并不是去直接分析文法符号栈中的符号是否形成句柄,但它给我们一个启示:我们可以把终结符和非是否形成句柄,但它给我们一个启示:我们可以把终结符和非终结符都看成一个有限状态机的输入符号,把每一个符号进栈终结符都看成一个有限状态机的输入符号,把每一个符号进栈看成已经识别过了该符号,当识别到可归前缀时,相当在栈顶看成已经识别过了该符号,当识别到可归前缀时,相当在栈顶形成句柄,则认为达到了识别句柄的终态。形成句柄,则认

319、为达到了识别句柄的终态。LR分析方法的基本原理分析方法的基本原理把每个句柄(某个产生式的右部)的识别过程划分为若干状态,把每个句柄(某个产生式的右部)的识别过程划分为若干状态,每个状态从左到右识别句柄中的一个符号,若干状态就可以识每个状态从左到右识别句柄中的一个符号,若干状态就可以识别句柄左端的一部分符号。别句柄左端的一部分符号。识别了句柄的这一部分就相当于识别了当前规范句型的左部分,识别了句柄的这一部分就相当于识别了当前规范句型的左部分,称为称为活前缀活前缀。因此,对句柄的识别就变成了对规范句型活前缀。因此,对句柄的识别就变成了对规范句型活前缀的识别。的识别。LR分析程序实际上就是利用有限自

320、动机来识别给定文法的所有分析程序实际上就是利用有限自动机来识别给定文法的所有规范句型的活前缀。规范句型的活前缀。编译原理与技术编译原理与技术3425.3.1LR分析方法概述分析方法概述形式上说,若形式上说,若S*A是文法是文法G的一个规范推导,并且的一个规范推导,并且符号串符号串是是的前缀,则称的前缀,则称是是G的一个活前缀,即的一个活前缀,即是规范句型是规范句型的一个前缀,但它的右端不超过该句型句柄的末端。的一个前缀,但它的右端不超过该句型句柄的末端。在在LR分析过程中,实际上是把分析过程中,实际上是把的前缀列出放在符号栈中,一的前缀列出放在符号栈中,一旦在栈中出现旦在栈中出现,即形成句柄,

321、则用产生式,即形成句柄,则用产生式A进行归约。进行归约。编译原理与技术编译原理与技术343定义定义5.10:一个符号串的前缀是指该串的:一个符号串的前缀是指该串的任意部分。一个规范句型的前缀若不含任意部分。一个规范句型的前缀若不含句柄之后的任何符号就称为活前缀。句柄之后的任何符号就称为活前缀。5.3.1LR分析方法概述分析方法概述例如,对于例例如,对于例5.9的输入串的输入串abbcde,其规范推导过程为,其规范推导过程为SaAcBeaAcdeaAbcdeabbcde每个句型都是规范句型。这个过程的逆过程就是归约过程,即每个句型都是规范句型。这个过程的逆过程就是归约过程,即从输入串从输入串ab

322、bcde归约到文法的开始符号归约到文法的开始符号S。我们分析其中的一个规范句型我们分析其中的一个规范句型aAbcde,它的句柄是,它的句柄是Ab(最左直(最左直接短语),前缀有接短语),前缀有aAbcde,aAbcd,aAbc,aAb,aA,a以及空以及空字符串字符串 ,活前缀是,活前缀是aAb,在它后面添上终结符串,在它后面添上终结符串cde就是规范句就是规范句型。型。编译原理与技术编译原理与技术3445.3.1LR分析方法概述分析方法概述在上述在上述LR分析过程的第分析过程的第5步,符号栈就是活前缀步,符号栈就是活前缀aAb,把句柄,把句柄Ab归约后就得到另外一个规范句型归约后就得到另外一

323、个规范句型aAcde,它的活前缀是它的活前缀是aAcd。因此,在。因此,在LR分析过程中的符号分析过程中的符号栈顶的符号串,或者输入串的已扫描过部分保持可规栈顶的符号串,或者输入串的已扫描过部分保持可规约成一个活前缀,就意味着所扫描过的输入串部分没约成一个活前缀,就意味着所扫描过的输入串部分没有错误。有错误。编译原理与技术编译原理与技术345对于一个文法G,可以构造一个识别所有活前缀的有限自动机,在此基础上,可以把这种自动机转变成LR分析表。5.3.2LR(0)分析表的构造分析表的构造定义定义5.7:如果如果P是文法是文法G的一个规则,的一个规则,其中其中或或可为空串,则称可为空串,则称P是文

324、法是文法G的的一个一个LR(0)项,简称项。项,简称项。空产生式空产生式P只有一个只有一个LR(0)项项P。例如,对于产生式例如,对于产生式EE+T,有四个,有四个LR(0)项,即项,即EE+T,EE+T,EE+T以及以及EE+T。圆点在产生式最右端的项目称为圆点在产生式最右端的项目称为归约项归约项,如,如EE+T圆点后面是终结符的项目称为圆点后面是终结符的项目称为移进项移进项,如,如EE+T圆点后面是非终结符的项目称为圆点后面是非终结符的项目称为待约项待约项,如,如EE+T编译原理与技术编译原理与技术3465.3.2LR(0)分析表的构造分析表的构造LR(0)项指明了在识别过程中以某个非终结

325、符项指明了在识别过程中以某个非终结符为目标时,某时刻已进行归约到相应规则右为目标时,某时刻已进行归约到相应规则右部的多大部分。部的多大部分。移进项目表示需要把当前符号,即圆点之后移进项目表示需要把当前符号,即圆点之后的终结符移进分析栈;的终结符移进分析栈;归约项目表示需要把当前句柄,即圆点之前归约项目表示需要把当前句柄,即圆点之前的符号串归约成产生式的左部符号,特别是的符号串归约成产生式的左部符号,特别是对于初始项,则表示句子分析成功;对于初始项,则表示句子分析成功;待约项目表示需要把当前符号,即圆点之后待约项目表示需要把当前符号,即圆点之后的非终结首先归约之后才能和圆点之前的符的非终结首先归

326、约之后才能和圆点之前的符号串构成可以归约的句柄。号串构成可以归约的句柄。编译原理与技术编译原理与技术3475.3.2LR(0)分析表的构造分析表的构造定义定义5.8:设设PX是文法是文法G的一个的一个LR(0)项,项,其中其中X是是G的任意符号,则称的任意符号,则称PX是其后是其后继项,继项,X是其后继符号。是其后继符号。直观上看,后继项是把项中圆点右移一个符号位置所直观上看,后继项是把项中圆点右移一个符号位置所得的项。从句型识别的角度看这是显然的:当扫描过得的项。从句型识别的角度看这是显然的:当扫描过的输入符号已与产生式右部的的输入符号已与产生式右部的匹配时,后继的输入匹配时,后继的输入符号

327、应该期望与产生式右部后继的符号符号应该期望与产生式右部后继的符号X项匹配。项匹配。为了构造识别文法为了构造识别文法G的所有活前缀的有限自动机,需的所有活前缀的有限自动机,需要介绍两个集合函数:要介绍两个集合函数:项集项集I的闭包的闭包CLOSURE(I)和状态转换函数和状态转换函数GO(I,X)文法文法G的的LR(0)项的集合称为项的集合称为LR(0)项集,简称项集,简称项集项集。编译原理与技术编译原理与技术3485.3.2LR(0)分析表的构造分析表的构造项集项集I的闭包的闭包CLOSURE(I)是一个项集,计算如下是一个项集,计算如下(1)I中任何项也属于中任何项也属于CLOSURE(I)

328、;(2)若)若AB属于属于CLOSURE(I)并且并且B是是G的一个产生式,的一个产生式,项项B也加到也加到CLOSURE(I)中。中。(3)反复使用上述两条规则,直到)反复使用上述两条规则,直到CLOSURE(I)不再变化为止。不再变化为止。直观上,直观上,CLOSURE(I)中的项中的项AB是指:在分析过程的某一是指:在分析过程的某一时刻,希望看到可以从时刻,希望看到可以从B归约的符号;归约的符号;若若B也是产生式,那么在同一时刻,当然也希望看到可以从也是产生式,那么在同一时刻,当然也希望看到可以从归约的符号。因此,也把归约的符号。因此,也把B加到加到CLOSURE(I)中。中。编译原理与

329、技术编译原理与技术3495.3.2LR(0)分析表的构造分析表的构造例如,文法例如,文法G5.4中中I=SaAcBe的闭包的闭包即即CLOSURE(SaAcBe)。根据规则(根据规则(1):CLOSURE(SaAcBe)SaAcBe;根据规则(根据规则(2):检查所有以检查所有以A为左部的产生式,有为左部的产生式,有Ab和和AAb,则把项目则把项目Ab和和AAb加入到加入到CLOSURE(SaAcBe)中,得中,得CLOSURE(SaAcBe)SaAcBe,Ab,AAb。编译原理与技术编译原理与技术3505.3.2LR(0)分析表的构造分析表的构造状态转换函数状态转换函数GO(I,X)的第一个

330、参数的第一个参数I是一个是一个LR(0)项集,第二个参数项集,第二个参数X是后继符号,它的是后继符号,它的计算公式为计算公式为GO(I,X)=CLOSURE(所有形如所有形如AX的项的项|项项AX I)直观上,若直观上,若I是某个活前缀是某个活前缀有效的项集,那么有效的项集,那么GO(I,X)就是活前缀就是活前缀X有效的项集。有效的项集。例如,对于例例如,对于例5.9,如果,如果I=SaAcBe,则,则GO(I,a)=SaAcBe,Ab,AAb。编译原理与技术编译原理与技术3515.3.2LR(0)分析表的构造分析表的构造若文法有若干个以开始符号若文法有若干个以开始符号S在左部的产生式,在左部

331、的产生式,则初始项就不唯一。为此,引进增广文法则初始项就不唯一。为此,引进增广文法GS,即增添一个,即增添一个G中没有出现过的非终结中没有出现过的非终结符符S和一个新的产生式和一个新的产生式SS,从而使得,从而使得GS只有一个初始项只有一个初始项SS 。如果把一个文法如果把一个文法G的的LR(0)项集规范族的每个项集规范族的每个项集当作一个状态,把项集当作一个状态,把GO函数看作是状态之函数看作是状态之间的转换函数,这样构成了可以识别文法间的转换函数,这样构成了可以识别文法G中中所有活前缀的有限状态机,它的初始状态就所有活前缀的有限状态机,它的初始状态就是包含是包含G中初始项的状态。中初始项的

332、状态。编译原理与技术编译原理与技术3525.3.2LR(0)分析表的构造分析表的构造识别文法识别文法G活前缀的活前缀的DFA的的LR(0)项集规范族项集规范族按照下列规则构造:按照下列规则构造:(1)令)令CCLOSURE(SS)(2)把)把C中每一个中每一个LR(0)项集项集I应用转换函数应用转换函数GO(I,X)得到新的项得到新的项集集J,并把,并把J加入到加入到C中;中;(3)重复步骤()重复步骤(2)直到)直到C不再增大为止。不再增大为止。这样就可以得到一个识别文法这样就可以得到一个识别文法G的活前缀的的活前缀的DFA:状态就是状态就是C中的每个中的每个LR(0)项集;项集;状态转换函

333、数就是状态转换函数就是GO;包含唯一初始项包含唯一初始项SS的的LR(0)项集就是初始状态。项集就是初始状态。编译原理与技术编译原理与技术3535.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术354例例5.10构造识别文法G5.5所有活前缀的DFA。SA|BAaAb|cBaBb|d首先把它改造成等价的增广文法GS(0)SS(1)SA(2)SB(3)AaAb(4)Ac(5)BaBb(6)BaBb其次,按照DFA的构造方法计算I0=CLOSURE(SS),得I0:SSSAAaAbAcSBBaBbBd5.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术35

334、5通过考察I0中每个项中圆点后面的第一个符号X可知:X=S,A,B,a,c,d利用GO(I0,X)可以求得I0的后继项集如下:I1=GO(I0,S)=CLOSURE(SS)=SSI2:SAI3:SBI4=GO(I0,a)=CLOSURE(AaAb,BaBb),计算可得I4:AaAbAaAbAcBaBbBaBbBdI5:AcI6:Bd例例5.10构造识别文法G5.5所有活前缀的DFA。5.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术356新的项集中只有I4有后继项集,而且X=A,B,a,c,d,于是I7=GO(I4,A)=CLOSURE(AaAb)=AaAbI8=GO(I

335、4,B)=CLOSURE(BaBb)=AaBb而GO(I4,a)=CLOSURE(AaAb,BaBb)=I4GO(I4,c)=CLOSURE(Ac)=I5GO(I4,d)=CLOSURE(Bd)=I6最后,求I7和I8的后继项集,分别为I9=GO(I7,b)=CLOSURE(AaAb)=AaAbI10=GO(I8,b)=CLOSURE(AaBb)=AaBb由于I9和I10没有后继项集,这样就结束了计算文法G5.5项集规范族CI0,I1.,I10,例例5.10构造识别文法G5.5所有活前缀的DFA。5.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术357若把其中的项集看作状

336、态,根据GO(I,X)就得到识别文法G5.5所有活前缀的DFA,如图5.5所示。其中每一个状态都是终态,由于I0包含了唯一的初始项,因而I0也是这个DFA的初态。把从初态I0出发,到达某一状态所经过的全部有向弧上的标记符号依次连接起来,就得到该DFA在到达该状态时所识别的某一个规范句型的一个活前缀。例如,状态5识别的活前缀是c,状态1识别的活前缀是S,状态9识别的活前缀是aa*A。bBcdcSaBAI0:SSSAAaAbAcSBBaBbBdI4:AaAbAaAbAcBaBbBaBbBdI1:SSI2:SAI3:SBI5:AcI6:BddI7:AaAbI8:BaBbI10:BaBbI9:AaAb

337、Aab5.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术358设LR(0)文法的项集规范族C=I0,I1.,Im,令任意一个项集Ik的下标为分析表的状态算法算法5.3A从识别G活前缀的DFA构造分析表输入:输入:G的DFA输出:输出:文法G的LR(0)分析表(1)fork=1tomdo(2)ifIk包含归约项Athen(3)ifA=Sand=SthenACTIONk,$=acc;/分析成功,接受输入串(4)elsefor每个aVTdoACTIONk,a=rj/按编号为j的产生式A归约(5)for从项集Ik发出的每条弧do/设弧上的标记为X,所连接的项集是Ij(6)ifXV

338、T(7)thenACTIONk,X=sj/表示把(k,a)分别移进状态栈和符号栈(8)elseGOTOk,X=j;/表示从状态k转移到状态j5.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术359算法5.3B:从文法G的项集规范族和状态转换函数构造LR(0)分析表:(1)若移进项Aa属于Ik并且GO(Ik,a)=Ij,a是终结符,则置ACTIONk,a=sj,表示把(k,a)分别移进状态栈和符号栈;(2)若归约项A属于Ik,对于任何终结符a(包括$),置ACTIONk,a=rj,表示按编号为j的产生式A进行归约;(3)若接受项SS属于Ik,则置ACTIONk,$=acc,

339、表示分析成功,接受输入串;(4)若GO(Ik,A)Ij,A是非终结符,则置GOTOk,A=j,表示从状态k转移到状态j。(5)凡不能用规则(1)至(4)填入信息的表格置“报错标志”,为了清晰起见通常用空白表示之。如此构造的分析表叫做LR(0)分析表分析表,使用LR(0)分析表的语法分析器叫做LR(0)分析器分析器。5.3.2LR(0)分析表的构造分析表的构造状态ACTIONGOTOabcd$SAB0s4s5s61231acc2r1r1r1r1r13r4r4r4r4r44s4s5s6785r3r3r3r3r36r6r6r6r6r67s98s109r2r2r2r2r210r5r5r5r5r5编译原

340、理与技术编译原理与技术360这两种方法完全等价。方法一从图形化的DFA构造分析表,比较直观,省去了在状态中寻找移进项;方法二无需画出DFA就可以构造分析表,直截了当,但是,需要寻找归约项,这实际上在构造DFA时已经完成。由于假定LR(0)文法规范族的每个项集不含冲突,因此,构造出分析表的每个表格不含多个动作,都有唯一的入口。表5.11是按照上述算法构造出的文法G5.5的LR(0)分析表。表5.11文法G5.5的LR(0)分析表5.3.2LR(0)分析表的构造分析表的构造u总结总结LR(0)分析表的构造步骤如下:分析表的构造步骤如下:(1)写出给定文法)写出给定文法G的的增广文法的的增广文法G;

341、(2)构造)构造G的的LR(0)项集,并根据项集,并根据CLOSURE和和GO函数构造函数构造G的项集规范族;的项集规范族;(3)构造识别)构造识别G的活前缀的的活前缀的DFA;(4)按照算法)按照算法5.3完成完成G的的LR(0)分析表。分析表。编译原理与技术编译原理与技术3615.3.2LR(0)分析表的构造分析表的构造u实际上,可以无需先构造文法实际上,可以无需先构造文法G G的项集规范族,再构造出识别其活的项集规范族,再构造出识别其活前缀的前缀的DFADFA,而可以从,而可以从CLOSURECLOSURE和和GOGO函数递增地直接构造出函数递增地直接构造出DFADFA。u如果把如果把D

342、FADFA看作一个有向图,那么,项集就可以看作该图的结点,看作一个有向图,那么,项集就可以看作该图的结点,有向边就是一个状态转移,即从状态有向边就是一个状态转移,即从状态I Ii i到经过文法的一个符号到经过文法的一个符号X X到达到达状态状态I Ij j,其中,其中,X X是状态是状态IiIi表示的项集中项的一个后继符号,而表示的项集中项的一个后继符号,而I Ij jGO(IGO(Ii i, X), X)。u只要有一个起始结点,就可以递增地通过只要有一个起始结点,就可以递增地通过GOGO与与CLOSURECLOSURE函数增加新函数增加新的状态(结点)和有向边,直到不再增加结点或边为止,这样

343、就完的状态(结点)和有向边,直到不再增加结点或边为止,这样就完成了成了DFADFA的构造。的构造。uDFADFA的这种构造方式可以依据图的深度优先搜索实现,也可以依据的这种构造方式可以依据图的深度优先搜索实现,也可以依据图的广度优先搜索来实现。识别规则的例图的广度优先搜索来实现。识别规则的例编译原理与技术编译原理与技术3625.3.2LR(0)分析表的构造分析表的构造编译原理与技术编译原理与技术363算法算法5.4识别文法活前缀的DFA的递增构造算法输入:输入:文法GS输出:输出:识别文法G活前缀的DFA声明:声明:V是G的LR(0)项集规范族,也是DFA的结点集合,是用数组实现的队列,函数e

344、xist(d,V)判断结点d是否存在,first和last分别指向V的下一个新结点和队尾;E是带标记的有向边集,每条边记做,其中I和J是结点,X是标记。构造文法GS的增广文法GS;first=last1;E=;V1=CLOSURE(SS);/计算出初始化状态;while(firstlast)doI=Vfirst+;/first的递增移动表示状态结点已经访问过forI中的每个项itemdoifitem的形式为AXthenJ=GO(I,X);/通过GO得到边,通过隐含的CLOSURE得到结点ifexist(J,V)thenV+last=J;E=E;/把边加入到Eendwhile;5.3.3SLR分

345、析表的构造分析表的构造我们以一个简单的变量声明作为例子来分析冲我们以一个简单的变量声明作为例子来分析冲突问题和解决方法。设整型变量的声明的文突问题和解决方法。设整型变量的声明的文法法G5.6为为integer,v|v将它缩写后并增广如下:将它缩写后并增广如下:(0)DD(1)DiL(2)LL,v(3)Lv编译原理与技术编译原理与技术3645.3.3SLR分析表的构造分析表的构造编译原理与技术编译原理与技术365I5:LL,v,viI0:DDDiLI1:DDI3:DiLLL,vI4:LvI2:DiLLL,vLvDLvI6:LL,v从I0=CLOSURE(RR)出发构造DFA,结果如下图。分析状态

346、I3可以发现,它包含了归约项DiL和移进项LL,v。依据LR(0)的分析方式,在该状态,不论面临什么输入符号,特别是包括符号逗号“,”都要把iL规约成D;但是,按照移进项LL,v则在面临符号“,”时,应该把它移入符号栈,同时转到状态5。这样,对于ACTION3,就会有r1和s5两个动作,出现移进归约冲突,控制程序将不知所措。5.3.3SLR分析表的构造分析表的构造对冲突情况有两种解决途径:修改文法,或者对分析方法加以对冲突情况有两种解决途径:修改文法,或者对分析方法加以改进。显然,修改文法改进。显然,修改文法G5.6以适应以适应LR(0)分析并不可取,更好的分析并不可取,更好的措施是改善分析技

347、术,以适应更广泛、实用的文法。措施是改善分析技术,以适应更广泛、实用的文法。在这种情况下,我们只需考察把句柄在这种情况下,我们只需考察把句柄iL归约成归约成D时,时,D之后的跟之后的跟随符号集是否包含当前所有移进项中移进符号(随符号集是否包含当前所有移进项中移进符号(“,”),如),如果不包含这样就可以解决这种移进归约冲突。果不包含这样就可以解决这种移进归约冲突。例子中例子中FOLLOW(D)=$,而状态,而状态3中移进符号的集合是中移进符号的集合是,,所,所以,在面临输入符号是以,在面临输入符号是“$”时,就执行归约,即时,就执行归约,即ACTION3,$=r1,在面临输入符号是,在面临输入

348、符号是“,”时,就执行移进操时,就执行移进操作,即作,即ACTION3,=s5。这种处理冲突的方法只需要对。这种处理冲突的方法只需要对LR(0)分析表简单地进行改动,称作简单分析表简单地进行改动,称作简单LR(0)分析,分析,SLR(1)。编译原理与技术编译原理与技术3665.3.3SLR分析表的构造分析表的构造一般,设一般,设LR(0)LR(0)项集规范族的某个项集项集规范族的某个项集I I含有含有m m个移进项个移进项A A1 1 a a1 11 1.A Am m a am mm m和和n n个归约项个归约项B B1 1 1 1.B Bn n n n若集合若集合aa1 1,., a,.,

349、am m 和和FOLLOW(BFOLLOW(B1 1),., FOLLOW(B),., FOLLOW(Bn n) )两两不相交,那么,两两不相交,那么,隐含在项集隐含在项集I I中冲突可以根据当前输入符号中冲突可以根据当前输入符号a a唯一地确定动作,解决冲突:唯一地确定动作,解决冲突:(1 1)若)若a a aa1 1,., a,., ai i ,则移进;,则移进;(2 2)若)若a a FOLLOW(B FOLLOW(Bi i) ),i=1,2,.,mi=1,2,.,m,则用,则用B Bi i i i进行归约;进行归约;(3 3)否则,报错。)否则,报错。编译原理与技术编译原理与技术367

350、5.3.3SLR分析表的构造分析表的构造如果一个文法项集中的所有冲突性动作的都可以按这如果一个文法项集中的所有冲突性动作的都可以按这种方法解决,这样的文法称作种方法解决,这样的文法称作SLR(1)文法,由此构造文法,由此构造的分析表叫做的分析表叫做SLR(1)分析表,使用分析表,使用SLR(1)分析表的分析表的分析器叫做分析器叫做SLR(1)分析器。分析器。SLR(1)分析表的构造步骤和分析表的构造步骤和LR(0)分析表的构造类似,分析表的构造类似,只须在完成只须在完成DFA以后按照以后按照SLR(1)方法解决可能的冲突。方法解决可能的冲突。类似地,完成类似地,完成SLR(1)分析表的算法也有

351、两种,而且只分析表的算法也有两种,而且只需对算法需对算法5.3A和和5.3B做简单修改即可做简单修改即可。编译原理与技术编译原理与技术3685.3.3SLR分析表的构造分析表的构造从从DFA构造构造SLR(1)分析表的算法分析表的算法5.5A:(1)修改算法修改算法5.3A第(第(4)句中)句中for每个每个a VTdoACTIONk,a=rj为为for每个每个a FOLLOW(A)doACTIONk,a=rj(2)其它不变。其它不变。从文法从文法G的项集规范族和状态转换函数构造的项集规范族和状态转换函数构造SLR(1)分分析表的算法析表的算法5.5B:(1)把算法把算法5.3B第(第(2)条

352、规则)条规则“对于任何终结符对于任何终结符a(包(包括括$),置),置ACTIONk,a=rj”修改为修改为“对任何属于对任何属于FOLLOW(A)的的a,置,置ACTIONk,a=rj”(2)其它不变。其它不变。编译原理与技术编译原理与技术3695.3.3SLR分析表的构造分析表的构造状态ACTIONGOTOi,v$DL0s211acc2s433s5r14r3r35s66r2r2编译原理与技术编译原理与技术370按照上述算法构造出文法5.6的SLR(1)分析表如表5.12。其中FOLLOW(D)=FOLLOW(D)=$,FOLLOW(L)=,,$。表表5.12文法文法G5.6的的SLR(1)

353、分析表)分析表5.3.3SLR分析表的构造分析表的构造编译原理与技术编译原理与技术371例例5.12构造G5.1的SLR(1)分析表EE+T|TTT*F|FF(E)|i首先把它改造为等价的增广文法G5.1E(0)EE(1)EE+T(2)ET(3)TT*F(4)TF(5)F(E)(6)FI构造初始项集I0CLOSURE(EE),结果是EE,EE+T,ET,TT*F,TF,F(E),Fi,以此为基础按照算法5.4,递增地构造出识别G5.1E活前缀的DFA:在每个状态Ik,对每个移进项和待归项AX,以圆点后的符号X为标记画出转移弧线,连接GO(Ik,X),直到完成所有状态Ik的全部转移弧线。例如,计

354、算I0中每个项目的圆点后续符号集,结果是E,T,F,(,i,画出标记为E转移弧线,另外一端项集是GO(I0,E)=CLOSURE(EE,EE+T)=EE,EE+T,结果如图5.7(a)。按广度优先的顺序最终构成的识别该文法活前缀的DFA如图5.7(b)。5.3.3SLR分析表的构造分析表的构造编译原理与技术编译原理与技术372(T(FiEI0:EEEE+TETTT*FTFF(E)FiI1:EEEE+TTI2:ETTT*FFI3:TF+I6:EE+TTT*FTFF(E)FiI5:FiI4:F(E)EE+TETTT*FTFF(E)Fi(FI7:TT*FF(E)FiiiT*I10:TT*FFI8:F

355、(E)EE+T+EI9:FEE+TTT*FI8:F(E)*i图5.7(b)识别文法G5.1活前缀的DFA在状态在状态I1,I2和和I8中存在移进归约冲突,中存在移进归约冲突,因而文法因而文法G5.1不是不是LR(0)文法。分别考察这文法。分别考察这三个状态,看是否能使用三个状态,看是否能使用SLR(1)的方法解的方法解决冲突。决冲突。对于状态对于状态1:包含移进项:包含移进项EE+T和归约项和归约项EE,由于,由于FOLLOW(E)=$不包含移进不包含移进符号符号“+”,所以可以解决冲突,置符号表,所以可以解决冲突,置符号表的的ACTION1,$=acc,ACTION1,+=s6。对于状态对于

356、状态2:包含移进项:包含移进项TT*F和归约项和归约项ET,由于,由于FOLLOW(E)=+,),$不包含不包含移进符号移进符号“*”,可以解决冲突,置符号表,可以解决冲突,置符号表ACTION2,+=ACTION2,)=ACTION2,$=r2,ACTION2,*=s7。对于状态对于状态9:包含移进项:包含移进项TT*F和归约项和归约项EE+T情况和状态情况和状态2的类似,解决方法也的类似,解决方法也类似。类似。5.3.3SLR分析表的构造分析表的构造状态ACTIONGOTOi+*()$ETF0s5s41231s6acc2r2s7r2r23r4r4r4r44s5s48235r6r6r6r66

357、s5s4937s5s4108s6s119r1s7r1r110r3r3r3r311r5r5r5r5编译原理与技术编译原理与技术373以上对包含的冲突都可以解决,因此该文法是SLR(1)文法,构造的SLR(1)分析表如表5.13步骤状态栈符号栈输入串ACTIONGOTO10$(i+i)*i$s4204$(i+i)*i$s53045$(i+i)*i$r634043$(F+i)*i$r425042$(T+i)*i$r286048$(E+i)*i$s670486$(E+i)*i$s5804865$(E+i)*i$r63904863$(E+F)*i$r491004869$(E+T)*i$r1811048$

358、(E)*i$s111204811$(E)*i$r531303$F*i$r421402$T*i$s715027$T*i$s5160275$T*i$r631702710$T*F$r311802$T$r211901$E$acc编译原理与技术编译原理与技术3745.3.3SLR分析表的构造分析表的构造编译原理与技术编译原理与技术375尽管用SLR(1)方法可以通过预测一个输入符号来解决某些LR(0)项集规范族中存在的动作冲突,但是,仍然存在许多文法构造的LR(0)项集规范族的动作冲突在不能用SLR(1)方法解决。例如,考虑下面普通编程语言(如C、Pascal)中语句的文法|identifier:=|i

359、dentifier|number这个文法描述的是赋值语句和无参数的过程调用,它们都是以标识符开始。编译需要看到赋值号“:=”或者语句结束才能决定这条语句是赋值句或调用句。5.3.3SLR分析表的构造分析表的构造编译原理与技术编译原理与技术376将它简化、缩写成文法G5.7:Sid|V:=EVidEV|n为了说明用SLR(0)解决文法G5.7的分析处突,把该文法改造如下:(0)SS(1)Sid(2)SV:=E(3)Vid(4)EV(5)En考虑识别文法G5.7活前缀的DFA的初始状态:SSSidSV:=EVid输入符号是“$”,决不能按Vid归约,因为一个变量决不会出现在一个语句的末尾,除非已经

360、看到赋值号“:=”会移入栈内。这个状态经过id以后转移到状态SidVid由于FOLLOW(S)=$,FOLLOW(V)=:=,$,所以,在面临输入符号“$”时SLR(1)方法将按照两个产生式Sid和Vid归约,导致归约归约冲突。实际上,在这个状态下,如果输入符号是“$”,决不能按Vid归约,因为一个变量决不会出现在一个语句的末尾,除非已经看到赋值号“:=”会移入栈内。下面两节将介绍更强的下面两节将介绍更强的LR分析方法,通分析方法,通过超前搜索输入符号来解决这类问题。过超前搜索输入符号来解决这类问题。5.3.4规范规范LR分析表的构造分析表的构造SLR(1)分析方法的问题是在构造完分析方法的问

361、题是在构造完DFA的的LR(0)项集以后才应项集以后才应用预测符号,而用预测符号,而LR(0)本身的构造没有考虑本身的构造没有考虑预测预测。LR(1)方法强大的识别能力在于从构造方法强大的识别能力在于从构造DFA的开始就用预测符,的开始就用预测符,让每个状态含有更多的让每个状态含有更多的“未来未来”信息,以便解决上面提到的归信息,以便解决上面提到的归约归约冲突。对于含有归约约归约冲突。对于含有归约A的状态,我们可以把它细的状态,我们可以把它细化,分割成若干个状态,使得化,分割成若干个状态,使得LR分析器的每个状态都能确切分析器的每个状态都能确切的指出,在的指出,在后跟哪些终结符才允许把后跟哪些

362、终结符才允许把归约成归约成A。为此,需要定义新的项,使得每个项都附带有为此,需要定义新的项,使得每个项都附带有k个终结符,代个终结符,代表预测符号,我们只讨论预测一个符号,即表预测符号,我们只讨论预测一个符号,即k=1的情况,它已的情况,它已经可以满足大多数的计算机程序语言。经可以满足大多数的计算机程序语言。编译原理与技术编译原理与技术3775.3.4规范规范LR分析表的构造分析表的构造LR(1)项由两部分组成:项由两部分组成:LR(0)和一个预测符和一个预测符号。因此,对于一个文法号。因此,对于一个文法G,构造有效的,构造有效的LR(1)项集规范族方法与构造项集规范族方法与构造LR(0)项集

363、规范项集规范族的方法类似,我们讨论主要的不同之处。族的方法类似,我们讨论主要的不同之处。首先,文法首先,文法GS的的LR(1)的初始项是的初始项是SS,$,S是是增广文法增广文法G的开始符号。的开始符号。编译原理与技术编译原理与技术378定义定义5.13:如果:如果P是文法是文法G的一个规则,其中的一个规则,其中或或可为空串,则称可为空串,则称P,a是文法是文法G的一个的一个LR(1)项,其中项,其中a是终结符,表示预测。是终结符,表示预测。5.3.4规范规范LR分析表的构造分析表的构造其次,其次,LR(1)项集项集I的闭包的闭包CLOSURE(I)的计算规则如下的计算规则如下(1)I中的任何

364、项也属于中的任何项也属于CLOSURE(I);(2)若)若AB,a CLOSURE(I),B是文法是文法G的的一个产生式,那么,对于每个一个产生式,那么,对于每个b FIRST(a),项,项B,b也加到也加到CLOSURE(I)中。中。(3)反复使用上述两条规则,直到)反复使用上述两条规则,直到CLOSURE(I)不再不再变化为止。变化为止。LR(1)项集的闭包运算和项集的闭包运算和LR(0)项集闭包运算的差别在规项集闭包运算的差别在规则(则(2)。)。编译原理与技术编译原理与技术3795.3.4规范规范LR分析表的构造分析表的构造项项AB,a的含义是:分析器此时想要识别的含义是:分析器此时想

365、要识别B,条,条件是件是B的后面是可以从的后面是可以从a推导出的符号串,而且以开推导出的符号串,而且以开始的符号必须在始的符号必须在FIRST(a)中。中。既然符号串既然符号串在产生式在产生式AB中的中的B之后,如果之后,如果a FOLLOW(A),那么,那么FIRST(a) FOLLOW(B),并且项并且项B,b也总是在也总是在FOLLOW(B)中。中。规范规范LR(1)方法的强大之处就在于集合方法的强大之处就在于集合FIRST(a)可可能是能是FOLLOW(B)的真子集,而的真子集,而SLR(1)分析器的核心分析器的核心就是就是从就是就是从FOLLOW取出了预测符取出了预测符b,因而不如,

366、因而不如LR(1)分析器做出更细致地解决冲突。分析器做出更细致地解决冲突。编译原理与技术编译原理与技术3805.3.4规范规范LR分析表的构造分析表的构造同时规则(同时规则(2)也说明了决定预测符号得简单规则:)也说明了决定预测符号得简单规则:已经存在项的预测符集不需要改变,此时已经存在项的预测符集不需要改变,此时* ;仅;仅当建立一个新的项时,才必须决定一个新的预测符集。当建立一个新的项时,才必须决定一个新的预测符集。转移函数转移函数GO的计算也由于的计算也由于LR(1)而有差别,它的计算而有差别,它的计算公式为公式为GO(I,X)=CLOSURE(所有形如所有形如AX,a的项的项|AX,a

367、 I)最后,应用修改的最后,应用修改的CLOSURE和和GO函数,就可以用函数,就可以用5.3.2.2的方法构造文法的方法构造文法G的的LR(1)项集规范族。项集规范族。编译原理与技术编译原理与技术3815.3.4规范规范LR分析表的构造分析表的构造编译原理与技术编译原理与技术382例例5.13文法G5.8的LR(1)项集规范族和GO函数的构造结果如图5.8所示。idVn:=EVidI0:SS,$Sid,$SV:=E,$Vid,$SI1:SS,$I2:Sid,$Vid,:=I3:SV:=E,$I4:SV:=E,$EV,$En,$Vid,$I5:SV:=E,$I6:EV,$I7:En,$I8:V

368、id,$初始项集是I0CLOSURE(SS,$)=SS,$,Sid,$,SV:=E,$,Vid,$考虑状态2:它包含两个归约项Sid,$和Vid,:=,用SLR(1)方法不能解决这个归约归约冲突归约归约冲突。但是LR(1)项却可以根据它们的预测符区分两个归约:当未来是“$”时就按照Sid归约,如果未来是“:=”时就按照Vid归约。所以,文法G5.8是LR(1)文法。5.3.4规范规范LR分析表的构造分析表的构造编译原理与技术编译原理与技术383类似于类似于5.3.2.2节介绍的节介绍的LR(0)分析表的构造,有两种方法构造分析表的构造,有两种方法构造LR(1)分析分析表,下面是其中的一个算法。

369、表,下面是其中的一个算法。算法5.6B:从文法G的项集规范族和状态转换函数构造LR(1)分析表:(1)若移进项Aa,b属于Ik并且GO(Ik,a)=Ij,b是终结符,则置ACTIONk,a=sj,表示把(k,a)分别移进状态栈和符号栈;(2)若归约项A,a属于Ik,对于任何终结符a(包括$),置ACTIONk,a=rj,表示按编号为j的产生式A进行归约;(3)若接受项SS,$属于Ik,则置ACTIONk,$=acc,表示分析成功,接受输入串;(4)若GO(Ik,A)Ij,A是非终结符,则置GOTOk,A=j,表示从状态k转移到状态j。(5)凡不能用规则(1)至(4)填入信息的表格置“报错标志”

370、,为了清晰起见通常用空白表示之。5.3.4规范规范LR分析表的构造分析表的构造状状态ACTIONGOTOid:=n$SVE0s2131acc2r3r13s44s8s7655r26r47r58r3编译原理与技术编译原理与技术384如此构造的分析表叫做如此构造的分析表叫做LR(1)分析表,使用分析表,使用LR(1)分析表的语法分析分析表的语法分析器叫做器叫做LR(1)分析器。分析器。表表5.13就是按照图就是按照图5.8直接构造的直接构造的LR(1)分析表。分析表。5.3.4规范规范LR分析表的构造分析表的构造编译原理与技术编译原理与技术385例例5.13我们用文法G5.8说明LR(1)强大的表达

371、能力和细致的分析,同时也说明构造LR(1)分析表的复杂程度。这个文法产生的语言是ub,anubn|n0。SA|ubAaAb|BBu首先,证明这个文法不是首先,证明这个文法不是LL(1)文法:由于文法:由于u在在FIRST(B)中,所以它也在中,所以它也在FIRST(A)中,这就导致中,这就导致FIRST(A) FIRSTub=u不为空,在不为空,在S上对于符号上对于符号u就出现了选择冲突,即不能唯一地确定就出现了选择冲突,即不能唯一地确定S的候选式。的候选式。其次,说明它也不是SLR(1)文法。从初始状态0的项集CLOSURE(SS)=SS,SA,Sub,AaAb,AB,Bu出发,构造的SLR

372、(1)的DFA如图5.9。状态3的项集是Sub,Bu,同时包含了移进项和归约项。计算归约符B的随集,得FOLLOW(B)=FOLLOW(A)=bFOLLOW(S)=b,$,而移进项Sub的后继符号是b,这样,按SLR(1)的方法就不能解决这个状态所包含的移进归约冲突。5.3.4规范规范LR分析表的构造分析表的构造编译原理与技术编译原理与技术386aBauI0:SSSASubAaAbABBuSI1:SSI3:SubBuI4:AaAbAaAbABBuI5:ABBuI8:AubI6:SubAI7:AaAbbI9:AaAbAI2:SA5.3.4规范规范LR分析表的构造分析表的构造最后,看它是否是LR(

373、1)文法。按照LR(1)方法构造的LR(1)自动机如图5.10。初始项集只包含了一个预测符$,在I3的项集中第一次出现了不同的预测符b,在那里关的第一个项中关于A的预测规则产生了AaAb,b和AB,b,它们都有一个预测符b,这是由于FIRST(b$)=b。可以看到在状态3中的处突不存在了:移进项在输入为b时起作用,当面临$时才执行归约。编译原理与技术编译原理与技术387uBauI0:SS,$SA,$Sub,$AaAb,$AB,$Bu,$SI1:SS,$I3:Sub,$Bu,$I4:AaAb,$AaAb,bAB,bBu,bI5:AB,$BI8:AB,bbI6:Sub,$AI7:AaAb,$bI9

374、:AaAb,$aaI11:AaAb,bAaAb,bAB,bBu,bBuI10:Bu,bAI12:AaAb,bbI13:AaAb,bAI2:SA,$5.3.4规范规范LR分析表的构造分析表的构造状态ACTIONGOTOuab$SAB0s3s41251acc2r13s64s10s11785r16r27s98r19r310r511s10s1112812s1313r3编译原理与技术编译原理与技术388表5.15文法G5.8的LR(1)分析表5.3.4规范规范LR分析表的构造分析表的构造可以看到可以看到LR(1)的的DFA比比SLR(1)的的DFA具备更强的识别能力。实际上,具备更强的识别能力。实际上,

375、如果我们可以在线性时间用一个预测符号自左至右分析任何一个语言,如果我们可以在线性时间用一个预测符号自左至右分析任何一个语言,那么,我们也能用那么,我们也能用LR(1)分析。也就是说,分析。也就是说,LR(1)时最强的自左至右的时最强的自左至右的新型分析方法。这是因为新型分析方法。这是因为LR项集对于句柄实现了最佳可能的宽度优先项集对于句柄实现了最佳可能的宽度优先查找。查找。然而,然而,LR(1)技术的更强分析能力也不是没有代价:本例子中技术的更强分析能力也不是没有代价:本例子中LR(1)的的状态数比状态数比SLR(1)的要多仅的要多仅30。一般来说,一般来说,LR(1)的状态及其分析表比的状态

376、及其分析表比SLR(1)的都要大到一个数量级。的都要大到一个数量级。当一个程序语言平均压缩当一个程序语言平均压缩SLR(1)的的DFA需要数十个需要数十个KB的存储空间,的存储空间,而而LR(1)的的DFA则可能会需要若干则可能会需要若干MB的存储容量,而构造的存储容量,而构造LR(1)表可表可能还会需要十倍于这个数量的存储空间。这对于现代的计算机而言不能还会需要十倍于这个数量的存储空间。这对于现代的计算机而言不是一个大问题,但是,传统的编译程序设计者不能也不允许为了提高是一个大问题,但是,传统的编译程序设计者不能也不允许为了提高分析能力而占用那么大的存储空间,同时,人们也在寻找消弱分析能力而

377、占用那么大的存储空间,同时,人们也在寻找消弱LR(1)存储量的途径。存储量的途径。这就导致了这就导致了LALR(1)的分析方法。的分析方法。编译原理与技术编译原理与技术3895.3.5LALR分析表的构造分析表的构造编译原理与技术编译原理与技术390仔细观察图5.10所示的DFA时可以发现,有些项集和另外的项集十分相似。例如,I5和I8,如果忽略了预测符好,它们就是相等的;同样的情况还有I7和I12,I9和I13,以及I4和I11。LR(1)项集中没有预测符号的部分称为LR(1)项集的核心核心。例如,项集I3的核心是Sub和Bu。所有LR(1)的核心都与LR(0)状态相对应。形成这种情况的原因

378、是,核心的内容仅仅是由其它状态经过所允许的移动所决定的。这些移动由GO函数决定,不受预测符号的影响。所以,如果不考虑预测符号的话,给定一个LR(1)状态,其核心就是一个LR(0)状态。一个LR(1)状态实际上是LR(0)状态经过分裂所得到的版本,而这正是LR(1)方法比LR(0)和SLR(1)分析更强的根源。但是,并非每个状态都需要这种分裂。例如,合并具有相同核心的I7和I12将形成一个新的状态I712,它只有一个项AaAb,b/$,其中b/$表示合并原来的预测符号。用新的状态I712取代I7和I12丝毫没有改变这个DFA的能力,但是却减少了DFA中的状态数目。5.3.5LALR分析表的构造分

379、析表的构造u问题:把问题:把LR(0)状态按照预测符进行分裂状态按照预测符进行分裂得到的具有同心集的得到的具有同心集的LR(1)状态还能够再状态还能够再合并吗?合并吗?由于并非每个由于并非每个LR(0)状态都需要这种分裂,所状态都需要这种分裂,所以合并以合并LR(1)是有条件的,即合并后不能产生是有条件的,即合并后不能产生新的分析动作冲突。新的分析动作冲突。如果如果LR(1)的同心集合并以后不产生新的处突,的同心集合并以后不产生新的处突,这样得到的项集称为这样得到的项集称为LALR(1)项集项集。编译原理与技术编译原理与技术3915.3.5LALR分析表的构造分析表的构造编译原理与技术编译原理

380、与技术392算法算法5.7 LALR分析表构造算法输入:输入:文法G输出:输出:G不是LALR文法,或者G的LALR分析表(1)构造LR(1)项集规范族C=I0,I1,.,In,如果其中包含处突项集,返回“G不是LR(1)文法”。(2)合并C中的所有同心集得,到C=J0,J1,.,Jm,如果其中包含处突项集,返回“G不是LALR文法”。(3)从C构造分析表ACTION部分的方法同5.3.4节的算法3.6B。(4)GOTO部分的构造:假设Jk是从Ii,Ii+1,.,Ij合并后得到的新项集。由于Ii,Ii+1,.,Ij同心,所以,GO(Ii,X),GO(Ii+1,X),.,GO(Ij,X)也同心,

381、令Jp是所有这些GO合并后的项集。那么,就有GO(Jk,X)Jp。于是,可以填写GOTOk,X=p。(5)凡是不能用规则(3)和(4)填入信息的空格都添上“出错标志”。5.3.5LALR分析表的构造分析表的构造编译原理与技术编译原理与技术393例例5.14试构造例5.13中文法G5.8的LALR分析表。例子5.13已经说明G5.8是LR(1)文法,现在执行步骤(2),合并同心集:I411:AaAb,b/$,AaAb,b,AB,b,Bu,bI58:AB,b/$I712:AaAb,b/$I913:AaAb,b/$其中只有I411含有两个归约项AaAb,b和AB,b,但是它们不会产生分析动作的冲突,

382、因此文法G5.8是LALR(1)文法,它的项集规范族如表5.16的列所示。现在来看如何计算转换函数GO。首先考虑GO(I411,A):在合并前,GO(I4,A)I7,GO(I11,A)I12,而I7和I12合并为I712,所以得出GO(I411,A)I712;再看GO(I411,a):在合并前,GO(I4,a)I11,GO(I11,a)I11,原来的I11在合并后成为I411的一部分,所以GO(I411,a)I411。在分析表的ACTIONI411,a为“s4-11”,表示把符号a和状态s4-11分别移进栈。最后的结果如表5.16,包含的状态数目和SLR(1)的相等,都是10,比LR(1)少了

383、4个。5.3.5LALR分析表的构造分析表的构造状状态ACTIONGOTOuab$SAB0s3s4-111251acc2r13s64-11s10s4-117-125-85-8r1r16r27-12s9-139-13r3r310r5编译原理与技术编译原理与技术394表表5.16文法文法G5.8的的LALR(1)分析表分析表5.3.5LALR分析表的构造分析表的构造同心集的合并可看作是有向图同心集的合并可看作是有向图DFA中结点的合并,相中结点的合并,相应的有向边也合并,即完成了应的有向边也合并,即完成了GO函数的计算。函数的计算。图图5.11是从图是从图5.10的的DFA采用图合并的方法得到的采

384、用图合并的方法得到的LALR(1)的的DFA。编译原理与技术编译原理与技术395BaI0:SS,$SA,$Sub,$AaAb,$AB,$Bu,$SI1:SS,$I3:Sub,$Bu,$I4-11:AaAb,b/$AaAb,bAB,bBu,bI5-8:AB,b/$uI10:Bu,bbI6:Sub,$AI7-12:AaAb,b/$bI9-13:AaAb,b/$aAI2:SA,$Bu图5.11文法G5.8的LALR(1)的DFA5.3.6LR分析方法小结分析方法小结LR分析是目前应用最广泛的语法分析技术,具有下列显著的优点:分析是目前应用最广泛的语法分析技术,具有下列显著的优点:(1 1)能够构造出

385、)能够构造出LRLR分析器来识别所有能用上下文无关文法描述的语言;分析器来识别所有能用上下文无关文法描述的语言;(2 2)LRLR方法是已知的最一般的无回溯的移进归约技术,而且能和其它移进方法是已知的最一般的无回溯的移进归约技术,而且能和其它移进归约方法一样有效地实现,这要归功于上世纪归约方法一样有效地实现,这要归功于上世纪7070年代开发的年代开发的LALRLALR文法自文法自动生成器动生成器yaccyacc。(3 3)LRLR方法能分析的文法类是预测分析方法的文法类的真超集。对于方法能分析的文法类是预测分析方法的文法类的真超集。对于LR(k)LR(k)文文法,分析程序只要求在看见了产生式右

386、部推出的所有符号以及从输入串中法,分析程序只要求在看见了产生式右部推出的所有符号以及从输入串中预测预测k k各符号后,就能够识别产生式右部的出现。这个要求比各符号后,就能够识别产生式右部的出现。这个要求比LL(k)LL(k)的要弱,的要弱,LL(k)LL(k)方法要求看见了右部推出的前方法要求看见了右部推出的前k k各符号后就识别所使用的产生式。所各符号后就识别所使用的产生式。所以,以,LRLR文法比文法比LLLL文法能够描述、识别更多的语言。文法能够描述、识别更多的语言。(4 4)LRLR分析器能够计时发现语法错误,在自左向右扫描输入的条件下,它快分析器能够计时发现语法错误,在自左向右扫描输

387、入的条件下,它快到几乎不能再快的程度。到几乎不能再快的程度。编译原理与技术编译原理与技术3965.3.6LR分析方法小结分析方法小结最后,给出与最后,给出与LR分析有关的一些结论:分析有关的一些结论:(1)LR(k)文法是无二义性的,而且满足文法是无二义性的,而且满足LR(0) SLR(1) LALR(1) LR(1);(2)对于所有自然数)对于所有自然数k,都有,都有LR(k) LR(k+1);(3)给定文法)给定文法G和特定的自然数和特定的自然数k,G是否是是否是LR(k)文法的问题是可判定的;文法的问题是可判定的;(4)给定文法)给定文法G,是否存在一个自然数,是否存在一个自然数k使得使

388、得G是是一个一个LR(k)文法的问题是不可判定的。文法的问题是不可判定的。编译原理与技术编译原理与技术3975.3.6二义文法的应用二义文法的应用任何二义性文法既不是任何二义性文法既不是LR类文法,也不是算符优先类文法,也不是算符优先文法或者文法或者LL类文法,也就不存在相应的确定的语法类文法,也就不存在相应的确定的语法分析器分析器。很多二义性文法由于表达简单、自然和清晰,在说明很多二义性文法由于表达简单、自然和清晰,在说明和实现程序语言中非常有用,如果把它们改造成非二和实现程序语言中非常有用,如果把它们改造成非二义性文法,就可以应用义性文法,就可以应用LR等分析技术。等分析技术。但是,文法改

389、造增加了不少工作量;另一方面,改造但是,文法改造增加了不少工作量;另一方面,改造后的文法可能会面目全非,不容易理解和实现。后的文法可能会面目全非,不容易理解和实现。实际上,对于有些二义性文法,只要增加足够的无二实际上,对于有些二义性文法,只要增加足够的无二义性规则,即消除动作冲突的规则,就可以构造出义性规则,即消除动作冲突的规则,就可以构造出LR分析器,而这些冲突一般不能由前述的方法(如分析器,而这些冲突一般不能由前述的方法(如预测更多的符号)解决。预测更多的符号)解决。编译原理与技术编译原理与技术3985.3.6二义文法的应用二义文法的应用u下面是三个常见、但非常有效的无二义性下面是三个常见

390、、但非常有效的无二义性规则:规则:(1)遇到)遇到移进归约移进归约冲突时,采取移进动作,冲突时,采取移进动作,这实际上就是按照最长匹配的原则,即给较这实际上就是按照最长匹配的原则,即给较长的产生式以较高的优先权;长的产生式以较高的优先权;(2)遇到)遇到规约归约规约归约冲突时,优先使用列在前冲突时,优先使用列在前面的产生式进行归约,即列在文法中前面的面的产生式进行归约,即列在文法中前面的产生式具有较高的优先权。产生式具有较高的优先权。(3)根据不同的数学运算符号,采用相应的作)根据不同的数学运算符号,采用相应的作结合或右结合运算规律,例如算术运算通常结合或右结合运算规律,例如算术运算通常符合左

391、结合律,幂运算服从右结合律。符合左结合律,幂运算服从右结合律。编译原理与技术编译原理与技术3995.3.6二义文法的应用二义文法的应用编译原理与技术编译原理与技术400例例5.15首先来看我们熟悉的算术表达式文法G5.3。(1)EE+E(2)EE*E(3)E(E)(4)Ei文法G5.1是它的一个非二义性文法EE+T|TTT*F|FF(E)|i它实际上就是对算符和*赋予了优先级和左结合规则,解决了文法G5.3的二义性,因而成为SLR(1)文法(详见例子5.11)。文法G5.3与G5.1相比有两个优点:若需要改变算符的优先级或结合规则,文法G5.1无需改变自身;文法G5.3的分析表所包含的状态肯定

392、比文法G5.1所包含的状态要少得多。因为后者包含了两个单非产生式,这些本在定义算符优先级和结合规则的产生式要占用不少的资源。5.3.6二义文法的应用二义文法的应用I0:EEEE+EEE*EE(E)|iEiI1:EEEE+EEE*EI2:E(E)|iEE+EEE*EE(E)|iEiI3:EiI4:EE+EEE+EEE*EE(E)|iEiI5:EE*EEE+EEE*EE(E)|iEiI6:E(E)EE+EEE*EI7:EE+EEE+EEE*EI8:EE*EEE+EEE*EI9:E(E)编译原理与技术编译原理与技术401可能引起移进归约冲突的状态有I1、I7和I8,计算得到FOLLOW(E)=+,*

393、,$,FOLLOW(E)=$。这样,状态I1可以运用SLR(1)方法解决冲突:当面临输入符号是$的时候,“接受”是唯一的动作;在面临+和*符号时,采取“移进”动作。5.3.6二义文法的应用二义文法的应用编译原理与技术编译原理与技术402状态I7在面临+和*符号时就不能用SLR(1)方法解决冲突,因为这两个符号都在FOLLOW(E)中。类似地,状态I8在面临+和*符号时就不能用SLR(1)方法解决冲突。事实上,这两个冲突状态也不能用LR(1)方法解决,因为G5.3是一个二义性文法(读者可以自己构造LR(1)项集来分析)。然而,使用算符的优先关系和结合关系可以解决这类冲突:按照算术优先规则,乘号*

394、的优先级高于加号,而且它们都符合左结合率。这样,在状态I7,当面临输入符号+的时候,遵循左结合率就应采用EE+E归约;而面临*符号时就对项EE*E采取移进动作。同理,在状态I8,无论输入符是+还是*都采用EE*E归约。5.3.6二义文法的应用二义文法的应用状状态ACTIONGOTOi+*()$E0s3s211s4s5acc2s3s263r4r4r4r44s3s275s3s286s4s5s97r1s5r1r18r2r2r2r29r3r3r3r3编译原理与技术编译原理与技术403表5.17二义文法G5.3的LR(0)分析表5.3.6二义文法的应用二义文法的应用步步骤状状态栈符号符号栈输入串入串AC

395、TIONGOTO10$(i+i)*i$s2202$(i+i)*i$s33023$(i+i)*i$r464026$(E+i)*i$s650234$(E+i)*i$s3602343$(E+i)*i$r46702346$(E+E)*i$s98023469$(E+E)*i$r31901$E*i$s510015$E*i$s3110153$E*i$r48120158$E*E$r211301$E$acc编译原理与技术编译原理与技术404表表5.18用二义性文法用二义性文法G5.3对符号串对符号串(i+i)*i的分析过程的分析过程5.3.6二义文法的应用二义文法的应用编译原理与技术编译原理与技术405例例5.

396、16考虑抽象了大多数程序语言中ifthenelse的文法G5.9:(1)SS(2)SiSeS(3)SiS(4)Sa它的LR(0)项集规范族如图5.12所示。可以看出,由于FOLLOW(S)=e,$,状态I4存在移进归约冲突:当面临输入符号e的时候,分析器不知是按照SiS归约还是移进符号e。用分支语句ifthenelse来说,就是当栈顶出现ifBooleanthenS1而又面临符号else时,是否要归约呢?按照程序语言的普遍习惯,通常让else分支与最近的一个then分支语句匹配,即此时应该执行移进动作。这也是最常匹配原则的一个例子。利用这个优先规则就可以构造出没有冲突的LR(0)分析表。5.3

397、.6二义文法的应用二义文法的应用状状态ACTIONGOTOiea$S0s2s311acc2s2s343r3r34s5r25s2s366r1r1编译原理与技术编译原理与技术406iieSBaSI0:SSSiSeSSiSAaSI1:SSI4:SiSeSSiSI3:AaiI2:SiSeSSiSSiSeSSiSAaaI5:SiSeSSiSeSSiSAaI6:SiSeSa5.3.6LR分析的错误恢复分析的错误恢复uLR分析器有两种处理错误的策略:分析器有两种处理错误的策略:紧急方式的错误恢复和短语级恢复。紧急方式的错误恢复和短语级恢复。紧急方式的错误恢复紧急方式的错误恢复l从栈顶开始推栈,直到退到状态从

398、栈顶开始推栈,直到退到状态s,它有余弦确定,它有余弦确定的非终结符的非终结符A的转移;然后抛弃若干个(可能是的转移;然后抛弃若干个(可能是0个)输入符号,制导找到符号个)输入符号,制导找到符号a,它能合法地跟随,它能合法地跟随A;分析器再把;分析器再把A和状态和状态gotos,A压进栈,恢复压进栈,恢复正常分析。正常分析。A的选择可能不唯一,一般而言的选择可能不唯一,一般而言A应该应该代表较大程序结构的非终结符,如表达式、语句代表较大程序结构的非终结符,如表达式、语句或过程。例如,如果或过程。例如,如果A是非终结符是非终结符stmt,那么,那么a可可以是分号或以是分号或end。编译原理与技术编

399、译原理与技术4075.3.6LR分析的错误恢复分析的错误恢复短语级恢复短语级恢复l当发现错误是,分析器对剩余输入做局部纠正,当发现错误是,分析器对剩余输入做局部纠正,用可以是分析器继续分析的串来代替剩余输入的用可以是分析器继续分析的串来代替剩余输入的前缀。典型的局部纠正是用分号代替逗号,删除前缀。典型的局部纠正是用分号代替逗号,删除多于的分号,或插入遗漏的分号等。多于的分号,或插入遗漏的分号等。lLR分析器实现短语级错误恢复就是检查分析表中分析器实现短语级错误恢复就是检查分析表中每一个错误条目,根据语言的使用情况,决定最每一个错误条目,根据语言的使用情况,决定最有可能进入该条目的输入错误,然后

400、为该条目设有可能进入该条目的输入错误,然后为该条目设计一个适当的错误恢复过程。假设已经完成了对计一个适当的错误恢复过程。假设已经完成了对栈顶符号和当前输入符号的修改,使之适合每个栈顶符号和当前输入符号的修改,使之适合每个错误恢复条目。错误恢复条目。编译原理与技术编译原理与技术4085.3.6LR分析的错误恢复分析的错误恢复状状态ACTIONGOTOi+*()$E0s3e1e1s2e2e111e3s4s5e3e2acc2s3e1e1s2e2e163e3r4r4r4r4r44s3e1e1s2e2e175s3e1e1s2e2e186e3s4s5e3s9e47r1r1s5r1r1r18r2r2r2r2

401、r2r29r3r3r3r3r3r3编译原理与技术编译原理与技术409例5.17再考虑表达式文法EE+E|E*E|(E)|i表5.20显示了这个文法的LR分析表,它在表5.18的基础上增加了错误诊断域恢复。我们把某些错误条目改成了规约,这样就会推迟错误的发现,多执行了一步或几步归约,但仍在移进下一个符号前发现错误。5.3.6LR分析的错误恢复分析的错误恢复编译原理与技术编译原理与技术410增加的错误处理例过程在表中无法用LR规则填写的空表单元,用e表示错去处理过程,含义如下:e1:调用的条件是,当分析器处于状态0、2、4或5,输入符号是+、*或输入串终结标志$的时候,因为此时期望的正确输入符号是

402、运算符的起始符号如i或左括号。执行的动作是把假想的i压入栈顶,上面盖上状态3(状态0、2、4或5面临i时所转移的状态),给出诊断信息“缺少运算对象”。e2:调用的条件是,当分析器处于状态0、1、2、4或5,面临输入符号为右括号。执行的动作是删除右括弧,给出诊断信息“不匹配的右括弧”。e3:调用的条件是,当分析器处于状态1或6,输入符号是i或左括号(期望运算符)。执行的动作是把+压入栈顶,上面盖上状态4,给出诊断信息“缺少运算符”。e4:调用的条件是,当分析器处于状态6,面临输入符号是$(期望运算符或右括弧)。执行的动作是把右括弧压入栈顶,上面盖上状态9,给出诊断信息“缺少右括弧”。5.3.6L

403、R分析的错误恢复分析的错误恢复步步骤状状态栈符号符号栈输入串入串注注释10$i+i)$203$i+i)$301$E+i)$4014$E+)$5014$E+$右括弧右右括弧右过程程e2删除除60143$E+i$过程程e1把把i压入入栈顶70147$E+E$801$E$分析完分析完毕编译原理与技术编译原理与技术411假如有一个错误的输入i+),采用表5.20的分析过程如下5.4LALR分析器的生成工具分析器的生成工具yaccuyacc的工作示意图如下的工作示意图如下“yacc源程序源程序”是用户用是用户用yacc提供的一种类似提供的一种类似BNF的语言写的的语言写的要处理的语言的语法描述,要处理的

404、语言的语法描述,yacc会自动地将这个源程序转换成会自动地将这个源程序转换成用用LR方法进行语法分析的语法分析程序方法进行语法分析的语法分析程序yyparse。同同Lex一样,一样,yacc的宿主语言也是的宿主语言也是C,因此,因此yyparse是一个是一个C语言语言的程序,用户在主程序中通过调用的程序,用户在主程序中通过调用yyparse进行语法分析。进行语法分析。编译原理与技术编译原理与技术412词法分析器yylex语法分析器yyparseyaccyacc源程序源程序单词符号串语法分析输出5.4LALR分析器的生成工具分析器的生成工具yaccuyacc源程序或规格说明文件包括三部分:源程序

405、或规格说明文件包括三部分:说明部分说明部分语义规则部分语义规则部分辅助部分辅助部分编译原理与技术编译原理与技术4135.4LALR分析器的生成工具分析器的生成工具yacc例子是算术表达式的计算器,它读入一个算例子是算术表达式的计算器,它读入一个算术表达式,求值,然后打印出结果。该整数术表达式,求值,然后打印出结果。该整数算术表达式的文法如下:算术表达式的文法如下:expexpaddopterm|termaddop+| termtermmulopfactor|factoraddop*factor(exp)|number其中其中number是数。是数。编译原理与技术编译原理与技术4145.4LAL

406、R分析器的生成工具分析器的生成工具yacc这个文法的这个文法的yacc源程序如下源程序如下:编译原理与技术编译原理与技术415%#include#include%tokenNUMBER%command:expprintf(“%dn”,$1);/*允许打印结果*/exp:exp+term$=$1+$3;|expterm$=$1$3;|term$=$1;term:exp*factor$=$1*$3;|factor$=$1;factor:NUMBER$=$1;|(exp) $=$2;%main()returnyyparse();intyylex(void)intc;while(c=getchar()

407、=);/*删除空格*/if(isdigit(c)ungetc(c,stdin);scanf(“%d”,&yylval);return(NUMBER);if(c=)return0;/*停止语法分析*/return(c);intyyerror(char*s)fprintf(stderr,“%sn”,s);return0;5.4LALR分析器的生成工具分析器的生成工具yaccu定义部分包含了两个组成:定义部分包含了两个组成:1.插入在插入在yacc输入开始的代码,就是在输入开始的代码,就是在%和和%之间之间的两条的两条#include指令;指令;2.代表了数字序列单词记号代表了数字序列单词记号NUM

408、BER的声明。的声明。Yacc有两种方式识别单词有两种方式识别单词:l首先,在语法规则单引号内的任何符号都代表了终结符,首先,在语法规则单引号内的任何符号都代表了终结符,可以识别为自身,例如运算符可以识别为自身,例如运算符“+”,“ ”和和“ ”以以及括弧。及括弧。l其次,符号单词可以在其次,符号单词可以在yacc的的%token说明中声明,例如说明中声明,例如BUMBER。Yacc赋予这些单词记号一个唯一的数值。一赋予这些单词记号一个唯一的数值。一般而言,般而言,yacc用用258开始给记号赋值,把这些记号的定义开始给记号赋值,把这些记号的定义作为作为#define子句插入到输出文件。所以,

409、对应子句插入到输出文件。所以,对应yacc的的%tokenNUMBER的声明可能在输出文件中产生下面的一的声明可能在输出文件中产生下面的一行:行:#defineNUMBER258编译原理与技术编译原理与技术4165.4LALR分析器的生成工具分析器的生成工具yacc在规则部分可以看到非终结符在规则部分可以看到非终结符exp、term和和factor的规则。由于希望打印表达式的值,所的规则。由于希望打印表达式的值,所以我们增加了一条称为以我们增加了一条称为command的规则,并的规则,并给它定义了打印动作。因为这条规则列在首给它定义了打印动作。因为这条规则列在首位,位,command也就当作文

410、法的起始符号。也就当作文法的起始符号。Yacc说明中的动作是用花括号括起来的说明中的动作是用花括号括起来的C代码,代码,通常放在每条候选式的末尾、分号或通常放在每条候选式的末尾、分号或“|”的的前面前面编译原理与技术编译原理与技术4175.4LALR分析器的生成工具分析器的生成工具yacc其中其中$表示语法规则左部非终结符的值,表示语法规则左部非终结符的值,$1,$2和和$3等分别代表产生式右部第一个、第二个和第三个等的等分别代表产生式右部第一个、第二个和第三个等的非终结符的属性值。所以,在上述非终结符的属性值。所以,在上述yacc说明中,语法说明中,语法规则和语义动作规则和语义动作exp :

411、exp+term$=$1+$3;表示在识别了规则表示在识别了规则expexp+term后,把规则右部后,把规则右部exp与与term属性之和赋给规则左部属性之和赋给规则左部exp的属性值。所有的属性值。所有的非终结符都通过这种用户提供的动作获得属性值。的非终结符都通过这种用户提供的动作获得属性值。Yacc约定,在识别了一个终结符的时候,其属性值赋约定,在识别了一个终结符的时候,其属性值赋必须赋给必须赋给yacc内部定义的变量内部定义的变量yylval。所以,在文法。所以,在文法规则和动作规则和动作factor :NUMBER $=$1;中,中,$1表示终结符表示终结符NUMBER的值。的值。编

412、译原理与技术编译原理与技术4185.4LALR分析器的生成工具分析器的生成工具yacc这个这个yacc源程序的辅助部分包含了三个子程序说明。源程序的辅助部分包含了三个子程序说明。l第一个是第一个是main的定义,把它包括进来可以使的定义,把它包括进来可以使yacc的输出直的输出直接编译成可执行代码。接编译成可执行代码。lmain调用调用yyparse,它是,它是yacc产生的语法分析的过程名。产生的语法分析的过程名。lYyparse又调用名字为又调用名字为yylex的词法扫描器,以便与的词法扫描器,以便与Lex扫描扫描器生成器(见第器生成器(见第2章)保持一致。章)保持一致。l所以,所以,ya

413、cc文件里也包含了文件里也包含了yylex的定义。的定义。l本例的本例的yylex只需返回下一个非空的字符,除非这个字符是只需返回下一个非空的字符,除非这个字符是数字,在这种情况下,它必须识别出构成数字,在这种情况下,它必须识别出构成NUMBER的数字的数字串并且返回在串并且返回在yylval中的数值。中的数值。l最后,最后,yacc定义了一个打印错误信息的过程定义了一个打印错误信息的过程yyerror,以便,以便在语法分析出现错误是调用。在语法分析出现错误是调用。编译原理与技术编译原理与技术4195.4LALR分析器的生成工具分析器的生成工具yacc对于二义性的算术表达式文法对于二义性的算术

414、表达式文法EE+E|E*E|E/E|E|(E)|iyacc允许用户规定运算符的优先级和结合性,就可以消除允许用户规定运算符的优先级和结合性,就可以消除上述文法的二义性。上述文法的二义性。用用yacc表示如下:表示如下:tokenNAMEleft+left*exp:exp+exp|expexp|exp*exp|exp/exp|expprec*|NAME;编译原理与技术编译原理与技术4205.4LALR分析器的生成工具分析器的生成工具yaccyacc提供了下面两条消除二义性的规则提供了下面两条消除二义性的规则A1出现移进归约冲突时,进行移进;出现移进归约冲突时,进行移进;A2.出现归约归约冲突时,

415、按照产生式在出现归约归约冲突时,按照产生式在yacc源程序中出现源程序中出现的次序,用先出现的产生式归约。的次序,用先出现的产生式归约。根据终结符和产生式的优先级和结合性,根据终结符和产生式的优先级和结合性,yacc又有两又有两个解决冲突的规则:个解决冲突的规则:P1.当出现移进归约冲突或归约归约冲突,而且当时输入当出现移进归约冲突或归约归约冲突,而且当时输入符号和语法规则均没有优先级和结合性,就用符号和语法规则均没有优先级和结合性,就用A1和和A2来解来解决这些冲突。决这些冲突。P2当出现移进归约冲突时,如果输入符号和语法规则都当出现移进归约冲突时,如果输入符号和语法规则都有优先级和结合性,

416、那么如果输入符号的优先级大于产生式有优先级和结合性,那么如果输入符号的优先级大于产生式的优先级就移进如果输入符号的优先级小于产生式的优先级的优先级就移进如果输入符号的优先级小于产生式的优先级就归约。如果二者优先级相等,则由结合性决定动作,左结就归约。如果二者优先级相等,则由结合性决定动作,左结合则归约,右结合则移进,无结合性则出错。合则归约,右结合则移进,无结合性则出错。编译原理与技术编译原理与技术421编译原理与技术编译原理与技术第第6章章符号表的组织和管理符号表的组织和管理主要内容主要内容u符号表的作用符号表的作用u符号表的主要属性及其作用符号表的主要属性及其作用u符号表的组织结构符号表的

417、组织结构u名字的作用范围名字的作用范围编译原理与技术编译原理与技术4236.1符号表的作用符号表的作用u登记符号属性值登记符号属性值在源程序的各个分析阶段,编译程序根据标在源程序的各个分析阶段,编译程序根据标识符的声明信息收集它属性的有关值,并把识符的声明信息收集它属性的有关值,并把它们存放在符合表中。它们存放在符合表中。每种语言规则定义了不同的符号属性;即使每种语言规则定义了不同的符号属性;即使是同一个语言,不同的编译程序也可能会定是同一个语言,不同的编译程序也可能会定义并且收集不同的属性信息。义并且收集不同的属性信息。现代编程语言中一般包括常数声明、变量声现代编程语言中一般包括常数声明、变

418、量声明、类型声明和过程明、类型声明和过程/函数声明等四类声明。函数声明等四类声明。对于每类声明,编译程序要收集、存储和应对于每类声明,编译程序要收集、存储和应用的属性完全不同。用的属性完全不同。编译原理与技术编译原理与技术4246.1符号表的作用符号表的作用例例6.1C语言的变量声明语言的变量声明shortinta;floatb=0.0;把标识符把标识符a声明为短整数型,把声明为短整数型,把b声明为浮点声明为浮点类型,而且初始化为类型,而且初始化为0。那么,编译程序对每。那么,编译程序对每个变量要记录它的类型,以便执行类型检查个变量要记录它的类型,以便执行类型检查和分配存储,比如短整型变量和分

419、配存储,比如短整型变量i占占2个字节;要个字节;要记录它在存储器中的位置(相对位移或绝对记录它在存储器中的位置(相对位移或绝对地址),以便目标程序运行时访问;若像地址),以便目标程序运行时访问;若像b有有初始值,则还需要记录这个初始值。初始值,则还需要记录这个初始值。编译原理与技术编译原理与技术4256.1符号表的作用符号表的作用例例6.2下面是计算阶乘下面是计算阶乘n!的的C语言的函数声明:语言的函数声明:intfactory(intn)intt;if(n=0)|n=1)t=1;elset=n factory(n1);returnt;对于函数对于函数factory要记录的属性包括:函数的名称

420、,各要记录的属性包括:函数的名称,各种变量如参数、返回值、局部变量及其类型,同时还种变量如参数、返回值、局部变量及其类型,同时还要记录函数的调用信息,以便在函数体执行完毕以后要记录函数的调用信息,以便在函数体执行完毕以后返回到调用点,特别是对这种允许递归调用的函数,返回到调用点,特别是对这种允许递归调用的函数,要为每次调用保留上面提到的所有信息。要为每次调用保留上面提到的所有信息。编译原理与技术编译原理与技术4266.1符号表的作用符号表的作用u查找符号的属性查找符号的属性符号表存放了源程序中的各种类型的信息,比如数值、符号表存放了源程序中的各种类型的信息,比如数值、变量类型、参数传递的地址等

421、,在分析和翻译源程序变量类型、参数传递的地址等,在分析和翻译源程序的过程中会被不断地查询。的过程中会被不断地查询。例如,对于上述的变量声明,如果源程序有代码例如,对于上述的变量声明,如果源程序有代码a+b时,就需要查找、计算表达式中运算数的类型和时,就需要查找、计算表达式中运算数的类型和值,以便计算出表达式。值,以便计算出表达式。又如,在源程序中如果出现了函数调用又如,在源程序中如果出现了函数调用factory(6),编译程序就需要查找到编译程序就需要查找到factory的声明,找到实参的声明,找到实参6的的地址并传给形参地址并传给形参n,执行函数,执行函数factory的体,并返回值,的体,

422、并返回值,等。等。编译原理与技术编译原理与技术4276.1符号表的作用符号表的作用u检查符号的合法性检查符号的合法性1.例如,对于上述声明,代码例如,对于上述声明,代码a=a+b,C语言的编语言的编译将检查变量译将检查变量a和和b的类型,把表达式的类型,把表达式a+b的结果转的结果转换成短整型,仅取整数部分进行赋值。换成短整型,仅取整数部分进行赋值。2.在其它强类型语言,如在其它强类型语言,如Pascal和和Ada,表达式运算数,表达式运算数的类型必须一致,不能进行隐式类型转换,对于这的类型必须一致,不能进行隐式类型转换,对于这样的表达式样的表达式a+b,编译程序在语义分析的过程中将,编译程序

423、在语义分析的过程中将发现并报告类型错误的信息。发现并报告类型错误的信息。3.又如,面向对象语言的继承性和多态性允许同一个又如,面向对象语言的继承性和多态性允许同一个消息在不同的环境中调用不同的方法(函数),即消息在不同的环境中调用不同的方法(函数),即调用同名但在不同的类中实现的方法。这就需要编调用同名但在不同的类中实现的方法。这就需要编译或者运行时在方法的符号表中查询在参数、返回译或者运行时在方法的符号表中查询在参数、返回数以及方法方面名字一致的实现。数以及方法方面名字一致的实现。编译原理与技术编译原理与技术4286.1符号表的作用符号表的作用u作为目标代码生成阶段地址分配的依据作为目标代码

424、生成阶段地址分配的依据标识符由它定义的存储类型或它在程序中的位置来确定。标识符由它定义的存储类型或它在程序中的位置来确定。首先是要确定变量存储的区域。例如,在首先是要确定变量存储的区域。例如,在Java语言中,整数的语言中,整数的类型(以及所占用的字节)有类型(以及所占用的字节)有byte(1个字节)、个字节)、short(2个字个字节)、节)、int(4个字节)以及个字节)以及long(8个字节),而个字节),而float类型占类型占4个个字节,字节,double类型占类型占8个字节。又如,对寄存器变量,编译将尽个字节。又如,对寄存器变量,编译将尽可能地把它们保留在机器的寄存器当中,以提高运

425、行速度;而可能地把它们保留在机器的寄存器当中,以提高运行速度;而对在一个文件中定义的外部变量,它们要在不同的源程序文件对在一个文件中定义的外部变量,它们要在不同的源程序文件之间访问,需要编译程序把它们放在所有源程序文件都可以方之间访问,需要编译程序把它们放在所有源程序文件都可以方便寻找到的存储器的位置。便寻找到的存储器的位置。其次,要根据标识符出现的顺序,决定标识符在某个存储区域其次,要根据标识符出现的顺序,决定标识符在某个存储区域中的具体位置,而有关区域的标志及其相对位置都是作为该标中的具体位置,而有关区域的标志及其相对位置都是作为该标识符的语义信息存放在它的符号表中的。识符的语义信息存放在

426、它的符号表中的。编译原理与技术编译原理与技术4296.2符号表的主要属性及其作用符号表的主要属性及其作用不同的符号类别包含了不同的属性,由于它不同的符号类别包含了不同的属性,由于它们的信息不同,也就导致了符号表的组织有们的信息不同,也就导致了符号表的组织有较大的差别。例如,数量类型的变量名字和较大的差别。例如,数量类型的变量名字和过程名字:过程名字:l对于一个变量名要记录其类型(如整型、实型、对于一个变量名要记录其类型(如整型、实型、布尔型等)、占用的存储字节以及相对与某个基布尔型等)、占用的存储字节以及相对与某个基准位置的相对位置;准位置的相对位置;l对一个过程名要记录的属性包括参数的个数及

427、其对一个过程名要记录的属性包括参数的个数及其类型,该过程是否有返回值,过程中的变量声明,类型,该过程是否有返回值,过程中的变量声明,甚至过程声明(如果像甚至过程声明(如果像Pascal语言允许嵌套过程语言允许嵌套过程声明)等信息。声明)等信息。编译原理与技术编译原理与技术4306.2符号表的主要属性及其作用符号表的主要属性及其作用符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性编译原理与技术编译原理与技术431符号名符号名l符号名可以是变量名、函数名(操作名)、类型名、类对象名等符号名可以是变量名、函数名(操作名)、类型名、类对象名等l每个

428、符号名通常由若干个(非空)的字符组成的字符串来表达,在每个符号名通常由若干个(非空)的字符组成的字符串来表达,在语言中它们通常是一个变量、函数或对象的位移标志,因此在符号语言中它们通常是一个变量、函数或对象的位移标志,因此在符号表中的符号名作为表项的唯一区别一般是不允许重名的。表中的符号名作为表项的唯一区别一般是不允许重名的。l符号名与它在符号表中的位置建立起一一对应的关系,这使得我们符号名与它在符号表中的位置建立起一一对应的关系,这使得我们可以用一个符号在表中的位置来代替该符号名、访问其信息。通常可以用一个符号在表中的位置来代替该符号名、访问其信息。通常把一个标识符在符号表中的位置值称为该标

429、识符的内部代码。把一个标识符在符号表中的位置值称为该标识符的内部代码。l在经过分析处理的源程序中,标识符不再是一个字符串而是一个表在经过分析处理的源程序中,标识符不再是一个字符串而是一个表示内部码的整数值,这不但便于识别,而且也可以压缩存储和表达示内部码的整数值,这不但便于识别,而且也可以压缩存储和表达的长度。的长度。6.2符号表的主要属性及其作用符号表的主要属性及其作用符号名符号名l语言中的符号名通常用标识符来表示。根据语言的定义,程序中出语言中的符号名通常用标识符来表示。根据语言的定义,程序中出现的重名标识符定义将按照该标识符在程序中的作用域和可视规则现的重名标识符定义将按照该标识符在程序

430、中的作用域和可视规则进行相应的处理。而在程序的运行过程中,符号表中的符号名始终进行相应的处理。而在程序的运行过程中,符号表中的符号名始终是唯一的标志。是唯一的标志。l在一些允许操作重载、类继承的语言中,函数名、操作名允许重名,在一些允许操作重载、类继承的语言中,函数名、操作名允许重名,对于重载操作的标识符,它们可以通过参数的个数与类型以及返回对于重载操作的标识符,它们可以通过参数的个数与类型以及返回值的类型来区别;而对于操作的继承,编译器可以构造继承图,同值的类型来区别;而对于操作的继承,编译器可以构造继承图,同时保存类结构,这样就可以为每个操作和属性找到唯一的定义。时保存类结构,这样就可以为

431、每个操作和属性找到唯一的定义。l例如,对应不同的参数类型,可以定义几个求和重载函数:例如,对应不同的参数类型,可以定义几个求和重载函数:intsum(inta,intb)doublesum(doublea,doubleb)floatsum(floata,floatb,floatc)l当某个函数中调用到重载函数时,编译器根据实参的类型和个数去当某个函数中调用到重载函数时,编译器根据实参的类型和个数去调用相应的函数。调用相应的函数。编译原理与技术编译原理与技术4326.2符号表的主要属性及其作用符号表的主要属性及其作用符号种属符号种属l由于语言中符号所拥有的属性可能不同,其组织就可以采用不同由于语

432、言中符号所拥有的属性可能不同,其组织就可以采用不同的数据结构,可以用符号的种属来区别每个符号的基本划分。的数据结构,可以用符号的种属来区别每个符号的基本划分。l根据不同的语言,符号的种属可以包括:简单变量、结构型变量、根据不同的语言,符号的种属可以包括:简单变量、结构型变量、数组、过程、类型、类等。数组、过程、类型、类等。l可以依据符号种属的划分来组织符号表,一种方式是为每个种属可以依据符号种属的划分来组织符号表,一种方式是为每个种属的标识符建立一张表,这样,可以对符号表类似地安排组织结构、的标识符建立一张表,这样,可以对符号表类似地安排组织结构、进行同样的操作;另外一种方式把所有种属的标识符

433、统一安排在进行同样的操作;另外一种方式把所有种属的标识符统一安排在一张表中,根据符号的种属进行条件判断,对不同种属的特殊型一张表中,根据符号的种属进行条件判断,对不同种属的特殊型执行不同的存储安排和操作。执行不同的存储安排和操作。符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性编译原理与技术编译原理与技术4336.2符号表的主要属性及其作用符号表的主要属性及其作用符号类型符号类型l现代程序语言中的一个重要构造就是数据类型(类型),它现代程序语言中的一个重要构造就是数据类型(类型),它是变量标识符的重要属性,函数的数据类型指的是该函数返是变量

434、标识符的重要属性,函数的数据类型指的是该函数返回值的数据类型。回值的数据类型。l现代语言通常都有如下的基本类型:整型、实型、字符型、现代语言通常都有如下的基本类型:整型、实型、字符型、布尔型、逻辑型等;布尔型、逻辑型等;l符号的类型属性从源程序中该符号的定义中得到符号的类型属性从源程序中该符号的定义中得到l变量符号的数据类型属性不但决定了该变量的数据在存储器变量符号的数据类型属性不但决定了该变量的数据在存储器中的存储格式,也规定了可以对该变量施加的操作运算。中的存储格式,也规定了可以对该变量施加的操作运算。符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其

435、它属性其它属性编译原理与技术编译原理与技术4346.2符号表的主要属性及其作用符号表的主要属性及其作用符号类型符号类型l目前的大多数语言都在基本类型的基础上定义复合数据类型,目前的大多数语言都在基本类型的基础上定义复合数据类型,如数组、集合和记录。如数组、集合和记录。l许多语言还允许程序员自己定义数据类型。这些复合类型的许多语言还允许程序员自己定义数据类型。这些复合类型的基本元素可以是基本类型,也可以是复合类型。基本元素可以是基本类型,也可以是复合类型。l作为存储变量地址的指针类型所指向的变量可以是基本的数作为存储变量地址的指针类型所指向的变量可以是基本的数据类型,也可以是其它任何一种复合数据

436、类型。据类型,也可以是其它任何一种复合数据类型。编译原理与技术编译原理与技术435符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性每一个变量的类型是符号表中标识符属性的重要信息。每一个变量的类型是符号表中标识符属性的重要信息。6.2符号表的主要属性及其作用符号表的主要属性及其作用存储类别存储类别l大多数程序语言对变量的存储类别采用两种方式。大多数程序语言对变量的存储类别采用两种方式。l一种是用关键字指定,例如,在一种是用关键字指定,例如,在FORTRAN语言中用语言中用COMMON来来定义公共存储区域,允许不同程序段都可以访问这些数据;又如

437、,定义公共存储区域,允许不同程序段都可以访问这些数据;又如,C和和C+语言规定语言规定static定义的变量属于文件的静态存储变量或属定义的变量属于文件的静态存储变量或属于函数内部的静态存储变量,这些变量在编译时分配存储空间,如于函数内部的静态存储变量,这些变量在编译时分配存储空间,如果定义时没有初值,编译器还需要将它们初始化为果定义时没有初值,编译器还需要将它们初始化为0。l另一种方式是根据定义变量的声明在程序中的位置来决定。例如,另一种方式是根据定义变量的声明在程序中的位置来决定。例如,C+规定在一个文件中定义的变量缺省为外部的,即程序的公共存规定在一个文件中定义的变量缺省为外部的,即程序

438、的公共存储变量;而在函数体内缺省存储类别关键字所定义的变量是内部变储变量;而在函数体内缺省存储类别关键字所定义的变量是内部变量,是属于该函数体所独有的私有存储变量,因而是动态地分配存量,是属于该函数体所独有的私有存储变量,因而是动态地分配存储空间。储空间。l区别符号存储类型地属性是编译过程中语义处理、检查和存储分配区别符号存储类型地属性是编译过程中语义处理、检查和存储分配的重要依据。的重要依据。l符号的存储类别同时还决定了符号变量的作用域、可见性和它的生符号的存储类别同时还决定了符号变量的作用域、可见性和它的生命周期等性质命周期等性质。编译原理与技术编译原理与技术436符号名符号名符号种属符号

439、种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性6.2符号表的主要属性及其作用符号表的主要属性及其作用作用域作用域l一个标识符在程序中起作用的范围称为其作用域一个标识符在程序中起作用的范围称为其作用域。l一般来说,定义一个符号的位置及存储类型就决定了该符号一般来说,定义一个符号的位置及存储类型就决定了该符号的作用域,就是它可以出现的场合,可以在程序中作为参数、的作用域,就是它可以出现的场合,可以在程序中作为参数、表达式的运算数等被引用。表达式的运算数等被引用。lC语言中外部变量的作用域是整个程序,一个外部符号的定语言中外部变量的作用域是整个程序,一个外部符号的定义

440、在整个策划能够许中只能出现一次,为了方便使用和编译,义在整个策划能够许中只能出现一次,为了方便使用和编译,同名标识符的其它说明可以多次出现。同名标识符的其它说明可以多次出现。lFORTRAN语言中的语言中的COMMON变量的作用域则不是整个程变量的作用域则不是整个程序,而只能在定义这个序,而只能在定义这个COMMON块的函数或过程中引用。块的函数或过程中引用。l面向对象语言,如面向对象语言,如C+,的每个类都引入了一个独立的类域。,的每个类都引入了一个独立的类域。编译原理与技术编译原理与技术437符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它

441、属性6.2符号表的主要属性及其作用符号表的主要属性及其作用作用域与可见性作用域与可见性l标识符的标识符的可见性可见性从另外一个角度说明其有效性,它与作用域从另外一个角度说明其有效性,它与作用域有一定一致性。有一定一致性。l标识符的作用域包含可见范围,但是,可见范围不会超过作标识符的作用域包含可见范围,但是,可见范围不会超过作用域。用域。l可见性在理解同名是不是合法的作用域嵌套时十分直观。对可见性在理解同名是不是合法的作用域嵌套时十分直观。对于外层块域内层块定义的同名标识符,在外层作用域中,内于外层块域内层块定义的同名标识符,在外层作用域中,内层所定义的标识符时不可见的,即外层所引用的是外层所定

442、层所定义的标识符时不可见的,即外层所引用的是外层所定义的标识符;义的标识符;l同样,在内层作用域中,外层的标识符将被内层的同名标识同样,在内层作用域中,外层的标识符将被内层的同名标识符所屏蔽,变得不可见,即外层中同名标识符的可见范围是符所屏蔽,变得不可见,即外层中同名标识符的可见范围是作用域中挖去内层块的范围,在内存块形成了作用域中挖去内层块的范围,在内存块形成了作用域洞作用域洞。编译原理与技术编译原理与技术438符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性6.2符号表的主要属性及其作用符号表的主要属性及其作用编译原理与技术编译原理与技

443、术439例例6.3图6.1(b)显示了下列程序段中变量的作用域与可见性,其中intm的作用域在括号中不可见,即这个程序块在intm的作用域中挖了一个洞。intm=1;floatn;floatm=3.14;n=5.5;m+;intm和和floatn的作用域的作用域intm和和n可见可见floatm不可见不可见floatm的作用域的作用域floatm和和n可见可见intm不可见不可见符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性6.2符号表的主要属性及其作用符号表的主要属性及其作用存储分配信息存储分配信息l编译程序需要根据符号的存储类别定义以

444、及它们在程序中出编译程序需要根据符号的存储类别定义以及它们在程序中出现的位置和顺序来确定每一个符号应该分配的存储区域及其现的位置和顺序来确定每一个符号应该分配的存储区域及其具体位置。具体位置。l通常情况下,编译为每个符号分配一个相对于某个基址的相通常情况下,编译为每个符号分配一个相对于某个基址的相对位移,而不是绝对的内存地址。对位移,而不是绝对的内存地址。l编译程序中有关源程序的存储组织和分配的问题将在第编译程序中有关源程序的存储组织和分配的问题将在第7章章中详细讨论。中详细讨论。编译原理与技术编译原理与技术440符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分

445、配信息其它属性其它属性6.2符号表的主要属性及其作用符号表的主要属性及其作用其它属性其它属性1.数组内情向量数组内情向量需要把描述数组属性的信息如数组类型、维数、需要把描述数组属性的信息如数组类型、维数、每个维的上下界、数组元素的首地址等登录在符号表中,以便确每个维的上下界、数组元素的首地址等登录在符号表中,以便确定数组在存储器占用的空间和数组元素的确定,并且完成数组的定数组在存储器占用的空间和数组元素的确定,并且完成数组的翻译。翻译。2.记录结构型的成员信息记录结构型的成员信息一个记录结构型的变量包含若干成员,一个记录结构型的变量包含若干成员,每个成员的数据类型可以彼此不同,因此,一个记录结

446、构型变量每个成员的数据类型可以彼此不同,因此,一个记录结构型变量在存储分配时所占空间的大小由其成员来确定,而且,对每个成在存储分配时所占空间的大小由其成员来确定,而且,对每个成员的访问还需要它所属成员排列次序的属性信息。员的访问还需要它所属成员排列次序的属性信息。3.函数或过程的形参函数或过程的形参函数或过程的形参作为其局部变量,同时又函数或过程的形参作为其局部变量,同时又是对外部调用的接口。每个函数或过程形参的个数、类型、排列是对外部调用的接口。每个函数或过程形参的个数、类型、排列顺序都体现了调用函数或过程时的属性,它们都应该反映在符号顺序都体现了调用函数或过程时的属性,它们都应该反映在符号

447、表中,以便在过程调用的时候进行参数传递,并且执行语义检查表中,以便在过程调用的时候进行参数传递,并且执行语义检查(如处理函数名的重载)。(如处理函数名的重载)。4.在面相对象语言中,还必须把一个类或其超类所定义同名方法存在面相对象语言中,还必须把一个类或其超类所定义同名方法存放在一个方法表中,指向每个方法的实现操作,以便实现面相对放在一个方法表中,指向每个方法的实现操作,以便实现面相对象的继承性质。象的继承性质。编译原理与技术编译原理与技术441符号名符号名符号种属符号种属符号符号类型型存存储类别作用域作用域存存储分配信息分配信息其它属性其它属性6.3符号表的组织结构符号表的组织结构符号表的整

448、体组织结构符号表的整体组织结构1.符符号号名名栏栏目目也也叫叫主主栏栏,其其内内容容是是源源程程序序中中出出现现的的标标识识符符,它它是是区区分分每每个个表表项项的的关键码。关键码。2.对于保留字、类型、函数,它们的名字(标识符)就是符号表的关键码;对于保留字、类型、函数,它们的名字(标识符)就是符号表的关键码;3.对于语言中的保留字,它们本身就是表项中的关键码;对于语言中的保留字,它们本身就是表项中的关键码;4.对对于于语语言言中中操操作作符符,如如乘乘幂幂“*”、赋赋值值号号“:=、+=”或或者者FORTRAN中中的的拼拼写操作写操作“.GT.”,其字符或字符串就作为该操作符表项的关键码。

449、,其字符或字符串就作为该操作符表项的关键码。符号名信息栏属性值1属性值2属性值m表项1(入口1)表项2(入口2)表项n(入口n)编译原理与技术编译原理与技术4426.3符号表的组织结构符号表的组织结构编译程序对符号表的总体组织可以有编译程序对符号表的总体组织可以有3种形式种形式假设有下列三种类型的符号及其属性:假设有下列三种类型的符号及其属性:第一第一类符号符号符号种属符号种属名字名字类型型值地址地址第二第二类符号符号符号种属符号种属名字名字字字节数数编译原理与技术编译原理与技术443第三第三类符号符号符号种属符号种属名字名字值嵌套数嵌套数地址地址6.3符号表的组织结构符号表的组织结构u第一种

450、(如图第一种(如图6.3所示):所示):根据符号类型进行分类,把属性完全相同的那些符号安排在一张表中根据符号类型进行分类,把属性完全相同的那些符号安排在一张表中这样就构造出许多不同的符号表,每个表的信息栏目中属性个数和结构完全这样就构造出许多不同的符号表,每个表的信息栏目中属性个数和结构完全一样,而且每个表项的属性栏目都是等长、有效。这种单个符号表的管理和一样,而且每个表项的属性栏目都是等长、有效。这种单个符号表的管理和操作都比较方便,空间使用效率也高。操作都比较方便,空间使用效率也高。缺点是一个编译程序要同时管理若干个不同类型的符号表,符号表分得太散,缺点是一个编译程序要同时管理若干个不同类

451、型的符号表,符号表分得太散,对不同类型符号表中的共同属性必须设置重复的管理机制,增加了符号表管对不同类型符号表中的共同属性必须设置重复的管理机制,增加了符号表管理的工作量和复杂度。理的工作量和复杂度。编译原理与技术编译原理与技术444符号表符号表1符号种属符号种属名字名字类型型值地址地址符号表符号表2符号种属符号种属名字名字字字节数数符号表符号表3符号种属符号种属名字名字值嵌套数嵌套数地址地址6.3符号表的组织结构符号表的组织结构u第二种(如图第二种(如图6.4所示):所示):把语言中的所有符号都组织在一张符号表中。把语言中的所有符号都组织在一张符号表中。优点是总体管理符号表集中、单一,不同类

452、型符号中的相同属性得优点是总体管理符号表集中、单一,不同类型符号中的相同属性得到了一致的管理和处理。到了一致的管理和处理。为了完整地存放所有属性就导致每个表项所包含的属性个数不一样,为了完整地存放所有属性就导致每个表项所包含的属性个数不一样,出现不等长的表项,这就极大地提高了符号表管理和处理的复杂性。出现不等长的表项,这就极大地提高了符号表管理和处理的复杂性。为了保持表项等长,把所有符号的全部属性都作为符号表中信息栏为了保持表项等长,把所有符号的全部属性都作为符号表中信息栏目的属性,这样的组织有助于减低符号表的管理和处理的复杂性。目的属性,这样的组织有助于减低符号表的管理和处理的复杂性。但是对

453、于不同类型的符号,可能增加了无用的属性空间,从而降低但是对于不同类型的符号,可能增加了无用的属性空间,从而降低编译程序的空间使用效率。例如,对于第二类符号,单一组织的符编译程序的空间使用效率。例如,对于第二类符号,单一组织的符号表中就超过一般的属性不需要,极大地浪费存储空间。号表中就超过一般的属性不需要,极大地浪费存储空间。符号种属名字类型值字节数地址1地址2编译原理与技术编译原理与技术4456.3符号表的组织结构符号表的组织结构u第三种是前两种的折衷形式。第三种是前两种的折衷形式。根根据据属属性性的的相相似似程程度度把把符符号号表表分分成成若若干干类类型型,每每个个类类型型组组织织成成一一张

454、张表,每张表中记录的符号都有很多相同的属性。表,每张表中记录的符号都有很多相同的属性。这这种种组组织织方方式式在在管管理理的的复复杂杂程程度度和和空空间间的的使使用用效效率率方方面面都都取取得得了了上上述两种组织的这种效果。述两种组织的这种效果。而而且且,可可以以根根据据目目标标系系统统的的体体系系结结构构和和编编译译程程序序设设计计者者的的经经验验,对对符符号号表表的的分分类类进进行行选选择择和和调调整整。例例如如,可可以以将将上上面面的的类类型型一一和和类类型型三的符号合成一张表,类型二构成一个单独的符号表。三的符号合成一张表,类型二构成一个单独的符号表。第一类和第三类共同的符号表第一类和

455、第三类共同的符号表第二类符号的符号表第二类符号的符号表符号种属名字类型值嵌套数地址符号种属名字字节数编译原理与技术编译原理与技术4466.3符号表的组织结构符号表的组织结构关键码域的组织关键码域的组织编译原理与技术编译原理与技术447符号表的关键码域就是符号本身,它可以是语言的保留字、操作符,也可以是标识符,如变量名、常数名、函数名、类型名、类名等程序结构。语言文法的词法对各种符号都有严格的定义。语言文法的词法对各种符号都有严格的定义。保留字和操作符的名字一般是只有唯一的拼写方法,而且不能用当作其它用途的用户定义的标识符。标识符通常是以字母开头的、长度确定或不限长度的字母和数字组成的字符串。如

456、果语言对标识符的长度有限制,可以让符号表中关键码域有标识符允许的最大长度以容纳语言的整个标识符单词。但是,如果语言对标识符的长度不加限制,或者为了避免最大标识符长度过大而浪费存储空间,通常的做法是另外设立一个存放标识符单词的字符数组,在符号表的名字栏目中仅给出标识符单词在这个数组中的首地址和符号长度的二元组。这样,符号表的关键码域可以有一致的大小,而且,同时也可以节省存储空间。6.3符号表的组织结构符号表的组织结构编译原理与技术编译原理与技术448例例6.4假定有标识符假定有标识符student、name、birthday、code、p,它们依次存放,它们依次存放在上述的标识符单词数组中,其间

457、没有间隔标志,这样的符号表结构如图在上述的标识符单词数组中,其间没有间隔标志,这样的符号表结构如图6.6所示所示。tudentnamebirthdaycodeps符号名符号名信息信息.6.3符号表的组织结构符号表的组织结构u不等长域的组织不等长域的组织编译原理与技术编译原理与技术449上述对标识符不限长度的处理是组织符号名的一种间接方式,这种方式可以推广上述对标识符不限长度的处理是组织符号名的一种间接方式,这种方式可以推广到属性域不相等的情形。我们可以把一些共同属性直接记录在符号表的信息栏,到属性域不相等的情形。我们可以把一些共同属性直接记录在符号表的信息栏,把某些特殊的属性记录在其它地方,并

458、且在符号表的信息栏中增设一个指针,指把某些特殊的属性记录在其它地方,并且在符号表的信息栏中增设一个指针,指向这个存放特殊属性值的位置。向这个存放特殊属性值的位置。例例6.5图图6.7示意了通过符号表访问内情向量的组织结构,符号表有两个数组示意了通过符号表访问内情向量的组织结构,符号表有两个数组array1和和array2,它们分别有,它们分别有n维和维和1维。维。符号名信息栏内情向量表类型.地址基址n上界1下界1array1数组.上界n下界narray2数组基址1上界1下界1.6.3符号表的组织结构符号表的组织结构u符号表的操作符号表的操作对于给定符号,查询此名字是否在符号表中;对于给定符号,

459、查询此名字是否在符号表中;对于给定符号,在符号表中访问它的属性信息;对于给定符号,在符号表中访问它的属性信息;对于给定符号,在符号表中更新它的属性信息;对于给定符号,在符号表中更新它的属性信息;在符号表中插入一个新的符号及其相关信息;在符号表中插入一个新的符号及其相关信息;删除一个或一组无用的表项。删除一个或一组无用的表项。编译原理与技术编译原理与技术4506.3符号表的组织结构符号表的组织结构插入、查找、更新和删除的函数可以说明如下:插入、查找、更新和删除的函数可以说明如下:procedureinsert(token.entry:Addess;type:Typekind);把入口是把入口是e

460、ntry的符号的符号token的类型的类型type插入符号表。类似地也可插入符号表。类似地也可以插入其它信息,如变量的值,譬如以插入其它信息,如变量的值,譬如proceduresetvalue(variable.entry:Addess;value:Valuetype);对在符号表入口的变量对在符号表入口的变量variable设置值设置值value。functionlookup(entry:Addess):Typekind;查找符号表中入口是查找符号表中入口是entry的标识符,返回它的类型。的标识符,返回它的类型。functiongetvalue(entry:Addess):Valuekin

461、d;得到符号表中入口是得到符号表中入口是entry的标识符的数值。的标识符的数值。functiondelete(entry:Addess):Boolean;把入口是把入口是entry的表项从符号表中删除,如果成功则返回真值的表项从符号表中删除,如果成功则返回真值true,不成功则为假值,不成功则为假值false。编译原理与技术编译原理与技术4516.3符号表的组织结构符号表的组织结构u三种常见的符号表的结构:三种常见的符号表的结构:线性表、搜索树和散列表组织线性表、搜索树和散列表组织线性表组织是按照符号被扫描到的先后顺序填写各个表项,可线性表组织是按照符号被扫描到的先后顺序填写各个表项,可以用

462、一个多维数组或多个一维数组来存放符号的信息。以用一个多维数组或多个一维数组来存放符号的信息。线性表需要两个指针来方便管理和操作:一个指针指向该符号线性表需要两个指针来方便管理和操作:一个指针指向该符号表的开始位置,另一个指针指向符号表的下一个可用位置。表的开始位置,另一个指针指向符号表的下一个可用位置。线性表是最基本的数据结构,可以方便、直接地实现上述的插线性表是最基本的数据结构,可以方便、直接地实现上述的插入、查找和删除三种基本操作,而且每种的操作时间都是符号入、查找和删除三种基本操作,而且每种的操作时间都是符号表大小的线性函数,对于有表大小的线性函数,对于有N个表项的符号表,个表项的符号表

463、,这些操作的平均这些操作的平均时间都是时间都是N/2左右(算法时间复杂性为左右(算法时间复杂性为(N))。由于线性表无需附加空间,比较节省存储。如果编译器对处理由于线性表无需附加空间,比较节省存储。如果编译器对处理时间要求不高,或者符号个数不大(如关键字),符号表就可时间要求不高,或者符号个数不大(如关键字),符号表就可以采用线性表结构。以采用线性表结构。编译原理与技术编译原理与技术4526.3符号表的组织结构符号表的组织结构u搜索树结构搜索树结构搜索树可以在构造符号表的同时,按照符号名的字典顺序把表搜索树可以在构造符号表的同时,按照符号名的字典顺序把表项整理排列,提高符号表查找操作的速度。项

464、整理排列,提高符号表查找操作的速度。这样就可以采用折半查找的方式,加快搜索的速度。这样就可以采用折半查找的方式,加快搜索的速度。对于有对于有N个表项的符号表,每次查找最多只需要做个表项的符号表,每次查找最多只需要做logN次比较次比较(因此这种查找法也叫对数查找法)。(因此这种查找法也叫对数查找法)。但是,由于符号表在编译过程中是边填写边引用,动态地建立、但是,由于符号表在编译过程中是边填写边引用,动态地建立、更新以及删除表项,这样,每增加和删除一个表项都需要对符更新以及删除表项,这样,每增加和删除一个表项都需要对符号表进行重新排序,这同样浪费时间。号表进行重新排序,这同样浪费时间。因此,搜索

465、树结构不适合用于构造符号表,除了需要额外的空因此,搜索树结构不适合用于构造符号表,除了需要额外的空间构造搜索树以外,整体而言,它们实现这三类操作效率不是间构造搜索树以外,整体而言,它们实现这三类操作效率不是最优,而且删除操作的实现过于复杂。最优,而且删除操作的实现过于复杂。编译原理与技术编译原理与技术4536.3符号表的组织结构符号表的组织结构u符号表处理的关键问题符号表处理的关键问题散列组织统一了查询与插入操作技术,相对来说具有较高的时散列组织统一了查询与插入操作技术,相对来说具有较高的时空效率,为上述三种操作提供的时间基本上是常数。空效率,为上述三种操作提供的时间基本上是常数。特别是散列表

466、结构符合编译过程边填写边引用符号表的特性,特别是散列表结构符合编译过程边填写边引用符号表的特性,是实现符号表的最佳数据结构,在实践中的使用最多。是实现符号表的最佳数据结构,在实践中的使用最多。编译原理与技术编译原理与技术454线性表结构填表快,查询慢;搜索树结构查询快,填表慢。线性表结构填表快,查询慢;搜索树结构查询快,填表慢。如何保证如何保证查询与插入查询与插入表项这两个基本操作的都能高效地完成。表项这两个基本操作的都能高效地完成。6.3符号表的组织结构符号表的组织结构u散列方法散列方法散列方法在表项的存储位置与它的关键码之间建立一个确定的散列方法在表项的存储位置与它的关键码之间建立一个确定

467、的对应函数关系(哈希函数,杂凑函数,对应函数关系(哈希函数,杂凑函数,hash),使每个关键码),使每个关键码symbol与散列结构(散列表,哈希表,杂凑表)中的唯一的存与散列结构(散列表,哈希表,杂凑表)中的唯一的存储位置相对应,即储位置相对应,即hash(symbol)。在搜索时,首先对表项的关键码用哈希函数计算出对应的表项在搜索时,首先对表项的关键码用哈希函数计算出对应的表项的存储位置,在散列表中按此位置取出表项进行比较,若关键的存储位置,在散列表中按此位置取出表项进行比较,若关键码相等,则搜索成功。码相等,则搜索成功。在填入表项时,依同样函数计算存储位置,并按此位置存放表在填入表项时,

468、依同样函数计算存储位置,并按此位置存放表项。由于使用这种方法进行搜索时不必多次比较关键码,因此项。由于使用这种方法进行搜索时不必多次比较关键码,因此搜速速度比较快,可以到达逼近具有此关键码的表项的实际存搜速速度比较快,可以到达逼近具有此关键码的表项的实际存放地址。放地址。编译原理与技术编译原理与技术4556.3符号表的组织结构符号表的组织结构u对哈希函数的基本要求对哈希函数的基本要求计算简单、高效;计算简单、高效;函数值能均匀地分布在函数值能均匀地分布在1和和N之间,之间,对不同的关键码都返回一个代表存储位置的不同值。对不同的关键码都返回一个代表存储位置的不同值。构造哈希函数的算法有许多,例如

469、,若取构造哈希函数的算法有许多,例如,若取N为素数,为素数,就可以定义哈希函数为就可以定义哈希函数为symbol/N的余数,其中的余数,其中symbol是某个符号的代码。由于语言中的标识符可以相互区是某个符号的代码。由于语言中的标识符可以相互区别,它们的代码值都是不同的。别,它们的代码值都是不同的。编译原理与技术编译原理与技术4566.3符号表的组织结构符号表的组织结构u散列冲突的解决散列冲突的解决不同的关键码经过杂凑运算以后,有可能得到相同的不同的关键码经过杂凑运算以后,有可能得到相同的杂凑值,这种现象称为杂凑值,这种现象称为散列冲突散列冲突。一种常用的方法是一种常用的方法是链地址法链地址法

470、。把有把有N个地址的散列表改为个地址的散列表改为N个桶,桶号与散列地址个桶,桶号与散列地址一一对应,第一一对应,第i(1iN)个桶号即为第)个桶号即为第i个散列地址,个散列地址,每个桶则是一个线性链表(称为同义词表),链表中每个桶则是一个线性链表(称为同义词表),链表中的表项具有相同的散列函数值。的表项具有相同的散列函数值。若出现了冲突,即一个表项的散列值所对应的地址已若出现了冲突,即一个表项的散列值所对应的地址已经被占据,则需把这个表项放到该桶的链尾或链首。经被占据,则需把这个表项放到该桶的链尾或链首。编译原理与技术编译原理与技术4576.3符号表的组织结构符号表的组织结构编译原理与技术编译

471、原理与技术458.addr.HashTable1hn名字.sym1sym2.symSymbolTablea2addra3属性1.属性j.属性m.hash()链表指针.nulla2.a3.next.如何在符号表中使用散列表技术如何在符号表中使用散列表技术对于每个符号表增加一个地址链项(初始化为null);建立一个散列表(桶),它是一个含n个符号表入口地址的一维数组,每个散列表的表项初始化为null,表示散列函数值所对应的符号表的表项没有占用。对符号表的操作实际上就是通过散列函数间接地操作符号表。6.3符号表的组织结构符号表的组织结构通过散列函数间接地操作符号表通过散列函数间接地操作符号表编译原理

472、与技术编译原理与技术459符号表中填入一个新的sym符号(1)首先对于符号sym用散列函数hash在散列表HashTable中查找出它在符号表SymbolTable中的位置h:hash(sym)返回的值在1n之间,令指针ptr:=HashTableh;否则p:=hull。(2)如果ptr为null,则把sym的信息填在SymbolTableptr所对应的一个符号表的表项中。如果ptr不是null,则表示出现了冲突:首先在符号表中得到下一个新的可用的项(地址用next表示),接着在散列表中把同义词表的表头改为next,然后填写这个新得到符号表的表项内容:把链表指针由null改成SymbolTab

473、leptr的链表指针,填写sym的其它属性值。6.4名字的作用范围名字的作用范围u名字的声明和作用域名字的声明和作用域程序语言中的声明是把信息联系到名字(标识符)的程序语言中的声明是把信息联系到名字(标识符)的一种语法结构。一种语法结构。编程语言中一般包括编程语言中一般包括5类声明:类声明:常量声明、变量声明、类型声明和过程常量声明、变量声明、类型声明和过程/函数声明以及类声明函数声明以及类声明l常量声明把值联解到名字常量声明把值联解到名字。l类型声明把名字绑定到新构造的类型或者为已经存在的名字类型声明把名字绑定到新构造的类型或者为已经存在的名字创建一个别名。创建一个别名。l变量声明通常把名字

474、绑定到一个类型,同时还隐式地绑定了变量声明通常把名字绑定到一个类型,同时还隐式地绑定了其它属性。其它属性。一个声明起作用的程序部分称为该声明的一个声明起作用的程序部分称为该声明的作用域作用域。过。过程中出现的名字,如果是在该过程的一个声明的作用程中出现的名字,如果是在该过程的一个声明的作用域内,就称为域内,就称为局部局部于该过程的;否则叫做于该过程的;否则叫做非局部的非局部的。编译原理与技术编译原理与技术4606.4名字的作用范围名字的作用范围先声明后使用是一条在大多数程序中普遍采先声明后使用是一条在大多数程序中普遍采用的规则,它要求一个名字的声明在程序的用的规则,它要求一个名字的声明在程序的

475、正文中在它的引用之前。正文中在它的引用之前。这条规则允许编译器一边分析程序一边建立这条规则允许编译器一边分析程序一边建立符号表:符号表:l一旦在程序代码中遇到了名字引用才执行符号表一旦在程序代码中遇到了名字引用才执行符号表的查找操作;的查找操作;l如果查找失败,就出现了违反先声明后使用的规如果查找失败,就出现了违反先声明后使用的规则,编译器就能发出一个适当的错误消息。则,编译器就能发出一个适当的错误消息。所以,这条规则允许一遍扫描的编译构造。所以,这条规则允许一遍扫描的编译构造。编译原理与技术编译原理与技术4616.4名字的作用范围名字的作用范围u块结构与符号表的分层次管理块结构与符号表的分层

476、次管理块结构是现代程序语言的普通构造,它是程序中可以包含声明块结构是现代程序语言的普通构造,它是程序中可以包含声明的任何程序构造。的任何程序构造。l在在Pascal语言中,主程序和过程语言中,主程序和过程/函数声明是块结构;函数声明是块结构;lC语言中,函数声明以及花括号内的复合语句表示块结构。语言中,函数声明以及花括号内的复合语句表示块结构。lJava语言中的包语言中的包package把一组相关的类封装在一起,也构成块把一组相关的类封装在一起,也构成块结构。结构。作用域服从最近嵌套规则作用域服从最近嵌套规则(1)程序块)程序块B中声明的作用域包括中声明的作用域包括b自身;自身;(2)如果名字

477、)如果名字x没有在没有在B中声明,那么,中声明,那么,B中中x的出现是在外围程序块的出现是在外围程序块B的的x声明的作用域中,而且声明的作用域中,而且B比其它任何含比其它任何含x声明的程序块在程序声明的程序块在程序正文中更接近被嵌套的程序块正文中更接近被嵌套的程序块B。编译原理与技术编译原理与技术4626.4名字的作用范围名字的作用范围编译原理与技术编译原理与技术463例例6.7考察下列考察下列C语言代码。语言代码。它有5个块:第一个块是整个代码,包含了整数i和j以及函数f的声明;第二个块是函数f自身,包含一个参数size;第三个块是函数f体,有两个变量i和temp的声明;第四个块是函数f内的

478、复合语句A,声明了实数j;第五个块也在函数f内,复合语句B包含了声明char*j函数f内的变量size和temp在符号表中只有一个唯一的声明,对这些名字的所有引用都参考这些声明。名字i在函数f内声明为字符类型,按照最近嵌套规则,这个声明覆盖了函数f以外的把i声明为int的声明。(称非局部声明inti在函数f内有一个洞)。函数f内的两个声明也覆盖了函数f以外的声明intj。在这个例子中,直到退出局部声明的块程序才恢复i和j的原始声明。inti,j;intf(intsize)chari,temp;A:doublej;B:char*j;6.4名字的作用范围名字的作用范围u实现嵌套作用域和最近嵌套规则

479、实现嵌套作用域和最近嵌套规则要实现嵌套作用域,符号表的插入操作不能覆盖以前要实现嵌套作用域,符号表的插入操作不能覆盖以前的声明,但是,必须暂时隐藏它们,以便查找操作只的声明,但是,必须暂时隐藏它们,以便查找操作只能找到一个最近插入名字的声明。能找到一个最近插入名字的声明。同样,删除操作不能删除对应名字的所有声明,而是同样,删除操作不能删除对应名字的所有声明,而是删除最近插入的那个,恢复任何先前的声明。删除最近插入的那个,恢复任何先前的声明。符号表可以这样构造:在每程序块的入口处对所有声符号表可以这样构造:在每程序块的入口处对所有声明的名字执行插入操作;在块的出口处对同样的名字明的名字执行插入操

480、作;在块的出口处对同样的名字执行删除操作。换言之,符号表的行为在处理嵌套作执行删除操作。换言之,符号表的行为在处理嵌套作用域时就像一个栈结构。用域时就像一个栈结构。编译原理与技术编译原理与技术4646.4名字的作用范围名字的作用范围u例例6.7的嵌套作用域的一种实现的嵌套作用域的一种实现编译原理与技术编译原理与技术465i(char)i(int)size(int)j(int)temp(char)f(function)桶同义词表图6.11(a)处理完函数f体的声明以后i(char)i(int)j(*char)j(int)temp(char)f(function)桶同义词表size(int)图6.

481、11(b)处理完函数f体内复合语句B的声明以后i(char)j(int)f(function)桶同义词表图6.11(c)退出函数f体(删除其声明)6.4名字的作用范围名字的作用范围u例例6.7的嵌套作用域的另一种实现的嵌套作用域的另一种实现编译原理与技术编译原理与技术466i(int)j(*char)j(int)temp(char)f(function)size(int)i(char)对应图6.11(b)的符号表结构每个作用域使用独立的表对每个作用域建立一张新的符号表,把它们按照作用域自里向外连接起来,对每个作用域建立一张新的符号表,把它们按照作用域自里向外连接起来,这样,查找操作如果不能在当

482、前不表中找到一个名字,就自动地在外面包含这样,查找操作如果不能在当前不表中找到一个名字,就自动地在外面包含它的表中继续查找。退出一个作用域时,删除操作的工作非常简单,只需删它的表中继续查找。退出一个作用域时,删除操作的工作非常简单,只需删除对应作用域的整个表。除对应作用域的整个表。编译原理与技术编译原理与技术第第7章章运行时环境运行时环境主要内容主要内容u程序运行的基本概念程序运行的基本概念u参数传递机制参数传递机制u静态运行时环境静态运行时环境u运行时存储空间的组织和管理运行时存储空间的组织和管理u栈式运行时环境栈式运行时环境u堆式运行时环境堆式运行时环境编译原理与技术编译原理与技术4687

483、.1程序运行的基本概念程序运行的基本概念u过程及其活动过程及其活动过程定义是把一个名字和若干语句联系起来过程定义是把一个名字和若干语句联系起来的一个声明。的一个声明。这个名字是过程名,而这些语句就是过程体。这个名字是过程名,而这些语句就是过程体。返回值的过程通常称为函数,完整的程序也返回值的过程通常称为函数,完整的程序也可以看作一个过程。可以看作一个过程。在面向对象技术中,过程叫做方法或操作。在面向对象技术中,过程叫做方法或操作。本章把过程、函数、方法这样的程序单元统本章把过程、函数、方法这样的程序单元统称为过程。称为过程。编译原理与技术编译原理与技术4697.1程序运行的基本概念程序运行的基

484、本概念u过程及其活动过程及其活动当过程名出现在程序中作为一个语句或表达当过程名出现在程序中作为一个语句或表达式使用时,就称这个过程在程序点被调用。式使用时,就称这个过程在程序点被调用。过程调用就是执行被调用过程的过程体。过程调用就是执行被调用过程的过程体。出现在过程定义中的参数叫做形式参数(形出现在过程定义中的参数叫做形式参数(形参),在过程调用点取代形参的称为实在参参),在过程调用点取代形参的称为实在参数(实参)。数(实参)。编译原理与技术编译原理与技术4707.1程序运行的基本概念程序运行的基本概念编译原理与技术编译原理与技术471programsort(input,output)vara

485、:array0.10ofinteger;procedurereadarray;vari:integer;beginfori:=1to9doread(ai)end;functionpartition(y,z:integer):integervari,j,x,y:integer;begin.end;procrdurequicksort(m,n:integer);vari:integer;beginif(nm)thenbegini=partition(m,n);quicksort(m,i 1);quicksort(i+1,n);end;end;begina0:= 9999;a10:=9999;rea

486、darray;quicksort(1,9);end;7.1程序运行的基本概念程序运行的基本概念u过程的活动过程的活动过程的每次调用就引起过程体的一次执行,过程的每次调用就引起过程体的一次执行,称为过程的一次活动。称为过程的一次活动。过程过程p的一个活动的生存期就是从过程体开始的一个活动的生存期就是从过程体开始执行到执行结束的时间,包括执行被执行到执行结束的时间,包括执行被P调用其调用其它所有过程所耗费的时间。它所有过程所耗费的时间。一般而言,术语一般而言,术语“生存期生存期”指的是程序执行指的是程序执行过程中若干步骤的一个连续序列。过程中若干步骤的一个连续序列。编译原理与技术编译原理与技术47

487、27.1程序运行的基本概念程序运行的基本概念u过程的活动过程的活动任何两个过程的活动任何两个过程的活动p和和q只能存在下列关系:只能存在下列关系:l不重叠或者嵌套。过程的活动不重叠或者嵌套。过程的活动p和和q不重叠,指的是它们的不重叠,指的是它们的执行时间(生存期)没有重叠;执行时间(生存期)没有重叠;l活动活动p嵌套在活动嵌套在活动q,指的是活动,指的是活动q的生存期包括了活动的生存期包括了活动p的的生存期。生存期。两个过程活动两个过程活动p和和q的关系表明,若程序的执行控制的关系表明,若程序的执行控制在退出在退出q之前进入了之前进入了p,则必须在退出,则必须在退出q之前退出之前退出p。如果

488、同一个过程的两个不同活动如果同一个过程的两个不同活动p和和q重叠,即在该重叠,即在该过程的活动过程的活动p没有退出之前又重新另外一个活动没有退出之前又重新另外一个活动q,这样的过程称为递归过程。这样的过程称为递归过程。同一个过程的不同调用产生不同的活动。同一个过程的不同调用产生不同的活动。编译原理与技术编译原理与技术4737.1程序运行的基本概念程序运行的基本概念u活动记录活动记录过程的一次执行要用一块连续的存储区来管理过程的一次执行要用一块连续的存储区来管理需要的信息,这块存储区叫做活动记录或帧。需要的信息,这块存储区叫做活动记录或帧。返回返回值实在参数在参数(可(可选)控制)控制链(可(可

489、选)访问链机器状机器状态局部数据局部数据临时数据数据编译原理与技术编译原理与技术474返回值域:用于存放被调用过程返回给调用过程的值,根据不同的返回值域:用于存放被调用过程返回给调用过程的值,根据不同的参数传递机制,返回值可以是数值或指向返回值地址的指针。参数传递机制,返回值可以是数值或指向返回值地址的指针。实参域:用于存放调用过程提供的实在参数,根据不同的形参和实参的传实参域:用于存放调用过程提供的实在参数,根据不同的形参和实参的传递机制,实参域可以是数值或指向实参地址的指针。递机制,实参域可以是数值或指向实参地址的指针。控制链域:指向调用者活动记录的指针,也称为动态链。控制链域:指向调用者

490、活动记录的指针,也称为动态链。访问链域:对于像访问链域:对于像Pascal这样的语言,需要访问链来访问非局部数据;对这样的语言,需要访问链来访问非局部数据;对于于FROTRAN和和C则不需要,访问链又称静态链。则不需要,访问链又称静态链。机器状态域:保存该过程调用前的机器状态信息,包括程序计数器(机器状态域:保存该过程调用前的机器状态信息,包括程序计数器(pc)的)的值以及控制从该过程返回时必须恢复的机器寄存器的值。值以及控制从该过程返回时必须恢复的机器寄存器的值。局部数据:存储用于该过程执行时的数据。局部数据:存储用于该过程执行时的数据。临时数据:保存该过程执行时临时的数据,如表达式的中间结

491、果。临时数据:保存该过程执行时临时的数据,如表达式的中间结果。7.1程序运行的基本概念程序运行的基本概念u调用序列和返回序列调用序列和返回序列调用序列,操作如调用序列,操作如l活动记录分配存储空间;活动记录分配存储空间;l计算并存储参数值;计算并存储参数值;l为调用分配并存储影响调用的存储器。为调用分配并存储影响调用的存储器。返回序列,操作如返回序列,操作如l把返回值放到调用者可以访问到的位置;把返回值放到调用者可以访问到的位置;l恢复机器状态;恢复机器状态;l调整寄存器;调整寄存器;l释放活动记录的存储。释放活动记录的存储。编译原理与技术编译原理与技术4757.1程序运行的基本概念程序运行的

492、基本概念编译原理与技术编译原理与技术476u活动树活动树1.每个结点代表过程的一个活动记录(调用);每个结点代表过程的一个活动记录(调用);2.根结点代表主程序;根结点代表主程序;3.结点结点p是结点是结点q的父结点,当且仅当控制流从的父结点,当且仅当控制流从p的活动进入的活动进入q的活动;的活动;4.在同一层中,结点在同一层中,结点p在结点在结点q的左边,当且仅的左边,当且仅当当p的生存期先于的生存期先于q的生存期。的生存期。7.1程序运行的基本概念程序运行的基本概念编译原理与技术编译原理与技术477例例7.1假如执行图7.1的程序,我们可以构造出程序执行期间的所有过程的活动记录,表示整个程

493、序运行的踪迹,如图7.3所示。树根是主程序sort的活动记录,它首先调用了readarray和quisksort(1,9),按照程序的执行顺序,readarray在quisksort(1,9)之前,即readarray的生存期先于在quisksort(1,9),所以,结点ra在qs(1,9)的左边。qs(1,9)又调用了一次partition和两次quisksort,图中显示的结点分别是pt(1,9),qs(1,3)和qs(5,9)。注意,qs(1,3)和qs(5,9)都是递归调用,它们在quisksort(1,9)结束之前结束。sortraqs(1,9)qs(1,3)pt(1,9)qs(5,

494、9)qs(1,0)pt(1,3)qs(2,3)qs(5,5)pt(5,9)qs(7,9)qs(7,7)pt(7,9)qs(9,9)qs(2,1)pt(2,3)qs(3,3)7.1程序运行的基本概念程序运行的基本概念u名字的绑定和环境名字的绑定和环境按照程序设计语言的语义,环境表示把一个名字映射到一个存按照程序设计语言的语义,环境表示把一个名字映射到一个存储位置的函数,状态表示把存储单元映射到所保存的值的函数储位置的函数,状态表示把存储单元映射到所保存的值的函数环境把名字映射到左值,状态把左值映射到右值。环境把名字映射到左值,状态把左值映射到右值。环境和状态是有区别的:赋值改变状态,但不改变环境

495、。例如,环境和状态是有区别的:赋值改变状态,但不改变环境。例如,和假设存储单元和假设存储单元100关联的变量关联的变量pi记录的值是记录的值是0。在赋值语句。在赋值语句pi:=3.14之后,同样是关联之后,同样是关联pi的存储单元的值就变成了的存储单元的值就变成了3.14。如果环境把名字如果环境把名字x映射到存储单元映射到存储单元s,就说,就说x被绑定到被绑定到s。术语存。术语存储单元是象征性的,因为如果储单元是象征性的,因为如果x不是一个基本类型,那么不是一个基本类型,那么x的存的存储单元储单元s可能是一组存储单元。可能是一组存储单元。编译原理与技术编译原理与技术4787.2参数传递机制参数

496、传递机制u按值调用按值调用实参在调用时刻计算得到的表达式,其值就是过程执实参在调用时刻计算得到的表达式,其值就是过程执行时形参的值。可以如下实现:行时形参的值。可以如下实现:(1)把形参当作所在过程的局部变量名看待,形参的存储单)把形参当作所在过程的局部变量名看待,形参的存储单元在该过程的活动记录中;元在该过程的活动记录中;(2)调用过程计算实参,并把值放入形参的存储单元中。)调用过程计算实参,并把值放入形参的存储单元中。它的最简单形式可以解释成值参数在过程的执行中作它的最简单形式可以解释成值参数在过程的执行中作为常数值,取代了过程体中所对应的形式参数。为常数值,取代了过程体中所对应的形式参数

497、。C语语言和言和Java语言使用按值调用,它们也是语言使用按值调用,它们也是Pascal和和Ada的缺省参数传递方法。的缺省参数传递方法。编译原理与技术编译原理与技术4797.2参数传递机制参数传递机制u按值调用按值调用把形参当作所在过程的局部变把形参当作所在过程的局部变量名看待,形参的存储单元在量名看待,形参的存储单元在该过程的活动记录中;该过程的活动记录中;调用过程计算实参,并把值放入调用过程计算实参,并把值放入形参的存储单元中。形参的存储单元中。编译原理与技术编译原理与技术480被调用过程的活动记录.形参1形参2.调用序列在调用点计算实参1的值计算实参2的值把值分别送入形参图7.5按值调

498、用示意图按值调用的显著特征是,按值调用的显著特征是,对形参的任何运算都不会影响调用者的实参的值。对形参的任何运算都不会影响调用者的实参的值。7.2参数传递机制参数传递机制u按值调用按值调用下列程序下列程序inc1不会达到它期望的效果:不会达到它期望的效果:voidinc1(intx)/*不正确的程序不正确的程序*/+x;returnx;在在C语言中,需要传递地址来实现上述预想的效果:语言中,需要传递地址来实现上述预想的效果:voidinc1(int*x)/*正确的程序正确的程序*/+(*x);return*x;当然,希望对变量当然,希望对变量y增值时,这个函数的调用形式为增值时,这个函数的调用

499、形式为inc1(&y),因为函数需要的是,因为函数需要的是y的地址而不是值。的地址而不是值。编译原理与技术编译原理与技术4817.2参数传递机制参数传递机制u引用调用(传地址调用)引用调用(传地址调用)(1)1.如果实参是变量或具有左值的如果实参是变量或具有左值的表达式,则把该左值放入形参表达式,则把该左值放入形参的存储单元;的存储单元;2.如果实参是如果实参是3或或3a这样没有左这样没有左值的表达式,则首先把它的计算值存入到一个新的存储单元,值的表达式,则首先把它的计算值存入到一个新的存储单元,然后把这个地址传递给形参。然后把这个地址传递给形参。(2)在被调用过程的目标代码中,对形参的任何引

500、用都是通)在被调用过程的目标代码中,对形参的任何引用都是通过形参存储的地址间接引用实参的。过形参存储的地址间接引用实参的。编译原理与技术编译原理与技术482被调用过程的活动记录.形参1形参2.调用序列在调用点实参1的地址计算实参2的值并存入临时变量t把t的地址图7.6引用调用示意图引用调用的显著特征是,对形参的任何运算都直接影响调用者的实参引用调用的显著特征是,对形参的任何运算都直接影响调用者的实参7.2参数传递机制参数传递机制u引用调用(传地址调用)引用调用(传地址调用)在在Pascal中,引用调用是通过在过程声明中使用关键字中,引用调用是通过在过程声明中使用关键字“var”实现的;实现的;

501、C和和C+在过程声明中使用地址符号在过程声明中使用地址符号“&”实现参数的引用调实现参数的引用调用,例如:用,例如:voidinc1(int&x)/*正确的程序正确的程序*/+x;returnx;这个函数可以直接调用,不必使用地址操作符:这个函数可以直接调用,不必使用地址操作符:inc1(y)就可就可以使变量以使变量y的值增加。的值增加。编译原理与技术编译原理与技术4837.2参数传递机制参数传递机制u值结果调用值结果调用(复写恢复复写恢复,复写入复写复写入复写)(1)调用过程把实参的值和实参的地址同时传给被调用过程;)调用过程把实参的值和实参的地址同时传给被调用过程;(2)被调用过程像按值调

502、用那样使用传递给形参的值;)被调用过程像按值调用那样使用传递给形参的值;(3)把形参在被调用过程中最后的值拷贝到实参的存储单元内。)把形参在被调用过程中最后的值拷贝到实参的存储单元内。编译原理与技术编译原理与技术484被调用过程的活动记录.形参1实参1的地址形参2调用序列在调用点计算实参1的值实参1的地址图7.7值结果调用示意图实参2的地址.计算实参2的值实参2的地址下列(按照C的语法格式)代码:voidp(intx,inty)+x;+y;main()inta=1;p(a,a);returna;如果参数传递使用引用调用的方式,在调用p之后a的值是3;如果使用值结果调用的方式,a的值则是2。7.

503、2参数传递机制参数传递机制u换名调用换名调用实参直到在被调用的过程中作为形参实际使实参直到在被调用的过程中作为形参实际使用的时候才计算,所以也叫延迟计算。用的时候才计算,所以也叫延迟计算。换名调用可以如下实现:换名调用可以如下实现:(1)在调用点,用被调用过程的体来替换调用者的)在调用点,用被调用过程的体来替换调用者的调用,但是形参用对应的实参来替换。这种文字调用,但是形参用对应的实参来替换。这种文字替换方式成为宏展开或内联展开。替换方式成为宏展开或内联展开。(2)在宏展开前,被调用过程的每个局部变量名字)在宏展开前,被调用过程的每个局部变量名字被系统地重新命名可以区别的名字。被系统地重新命名

504、可以区别的名字。编译原理与技术编译原理与技术4857.2参数传递机制参数传递机制编译原理与技术编译原理与技术486例如,例如,对于代码voidp(intx)+x;如果出现了p(ai)这样的调用,其效果就是+(ai)。所以,如果i在过程p内的变量x之前改变了值,结果将既不同于引用调用,也不同于值结果调用。7.2参数传递机制参数传递机制编译原理与技术编译原理与技术487例例7.2考虑下列(C语法格式的)代码:inti;inta10;voidp(intx)+i;+x;main()i=1;a1=1;a2=5;p(ai);采用按值调用的参数传递机制,调用过程p:尽管在过程p中改变了i的值为2,但a2的值

505、不变;a1的值仍然为1。采用按值调用的参数传递机制,调用过程p不改变a1和a2的值。采用引用调用和值结果的参数传递机制,调用过程p的结果一样,都是把a1的值改变为2,而没改变a2的值。(但是,如果采用值结果参数传递机制的另外一种实现,在复写出形参值的时候重新计算对应实参的地址,那么,调用过程p的结果把a2设置为2,保持a1的值不变。)采用换名参数传递机制,调用过程p的结果是把a2设置为6并保持a1的值不变。7.2参数传递机制参数传递机制编译原理与技术编译原理与技术488例例7.2考虑下列(C语法格式的)代码:inti;inta10;voidp(intx)+i;+x;main()i=1;a1=1

506、;a2=5;p(ai);在这个程序中,由于i对于p是非局部变量,无论哪种参数传递方式都在p中改变了值为2。但是,其它变量的结果随着参数传递机制的不同而有差异。采用按值调用的参数传递机制,调用过程p:尽管在过程p中改变了i的值为2,但a2的值不变;语句+x的效果相当于+(a1),调用没有把改变的值送回a1,即a1的值仍然为1。采用引用调用的参数传递机制,调用过程p时把参数a1的地址传递给p的活动记录中对应的形式单元x,执行p的结果是a1值增加1,为2。采用值结果的参数传递机制,调用过程p:把a1的值(此时为1)传给p的活动记录中对应的形式单元x,再把a1的地址传给对应x的作为地址的形式单元add

507、r;执行p的语句之后,x的值增加1为2,它作为x的最终值通过addr送入a1内,而a2的值保持不变。采用换名的参数传递机制,调用过程p把语句+x替换成+(ai),a2设置为6,但没改变a1的值。7.2参数传递机制参数传递机制u换名调用与内联函数换名调用与内联函数l内联展开可以缩短程序的运行时间这是因为,过内联展开可以缩短程序的运行时间这是因为,过程活动的建立包括活动记录的空间分配、机器状程活动的建立包括活动记录的空间分配、机器状态的保存、调用数据的传递以及控制的转移等,态的保存、调用数据的传递以及控制的转移等,都需要占用机器的资源。若过程体较小,则过程都需要占用机器的资源。若过程体较小,则过程

508、调用序列的代码可能会超过过程体本身的代码。调用序列的代码可能会超过过程体本身的代码。如果把过程体的代码内联展开到调用者的代码中,如果把过程体的代码内联展开到调用者的代码中,即使程序的代码会稍长一些,程序的执行速度也即使程序的代码会稍长一些,程序的执行速度也会提高。会提高。l对于诸如对于诸如Java和和C#这样的面向对象语言,内联函这样的面向对象语言,内联函数还可以减少在程序运行时消息和函数体的动态数还可以减少在程序运行时消息和函数体的动态查找和联编。查找和联编。l内联函数产生了更大的代码块,使得代码优化技内联函数产生了更大的代码块,使得代码优化技术的应用更加方便。术的应用更加方便。编译原理与技

509、术编译原理与技术4897.2参数传递机制参数传递机制u换名调用与内联函数换名调用与内联函数例如,下面的例如,下面的C+函数读入一行字符串,逐个判断是函数读入一行字符串,逐个判断是否为数字字符:否为数字字符:isNumber(charch)rerurnch=0&ch=9?1:0;这个函数本身的代码很短,程序频繁调用该函数所花这个函数本身的代码很短,程序频繁调用该函数所花费的时间却很多,从而降低了程序的执行效率。费的时间却很多,从而降低了程序的执行效率。C+提供了内联函数的机制,把上面的函数可以改为:提供了内联函数的机制,把上面的函数可以改为:inlineisNumber(charch)这样就既保

510、证了程序的可读性,又提高了程序的执行这样就既保证了程序的可读性,又提高了程序的执行效率。效率。编译原理与技术编译原理与技术4907.3运行时存储空间的组织和管理运行时存储空间的组织和管理u局部数据的存放局部数据的存放编译对一个过程所声明的局部变量按照它们编译对一个过程所声明的局部变量按照它们的声明顺序,在活动记录的局部数据域中依的声明顺序,在活动记录的局部数据域中依次分配存储空间。次分配存储空间。这些局部数据的地址可以相对于一个特殊的这些局部数据的地址可以相对于一个特殊的位置,譬如活动记录的开始地址,用相对地位置,譬如活动记录的开始地址,用相对地址来表示。址来表示。相对地址(偏移)就是数据对象

511、和这个基准相对地址(偏移)就是数据对象和这个基准位置的地址差。活动记录中其它域的访问也位置的地址差。活动记录中其它域的访问也可以用相对于这个位置的相对地址来处理。可以用相对于这个位置的相对地址来处理。编译原理与技术编译原理与技术4917.3运行时存储空间的组织和管理运行时存储空间的组织和管理u运行时存储空间的划分代码运行时存储空间的划分代码为了使目标代码能够运行,编译程序必须从系统中申请一块存为了使目标代码能够运行,编译程序必须从系统中申请一块存储区域,分配给目标代码。储区域,分配给目标代码。典型的计算机存储包括快速运算域访问的寄存器区域和比较慢典型的计算机存储包括快速运算域访问的寄存器区域和

512、比较慢的随机访问存储(的随机访问存储(RAM),而),而RAM又可以划分成为代码区和数又可以划分成为代码区和数据区。据区。对大多数需要编译的语言,程序运行对大多数需要编译的语言,程序运行的时候不能改变程序的存储区域,而且,的时候不能改变程序的存储区域,而且,代码区域和数据区域在概念上被认为是代码区域和数据区域在概念上被认为是分离的。分离的。因为代码区域在运行前就已经确定,所因为代码区域在运行前就已经确定,所有的代码地址在编译时都可以计算出来。有的代码地址在编译时都可以计算出来。编译原理与技术编译原理与技术492过程1的代码过程2的代码.过程n的代码过程1的入口点过程2的入口点过程n的入口点.图

513、7.8代码区域7.3运行时存储空间的组织和管理运行时存储空间的组织和管理u运行时存储空间的划分整个程序运行时存储空间的划分整个程序静态数据可以象代码一样在编译时刻分配地址。静态数据可以象代码一样在编译时刻分配地址。动态数据的典型组织包括把存储空间划分为栈式数据区域和堆动态数据的典型组织包括把存储空间划分为栈式数据区域和堆式数据区域。式数据区域。存储分配的重要单元是过程的活动记录。根据语言特性,活动存储分配的重要单元是过程的活动记录。根据语言特性,活动记录可以分配在静态区域(如记录可以分配在静态区域(如FORTRAN)、栈式数据区()、栈式数据区(C和和Pascal)或者堆式数据区()或者堆式数

514、据区(Lisp语言)语言)。编译原理与技术编译原理与技术493图7.9运行时存储空间的划分代码区域全局/静态数据区域栈式数据区域堆式数据区域7.3运行时存储空间的组织和管理运行时存储空间的组织和管理u存储分配要考虑的问题存储分配要考虑的问题过程能否递归;过程能否递归;当控制从过程的活动返回时,是否需要保留局部变量当控制从过程的活动返回时,是否需要保留局部变量的值;的值;过程能否访问非局部变量,如何有效地访问;过程能否访问非局部变量,如何有效地访问;过程调用时形参和实参的传递方式;过程调用时形参和实参的传递方式;过程能否作为参数传递和结果返回;过程能否作为参数传递和结果返回;存储区域能否在程序控

515、制下动态地分配;存储区域能否在程序控制下动态地分配;存储区域能否在程序控制下显示地回收。存储区域能否在程序控制下显示地回收。编译原理与技术编译原理与技术4947.3运行时存储空间的组织和管理运行时存储空间的组织和管理3种常见的存储分配策略种常见的存储分配策略静态存储分配、栈式动态存储分配和堆式动态分配存储静态存储分配、栈式动态存储分配和堆式动态分配存储编译原理与技术编译原理与技术495静态存储分配在程序编译的时后就把数据对象分配在固定的存储单元,且在运行时始静态存储分配在程序编译的时后就把数据对象分配在固定的存储单元,且在运行时始终保持不变。语言要求不含递归过程,不允许体积改变的数据对象和待定

516、性质的名字。终保持不变。语言要求不含递归过程,不允许体积改变的数据对象和待定性质的名字。FORTRAN语言的编译程序可以采用静态存储分配策略。语言的编译程序可以采用静态存储分配策略。栈式动态存储分配栈式动态存储分配存储器作为一个栈来管理,每当一个过程被激活和调用,程序就动存储器作为一个栈来管理,每当一个过程被激活和调用,程序就动态地为这个过程在栈的顶部分配存储区域,一旦过程执行结束,就释放所占用的存储态地为这个过程在栈的顶部分配存储区域,一旦过程执行结束,就释放所占用的存储空间。这种策略适合那些允许过程嵌套的语言,如空间。这种策略适合那些允许过程嵌套的语言,如Pascal、Ada等。等。堆式动

517、态存储分配则在程序运行时把存储器作为一个堆来管理,以便程序管理存储的申请和回收。对于允许递归过程的Pascal、Ada、C、C#和Java等语言,编译无法预先确定哪些递归过程在运行时被激活,也不能确定它们的递归深度,而对每个过程都需要为其活动记录分配新的存储空间。此外,程序员在程序中动态地申请和释放存储空间,存储空间申请和释放的顺序随意,并不遵守先申请先释放的队列原则或后先请后释放栈的原则。因此,这些语言还需要采用更复杂的动态堆式存储分配策略。7.4静态运行时环境静态运行时环境FORTRAN是这种分配策略的典型语言。是这种分配策略的典型语言。在这种运行时环境中,不仅全局变量、而是所有变量都可以

518、在编译时在这种运行时环境中,不仅全局变量、而是所有变量都可以在编译时分配存储。此外,每个过程只有一个活动记录,它可以在程序运行前分配存储。此外,每个过程只有一个活动记录,它可以在程序运行前静态地分配存储。所有变量,无论是全局的还是局部的,都可以通过静态地分配存储。所有变量,无论是全局的还是局部的,都可以通过固定的地址访问。固定的地址访问。编译原理与技术编译原理与技术496主程序的代码过程1的代码.过程n的代码全局数据区域主程序的活动记录过程1的活动记录.过程n的活动记录代码区域数据区域图7.10静态存储分配结构在这种运行时环境中,活动记录的信息簿记工作和调用序列十分简单。调用过程的时候,就计算

519、每个实参并不它们放到被调用过程活动记录中相应的形参的位置,然后保留调用者的返回地址,生成一个到被调用过程起始的跳转指令。返回时就需要一个简单的到返回地址的跳转指令。7.4静态运行时环境静态运行时环境编译原理与技术编译原理与技术497例例7.3图7.11是一个FORTRAN程序,它计算并打印一个表中实数的平均值。它只有一个主程序和一个子程序QUADMEAN,唯一一个全局变量是分别在主程序和QUADMEAN声明的MAXSIZE,程序还调用了库函数SORT。PROGRAMTESTCOMMONMAXSIZEINTEGERMAXSIZEREALTABLE(10),TEMPMAXSIZE=10READ*,

520、TABLE(1),TABLE(2),TABLE(3)CALLQUADMEAN(TABLE,3,TEMP)PRINT*,TEMPENDSUBROUTINQUADMEAN(A,SIZE,QMEAN)COMMONMAXSIZEINTEGERMAXSIZE,SIZEREALA(SIZE),QMEAN,TEMPINTEGERKTEMP=0.0IF(SIZE.GT.MAXSIZE).OR.(SIZE.LT.1)GOTO99DO10K=1,SIZE10TEMP=TEMP+A(K)*A(K)99CONTINUEQMEAN=SORT(TEMP/SIZE)RETURNEND7.4静态运行时环境静态运行时环境u这个

521、程序的运行时环境可以如图这个程序的运行时环境可以如图7.12所示。所示。其中箭头连线表示过程其中箭头连线表示过程QUADMEAN的三的三个参数个参数A、SIZE和和QMEAN的值在调用过的值在调用过程中从主程序拷贝得到,这是由于程中从主程序拷贝得到,这是由于FORTRAN语言的参数传递是隐含的存储语言的参数传递是隐含的存储引用,所以把它们的地址赋值到被调用的引用,所以把它们的地址赋值到被调用的QUADMEAN中。中。uQUADMEAN活动记录中的临时变量用来活动记录中的临时变量用来存放计算存放计算TEMP+A(K)*A(K)或者或者TEMP/SIZE的值。如果没有足够的寄存器的值。如果没有足够

522、的寄存器来存放临时值,或者有一个调用要求保留来存放临时值,或者有一个调用要求保留这个值,那么,在完成计算之前要把该值这个值,那么,在完成计算之前要把该值存储在活动记录中。存储在活动记录中。u编译程序根据目标机器的体系结构(如寄编译程序根据目标机器的体系结构(如寄存器的个数和使用规则)可以确定运行时存器的个数和使用规则)可以确定运行时是否需要存放临时变量的存储,并安排大是否需要存放临时变量的存储,并安排大小合适的临时存储单元。小合适的临时存储单元。编译原理与技术编译原理与技术498全局数据主程序的活动记录QUADMEAN的活动记录MAXSIZETEMP3ASIZEQMEAN返回地址TEMPK临时

523、变量TABLE(1)TABLE(2)TABLE(10)7.5栈式运行时环境栈式运行时环境如果一个语言允许过程递归调用,而且每次如果一个语言允许过程递归调用,而且每次调用都重新分配局部变量,活动记录就不可调用都重新分配局部变量,活动记录就不可能静态地分配。能静态地分配。这样,活动记录可以基于栈结构进行分配,这样,活动记录可以基于栈结构进行分配,每一个新的过程调用都需要把一个新的活动每一个新的过程调用都需要把一个新的活动记录放在栈顶,在过程执行退出以后就再回记录放在栈顶,在过程执行退出以后就再回收这个活动记录的存储单元。收这个活动记录的存储单元。活动记录栈(又叫运行栈或调用栈)在程序活动记录栈(又

524、叫运行栈或调用栈)在程序的执行过程中随着调用链增长和缩减。的执行过程中随着调用链增长和缩减。又分成无过程嵌套的栈式运行时环境又分成无过程嵌套的栈式运行时环境和有过和有过程嵌套的栈式运行时环境。程嵌套的栈式运行时环境。编译原理与技术编译原理与技术4997.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境在这类语言中,例如在这类语言中,例如C,过程都是全局过程,不允许,过程都是全局过程,不允许嵌套,但是允许递归调用。这样的运行时环境需要两嵌套,但是允许递归调用。这样的运行时环境需要两类信息:类信息:维护指向当前活动记录的指针以便访问局部变量维护指向当前活动记录的指针以便访问局部变量;记录

525、直接调用者的位置或大小以便当前调用结束后恢复调记录直接调用者的位置或大小以便当前调用结束后恢复调用者的活动记录。用者的活动记录。指向当前活动记录的指针通常称为帧指针指向当前活动记录的指针通常称为帧指针sp,保存在,保存在一个寄存器中(就叫一个寄存器中(就叫sp)。)。前一个活动记录通常用一个存放在当前活动记录中称前一个活动记录通常用一个存放在当前活动记录中称为控制链的指针访问。这个指针又叫动态链,因为它为控制链的指针访问。这个指针又叫动态链,因为它是在程序运行时指向调用程序的活动记录;又称为老是在程序运行时指向调用程序的活动记录;又称为老的帧指针,因为它代表的了的帧指针,因为它代表的了sp的前

526、一个值。的前一个值。编译原理与技术编译原理与技术5007.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境编译原理与技术编译原理与技术501例例7.3一个计算两个非负数最大公因子的欧一个计算两个非负数最大公因子的欧几理德算法的递归程序几理德算法的递归程序gcd,代码如图,代码如图7.13(a)。假设输入了15和10,main开始调用gcd(15,10),它的第二次调用gcd(10,5),接着是第三次调用gcd(5,0)并且返回5。第三次调用的运行时环境如图7.13(b)所示。每次调用gcd都在栈顶新增一个大小一样的活动记录,而且在这个活动记录中都有控制链指向调用它的前一个活动记录。帧

527、指针fp指向当前活动记录的控制链,这样,在下一次调用时(比如第4次调用),这个fp就成为下一个活动记录的控制链的目的地址。每次调用gcd完成过程体的动作之后,每个gcd的活动记录都从栈顶移出。当执行main中打印语句pringf的时候,存储器中只剩下main的活动记录和全局/静态数据。#includeintm,n;intgcd(intu,v)if(v=0)returnu;elsereturngcd(v,u%v);main()scanf(“%d%d”,&m,&n);printf(“%dn”,gcd(m,n);return7.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境编译原理与技术

528、编译原理与技术502m:15n:10u:15v:10控制链返回地址u:10v:5控制链返回地址u:5v:0控制链返回地址自由空间全局/静态区域main的活动记录第1次调用gcd时的活动记录第2次调用gcd时的活动记录第3次调用gcd时的活动记录sp假设输入了假设输入了15和和10,main开始调用开始调用gcd(15,10),它的第二次调用,它的第二次调用gcd(10,5),接着是第三次调用,接着是第三次调用gcd(5,0)并且返并且返回回5。第三次调用的运行时环境如图。第三次调用的运行时环境如图7.13(b)所示。每次调用所示。每次调用gcd都在栈顶新都在栈顶新增一个大小一样的活动记录,而且

529、在这增一个大小一样的活动记录,而且在这个活动记录中都有控制链指向调用它的个活动记录中都有控制链指向调用它的前一个活动记录。帧指针前一个活动记录。帧指针fp指向当前活指向当前活动记录的控制链,这样,在下一次调用动记录的控制链,这样,在下一次调用时(比如第时(比如第4次调用),这个次调用),这个fp就成为下就成为下一个活动记录的控制链的目的地址。一个活动记录的控制链的目的地址。每次调用每次调用gcd完成过程体的动作之后,每完成过程体的动作之后,每个个gcd的活动记录都从栈顶移出。的活动记录都从栈顶移出。当执行当执行main中打印语句中打印语句pringf的时候,的时候,存储器中只剩下存储器中只剩下

530、main的活动记录和全局的活动记录和全局/静态数据。静态数据。fp7.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境u名字的访问名字的访问在栈式环境中,参数和局部变量不能通过固在栈式环境中,参数和局部变量不能通过固定的地址访问,而必须依据它们和当前活动定的地址访问,而必须依据它们和当前活动记录的位差来定位。记录的位差来定位。在大多数语言中,过程声明在编译时确定并在大多数语言中,过程声明在编译时确定并且分配给每个局部声明的存储大小,按照数且分配给每个局部声明的存储大小,按照数据类型是固定的,所以,每个局部声明的位据类型是固定的,所以,每个局部声明的位差可以由编译程序计算出来。差可以由

531、编译程序计算出来。编译原理与技术编译原理与技术5037.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境编译原理与技术编译原理与技术504例例7.4考虑下列C程序voidf(intx,charc)inta10;doubley;调用过程f的活动记录如图所示。假设整型占2个字节,地址用4个字节,字符型用1个字节,双精度浮点型占8个字节,栈向下增长,那么,就可以在编译时刻得到每个局部变量的位移值。这样,假访问ai就需要按照下列公式计算地址:(242i)(fp)。根据i的位置和机器的体系结构,这样的存储访问可能只需一条机器指令。xc控制链返回地址a9a8.a0yx的偏移量y的偏移量a的偏移量

532、c的偏移量fp图7.14例7.4调用过程f的活动记录名字偏移量x+5c+4a-24y-327.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境u临时局部量临时局部量临时变量是在整个过程调用中存放计算的中间结果的临时变量是在整个过程调用中存放计算的中间结果的变量。变量。例如,例如,C语言的表达式语言的表达式ai=(i+j) (i/k+f(j),在自左向,在自左向右的计算中需要临时存储下列结果:右的计算中需要临时存储下列结果:ai的地址、和的地址、和i+j以及以及i/k。这些临时结果可以存放在寄存器中,或者在调用这些临时结果可以存放在寄存器中,或者在调用f以以前存放在运行栈的作的临时单元

533、。前存放在运行栈的作的临时单元。由于临时变量的个数和类型可以事先确定,它们的访由于临时变量的个数和类型可以事先确定,它们的访问也可以在编译时按照局部变量的方式进行。问也可以在编译时按照局部变量的方式进行。编译原理与技术编译原理与技术5057.5.1无过程嵌套的栈式运行时环境无过程嵌套的栈式运行时环境u嵌套声明嵌套声明编译原理与技术编译原理与技术506例例7.5考虑C语言的代码:viodp(intx,doubley)chara;inti;A:doublex;intj;B:char*a;intk;在上面的代码中,位于块A上的变量j到过程p的fp的偏移量是17,块B中k的偏移量是13(假设整型数占2

534、个字节,地址占4个字节,字符型占1个字节,双精度浮点型占8个字节)。xy控制链返回地址aixj自由区域调用p时的活动记录为 块 A分配的区域spfpxy控制链返回地址aiak自由区域调用p时的活动记录为块B分配的区域spfp图7.12(a)进入过程p中块A的活动记录图7.15(b)进入过程p中块B的活动记录7.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境编译原理与技术编译原理与技术507例例7.6考虑下面的Pascal语言的程序。programnonLocalRef;procedurep;varn:integer;procedureq;begin(*对变量n的任何引用就是非局部、

535、非全局的*)end;procedurer(n:integer);beginq;end;begin(*开始过程q*)n:=1;r(2);end;begin(*开始主程序*)p;end;在Pascal、Ada等语言中,由于过程允许嵌套声明,非局部名字不一定就是全局的。如果程序语言允许过程声明的嵌套,那么,截至目前所描述的运行时环境由于没有提供访问非局部、非全局名字的手段,其效率而显得不高。7.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境编译原理与技术编译原理与技术508调用过程q时的运行栈如图7.16所示.在q内任何对n的引用都必须引用p的局部变量n。但是,在这个程序中,仅仅依靠如图

536、所示的运行栈,编译不能通过确定的偏移量找到在q内引用的n的具体声明,因为n在过程r和p中的偏移量不同。因此还需要区别名字作用域的定义规则。自由空间返回地址n:1n:2返回地址返回地址主程序的活动记录调用p时的活动记录调用r时的活动记录调用q时的活动记录fpsp控制链控制链控制链图7.16Pascal程序没有访问链的运行栈7.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境u用静态链访问非局部量用静态链访问非局部量解决上述静态作用域问题的途径是在每个活解决上述静态作用域问题的途径是在每个活动记录中增加访问链(又称为静态链)来记动记录中增加访问链(又称为静态链)来记录额外的信息。录额外的

537、信息。访问链指向过程定义环境的活动记录,即指访问链指向过程定义环境的活动记录,即指向直接外层的最新的活动记录,而不是调用向直接外层的最新的活动记录,而不是调用环境的活动记录。环境的活动记录。编译原理与技术编译原理与技术5097.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境编译原理与技术编译原理与技术510图7.17增加了访问链,这样,在过程q内对非局部量n的引用将按照访问链到过程p,通过固定的偏移量找到。如果把访问链载入一个寄存器,则可以方便地用一条指令实现非局部变量的访问。例如,若寄存器r用作访问链,地址占用4个字节,则它在q中的值是4(fp),那么,在q中就可以按照地址6(r

538、)访问到在过程p中定义的变量n。注意,由于过程p是全局过程,所以它的活动记录中无需访问链。在p中对任何非局部量的引用都可以按照全局了的访问机制实现。图7.17Pascal程序带访问链的运行栈自由空间返回地址n:1n:2返回地址返回地址主程序的活动记录调用p时的活动记录调用r时的活动记录调用q时的活动记录fpsp控制链控制链控制链无访问链访问链访问链7.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境编译原理与技术编译原理与技术511programsort(input,output)vara:array0.10ofinteger;x:integer;procedurereadarray

539、;vari:integer;beginaiendreadarray;procedureexchange(i,j:integer);beginx:=ai;ai:=aj;aj:=xendexchange;procrdurequicksort(m,n:integer);vark,v:integer;functionpartition(y,z:integer):integervari,j,x,y:integer;beginavexchange(i,j);endpartition;beginif(nm)thenbegini=partition(m,n);quicksort(m,i1);quicksort

540、(i+1,n);end;endquicksort;begina0:=9999;a10:=9999;readarray;quicksort(1,9);endsort;图7.18有过程嵌套Pascal语言的快速排序程序7.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境编译原理与技术编译原理与技术512按照最近的嵌套规则,在partition中出现的a的声明在主程序中。同样,在partition中调用的过程exchange对partition而言也是非局部的,它的定义首先quicksort在找,如果没有,则在其外部过程sort中寻找。图7.19显示了程序运行时栈的简化情形,每个活动记录占

541、三行:参数、访问链(sort没有访问链)以及局部变量分别依次放在一行。无访问链a,xm,n访问链k,vsort的活动记录quicksort(1,9)的活动记录图7.19(a)主程序sort调用quicksort无访问链a,xm,n访问链k,vsort的活动记录quicksort(1,9)的活动记录m,n访问链k,vy,z访问链i,jquicksort(1,3)的活动记录partition(1,3)的活动记录图7.19(b)quicksort第一次递归调用partition,执行exchange调用前sort的活动记录quicksort(1,9)的活动记录quicksort(1,3)的活动记录p

542、artition(1,3)的活动记录exchange(1,3)的活动记录无访问链a,xm,n访问链k,vm,n访问链k,vy,z访问链i,ji,j访问链图7.19(c)quicksort第一次递归调用partition,执行exchange调用7.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境u过程过程quicksort的每个活动记录以及的每个活动记录以及exchange的活动记录都有访问链指向主程序的活动记录都有访问链指向主程序sort,以便访问非局部数组以便访问非局部数组a和变量和变量x。特别地,在图。特别地,在图7.19(c)中,过程)中,过程partition活动记录活动记

543、录的访问链指向最靠近的的访问链指向最靠近的quicksort的活动记录,即的活动记录,即quicksort(1,3)。对于过程。对于过程partition,可以通过一次访问链在最近的外围,可以通过一次访问链在最近的外围quicksort中找到中找到v的数值;而要找到变量的数值;而要找到变量ai的的值,则首先需要通过访问链找到值,则首先需要通过访问链找到quicksort,其中没有,其中没有a的声明,则继续延着的声明,则继续延着quicksort的访问链找到的访问链找到sort,通过偏移量可以访问,通过偏移量可以访问ai的值。的值。u为了实现这种嵌套的静态作用域,编译程序在局部地访问名字以前必须

544、能够决定访为了实现这种嵌套的静态作用域,编译程序在局部地访问名字以前必须能够决定访问链的嵌套层次。为此,需要引入声明或过程的嵌套层次或嵌套深度的概念。通常问链的嵌套层次。为此,需要引入声明或过程的嵌套层次或嵌套深度的概念。通常定义最外层作用域(定义最外层作用域(PascalPascal语言的主程序层、语言的主程序层、C C语言的外部作用域)的嵌套深度为语言的外部作用域)的嵌套深度为0 0;编译时刻每进入一个过程,层数就加;编译时刻每进入一个过程,层数就加1 1,过程退出时,层数就减,过程退出时,层数就减1 1。例如,图。例如,图7.197.19中,过程中,过程sortsort的层数为的层数为0

545、 0,变量,变量a a和和x x的层数是的层数是1 1,因为它们在过程,因为它们在过程sortsort内部。同样,内部。同样,过程过程quicksortquicksort的层数是的层数是1 1,过程变量,过程变量k k和和v v以及过程以及过程partitionpartition的层数是的层数是2 2;partitionpartition内变量的层数是内变量的层数是3 3,exchangeexchange的层数也是的层数也是3 3。u如果引入了嵌套层数,那么访问一个非局部量所需要的链接个数就是调用点的层数如果引入了嵌套层数,那么访问一个非局部量所需要的链接个数就是调用点的层数减去名字的声明层数

546、。例如,减去名字的声明层数。例如,partitionpartition内部引用非局部量内部引用非局部量a a和和v v的嵌套层数分别是的嵌套层数分别是1 1和和2 2,追踪声明这些变量活动记录的访问链的次数分别就是,追踪声明这些变量活动记录的访问链的次数分别就是3 31 12 2和和3 32 21 1。编译原理与技术编译原理与技术5137.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境实现访问链的调用序列相当简单。实现访问链的调用序列相当简单。过程调用时,访问链在过程调用时,访问链在fp之前存入当前活动记录中;之前存入当前活动记录中;过程退出时,除了删除访问链和参数以外,还必须调过

547、程退出时,除了删除访问链和参数以外,还必须调整整sp的值。的值。主要问题是在过程调用时如何找到访问链。这可以使主要问题是在过程调用时如何找到访问链。这可以使用在编译时附加在被调用过程中声明的嵌套层数来实用在编译时附加在被调用过程中声明的嵌套层数来实现。现。事实上,需要做的就是生成一条访问序列,就像要访事实上,需要做的就是生成一条访问序列,就像要访问被调用过程中变量一样访问同一嵌套层中的变量,问被调用过程中变量一样访问同一嵌套层中的变量,这样计算出来的地址就是访问链。当然,如果过程是这样计算出来的地址就是访问链。当然,如果过程是局部的(差别在局部的(差别在0层),那么,访问链和控制链就相层),那

548、么,访问链和控制链就相同,等于调用点的同,等于调用点的fp的值。的值。编译原理与技术编译原理与技术5147.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境u用嵌套显示表(用嵌套显示表(display)访问非局部量)访问非局部量如果非局部量的嵌套层次很大的时候,由于需要执行很长的操如果非局部量的嵌套层次很大的时候,由于需要执行很长的操作序列才能找到它的声明处,用访问链接来访问非局部量的方作序列才能找到它的声明处,用访问链接来访问非局部量的方式看起来缺乏效率。式看起来缺乏效率。为了提高访问非局部量的速度,可以采用一个指针数组,称为为了提高访问非局部量的速度,可以采用一个指针数组,称为嵌

549、套层次显示表(嵌套层次显示表(display)。)。每进入一个过程后,就在它的活动记录中建立一个每进入一个过程后,就在它的活动记录中建立一个display表,表,表元素的个数就是该过程嵌套层数加表元素的个数就是该过程嵌套层数加1。display本身采用栈的结本身采用栈的结构,自顶向下依次存放当前层、直接外层,构,自顶向下依次存放当前层、直接外层,直至最外层,直至最外层(0层,主程序)等的最新活动记录的基地址。层,主程序)等的最新活动记录的基地址。由于过程的层数可以静态地确定,所以,每个过程由于过程的层数可以静态地确定,所以,每个过程display表的表的体积可以在编译时刻确定。这样,从一个非局

550、部声明所在的静体积可以在编译时刻确定。这样,从一个非局部声明所在的静态层数和相对于活动记录的偏移,就可以得到该非局部量的绝态层数和相对于活动记录的偏移,就可以得到该非局部量的绝对地址:对地址:非局部量的绝对地址非局部量的绝对地址=display嵌套层数嵌套层数+偏移偏移offset编译原理与技术编译原理与技术5157.5.2有过程嵌套的栈式运行时环境有过程嵌套的栈式运行时环境编译原理与技术编译原理与技术516图7.20把图7.19的程序运行时的环境用display表描述出来。在每个活动记录中访问链的位置上存放一个display表,它的大小可以在编译时确定。图中仅仅在当前活动记录中列出了disp

551、lay表的所有元素,每个写成i-display,表示指向第i层活动记录的指针。在当前活动记录中还有一个指针指向调用当前过程的最近的活动记录,以便过程结束后可以找到当前过程的直接调用者的display表。图7.20(a)主程序sort调用quicksort图7.20(b)quicksort第一次递归调用partition,执行exchange调用前图7.20(c)quicksort第一次递归调用partition,执行exchange调用 0-displaya,xm,nk,vsort的活动记录quicksort(1,9)的活动记录 0-display 1-display nextdisplays

552、ort的活动记录quicksort(1,9)的活动记录quicksort(1,3)的活动记录partition(1,3)的活动记录exchange(1,3)的活动记录0-displaya,xm,ndisplay表k,vm,ndisplay表k,vy,zdisplay表i,ji,jnext-display 3-display 0-display 1-display 2-display0-displaya,xm,ndisplay表k,vm,ndisplay表sort的活动记录quicksort(1,9)的活动记录k,vy,znextdisplayi,jquicksort(1,3)的活动记录part

553、ition(1,3)的活动记录 0-display 1-display 2-display7.6堆式运行时环境堆式运行时环境u栈式运行时环境的局限性栈式运行时环境的局限性悬挂引用悬挂引用一个最简单的情况就是要返回局部变量的地址如下列一个最简单的情况就是要返回局部变量的地址如下列C代码:代码:int*dangle()intx;return&x;赋值语句赋值语句addrdangle()就可能造成就可能造成addr指向活动记录中一个不安全的位置,指向活动记录中一个不安全的位置,其值可能被随后的任何调用随意改变。其值可能被随后的任何调用随意改变。垃圾收集垃圾收集l程序员动态地、没有时间限制地申请数据空

554、间和程序员动态地、没有时间限制地申请数据空间和回收数据空间,如指针和对象,因此,这些语言回收数据空间,如指针和对象,因此,这些语言的运行时环境也需要处理指针的分配和回收。的运行时环境也需要处理指针的分配和回收。编译原理与技术编译原理与技术5177.6堆式运行时环境堆式运行时环境u堆式动态存储分配的实现堆式动态存储分配的实现堆式存储分配的实现可以按照定长块和变长块进行。堆式存储分配的实现可以按照定长块和变长块进行。初始时,将堆存储空间分成大小相等的若干存储块,每块制订一个链域,按照初始时,将堆存储空间分成大小相等的若干存储块,每块制订一个链域,按照相邻块的顺序把所有块链成一个链表,用指针相邻块的

555、顺序把所有块链成一个链表,用指针avaiable指向链表中的第一块。每指向链表中的第一块。每次分配时都首先分配指针次分配时都首先分配指针avaiable所指的块,然后所指的块,然后avaiable指向邻接的下一块,指向邻接的下一块,如图如图7.21(a)所示。所示。回收后,把归还的块插入链表,如图回收后,把归还的块插入链表,如图7.21(b)所示。所示。编译原理与技术编译原理与技术518占用占用占用空闲空闲空闲(a)avaiable占用空闲占用空闲空闲空闲(b)avaiable7.6堆式运行时环境堆式运行时环境u变长块存储管理变长块存储管理需要分配长度不同的存储块,可以随请求而变。若空闲块链表

556、中有不止一个可需要分配长度不同的存储块,可以随请求而变。若空闲块链表中有不止一个可以满足需要的空闲块时,通常有三种不同的分配策略。以满足需要的空闲块时,通常有三种不同的分配策略。1.次满足法:只要在空闲块链表中找到可以满足需要的一个空闲块,就进行分配。次满足法:只要在空闲块链表中找到可以满足需要的一个空闲块,就进行分配。如果该块很大,则按申请的大小分割,剩余的部分作为空闲块仍然留在空闲块如果该块很大,则按申请的大小分割,剩余的部分作为空闲块仍然留在空闲块链表中;如果该块比需要的不是大出很多,则把整个块分配出去,以免在空闲链表中;如果该块比需要的不是大出很多,则把整个块分配出去,以免在空闲块链表

557、中留下许多无用的小碎块。块链表中留下许多无用的小碎块。2.最优满足法:这个方法通常将空闲块链表中的空闲块按照块的体积从小到大排最优满足法:这个方法通常将空闲块链表中的空闲块按照块的体积从小到大排序。分配时只需将空闲块链表中第一个大于申请块大小的空闲块分配,这就是序。分配时只需将空闲块链表中第一个大于申请块大小的空闲块分配,这就是一个不小于申请块、但又最接近申请块大小的空闲块。当然,在回收时也需要一个不小于申请块、但又最接近申请块大小的空闲块。当然,在回收时也需要将释放的空闲块插入到链表的适当位置上。将释放的空闲块插入到链表的适当位置上。3.最差满足法:这个方法就分配是一个不小于申请块、但又是最

558、大的空闲块。通最差满足法:这个方法就分配是一个不小于申请块、但又是最大的空闲块。通常将空闲块链表中的空闲块按照块的体积从大到小排序。分配时只需将空闲块常将空闲块链表中的空闲块按照块的体积从大到小排序。分配时只需将空闲块链表中的第一个空闲块删除,将其中的一部分分配出去,而其它部分作为一个链表中的第一个空闲块删除,将其中的一部分分配出去,而其它部分作为一个新的空闲块插入到链表的适当位置上。当然,在回收时也需要将释放的空闲块新的空闲块插入到链表的适当位置上。当然,在回收时也需要将释放的空闲块插入到链表的适当位置上。插入到链表的适当位置上。编译原理与技术编译原理与技术5197.6.2堆的自动管理堆的自

559、动管理使用使用malloc和和free执行指针的动态分配和回收是堆管执行指针的动态分配和回收是堆管理的手工方式,因为程序员需要明显地编写这些函数理的手工方式,因为程序员需要明显地编写这些函数的调用语句。与之相对应,运行栈由调用序列自动地的调用语句。与之相对应,运行栈由调用序列自动地管理。管理。自动存储管理不需要程序员的干预,回收已经分配、自动存储管理不需要程序员的干预,回收已经分配、而且尽可能长时间不再使用的存储。这个过程需要操而且尽可能长时间不再使用的存储。这个过程需要操作系统、机器体系结构方面的支持,并且由运行系统作系统、机器体系结构方面的支持,并且由运行系统来决定何时和如何执行无用单元的

560、收集,这个过程也来决定何时和如何执行无用单元的收集,这个过程也称为垃圾收集(无用单元回收)。称为垃圾收集(无用单元回收)。由于无需程序员在程序代码中编写语句,它完全由编由于无需程序员在程序代码中编写语句,它完全由编译程序及其它系统完成的,所以又称为隐式回收。在译程序及其它系统完成的,所以又称为隐式回收。在大多数面向对象语言、函数式语言和逻辑式语言中,大多数面向对象语言、函数式语言和逻辑式语言中,存储单元的自动分配和回收应用的更加广泛。存储单元的自动分配和回收应用的更加广泛。编译原理与技术编译原理与技术5207.6.2堆的自动管理堆的自动管理u垃圾收集的不同技术垃圾收集的不同技术垃圾集合的两个比

561、较现实的近义词是垃圾集合的两个比较现实的近义词是“没有没有指针指向的存储块的集合指针指向的存储块的集合”和和“从堆式分配从堆式分配程序数据中不可达的存储块的集合程序数据中不可达的存储块的集合”。这两类集合中的数据不可能被程序使用。这两类集合中的数据不可能被程序使用。“无指针无指针”引出了一项称为引用计数的垃圾引出了一项称为引用计数的垃圾回收技术,使用回收技术,使用“不可达不可达”标准的回收技术标准的回收技术是标记和清扫、双空间复制以及分代垃圾收是标记和清扫、双空间复制以及分代垃圾收集。集。编译原理与技术编译原理与技术5217.6.2堆的自动管理堆的自动管理u引用计数引用计数引用计数是一个很直观

562、的垃圾收集引用计数是一个很直观的垃圾收集算法,它在每个存储块中增加一个算法,它在每个存储块中增加一个引用计数,记录指向该块的指针数引用计数,记录指向该块的指针数目(引用的次数)。每当从堆中分目(引用的次数)。每当从堆中分配一个存储块的时候,它的引用计配一个存储块的时候,它的引用计数就初始化为数就初始化为1。只要存储块的应。只要存储块的应用被复制,它的引用计数就加用被复制,它的引用计数就加1(递增)。同样,每当存储块的引(递增)。同样,每当存储块的引用被删除,它的引用计数就减用被删除,它的引用计数就减1(递减)。当该数目下降到(递减)。当该数目下降到0时就时就声明该块为无用而可以收回。声明该块为

563、无用而可以收回。引用计数的实现需要跟踪所有的引引用计数的实现需要跟踪所有的引用操作以及递归地回收应用计数为用操作以及递归地回收应用计数为0的存储块。引用计数的垃圾收集的存储块。引用计数的垃圾收集技术存在效率等问题,但是它在管技术存在效率等问题,但是它在管理相对较小的动态存储分配方面是理相对较小的动态存储分配方面是一个非常流行的技术,通常在人工一个非常流行的技术,通常在人工开发的软件中使用,开发的软件中使用,编译原理与技术编译原理与技术522a2程序数据区堆d1b3c2e1f27.6.2堆的自动管理堆的自动管理u标记和清扫技术标记和清扫技术标记和清扫技术首先标记堆上所有可以达到存储块记标记和清扫

564、技术首先标记堆上所有可以达到存储块记录,然后回收未被标记的记录。录,然后回收未被标记的记录。l标记从当前可以访问的指针记录开始,递归地标记所有可以标记从当前可以访问的指针记录开始,递归地标记所有可以达到的存储块记录。达到的存储块记录。l然后线性地清扫存储器,释放没有标记的存储块。这个过程然后线性地清扫存储器,释放没有标记的存储块。这个过程通常包括找到足够的连续的自由存储区域,以满足一系列新通常包括找到足够的连续的自由存储区域,以满足一系列新的存储要求。这是因为,即使经过无用单元收集以后,存储的存储要求。这是因为,即使经过无用单元收集以后,存储器中也可能没有足够大存储块来满足存储请求。因此,无用

565、器中也可能没有足够大存储块来满足存储请求。因此,无用单元收集一般也包括执行存储紧缩(单元收集一般也包括执行存储紧缩(7.6.2.2详细讨论),把详细讨论),把所有已经分配的块移到堆的一端,把未分配的一大块连续自所有已经分配的块移到堆的一端,把未分配的一大块连续自由空间放在存储的另一端。由空间放在存储的另一端。l这个过程必须更新那些在程序执行时已经移动区域的所有引这个过程必须更新那些在程序执行时已经移动区域的所有引用。用。编译原理与技术编译原理与技术5237.6.2堆的自动管理堆的自动管理u停止和拷贝、或双空间复制垃圾停止和拷贝、或双空间复制垃圾通过把可以得到的存储划分成两各相等部分通过把可以得

566、到的存储划分成两各相等部分(源空间和目的空间),并且每次只分配其(源空间和目的空间),并且每次只分配其中的一般存储区域,就可以改进标记和清扫中的一般存储区域,就可以改进标记和清扫技术技术。技术技术。在标记过程就把所有可到达块立即拷贝到没在标记过程就把所有可到达块立即拷贝到没有使用的另一半。这表明无需占用额外标记有使用的另一半。这表明无需占用额外标记字节,而且只需对存储器扫描一遍。而且,字节,而且只需对存储器扫描一遍。而且,它也自动完成了存储清理。一旦在使用区域它也自动完成了存储清理。一旦在使用区域的所有达到块都拷贝完,使用的一半和未用的所有达到块都拷贝完,使用的一半和未用的一半存储相互交错,处

567、理就继续下去。的一半存储相互交错,处理就继续下去。编译原理与技术编译原理与技术5247.6.2堆的自动管理堆的自动管理u分代垃圾收集分代垃圾收集它同双空间复制一样,利用了存储分配和回收的统计它同双空间复制一样,利用了存储分配和回收的统计属性:大多数分配的存储块都几乎很快地不再使用;属性:大多数分配的存储块都几乎很快地不再使用;相反,如果一个存储块已经使用了很长的时间,那么,相反,如果一个存储块已经使用了很长的时间,那么,它很可那个会在以面的某个时刻用到。它很可那个会在以面的某个时刻用到。分代垃圾收集在前面技术的基础上增加了永久存储区分代垃圾收集在前面技术的基础上增加了永久存储区域,所有已经分配

568、的对象,只要生存(被引用)了足域,所有已经分配的对象,只要生存(被引用)了足够长的时间,就拷贝到这个永久存储区域,自随后的够长的时间,就拷贝到这个永久存储区域,自随后的存储回收过程不再释放。这意味着无用单元收集器为存储回收过程不再释放。这意味着无用单元收集器为新的存储分配仅仅需要搜索很小的存储区域,这样的新的存储分配仅仅需要搜索很小的存储区域,这样的搜索时间自然就减少了许多。当然,永久存储区域也搜索时间自然就减少了许多。当然,永久存储区域也可能耗尽,由于临时存储趋向于很快消失,而且已经可能耗尽,由于临时存储趋向于很快消失,而且已经分配的存储趋向于永远使用,这个问题没有以前方法分配的存储趋向于永

569、远使用,这个问题没有以前方法那么严重。那么严重。实践表明,这个方法对虚拟存储系统特别有效。实践表明,这个方法对虚拟存储系统特别有效。编译原理与技术编译原理与技术5257.6.2堆的自动管理堆的自动管理u存储紧缩存储紧缩一种实现是,为每个存储块增加一个额外的指针,指向移动后的新地一种实现是,为每个存储块增加一个额外的指针,指向移动后的新地址,把堆存储器从左到右(或自底向上)三次,第一次扫描计算存储址,把堆存储器从左到右(或自底向上)三次,第一次扫描计算存储块新位置的地址,第二次扫描更新存在的指针使其指向新地址,第三块新位置的地址,第二次扫描更新存在的指针使其指向新地址,第三次扫描实际完成存储块的

570、移动,次扫描实际完成存储块的移动,(1)地址计算:如图)地址计算:如图7.24(a),从左到右扫描存储器,堆每个占用的存,从左到右扫描存储器,堆每个占用的存储块储块B计算出紧缩之后的新地址。由于第一个占用的存储块的地址已知计算出紧缩之后的新地址。由于第一个占用的存储块的地址已知在存储器的低端,而且也知道存储块的大小,所以可以计算出每个存在存储器的低端,而且也知道存储块的大小,所以可以计算出每个存储块的地址,保存在存储块储块的地址,保存在存储块B的管理域中。的管理域中。编译原理与技术编译原理与技术526块的新地址占用块2占用块1占用块1占用块2垃圾块垃圾块旧新7.6.2堆的自动管理堆的自动管理u

571、存储紧缩存储紧缩(2)指针更新:如图)指针更新:如图7.24(b),扫描程序数据区和存储块,寻找堆的指,扫描程序数据区和存储块,寻找堆的指针,把每个存储块针,把每个存储块B的指针更新为它的管理域的值。的指针更新为它的管理域的值。(3)块移动:从低到高扫描存储器,按照存储块中管理域的指针把每)块移动:从低到高扫描存储器,按照存储块中管理域的指针把每一个占用的存储块都移到新的地址。块中的所有指针重新指向紧缩之一个占用的存储块都移到新的地址。块中的所有指针重新指向紧缩之前它所指向的存储块。最后一个占用的存储块之后的存储区就形成了前它所指向的存储块。最后一个占用的存储块之后的存储区就形成了一个连续的空

572、闲块。一个连续的空闲块。编译原理与技术编译原理与技术527块的新地址块的新地址占用块2占用块1占用块1占用块2垃圾块垃圾块旧新空闲占用块2占用块1旧新7.7面向对象语言的动态存储管理面向对象语言的动态存储管理u实现对象的机制实现对象的机制对代码初始化,把所有当前的继承属性和方法直接地复制到记对代码初始化,把所有当前的继承属性和方法直接地复制到记录结构中(将方法当作代码指针)录结构中(将方法当作代码指针)在程序执行的每一时刻把类结构的完整描述保存在存储器中,在程序执行的每一时刻把类结构的完整描述保存在存储器中,由超类的指针维护继承关系。每个对象连同它的实例变量一起由超类的指针维护继承关系。每个对

573、象连同它的实例变量一起保持一个指向定义它的类的指针,通过这个指针可以找到包括保持一个指向定义它的类的指针,通过这个指针可以找到包括局部和继承的所有的方法。这样,方法指针只在类结构中记录局部和继承的所有的方法。这样,方法指针只在类结构中记录一次,而不必为每个对象都复制到存储器中。由于这个机制是一次,而不必为每个对象都复制到存储器中。由于这个机制是通过搜索类结构来找到方法的,所以它也可以用来实现继承和通过搜索类结构来找到方法的,所以它也可以用来实现继承和动态联编动态联编把整个类结构保存在环境中的另一种方法是,对每个类的每一把整个类结构保存在环境中的另一种方法是,对每个类的每一个可用方法计算出代码指

574、针列表,把它们作为虚拟函数表个可用方法计算出代码指针列表,把它们作为虚拟函数表(C+的术语)静态地存入存储器。的术语)静态地存入存储器。编译原理与技术编译原理与技术5287.7面向对象语言的动态存储管理面向对象语言的动态存储管理编译原理与技术编译原理与技术529例例7.7考虑下列的C+类声明:classApublic:doublex,y;voidf();virtualvoidg();classB:publicApublic:doublez;voidf();virtualvoidh();(a)(b)xy虚拟函数表A:gA的虚拟函数表xy虚拟函数表A:gB:hzB的虚拟函数表编译原理与技术编译原理

575、与技术第第8章章属性文法和语义分析属性文法和语义分析主要内容主要内容u语义分析概况语义分析概况u属性与属性文法属性与属性文法u属性的计算属性的计算u数据类型与类型检查数据类型与类型检查编译原理与技术编译原理与技术5318.1语义分析概况语义分析概况语义分析在编译程序中的位置语义分析在编译程序中的位置程序的语义涉及两个方面,即数据结构的语程序的语义涉及两个方面,即数据结构的语义域控制结构的语义。义域控制结构的语义。数据结构的语义主要指域标识符相关联的数数据结构的语义主要指域标识符相关联的数据对象,也即量的含义。控制结构的语义是据对象,也即量的含义。控制结构的语义是语言定义的。语言定义的。编译原理

576、与技术编译原理与技术532语法分析器语义分析器中间代码生成器语法树语法树单词记号中间代码8.1语义分析概况语义分析概况量涉及类型与值,值在程序运行时刻确定,而类型则由程序的量涉及类型与值,值在程序运行时刻确定,而类型则由程序的说明部分来规定。说明部分来规定。例如,例如,intx,y;floatz;chararray100;把把x、y、z和和array分别与整型、整型、实型和字符数组型关联分别与整型、整型、实型和字符数组型关联起来,它们分别代表相应类型的数据对象。起来,它们分别代表相应类型的数据对象。不同类型的数据对象有不同的机器内部表示,占用不同的存储不同类型的数据对象有不同的机器内部表示,占

577、用不同的存储空间,有不同的取值范围,对它们所能进行的运算也不同。只空间,有不同的取值范围,对它们所能进行的运算也不同。只有相同类型、因而具有相同机内表示的数据对象,或符合特定有相同类型、因而具有相同机内表示的数据对象,或符合特定要求的数据对象才能进行相应的运算。要求的数据对象才能进行相应的运算。当考虑标识符的相关含义时还必须要考虑到作用域的问题。当考虑标识符的相关含义时还必须要考虑到作用域的问题。确定标识符所关联的类型、作用域等属性信息,进行类型正确确定标识符所关联的类型、作用域等属性信息,进行类型正确性的检查成为语义分析的一个基本工作。性的检查成为语义分析的一个基本工作。编译原理与技术编译原

578、理与技术5338.1语义分析概况语义分析概况例如,对于例如,对于C的的while循环语句:循环语句:while();规定了首先计算规定了首先计算的值,如果为真的值,如果为真(或非(或非0)时,就执行)时,就执行;然后再计算然后再计算的值,并重复以上过程,的值,并重复以上过程,直到直到的值为假(或为的值为假(或为0),便结束循),便结束循环语句,执行环语句,执行while语句之后的语句。语句之后的语句。语义分析将分析各个语法结构的含义并做出语义分析将分析各个语法结构的含义并做出相应的语义处理。相应的语义处理。编译原理与技术编译原理与技术5348.1语义分析概况语义分析概况u语义分析的基本功能语义

579、分析的基本功能确定类型确定类型确定标识符所关联对象的数据类型。这部分工作有确定标识符所关联对象的数据类型。这部分工作有时由扫描器完成,扫描器将处理源程序的声明部分。时由扫描器完成,扫描器将处理源程序的声明部分。类型检查类型检查按照语言的类型规则,对参加运算的运算分量进行按照语言的类型规则,对参加运算的运算分量进行类型检查,检查运算的合法性、运算分量类型的一致性(相容类型检查,检查运算的合法性、运算分量类型的一致性(相容性),对于不相容的运算对象,报告错误,必要时进行相应的性),对于不相容的运算对象,报告错误,必要时进行相应的类型转换。类型转换。l例如,对于数组变量个函数变量的加法运算额出现,报

580、告语义错例如,对于数组变量个函数变量的加法运算额出现,报告语义错误;对于整型与实型数据对象的加法,把它们转换成同一类型。误;对于整型与实型数据对象的加法,把它们转换成同一类型。控制流检查控制流检查对于任何引起控制流离开一个结构的语句,程序对于任何引起控制流离开一个结构的语句,程序中必须由该控制转移可以转到的地方。中必须由该控制转移可以转到的地方。l例如,例如,C的的break语句引起控制离开最小包围的语句引起控制离开最小包围的while、for或或switch语句,如果这样的包围语句不存在,则是一个错误。语句,如果这样的包围语句不存在,则是一个错误。编译原理与技术编译原理与技术5358.1语义

581、分析概况语义分析概况u语义分析的基本功能语义分析的基本功能唯一性检查唯一性检查有些场合,对象必须正好被定义一次。有些场合,对象必须正好被定义一次。例如,集合中的元素只能出现一次,对象类的名字不例如,集合中的元素只能出现一次,对象类的名字不能重复,分支语句的分情形常量必须区分能重复,分支语句的分情形常量必须区分.l在在Pascal语言中,标识符只能唯一第定义一次。语言中,标识符只能唯一第定义一次。关联名字检查关联名字检查有时,同样的名字必须出现两次或更有时,同样的名字必须出现两次或更多次。多次。l例如,在例如,在C+语言中,构造函数的名字必须和类型一致;在语言中,构造函数的名字必须和类型一致;在

582、Ada语言中,循环或程序块可以有名字出现在其开始和结束,语言中,循环或程序块可以有名字出现在其开始和结束,编译程序必须检查两个地方的名字是否相同。编译程序必须检查两个地方的名字是否相同。识别含义识别含义根据程序语言的形式或非形式语义规则,根据程序语言的形式或非形式语义规则,识别程序中各个构造成分组合到一起的含义,并做相识别程序中各个构造成分组合到一起的含义,并做相应的语义处理,应的语义处理,编译原理与技术编译原理与技术5368.2属性与属性文法属性与属性文法编译原理与技术编译原理与技术537u属性的引入属性的引入为了解释程序的语义、把程序翻译成可执行的代码,需要对为了解释程序的语义、把程序翻译

583、成可执行的代码,需要对文法符号引进一些表示程序语言结构性质的属性。文法符号引进一些表示程序语言结构性质的属性。例如变量数据类型、表达式值、存储地址、过程体代码以及例如变量数据类型、表达式值、存储地址、过程体代码以及数的有效数字个数等数的有效数字个数等.计算属性的值并把它和语言结构联系起来的过程称作属性的计算属性的值并把它和语言结构联系起来的过程称作属性的绑定。属性绑定发生在编译或运行过程的时刻叫做绑定时刻。绑定。属性绑定发生在编译或运行过程的时刻叫做绑定时刻。不同属性的绑定时刻不同,对于不同的语言,甚至同样的属不同属性的绑定时刻不同,对于不同的语言,甚至同样的属性也有不同的绑定时刻。性也有不同

584、的绑定时刻。在程序运行前绑定的属性称为静态的,只能在程序运行期间在程序运行前绑定的属性称为静态的,只能在程序运行期间才能绑定的属性是动态的。才能绑定的属性是动态的。8.2属性与属性文法属性与属性文法u属性的引入属性的引入在在静静态态类类型型语语言言诸诸如如C和和Pascal中中,变变量量或或表表达达式式的的数数据据类类型型是是主主要要的的编编译译时时刻刻的的属属性性,类类型型检检查查器器就就是是一一个个语语义义分分析析器器,它它计计算算语语言言实实体体的的数数据据类类型型属属性并验证这些类型符合语言的类型规则。性并验证这些类型符合语言的类型规则。而而LISP或或Smalltalk中中的的数数据

585、据类类型型是是动动态态的的,它它们们的的编编译译必必须须产产生生计计算算类类型型的的代代码码,然然后后在在程程序序的的运运行行过程中进行类型检查。过程中进行类型检查。表表达达式式的的值值通通常常是是动动态态的的,编编译译只只产产生生在在程程序序运运行行期期间间计计算算表表达达式式的的值值的的代代码码。然然而而,有有些些表表达达式式可可能能是是常常数数,例例如如,3.12*5+10,语语义义分分析析器器可可以以在在编编译的时候计算它们的值。译的时候计算它们的值。编译原理与技术编译原理与技术5388.2属性与属性文法属性与属性文法u属性的引入属性的引入对于不同的语言或者变量自身的性质,变量的存储分

586、对于不同的语言或者变量自身的性质,变量的存储分配可以是静态的、也可以是动态的。由于属性的计算配可以是静态的、也可以是动态的。由于属性的计算依赖与程序的运行环境,甚至时目标机的细节,所以依赖与程序的运行环境,甚至时目标机的细节,所以编译通常把属性的计算推迟到代码生成期间。编译通常把属性的计算推迟到代码生成期间。过程的目标码显然是静态属性。编译的代码产生器全过程的目标码显然是静态属性。编译的代码产生器全权负责这类属性的计算。权负责这类属性的计算。数的有效数字个数这个属性一般不在编译期间处理,数的有效数字个数这个属性一般不在编译期间处理,它隐含在编译程序构造期间对这些数值实现的处理,它隐含在编译程序

587、构造期间对这些数值实现的处理,通常是运行环境的一部分。然而,如果要正确地翻译通常是运行环境的一部分。然而,如果要正确地翻译常数,扫描器也需要知道允许的有效数字的个数。常数,扫描器也需要知道允许的有效数字的个数。编译原理与技术编译原理与技术5398.2属性与属性文法属性与属性文法u属性文法的定义属性文法的定义定定义义8.1如如果果用用X表表示示一一个个文文法法符符号号,a代代表表X的的一一个属性,那么,个属性,那么,X.a代表代表X的关联属性的关联属性a。定义定义8.2对于一组属性对于一组属性a1,a2,.,am和文法和文法G的每个产的每个产生式生式X0X1X2.Xn(X0是非终结符,其它的是非

588、终结符,其它的Xi是是任意符号),意味着每个属性任意符号),意味着每个属性Xi.aj的值都和产生式的值都和产生式中其它属性有关系,每个关系可以表示成属性等式中其它属性有关系,每个关系可以表示成属性等式或语义规则的形式:或语义规则的形式:Xi.aj i(a1,a2,.,am)定义定义8.3属性文法就是对于一组属性属性文法就是对于一组属性a1,a2,.,am和文和文法法G的每个产生式的所有的语义规则,其中文法的每个产生式的所有的语义规则,其中文法G称为基础文法。称为基础文法。编译原理与技术编译原理与技术5408.2属性与属性文法属性与属性文法u属性文法通常写成下列表格形式:属性文法通常写成下列表格

589、形式:语法产生式语法产生式语义规则语义规则X关联的属性等式关联的属性等式编译原理与技术编译原理与技术5418.2属性与属性文法属性与属性文法编译原理与技术编译原理与技术542数的最重要的属性就是它的值,命名为数的最重要的属性就是它的值,命名为val。每个数字都有一个值,可以直接从它表。每个数字都有一个值,可以直接从它表示的实际数字得到。所以,文法规则示的实际数字得到。所以,文法规则digit0意味着在这个情况下意味着在这个情况下digit有有0值,可值,可以表示成属性等式以表示成属性等式digit.val:=0,并把它和文法规则,并把它和文法规则digit0关联在一起。关联在一起。如果运用规则

590、如果运用规则numberdigit得到了数,那么,这个数就值包含一个数字,其值就得到了数,那么,这个数就值包含一个数字,其值就等于这个数字的值,它的属性等式可以表示成等于这个数字的值,它的属性等式可以表示成number.val=digit.val。如果数从文法规则如果数从文法规则numbernumberdigit得到,那么我们必须规则左边符号的值得到,那么我们必须规则左边符号的值和文法规则右边符号的值之间的关系。和文法规则右边符号的值之间的关系。注意,文法规则两边出现的注意,文法规则两边出现的number完全不同(用下标表示),这是由于它们具有不完全不同(用下标表示),这是由于它们具有不同的值

591、。同的值。考虑数考虑数83的最右推导:的最右推导:numbernumberdigitdigitdigitdigit383在第一步使用的文法规则在第一步使用的文法规则number1number2digit,其中,其中number2表示数字表示数字8,而而digit对应对应3,它们的值分别是,它们的值分别是8和和3。为了得到。为了得到number1的值的值83,必须使用的下列,必须使用的下列计算:计算:838*10+3。这对应了属性等式:。这对应了属性等式:number1.val:=number2.val 10+digit.val例例8.1考虑下列无符号数的简单语法G8.1numbernumber

592、digit|digitdigit0|1|2|3|4|5|6|7|8|98.2属性与属性文法属性与属性文法编译原理与技术编译原理与技术543表8.2例8.1的属性文法文法规则语义规则number1number2digitnumber1.val:=number2.val10+digit.valnumberdigitnumber.val:=digit.valdigit0digit.val:=0digit1digit.val:=1.digit8digit.val:=8digit9digit.val:=9number(val=81*10+3=813)number(val=8*10+1=81)digit(

593、val=3)digit(val=1)digit(val=8)digit(val=8)831图8.1数813带属性计算的的语法分析树8.2属性与属性文法属性与属性文法u例例8.2考虑的简单算术表达式考虑的简单算术表达式的语法的语法G5.1:EE+T|E-T|TTT F|FF(E)|num文法规则语义规则E1E2+TE1.val:=E2.val+T.valE1E2-TE1.val:=E2.valT.valETE.val:=T.valT1T2FT1.val:=T2.val*F.valTFT.val:=F.valF(E)F.val:=E.valFnumF.val:=num.val编译原理与技术编译原理

594、与技术544主要属性就是所有非终结符的数值,记做主要属性就是所有非终结符的数值,记做val。这些属性等式描述了表达式的语法关系以及要执行的算术运算的语义。这些属性等式描述了表达式的语法关系以及要执行的算术运算的语义。注意,这个文法没有把注意,这个文法没有把num当作非终结符,也就没有当作非终结符,也就没有num.val在等号在等号左边的属性等式,所以,使用该属性文法时左边的属性等式,所以,使用该属性文法时num.val的值必须在语义的值必须在语义分析前(通常由词法分析器)得到。分析前(通常由词法分析器)得到。如果想明显地在文法中计算如果想明显地在文法中计算num的属性值,可以增加产生式规则,并

595、的属性值,可以增加产生式规则,并对这个属性文法增加如同例子对这个属性文法增加如同例子8.1一样的属性等式。一样的属性等式。8.2属性与属性文法属性与属性文法(52 3) 30的计算的计算语义表示在如图语义表示在如图8.2的的语法分析树中。语法分析树中。自底向上地遍历树就可以自底向上地遍历树就可以得到表达式的值。得到表达式的值。编译原理与技术编译原理与技术545E(val=1470)E(val=523=49)num(val=30)T(val=1)E(val=52)E(val=49*30=1470)F(val=30)T(val=49)()F(val=3)num(val=3)F(val=53)num

596、(val=52)T(val=52)8.2属性与属性文法属性与属性文法文法规则语义规则decltypevar-listvar-list.dtype:=type.dtypetypeinttype.dtype:=integertypefloattype.dtype:=realvar-list1id,var-list2id.type:=var-list1.dtypevar-list2.type:=var-list1.dtypevar-listidid.type:=var-list.dtype编译原理与技术编译原理与技术546例例8.3:考虑下面这个表示变量声明的语法G8.2:decltypevar-l

597、isttypeint|floatvar-listid,var-list|id我们希望为声明中标识符代表的每个变量定义一个数据类型的属性,并且构造属性等式来表达数据类型属性与声明类型的关系。定义的dtype表示数据类型属性。属性dtype的取值范围是集合integer,real,对应了符号int和float。非终结符type的属性dtype由其所代表的符号给定。根据语法规则decltypevar-list,这个属性dtype对应了变量表var-list的dtype。变量表中的每个标识符id都有相同的属性dtype。注意,非终结符decl没有属性定义。事实上,并非每个文法符号都需要定义属性和属性等

598、式。8.2属性与属性文法属性与属性文法文法规则语义规则based-numnumbasecharbased-num.val:=num.valnum.base:=basechar.basebasecharobasechar.base:=8basechardbasechar.base:=10num1num2digitnum1.val:=ifdigit.val=errorornum2.val=errorthenerrorelsenum2.valnum1.base+digit.valnum2.base:=num1.basedigit.base:=num1.basenumdigitnum.val:=dig

599、it.valdigit0digit.val:=0digit1digit.val:=1.digit7digit.val:=7digit8digit.val:=ifdigit.base=8thenerrorelse8digit9digit.val:=ifdigit.base=8thenerrorelse9编译原理与技术编译原理与技术547例例8.4考虑下面这个对文法G8.1做了改动的文法G8.3:based-numnumbasecharbasecharo|dnumnumdigit|digitdigit0|1|2|3|4|5|6|7|8|9定义的数可以是8进制(末尾是记号o)、也以是10进制(末尾是

600、记号d)。在这中情况下,num和digit需要一个新的表示底数的属性base,用于计算值属性val。8.2属性与属性文法属性与属性文法u这个属性文法出现了两个新的特性这个属性文法出现了两个新的特性这个文法没有排除八进制数时错误的数字这个文法没有排除八进制数时错误的数字8和和9。例如,按照语。例如,按照语法规则法规则298o是个正确的符号串,但是不能赋予任何值,这就需是个正确的符号串,但是不能赋予任何值,这就需要引进一个新值要引进一个新值error,表示出错。,表示出错。属性文法必须描述对于后缀属性文法必须描述对于后缀o,一旦符号串包含了数字,一旦符号串包含了数字8或或9就出就出错这中事实。它的

601、最简单的实现方法是在属性等式的函数中使错这中事实。它的最简单的实现方法是在属性等式的函数中使用一个用一个if-then-else表达式或类似表达式或类似C中的条件表达式运算中的条件表达式运算“?:”。例如,对应文法例如,对应文法numnumdigit的属性等式的属性等式num1.val:=ifdigit.val=errorornum2.val=errorthenerrorelsenum2.val num1.base+digit.val表达了如下的情况:如果表达了如下的情况:如果digit.val或或num2.val其中的一个是其中的一个是error,那么,那么num1.val也必须是也必须是e

602、rror;否则,;否则,num1.val的值由公的值由公式式num2.val num1.base+digit.val给出。给出。编译原理与技术编译原理与技术5488.2属性与属性文法属性与属性文法u属性文法的形式化定义属性文法的形式化定义一个属性文法一个属性文法AG是一个三元组是一个三元组,其中,其中G是一个上下文无关文法,称作基础文法,当同一个是一个上下文无关文法,称作基础文法,当同一个符号在产生式中多次出现时要用序号加以区别;符号在产生式中多次出现时要用序号加以区别;A是有限的属性集合,是有限的属性集合,G中的符号中的符号X关联于属性集关联于属性集A(X),X的一个属性的一个属性a记做记做

603、X.a;R是有限的属性等式的集合。是有限的属性等式的集合。对于一组属性对于一组属性a1,a2,.,am和文法和文法G的每个产生式的每个产生式X0X1X2.Xn(X0是非终结符,其它的是非终结符,其它的Xi是任意符是任意符号),属性等式或语义规则的形式:号),属性等式或语义规则的形式:Xi.aj i(a1,a2,.,am)。 i一般是表达式,可以函数甚至是子一般是表达式,可以函数甚至是子程序。程序。编译原理与技术编译原理与技术5498.2属性与属性文法属性与属性文法u属性文法的简化与扩展属性文法的简化与扩展可以在属性等式中使用这些的元语言尽量地可以在属性等式中使用这些的元语言尽量地接近实际的程序

604、语言,以便把属性等式转换接近实际的程序语言,以便把属性等式转换成语义分析器中的工作代码成语义分析器中的工作代码。在属性等式中应用在定义好的函数在属性等式中应用在定义好的函数。使用二义性的更加简单的基础文法使用二义性的更加简单的基础文法。编译原理与技术编译原理与技术5508.2属性与属性文法属性与属性文法u抽象语法树抽象语法树在参考文献中,通常把抽象语法树在参考文献中,通常把抽象语法树AST(AbstractSyntaxTree)简称为语法树,而把语法分析树)简称为语法树,而把语法分析树(parsingtree)简称为分析树。)简称为分析树。在语法树中,算符和关键字不是作为叶结点,而是作在语法树

605、中,算符和关键字不是作为叶结点,而是作为内部结点,它们对应分析树中的这些叶结点的父结为内部结点,它们对应分析树中的这些叶结点的父结点。可按下面的步骤从分析树构造出相应的语法树:点。可按下面的步骤从分析树构造出相应的语法树:l去掉与单非产生式相关的子树,并上提相关分支上的终结符去掉与单非产生式相关的子树,并上提相关分支上的终结符结点;结点;l对于直接包含运算符的多个子树,用算符取代其父结点;对于直接包含运算符的多个子树,用算符取代其父结点;l去掉括号并上提算符,让它代替父结点。去掉括号并上提算符,让它代替父结点。编译原理与技术编译原理与技术5518.2属性与属性文法属性与属性文法对于产生式对于产

606、生式SifEthenS1elseS2,可以看成是一个三元算符,可以看成是一个三元算符的式子,它的语法树如下图的式子,它的语法树如下图表达式表达式8+1*3的语法树示意在下图的语法树示意在下图编译原理与技术编译原理与技术552S1if-then-elseS2B3+*818.2属性与属性文法属性与属性文法编译原理与技术编译原理与技术553例例8.5对于例对于例8.2中的算术表达式的文法中的算术表达式的文法G5.1,可以用表,可以用表8.7的属性文法定义表达式的属性文法定义表达式的抽象语法树。的抽象语法树。其中其中number.lexval指出它是由扫描器构造出来的;指出它是由扫描器构造出来的;辅助

607、函数辅助函数mknode(op,left,right)把输入的三个参数构造成一个树结点,标号是第一把输入的三个参数构造成一个树结点,标号是第一个参数,两个域个参数,两个域left和和right分别指向左子树和右子树;分别指向左子树和右子树;mkleaf(num,num.lexval)构造一个标记为构造一个标记为num的叶结点,其中的一个域代表具有参的叶结点,其中的一个域代表具有参数值的数。数值的数。表表8.7构造算术表达式抽象语法树的属性文法构造算术表达式抽象语法树的属性文法文法规则语义规则E1E2+TE1.tree:=mknode(+,E2.tree,T.tree)E1E2-TE1.tree

608、:=mknode(,E2.tree,T.tree)ETE.tree:=T.treeT1T2FT1.tree:=mknode(*,T2.tree,F.tree)TFT.tree:=F.treeF(E)F.tree:=E.treeFnumF.tree:=mkleaf(num.lexval)8.2属性与属性文法属性与属性文法编译原理与技术编译原理与技术554图8.6给出了表达式(523)30根据表8.6的规则构造的抽象语法树,同时也画出了相应的分析树(虚线)。num30num52num3EtreeEtreeEtreeEtreeTtreenumnumnum*EtreeFtree*8.2属性与属性文法属

609、性与属性文法编译原理与技术编译原理与技术555利用属性文法说明属性的一个中心问题是:利用属性文法说明属性的一个中心问题是:如何确保一个特定的属性文法一致性和完整如何确保一个特定的属性文法一致性和完整性,即唯一地定义可给定的属性?性,即唯一地定义可给定的属性?对于这个问题我们目前无法给出简单的答案。这个问对于这个问题我们目前无法给出简单的答案。这个问题类似于确定一个文法是否是二义的。题类似于确定一个文法是否是二义的。在实践中,我们使用的语法分析算法决定一个文法的在实践中,我们使用的语法分析算法决定一个文法的适应性,类似的情况出现在属性文法:计算属性的算适应性,类似的情况出现在属性文法:计算属性的

610、算法决定一个属性文法的一致性和完整性。法决定一个属性文法的一致性和完整性。 8.3属性的计算属性的计算u依赖图和计算顺序名字的访问依赖图和计算顺序名字的访问前面描述的属性等式前面描述的属性等式Xi.aj i(a1,a2,.,am)可以看作是把等号右部的函数表达式的值赋给等号左部的属可以看作是把等号右部的函数表达式的值赋给等号左部的属性性Xi.aj。为了可以赋值,出现在右部函数中的所有属性的值。为了可以赋值,出现在右部函数中的所有属性的值必须已经存在。必须已经存在。实现属性文法的算法必须为属性的计算找到一个合适顺序,实现属性文法的算法必须为属性的计算找到一个合适顺序,确保在进行计算的时候每个需要

611、的属性值都可以得到。确保在进行计算的时候每个需要的属性值都可以得到。属性等式本身已经蕴含了计算属性顺序的约束,属性计算的属性等式本身已经蕴含了计算属性顺序的约束,属性计算的首要任务是利用有向图把这些隐含的顺序约束明确地表示出首要任务是利用有向图把这些隐含的顺序约束明确地表示出来,这个有向图就叫做属性依赖图。图的结点标号是一个属来,这个有向图就叫做属性依赖图。图的结点标号是一个属性,如果属性性,如果属性a的计算依赖于属性的计算依赖于属性b,那么,存在从结点,那么,存在从结点a到结到结点点b的一条有向边。的一条有向边。编译原理与技术编译原理与技术5568.3属性的计算属性的计算语法产生式语法产生式

612、var-listid,var-list有两条关联的属性等式:有两条关联的属性等式:id.type:=var-list1.dtypevar-list2.type:=var-list1.dtype对应这个候选产生式的依赖图是对应这个候选产生式的依赖图是语法规则语法规则var-listid和和decltypevar-list的依赖图分别的依赖图分别编译原理与技术编译原理与技术557var-list.dtypevar-list.dtypid.dtypevar-list.dtypeid.dtypetype.dtypevar-list.dtype8.3属性的计算属性的计算如果依赖图和分析树同时画出,通常把

613、树结如果依赖图和分析树同时画出,通常把树结点标记的属性省去,而把它写在结点的旁边点标记的属性省去,而把它写在结点的旁边比如,串比如,串floatx,y分析树的依赖图如图分析树的依赖图如图8.7所示所示编译原理与技术编译原理与技术558decltypedtypevar-listid(x),var-listfloatid(y)dtypedtypedtypedtype8.3属性的计算属性的计算编译原理与技术编译原理与技术559例例8.7下面四个产生式based-numnumbasecharnumnumdigitnumdigitdigit9和串813o构造依赖图。(1)为based-numnumbas

614、echar构造依赖图:valvalbasebasebasecharnumbased-num(2)为语法产生式numnumdigit构造对应的依赖图valvalbasevaldigitnumnumbasebase这个图表达了下面三个属性等式的依赖关系:num1.val:=ifdigit.val=errorornum2.val=errorthenerrorelsenum2.valnum1.base+digit.valnum2.base:=num1.basedigit.base:=num1.base8.3属性的计算属性的计算编译原理与技术编译原理与技术560(3)语法规则numdigit和digit

615、1的依赖图分别如下basebasebasenumdigitvalvalvaldigit9(4)为数字串813o构造的分析树的依赖图显示如下valvalbasebasebasecharnumbased-numvalbasevaldigitnumbasevalbasevaldigitnumbaseo3valdigitbase818.3属性的计算属性的计算定义定义8.5对于包含了对于包含了k结点的有向图,结点的拓扑排序结点的有向图,结点的拓扑排序m1,m2,.,mk使得任何一条边使得任何一条边在这个序列中都是在这个序列中都是mi在在mj之前出现之前出现例例8.8图图8.8的依赖图是一个有向无环图。图

616、的依赖图是一个有向无环图。图8.9只画出了依赖图,只画出了依赖图,按照结点编号的顺序得到的结点排序就是这个依赖图的一个拓按照结点编号的顺序得到的结点排序就是这个依赖图的一个拓扑排序。另外一个拓扑排序是下列结点编号的序列:扑排序。另外一个拓扑排序是下列结点编号的序列:12,6,9,1,2,11,3,8,4,5,7,10,13,14编译原理与技术编译原理与技术561valvalbasebase11314valbaseval1210basevalbaseval97basevalbase234568118.3属性的计算属性的计算使用依赖图的拓扑排序计算属性值的一个关键问题是,如何找使用依赖图的拓扑排序

617、计算属性值的一个关键问题是,如何找到图的根的属性值。一个图的根是没有前驱的结点。到图的根的属性值。一个图的根是没有前驱的结点。例如,图例如,图8.9中编号为中编号为1、6、9和和12的结点都没有前驱,是图的根。的结点都没有前驱,是图的根。这些结点上的属性值独立于其它任何属性,必须利用可以直接这些结点上的属性值独立于其它任何属性,必须利用可以直接得到的信息求值。得到的信息求值。而这些信息通常就是对应分析树中结点子女的终结符串的形式。而这些信息通常就是对应分析树中结点子女的终结符串的形式。例如,图例如,图8.9中结点中结点6的属性的属性val依赖于于符号依赖于于符号8,它是属性,它是属性val对应

618、对应的结点的结点digit的子女(参见图的子女(参见图8.8)。所以,结点)。所以,结点6的属性值是的属性值是8。所有这种根的属性值都必须在其它任何属性求值之前计算出来。所有这种根的属性值都必须在其它任何属性求值之前计算出来。这些计算通常由扫描器或语法分析器完成。这些计算通常由扫描器或语法分析器完成。编译原理与技术编译原理与技术5628.3属性的计算属性的计算u综合属性和综合属性和S-属性文法属性文法定义定义8.6一个属性是综合属性,如果它的所有依赖点都是从分一个属性是综合属性,如果它的所有依赖点都是从分析树的子女到父母结点。换句话,属性析树的子女到父母结点。换句话,属性a是综合属性,如果对于

619、是综合属性,如果对于文法文法AX1X2.Xn,a在左边的与在左边的与a关联的属性等式只有下列形关联的属性等式只有下列形式:式:A.a (X1.a1,.,X1.ak,.,Xn.a1,.,Xn.ak)其中其中a1,.ak是所有的属性。其它属性叫做继承属性。所有属性是所有的属性。其它属性叫做继承属性。所有属性都是综合属性的属性文法称作都是综合属性的属性文法称作S-属性文法属性文法。按照定义可知,全部非终结符的属性都是综合属性;同一产生按照定义可知,全部非终结符的属性都是综合属性;同一产生式中相同符号的个综合属性之间没有依赖关系;如果式中相同符号的个综合属性之间没有依赖关系;如果b使某个产使某个产生式

620、中符号生式中符号X的继承属性,那么属性的继承属性,那么属性b的值仅仅依赖于该产生式的值仅仅依赖于该产生式右部位于符号右部位于符号X的左边的符号的属性。的左边的符号的属性。编译原理与技术编译原理与技术5638.3属性的计算属性的计算S-属性文法的属性计算可以用自底向上或者属性文法的属性计算可以用自底向上或者后序遍历(深度优先)分析树后序遍历(深度优先)分析树/语法树的方式语法树的方式一趟完成。这可以表达成下列的递归后序遍一趟完成。这可以表达成下列的递归后序遍历属性求值的算法:历属性求值的算法:procedurepostEvaluation(T:treenode)beginforT中的每个子女结点

621、中的每个子女结点CdopostEvaluation(C);计算计算T的所有综合属性;的所有综合属性;endpostEvaluation;编译原理与技术编译原理与技术5648.3属性的计算属性的计算编译原理与技术编译原理与技术565例例8.9可以用C语言实现构造表达式的语法树。首先定义语法数的数据结构typedefenumPlus,Minus,TimesOPKind;typedefenumOPKind,COnstKindExpKind;typedefstructtreenodeExpKindkind;OPKindop;structtreenode*lchild,*rchild;intval;Tr

622、eenode;typedefTreenode*SyntaxTree;然后,把算法postEvaluation翻译成下列C代码。voidpostEval(SyntaxTreetree)inttemp;if(treekind=OPKind)postEval(treelchild);postEval(treerchild);switch(treeop)casePlus:treeval=treelchildval+treerchildval;caseMinus:treeval=treelchildvaltreerchildval;caseTimes:treeval=treelchildval*tree

623、rchildval;8.3属性的计算属性的计算单纯计算继承属性可以通过前序遍历,或者前序遍历单纯计算继承属性可以通过前序遍历,或者前序遍历和中序遍历分析树来完成,如下列算法所示:和中序遍历分析树来完成,如下列算法所示:procedurepreEval(T:treenode)beginforT中的每个子女结点中的每个子女结点Cdo计算计算T的所有继承属性;的所有继承属性;preEval(C);endfor;endpreEval;由于继承属性可能依赖于子女的属性,与综合属性求由于继承属性可能依赖于子女的属性,与综合属性求值不同,在计算继承属性的时候,子女继承属性的计值不同,在计算继承属性的时候,子

624、女继承属性的计算顺序非常重要。上述代码中对于算顺序非常重要。上述代码中对于T中的每个子女结中的每个子女结点点C的访问顺序必须满足这些依赖关系。的访问顺序必须满足这些依赖关系。编译原理与技术编译原理与技术5668.3属性的计算属性的计算u例例8.11考虑表达式文法的考虑表达式文法的一个简单版本一个简单版本G8.5:expexp/exp|num|num.numu设计这个文法的想法是,运算符依据设计这个文法的想法是,运算符依据操作数是浮点数还是整数可以有完全操作数是浮点数还是整数可以有完全不同的解释。特别是除法运算,是否不同的解释。特别是除法运算,是否允许小数部分,是有区别的。如果不允许小数部分,是

625、有区别的。如果不允许有小数部分,除法符号通常记做允许有小数部分,除法符号通常记做div,这样,这样,5/4就是就是5div4=1。否则,。否则,就是浮点除法,就是浮点除法,5/4=1.2。u描述这些语义需要三个属性:综合的描述这些语义需要三个属性:综合的布尔属性布尔属性isFloat指出一个表达式的任指出一个表达式的任何部分是否是浮点数;一个具有值何部分是否是浮点数;一个具有值int和和float的继承属性的继承属性etype,根据,根据isFloat的值指出每个子表达式的类型;最后的值指出每个子表达式的类型;最后一个是依据一个是依据etype而计算每个子表达式而计算每个子表达式得到的值。得到

626、的值。文法规则语义规则exp1exp2/exp3exp1.isFloat:=exp2.isFloatORexp3.isFloatexp2.etype:=exp1.etypeexp3.etype:=exp1.etypeexp1.val:=ifexp1.etype=intthenexp2.valdivexp3.valelseexp2.val/exp3.valexpnumexp.isFloat:=Falseexp.val:=ifexp.etype=intthennum.valelsefloat(num.val)expnum.numexp.isFloat:=Trueexp.val:=num.num.v

627、al编译原理与技术编译原理与技术567属性isFloat、etype和val需要两次遍历分析树或语法树才能计算出来。第一遍后序遍历计算出综合属性isFloat的值;第二遍混合的前序遍历和后序遍历计算出继承属性etype与综合属性val的值8.3属性的计算属性的计算uS-属性文法的自底属性文法的自底向上计算向上计算在在LR分析方法中,我们通常分析方法中,我们通常使用一个栈来存放已经分析使用一个栈来存放已经分析过的子树的信息。现在,增过的子树的信息。现在,增加一个属性值栈,或者在分加一个属性值栈,或者在分析栈中增加一个子域,存放析栈中增加一个子域,存放属性值。这个属性值栈和分属性值。这个属性值栈和

628、分析栈被同步地操作,每当分析栈被同步地操作,每当分析栈发生移进或归约时,就析栈发生移进或归约时,就根据属性等式计算新的属性根据属性等式计算新的属性值,并存放在属性值栈内。值,并存放在属性值栈内。图图8.10给出了一个示意图给出了一个示意图。编译原理与技术编译原理与技术568状态属性值.ZZ.zYY.yXX.x.栈top图8.10带综合属性域的分析栈设当前的栈顶由指针top指示,并假定综合属性刚好在每次归约前计算。譬如产生式AXYZ的语义规则是A.a=(X.x,Y.y,Z.z)那么,在XYZ归约成A之前,属性Z.z的值在栈顶即,valtop,属性Y.y的值在valtop1,属性X.x的值在val

629、top2。如果某个符号没有综合属性,那么,数组val对应的元素就没有定义。归约后,top的值减2,代表A的状态放在当前栈顶statetop(即原先X的位置),根据相应属性等式计算得到的综合属性A.a的值放在valtop。8.3属性的计算属性的计算例例8.12考考虑虑下下列列一一个个台台式式计计算算器器的的文文法法G8.5及及其其语义规则:语义规则:文法规则语义规则语义分析代码段LEnprint(E.val)print(valtop-1)EE1+TE.val:=E1.val+T.valvaltop-2:=valtop-2+valtopETE.val:=T.valTT1FT.val:=T1.val

630、*F.valvaltop-2:=valtop-2*valtopTFT.val:=F.valF(E)F.val:=E.valvaltop-2:=valtop-1FdigitF.val:=digit.lexval编译原理与技术编译原理与技术569每个非终结符都有一个存放数值的综合属性val,终结符digit的综合属性lexval由词法扫描器给出,就是每个数字的值。语义分析代码段是把语义规则中的属性用val数组替换得到的。属性计算的结果都是放在valtop2,这是因为恰好对应的产生式的右部都是3个符号。这里的top指归约前的栈顶,归约以后它的值会根据右部符号的多少进行调整。8.3属性的计算属性的计算

631、我我们们用用相相应应的的文文法法符符号号代代替替state的的状状态态,给给出出实实际际的的输输入入数数字字的的值值而而不不是是符符号号digit,而而且且假假定定分分析析器器把把digit移移进进栈栈时时,记记号号digit进进入入statetop,其属性值放在,其属性值放在valtop。我我们们讨讨论论看看第第一一个个符符号号8时时的的动动作作序序列列。第第一一步步,分分析析器器把把对对符符号号digit对对应应的的状状态态(用用8表表示示,其其值值8存存放放val域域)移移入入栈栈中中;第第二二步步,分分析析器器按按照照产产生生式式Fdigit进进行行归归约约,并并执执行行语语义义规规则

632、则F.val:=digit.lexval的的计计算算;第第三三步步,分分析析器器按按照照产产生生式式TF进进行行归归约约,没没有有代代码码段段与与这这个个产产生生式式对对应应,所所以以val数数组组的的值值没没有有改改变变。注注意意,每每次次归归约约后后val栈栈顶顶存存放放的的都都是是所用产生式左部符号的属性值。所用产生式左部符号的属性值。输入状态state属性值val用到的产生式8+1*3n+1*3n88+1*3nF8Fdigit+1*3nT8TF+1*3nE8ET1*3nE+8+*3nE+18+1*3nE+F8+1Fdigit*3nE+T8+1TF3nE+T*8+1*nE+T*38+1*

633、3nE+T*F8+1*3FdigitnE+T8+3TT*FnE11EE+TEn18-L18LEn编译原理与技术编译原理与技术570对输入串对输入串8+1*3n基于基于LR分析器的语义动作分析器的语义动作8.3属性的计算属性的计算u翻译模式与翻译模式与L-属性文法自顶向下计算属性文法自顶向下计算定义定义8.7对于一组属性对于一组属性a1,a2,.,am的属性文法的属性文法AG是是L-属性文法,如果对于每个继承属性属性文法,如果对于每个继承属性aj以及每个产生式以及每个产生式X0X1X2.Xn(X0是非终结符,其它的是非终结符,其它的Xi是任意是任意符号),和符号),和aj关联的属性等式的形式都是

634、:关联的属性等式的形式都是:Xi.aj (X1.a1,.,X1.ak,X1.a1,.,X1.ak,.,Xi-1.a1,.,Xi-1.ak)即,即,Xi的的aj仅仅依赖于出现在仅仅依赖于出现在Xi左边符号左边符号X0,X1,.,Xi-1的属性。的属性。S-属性文法作为一个特例也是属性文法作为一个特例也是L-属性文法。属性文法。编译原理与技术编译原理与技术5718.3属性的计算属性的计算对对于于一一个个L-属属性性文文法法,它它的的继继承承属属性性都都不不依依赖赖综综合合属属性性,可可以以在在自自顶顶向向下下的的递递归归下下降降分分析析过过程程计计算算出出所所有有的属性值。的属性值。但但是是,LR

635、分分析析器器,如如yacc生生成成的的LALR(1)分分析析器器,却主要适合处理综合属性或却主要适合处理综合属性或S-属性文法。属性文法。LL分分析析技技术术只只有有在在推推导导过过程程知知道道了了使使用用的的语语法法规规则则时时才才能能计计算算属属性性,因因为为只只有有那那时时才才有有确确定定的的计计算算属属性性的等式。的等式。而而LR分分析析器器在在推推导导过过程程推推迟迟决决定定使使用用哪哪个个产产生生式式,直直到到一一个个产产生生式式的的右右部部完完全全形形成成。这这就就难难易易得得到到继继承承属属性性的的值值,除除非非它它们们的的性性质质对对于于所所有有右右边边的的候候选选式式都都是

636、固定的。是固定的。编译原理与技术编译原理与技术5728.3属性的计算属性的计算上面介绍的根据语义规则编写的语义分析代码段是放上面介绍的根据语义规则编写的语义分析代码段是放在文法产生式的后面,只能在归约前执行。在文法产生式的后面,只能在归约前执行。翻译模式把代码段表示的语义动作放在花括弧翻译模式把代码段表示的语义动作放在花括弧内,内,如同如同yacc的语义动作,并且可以插在产生式右部的任的语义动作,并且可以插在产生式右部的任何地方。这样,翻译模式就给出了语义动作的执行顺何地方。这样,翻译模式就给出了语义动作的执行顺序。若序。若A. ,则,则.中的语义动作在中的语义动作在 的推导的推导(或向(或向

637、 的归约)结束后、在的归约)结束后、在 的推导(或向的推导(或向 的归约)的归约)开始前执行。开始前执行。可以把可以把之间的语义动作想象测绘那个为一个文法符之间的语义动作想象测绘那个为一个文法符号,在分析过程对该号,在分析过程对该“符号符号”的推导或归约就是执行的推导或归约就是执行器语义动作。器语义动作。编译原理与技术编译原理与技术5738.3属性的计算属性的计算例例8.13面是一个简单的翻译模式的例子,它面是一个简单的翻译模式的例子,它把有加号和减号的中缀表达式翻译成后缀表把有加号和减号的中缀表达式翻译成后缀表达式。达式。ETREaddopTprint(addop.lexvalR| Eidp

638、rint(id.lexval第二行的动作第二行的动作print(addop.lexval必须放在必须放在T和和R之间,移到别的位置都不会得到正确的结之间,移到别的位置都不会得到正确的结果。例如,如果输入果。例如,如果输入a+b c,该翻译模式的,该翻译模式的输出是输出是ab+c 。编译原理与技术编译原理与技术5748.3属性的计算属性的计算图图8.11表示了分析输入串表示了分析输入串a+b c的语法树,每个语义动作都作的语法树,每个语义动作都作为相应产生式左部符号的结点的子女。这样,把语义动作看作为相应产生式左部符号的结点的子女。这样,把语义动作看作是终结符号,表示在什么时候执行哪些动作。是终

639、结符号,表示在什么时候执行哪些动作。为了便于说明,图中用实际的数字和运算符代替了单词为了便于说明,图中用实际的数字和运算符代替了单词id和和addop。当按照深度优先或后序遍历语法树的时候,就执行相应。当按照深度优先或后序遍历语法树的时候,就执行相应的语义动作,打印出的语义动作,打印出ab+c 。编译原理与技术编译原理与技术575ETR+EabEprint()Tprint(a)print(b)print(+)print(c)Rc8.3属性的计算属性的计算对于既有综合属性、又有继承属性的更一般对于既有综合属性、又有继承属性的更一般的的L-属性文法,在建立翻译模式时必须遵循属性文法,在建立翻译模式

640、时必须遵循下列三个条件:下列三个条件:1.产生式右部符号的继承属性必须在这个符号之前产生式右部符号的继承属性必须在这个符号之前的动作中计算;的动作中计算;2.一个动作不能引用该动作左边符号的综合属性;一个动作不能引用该动作左边符号的综合属性;3.产生式左边非终结符的综合属性只能在它所引用产生式左边非终结符的综合属性只能在它所引用的所有属性都计算完后才能计算。计算这种属性的所有属性都计算完后才能计算。计算这种属性的动作通常放在产生式右部的末端。的动作通常放在产生式右部的末端。编译原理与技术编译原理与技术5768.3属性的计算属性的计算例例8.14表表8.12是根据表是根据表8.3设计的翻译模式,

641、它的基设计的翻译模式,它的基础文法是含左递归的简单算术文法础文法是含左递归的简单算术文法G5.1(为了简化(为了简化省去了减法),其中的属性省去了减法),其中的属性val是综合属性是综合属性。文法规则语义规则翻译模式E1E2+TE1.val:=E2.val+T.valE1.val:=E2.val+T.valETE.val:=T.valE.val:=T.valT1T2FT1.val:=T2.val*F.valT1.val:=T2.val*F.valTFT.val:=F.valT.val:=F.valF(E)F.val:=E.valF.val:=E.valFnumF.val:=num.valF.v

642、al:=num.val编译原理与技术编译原理与技术5778.3属性的计算属性的计算编译原理与技术编译原理与技术578为了能够用LL方法分析,必须消去基础文法的左递归,这就导致产生了继承属性。改造后的翻译模式如下所示。我们为新的非终结符R引入了继承属性i和综合属性s,作用就是对应产生式R把R的继承属性值传给其综合属性。这个翻译模式中,每个数都是由T产生的,而且T.val的值就是由属性num.lexval给出的数的词法值。ET R.i:=T.valRE.val:=R.sR+TR1.i:=R.i+T.valR1R.s:=R1.sR*T R1.i:=R.i*T.valR1R.s:=R1.sRR.s:=

643、R.iT(E) T.val:=E.valTnumT.val:=num.lexval8.3属性的计算属性的计算图图8.13给出了按照上述翻译模式对输入串给出了按照上述翻译模式对输入串(8+1)*3的计算。的计算。子表达式子表达式8+1中的数中的数8是由最左边的是由最左边的T生成的,但是加号和生成的,但是加号和1却是却是由根的右结点由根的右结点R生成的。继承属性生成的。继承属性R.i从从T.val得到值得到值8。计算计算8+1并把结果并把结果9传递到中间的传递到中间的R结点,这是通过产生式中嵌入结点,这是通过产生式中嵌入的动作的动作R1.i:=R.i+T.val完成的。完成的。类似的动作把类似的动

644、作把3乘到乘到8+1的值上,在最下面的的值上,在最下面的R结点中产生结果结点中产生结果R.i=27;这个结果通过动作;这个结果通过动作R.s:=R.i和和E.val:=R.s向上复向上复制完成。制完成。编译原理与技术编译原理与技术579E.val=27R.i=8*+T.val=1R.i=9num.val=8num.val=1T.val=3R.i=27T.val=8num.val=3R.s=27R.s=27R.s=278.3属性的计算属性的计算u根据根据L-属性构造递归下降属性求值器的方法属性构造递归下降属性求值器的方法为每个非终结符为每个非终结符A构造一个函数,其形式参数是构造一个函数,其形式

645、参数是A的所有继承属性,返的所有继承属性,返回值是回值是A的综合属性(可以是记录,或者指向记录的一个指针,每个综的综合属性(可以是记录,或者指向记录的一个指针,每个综合属性对应一个子域);合属性对应一个子域);在函数体内为含在函数体内为含A的产生式中其它文法符号的每个属性都声明一个局部的产生式中其它文法符号的每个属性都声明一个局部变量,把属性变量,把属性B.b对应的局部变量记做对应的局部变量记做B.b。每个产生式对应的程序代码,是从左到右依次考察单词记号、非终结每个产生式对应的程序代码,是从左到右依次考察单词记号、非终结符和语义动作,按照下列方法编写的:符和语义动作,按照下列方法编写的:(1)

646、对于带有综合属性)对于带有综合属性c的符号的符号X,把,把c的值存入声明为的值存入声明为X.c的局部变量中;然的局部变量中;然后产生匹配后产生匹配X的调用,并扫描下一个输入符号;的调用,并扫描下一个输入符号;(2)对于非终结符)对于非终结符B,产生一个右边是函数调用的赋值语句,产生一个右边是函数调用的赋值语句c:=B(b1,b2,.,bk),其中,其中c是为是为B的综合属性设置的变量,输入参数的综合属性设置的变量,输入参数b1,b2,.,bk是是B的继的继承属性;承属性;(3)对于每个语义动作,把代码复写到求值器中,把对属性的引用改成对相)对于每个语义动作,把代码复写到求值器中,把对属性的引用

647、改成对相应局部变量的引用。应局部变量的引用。编译原理与技术编译原理与技术5808.3属性的计算属性的计算编译原理与技术编译原理与技术581例例8.15图8.12的基础文法是LL(1)文法,适合自顶向下的分析和属性求值。非终结符E、R和T的函数声明如下,其中E和T没有继承属性,所以,它们的函数没有输入参数,Number表示类型。functionE:Number;functionR(i:Number):Number;functionT:Number;图8.14是根据上述方法编写的算术表达式的递归下降属性求值器。其中getchar超前搜索下一个符号并存入变量token,函数isNumber判断输入参

648、数是否是一个数值,过程error报错。functionE:Number;Numberi,s,val1,val2;/*val1是T的属性,val2是E的属性*/beginval1:=T;i:=val1;/*把语义动作拷贝过来,用临时变量取代属性*/s:=R(i);val2:=send;functionR(i:Number):Number;Numberi1,i,s1,s,val;8.3属性的计算属性的计算编译原理与技术编译原理与技术582functionR(i:Number):Number;Numberi1,i,s1,s,val;beginswitchtokenofcase+:/*对应产生式R+T

649、R1*/getchar;val:=T;i1:=i+val;s1:=R(i1);s:=s1;case*:/*对应产生式R*TR1*/getchar;val:=T;i1:=i*val;s1:=R(i1);s:=s1;default:s:=i;/*对应产生式R*/endswitch;end;functionT:Number;Numberval1,val2,val3;/*val1是是T的属性,的属性,val2是是E的属性的属性,val3是是num的属性的属性*/beginiftoken=(then/*对应产生式对应产生式T(E)*/getchar;val1:=E;iftoken=)thengetcha

650、r;val2:=val1;elseerror;elseifisNumber(token)/*对应产生式对应产生式Tnum*/thengetchar;val1:=val3;elseerror;end;8.3属性的计算属性的计算u删除翻译模式中嵌入的动作删除翻译模式中嵌入的动作对于属性文法中每个嵌入的语义动作,用一个不在原对于属性文法中每个嵌入的语义动作,用一个不在原基础文法的非终结符号比如基础文法的非终结符号比如M代替,增加一个产生式代替,增加一个产生式M ,并把嵌入的语义动作放在该产生式的末尾。,并把嵌入的语义动作放在该产生式的末尾。对于翻译模式对于翻译模式ETRE+Tprint(+R| Tp

651、rint( R| Eidprint(id.lexval新增两个非终结符新增两个非终结符M和和N后,转换为后,转换为ETRE+TMR| TNR| M print(+N print( Eid print(id.lexval编译原理与技术编译原理与技术5838.3属性的计算属性的计算u用综合属性代替继承属性用综合属性代替继承属性文法规则语义规则declvar-listidid.type=var-list.dtypevar-list1var-list2id,var-list1.dtype=var-list2.dtypeid.type=var-list1.dtypevar-listtypevar-lis

652、t.dtype=type.dtypetypeintdtype=integertypefloatdtype=real编译原理与技术编译原理与技术584例例8.16前面多次讨论过的简单声明语句的文法G8.3:decltypevar-listtypeint|floatvar-listid,var-list|id在语义规则表8.3中的属性dtype是继承属性。然而,重写文法如下:declvar-listidvar-listvar-listid,|typetypeint|float8.4数据类型与类型检查数据类型与类型检查类型推断:计算并维持数据类型的信息。类型推断:计算并维持数据类型的信息。类型检查:

653、使用这些信息来确保程序的每个部分语言类型检查:使用这些信息来确保程序的每个部分语言类型规则下都有意义。类型规则下都有意义。类型化语言:每个变量都给出了确定类型的语言。类型化语言:每个变量都给出了确定类型的语言。编译原理与技术编译原理与技术585类型化语言的优点:类型化语言的优点:1.可以早期发现程序的错误,特别是在编译是发现程序的语义错误,避可以早期发现程序的错误,特别是在编译是发现程序的语义错误,避免在程序运行时出错。免在程序运行时出错。2.类型信息可以组织在大型软件系统中模块的接口中,便于程序模块的类型信息可以组织在大型软件系统中模块的接口中,便于程序模块的独立开发和集成;同时,声明了标识

654、符和表达式的类型,这些信息有独立开发和集成;同时,声明了标识符和表达式的类型,这些信息有助于理解程序。助于理解程序。3.可以实现各个模块的独立编译,一个模块的修改不会引起其它模块的可以实现各个模块的独立编译,一个模块的修改不会引起其它模块的的重新编译,有效地提高了大型软件系统的编译和开发。的重新编译,有效地提高了大型软件系统的编译和开发。4.在编译时搜集的类型信息可以更加合理地安排存储空间的大小和组织,在编译时搜集的类型信息可以更加合理地安排存储空间的大小和组织,提高目标代码的时空效率。提高目标代码的时空效率。8.4数据类型与类型检查数据类型与类型检查u类型表达式与类型构造器类型表达式与类型构

655、造器一个程序变量在程序运行期间的值可以想象为有一个一个程序变量在程序运行期间的值可以想象为有一个范围,这个范围的界叫做该变量的类型。范围,这个范围的界叫做该变量的类型。一个数据类型是一组值以及在这些值上的特定操作。一个数据类型是一组值以及在这些值上的特定操作。在实际的编译构造中,类型的值通常被一个类型表达在实际的编译构造中,类型的值通常被一个类型表达式描述,它可以是一个类型名,例如式描述,它可以是一个类型名,例如integer,或者是,或者是结构化表达式,例如结构化表达式,例如array1.100ofreal,而且隐含,而且隐含了操作。了操作。一个类型表达式或者是基本类型,或者由类型构造符一个

656、类型表达式或者是基本类型,或者由类型构造符作用于其类型表达式组成。作用于其类型表达式组成。编译原理与技术编译原理与技术5868.4数据类型与类型检查数据类型与类型检查u典型的类型构造符典型的类型构造符数组数组如果如果I和和T都是类型表达式,都是类型表达式,I是一个整数域,那么,是一个整数域,那么,array(I,T)是一个表示是一个表示数组元素为数组元素为T的类型表达式的类型表达式。乘积乘积如果如果T1和和T2是两个类型表达式,则它们的笛卡儿积是两个类型表达式,则它们的笛卡儿积T1 T2也是一个类型也是一个类型表达式。表达式。记录记录记录或结构类型是通过作用在一组二元式记录或结构类型是通过作用

657、在一组二元式得到的。得到的。例如,例如,Pascal的纪录类型的纪录类型typestudent=recordname:array1.20ofchar;birthday:Date;sex:integer;end;声明了一个声明了一个student类型,它包含三个成员。可以表示成:类型,它包含三个成员。可以表示成:record(name (array(I,char) (birthday Date) (sex integer)。编译原理与技术编译原理与技术5878.4数据类型与类型检查数据类型与类型检查指针指针如果如果T是一个类型表达式,则是一个类型表达式,则pointer(T)表示指表示指向向T类

658、型对象的指针。指针类型的值是一个存放了基类型对象的指针。指针类型的值是一个存放了基类型类型T的值的存储地址。例如,在的值的存储地址。例如,在Pascal中中integer表表示指向整数的指针,示指向整数的指针,C中对应的类型为中对应的类型为int*。函数函数在程序语言中,可以把函数类型处理成由定义在程序语言中,可以把函数类型处理成由定义域类型域类型D1 D2 . Dn到值域类型到值域类型R的映射,表达成的映射,表达成D1 D2 . DnR。例如,。例如,C语言的函数语言的函数floatpower(floatx,intn)它的类型表达式是它的类型表达式是float intfloat。类类面向对象

659、的程序语言都有一个和记录声明相似的面向对象的程序语言都有一个和记录声明相似的类声明,除了包含变量声明以外,还包括方法或成员类声明,除了包含变量声明以外,还包括方法或成员函数的操作声明。类声明可以创造一个新的类型,也函数的操作声明。类声明可以创造一个新的类型,也可能不创建新的类型。可能不创建新的类型。编译原理与技术编译原理与技术5888.4数据类型与类型检查数据类型与类型检查u例例8.17编译原理与技术编译原理与技术589设有如下的设有如下的Java方法(函数)说明:方法(函数)说明:Rectanglerec(intxx,intyy,inthh,intww);对应的类型表达式是:对应的类型表达式

660、是:integer integer integer integerRectangle。Pascal的程序片断的程序片断typestudent=recordname:array1.20ofchar;birthday:Date;sex:integer;end;table:array1.50ofstudent;person: studentperson和和table所对应的类型表达式分别是所对应的类型表达式分别是pointer(student),array(1.50,record(name array(1.20,char) (birthday Date) (sex integer)。8.4数据类型与

661、类型检查数据类型与类型检查u类型系统类型系统是把类型表达式赋给语言各相关结构成分的是把类型表达式赋给语言各相关结构成分的的规则集合,它始终检测程序中变量以及表的规则集合,它始终检测程序中变量以及表达式的类型,防止程序运行时出现错误。达式的类型,防止程序运行时出现错误。PD;SDD;D|id:TTBT|STBTinteger|boolean|real|char|voidSTarraynumofT|recordDend| T|TTSid:=E|ifEthenS|whileEdoS|S;SEtrue|num|id|E+E|EandE|EE|E |E(E)编译原理与技术编译原理与技术5908.4数据类

662、型与类型检查数据类型与类型检查u等价类型等价类型两个等价的类型两个等价的类型T1和和T2记做记做T1T2。程序语言的类型规则中常见的有三种类型等价的定义:程序语言的类型规则中常见的有三种类型等价的定义:类型结构等价、类型名字等价和类型声明等价。类型结构等价、类型名字等价和类型声明等价。编译原理与技术编译原理与技术591u类型结构等价类型结构等价定义定义8.9类型结构等价指的是两个类型类型结构等价指的是两个类型T1和和T2具有相具有相同的结构,递归定义如下:同的结构,递归定义如下:如果如果T1和和T2都是相同的基本类型,则它们是结构等都是相同的基本类型,则它们是结构等价;价;如果如果T1和和T2

663、都是数组类型,都是数组类型,T1T2当且仅当当且仅当T1和和T2的基类型结构等价,成员类型结构等价;的基类型结构等价,成员类型结构等价;如果如果T1和和T2都是记录类型,都是记录类型,T1T2当且仅当当且仅当T1和和T2的每个成员类型结构等价;的每个成员类型结构等价;如果如果T1和和T2都是指针类型,都是指针类型,T1T2当且仅当当且仅当T1和和T2的基类型结构等价;的基类型结构等价;如果如果T1和和T2都是函数类型,都是函数类型,T1T2当且仅当当且仅当T1和和T2的定义域类型结构和值域类型结构等价。的定义域类型结构和值域类型结构等价。编译原理与技术编译原理与技术5928.4数据类型与类型检

664、查数据类型与类型检查u类型名等价类型名等价定义定义8.10两个类型表达式类型名等价,当且两个类型表达式类型名等价,当且仅当它们都是简单类型或者具有相同的类型仅当它们都是简单类型或者具有相同的类型名。名。对于下面的类型声明对于下面的类型声明type1=integer;type2=integer;由于由于type1和和type2的名字不同,它们不是等价的名字不同,它们不是等价类型,和类型,和integer也不等价。也不等价。编译原理与技术编译原理与技术5938.4数据类型与类型检查数据类型与类型检查u声明等价声明等价在声明等价的意义下,每个类型名都和其基类型名等价。而基在声明等价的意义下,每个类型

665、名都和其基类型名等价。而基类型名或者是预定义的类型,或者是用类型构造器构造出的类类型名或者是预定义的类型,或者是用类型构造器构造出的类型表达式。型表达式。因而,因而,type1=integer;type2=integer;type1和和type2都和都和integer等价,或者说是类型等价,或者说是类型integer的别名。又的别名。又如如t1=array20char;t2=array20char;t3=t2;t1和和t3在声明等价的意义下等价,但是都不等价于在声明等价的意义下等价,但是都不等价于t2。编译原理与技术编译原理与技术5948.4.3类型检查类型检查u类型检查的属性文法类型检查的属

666、性文法假设存在包含了变量名及其类型的符号假设存在包含了变量名及其类型的符号表,并且提供函数表,并且提供函数insert(name,type),把变量名把变量名name及其类型及其类型type填入符号表,填入符号表,函数函数lookup(name)返回变量名返回变量名name的类的类型。型。文法符号的属性文法符号的属性type表示它的类型,标表示它的类型,标识符识符id具有具有name的属性。的属性。type-error表示表示类型错误。类型错误。假定该语言的数组从下标假定该语言的数组从下标1开始,开始,采用了采用了Pascal的前缀运算符的前缀运算符“ ”建立指建立指针类型,所以,由针类型,所

667、以,由 real得到一个指针类得到一个指针类型表达式型表达式pointer(real)。记录类型的类型。记录类型的类型表达式实际上是表达式实际上是D的子域的笛卡儿积的形的子域的笛卡儿积的形式。式。函数类型表达式就采用了映射符号函数类型表达式就采用了映射符号“”。文法规则语义规则TBTT.type:=BT.typeTSTT.type:=ST.typeBTintegerBT.type:=integerBTbooleanBT.type:=booleanBTrealBT.type:=realBTcharBT.type:=charBTvoidBT.type:=voidSTarraynumofTST.ty

668、pe:=array(num.val,T.type)STrecordDendST.type:=record(D.type)STTST.type:=pointer(T.type)STT1T2ST.type:=T1.typeT2.type编译原理与技术编译原理与技术5958.4.3类型检查类型检查文法规则语义规则EtrueE.type:=booleanEnumE.type:=integerEidE.type:=lookup(id.name)EE1+E2E.type:=ifE1.typeintegerandE2.typeintegerthenintegerelsetype-errorEE1andE2E

669、.type:=ifE1.typebooleanandE2.typebooleanthenbooleanelsetype-errorEE1E2E.type:=iftypeEqual(E1.type,array(s,t)andE2.typeintegerthentelsetype-errorEE1E.type:=iftypeEqual(E1.type,pointer(t)thentelsetype-errorEE1(E2)E.type:=iftypeEqual(E2.type,s)andtypeEqual(E1.type,st)thentelsetype-error编译原理与技术编译原理与技术59

670、6表达式的类型检查表达式的类型检查1.E的综合属性的综合属性type通过类型系统把类型通过类型系统把类型表达式赋给由表达式赋给由E产生的表达式产生的表达式.2.Eid的语义规则表示,的语义规则表示,E的类型就是的类型就是标识符标识符id的类型,用函数的类型,用函数lookup在符号在符号表中读取。表中读取。3.对于二元表达式,该语言要求两个运算对于二元表达式,该语言要求两个运算数的类型一致,否则就出现类型错误,数的类型一致,否则就出现类型错误,用用type-error表示。表示。4.在数组引用在数组引用E1E2中,下标表达式中,下标表达式E2必必须具有整型。在这种情况下,结果是从须具有整型。在

671、这种情况下,结果是从E1的类型的类型array(s,t)获得的元素类型获得的元素类型t,而和数组下标类型无关。而和数组下标类型无关。5.在指针在指针E1 表达式里,运算符表达式里,运算符“ ”产产生由其操作数生由其操作数E1所指的数据对象,故所指的数据对象,故E1 的类型是指针的类型是指针E所指的对象的类型所指的对象的类型t。6.函数调用函数调用E1(E2),E1的类型必须是从的类型必须是从E2的类型的类型s到某个值域类型到某个值域类型t的函数类型的函数类型st,所以,所以E1(E2)的类型是的类型是t。8.4.3类型检查类型检查文法规则语义规则PD;SP.type:=iftypeEqual(

672、S.type,void)thenvoidelsetype-errorDD;DDid:Tinsert(id.name,T.type)Sid:=ES.type:=iftypeEqual(id.type,E.type)thenvoidelsetype-errorSifEthenSS.type:=ifE.typebooleanthenvoidelsetype-errorSwhileEdoS1S.type:=ifE.typebooleanthenvoidelseS1.typeSS1;S2S.type:=ifS1.typevoidandS2.typevoidthenvoidelsetype-error编译

673、原理与技术编译原理与技术597语句的类型检查语句的类型检查该语言只包括常见的赋值语句、分支语句、循环语句和顺序语句序列。赋值语句的类该语言只包括常见的赋值语句、分支语句、循环语句和顺序语句序列。赋值语句的类型检查规则判断赋值号两边的类型是否等价,对分支语句和循环语句则是检查条件表型检查规则判断赋值号两边的类型是否等价,对分支语句和循环语句则是检查条件表达式是否是布尔类型;对顺序语句的类型检查,仅当其中的每个语句都有类型达式是否是布尔类型;对顺序语句的类型检查,仅当其中的每个语句都有类型void时时才能时顺序语句也有类型才能时顺序语句也有类型void,否则出现类型错误,否则出现类型错误type-

674、error。特别地,从语句和程序产生式的语义规则可以看出,只有再没有类型错的时候,整个特别地,从语句和程序产生式的语义规则可以看出,只有再没有类型错的时候,整个程序的类型才是程序的类型才是void,表示程序的类型正确;否则程序包含类型错误。,表示程序的类型正确;否则程序包含类型错误。8.4.4类型转换类型转换一个编程语言通常允许混合类型的算术表达式,例如一个编程语言通常允许混合类型的算术表达式,例如实数和整数相加实数和整数相加3.14+5因为在计算机中浮点数和整数有不同的表示,并且对浮点数和因为在计算机中浮点数和整数有不同的表示,并且对浮点数和整数有不同的机器指令,因此编译程序应该首先把其中的

675、一个整数有不同的机器指令,因此编译程序应该首先把其中的一个运算对象进行类型转换,以保证两个参加算术运算的数据有同运算对象进行类型转换,以保证两个参加算术运算的数据有同样的类型。程序语言通常采用两种方式处理类型转换。样的类型。程序语言通常采用两种方式处理类型转换。例如在例如在Ada语言中,要求程序员提供一个转换函数,因此,上面语言中,要求程序员提供一个转换函数,因此,上面的例子应该写成的例子应该写成3.14+Float(5),否则就出现类型错误。,否则就出现类型错误。另外一种方式是编译程序中类型检查器根据子表达式的类型自另外一种方式是编译程序中类型检查器根据子表达式的类型自动地提供转换操作,一般

676、规则时把精度低的数据转换成高精度动地提供转换操作,一般规则时把精度低的数据转换成高精度的数。例如,在的数。例如,在C+语言当中,对于这个例子,编译器把语言当中,对于这个例子,编译器把5转换转换成浮点数,然后进行加法运算,结果也是浮点类型。成浮点数,然后进行加法运算,结果也是浮点类型。编译原理与技术编译原理与技术5988.4.4类型转换类型转换当赋值号的左值和右值类型不一致的时候,也可以实当赋值号的左值和右值类型不一致的时候,也可以实型类型转换。例如,在型类型转换。例如,在C语言中语言中doubler;inti;r=i;在把在把i的值存入的值存入r表示的地址之前,强制地把表示的地址之前,强制地把

677、i转换成转换成double类型。类型。这种强制类型转换也可能丢失信息,例如,上述赋值这种强制类型转换也可能丢失信息,例如,上述赋值的语句的方向相反,即,的语句的方向相反,即,i=r。类似的情况也出现在面向对象的语言中,通常允许子类似的情况也出现在面向对象的语言中,通常允许子类的对象赋给超类的对象。例如,在类的对象赋给超类的对象。例如,在C+中,如果类中,如果类B是是A的子类,的子类,a是类是类A的对象,的对象,b是类是类B的对象,那么,的对象,那么,允许赋值语句允许赋值语句a=b,但是不允许,但是不允许b=a。编译原理与技术编译原理与技术5998.4.4类型转换类型转换EnumE.type:=

678、integerE.val:=num.lexvalEnum.numE.type:=realE.val:=num.num.lexvalEidE.type:=lookup(id.entry)E.val:=getval(id.entry)EE1opE2ifE1.type=realandE2.type=realthenE.type:=real;E.val:=E1.valopE2.valelseifE1.type=realandE2.type=integerthenE.type:=real;E2.val:=setvalue(E2.entry,real(E2.val)E.val:=E1.valopE2.va

679、llelseifE1.type=integerandE2.type=realthenE.type:=real;E1.val:=setvalue(E1.entry,real(E1.val)E.val:=E1.valopE2.valelseifE1.type=integerandE2.type=integerthenE.type:=integer;E.val:=E1.valopE2.valelsetype-error编译原理与技术编译原理与技术600从整型到实型的类型检查从整型到实型的类型检查(含类型转换含类型转换)编译原理与技术编译原理与技术第第9章章语法制导的中间代码翻译语法制导的中间代码翻译

680、主要内容主要内容u中间语言中间语言u声明语句的翻译声明语句的翻译u赋值语句的翻译赋值语句的翻译u基本控制结构的翻译基本控制结构的翻译u转向语句的翻译转向语句的翻译编译原理与技术编译原理与技术6029.1语法制导的中间代码翻译引论语法制导的中间代码翻译引论中间代码翻译在编译程序中的位置中间代码翻译在编译程序中的位置使用中间代码的好处包括:使用中间代码的好处包括:(1)把源程序翻译成目标代码的工作分阶段进行,便于控制和管理)把源程序翻译成目标代码的工作分阶段进行,便于控制和管理开发工作的复杂度,集中地解决不同阶段的不同问题。例如,语义开发工作的复杂度,集中地解决不同阶段的不同问题。例如,语义检查可

681、以发现类型不匹配、缺乏类型等可能导致程序运行得错误。检查可以发现类型不匹配、缺乏类型等可能导致程序运行得错误。(2)便于把与机器特性密切相关的目标代码的生成尽可能的限制在便于把与机器特性密切相关的目标代码的生成尽可能的限制在编译的后端,有利于重定目标机器,使得一种中间代码可以为多种编译的后端,有利于重定目标机器,使得一种中间代码可以为多种不同类型的目标机器服务。不同类型的目标机器服务。这是目前最流行的编程语言这是目前最流行的编程语言Java以及以及.NET编程环境所采用的策编程环境所采用的策略(当然,除了中间表示以外,它们的运行系统还需要虚拟机略(当然,除了中间表示以外,它们的运行系统还需要虚

682、拟机VM或通用语言运行时或通用语言运行时CLR等技术)。等技术)。编译原理与技术编译原理与技术603语义分析器中间代码生成器代码优化器语法树中间代码语法树中间代码目标代码生成器9.1中间语言中间语言在编译过程中表示源程序的数据结构统称为在编译过程中表示源程序的数据结构统称为中间表示(中间语言)。中间表示(中间语言)。用中间语言表示的程序叫做中间代码。用中间语言表示的程序叫做中间代码。中间语言的设计既要包含足够的结构,可以中间语言的设计既要包含足够的结构,可以支持高级程序语言的结构,如类型、模块、支持高级程序语言的结构,如类型、模块、接口、安全机制、垃圾搜集等,又要便于到接口、安全机制、垃圾搜集

683、等,又要便于到目标机器的的自动映射,有助于翻译成时空目标机器的的自动映射,有助于翻译成时空方面都高效的运行代码。方面都高效的运行代码。编译原理与技术编译原理与技术6049.1中间语言中间语言中间语言可以按照下列特性分类:中间语言可以按照下列特性分类:l抽象程度抽象程度中间语言可以非常抽象,像语法树一样抽象地表中间语言可以非常抽象,像语法树一样抽象地表示几乎所有的操作,也可以具体到接近于目标机器及其指令。示几乎所有的操作,也可以具体到接近于目标机器及其指令。抽象的中间语言如分析树、有向无环图,底层的中间语言包抽象的中间语言如分析树、有向无环图,底层的中间语言包括字节代码,它们的语句集合类似于汇编

684、语言或机器的符号括字节代码,它们的语句集合类似于汇编语言或机器的符号指令,而三地址码介于这两类表示之间。现代计算机体系结指令,而三地址码介于这两类表示之间。现代计算机体系结构(存储管理、寄存器、指令)的发展对中间代码的设计产构(存储管理、寄存器、指令)的发展对中间代码的设计产生了深刻的影响。例如,生了深刻的影响。例如,P-code和和Javabytecode都属于字都属于字节代码,但是,它们的代码本身与支持环境存在很大的差异。节代码,但是,它们的代码本身与支持环境存在很大的差异。l运行时信息运行时信息中间语言可能使用目标机器和运行环境的详细中间语言可能使用目标机器和运行环境的详细信息(如数据类

685、型、变量的位置),也可以不使用这些信息。信息(如数据类型、变量的位置),也可以不使用这些信息。抽象程度高的语言一般不包含目标机器的信息,而字节代码抽象程度高的语言一般不包含目标机器的信息,而字节代码和三地址码通常都包含了数据类型以及相关的算符。一般的和三地址码通常都包含了数据类型以及相关的算符。一般的字节码如字节码如P-code和和Javabytecode都有相应特殊的虚拟机器都有相应特殊的虚拟机器来解释并运行字节代码。现代计算机技术的发展对程序的安来解释并运行字节代码。现代计算机技术的发展对程序的安全性、互操作性、并发性等严格要求,使得运行环境更加复全性、互操作性、并发性等严格要求,使得运行

686、环境更加复杂。杂。编译原理与技术编译原理与技术6059.1中间语言中间语言l使用编译的数据结构使用编译的数据结构中间语言可以包含符号表的全部信息,例中间语言可以包含符号表的全部信息,例如符号范围、嵌套层次和变量的偏移,目标代码的产生就可以完如符号范围、嵌套层次和变量的偏移,目标代码的产生就可以完全倚赖于这样的中间代码。否则,产生目标代码时就需要查询符全倚赖于这样的中间代码。否则,产生目标代码时就需要查询符号表等数据结构。语法树、分析树和有向无环图通常需要符号表号表等数据结构。语法树、分析树和有向无环图通常需要符号表的信息才能完成分析和翻译的工作,三地址码也需要使用符号表,的信息才能完成分析和翻

687、译的工作,三地址码也需要使用符号表,一般的字节码已经把这些信息转换成对应虚拟机的信息。互联、一般的字节码已经把这些信息转换成对应虚拟机的信息。互联、开放、异构等特性增加了编译系统数据结构与管理的复杂性,例开放、异构等特性增加了编译系统数据结构与管理的复杂性,例如,统一的命名空间,除了要包含一个程序的名字以外,还要处如,统一的命名空间,除了要包含一个程序的名字以外,还要处理甚至是用其它语言编写的构件中的各种名字。理甚至是用其它语言编写的构件中的各种名字。l用途用途编译过程包含了不同的阶段和任务,每个阶段和任务都有编译过程包含了不同的阶段和任务,每个阶段和任务都有最合适的中间表示:分析树特别适合对

688、源程序进行语法和语义分最合适的中间表示:分析树特别适合对源程序进行语法和语义分析,有向无环图适合代码优化和生成,后缀表示便于计算机的计析,有向无环图适合代码优化和生成,后缀表示便于计算机的计算,字节码和三地址码由于更接近机器代码而最适宜目标码的生算,字节码和三地址码由于更接近机器代码而最适宜目标码的生成和移植。但是,一个编译程序通常都不会使用太多的中间表示,成和移植。但是,一个编译程序通常都不会使用太多的中间表示,以免各个中间表示之间的转换造成的效率损失。以免各个中间表示之间的转换造成的效率损失。编译原理与技术编译原理与技术6069.1中间语言后缀式中间语言后缀式u后缀式后缀式定义定义9.1后

689、缀式的递归定义如下:后缀式的递归定义如下:(1)如果)如果E是一个变量或常量,则是一个变量或常量,则E的后缀式就是的后缀式就是E本身;本身;(2)如果)如果E是形如是形如E1opE2的表达式,其中的表达式,其中op是任意的二元是任意的二元运算符运算符,那么,那么,E的后缀式为的后缀式为E1E2op,其中,其中E1和和E2分别是分别是E1和和E2的后缀式;的后缀式;(3)如果)如果E是是(E1)形式的表达式,那么,形式的表达式,那么,E1的后缀式就是的后缀式就是E的的后缀式。后缀式。上述定义容易扩充到含单目算符如负号上述定义容易扩充到含单目算符如负号“ ”或否或否“not”的表达式,也不难扩充到

690、包含数组元素。的表达式,也不难扩充到包含数组元素。编译原理与技术编译原理与技术6079.1中间语言后缀式中间语言后缀式编译原理与技术编译原理与技术608u后缀式的例子后缀式的例子(a+b) (a+c)的后缀式为的后缀式为ab+ac+ a+b+c/d (a+c)的后缀式为的后缀式为a bcd/ac+ +notAornot(CandnotB)的后缀式为的后缀式为AnotCBnotandnotor对于数组变量,把对于数组变量,把和分割维数的逗号和分割维数的逗号“,”都看作都看作是二目算符,那么是二目算符,那么ai的后缀式可以表示成为的后缀式可以表示成为aiai,j,k的后缀式为的后缀式为aijk,9

691、.1中间语言后缀式中间语言后缀式u后缀式的两个特点后缀式的两个特点(1)后缀式形式的表达式计算顺序唯一,无需使用括号来明确)后缀式形式的表达式计算顺序唯一,无需使用括号来明确计算顺序;计算顺序;(2)只要直到每个算符的目数,计算参与运算数的个数,对于)只要直到每个算符的目数,计算参与运算数的个数,对于后缀式不论从左还是从右进行扫描,都能对它进行唯一的分后缀式不论从左还是从右进行扫描,都能对它进行唯一的分解。例如解。例如ab c /所代表的中缀表达式是所代表的中缀表达式是a/( b c)ab+cd+ 所代表的中缀表达式是所代表的中缀表达式是(a+b) (c+d)后缀式特别适合利用栈的结构进行计算

692、:后缀式特别适合利用栈的结构进行计算:1.自左向右扫描表达式的后缀表示,每遇到一个对象就把它压入栈自左向右扫描表达式的后缀表示,每遇到一个对象就把它压入栈内;内;2.每遇到一个算符,就从栈顶取出相应个数的运算对象进行计算,每遇到一个算符,就从栈顶取出相应个数的运算对象进行计算,再将结果压入栈顶。再将结果压入栈顶。3.最后,栈顶元素就是表达式的运算结果。最后,栈顶元素就是表达式的运算结果。编译原理与技术编译原理与技术6099.1中间语言后缀式中间语言后缀式u后缀形式扩充到其它的语言结构后缀形式扩充到其它的语言结构对于赋值表达式对于赋值表达式V=E,如果把赋值号看作,如果把赋值号看作是二目算符,那

693、么,它的后缀形式为是二目算符,那么,它的后缀形式为VE=,其中,其中V和和E分别是分别是V和和E的后缀式。例如,的后缀式。例如,赋值语句赋值语句t=(a+b)/c (d e)的后缀形式为的后缀形式为tab+c/de=赋值语句赋值语句ai=aj+m,k的后缀形式为的后缀形式为aiajm+k,=无条件转移语句无条件转移语句gotoL的后缀表达形式为的后缀表达形式为LGOTO,其中,其中GOTO看作是为单目运算符。看作是为单目运算符。编译原理与技术编译原理与技术6109.1中间语言后缀式中间语言后缀式对于条件语句对于条件语句ifEthenS1elseS2,设,设E、S1和和S2分别是分别是E、S1和

694、和S2的后缀形式,的后缀形式,L1对应语句对应语句S1的起点,的起点,L2对应语句对应语句S2的起点,那么,上述条件的起点,那么,上述条件语句的后缀形式可以表示为语句的后缀形式可以表示为EL1GOTOS1L2GOTOS2。例如,条件语句。例如,条件语句if(ab)thenmax=belsemax=a的后缀形式为的后缀形式为ab10GOTOmaxb=20GOTOmaxa=其中其中10表示条件为真时转移到的标号,表示条件为真时转移到的标号,10表示条件为假时无表示条件为假时无条件转移到的标号处。条件转移到的标号处。编译原理与技术编译原理与技术6119.1中间语言后缀式中间语言后缀式复合语句复合语句

695、S1;S2的后缀形式可以简单的表示成的后缀形式可以简单的表示成S1S2,其中,其中S1和和S2分别时语句分别时语句S1和和S2的后缀形式。的后缀形式。但是,如果像但是,如果像C、Java、Pascal等语言允许在程序块等语言允许在程序块中增加声明语句,那么,就应改对程序块的标志中增加声明语句,那么,就应改对程序块的标志“”或或begin和和end分别引进相应的标志,如分别引进相应的标志,如BLOCKBEGIN和和BLOCKEND。这样,程序块。这样,程序块S1;S2的后缀式为的后缀式为BLOCKBEGINS1S2BLOCKEND循环语句循环语句whileEdoS的后缀式可以下列语句:的后缀式可

696、以下列语句:L1:ifnotEthengotoL2elseS;gotoL1L2:参照条件语句和复合语句,可以把参照条件语句和复合语句,可以把while循环语句表循环语句表示成示成EL2GOTOSL1GOTO编译原理与技术编译原理与技术6129.1中间语言后缀式中间语言后缀式例例9.1把下面的程序段写成后缀的形式把下面的程序段写成后缀的形式 inti;floata,b,result;i=1;a=0;while(i=20)b=b+a;a=b;i=i+1result=b;它的后缀形式为它的后缀形式为BLOCKBEGINiintabresult,floati1=a0=i20=L2GOTObba+=ab

697、=ii1+=L1GOTOresultb=BLOCKEND编译原理与技术编译原理与技术6139.1中间语言后缀式中间语言后缀式文法规则语义规则Sid=ES.code:=id.name|E.code|“”EE1bopE2E.code:=E1.code|E2.code|bop.opEsopE1E.code:=E1.code|sop.opE(E1)E.code:=E1.codeEidE.code:=id.nameEnumE.code:=num.lexval编译原理与技术编译原理与技术614例例9.2表9.1描述了把赋值语句转换成后缀形式的语法制导的翻译规则。其中综合属性code表示符号的后缀形式,属性

698、id.name表示标识符id的名字,num.lexva表示常数num的值;符号“|”表示把语义代码连接起来(读作“捻接”或“并置”)。运算符的属性“op”给出具体的算符,如“+”、“*”或“and”。9.1中间语言图形表示中间语言图形表示编译原理与技术编译原理与技术615a:=( b+c d)+c d的语法树如的语法树如图图9.2(a)所示,它的后缀式为所示,它的后缀式为ab cd +cd+=。有向无环图有向无环图DAG也是一种中间表示,也是一种中间表示,和语法树相比,它以更紧凑的方式给和语法树相比,它以更紧凑的方式给出同样的信息,因为它把公共子表达出同样的信息,因为它把公共子表达式也标示出来

699、。图式也标示出来。图9.2(b)的公共子表达的公共子表达式式c d不止一个父结点。不止一个父结点。:=a+ +c bd cd语法树和分析树都是常见的图形中间代码,语法树省略了语法的终结符号,描述语法树和分析树都是常见的图形中间代码,语法树省略了语法的终结符号,描述了源程序在语义上的层次结构,是分析树的浓缩表示。语法树作为中间表示允许了源程序在语义上的层次结构,是分析树的浓缩表示。语法树作为中间表示允许把翻译从分析中分离出来,形成先分析后翻译的方式。把翻译从分析中分离出来,形成先分析后翻译的方式。后缀式可以看作式语法树的一种线性表示,它是后序遍历或深度优先遍历语法得后缀式可以看作式语法树的一种线

700、性表示,它是后序遍历或深度优先遍历语法得到结点的一个序列。到结点的一个序列。:=a+cd+b图图9.2(b)9.2(a)9.1中间语言图形表示中间语言图形表示例例9.3表表9.2描述了把一个简化的描述了把一个简化的赋值语句改造成语法树的翻译规赋值语句改造成语法树的翻译规则,使用的是二义性文法,假定则,使用的是二义性文法,假定算符的优先性和结合性遵循通常算符的优先性和结合性遵循通常的约定,二目运算的约定,二目运算+和和 是典型程是典型程序语言运算符号集合中选出的两序语言运算符号集合中选出的两个代表,其中个代表,其中mkunode(op,child)是构造单目运算结点的函数。是构造单目运算结点的函

701、数。按照这个定义,可以把输入按照这个定义,可以把输入a:=( b+c d)+c d翻译成图翻译成图9.2(a)形式的语法树。形式的语法树。文法规则语义规则Sid=ES.tree:=mknode(,mkleaf(id,id.name),E.tree)EE1+E2E.tree:=mknode(+,E1.tree,E2.tree,)EE1E2E.tree:=mknode(,E1.tree,E2.tree,)EE1E.tree:=mkunode(,E1.tree)E(E1)E.tree:=E1.treeEidE.tree:=mkleaf(id,id.name)EnumF.tree:=mkleaf(nu

702、m,num.lexval)编译原理与技术编译原理与技术616:=a+ +c bd cd9.1中间语言图形表示中间语言图形表示编译原理与技术编译原理与技术617可以修改构造结点的函数,仍然使用这个语义规则构造出有向无环图。它首先检查是否已经存在一个结点和要构造的结点相同,如果是就返回这个已经存在的结点,否则就构造并返回一个新的结点。例如,对mknode的修改如下:functionmknode(op:OPKind,lchild,rchild:SyntaxTree):SyntaxTreenode:SyntaxTree;beginfor(每个已经产生的语法树的结点每个已经产生的语法树的结点node)d

703、oif(node.operator=opandnode.leftchild=lchildandnode.rightchild=rchild)thenreturnnode;returnSyntaxTree(op,lchild,rchild);end;按照这个定义,从输入a:=(b+cd)+cd构造的DAG如图9.2(b)。9.1中间语言字节代码中间语言字节代码字节代码通常就是运行它的机器模型或体系字节代码通常就是运行它的机器模型或体系结构的指令系统,与机器的符号指令或汇编结构的指令系统,与机器的符号指令或汇编语言类似,设计时考虑机器的字节特性以及语言类似,设计时考虑机器的字节特性以及存储方式、寻

704、址方式、数据类型。存储方式、寻址方式、数据类型。使用字节代码作为中间语言最著名的例子是使用字节代码作为中间语言最著名的例子是在在1970末和末和1980初许多初许多Pascal语言编译器中的语言编译器中的P-code,它的形式如同一个假想的栈式机器,它的形式如同一个假想的栈式机器(称为(称为P-机器)的汇编代码,从而省缺了寄机器)的汇编代码,从而省缺了寄存器。对于不同的实际机器需要为存器。对于不同的实际机器需要为P-code构构造一个解释程序,这样,就可以使造一个解释程序,这样,就可以使Pascal语言语言及其编译方便地移植到新的平台上。及其编译方便地移植到新的平台上。编译原理与技术编译原理与

705、技术6189.1中间语言字节代码中间语言字节代码编译原理与技术编译原理与技术619作为例子,我们考虑把一个表达式x:=2a+(b3)转换成P-code代码(分号后面是注释):ldax;把x的地址存入栈内ldc2;加载常数2到临时栈loda;加载变量a的值到临时栈Mpi;把栈顶的两个数据2和a弹出,执行整数乘法运算,即2a,结果存放在栈内lodb;加载变量b的值到临时栈ldc3;加载常数3到临时栈Sbi;把栈顶的两个数据b和3弹出,执行整数减法运算,即b3,结果存放在栈内Adi;把栈顶的2a和b3结果弹出,完成整数加法,2a+(b3),结果存放在栈顶Sto;把栈顶的值存入栈顶下地址所指向的变量存

706、储区域中,同时弹出这两个元素上述的计算顺序正好对应了表达式的后缀形式:x2ab3:=由于P-code被设计成是可以直接执行的,所以,它还隐含了一个特殊的运行时环,包括数据大小以及很多P-机器的特殊信息。9.1中间语言字节代码中间语言字节代码我们看一下如何使用我们看一下如何使用语义规则把例语义规则把例9.3语言语言生成生成P-code。我们用综合属性我们用综合属性pcode表示表示P-code串,用串,用“|”表示在表示在P-code串中插入一个换行。串中插入一个换行。文法规则语义规则Sid=ES.pcode:=“lda”|id.name|E.pcode|“sto”EE1+E2E.pcode:=

707、E1.pcode|E2.pcode|“adi”EE1E2E.pcode:=E1.pcode|E2.pcode|“mpi”E(E1)E.pcode:=E1.pcodeEidE.pcode:=”lod”|id.nameEnumE.pcode:=”ldc”|num.lexval编译原理与技术编译原理与技术6209.1中间语言三地址代码中间语言三地址代码三地址语句是由下列形式的指令三地址语句是由下列形式的指令x:=yopz其中其中x、y和和z是名字、常数或编译其产生的临时变量,是名字、常数或编译其产生的临时变量,op表示运算符表示运算符如加法、减法、乘法或逻辑算符。如加法、减法、乘法或逻辑算符。它的含

708、义是,对它的含义是,对y和和z的值施加的值施加op所代表的运算,结果存入所代表的运算,结果存入x表示的地表示的地址。址。源语言的表达式源语言的表达式2 a+(b 3)可以翻译成的三地址代码是:可以翻译成的三地址代码是:t1:=2 at2:=b 3t3:=t1+t2其中其中t1、t2和和t3是编译器产生的临时变量。是编译器产生的临时变量。编译原理与技术编译原理与技术621三地址码是语法树或DAG的一种线性表示,其中新增的临时变量名对应树的内部结点。2a+(b3)的分析树如图9.3所示+*2ab图9.32a+(b3)的语法树9.1中间语言三地址代码中间语言三地址代码u把赋值语句翻译成三地把赋值语句

709、翻译成三地址代码的语义规则址代码的语义规则综合属性综合属性E.place表示存表示存放放E值的名字,值的名字,E.code表示三地址指令,表示三地址指令,函数函数gencode(p,:=,E.place)表示生成三地址表示生成三地址语句语句p:=E.place。函数函数newtemp在每次调在每次调用的时候,产生不同的用的时候,产生不同的临时变量名。临时变量名。文法规则语义规则Sid=ES.code:=E.code|gencode(id.name,:=,E.place)EE1+E2E.place:=newtemp;E.code:=E1.tacode|E2.code|gencode(E.plac

710、e,:=,E1.place,+,E2.place)EE1E2E.place:=newtemp;E.code:=E1.code|E2.tacode|gencode(E.place,:=,E1.place,E2.place)EE1E.place:=newtemp;E.code:=E1.code|gencode(E.place,:=,uminus,E1.place)E(E1)E.place:=newtemp;E.tacode:=E1.tacodeEidE.place:=id.name;E.code:=EnumE.code:=num.lexval编译原理与技术编译原理与技术6229.1中间语言三地址代

711、码中间语言三地址代码下面是本书用到的三地址指令下面是本书用到的三地址指令形式为形式为x:=yopz的赋值语句,其中的赋值语句,其中op是二目算术或逻辑运算。是二目算术或逻辑运算。形式为形式为x:=opz的赋值语句,其中的赋值语句,其中op是单目算术或逻辑运算。是单目算术或逻辑运算。形式为形式为x:=y的赋值语句,它把的赋值语句,它把y的值赋给的值赋给x。形式为形式为gotoL的无条件转移语句,即下一条将被执行的语句是带标号的无条件转移语句,即下一条将被执行的语句是带标号L的三的三地址语句。地址语句。形式为形式为ifxrelopygotoL或或ifbgotoL的条件转移语句,第一中语句对的条件转

712、移语句,第一中语句对x和和y施加关系运算施加关系运算relop(如(如),如果关系成立则执行标号为),如果关系成立则执行标号为L的三的三地址语句,否则执行后续语句;第二种形式中,地址语句,否则执行后续语句;第二种形式中,b为布尔变量或常量,若为布尔变量或常量,若b为为真,则执行标号为真,则执行标号为L的语句,否则执行后续语句。的语句,否则执行后续语句。形式为形式为paramx和和callp,n表示过程调用语句,其中表示过程调用语句,其中x表示参数,表示参数,n表示参数表示参数个数。源程序的过程调用语句个数。源程序的过程调用语句p(x1,x2,.,xn)通常生成如下的三地址码:通常生成如下的三地

713、址码:paramx1.paramxncallp,n编译原理与技术编译原理与技术6239.1中间语言三地址代码中间语言三地址代码返回语句返回语句returnx表示过程返回值表示过程返回值x,其中,其中x也可以不出现。也可以不出现。形式为形式为x:=yi和和xi:=y的索引赋值,第一条语句把相对于地的索引赋值,第一条语句把相对于地址址y后面第后面第i的单元的值赋给的单元的值赋给x;第而条语句把;第而条语句把y的值赋给相对于的值赋给相对于地址地址x后面第后面第i的单元里。的单元里。形式为形式为x:=&y,x:=*y和和*x:=y的地址指针赋值。第一个语句的地址指针赋值。第一个语句把把y的存储单元地址

714、赋给的存储单元地址赋给x,假定,假定y是一个名字或临时变量,代是一个名字或临时变量,代表一个具有左值的表达式,如表一个具有左值的表达式,如Ai,j;而;而x是指针变量或临时变是指针变量或临时变量,即量,即x的右值是某个对象的值。第二条语句是把的右值是某个对象的值。第二条语句是把y的存储单元的存储单元里的值赋给里的值赋给x,其中,其中y是指针变量或右值为地址的临时变量。第是指针变量或右值为地址的临时变量。第三条语句把三条语句把x所指向额对象的右值赋给所指向额对象的右值赋给y的左值。的左值。形式为形式为readx的的writex输入和输出语句,它们只有一个地址。输入和输出语句,它们只有一个地址。没

715、有地址的停机语句没有地址的停机语句halt。编译原理与技术编译原理与技术6249.1中间语言三地址指令的四元式实现中间语言三地址指令的四元式实现三地址指令操作符运算数1运算数2结果书写形式x:=yopzopyzx(op,y,z,x)x:=opzopzx(op,z,x)x:=y:=yx(:=,y,x)gotoLjumpL(jump,L)ifxrelopygotoLjropxyL(jrop,x,y,L)ifbgotoLjnzbL(jnz,b,L)paramxparamx(param,x,)callp,ncallpn(call,p,n,)returnxreturnx(return,x)x:=yi=y

716、ix(=,yi,x)xi:=y=yxi(=,y,xi)x:=&y&yx(&,y,x)x:=*yyx(,y,x)*x:=y:=yx(:=,y,x)readxreadx(read,x)writexwritex(write,x)halthalt(halt,)编译原理与技术编译原理与技术6259.2声明语句的翻译声明语句的翻译u过程中的声明过程中的声明例如,对于一个例如,对于一个C+的声明序列:的声明序列:intindex;doublesum;chartoken;如果该声明在内存中可以得到的首地址是如果该声明在内存中可以得到的首地址是1000,那么局部名,那么局部名字字x的地址都可以按照下列公式计算:

717、的地址都可以按照下列公式计算:index的地址是的地址是1000sum的地址是的地址是 1000+index的字节宽度的字节宽度10041000+4token的地址是的地址是1004+sum的字节宽度的字节宽度1012100+4+8实际上,为声明语句中的名字分配存储的主要问题就是计算实际上,为声明语句中的名字分配存储的主要问题就是计算每个名字的相对地址,即相对于基址的偏移,再加上为该声每个名字的相对地址,即相对于基址的偏移,再加上为该声明分配的基址(例如过程活动记录的首址),就可以在存储明分配的基址(例如过程活动记录的首址),就可以在存储器中访问到每个名字。器中访问到每个名字。编译原理与技术编

718、译原理与技术6269.2声明语句的翻译声明语句的翻译非终结符非终结符P产生一系列形如产生一系列形如“Tid”的声明的声明语句。语句。全局变量全局变量offset记录下一个可用的相对地记录下一个可用的相对地址初始化址初始化offset为为0。以后每次遇到一个。以后每次遇到一个新的名字,将该名字连同类型和当前的新的名字,将该名字连同类型和当前的offset填入符号表,然后使填入符号表,然后使offset增加该名增加该名字所表示的数据的宽度。字所表示的数据的宽度。过程过程enter(name,type,offset)用来把名字用来把名字为为name的标识符填入符号表。的标识符填入符号表。综合属性综合

719、属性type和和width分别表示非终结符分别表示非终结符T的类型和宽度。属性的类型和宽度。属性type的取值范围是的取值范围是基本类型基本类型integer和和real以及应用类型构造以及应用类型构造器器pointer和和array得到的结构类型。得到的结构类型。假设整数的宽度是假设整数的宽度是4,实数的宽度是,实数的宽度是8,指针类型的宽度是指针类型的宽度是4,数组所占用的存储,数组所占用的存储单元个数是数组元素的个数乘以基类型单元个数是数组元素的个数乘以基类型元素的宽度。元素的宽度。文法规则语义动作PDoffset:=0DD1;D2Did:Tenter(id.name,T.type,of

720、fset);offset:=offset+T.widthTintegerT.type:=integer;T.width:=4TrealT.type:=real;T.width:=8TarraynumofT1T.type:=array(num.val,T1.type);T.width:=num.val*T1.widthTT1T.type:=pointer(T1.type);T.width:=4编译原理与技术编译原理与技术6279.2声明语句的翻译声明语句的翻译u记录作用域信息记录作用域信息考虑像考虑像Pascal和和Ada这样允许过程嵌套的语言,主要讨论如何保存声明这样允许过程嵌套的语言,主要讨

721、论如何保存声明的作用域信息(的作用域信息(6.4)。所讨论的文法如下:)。所讨论的文法如下:PDSDD;D|id:T|procid;D;S第一条产生式提供程序的规则,在声明第一条产生式提供程序的规则,在声明D后面跟随语句序列后面跟随语句序列S。声明语。声明语句允许嵌套地声明过程。为了简化讨论的问题,我们不考虑递归调用句允许嵌套地声明过程。为了简化讨论的问题,我们不考虑递归调用的过程,也不考虑过程的参数说明,这是因为参数可以类似于表的过程,也不考虑过程的参数说明,这是因为参数可以类似于表9.6的的局部变量的处理技术。局部变量的处理技术。在处理嵌套过程时为每个过程都建立一张独立的符号表,每个符号表

722、在处理嵌套过程时为每个过程都建立一张独立的符号表,每个符号表都有自己的符号表指针都有自己的符号表指针tableptr、基址、基址base和自己的和自己的offset。这些符号表。这些符号表可以一边扫描源程序一边建立并且完成内存分配。可以一边扫描源程序一边建立并且完成内存分配。每遇到每遇到Dprocid;D1;S时,就创建一张新的符号表,把时,就创建一张新的符号表,把D1中的所有中的所有说明都添在该符号表;用一个指针记录包含说明都添在该符号表;用一个指针记录包含D1的最近的外围过程的最近的外围过程D的的符号表,符号表,id也是其中的一个局部名字。在最近的外围过程也是其中的一个局部名字。在最近的外

723、围过程D的符号表中的符号表中对于每个直接嵌入其中的过程,也都有一个指向它们符号表指针。对于每个直接嵌入其中的过程,也都有一个指向它们符号表指针。编译原理与技术编译原理与技术6289.2声明语句的翻译声明语句的翻译表表9.7给出了多趟扫描含有嵌套过程的语法制导翻译规则,它使用了两个给出了多趟扫描含有嵌套过程的语法制导翻译规则,它使用了两个数据结构:数据结构:l一个保存各外层过程的符号表指针的指针栈一个保存各外层过程的符号表指针的指针栈tblptr,一个对应的存放外层过程,一个对应的存放外层过程相对地址的偏移栈相对地址的偏移栈offset。指针栈。指针栈tblptr的栈顶的栈顶top(tblptr

724、)总是指向当前层的符总是指向当前层的符号表;偏移栈号表;偏移栈offset的栈顶保存了当前层已经处理的声明的偏移之和。的栈顶保存了当前层已经处理的声明的偏移之和。整个翻译模式同时使用了下列操作:整个翻译模式同时使用了下列操作:lSymbolTable*mktable(SymbolTable*previous,integerbase)创建一张新创建一张新的符号表,填入基址,把参数指针的符号表,填入基址,把参数指针previous放在该表的表头,表示指向已经创放在该表的表头,表示指向已经创建好的一个符号表,比如刚好包围嵌入过程的符号表;返回指向这张新表的指建好的一个符号表,比如刚好包围嵌入过程的符

725、号表;返回指向这张新表的指针。符号表的信息还可以包含局部变量所需要的存储单元个数等。针。符号表的信息还可以包含局部变量所需要的存储单元个数等。lvoidenter(SymbolTable*table,Stringname,DTypetype,Integeroffset)在指针在指针table指向的符号表中为变量名指向的符号表中为变量名name建立一个新项,类型是建立一个新项,类型是type,在该,在该表中的相对地址是表中的相对地址是offset。lvoidaddwidth(SymbolTable*table,Integerwidth)把符号表把符号表table的所有项的所有项的累加宽度记录在该

726、表的首部。的累加宽度记录在该表的首部。lvoidenterproc(SymbolTable*table,Stringname,SymbolTable*newtable)在在table指向的符号表中为过程名指向的符号表中为过程名name建立一个新项,指针建立一个新项,指针newtable指向它的符号表。指向它的符号表。编译原理与技术编译原理与技术6299.2声明语句的翻译声明语句的翻译文法规则语义动作PMDSaddwidth(top(tblptr),top(offset);pop(tblptr);pop(offset)Mt:=mktable(nil,0);push(t,tblptr);push(

727、0,offset)DD1;D2Dprocid;ND1;St:=top(tblptr);wide:=top(offset);addwidth(t,wide);pop(tblptr);pop(offset);top(offset):=top(offset)+wide;enterproc(top(tblptr),id.name,t)Did:Tenter(top(tblptr),id.name,T.type,top(offset);top(offset):=top(offset)+T.widthNt:=mktable(top(tblptr),top(offset);push(t,tblptr);pus

728、h(wide,offset)编译原理与技术编译原理与技术630对于PDS,首先要建立一张空的符号表,动作要在DS之前完成。为了使整个动作都在文法产生式的末尾,我们采取了7.3.3.3的技术,引入一个非终结符M和产生式,以消除嵌入在产生式中的语义动作。M的语义动作是初始化外层符号表的指针栈tblptr和偏移栈offset:最外层过程没有包围的过程了,所以指针为空null,相对地址和基址都为0。这样,PMDS的语义动作就是把当前(栈顶)符号表top(tblptr)的所有项的累加宽度记录在该表的首部,表示该过程的局部变量、过程等声明所需要的总的存储单元数;之后,退掉指针栈tblptr和偏移栈offs

729、et的栈顶。对嵌入过程说明Dprocid;D;S做了类似的语法处理,改成Dprocid;ND1;S。首先,在扫描到嵌入的过程D1之前,需要为它建立一个空的符号表,让它指向直接外围过程的符号表,初始化这个新过程的偏移为0,它的基址就是直接外围过程当前所有条目(变量、过程)宽度的总和。这些都由对应N的语义动作完成。然后,执行Dprocid;ND1;S右部的语义动作:首先把D1产生的所有声明的宽度存入它的符号表内(此时放在栈顶的都是有关D1的值),退掉指针栈tblptr和偏移栈offset的栈顶表示结束嵌入过程的处理,继续处理外层过程的声明:把D的当前的条目宽度加上D1所有声明的宽度,为这个嵌入过程

730、的名字id建立符号表条目。9.2声明语句的翻译声明语句的翻译编译原理与技术编译原理与技术631programsort(input,output)vara:array1.10ofinteger;x:integer;procedurereadarray;vari:integer;begin.end;44tblptr栈顶offset栈顶1扫描完sort局部变量的声明时2、扫描过程完readarray局部声明tblptr栈offset栈44tblptr栈顶offset栈顶nullaarray(1.10,integer)xinteger0404iintegerreadarray0sort00440nul

731、laarray(1.10,integer)xinteger040sort00在符号表中,首部包含三个子域:左域是指向直接外围过程符号表的指针,主程序sort的为空null;中域保存该表的基址,右域记录了该过程声明所占存储单元的总的个数(累加宽度之和)。符号表中记录了变量名、类型及其在过程中的相对地址:例如,变量a在sort中的相对偏移就是基址0,而x的相对偏移是在分配完10个整型数(4个字节)之后的地址,即10440。当扫描完readarray的局部变量声明时(对应图9.4中步骤2),首先执行就执行N对应的动作,得到基址44,tblptr的栈顶是指向readarray符号表的指针;然后执行Di

732、d:T对应的动作,在这个符号表中存入i的信息,同时得到readarray符号表的局部声明的累加宽度4。9.2声明语句的翻译声明语句的翻译编译原理与技术编译原理与技术6323、扫描完过程readarray48nullaarray(1.10,integer)xinteger040iintegerreadarray0sortreadarraytblptr栈顶offset栈顶00444programsort(input,output)vara:array1.10ofinteger;x:integer;procedurereadarray;vari:integer;begin.end;扫 描 完 rea

733、darray过 程 时 , 用addwidth把对应readarray的offset值4存入该过程符号表的首部,把指向readarray的指针及其偏移量之和分别从指针栈tblptr和偏移栈offset的栈顶删除,修改sort的当前总偏移为48,最后在sort中添加过程readarray的条目,填入一个指向readarray符号表的指针。9.2声明语句的翻译声明语句的翻译编译原理与技术编译原理与技术633040484、扫描完程序sortnillaarray(1.10,integer)xinteger04044iintegerreadarray0sortreadarrayexchangequick

734、sortexchangekintegerquicksortvintegerpartitioniintegerpartitionjinteger064448048568programsort(input,output)vara:array1.10ofinteger;x:integer;procedurereadarray;vari:integer;begin.end;procedureexchange(i,j:integer);begin.end;procrdurequicksort(m,n:integer);vark,v:integer;functionpartition(y,z:intege

735、r):integervari,j:integer;begin.end;begin.endquicksort;begin.endsort;图9.4中的步骤4示意了处理完整个程序后所建立的符号表。最终得到sort中声明所占用的最大存储单元数量是64(=44+4+8+8)。9.2声明语句的翻译声明语句的翻译u记录中的域名记录中的域名现在,我们可以对表现在,我们可以对表9.6的语言文法增添一个的语言文法增添一个新的产生式,构造程序语言的基本结构类型,新的产生式,构造程序语言的基本结构类型,记录类型:记录类型:TrecordDend对记录的处理类似于对过程的处理。我们为对记录的处理类似于对过程的处理。我

736、们为每个记录建立一张符号表,保存每个域的名每个记录建立一张符号表,保存每个域的名字及其类型等信息。由于在表字及其类型等信息。由于在表9.7中的过程定中的过程定义不影响域宽的计算,因此,允许上述产生义不影响域宽的计算,因此,允许上述产生式的式的D包含过程定义,这样做的目的是为了简包含过程定义,这样做的目的是为了简化翻译模式化翻译模式编译原理与技术编译原理与技术6349.2声明语句的翻译声明语句的翻译u当编译扫描到关键子当编译扫描到关键子recordrecord的时候,与非的时候,与非终结符终结符L L所对应的动作是,为该记录中的所对应的动作是,为该记录中的各个域名创建一张新的记录符号表。把指各个

737、域名创建一张新的记录符号表。把指向该表的指针压入指针栈向该表的指针压入指针栈tblptrtblptr,并把相,并把相对地址对地址0 0压入偏移栈压入偏移栈offsetoffset。u根据表根据表9.79.7可知,产生式可知,产生式D D id:T id:T的动作的动作是把域名是把域名idid的有关信息填入该记录的符号的有关信息填入该记录的符号表。当记录中的所有域名都被检查之后,表。当记录中的所有域名都被检查之后,在在offsetoffset的栈顶就存放着记录中的所有数的栈顶就存放着记录中的所有数据对象的总的宽度。据对象的总的宽度。u在表在表9.89.8中中endend之后的动作是把之后的动作是

738、把offsetoffset栈顶栈顶的总的域宽度存入记录类型的总的域宽度存入记录类型T T的综合属性的综合属性widthwidth,T T的类型属性的类型属性typetype值则是通过对指值则是通过对指向本记录符号表的指针使用类型构造符向本记录符号表的指针使用类型构造符recordrecord得到。得到。 文法规则语义动作TrecordLDendT.type:=pointer(top(tblptr);T.width:=top(offset)Lt:=mktable(nil,0);push(t,tblptr);push(0,offset)编译原理与技术编译原理与技术6359.3赋值语句的翻译赋值语句

739、的翻译赋值语句是命令式和面向对象语言改变存储器的内容赋值语句是命令式和面向对象语言改变存储器的内容以及程序状态的基本操作,它的一般语法定义是:以及程序状态的基本操作,它的一般语法定义是:V:=E;V和和E可以是简单类型的表达式,也可以是数组元素、可以是简单类型的表达式,也可以是数组元素、记录中子域变量、指针变量的内容或地址等。记录中子域变量、指针变量的内容或地址等。赋值语句的语义是:把右部表达式的值赋给左部变量,赋值语句的语义是:把右部表达式的值赋给左部变量,对于许多语言还要求左部变量与右部表达式的类型相对于许多语言还要求左部变量与右部表达式的类型相容。赋值语句的执行步骤包括:容。赋值语句的执

740、行步骤包括:计算右部表达式计算右部表达式E的值;的值;必要时对必要时对E的值进行类型转换,强制到的值进行类型转换,强制到V的类型;的类型;计算做变量计算做变量V的地址;的地址;把把E的值送入的值送入V的地址。的地址。这是设计翻译赋值语句的基础。这是设计翻译赋值语句的基础。编译原理与技术编译原理与技术6369.3赋值语句的翻译赋值语句的翻译u简简单单算算术术表表达达式式及及赋赋值语句值语句文法规则语义规则Sid:=Ep:=lookup(id.name);ifp=nullthenerrorelseemit(p,:=,E.place)EE1+E2E.place:=newtemp;emit(E.pla

741、ce,:=,E1.place,+,E2.place)EE1E2E.place:=newtemp;emit(E.place,:=,E1.place,E2.place)EE1E.place:=newtemp;emit(E.place,:=,uminus,E1.place)E(E1)E.place:=E1.place;Eidp:=lookup(id.name);ifp=nullthenerrorelseE.place:=pEnumE.place:=num.lexval编译原理与技术编译原理与技术637我们在三地址代码中直接使用了表达式的名字,并将它理解为指向符号表中该名字的入口指针。现在,我们使用函

742、数lookup(id.name)在符号表中查找名为id.name的标识符的入口地址。表9.9给出了如何使用符号表把简单表达式及赋值语句翻译成三地址代码的语义动作。其中E的属性place表示E在符号表中的入口,使用输出代码的函数,emit(id.place,:=,E.place)表示把赋值语句的三地址码按顺序地写在一个输出文件。9.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术638在嵌套结构中查询名字的函数lookup的一个实现。首先,设计使用的符号表的数据结构SymbolTable(参考图9.4),给出主要的结构和子域如下:structIditemStringname;DType

743、type;AddressoffsetstructSymbolTableSymbolTable*previous;Integerbase,offset;Idlistids;/*名字项的表*/Proclistprocs; /*过程项的表*/假设符号表中的变量项放在表结构类型Idlist的变量ids中,并且假设函数search(name,list)当前过程的符号表中查询name是否在局部标识符表list中:如果在就返回指向名字是name的标识符项,否则返回null。ditem*lookup(Stringname,SymbolTable*table)SymbolTable*next,*p;next=t

744、able;while(next!=null)p=serach(name,nextids);ifp!=nullreturnp;next=nextprevious;returnnull;9.3赋值语句的翻译赋值语句的翻译在翻译规则中对中间代码就有两种表示方法:在翻译规则中对中间代码就有两种表示方法:利用生成函数利用生成函数gencode把中间代码存入符号的属性把中间代码存入符号的属性code中,中,并可以利用并置符号把代码段连接起来,形成更大的代码段并可以利用并置符号把代码段连接起来,形成更大的代码段直至程序,随时整理并输出到文件上。这个方法更适合多趟直至程序,随时整理并输出到文件上。这个方法更适

745、合多趟扫描的编译构造,允许在多次分析源程序的过程中多次访问扫描的编译构造,允许在多次分析源程序的过程中多次访问和处理存在属性内的中间代码。和处理存在属性内的中间代码。一边分析和翻译源程序的语句,一边按照源程序的顺序把中一边分析和翻译源程序的语句,一边按照源程序的顺序把中间代码(用函数间代码(用函数emit)写在一个输出文件中。这个方法可以)写在一个输出文件中。这个方法可以在一趟编译扫描过程中同时完成代码的翻译,但是,没有办在一趟编译扫描过程中同时完成代码的翻译,但是,没有办法对产生的代码进行优化,因为函数法对产生的代码进行优化,因为函数emit通常是把中间代码通常是把中间代码输出在一个顺序文件

746、中。输出在一个顺序文件中。编译原理与技术编译原理与技术6399.3赋值语句的翻译赋值语句的翻译u数组元素的引用数组元素的引用数组的有关信息,如分配的基址、维数、每数组的有关信息,如分配的基址、维数、每个维的上下界、维数的大小、成员数据占用个维的上下界、维数的大小、成员数据占用的存储大小等,是存放在一个称作数组向量的存储大小等,是存放在一个称作数组向量的符号表中。的符号表中。我们主要研究如何计算静态数组中一个数组我们主要研究如何计算静态数组中一个数组元素的地址,并翻译成三地址代码。我们不元素的地址,并翻译成三地址代码。我们不讨论诸如数组的下标是否越界、类型是否合讨论诸如数组的下标是否越界、类型是

747、否合适等其它问题。适等其它问题。编译原理与技术编译原理与技术6409.3赋值语句的翻译赋值语句的翻译u数组元素的引用数组元素的引用假如一维数组假如一维数组A的下界是的下界是low,分配的相对,分配的相对地址是地址是base,即,即Alow的相对地址,每个元的相对地址,每个元素的宽度是素的宽度是w,那么,那么A的第的第i个元素个元素Ai的起的起始地址是:始地址是:base+(i low)w (9.1)把它整理成把它整理成iw+(base loww)这样,这样,(base loww)就可以在编译时刻完成,就可以在编译时刻完成,从而减少了运行时的地址计算。从而减少了运行时的地址计算。编译原理与技术编

748、译原理与技术6419.3赋值语句的翻译赋值语句的翻译二维数组的元素也必须转化为一维方式存储,通常有两种方二维数组的元素也必须转化为一维方式存储,通常有两种方式存储:以行为主或以列为主。对于多维数组的存储,式存储:以行为主或以列为主。对于多维数组的存储,FORTRAN使用以列为主,使用以列为主,Pascal、C+和和Java都以行为主。都以行为主。在行为主的两维数组的情况下,在行为主的两维数组的情况下,Ai,j的地址可以用下列公式的地址可以用下列公式计算:计算:base+(i low1)n2+(j low2)w其中其中low1和和low2分别是这两维的下界,分别是这两维的下界,n2是第是第2维的

749、大小,即维的大小,即若若high2是是j值的上界,则有值的上界,则有n2=high2 low2+1。假定。假定i和和j的的值在编译时不知道,而可以知道其它值,那么,把上式变换值在编译时不知道,而可以知道其它值,那么,把上式变换成成(in2+j)w+(base (low1n2)+low2)w) (9.2)同样,后一项子表达式同样,后一项子表达式(base (low1n2)+low2)w)可以在编可以在编译时计算。译时计算。编译原理与技术编译原理与技术6429.3赋值语句的翻译赋值语句的翻译推广到推广到k维数组,得到维数组,得到Ai1,i2,.,ik的地址表达式如下:的地址表达式如下:(.(i1n

750、2+i2)n3+i3).)nk+ik)w+base (.(low1n2+low2)n3+low3).)nk+lowk)w(9.3)假定对所有的假定对所有的j,nj=highj lowj+1是固定的,那么,公式是固定的,那么,公式(9.3)的第的第2行就可以在编译时计算出来,存放在数组行就可以在编译时计算出来,存放在数组A的信息域里的信息域里Ai1,i2,.,ik地址的动态部分需要在运行时计算,必须设计出计算这个地址的动态部分需要在运行时计算,必须设计出计算这个地址的代码。地址的代码。下面,我们考虑如何计算数组引用的动态部分下面,我们考虑如何计算数组引用的动态部分(.(i1n2+i2)n3+i3

751、).)nk+ik(9.4)它可以用下列递推公式它可以用下列递推公式e1=i1em=em-1nm+im(9.5)进行计算,直到进行计算,直到m=k为止。然后将为止。然后将ek乘以数组元素的宽度乘以数组元素的宽度w,再加上公,再加上公式式(9.3)的第的第2行就可以得到数组引用元素的地址。行就可以得到数组引用元素的地址。编译原理与技术编译原理与技术6439.3赋值语句的翻译赋值语句的翻译从从(9.5)的递推计算中可以看出,除了第一个下标的递推计算中可以看出,除了第一个下标i1以外,其它每个以外,其它每个em的计算都要访问数组的符号表项,以便得到各维的大小。如果在的计算都要访问数组的符号表项,以便得

752、到各维的大小。如果在表表9.9的文法中出现的文法中出现id的地方也允许出现下面产生式中的地方也允许出现下面产生式中L出现,则可出现,则可以把数组元素引用加入到赋值语句中。以把数组元素引用加入到赋值语句中。LidElist|idElistElist,E|E如果仅用综合属性,在处理如果仅用综合属性,在处理ElistElist,E和和ElistE的时候就访的时候就访问不到数组问不到数组L的有关信息,因为这是与数组名的有关信息,因为这是与数组名id关联的信息。因此关联的信息。因此把产生式等价地改写成把产生式等价地改写成LElist|idElistElist,E|idE即数组名与最左下标表达式连在一起,

753、这样在翻译即数组名与最左下标表达式连在一起,这样在翻译Elist的过程中都的过程中都能知道符号表中相应于数组名能知道符号表中相应于数组名id的信息。的信息。编译原理与技术编译原理与技术644要生成数组引用的三地址代码,关键是把要生成数组引用的三地址代码,关键是把(9.5)的计算的计算与数组引用的文法联系起来。与数组引用的文法联系起来。9.3赋值语句的翻译赋值语句的翻译u对对Elist设置如下的综合属性:设置如下的综合属性:array表示指向符号表中相表示指向符号表中相应数组名表项的指针,应数组名表项的指针,ndim保存已经分析过的下标表达保存已经分析过的下标表达式的个数,式的个数,place保

754、存根据下标表达式计算的值,即公式保存根据下标表达式计算的值,即公式(9.5)中中em的值。的值。u函数函数esizeof(array)给出数组元素的宽度,给出数组元素的宽度,limit(array,j)返返回回array所指示数组的第所指示数组的第j维的维数维的维数nj,base(array)给出给出符号表中符号表中array所指示数组的基址,即公式所指示数组的基址,即公式(9.3)中的中的base,函数,函数adrconst(array)表示公式表示公式(9.3)第第2行的减号之后的行的减号之后的值。值。u左值左值L有两个属性,有两个属性,place和和offset。当。当L是简单名字时,是

755、简单名字时,L.offset为为null,L.place是指向符号表中对应此名字表项是指向符号表中对应此名字表项的指针;的指针;u否则,否则,L.place=base(array) adrconst(array),表示公,表示公式式(9.3)中第中第2行的值,行的值,L.offset表示某个数组元素的索引,表示某个数组元素的索引,等于等于Elist.placew。编译原理与技术编译原理与技术6459.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术646下面考虑在赋值语句中加入数组元素之后的一种翻译模式,把语义动作加入到下列文法中:(1)SL:=E(2)EE1+E2(3)E(E1)(

756、4)EL(5)LElist(6)Lid(7)ElistElist,E(8)ElistidE9.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术647若L是个简单名字,则产生正常的赋值;否则,产生对L所指示地址的索引赋值:(1)SL:=EifL.offset=nullthenemit(L.place,:=,E.place)elseemit(L.place,L.offset,:=,E.place)算术表达式语义动作和表9.9一样:(2)EE1+E2E.place:=newtemp;emit(E.place,:=,E1.place,+,E2.place)(3)E(E1)E.lpace:=E

757、1.lpace9.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术648若L是个简单名字,则产生正常的赋值;否则,产生对L所指示地址的索引赋值:(1)SL:=EifL.offset=nullthenemit(L.place,:=,E.place)elseemit(L.place,L.offset,:=,E.place)算术表达式语义动作和表9.9一样:(2)EE1+E2E.place:=newtemp;emit(E.place,:=,E1.place,+,E2.place)(3)E(E1)E.lpace:=E1.lpace9.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术

758、649若一个数组引用L归约到E时,即分析的源程序串中出现了a10或b5,8这样的数组引用的时候,则需要L的右值,可以用索引得到存储单元地址L.placeL.offset的内容:(4)ELifL.offset=nullthenE.place:=L.placeelsebeginE.place:=newtemp;emit(E.place,:=,L.place,L.offset,)end9.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术650下面计算在这个归约中用到的L的属性值:(5)LElistL.place:=newtemp;L.offset:=newtemp;emit(L.place

759、,:=,base(Elist.array),adrconst(Elist.array);emit(L.offset,:=,Elist.place,*,esizeof(Elist.aray);把offset置为空值null,表示L是一个简单变量名:(6)LidLoffset:=null;L.place:=id.place9.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术651每当扫描到下标表达式时,就运用递推公式(9.5)。在下面的语义动作中,Elist.place和Elist1.place分别对应公式(9.5)中的em和em-1。若Elist1有m1个元素,则Elist有m个元素。

760、(7)ElistElist1,Et:=newtemp;m:=Elist1.ndim+1;emit(t,:=,Elist1.place,*,limit(Elist1.array,m);emit(t,:=,t,+,E.place);Elist.array:=Elist1.array;E.place:=t;E.ndim:=m对于最后一个产生式中的语义动作,E.place同时表示E的值和m=1时em的值:(8)ElistidEElist.place:=E.place;Elist.ndim:=1;Elist.array:=id.place9.3赋值语句的翻译赋值语句的翻译例例9.6按照本节讨论的赋值语句

761、的翻译模式,把下面按照本节讨论的赋值语句的翻译模式,把下面Java的赋值语句翻译成三地址代码:的赋值语句翻译成三地址代码:intx,i,j;int1020matrix;x=matrixi,j;对应赋值语句对应赋值语句x=matrixi,j的三地址代码是:的三地址代码是:t1:=i*20t1:=t1+jt2:=matrix 84t3:=4*t1t4:=t2t3x:=t4编译原理与技术编译原理与技术6529.3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术653这个二维数组的这个二维数组的n1=10,n2=20,每个元素的每个元素的字宽是字宽是4。当扫描到当扫描到matrixi的时候,开

762、始分析数组,得的时候,开始分析数组,得Elist.place:=i,Elist.ndim:=1和和Elist.array:=matrix,对应深度优先的第一,对应深度优先的第一个三叉树树根的结点,此时还没有产生三地个三叉树树根的结点,此时还没有产生三地址代码。址代码。当扫描到当扫描到i,j时(对应产生式时(对应产生式ElistElist1,E),开始产生代码:),开始产生代码:t1:=i*20和和t1:=t1+j。当扫描到当扫描到j时(对应产生式时(对应产生式LElist),产),产生下列代码:生下列代码:t2:=matrix 84和和t3:=4*t1。其中其中adrconst(array)是

763、是84(假设按照我们语(假设按照我们语法定义的方式,数组的第一个下标是法定义的方式,数组的第一个下标是1而不是而不是Java语言的语言的0),数组的基址),数组的基址base用数组名用数组名字表示。字表示。当扫描完数组串当扫描完数组串matrixi,j而归约到表达式而归约到表达式E时(对应产生式时(对应产生式EL),产生一条代码:),产生一条代码:t4:=t2t3。最后,当扫描到完归约到句子最后,当扫描到完归约到句子S时(对应产时(对应产生式生式SL:=E),产生一条代码:),产生一条代码:x:=t4。SE.place:=t4L.place:=xL.offset:=nullx:=L.place

764、:=t2L.offset:=t3Elist.place:=t1Elist.ndim:=2Elist.array:=matrixElist.place:=iElist.ndim:=1Elist.array:=matrix,E.place:=jL.place:=jL.offset:=nulljmatrixE.place:=iL.place:=iL.offset:=nulli9.3赋值语句的翻译赋值语句的翻译u记录和指针的引用记录和指针的引用9.3.1中对嵌套过程的符号搜索函数中对嵌套过程的符号搜索函数lookup的的C语言表达式语言表达式next previous显示,显示,next是一个指向某个

765、记录的指针,这个记录类型有一是一个指向某个记录的指针,这个记录类型有一个子域名个子域名previous。按照表。按照表9.7和和9.8的翻译模式,的翻译模式,next的类型表达式为的类型表达式为pointer(record(t),即,即next指向的类型就是指向的类型就是record(t),而它又是通过把,而它又是通过把record类型构造器作用在一个指向记录符号表的指针类型构造器作用在一个指向记录符号表的指针t上得到的类型。上得到的类型。这里的这里的record(t)类型就是类型就是SymbolTable,它有一个子域名为,它有一个子域名为previous,可以根据相对地址得到。可以根据相对

766、地址得到。文法规则语义动作EE1emit(E.place,:=,&E1);EE1.fieldt:=newtemp;emit(t,:=,&E1,+,offset(E1,field);emit(E.place,:=,t);编译原理与技术编译原理与技术654可以扩充赋值语句,允许其左部为可以扩充赋值语句,允许其左部为地址或者记录中的某个域。表地址或者记录中的某个域。表9. 109. 10还给出了部分翻译规则,其中函数还给出了部分翻译规则,其中函数offset(r, f)offset(r, f)返回记录变量返回记录变量r r中域中域f f的相对地址,可从其符号表得到。的相对地址,可从其符号表得到。9.

767、3赋值语句的翻译赋值语句的翻译编译原理与技术编译原理与技术655例例9.7考虑下面定义的数据结构、变量声明typedefstructtreenodeintval;structtreenode*ltree,*rtree;TreeNode;TreeNode*p以及三条赋值语句:pltree=p;p=prtree;pval=100;可以把它们翻译成下列的三地址代码:t1:=p+offset(*p,ltree)*t1:=pt2:=p+offset(*p,rtree)p:=*t2t3:=p+offset(*p,val)*t3:=1009.3赋值语句的翻译赋值语句的翻译-类型转换类型转换u类型转换类型转换

768、按照一般的语言规则,在赋值和算术运算时要进行必按照一般的语言规则,在赋值和算术运算时要进行必要的类型转换。对于赋值语句要的类型转换。对于赋值语句id:=E,必要时把,必要时把E的的值类强制转换到值类强制转换到id的类型。的类型。下面给出了赋值语句下面给出了赋值语句Sid:=E进行必要转换的完进行必要转换的完整的翻译规则,其中使用了属性整的翻译规则,其中使用了属性type和单目运算符和单目运算符inttoreal和和realtoint,inttorealx把整型数把整型数x转换成实型转换成实型数,数,realtointx把实数把实数x转换成整数。转换成整数。编译原理与技术编译原理与技术6569.

769、3赋值语句的翻译赋值语句的翻译-类型转换类型转换编译原理与技术编译原理与技术657p:=lookup(id.name);ifp=nullthenerror;elseifE.type=p.typethenemit(p,:=,E.place);elseifE.type=realandp.type=integerthenbegint:=newtemp;mit(t,:=,realtoint,E.place);emit(p,:=,t)endelseifE.type=integerandp.type=realthenbegint:=newtemp;emit(t,:=,inttoreal,E.place);

770、emit(p,:=,t)endelseE.type:=typeerror9.3赋值语句的翻译赋值语句的翻译-类型转换类型转换例例9.8把下列的执行语句翻译成三地址码,并进行把下列的执行语句翻译成三地址码,并进行必要的类型转换。必要的类型转换。inttime,bonus;floatsalary;bonus=salary+time 0.85;按照上述语义规则,赋值语句可以翻译成下列按照上述语义规则,赋值语句可以翻译成下列三地址代码:三地址代码:t1:=inttorealtimet2:=t1real 0.85t3:=salaryfloat+t2t4:=traltointt3bonus:=t4编译原理

771、与技术编译原理与技术6589.4基本控制结构的翻译基本控制结构的翻译u结构化理论证明,任何程序都可以只用三结构化理论证明,任何程序都可以只用三种基本的控制结构实现,它们是顺序结构、种基本的控制结构实现,它们是顺序结构、分支结构和循环结构。分支结构和循环结构。一般的程序设计语言都提供了这三种结构的一般的程序设计语言都提供了这三种结构的不同实现,为了简化编程,还提供了开关、不同实现,为了简化编程,还提供了开关、过程调用等转向语句。过程调用等转向语句。u本节讨论与布尔表达式的计算密切关联的本节讨论与布尔表达式的计算密切关联的基本控制结构的翻译。基本控制结构的翻译。编译原理与技术编译原理与技术6599

772、.4.1布尔表达式的翻译布尔表达式的翻译u本章用到的布尔表达式的文法本章用到的布尔表达式的文法:EEorE|EandE|notE|(E)|idrelopid|id|true|false其中其中E是算术表达式,是算术表达式,relop是关系运算符(如是关系运算符(如),为简单起见,它的运算数都是布尔变量。),为简单起见,它的运算数都是布尔变量。按照惯例,我们假定按照惯例,我们假定or和和and都是左结合,而且都是左结合,而且or的的优先级最低,其次是优先级最低,其次是and,最后是,最后是not。u在程序语言中,布尔表达式有两个基本作用:在程序语言中,布尔表达式有两个基本作用:计算逻辑值,在控制

773、流语句如计算逻辑值,在控制流语句如if-then-else或或while-do中当作条件表达式。中当作条件表达式。u因此一般有两种方式计算布尔表达式的值。因此一般有两种方式计算布尔表达式的值。编译原理与技术编译原理与技术6609.4.1布尔表达式的翻译布尔表达式的翻译方法方法1:把布尔值用数:把布尔值用数值表示,如同计算算值表示,如同计算算术表达式一样,求出术表达式一样,求出布尔表达式中所有子布尔表达式中所有子表达式的值。表达式的值。习惯上用习惯上用1代表真,代表真,0代表假。例如,一个代表假。例如,一个布尔式的计算过程:布尔式的计算过程:编译原理与技术编译原理与技术6611or(not0an

774、d1)andnot1=1or(1and1)andnot1=1or1andnot1=1or1and0=1or0=19.4.1布尔表达式的翻译布尔表达式的翻译方法方法2:用控制流,即用控制到达程序的位置:用控制流,即用控制到达程序的位置来代表布尔表达式的值。来代表布尔表达式的值。l这种方式由于可以减少布尔表达式的计算过程、这种方式由于可以减少布尔表达式的计算过程、允许采取某种优化而特别适于控制流语句中的布允许采取某种优化而特别适于控制流语句中的布尔表达式。尔表达式。l例如,对于上面形式的布尔表达式例如,对于上面形式的布尔表达式Aor(notBandC)andnotD,如果知道,如果知道A的值为真,

775、就可以的值为真,就可以确定整个表达式为真,而不用进行下面的计算了。确定整个表达式为真,而不用进行下面的计算了。这种计算方式因而称为这种计算方式因而称为“短路短路”方法。方法。编译原理与技术编译原理与技术6629.4.1布尔表达式的翻译布尔表达式的翻译u比较比较每个程序语言的语义决定是否计算布尔表达式的每个每个程序语言的语义决定是否计算布尔表达式的每个部分。例如,部分。例如,C和和C+采用短路计算方法,一旦能够采用短路计算方法,一旦能够决定整个布尔表达式的值,就不再计算后面的部分了;决定整个布尔表达式的值,就不再计算后面的部分了;而而Pascal和和Ada则是无论如何都要布尔表达式的所有则是无论

776、如何都要布尔表达式的所有部分求出值。部分求出值。这两种计算方法难分仲伯,个有优劣。短路法可以加这两种计算方法难分仲伯,个有优劣。短路法可以加快编译和程序的运行速度,但是,如果允许布尔表达快编译和程序的运行速度,但是,如果允许布尔表达式有副作用(即含有改变非局部变量的函数),那么,式有副作用(即含有改变非局部变量的函数),那么,程序的运行就捉么不定了。程序的运行就捉么不定了。编译原理与技术编译原理与技术6639.4.1布尔表达式的翻译布尔表达式的翻译u用数值表示的布尔表达式的翻译用数值表示的布尔表达式的翻译编译原理与技术编译原理与技术664这种布尔表达式的中间代码和算术表达式的中间代码没有多少区

777、别,只需从左至右按类似算术表达式的求值方法计算。例如,逻辑表达式:aor(notbandc)andnotd的三地址代码是:t1:=notbt2:=t1andct3:=notdt4:=t2andt3t5:=aort4形如ab的关系表达式可以等价地写成ifabthen1else0,可以翻译成如下的三地址代码(由于涉及转移指令,所以需要给语句编号,假定的开始语句100是随意的):100:ifabgoto103101:t:=0102:goto104103:t:=1104:9.4.1布尔表达式的翻译布尔表达式的翻译u例例9.9按照表按照表9.11翻译模式把翻译模式把布尔表达式布尔表达式af翻译成三地址码

778、:翻译成三地址码:文法规则语义规则EE1orE2E.place:=newtemp;emit(E.place,:=,E1.place,or,E2.place)EE1andE2E.place:=newtemp;emit(E.place,:=,E1.place,nand,E2.place)EnotE1E.place:=newtemp;emit(E.place,:=,not,E1.place)E(E1)E.place:=E1.place;Eid1relopid2E.place:=newtemp;emit(ifid1.place,relop.op,id2.place,goto,nextstat+3);e

779、mit(E.place,:=,0);emit(goto,nextstat+2);emit(E.place,:=,1);Eidemit(E.place,:=,id.place)Etrueemit(E.place,:=,1)Efalseemit(E.place,:=,0)编译原理与技术编译原理与技术665100:ifafgoto111109:t3:=0110:goto112111:t3:=1112:t4:=t2andt3113:t5:=t1ort49.4.1布尔表达式的翻译布尔表达式的翻译u作为条件控制的布尔表达式翻译作为条件控制的布尔表达式翻译出现在条件语句出现在条件语句ifEthenS1els

780、eS2中布尔表达式中布尔表达式E的作用仅仅在于如何控制选择语句的作用仅仅在于如何控制选择语句S1和和S2。只要能完成这个任务,。只要能完成这个任务,E的值就无需保留在任的值就无需保留在任何一个临时单元内。何一个临时单元内。因此,可以为转移条件的布尔表达式因此,可以为转移条件的布尔表达式E设置两个标号设置两个标号属性属性reue和和false,分别表示条件为真时的控制流转移,分别表示条件为真时的控制流转移到的标号,以及条件为假时控制流转向的标号。到的标号,以及条件为假时控制流转向的标号。编译原理与技术编译原理与技术6669.4.1布尔表达式的翻译布尔表达式的翻译设计这个翻译的基本思想如下:设计这

781、个翻译的基本思想如下:(1)若)若E的形式为关系表达式的形式为关系表达式ab,则将生成如下形,则将生成如下形式的代码:式的代码:ifabgotoE.truegotoE.false(2)假如)假如E是是E1orE2形式的逻辑表达式,那么,形式的逻辑表达式,那么,E1为真时就无需再计算为真时就无需再计算E2以及以及E1orE2的值,因为无的值,因为无论论E2得出什么值,得出什么值,E1orE2都是为真;否则,如果都是为真;否则,如果E1为假,就必须计算为假,就必须计算E2的值,它就代表了的值,它就代表了E1orE2的值。的值。同样可以考虑同样可以考虑E是形如是形如E1andE2的翻译。对于形式的翻

782、译。对于形式为为notE1的布尔表达式就无需代码,只要交换的布尔表达式就无需代码,只要交换E的的true和和false就得到就得到E1的的true和和false。编译原理与技术编译原理与技术6679.4.1布尔表达式的翻译布尔表达式的翻译u把布尔表达式翻译把布尔表达式翻译成三地址代码的成三地址代码的语义规则。语义规则。u其中函数其中函数newlabel类似临时变量的类似临时变量的产生函数产生函数newtemp,每次调用的时,每次调用的时候就返回一个没候就返回一个没有用过的标号有用过的标号Li。文法规则语义规则EE1orE2E1.true:=E.true;E1.false:=newlabel;E

783、2.true:=E.true;E2.false:=E.false;E.code:=E1.code|gencode(E1.false,:)|E2.codeEE1andE2E1.true:=newlabel;E1.false:=E.false;E2.true:=E.true;E2.false:=E.false;E.code:=E1.code|gencode(E1.true,:,)|E2.codeEnotE1E1.true:=E.false;E1.false:=E.trueE.code:=E1.codeE(E1)E1.true:=E.true;E1.false:=E.falseE.code:=E1.

784、codeEid1relopid2E.code:=gencode(if,id1.place,relop.op,id2.place,goto,E.true)|gencode(goto,E.false)EidE.code:=id.placeEtrueE.code:=gencode(goto,E.true)EfalseE.code:=gencode(goto,E.false)编译原理与技术编译原理与技术6689.4.1布尔表达式的翻译布尔表达式的翻译例例9.10按照表9.11翻译模式把布尔表达式af翻译成三地址码,假设整个表达式的两个出口标号已经分别设置为Ltrue和Lfalse编译原理与技术编译原理

785、与技术669ifafgotoLtrueE2.true来自EE1andE2的规则,即整个表达式的值为真的出口gotoLfalseE2.false来自EE1andE2的规则,即整个表达式的值为假的出口9.4.2控制流语句的翻译控制流语句的翻译u这些控制结构的文法如下:这些控制结构的文法如下:其中的其中的E是布尔表达式。我们根据对布尔表达是布尔表达式。我们根据对布尔表达式的两种翻译方式,对控制结构也有两种方式的两种翻译方式,对控制结构也有两种方式。式。首先看需要多趟扫描的翻译方式首先看需要多趟扫描的翻译方式.编译原理与技术编译原理与技术670SifEthenS1|ifEthenS1elseS2|wh

786、ileEdoS|S1;S29.4.2控制流语句的翻译控制流语句的翻译编译原理与技术编译原理与技术671(a)if-thenS.code.E.trueS.nextE.codeE.trueS.next在(a)的if-then结构中,由于S1.code的代码究竟有多少条三地址语句,在翻译E的时候是不知道的,所以,不能象9.4.1.1节那样,用nextstat加一个适当的常数来确定S1.code的第一个代码的标号。这里,我们用E.true来记录这个新的标号。在(b)的if-then-else结构中,还有当执行完E为真时的S1语句后,需要转移到紧随if-then-else语句之后的下一条语句,我们用继承

787、属性S.next表示这个标号。由于允许语句的互相嵌套,S.next未必是S2.code之后的语句标号,例如:ifE1thenifE2thenS1elseS2elseS3就说明了这种情况。.E.nextE.nextE.codeS1.codegotoS.nextS2.codeE.trueE.falseE.falseE.true(b)if-then-else9.4.2控制流语句的翻译控制流语句的翻译编译原理与技术编译原理与技术672(c)while-do在(c)的while-do结构中,由于要反复执行条件E,就需要为整个语句设置一个开始标号S.begin,它表示E的代码中第一条指令的标号。在E的代码

788、中有两条转移指令:当E为真时转移到E.true开始执行循环体内的语句,当E为假时转移到整个语句之后的语句S.next,它等于E.false。类似地,S1.next就是S.begin,表明控制不能从循环体内跳转出来,而只能出现在条件表达式那里。顺序语句的情形比较简单,S1.next就是S2.begin,需要这个标号以便S1跳转。(d)S1;S2.E.codeS1.codegotoS.beginE.trueS.beginE.trueS.nextS.nextS1.codeS2.code.S.next9.4.2控制流语句的翻译控制流语句的翻译文法规则语义规则SifEthenS1E.true:=newl

789、abel;E.false:=S.next;S1.next:=S.nextS.code:=E.code|gencode(E.true,:)|S.codeSifEthenS1elseS2E.true:=newlabel;E.false:=newlabel;S1.next:=S.next;S2.next:=S.nextS.code:=E.code|gencode(E.true,:)|S1.code|gencode(goto,S.next)|gencode(E.false,:)|S2.codeSwhileEdoSS.begin:=newlabel;E.true:=newlabel;E.false:=n

790、ewlabel;S1.next:=S.begin;S.code:=gencode(S.begin,:)|E.code|gencode(E.true,:)|S1.code|gencode(goto,S.begin)SS1;S2S.code:=S1.code|gencode(S1.next,:)|S2.code编译原理与技术编译原理与技术673表表9.13控制流语句的翻译模式控制流语句的翻译模式9.4.2控制流语句的翻译控制流语句的翻译L1:L2:L3:L4:ifimgotoL3gotoL4t1:=5timebonus:=t2t2:=sum+bonussum:=t2gotoL1t3:=sumbon

791、ussum:=t3gotoL1编译原理与技术编译原理与技术674例例9.11根据上面的语义规则把下列Java语句翻译成三地址代码:while(im)bonus:=5*time;sum:=sum+bonus;elsesum=sum-bonus设开始整个语句的出口标号是Wnext,下面就是结合表9.11得到的三地址代码。所有标号的具体值都无法在产生代码的同时一次就确定,需要再次扫描一边源程序的分析树或者语法树,才能计算出每个标号的具体值。例如,在产生标号L4的时候,还不知道then部分的语句有多少,所以,无法确定L4的具体值。9.4.3回填技术基础回填技术基础我们已经说明过,语法制导生成的中间代码

792、可以直接我们已经说明过,语法制导生成的中间代码可以直接写在文件上,也可以存储在文法符号的属性中。前者写在文件上,也可以存储在文法符号的属性中。前者适于单趟扫描,后者适于可以多趟扫描。适于单趟扫描,后者适于可以多趟扫描。表表9.11的语义动作把产生的三地址代码写在符号的属的语义动作把产生的三地址代码写在符号的属性中,而不能直接写在输出文件中,这种翻译方式不性中,而不能直接写在输出文件中,这种翻译方式不能在一趟扫描的过程中实现。这是由于为语句向前产能在一趟扫描的过程中实现。这是由于为语句向前产生的标号在产生的时候还不能具体确定是多少。例如,生的标号在产生的时候还不能具体确定是多少。例如,例子例子9

793、.12的代码中,在的代码中,在L2语句还不能确定标号语句还不能确定标号L4的值的值是什么,只能在构造出后序的语句之后,才知道它的是什么,只能在构造出后序的语句之后,才知道它的具体值。具体值。为了实现单趟扫描的翻译,可以在生成分支的跳转指为了实现单趟扫描的翻译,可以在生成分支的跳转指令时暂时不确定跳转目标,而是建立一个链表,把转令时暂时不确定跳转目标,而是建立一个链表,把转向这个目标的跳转指令的标号存入这个链表。一旦目向这个目标的跳转指令的标号存入这个链表。一旦目标确定之后再把它填入有关的跳转指令中。这就是所标确定之后再把它填入有关的跳转指令中。这就是所谓的谓的回填技术回填技术。编译原理与技术编

794、译原理与技术6759.4.3回填技术基础回填技术基础u思路思路表表9.11的语义动作把产生的三地址代码写在符号的属的语义动作把产生的三地址代码写在符号的属性中,而不能直接写在输出文件中,这种翻译方式不性中,而不能直接写在输出文件中,这种翻译方式不能在一趟扫描的过程中实现。这是由于为语句向前产能在一趟扫描的过程中实现。这是由于为语句向前产生的标号在产生的时候还不能具体确定是多少。生的标号在产生的时候还不能具体确定是多少。为了实现单趟扫描的翻译,可以在生成分支的跳转指为了实现单趟扫描的翻译,可以在生成分支的跳转指令时暂时不确定跳转目标,而是建立一个链表,把转令时暂时不确定跳转目标,而是建立一个链表

795、,把转向这个目标的跳转指令的标号存入这个链表。一旦目向这个目标的跳转指令的标号存入这个链表。一旦目标确定之后再把它填入有关的跳转指令中。这就是所标确定之后再把它填入有关的跳转指令中。这就是所谓的回填技术。谓的回填技术。为非终结符为非终结符E建立两个综合属性建立两个综合属性E.trulist和和E.falselist,分别记录布尔表达式,分别记录布尔表达式E对应的三地址语句中需要回对应的三地址语句中需要回填填“真真”、“假假”出口的三地址语句的标号所构成的出口的三地址语句的标号所构成的链表。链表。编译原理与技术编译原理与技术6769.4.3回填技术基础回填技术基础采用三地址代码的四元式形式来说明

796、具体的实现。把采用三地址代码的四元式形式来说明具体的实现。把四元式存入一个数组,数组的下标就代表四元式的标四元式存入一个数组,数组的下标就代表四元式的标号,用四元式的第四个域来构造这种回填链表。号,用四元式的第四个域来构造这种回填链表。例如,假定例如,假定E的四元式中需要回填的四元式中需要回填“假假”值出口的由值出口的由标号时标号时i、j和和k这三个四元式,它们可以连接成一条链,这三个四元式,它们可以连接成一条链,链首(链首(k)存入)存入E.falselist,如下图所示:,如下图所示:编译原理与技术编译原理与技术677(i)(x,x,x,0).(j)(x,x,x,i).(k)(x,x,x,

797、j)0表示链尾地址k是falselist的链首9.4.3回填技术基础回填技术基础为了构造单趟扫描的中间代码翻译模式,我为了构造单趟扫描的中间代码翻译模式,我们需要介绍下列几个变量和函数:们需要介绍下列几个变量和函数:l变量变量nextquad指向下一个将要产生、但尚未形成指向下一个将要产生、但尚未形成的四元式的标号;每执行一次输出代码的函数的四元式的标号;每执行一次输出代码的函数emit的时候,的时候,nextquad就加就加1;l函数函数makelist(i)创建一个仅含地址为创建一个仅含地址为i的四元式的新的四元式的新链表,并返回指向这个链表的指针;链表,并返回指向这个链表的指针;l函数函

798、数merge(p1,p2)把链首为把链首为p1和和p2的两条链合并,的两条链合并,返回返回p1作为合并后的链首;作为合并后的链首;l过程过程backpatch(p,t)把链首为把链首为p的链表中的所有四的链表中的所有四元式的第元式的第4域都填写上标号域都填写上标号t。编译原理与技术编译原理与技术6789.4.3回填技术基础回填技术基础u表9.14布尔表达式短路方法的单趟扫描翻译模式u首先考虑产生式EE1orME2:E1若为真,则E也为真;若E1为假,则E的值就和E2的值一致。因此,E.falselist就等于E2.falselist,E.falselist就是E1.truelist和E2.tr

799、uelist的合并。M.quad记录了E2的第一条语句的标号,也就是E1.falselist的地址。u关系表达式产生两条转移语句:一个是根据条件运算结果的条件转移,另一条是无条件转移。它们转移目标的地址都不知道,分别存放在E.ftruelist和E.falselist,等待回填。编译原理与技术编译原理与技术679EE1orME2backpatch(E1.falselist,M.quad);E.truelist:=merge(E1.truelist,E2.truelist);E.falselist:=E2.falselistEE1andME2backpatch(E1.truelist,M.qua

800、d);E.truelist:=E2.truelistE.falselist:=merge(E1.falselist,E2.falselist);EnotE1E1.truelist:=E.falselist;E1.falselist:=E.truelistE(E1)E1.truelist:=E.truelist;E1.falselist:=E.falselistEid1relopid2E.truelist:=makelist(nextquad);E.falselist:=makelist(nextquad+1);emit(j,relop.op,id1.place,id2.place,0);emi

801、t(jump,0);EidE.truelist:=makelist(nextquad);E.falselist:=makelist(nextquad+1);emit(jnz,id.place,0);emit(jump,0);EtrueE.truelist:=makelist(nextquad);emit(jump,0);EfalseE.falselist:=makelist(nextquad);emit(jump,0);9.4.3回填技术基础回填技术基础编译原理与技术编译原理与技术680例例9.12重新考虑布尔表达式af,一边进行语法分析一边用表9.12规则执行代码翻译。假定语句开始标号是10

802、0。当把ab归约成E的时候,产生如下的四元式:100:(j,a,b,0)101:(jump,0)为标号100构造第1个链表,链首存在E.truelist;标号101构造第2个链表,链首存在E.falselist(此时的E实际上是E1orME2中的E1)。我们用标号表示四元式,上面的链表分别是和。扫描到or时,在M.quad中记录下一条四元式的地址nextquad,此时是102。表达式cd的代码是:102:(j,c,d,0)103:(jump,0)为标号102构造第3个链表,链首存在E.truelist;为标号103构造第4个链表,链首存在E.falselist(此时的E既是E1orME2中E2

803、的又是E1andME2中的E1)。9.4.3回填技术基础回填技术基础编译原理与技术编译原理与技术681扫描到and时,在M.quad中记录下一条四元式的地址nextyquad,此时是104。表达式ef的代码是:104:(j,e,f,0)105:(jump,0)为标号104构造第4个链表,链首存在E.truelist;为标号105构造第5个链表,链首存在E.falselist(此时的E实际上是E1andME2中的E2)。这时,开始执行E1andME2后面的语义动作。首先是回填backpatch(,104),链表是E1的真值链表,也就是E1orME2中E2的真值链表。回填后得到102:(j,c,d

804、,104)然后再执行链表合并merge(,)=,即为E1orME2中E2的falselist,同时得E1orME2中E2的truelist是。最后,执行E1orME2后面的语义动作。首先是回填backpatch(,102),链表是E1的假值链表。回填后得到101:(jump,102)9.4.3回填技术基础回填技术基础编译原理与技术编译原理与技术682然后再执行链表合并merge(,)=,即为整个布尔表达式的真值出口链,同时得到整个布尔表达式的假值出口链,即E1andME2的falselist。这样,得到下列三地址代码:100:(j,e,f,0)105:(jump,0)整个表达式的真值出口有两条

805、语句,假值出口也有两条语句。这四条语句的转移目标需要等到编译程序翻译控制语句时知道条件为真时做什么、条件为假时做什么,才能填入。假如真值出口的标号是200,假值出口的标号是400,那么回填之后的代码就是:100:(j,e,f,200)105:(jump,400)9.4.3回填技术基础回填技术基础编译原理与技术编译原理与技术683图9.8显示了带属性注释的分析树。E.truelist:=E.falselist:=M.quad:=102E.truelist:=E.falselist:=aborE.truelist:=E.falselist:=M.quad:=104andE.truelist:=E.

806、falselist:=cdE.truelist:=E.falselist:=df9.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式u考虑的文法如下:考虑的文法如下:S ifEthenS|ifEthenSelseS|whileEdoS|beginLend|ALL;S|S编译原理与技术编译原理与技术6849.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式文法规则语义规则SifEthenMS1backpatch(E.truelist,M.quad);S.nextlist:=merge(E.falselist,S1.nextlist);MM.quad:=nextquad;SifEthe

807、nM1S1NelseM2S2backpatch(E.truelist,M1.quad);backpatch(E.falselist,M2.quad);S.nextlist:=merge(S1.nextlist,N.nextlist,S2.nextlist);NN.nextlist:=makelist(nextquad);emit(jump,0);SwhileM1EdoM2S1backpatch(S1.nextlist,M1.quad);backpatch(E.truelist,M2.quad);S.nextlist:=E.falselist;emit(jump,M1.quad);SbeginL

808、endS.nextlist:=L.nextlistSAS.nextlist:=makelist();LL1;MSbackpatch(L1.nextlist,M.quad);L.nextlist:=S.nextlistLSL.nextlist:=S.nextlist编译原理与技术编译原理与技术6859.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式为了能够在单趟扫描进行翻译,需要把文法作些变换,以便消除嵌入在文法中的为了能够在单趟扫描进行翻译,需要把文法作些变换,以便消除嵌入在文法中的语义动作。语义动作。在在9.7(a)中中if-then的代码结构中,执行完布尔表达式的代码结构中,执行完

809、布尔表达式E的计算之后要跳到的计算之后要跳到S的起始地的起始地址,但是,在生成址,但是,在生成E的代码的时候还不知道确切的转移位置,我们加一个符号的代码的时候还不知道确切的转移位置,我们加一个符号M和和 产生式,便可以在翻译产生式,便可以在翻译S的代码时给出这个转移地址,进行回填。的代码时给出这个转移地址,进行回填。在在9.7(b)中中if-then-else的代码结构中,当执行完的代码结构中,当执行完S1的代码之后要跳过的代码之后要跳过S2的代码。因的代码。因此,在此,在S1的代码之后有一个无条件转移指令,我们我们加一个符号的代码之后有一个无条件转移指令,我们我们加一个符号N和和 产生式,产

810、生式,属性属性N.nextlist记录了转移的回填地址。当然,文法也要象记录了转移的回填地址。当然,文法也要象if-then结构一样,在语结构一样,在语句句S1和和S2之前分别增加一个符号和之前分别增加一个符号和 产生式。产生式。在在9.7(c)中中while-do的代码结构中,标号的代码结构中,标号S.begin和和E.true分别指向了整个语句的第分别指向了整个语句的第一条地址和循环体一条地址和循环体S1的起始地址。因此,我们分别在的起始地址。因此,我们分别在E和和S1之前增加符号,以便回之前增加符号,以便回填。填。值得注意的是,在上面的翻译模式中,只有值得注意的是,在上面的翻译模式中,只

811、有while语句和符号语句和符号N产生式的语义动作产生式的语义动作包含了产生新代码,其它的产生式均未生成三地址代码。所有其它的代码均由赋包含了产生新代码,其它的产生式均未生成三地址代码。所有其它的代码均由赋值语句和表达式相连的语义动作产生。所谓控制流程,就是在适当的时候进行回值语句和表达式相连的语义动作产生。所谓控制流程,就是在适当的时候进行回填,以使赋值语句和布尔表达式的求值得到适当的连接。填,以使赋值语句和布尔表达式的求值得到适当的连接。编译原理与技术编译原理与技术6869.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式编译原理与技术编译原理与技术687例例9.13根据上面的语义规

812、则把下列Java语句翻译成四元式:while(im)bonus:=5*time;sum:=bonus+sum;elsesum=sum-bonus设开始整个while语句的开始标号是100,随后的语句标号目前未知,在将来某个时候回填。下面,我们详细地说明代码产生的过程。当分析到i=n的时候,产生了两条代码:100:(jm产生了两条语句:102: (j,j,m,0)103:(jump,0)此时的代码标号nextquad为104,即为SifEthenM1S1NelseM2S2中M1.quad的值。9.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式编译原理与技术编译原理与技术688然后开始分析

813、then部分的语句序列。对于其中的第一个赋值语句bonus:=5*time产生了两条代码:104:(*,5,time,t1)105: (:=,t1,bonus)此时的代码标号nextquad为106,即为LL1;MS中M.quad的值;接着为第二条赋值语句产生代码:106:(+,sum,bonus,t2)107: (:=,t2,sum)此时的代码标号nextquad为108。注意,由于赋值语句归约为S的时候,生成一个空的回填链,所以语句序列SbeginLend及其子树的回填链表都是空,即没有回填的代码。下面开始分析else部分,之前构造一个包含当前标号nextquad(为108)的链表,表头存

814、入为N.nextlist,并且产生一条无条件转移代码:108: (jump,0)并将nextquad加1,就是SifEthenM1S1NelseM2S2中M2.quad的值。在分析else部分的时候产生代码:109: (,sum,bonus,t3)110: (:=,t3,sum)9.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式100:101:102:103:104:105:(j,j,m,104)(jump, , ,109)(*,5,time,t1)(:=,t1, ,bonus)106:107:108:109:110:111:(+,sum,bonus,t2)(:=,t2, ,sum)(

815、jump, , ,100)( ,sum,bonus,t3)(:=,t3, ,sum)(jump, , ,100)编译原理与技术编译原理与技术689然后开始回填SifEthenM1S1NelseM2S2中E的真值和假值链表得到:102: (j,j,m,104)103: (jump,109)接着合并S1、N以及S2链表,得到,并存入S.nextlist中。最后,执行while语句对应的动作:回填backpatch(S1.nextlist,M1.quad),把标号100填入标号为108的四元式,得到108:(jump,100);另外的回填结果是100:(j=,i,n,102);最后再产生一个转移指令

816、111:(jump,100)。在将来某个时候需要回填的四元式的链表的头存在S.nextlist中,只有一个四元式。9.4.4控制流语句的单趟翻译模式控制流语句的单趟翻译模式u图图9.9是这个语句的注释分析树,深度优先地遍历分析树就可以得到是这个语句的注释分析树,深度优先地遍历分析树就可以得到产生的四元式序列。产生的四元式序列。编译原理与技术编译原理与技术690S.nextlist:=M2.quad:=102E.truelist:=E.falselist:=inM1.quad:=100S.nextlist:=M1.quad:=104jE.truelist:=E.falselist:=mS2.ne

817、xtlist:=sumsumbonusN.nextlist:=M2.quad:=109L.nextlist:=S.nextlist:=5bonustime*S.nextlist:=sumsumbonus+;M.quad:=106S1.nextlist:=beginendL.nextlist:=9.5转向语句的翻译转向语句的翻译u标号语句与标号语句与goto语句的翻译语句的翻译goto语句一般形式是语句一般形式是gotoL,它通常和标号语句共同,它通常和标号语句共同使用的,标号语句的形式是使用的,标号语句的形式是L:S,产生式语法如下:,产生式语法如下:SL:SLi在处理标号语句在处理标号语句L

818、:S时,如果标号时,如果标号L是是“已定义已定义”的,的,即在符号表中,则把登记即在符号表中,则把登记L的符号表项中的地址域加的符号表项中的地址域加上代码语句上代码语句S的地址。的地址。如果源程序中如果源程序中gotoL是一个向后转移的语句,那么,是一个向后转移的语句,那么,编译可以在符号表中查找编译可以在符号表中查找L的定义地址的定义地址p,产生出相,产生出相应的三地址代码:应的三地址代码:(jump, , ,p)(我们用四元式说明)(我们用四元式说明)。编译原理与技术编译原理与技术6919.5转向语句的翻译转向语句的翻译u标号语句与标号语句与goto语句的翻译语句的翻译如果源程序中如果源程

819、序中gotoL是一个向前转移的语句,那么,是一个向前转移的语句,那么,L尚未定义尚未定义好,编译需要首先把好,编译需要首先把L填入符号表,标记填入符号表,标记“未定义未定义”,产生一个,产生一个没有目标地址的转移语句没有目标地址的转移语句(jump, , ,0),等到目标地址清楚的,等到目标地址清楚的时候再回填。这样,就必须类似控制流布尔表达式的翻译,把时候再回填。这样,就必须类似控制流布尔表达式的翻译,把所有以所有以L为转移目标的三地址码的地址都链接起来,等待回填。为转移目标的三地址码的地址都链接起来,等待回填。一种实现方式是利用符号表把有关的信息连接起来,如图一种实现方式是利用符号表把有关

820、的信息连接起来,如图9.10所所示。示。编译原理与技术编译原理与技术692名字类型定义标志.地址.LLabelnotr四元式(p)(q).(r).9.5转向语句的翻译转向语句的翻译u出口语句的翻译出口语句的翻译出口语句是一种结构化的跳转语句,它的作用是引起出口语句是一种结构化的跳转语句,它的作用是引起循环或程序块的终止。各种语言的语句名称以及实现循环或程序块的终止。各种语言的语句名称以及实现方式有所差别。在方式有所差别。在C、C+和和C#语言中,出口语句语言中,出口语句break跳出本层的循环语句,还可以终止开关语句;跳出本层的循环语句,还可以终止开关语句;Ada和和Java语言中的语言中的e

821、xit或或break没有标号是退出当前没有标号是退出当前循环体,如果形式是循环体,如果形式是breakL,则控制就跳转到标号,则控制就跳转到标号L定义的循环语句的开始地址。定义的循环语句的开始地址。程序语言中类似的语句还有继续语句程序语言中类似的语句还有继续语句continue,它是,它是终止该语句之后的所有语句,回到当前的循环语句开终止该语句之后的所有语句,回到当前的循环语句开始位置。始位置。编译原理与技术编译原理与技术6939.5转向语句的翻译转向语句的翻译u出口语句的翻译出口语句的翻译不论哪种出口语句,都要翻译成一个转移指令。根据不论哪种出口语句,都要翻译成一个转移指令。根据语义的不同,

822、转移目标的处理需要使用不同的技术,语义的不同,转移目标的处理需要使用不同的技术,我们以我们以Java的的break语句为例,说明转向语句的翻译语句为例,说明转向语句的翻译思想。我们用下列文法定义跳转语句:思想。我们用下列文法定义跳转语句:Sbreak|brealLLi不带标号的不带标号的beak和和continue的语义相同。从形式上看的语义相同。从形式上看带标号的带标号的break语句和语句和gotoL一样。但是,我们规定一样。但是,我们规定L只能是循环语句头的标号,转移目标必须已经定义。只能是循环语句头的标号,转移目标必须已经定义。即,即,breakL只能是向后转移,而且只能用在循环语只能

823、是向后转移,而且只能用在循环语句内。句内。编译原理与技术编译原理与技术6949.5转向语句的翻译转向语句的翻译例如,在下面的例如,在下面的Java程序段中程序段中test:for(inti=index;i+max1=max2;i+)ifcharAt(i)=cfor(intk;kX如CT=2转到X单元CMPM,N比较内存单元M和N的值,根据结果在机器内部特征寄存器CT中设置相应的状态值:MN时CT2CJX如CT=2或CT=1转到X单元CJ=X如CT=1转到X单元CJX如CT1转到X单元CJX如CT=0转到X单元编译原理与技术编译原理与技术71310.3语法制导的目标代码生成语法制导的目标代码生成

824、u利用属性文法和语法制导技术,直接产生目标代码。基本原理和技术利用属性文法和语法制导技术,直接产生目标代码。基本原理和技术同第同第9章介绍的语法制导的中间代码翻译类似,只是产生的目标语言章介绍的语法制导的中间代码翻译类似,只是产生的目标语言是机器指令。是机器指令。u本节只讨论如何用翻译模式把源程序语言的简单赋值语句和表达式翻本节只讨论如何用翻译模式把源程序语言的简单赋值语句和表达式翻译成目标代码,文法如下:译成目标代码,文法如下:Sid:=EEE1+E2|E1 E2| E1|( E1)|idBB1andB2|B1orB2|notB1|( B1)|id1relopid2|true|falseu为

825、了简单起见,这个算术表达式为了简单起见,这个算术表达式E只有加法、乘法与取负运算,不包只有加法、乘法与取负运算,不包含数组、记录等复杂的结构的访问,布尔表达式含数组、记录等复杂的结构的访问,布尔表达式B只包括了三个逻辑只包括了三个逻辑运算符和关系运算符。运算符和关系运算符。u文法表达式文法是二义性的,解决文法二义性的原则采用通常意义的文法表达式文法是二义性的,解决文法二义性的原则采用通常意义的优先级和结合性。下面的翻译模式把目标代码写在了文法的属性优先级和结合性。下面的翻译模式把目标代码写在了文法的属性code中,所使用的函数、变量和属性等与第中,所使用的函数、变量和属性等与第9章的相同。章的

826、相同。编译原理与技术编译原理与技术71410.3语法制导的目标代码生成语法制导的目标代码生成编译原理与技术编译原理与技术715(1)Sid:=Ep:=lookup(id.name);ifp=nilthenerrorelseS.code:=E.code|gencode(MOV,E.place,p)(2)EE1+E2E.place:=newtemp;E.code:=E1.code|E2.code|gencode(MOV,E1.place,E.place)|gencode(ADD,E2.place,E.place);(3)EE1*E2E.place:=newtemp;E.code:=E1.code|

827、E2.code|gencode(MOV,E1.place,E.place)|gencode(MUL,E2.place,E.place);10.3语法制导的目标代码生成语法制导的目标代码生成编译原理与技术编译原理与技术716(4)EE1E.place:=newtemp;E.code:=E1.code|gencode(MOV,E1.place,E.place);gencode(NEG,E.place);(5)E(E1)E.place:=E1.place;E.code:=E1.code;(6)Eidp:=lookup(id.name);ifp=nilthenerrorelseE.place:=p;E

828、.code:=;10.3语法制导的目标代码生成语法制导的目标代码生成编译原理与技术编译原理与技术717在下面布尔表达式的翻译中,我们对布尔表达式的求值翻译采用了短路法。其中J|relop.op表示各种条件的转移指令(表10.2)。(7)BB1andB2B1.true:=newlabel;B1.false:=B.false;B2.true:=B.true;B2.false:=B.false;B.code:=B1.code|gencode(B1.true,:)|B2.code(8)BB1orB2B1.true:=B.true;B1.false:=newlabel;B2.true:=B.true;B

829、2.false:=B.false;B.code:=B1.code|gencode(B1.false,:)|B2.code(9)BB1B1.true:=B.false;B2.false:=B.true;B.code:=B1.code10.3语法制导的目标代码生成语法制导的目标代码生成编译原理与技术编译原理与技术718(10)B(B1)B1.true:=B.true;B2.false:=B.false;B.code:=B1.code;(11)Bid1relopid2t:=newtemp;B.code:=gencode(MOV,id1.place,t)|gencode(CMP,t,id2.place

830、)|gencode(CJ|relop.op,B.true)|gencode(J,B.false)(12)Btruegencode(J,B.true)(13)Bfalsegencode(J,B.false)10.3语法制导的目标代码生成语法制导的目标代码生成u例例10.1把布尔表达式把布尔表达式ad翻译成目标代码。翻译成目标代码。按照上述翻译模式得到的机器指令如下:按照上述翻译模式得到的机器指令如下:MOVa,t1CMPt2,bCJB.trueJB.falseu其中其中B.true和和B.false需要应用这个布尔条件的语句确需要应用这个布尔条件的语句确定。定。编译原理与技术编译原理与技术719

831、10.3语法制导的目标代码生成语法制导的目标代码生成本本节节介介绍绍的的翻翻译译技技术术可可以以应应用用在在简简单单语语言言的的编编译译器器中中,不不适适合合许许多多大大型型实实际际的的程程序序设设计计语语言言。主主要要原原因包括:因包括:(1)从从语语义义分分析析直直接接生生成成目目标标代代码码有有许许多多局局限限性性,例例如如,由由于于目目标标代代码码于于机机器器特特性性紧紧密密相相关关,不不利利于于代代码码的的移移植植和和优优化化,更更好好的的策策略略是是先先产产生生某某种种直直接接代码,然后再翻译成目标指令序列;代码,然后再翻译成目标指令序列;(2)在在上上面面的的翻翻译译模模式式中中

832、,多多处处用用到到了了产产生生临临时时变变量量的的函函数数newtemp,没没有有充充分分考考虑虑目目标标机机器器体体系系结结构构中中的的寄寄存存器器以以及及变变量量值值的的使使用用关关系系,而而且且过过多多的的临临时时变变量量名名还还会会造造成成存存储储分分配配与与寄寄存存器器分分配配的的问问题。题。编译原理与技术编译原理与技术72010.4基本块和待用信息基本块和待用信息u基本块及其构造基本块及其构造对于给定的程序,我们通常把它划分为一系列的基本对于给定的程序,我们通常把它划分为一系列的基本块,根据程序的控制流把这些基本块连接起来,形成块,根据程序的控制流把这些基本块连接起来,形成程序流图

833、。在逐步完成各个基本块的代码生成之后,程序流图。在逐步完成各个基本块的代码生成之后,就生成了整个程序的目标代码。就生成了整个程序的目标代码。基本块是指程序中一顺序执行的语句序列,其中只有基本块是指程序中一顺序执行的语句序列,其中只有一个入口语句和一个出口语句。基本块运行时只能从一个入口语句和一个出口语句。基本块运行时只能从其入口语句进入,从出口语句退出。其入口语句进入,从出口语句退出。例如,下面的三地址代码组成了一个基本块:例如,下面的三地址代码组成了一个基本块:t1:=a*at2:=a*bt3:=a*t2t4:=t1*t3t5:=t4+t2编译原理与技术编译原理与技术72110.4基本块和待

834、用信息基本块和待用信息编译原理与技术编译原理与技术722算法算法10.1划分基本块输入:输入:三地址语句序列输出:输出:基本块列表,每个三地址语句仅在基本块中。(1)找出三地址代码中各个基本块的入口语句,它们是:程序的第一个语句,或者条件语句活无条件语句的转移目标语句,或者紧跟在条件语句之后的语句。(2)对每一个入口语句,它所在的基本块就是由它开始到下一个入口语句之前、或者到一转移语句之前、或到程序结束的所有语句。凡是未被纳入某一基本块的语句,都是程序控制流无法到达的语句,因而也是不会被执行的语句,可以把它们删除。10.4基本块和待用信息基本块和待用信息编译原理与技术编译原理与技术723例例1

835、0.2:考虑下面计算长度为20的两个向量的点积的程序段,如图10.2。beginprod:=0;index:=;dobeginprod:=prod+aindex*bindex;index:=index+1;endwhileindex=10;end图10.2计算点积的程序在虚拟机器上执行这个计算的三地址代码序列如图10.3。(1)prod:=0(2)index:=1(1)t1:=4*index/*字长是4个字节*/(4)t2:=at1/*计算aindex*/(5)t3:=4*index(6)t4:=bt1/*计算bindex*(7)t5:=t3*t4(8)t6:=prod+t5(9)prod:=

836、t6(10)t7:=index+1(11)index:=t7(12)ifindex=20goto(3)10.4基本块和待用信息基本块和待用信息我我们们运运用用算算法法10.1来来决决定定图图10.3的基本块。的基本块。按按照照算算法法的的规规则则语语句句(1)是是入入口口语语句句,按按照照规规则则语语句句(3)是是条条件件转转移移的的目目标标语语句句,也也是是入入口口语语句;句;按按 照照 规规 则则 跟跟 随随 语语 句句(12)的是入口语句。)的是入口语句。所所以以,语语句句(1)和和(2)组组成成了了一一个个基基本本块块,以以语语句句(3)开开始始的的程程序序的的其其它它语语句句组组成成

837、了第二个基本块。了第二个基本块。编译原理与技术编译原理与技术724在虚拟机器上执行这个计算的三地址代码序列如图10.3。(1)prod:=0(2)index:=1(1)t1:=4*index/*字长是4个字节*/(4)t2:=at1/*计算aindex*/(5)t3:=4*index(6)t4:=bt1/*计算bindex*(7)t5:=t3*t4(8)t6:=prod+t5(9)prod:=t6(10)t7:=index+1(11)index:=t7(12)ifindex=20goto(3)10.4基本块和待用信息基本块和待用信息基本块B1基本块B2基本块B3基本块B4(1)readX(2)

838、readY(3)R:=XmodY(4)ifR=0goto(8)(5)X:=Y(6)Y:=R(7)goto(3)(8)writeY编译原理与技术编译原理与技术725例例10.3考虑下面求最大公因子的三地址代码,求出所有基本块。(1)readX(2)readY(3)R:=XmodY(4)ifR=0goto(8)(5)X:=Y(6)Y:=R(7)goto(3)(8)writeY按照定义,可以找到四条入口语句(1)、(3)、(5)和(8)。然后,构造的四个基本块如下:10.4基本块和待用信息基本块和待用信息u流图流图把程序控制流的信息增加到基本块的集合,形成一个把程序控制流的信息增加到基本块的集合,形

839、成一个有向图来表示程序,这样的有向图叫做流图。每个流有向图来表示程序,这样的有向图叫做流图。每个流图以基本块为结点,其中包含了程序第一条语句的基图以基本块为结点,其中包含了程序第一条语句的基本块称为起始结点。如果在程序的某个执行序列中,本块称为起始结点。如果在程序的某个执行序列中,基本块基本块Bj紧跟在基本块紧跟在基本块Bi之后执行,则从之后执行,则从Bi到到Bj有一有一条有向边。也就是,若:条有向边。也就是,若:l有一个条件或无条件转移语句作为有一个条件或无条件转移语句作为Bi的最后一条语句转移到的最后一条语句转移到Bj的第一条语句,或者的第一条语句,或者l按照程序的正文序列,按照程序的正文

840、序列,Bj紧跟在紧跟在Bi之后,而且之后,而且Bi的最后一条的最后一条语句不是一个无条件转移语句。语句不是一个无条件转移语句。那么,块那么,块Bi到到Bj有一条有向边。我们称有一条有向边。我们称Bi是是Bj的前驱,的前驱,Bj是是Bi的后继。的后继。编译原理与技术编译原理与技术72610.4基本块和待用信息基本块和待用信息l例例10.4对例对例10.2中的程序构造的流图如图中的程序构造的流图如图10.4所示。所示。B1是初是初始结点,在始结点,在B2中的最后一个跳转语句改成了跳转到中的最后一个跳转语句改成了跳转到B1。编译原理与技术编译原理与技术727prod:=0index:=1B1B2t1

841、:=4*indext2:=at1t3:=4*indext4:=bt1t5:=t3*t4t6:=prod+t5prod:=t6t7:=index+1index:=t7ifindex=20gotoB110.4基本块和待用信息基本块和待用信息l例例10.5:对例:对例10.3中的程序构造的流图如图中的程序构造的流图如图10.5所示。所示。编译原理与技术编译原理与技术728(1)readX(2)readY(5)X:=Y(6)Y:=R(7)gogo(3)(3)R:=XmodY(4)ifR=0gogo(8)(8)writeXB1B2B3B410.4基本块和待用信息基本块和待用信息u待用信息待用信息如如果果

842、三三地地址址代代码码i对对变变量量A通通过过赋赋值值语语句句定定值值(即即存存在在一一个个赋赋值值语语句句(i)A:=E),中中间间代代码码j要要用用A作作为为运运算算对对象象(引引用用A的的值值),存存在在一一个个控控制制可可以以从从语语句句i到到j的的路路径径,并并且且这这条条路路径中没有对径中没有对A的其它赋值,的其它赋值,那那么么就就称称中中间间代代码码j引引用用了了A在在中中间间代代码码i的的定定值值,称称中中间间代代码码j是是中中间间代代码码i中中对对变变量量A的的待待用用信信息息或或下下次次引引用用信信息息,同同时时称称A在在语语句句i是是活跃变量。活跃变量。编译原理与技术编译原

843、理与技术72910.4基本块和待用信息基本块和待用信息我们为一个基本块内的每个三地址语句我们为一个基本块内的每个三地址语句x:=aopb中中的所有变量确定待用信息。的所有变量确定待用信息。为了得到一个基本块内每个变量的待用信息和活跃信为了得到一个基本块内每个变量的待用信息和活跃信息,可以从基本块的出口由后向前逐句扫描每条语句息,可以从基本块的出口由后向前逐句扫描每条语句对每个变量建立相应的待用信息链和活跃变量信息链。对每个变量建立相应的待用信息链和活跃变量信息链。为了简化处理,我们对于基本块内的变量分为下列两为了简化处理,我们对于基本块内的变量分为下列两中情况处理:中情况处理:l对没有经过数据

844、流分析(见对没有经过数据流分析(见11.5的介绍)且三地址代码生成的介绍)且三地址代码生成的临时变量不允许在基本块外使用,那么就认为这些临时变的临时变量不允许在基本块外使用,那么就认为这些临时变量在基本块出口处都是不活跃的;量在基本块出口处都是不活跃的;l如果某些临时变量可以在本基本块之外引用,则把它们看作如果某些临时变量可以在本基本块之外引用,则把它们看作基本块之后的活跃变量,同时也把基本块的非临时变量均看基本块之后的活跃变量,同时也把基本块的非临时变量均看作是基本块之后的活跃变量。作是基本块之后的活跃变量。编译原理与技术编译原理与技术73010.4基本块和待用信息基本块和待用信息编译原理与

845、技术编译原理与技术731算法算法10.2计算待用信息计算待用信息输入:输入:基本块的三地址语句序列基本块的三地址语句序列输出:输出:基本块中所有变量的待用信息和活跃信息基本块中所有变量的待用信息和活跃信息初始化:把基本块中每个变量在符号表登记项中的待用信息填为“非待用”;根据每个变量在出口之后是否活跃,在活跃信息栏填上“活跃”或“非活跃”。从基本块出口语句到入口语句由后向前依次处理每个中间代码。对每个形式为i:A:=BopC的代码,以此执行下列步骤:把符号表中变量A的待用信息和活跃信息附加到中间代码i上;把符号表中变量A的待用信息栏和活跃信息栏分别设置为“非待用”和“非活跃”;(因为在i中对A

846、的定值只能在i之后才能引用,对i之前的语句而言A既不是活跃的也不可待用)把符号表中变量B和C的待用信息和活跃信息附加到中间代码i上;把符号表中变量B和C的待用信息设置为i,活跃信息均设置为“活跃”。10.4基本块和待用信息基本块和待用信息编译原理与技术编译原理与技术732例例10.6对于下列基本块,假设变量D在基本块之后活跃,计算所有变量的待用信息。(1)T:=AB(2)U:=AC(3)V:=TU(4)D:=VU用F表示“非待用”和“非活跃”,用L表示“活跃”,用序号表示待用信息(即下一个引用点),用二元对表示变量的待用信息和活跃信息,其中X取指为F或L,表10.3显示了符号表中待用信息和活跃

847、信息,表10.4显示了中间代码上的待用信息和活跃信息。对表10.3中的每一个变量,把其二元对从左到右连接起来,就得到了变量的待用信息和活跃信息变化。10.4基本块和待用信息基本块和待用信息u表表10.3例例10.4的符号表中待用和活跃信息的符号表中待用和活跃信息变量名待用信息和活跃信息初值处理(4)处理(3)处理(2)处理(1)ABCDTUV编译原理与技术编译原理与技术73310.4基本块和待用信息基本块和待用信息序号中间代码左值左操作数右操作数(1)T:=AB(2)U:=AC(3)V:=TU(4)D:=VU编译原理与技术编译原理与技术734表10.4例10.4的中间代码的待用和活跃信息10.

848、5一个简单代码生成器一个简单代码生成器本节要介绍一个简单的代码生成器,它依次考虑每条语句以及本节要介绍一个简单的代码生成器,它依次考虑每条语句以及如何在一个基本块范围内充分利用寄存器的问题,生成目标代如何在一个基本块范围内充分利用寄存器的问题,生成目标代码,并根据产生的代码修改寄存器的使用情况。码,并根据产生的代码修改寄存器的使用情况。为了简单起见,假定计算结果尽量常时间地留在寄存器中,只为了简单起见,假定计算结果尽量常时间地留在寄存器中,只有在下面两种情况下才把它存入内存:有在下面两种情况下才把它存入内存:(1)如果需要此寄存器用于其它计算;)如果需要此寄存器用于其它计算;(2)正好在转移或

849、标号语句之前。)正好在转移或标号语句之前。条件(条件(2)暗示在基本块的结尾必须把所有的计算结果都保存起)暗示在基本块的结尾必须把所有的计算结果都保存起来。原因是,离开一个基本块后,可能进入几个不同基本块中来。原因是,离开一个基本块后,可能进入几个不同基本块中的一个,或者进入一个还可以从其它基本块进入的基本块。在的一个,或者进入一个还可以从其它基本块进入的基本块。在这两种情况下,就认为基本块引用的某个数据在入口点一定处这两种情况下,就认为基本块引用的某个数据在入口点一定处在某个寄存器中是不妥的。因此,为了不免可能出先的错误,在某个寄存器中是不妥的。因此,为了不免可能出先的错误,本节介绍的简单代

850、码生成算法在离开基本块时,存储所有的东本节介绍的简单代码生成算法在离开基本块时,存储所有的东西。西。编译原理与技术编译原理与技术73510.5一个简单代码生成器一个简单代码生成器编译原理与技术编译原理与技术736通过一个简单的例子来说明要介绍的简单代码生成算法的一些问题。对三地址语句a:=b+c,我们可以生成一条简单的指令ADD Rj,Ri(1)把结果留在Ri。但是,可以生成这条简单指令的前提是:Rj包含了c,Ri包含了b,而且它以后不再被引用了。如果Ri包含了b,而c在内存中(假设就用c表示内存单元),我们可以产生:ADDc,Ri(2)或者MOVc,Rj(3)ADDRj,Ri同样要求b不再被

851、引用了。从机器指令执行的时间上讲,使用寄存器比使用内存单元要快。但是,任何机器的寄存器数量优先,而且某些寄存器还有特殊通途,不能作为通用寄存器使用。而且,还要考虑到存入寄存器额名字的值今后是否还要引用。所以,判定翻译模板代码优劣的因素很多、很复杂。但从本例而言,代码(1)的执行速度最快,但是,要求的条件也最多。代码(2)的执行速度由于要访问内存(c的值),比代码(1)慢,但是比(3)要快,而且,代码(1)和(2)都是一条指令,占用较少的内存。然而,如果以后肯定要使用c的值,翻译(3)比(2)更有吸引力,因为c的值已经在一个寄存器Rj中。10.5一个简单代码生成器一个简单代码生成器u寄存器和地址

852、的描述寄存器和地址的描述为了在代码生成的过程中合理地分配寄存器,需要随时掌握每为了在代码生成的过程中合理地分配寄存器,需要随时掌握每个寄存器的使用情况,了解个寄存器的使用情况,了解它是否空闲,它是否空闲,还是已经分配给还是已经分配给某个或某几个变量。某个或某几个变量。l使用一个数组使用一个数组RVALUE来动态地记录寄存器的这些信息,这个数组来动态地记录寄存器的这些信息,这个数组称作寄存器描述数组。用寄存器称作寄存器描述数组。用寄存器Ri的编号值作为寄存器描述数组的的编号值作为寄存器描述数组的RVALUE下标,数组元素值是一个或多个变量名。下标,数组元素值是一个或多个变量名。另外,一个变量的值

853、可以存储在寄存器中,也可以存放在内存,另外,一个变量的值可以存储在寄存器中,也可以存放在内存,或者同时存放在寄存器和内存中。在代码生成过程中,每当生或者同时存放在寄存器和内存中。在代码生成过程中,每当生成的指令要涉及到引用某个变量的值时,若它已经在某个寄存成的指令要涉及到引用某个变量的值时,若它已经在某个寄存器,我们希望直接引用该变量在寄存器中的值,以便提高代代器,我们希望直接引用该变量在寄存器中的值,以便提高代代码的执行速度。码的执行速度。l使用一个称作变量地址描述数使用一个称作变量地址描述数AVALUE来动态地记录每个变量当前来动态地记录每个变量当前值的存放位置,这个数组的下标就用变量名。

854、值的存放位置,这个数组的下标就用变量名。编译原理与技术编译原理与技术73710.5一个简单代码生成器一个简单代码生成器u寄存器和地址的描述寄存器和地址的描述几个例子:几个例子:编译原理与技术编译原理与技术738RVALUER1=A,B表示R1存储的是变量A和B的值AVALUEA=A表示变量A的值只存放在内存中AVALUEA=R1,A 表示变量A的值同时存放在寄存器R1和内存中AVALUEB=R1表示变量B的值只存放在寄存器R1内10.5一个简单代码生成器一个简单代码生成器u寄存器的分配原则寄存器的分配原则1.当生成某变量的目标代码时,尽可能让变量的值或当生成某变量的目标代码时,尽可能让变量的值

855、或计算结果驻留在寄存器中,除非该寄存器必须用来计算结果驻留在寄存器中,除非该寄存器必须用来存放其它变量的值而不得不放弃其中的内容;存放其它变量的值而不得不放弃其中的内容;2.达到基本块出口时,将变量的值存放到内存中,以达到基本块出口时,将变量的值存放到内存中,以便后续基本块的代码可以继续引用其值;便后续基本块的代码可以继续引用其值;3.一个基本块后不再引用的变量所占用的寄存器应该一个基本块后不再引用的变量所占用的寄存器应该尽早释放出来,以提高寄存器的使用率。尽早释放出来,以提高寄存器的使用率。为了在一个基本块内的目标代码中让寄存器得到充为了在一个基本块内的目标代码中让寄存器得到充分利用,需要把

856、基本块内还要被引用的变量值尽可分利用,需要把基本块内还要被引用的变量值尽可能地保存在寄存器中,而把基本块内不再被引用的能地保存在寄存器中,而把基本块内不再被引用的变量所占的寄存器尽早地释放掉。在代码生成的过变量所占的寄存器尽早地释放掉。在代码生成的过程中,需要不断地为程序中的变量选择寄存器。程中,需要不断地为程序中的变量选择寄存器。编译原理与技术编译原理与技术73910.5一个简单代码生成器一个简单代码生成器编译原理与技术编译原理与技术740算法算法10.3寄存器选择函数GETREG输入:输入:中间代码i:A:=BopC输出:输出:一个用来存放变量A的值的寄存器Rfor每个AVALUEB中的R

857、idoif(RVALUERi=B)&(B=A|B在该语句之后不会在被引用)thenreturnRi;/即语句i的附加信息中,B的待用和活跃信息为“非待用”和“非活跃”if(存在Ri&RVALUERi=)thenreturnRi;按照下列原则,从已经分配的寄存器中选择一个寄存器Ri:占用寄存器Ri的变量,或者其值同时也存储在内存,或者它在基本块的最远处引用或不被引用。for每个RVALUEB中的Mdoif(M!=A|(M=A&M=C&(M!=B&B不属于RVALUERi)thenif(M不属于AVALUEM)then生成目标代码MOVRi,M;/把Ri的值存入内存MAVALUEM=AVALUEM

858、Riif(M!=B|(M=C&B属于RVALUERi)thenAVALUEM=M,BelseAVALUEM=MRVALUERi=RVALUERiMreturnRi10.5一个简单代码生成器一个简单代码生成器u代码生成算法代码生成算法本节介绍一个简单代码生成算法。本节介绍一个简单代码生成算法。不失一般性,假设中间代码的形式为不失一般性,假设中间代码的形式为A:=BopC,对于其它形式的中间代码,可以仿照,对于其它形式的中间代码,可以仿照算法算法10.4完成。完成。这个算法调用了算法这个算法调用了算法10.3和和10.2,需要待用信,需要待用信息的目的就是决定是否释放这写变量所占用息的目的就是决定

859、是否释放这写变量所占用的寄存器。的寄存器。编译原理与技术编译原理与技术74110.5一个简单代码生成器一个简单代码生成器编译原理与技术编译原理与技术742算法算法10.4简单代码生成算法输入:输入:基本块BBn,每条中间代码形式为i:A:=BopC输出:输出:目标代码for(j=1;jn;j+)/调用寄存器选择函数GETREG(i:A:=BopC)得到一个存放A值的寄存器R;R=getreg(BBj);B=AVALUEB;C=AVALUEC;/到B和C的存放位置B和Cif(B=R)生成目标代码opR,Celse生成目标代码MOVB,RopR,C/*修改寄存器描述数组和地址描述数组,释放B和C所

860、占用的寄存器,使A只在寄存器R且独占寄存器R*/if(B=R)AVALUEB=AVALUEBR;if(C=R)AVALUEC=AVALUECR;AVALUEA=R;RVALUER=A;/*若B或C不再被引用,就释放B或C占用的每一个寄存器*/If(B不再被引用)RVALUERi=RVALUERiB;AVALUEB=AVALUEBRiIf(C不再被引用)RVALUERi=RVALUERiC;AVALUEC=AVALUECRi10.5一个简单代码生成器一个简单代码生成器中间代码目标代码RVALUEAVALUET:=ABMOVA,R0SUBB,R0R0含TT在R0U:=ACMOVA,R1SUBC,R

861、1R0含TR1含UT在R0U在R1V:=TUADDR1,R0R0含VR1含UV在R0U在R1D:=VUADDR1,R0MOVR0,DR0含DD在R0D在内存编译原理与技术编译原理与技术743例例10.7对于例10.6的三地址代码:(1)T:=AB(2)U:=AC(3)V:=TU(4)D:=VU假设只有R0和R1两个可用的寄存器,用算法10.3和10.4生成的目标代码以及相应的寄存器描述和地址描述下表所示。函数getreg的第一次调用返回寄存器R0来存放计算结果T。因为A不在R0中,所以产生数据移动代码“MOVA,R0”和运算代码“SUBB,R0”,然后修改寄存器和内存地址描述以表示R0包含临时

862、变量T。代码生成以这种方式进行,直到最后一个语句处理完。这时,R1已被释放为空闲,活跃变量D的值通过指令“MOVR0,D”保留在内存当中。10.5一个简单代码生成器一个简单代码生成器中间代码目标代码备注A:=BopCMOVB,RiopC,Ri见10.5.3节的算法A:=op1CMOVC,Riop1Ri,Ri见10.5.3节的算法,其中op1是单目运算符A:=BMOVB,Ri见10.5.3节的算法,但是如果B的当前值已经在某个寄存器Ri,则不生成任何代码。A:=BiMOVi,RiMOVB(R),Rj(1)Ri是分配给A的寄存器(2)若i在某个寄存器中,则第一条代码可以省去Ai:=BMOVi,Ri

863、MOVB,RjMOVRj,A(Ri)(1)Ri是分配给A的寄存器(2)若i在某个寄存器中,则第一条代码可以省去(3)若B在某个寄存器中,则第二条代码可以省去gotoXJXX是标号为X的中间代码的目标代码的首地址ifAropBgotoCMOVA,Ri,CMPB,RiCJropX(1)X是标号为X的中间代码的目标代码的首地址(2)若A的值在寄存器中Ri中,则可以省去第一条代码(3)若B在某个寄存器中Rj,则目标代码中的B就是Rj(4)rop指的是,=,A:=*PMOVP,RiRi是分配给A的寄存器*P:=AMOVA,RiMOVRi,P若A在某个寄存器中,则第一条代码可以省去编译原理与技术编译原理与

864、技术744各种三地址语句所对应的机器指令各种三地址语句所对应的机器指令 10.5一个简单代码生成器一个简单代码生成器机器实现条件转移的方式有两种。机器实现条件转移的方式有两种。1.根据寄存器的值是否为下面根据寄存器的值是否为下面6个条件之一而进行分支:负、个条件之一而进行分支:负、零、正、非负、非零和非正。在这样的机器上,象零、正、非负、非零和非正。在这样的机器上,象ifxy,则,则CMPx,y把条件码设置为正;若把条件码设置为正;若xy,则,则CMPx,y把条件码设置为负。条件转移指令根据同上面一样把条件码设置为负。条件转移指令根据同上面一样放入条件决定向何处转移。指令放入条件决定向何处转移

865、。指令CJ=z的含义是如果条件为的含义是如果条件为负或者零则转移到地址负或者零则转移到地址z。编译原理与技术编译原理与技术74510.5一个简单代码生成器一个简单代码生成器u例例10.8:对于语句对于语句x:=y+zifx0gotoz可以翻译成下列目标代码可以翻译成下列目标代码MOVy,R0ADDz,R0MOVR0,xCJzu因为根据内部特征寄存器因为根据内部特征寄存器CT可以知道在可以知道在ADDz,R0指令之后,它是根据指令之后,它是根据x的的值设置的。的的值设置的。编译原理与技术编译原理与技术746编译原理与技术编译原理与技术第第11章章代码优化代码优化主要内容主要内容u优化的概念优化的

866、概念u代码优化的基本技术代码优化的基本技术u局部优化局部优化u机器代码优化窥孔技术机器代码优化窥孔技术编译原理与技术编译原理与技术74811.1代码优化的概念代码优化的概念u代码优化在整个编译过程的位置代码优化在整个编译过程的位置编译原理与技术编译原理与技术749编译前端中间代码优化目标代码生成中间代码生成源程序目标程序中间代码中间代码目标代码优化中间代码编译前端编译器可以改进过程调用、循环和地址计算目标代码生成中间代码源程序目标程序程序员可以改进算法,改变循环编译器可以利用寄存器,选择指令和窥孔优转换程序员和编译器可能改上程序的位置程序员和编译器可能改上程序的位置11.1代码优化的概念代码优

867、化的概念u设计和实现编译程序代码优化的原则:设计和实现编译程序代码优化的原则:(1)等价原则:经过优化后的代码应该保持程)等价原则:经过优化后的代码应该保持程序的输入输出,不应改变程序运行的结果。序的输入输出,不应改变程序运行的结果。(2)有效原则:优化后的代码应该在占用空间、)有效原则:优化后的代码应该在占用空间、运行速度这两个方面,或者其中的一个方面运行速度这两个方面,或者其中的一个方面得到改善。得到改善。(3)经济原则:代码优化需要占用计算机和编)经济原则:代码优化需要占用计算机和编译程序的资源,代码优化取得的效果应该超译程序的资源,代码优化取得的效果应该超出优化工作所付出的代价。否则,

868、代码优化出优化工作所付出的代价。否则,代码优化就失去了意义。就失去了意义。编译原理与技术编译原理与技术75011.1代码优化的概念代码优化的概念u代码优化依据机器相关性、优化范围和优化语言代码优化依据机器相关性、优化范围和优化语言级别的分类级别的分类按照与机器相关的程度,可以分为与机器相关的代码按照与机器相关的程度,可以分为与机器相关的代码优化和与机器无关的代码优化。优化和与机器无关的代码优化。l与机器相关的优化一般有寄存器的优化、多处理器的优化、与机器相关的优化一般有寄存器的优化、多处理器的优化、特殊指令的优化以及无用指令的消除等技术。显然,特殊指令的优化以及无用指令的消除等技术。显然,这几

869、这几类优化与具体机器的特性密切相关,例如寄存器的总数,寄类优化与具体机器的特性密切相关,例如寄存器的总数,寄存器的具体使用规定,等等。这类优化通常的在目标代码生存器的具体使用规定,等等。这类优化通常的在目标代码生成之后进行。成之后进行。l与机器无关的优化是在目标代码生成以前进行,主要是根据与机器无关的优化是在目标代码生成以前进行,主要是根据程序的控制信息和数据信息,对程序进行优化,与机器无关。程序的控制信息和数据信息,对程序进行优化,与机器无关。编译原理与技术编译原理与技术75111.1代码优化的概念代码优化的概念根据优化的范围,可以划分为局部优化和全局优化两类。根据优化的范围,可以划分为局部

870、优化和全局优化两类。l考察一个基本块的三地址中间代码序列就可以完成的优化,称为局部优化。考察一个基本块的三地址中间代码序列就可以完成的优化,称为局部优化。而全局优化则必须在考察基本块之间的相互联系与作用的基础上才能完成。而全局优化则必须在考察基本块之间的相互联系与作用的基础上才能完成。l代码优化总是在内部的中间代码和目标代码上进行的。在通常的编译程序代码优化总是在内部的中间代码和目标代码上进行的。在通常的编译程序中,代码优化往往是在中间代码这一级执行,例如对源程序的三地址代码中,代码优化往往是在中间代码这一级执行,例如对源程序的三地址代码或抽象语法树上采取优化措施。相对于在目标代码级别的优化,

871、在中间代或抽象语法树上采取优化措施。相对于在目标代码级别的优化,在中间代码优化的好处是:码优化的好处是:(1)容易从中间代码中识别处进行优化的情况,对目标语言代码的信息识别要困)容易从中间代码中识别处进行优化的情况,对目标语言代码的信息识别要困难,成本较高;难,成本较高;(2)中间代码于机器无关,因此,一个代码优化的程序可以适用于多种型号的机)中间代码于机器无关,因此,一个代码优化的程序可以适用于多种型号的机器。器。l有时也在目标代码语言级上进行代码优化,如寄存器优化等。在目标代码有时也在目标代码语言级上进行代码优化,如寄存器优化等。在目标代码级别上进行全局优化的代价昂贵,本书仅仅讨论局部范围

872、的目标代码优化级别上进行全局优化的代价昂贵,本书仅仅讨论局部范围的目标代码优化技术,即所谓的窥孔优化。技术,即所谓的窥孔优化。编译原理与技术编译原理与技术75211.2代码优化的基本技术代码优化的基本技术u分类:分类:与与机机器器无无关关的的、在在中中间间代代码码语语言言级级的的代代码码优优化主要包括:化主要包括:删除公共子表达式删除公共子表达式复写传播复写传播删除无用代码删除无用代码代码外提代码外提强度消弱强度消弱删除归纳变量删除归纳变量其中最后三种是专门针对循环语句的优化。其中最后三种是专门针对循环语句的优化。编译原理与技术编译原理与技术75311.2代码优化的基本技术代码优化的基本技术v

873、oidquicksort(m,n)intm,n;inti,j,v,x;if(n=m)return;/*程序段开始*/i=m1;j=n;v=an;while(1)doi=i1;while(aiv);if(i=j)break;x=ai;ai=aj;aj=x;x=ai;ai=an;an=x/*程序段结束*/quicksort(m,j);quicksort(i+1,n);图图11.3 11.3 快速排序的快速排序的C C代代码码(1)i:=m1(2)j:=n(3)t1:=4n(4)v:=at1(5)i:=i1(6)t2:=4i(7)t3:=at2(8)ift3vgoto(5)(9)j:=j1(10)t

874、4:=4j(11)t5:=at4(12)ift5vgoto(9)(13)ifi=jgoto(23)(14)t6:=4i(15)x:=at6(16)t7:=4i(17)t8:=4j(18)t9:=at8(19)at7:=t9(20)t10:=4j(21)at10:=x(22)goto(5)(23)t11:=ai(24)x:=at11(25)t12:=4i(26)t13:=4n(27)t14:=at12(28)at12:=t14(29)t15:=4n(30)at15:=x图图11.4 11.4 快速排序部分程序的三地址代码快速排序部分程序的三地址代码编译原理与技术编译原理与技术75411.2代码优

875、化的基本技术代码优化的基本技术u删除公共子表达式删除公共子表达式编译原理与技术编译原理与技术755i := m 1j := nt1 := 4nv := at1t11 := 4ix := at11t12 := 4it13 := 4nt14 := at12at12 := t14t15 := 4nat15 := xi := i 1t2 := 4it3 := at2if t3 v goto B1if i = j goto B6B1B2B4j := j 1t4 := 4jt5 := at4if t5 v goto B3t6 := 4ix := at6t7 := 4it8 := 4jt9 := at8at

876、7 := t9t10 := 4jat10 := xgoto B2B3B5B6如果表达式如果表达式E已经计算过,已经计算过,并且在这之后并且在这之后E中变量的值中变量的值没有改变,那么,没有改变,那么,E的这个的这个再次出现称为公共子表达再次出现称为公共子表达式。如果可以利用先前的式。如果可以利用先前的计算结果,就可以避免表计算结果,就可以避免表达式的重复计算,这种优达式的重复计算,这种优化称为删除公共子表达式。化称为删除公共子表达式。11.2代码优化的基本技术代码优化的基本技术编译原理与技术编译原理与技术756这是仅限于基本块的局部优化,B5仍然需要计算4*i和4*j。若在整个图11.3的程序

877、来看,它们依然是公共子表达式,4*i在B2中计算并且赋给了t2,4*j在B2中计算并且赋给了t4。继续删除这些公共子表达,在B5中就可以把t6:=4*i替换为t6:=t2t8:=4*j替换为t8:=t4这是因为在B2中计算的4*i传到B5时,没有改变i的值。同样,在B3中计算的4*j传到B5时,没有改变j的值。对于B6也可以作同样的处理。例如,在图例如,在图11.5的的B5中分别把公中分别把公共子表达式共子表达式4*i和和4*j的值赋给的值赋给t7和和t10,这些重复计算的公共子表达,这些重复计算的公共子表达式可以删除,用式可以删除,用t6代替代替t7,用,用t8代替代替t10,这样就把,这样

878、就把B5变换为如变换为如下的代码:下的代码:B5:t6:=4 ix:=at6t7:=t6t8:=4 jt9:=at8at7:=t9t10:=t8at10:=xgotoB211.2代码优化的基本技术代码优化的基本技术删除公共子表达式后的B5和B6分别是分别是编译原理与技术编译原理与技术757t6:=t2x:=at6t7:=t6t8:=t4t9:=at8at7:=t9t10:=t8at10:=xgotoB2B5B6t11:=t2x:=at11t12:=t11t13:=t1t14:=at12at12:=t14t15:=t1at15:=x11.2代码优化的基本技术代码优化的基本技术复写传播复写传播编译

879、原理与技术编译原理与技术758B5:t6:=t2x:=t3t7:=t2t8:=t4t9:=t5at2:=t5t10:=t4at4:=t3gotoB2观察上面观察上面B5中的语句:中的语句:t6:=t2和和x:=at6,t6在赋值完成之后就被引用,而期间没有改变在赋值完成之后就被引用,而期间没有改变t6的值。因此,可以把的值。因此,可以把x:=at6变换为变换为x:=at2。这种变换叫做复写传播,它是对形式。这种变换叫做复写传播,它是对形式为为f:=g的赋值语句(复写)的变换。的赋值语句(复写)的变换。完成上述的复写传播之后,进一步考察可以完成上述的复写传播之后,进一步考察可以发现,在发现,在B

880、2中计算了中计算了t3:=at2,因此,在,因此,在B5中可以删除公共子表达式,把中可以删除公共子表达式,把x:=at2替换为替换为x:=t3进而,通过复写传播把进而,通过复写传播把B5中中at4:=x替换为替换为at4:=t3同样,同样,B5中的中的t9:=at4和和at2:=t9可以分可以分别替换为别替换为t9:=t5和和at2:=t5。这样,。这样,B5就就变为变为11.2代码优化的基本技术代码优化的基本技术编译原理与技术编译原理与技术759i:=m1j:=nt1:=4nv := at1i:=i1t2:=4it3 := at2if t3 v goto B1ifi=jgotoB6B1B2B

881、4j:=j1t4:=4jt5 := at4if t5 v goto B3at2:=t5at4:=t3gotoB2at2:=vat1:=t3B3B5B6图图11.7 复写传播和删除无用代复写传播和删除无用代码之后码之后删除无用语句删除无用语句 观察经过复写变换之后的B5,可以发现,变量x、t7、t8、t9、t10的值在整个程序汇总不再使用,因此,这些变量的赋值对程序的运算结果没有任何作用,可以把他们删除掉。我们称之为删除无用代码。删除无用赋值语句之后的B5变为:B5:at2:=t5at4:=t3gotoB2对B6可以进行同样的处理,结果如图11.7所示11.2代码优化的基本技术代码优化的基本技术

882、u代码外提代码外提循循环环是是多多次次重重复复执执行行的的代代码码段段,在在编编译译程程序序的的优优化化工作中,循环优化占有十分重要的地位。这是因为,工作中,循环优化占有十分重要的地位。这是因为,l对对于于循循环环次次数数为为n的的一一个个循循环环,每每节节省省循循环环体体内内一一条条目目标标指指令令,运运行行时时间间就就可可以以少少执执行行n条条指指令令;特特别别地地,对对于于m重重循循环环的的最最内内循循环环,每每节节省省一一条条指指令令就就可可以以减减少少执执行行n1*n2*nm条指令。条指令。l此此外外,现现代代的的高高级级编编程程语语言言中中都都支支持持数数组组、集集合合、树树、图图

883、等等数数据据结结构构,它它们们广广泛泛地地应应用用在在程程序序中中。因因此此,按按照照经济原则,循环优化的效果最为显著。经济原则,循环优化的效果最为显著。编译原理与技术编译原理与技术76011.2代码优化的基本技术代码优化的基本技术如果它产生的结果在循环中保持不变,就可如果它产生的结果在循环中保持不变,就可以把它提到循环的外边,以避免每次循环都以把它提到循环的外边,以避免每次循环都执行这条代码。例如,对于下面的执行这条代码。例如,对于下面的while语句语句while(i=limit-2)如果在循环体内如果在循环体内limit的值不变,就可以把它的值不变,就可以把它变换为变换为t:=limit

884、-2;while(i=jgotoB6之外,不再被引用。因此,可以删除这些归纳变量i和j,把这个语句变换为ift2=t4gotoB6。经过强度消弱和删除归纳变量,图11.7以及图11.5最后就变换为图11.8。优化的效果:1.B2和B3的代码数从4条减到3条,其中一条从乘法变为加法;2.B5从9条变为3条,B6从8条变为2条。3.尽管B1从4条增加到6条,但是,B1这段代码在程序中仅仅执行一次,所以程序总的运行时间几乎不受B1大小的影响。i:=m1j:=nt1:=4nv:=at1t2:=4it4:=4jt2:=t24t3:=at2ift3vgotoB1ifi=jgotoB6B1B2B4t4:=t

885、4-4t5 := at4if t5 v goto B3at2:=t5at4:=t3gotoB2at2:=vat1:=t3B3B5B6图图11.8B5和和B6删除公共子表达式以后删除公共子表达式以后11.3局部优化局部优化u基本块的变换基本块的变换除除了了上上面面介介绍绍的的删删除除公公共共子子表表达达式式和和删删除除无无用用代代码码这这两两种种优优化化以以外外,在在基基本本块块内内可可以以进进行行多多种种等等价价变变换换,改进代码的质量。改进代码的质量。合并已知量合并已知量l假设在一个基本块内有两条语句:假设在一个基本块内有两条语句:t1:=2t2:=3 t1l如果对如果对t1赋值后,赋值后,

886、t1的值没有改变过,那么,赋值语句的值没有改变过,那么,赋值语句t2:=3 t1右边的两个运算数在编译时刻都是已知量,就可以在右边的两个运算数在编译时刻都是已知量,就可以在编译时计算出这条赋值语句的右边,而不必等到程序运行时编译时计算出这条赋值语句的右边,而不必等到程序运行时再计算。即,可以把这条语句改为再计算。即,可以把这条语句改为t2:=6。这种变换称为合。这种变换称为合并已知量。并已知量。编译原理与技术编译原理与技术76411.3局部优化局部优化临时变量更名临时变量更名l在一个基本块内有两条语句在一个基本块内有两条语句u:=a+b和和v:=a+b,其中,其中u和和v都是基本快内的临时变量

887、,如果用都是基本快内的临时变量,如果用u替换基本块内的所有替换基本块内的所有v的的引用,那么,不改变基本块内的值。事实上,总可以通过改引用,那么,不改变基本块内的值。事实上,总可以通过改变基本块内临时变量的名字而得到一个等价的基本块。变基本块内临时变量的名字而得到一个等价的基本块。交换语句的次序交换语句的次序l一个基本块内的两条邻近的语句:一个基本块内的两条邻近的语句:u:=a+bz:=x+yl当且仅当当且仅当x和和y都不是都不是u,并且,并且a和和b都不是都不是z时可以改变这两时可以改变这两条语句的位置而不影响基本块。在代码生成的算法中可以发条语句的位置而不影响基本块。在代码生成的算法中可以

888、发现,有时通过改变语句的执行次序,可以产生更高效的目标现,有时通过改变语句的执行次序,可以产生更高效的目标代码。代码。编译原理与技术编译原理与技术76511.3局部优化局部优化u代数变换代数变换编译原理与技术编译原理与技术766许多代数变换可以简化表达式的运算或加快计算速度,同时保持表达式的值不变。例如,x:=x+0,x:=x0或x:=x1执行的运算构没有改变x的值,没有任何意义,可以从基本块中删除。又如,x:=y2的指数运算通常要调用一个函数来实现。可以使用等价的代数变换,用简单的乘法运算x:=yy代替幂运算。结合性也可以用于生成公共子表达式。例如,如果源程序a:=b+ce:=c+d+b被翻

889、译成三地址代码:a:=b+ct:=c+de:=t+b如果t的值在基本块之后不再需要了,可以利用加法的结合性和交换性,产生更简洁的代码:a:=b+ce:=a+d11.3局部优化局部优化u基本块的基本块的DAG实现实现如果有向图中不存在环路,则称该有向图为如果有向图中不存在环路,则称该有向图为无环有向无环有向图,简称图,简称DAG。本节使用的。本节使用的DAG的结点,带有下属的结点,带有下属标记或附加信息:标记或附加信息:l图的叶结点(无后继结点)用一个标识符(变量名)或常量图的叶结点(无后继结点)用一个标识符(变量名)或常量作标记,表示该结点代表该变量或常数的值。如果叶结点代作标记,表示该结点代

890、表该变量或常数的值。如果叶结点代表某变量表某变量X的地址,则用的地址,则用addr(X)作为该结点的标记。通常对作为该结点的标记。通常对叶结点上作为标记的标识符使用下标叶结点上作为标记的标识符使用下标0以表示它是该变量的以表示它是该变量的初始值。初始值。l图的内部结点(有后继结点)用一个运算符作标记,表示该图的内部结点(有后继结点)用一个运算符作标记,表示该结点代表应用该运算赋对其后继结点所代表的值进行运算的结点代表应用该运算赋对其后继结点所代表的值进行运算的结果。结果。l图上的各个结点还可以附加一个或若干个标识符,表示这些图上的各个结点还可以附加一个或若干个标识符,表示这些变量具有该结点所代

891、表的值。变量具有该结点所代表的值。编译原理与技术编译原理与技术76711.3局部优化局部优化u基本块的基本块的DAG实现实现编译原理与技术编译原理与技术768n1ABn2Bn1Aopn1ABn2n3C中间代码类型DAG结点(0)A:=B(1)A:=opB(2)A:=BopC=n1An2n3C=n4ABn1n3Cn2(s)n3(s)n1n2ropBCn1(s)(3)A:=BC(4)AC:=B(5)ifBropCgoto(s)(6)goto(s)11.3局部优化局部优化编译原理与技术编译原理与技术769算法算法11.1:基本块的DAG构造算法输入:基本块BBk包含k条语句输出:输出:基本块BBk的

892、DAG图说明:说明:(1)假设DAG结点用一个对象类或结构NODE表示,它包含下列数据成员:label/结点的标记,类型为标识符或常量left,right/类型为结点NODE,分别指向该结点的左后继结点和右后继结点labelList/标识符或常量链表,记录结点的附加标识符或常量函数createNode(A)创建并返回一个标记为A的结点,把left,right和labelList初始化为空。(2)假设有一个标识符(常数)与结点的对应表,Node(A)是描述这种对应关系的函数,它对每个标识符给出DAG中一个结点,或者为null表示该标示符还没有定义。NODEn=null,b=null,c=null

893、;/初始化,b和c用以记录是否为当前语句B和C产生了新结点forBB中的每个变量或常量SNode(S)=null;/初始化基本块中的每个变量或常量DAG=null;/可以用链表结构11.3局部优化局部优化编译原理与技术编译原理与技术770for(i=1;ik;i+)if(Node(B)=null)b=createNode(B);把b加入到DAG中;switchBBi的代码类型case0型:Node(B)=n;case1型:if(Node(B)的类型是叶子常量)=opB;/合并已知量if(b!=null)删除结点Node(B);if(n=Node(p)=null)n=createNode(p);

894、Node(p)=n;else/查找公共子表达式Booleanfound=false;while(notfound&DAG还没有检查的结点n)if(n.left=Node(B)|n.right=Node(B)&n.label=op)found=true;else标记该结点为检查过if(notfound)n=createNode(op);n.left=Node(B);n.right=Node(C);/结束case1的分支语句11.3局部优化局部优化编译原理与技术编译原理与技术771case2型:if(Node(C)=null)c=createNode(C);把b加入到DAG中;if(Node(B)

895、的类型是常量&Node(C)的类型是常量)=BopC;/合并已知量if(b!=null)删除结点Node(B);if(c!=null)删除结点Node(C);if(n=Node(p)=null)n=createNode(p);Node(p)=n;else/查找公共子表达式Booleanfound=false;while(notfound&DAG还有没有检查的结点n)if(n.left=Node(B)&n.right=Node(C)&n.label=op)found=true;else标记该结点为检查过if(notfound)n=createNode(op);n.left=Node(B);n.r

896、ight=Node(C);/结束case2的分支语句/结束分支语句/删除无用赋值语句if(n=Node(A)!=null&n不是叶结点)把A从n.labelList中去掉;把A加入n.labelList;/把A加入结点n的附加信息中Node(A)=n;b=null;c=null;/清除b和c以便记录是否为当前语句的B和C产生了新结点11.3局部优化局部优化例例11.1构造对下面的基本块构造对下面的基本块B的的DAG。(1)T0:=3.14(2)T1:=2 T0(3)T2:=R+r(4)A:=T1 T2(5)B:=A(6)T3:=2 T0(7)T4:=R+r(8)T5:=T3 T4(9)T6:=

897、Rr(10)B:=T5 T6处理每一条语句后构造的处理每一条语句后构造的DAG如图如图11.10所示中各所示中各个子图所示,每个子图对应每条语句。个子图所示,每个子图对应每条语句。编译原理与技术编译原理与技术77211.3局部优化局部优化编译原理与技术编译原理与技术773图11.10为例11.1基本块构造的DAG11.3局部优化局部优化u基于基于DAG的局部优化的局部优化将中间代码表示成相应的将中间代码表示成相应的DAG后,就可以对基本块利用后,就可以对基本块利用DAG进进行代码优化。观察行代码优化。观察DAG的构造过程就可以发现:的构造过程就可以发现:算法注释算法注释的步骤起到了合并已知量的

898、作用。若参与运算的对的步骤起到了合并已知量的作用。若参与运算的对象都是编译时刻的已知量,则它并不生成计算该结点值的内部象都是编译时刻的已知量,则它并不生成计算该结点值的内部结点,而是执行该运算,把计算的结果生成一个叶结点。结点,而是执行该运算,把计算的结果生成一个叶结点。算法注释算法注释的步骤的作用是检查公共子表达式。对所有具有公的步骤的作用是检查公共子表达式。对所有具有公共子表达式的语句,它只生成一个计算该表达式值的内部结点,共子表达式的语句,它只生成一个计算该表达式值的内部结点,而把哪些被赋值的变量标识符附加到该结点。这样就可以删除而把哪些被赋值的变量标识符附加到该结点。这样就可以删除冗余

899、运算。冗余运算。算法注释算法注释的步骤具有删除无用赋值语句的作用。若某变量被的步骤具有删除无用赋值语句的作用。若某变量被赋值以后,在它被引用前又被重新赋值,则把该变量从具有前赋值以后,在它被引用前又被重新赋值,则把该变量从具有前一个值的结点上删除。一个值的结点上删除。编译原理与技术编译原理与技术77411.3局部优化局部优化编译原理与技术编译原理与技术775这样,在一个基本块构造成DAG的过程就已经进行了一些局部优化工作。然后,从DAG就可以重新生成优化过的基本块。例如,将图11.10(10)按照构造结点的顺序重新写成四元式的中间代码,得到下列序列B:(1)T0:=3.14(2)T1:=6.2

900、8(3)T3:=6.28(4)T2:=R+r(5)T4:=T2(6)A:=6.28T2(7)T5:=A(8)T6:=Rr(9)B:=AT6和原来的代码相比,可以看出:B中代码(2)和(6)的已知量都被合并了;B中(5)的无用赋值也被删除;B中(3)和(7)的公共子表达式R+r只计算了一次,删除了多余运算。11.3局部优化局部优化除了可以除了可以DAG进行上述优化以外,还可以从基本块的进行上述优化以外,还可以从基本块的DAG中得中得到一些可以用以优化的信息,包括:到一些可以用以优化的信息,包括:在基本块外被定值并在基本块内被引用的所有标识符,就是作在基本块外被定值并在基本块内被引用的所有标识符,

901、就是作为叶子结点上标记的那些标识符。为叶子结点上标记的那些标识符。在基本块内被定值且该值在基本块后被引用的所有标识符,就在基本块内被定值且该值在基本块后被引用的所有标识符,就是是DAG各结点上的那些附加标识符。各结点上的那些附加标识符。利用这些信息以及有关变量在基本块之后的引用情况(可以通利用这些信息以及有关变量在基本块之后的引用情况(可以通过全局数据流分析得到,参见过全局数据流分析得到,参见10.5节),可以进一步删除基本块节),可以进一步删除基本块中其它情况的无用赋值语句。中其它情况的无用赋值语句。在上面的例子中,加入临时变量在上面的例子中,加入临时变量T0、T1、.、T6在基本块之后在基

902、本块之后都没有被引用,则可以把图都没有被引用,则可以把图10.8(10)中的)中的DAG重新写成下列重新写成下列代码:代码:(1)T2:=R+r(2)A:=6.28 T2(3)T6:=Rr(4)B:=A T6删除了其中的四个临时变量删除了其中的四个临时变量T1、T3、T4和和T5。编译原理与技术编译原理与技术77611.4机器代码优化窥孔技术机器代码优化窥孔技术窥孔优化是在目标代码级上进行局部改进的窥孔优化是在目标代码级上进行局部改进的简单而有效的技术,它只考察一小段目标指简单而有效的技术,它只考察一小段目标指令(窥孔),只要可能,就把它用更快、更令(窥孔),只要可能,就把它用更快、更短的指令

903、替换,提高目标指令的质量。短的指令替换,提高目标指令的质量。这里,窥孔是在目标程序中一个可以移动的这里,窥孔是在目标程序中一个可以移动的窗口。而且,窥孔中的代码不一定是邻接的。窗口。而且,窥孔中的代码不一定是邻接的。窥孔优化的一个特点是,优化后所产生的结窥孔优化的一个特点是,优化后所产生的结果可能会给后面的优化提供进一步的机会。果可能会给后面的优化提供进一步的机会。因此,为了得到最大的优化效果,应该对目因此,为了得到最大的优化效果,应该对目标代码进行若干遍的优化处理。标代码进行若干遍的优化处理。编译原理与技术编译原理与技术77711.4机器代码优化窥孔技术机器代码优化窥孔技术u冗余存取的删除冗

904、余存取的删除假设在源程序中有下列两条赋值语句:假设在源程序中有下列两条赋值语句:a=ac;c=ad;编译程序为它们生成的目标代码可能是下列指令序列:编译程序为它们生成的目标代码可能是下列指令序列:(1)MOVb,R0(2)ADDc,R0(3)MOVR0,a(4)MOVa,R0(5)SUBd,R0(6)MOVR0,c前三条指令对应第一条语句,后三条指令对应第二条语句,它前三条指令对应第一条语句,后三条指令对应第二条语句,它们都是正确的。然而,不难指令(们都是正确的。然而,不难指令(4)是多余的,因为指令)是多余的,因为指令(3)表明)表明a的值已经在寄存器的值已经在寄存器R0中,可以把指令(中,

905、可以把指令(4)这条多)这条多余的存储删除。但是要注意,如果指令(余的存储删除。但是要注意,如果指令(4)有标号,则有可能)有标号,则有可能抓你指令从其它指令转移控制来执行这条指令,这样的化就不抓你指令从其它指令转移控制来执行这条指令,这样的化就不能删除指令(能删除指令(4),以免不能保证),以免不能保证a的值在的值在R0中。中。编译原理与技术编译原理与技术77811.4机器代码优化窥孔技术机器代码优化窥孔技术u不可达代码的删除不可达代码的删除如果在一条无条件转移指令之后的代码没有标号,就表示没有控制会转移到这如果在一条无条件转移指令之后的代码没有标号,就表示没有控制会转移到这条代码,这条代码

906、就是不可能执行的不可达代码或死代码,可以删除。不可达条代码,这条代码就是不可能执行的不可达代码或死代码,可以删除。不可达代码的删除可以重复执行,删除一系列指令。代码的删除可以重复执行,删除一系列指令。产生不可达代码的典型情况是处于调试程序的目的,在一个大程序中插入调试产生不可达代码的典型情况是处于调试程序的目的,在一个大程序中插入调试状态变量状态变量debug。在程序的调试状态下(。在程序的调试状态下(debug=1)可以打印调试信息;在程序)可以打印调试信息;在程序正常运行的状态下(正常运行的状态下(debug=0),不打印调试信息。例如,在),不打印调试信息。例如,在C语言程序中,语言程序

907、中,可以有如下的代码片断:可以有如下的代码片断:#definedebug0if(debug=1)打印调试信息打印调试信息翻译的中间代码可能是翻译的中间代码可能是ifdebug=1gotoL1gotoL2L1:打印调试信息打印调试信息L2:在非调试状态下,调试状态变量在非调试状态下,调试状态变量debug为常量为常量0,所以,条件,所以,条件debug=1永远不满永远不满足,在无条件代码足,在无条件代码gotoL2之后的标号为之后的标号为L1的代码不可能执行,应该把它删除。的代码不可能执行,应该把它删除。编译原理与技术编译原理与技术77911.4机器代码优化窥孔技术机器代码优化窥孔技术u控制流优

908、化控制流优化由于源程序书写的任意性和随意性,生成的由于源程序书写的任意性和随意性,生成的目标代码中经常出现各种转移指令,造成连目标代码中经常出现各种转移指令,造成连续的跳转情况。控制流的窥孔优化可以在目续的跳转情况。控制流的窥孔优化可以在目标代码中删除不必要的转移。标代码中删除不必要的转移。编译原理与技术编译原理与技术78011.4机器代码优化窥孔技术机器代码优化窥孔技术编译原理与技术编译原理与技术781转移到转移转移到转移例如转移序列gotoL1L1:gotoL2可以转换为gotoL2L1:gotoL2如果没有别的指令跳到L1,并且L1的前面是无条件转移指令,那么,指令L1:gotoL2也可

909、以删除。转移到条件转移转移到条件转移假设有转移序列L0:gotoL1L1:if(b)gotoL2L3:如果只有一个转移到L1,并且L1的紧前面是无条件转移指令,那么,上述这些指令可以替换为:L0:if(b)gotoL2gotoL3L1:if(b)gotoL2L3:尽管改变前后的指令一样多,但是,改变后的转移更直接了,节省了一次无条件转移。11.4机器代码优化窥孔技术机器代码优化窥孔技术编译原理与技术编译原理与技术782条件转移到转移条件转移到转移假设有转移序列if(b)gotoL1L1:gotoL2可以替换为if(b)gotoL2L1:gotoL2改变之后,当b为true时可以节省一次控制转移

910、。11.4机器代码优化窥孔技术机器代码优化窥孔技术u代数化简与强度消弱代数化简与强度消弱窥孔优化时,可以利用代数化简,既代数恒窥孔优化时,可以利用代数化简,既代数恒等式进行等价变换,来减弱目标代码,优化等式进行等价变换,来减弱目标代码,优化代码。考虑到优化的代价和效果,仅对经常代码。考虑到优化的代价和效果,仅对经常出现的一些进行代数化简。例如,用出现的一些进行代数化简。例如,用x=x+0和和x=x 1,删除相应的指令;利用,删除相应的指令;利用x2=x x把耗时的指数运算用简单的乘法预算代把耗时的指数运算用简单的乘法预算代替。替。有些指令可以用更优的指令代替。例如,假有些指令可以用更优的指令代

911、替。例如,假设设shiftleft为左操作指令,则为左操作指令,则MULR,#2可替换为可替换为shiftleftR,#1MULR,#4可替换为可替换为shiftleftR,#2编译原理与技术编译原理与技术78311.4机器代码优化窥孔技术机器代码优化窥孔技术u特殊指令的使用特殊指令的使用若目标机器指令系统包含有实现某些操作的若目标机器指令系统包含有实现某些操作的高效指令时,在可能的情况下,应该尽量使高效指令时,在可能的情况下,应该尽量使用目标机的特性而改进目标代码的执行效率。用目标机的特性而改进目标代码的执行效率。例如,如果目标机器指令系统有减例如,如果目标机器指令系统有减1指令指令DEC,对应于赋值语句,对应于赋值语句i=i1的目标代码的目标代码MOVi,R0SUB#1,R0MOVR0,i就可以简单地用一条指令代替为:就可以简单地用一条指令代替为:DECi。编译原理与技术编译原理与技术784

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号