编译原理 第9章 语义分析和代码生成(Modified).

上传人:zh****71 文档编号:145289357 上传时间:2020-09-18 格式:PPT 页数:78 大小:625.01KB
返回 下载 相关 举报
编译原理 第9章 语义分析和代码生成(Modified)._第1页
第1页 / 共78页
编译原理 第9章 语义分析和代码生成(Modified)._第2页
第2页 / 共78页
亲,该文档总共78页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《编译原理 第9章 语义分析和代码生成(Modified).》由会员分享,可在线阅读,更多相关《编译原理 第9章 语义分析和代码生成(Modified).(78页珍藏版)》请在金锄头文库上搜索。

1、,9.1 语义分析的概念 9.2 中间代码 9.3 声明的处理 9.4 表达式语句 9.5 if语句 9.6 while语句 9.7 for循环语句 9.8 write语句 9.9 read语句 9.10 过程调用和返回 9.11 语义分析及代码生成实现,第9章 语义分析和代码生成(P169),声明的处理 表达式语句 if语句 while语句 for循环语句 write语句 read语句 过程调用和返回,学习重点,9.1 语义分析的概念,语义错误:在语法分析中,严格按文法(上下文无关文法)来检查语句的语法是否正确,但有些语句单看语法结构并没有错误,但考虑该语句所处的上下文会引起错误,称这种错误

2、为语义错误。在语义分析时会处理语义错误。,例(P169) 考虑下段程序,有哪些语义错误? int a; int b; real a,c; d=a+b; ,9.1 语义分析的概念,语义分析主要借助符号表记录的信息来实现语义分析动作。常见的语义分析动作有: 1)对表达式中的操作数进行类型的一致性检查。当发现类型不一致时,则强制或自动作相应的类型转换。 2)语义分析的另一个重要功能是要分析由语法分析所识别出来的语句的意义并作相应的语义处理。例如,对声明语句,用户通过这类语句声明程序中要使用的变量,并说明其种类和类型等特性。语义分析程序就要将变量名及其有关属性填入符号表,以备后面使用。对于程序中的可执

3、行语句,则要根据该语句的语义生成相应的中间代码或目标代码。,9.2 中间代码,使用中间代码的优点: 不考虑机器的特性,使生成的中间代码较为简单。 生成中间代码的编译程序移植性好,只需为该中间代码开发一个解释器或者将中间代码翻译为目标机指令就能在目标机上运行。 在中间代码上更便于做优化处理。,常见的中间代码: (1)波兰后缀表示 (2)N-元表示 三元式 四元式,9.2 中间代码,波兰后缀表示的特点: 操作符位于操作数之后。,例(P170) 算术表达式:F*3.1416*R*(H+R) 波兰后缀表示:F3.1416*R*HR+* 赋值表达式:S=F*3.1416/R*(H+R) 波兰后缀表示:

4、SF3.1416*R/HR+*=,9.2 中间代码,9.2 中间代码,将中缀表达式转换成波兰后缀表示的算法实现: 设置一个操作符栈,当扫描到操作数时,就立即输出该操作数。当遇到操作符时,则要与栈顶操作符比较其优先级,若栈顶操作符优先级高于栈外操作符,则输出该栈顶操作符;反之,则栈外操作符入栈。而对于赋值表达式,只需定义赋值操作符“=”的优先级低于可在表达式中出现的其他操作符。,9.2 中间代码,波兰后缀表示除可用来表示表达式类的语言结构以外,也能够通过操作符的扩充来表示其他的语言结构。,9.2 中间代码,例(P171) 条件语句:if then else 可转换成波兰后缀表示: BZBR 注意

5、:在该波兰表示中,引入了BZ和BR操作符。,9.2 中间代码,9.2 中间代码,三元式的一般表示: ,,9.2 中间代码,例(P171) 表达式W*X+(Y+Z)可用如下三元式序列表示: (1) *,W, X (2) +,Y, Z (3) +,(1),(2),对每一个三元式都编上号码,且对前面三元式的计算结果的引用可用三元式的编号来表示。,例(P171)条件语句if (XY) Z=X;else Z=Y+1; 可以用如下三元式序列表示: (1) ,X,Y (2) BMZ,(1),(5) (3) =,Z,X (4) BR, ,(7) (5) +,Y,1 (6) =,Z,(5) (7) 注意:操作符

6、BMZ和BR分别表示零转移和无条件转移。,9.2 中间代码,四元式的一般形式: , 其中,表示操作符操作的结果,该结果通常是一临时变量,在以后可以由编译程序分配给一个寄存器或者一个主存地址。四元式的优点是便于进行优化处理。,9.2 中间代码,例(P172)表达式(A+B)*(C+D)-E能够由下列四元式序列表示: +,A,B, T1 +,C,D,T2 *,T1,T2,T3 -,T3,E,T4 注意:T1、T2、T3和T4均是临时变量。,要开发出可移植又适用的编译程序的一种方法是使编译程序产生一种作为源程序中间形式的抽象机的代码。而该抽象机的指令应当尽可能模仿所编译的源语言的结构,且具备下列特点

7、: 1、可移植性:如果花很小的代价,就能将一个程序移植到另一台机器上,那么称该程序是可移植的。 2、可适应性:如果一个程序能够容易地进行修改就能满足不同的用户和系统的需求,那么称该程序是可适应的。,9.2 中间代码,要将一个给定的编译程序从X机移植到Y机,如果给定的编译程序已分成前端和后端两部分,而且这两部分之间定义有良好接口,那么移植的主要工作仅仅是重写现有编译程序的代码生成程序以产生Y机的代码。一种较理想的接口形式是抽象机的汇编程序,即能够将源语言的各种语法结构映射到该抽象机的伪操作上。,9.2 中间代码,构建抽象机模型的基本原则: 1)与源语言的操作和数据的良好对应:编译程序的前端将源程

8、序中的每一个原始操作和原始数据模式翻译成抽象机指令。 2)在目标机上的高效实现:抽象机的伪操作能够迅速转换成目标机的机器指令。 3)虚拟体系结构:需要为抽象机体系构建一个运行环境,以便在该环境中模拟语言的数据模式和操作的相互作用。,9.2 中间代码,用一个抽象机的汇编语言作为TEST编译器的目标语言。TEST机的指令仅能作为TEST语言的目标。TEST机的模拟程序直接从一个文件中读取汇编代码并执行它,因此避免了由汇编语言翻译为机器代码的过程。但是,这个模拟程序并非是一个真正的汇编程序,它没有符号地址或标号。,TEST编译器,TEST程序,TEST机,TEST机 汇编代码,运行 结果,9.2 中

9、间代码,为了提高可读性、简化代码生成过程,我们的目标平台所采用的机器是一台抽象的栈式计算机,它用一个栈来保存操作数,并有足够的内存空间,该抽象机(TEST机)的常用汇编指令(P173)如下: LOAD D 将D中的内容加载到操作数栈。 LOADI 常量 将常量压入操作数栈 STO D 将操作数栈栈顶单元内容存入D,且栈顶单元内容保持不变。 STI D 将操作数栈栈顶单元内容存入D,且栈顶单元出栈。 ADD 将次栈顶单元与栈顶单元内容出栈并相加,和置于栈顶。,9.2 中间代码,SUB 将次栈顶单元减去栈顶单元内容并出栈,差置于栈顶。 MULT 将次栈顶单元与栈顶单元内容出栈并相乘,积置于栈顶。

10、DIV 将次栈顶单元与栈顶单元内容出栈并相除,商置于栈顶。 BR lab 无条件转移到lab BRF lab 检查栈顶单元逻辑值,若为假(0)则转移到lab EQ 将栈顶两单元做等于比较,并将结果真或假(1或0)置于栈顶 NOTEQ 将栈顶两单元做不等于比较,并将结果真或假(1或0)置于栈顶 GT 次栈顶大于栈顶操作数,则栈顶置1,否则置0,9.2 中间代码,LES 次栈顶小于栈顶操作数,则栈顶置1,否则置0 GE 次栈顶大于等于栈顶操作数,则栈顶置1,否则置0 LE 次栈顶小于等于栈顶操作数,则栈顶置1,否则置0 AND 将栈顶两单元做逻辑与运算,并将结果真或假(1或0)置于栈顶 OR 将栈

11、顶两单元做逻辑或运算,并将结果真或假(1或0)置于栈顶 NOT 将栈顶的逻辑值取反 IN 从标准输入设备(键盘)读入一个整型数据,并入操作数栈 OUT 将栈顶单元内容出栈,并输出到标准输出设备(显示器) STOP 停止执行,9.2 中间代码,例(P173) 有下段程序: int a,b; a=10; b=20*a; 假设记录a、b属性信息符号表为:,则相对应的抽象机汇编程序如下: LOADI 10 /将常量10入栈 STO 0 /将栈顶内容10存入地址0 LOADI 20 /将常量20入栈 LOAD 0 /将地址0的内容10加载到操作数栈 MULT /将次栈顶单元与栈顶单元内容出栈并相乘,积置

12、于栈顶。 STO 2 /将栈顶内容200存入地址2,9.3 声明的处理,从代码生成和语义分析的要求出发,处理声明时编译程序的主要任务: 1)分离出每一个被声明的实体,并将它的名字填入符号表。 2)尽可能多地将要保留在符号表中的有关该实体的信息填入符号表。,一旦声明了一个实体,编译器就可使用符号表中的信息进行如下的分析和处理: 1)检查对所声明实体的引用是否正确。 2)利用已声明实体的属性信息,例如类型和已分配的目标地址,为其它执行语句生成正确的目标代码。,不同的程序设计语言,声明语句的结构也不一样。 有的语言类型说明在实体前,有的在实体后。 有的语言要求每一个实体都要用一个独立的声明语句进行声

13、明(Ada语言即属于此类),有的语言在一个独立的声明语句中可声明多个类型相同的实体。,9.3 声明的处理,9.3 声明的处理,PASCAL声明语句的类型说明是在实体后且允许一次声明多个实体,如“VAR age,day:integer”,那么,在自左向右扫描和处理这样的声明语句时,编译器分离出实体后,不知道该实体的类型,填符号表时,无法填写类型信息,因此,必须记住这些未填类型的实体在符号表中的位置,以便在扫描到类型之后,将声明语句中有关实体的全部信息回填到符号表中。,C语言声明语句的类型说明是在实体前,而且,允许一条声明语句可声明多个类型相同的实体,如“int a,b,c;”。在自左向右扫描和处

14、理C语言声明语句时,编译器首先知道类型,在扫描到后面的实体后,就可为该实体建立符号表的记录,并可将类型及其它信息填入符号表中。,符号常量在程序的执行期间不发生改变,其优点是:符号常量名声明一次,在程序中就可以多次使用;若要改变符号常量的值只需修改符号常量声明中符号常量的定义值。符号常量标识符被看作是全局的,因此要存放在符号表的全局部分。,9.3 声明的处理,PASCAL语言定义符号常量的属性翻译文法: const n=c,s插入n,c,s c,sc,s|c,s |c,s,9.3 声明的处理,常量声明的处理流程: 1)首先识别常量的名字,将其赋给属性n; 2)识别综合常量表达式,将其值放在c中,

15、并将表达式的类型赋给属性s。 3)调用动作程序插入,其功能是调用符号表管理程序,将名字n、类型s及表达式的值c填入符号表中。,例(P175) const SYMBSIZE=1024,如果一个标识符声明为常量,在程序中不能对该标识符进行赋值,只能引用。因为符号常量名虽然也保存在符号表中,但符号表中记录了符号常量的名字、类型及符号常量的值,并没有为其分配内存地址,这是符号常量与变量的关键区别。,9.3 声明的处理,C语言没有符号常量的概念,但C语言提供了宏定义,如#define PI 3.14159,其功能与符号常量差不多,但概念不一样。C语言的宏定义是在预处理中完成的,在预处理中将C源程序中的所

16、有PI替换成3.14159,因此,C编译系统实际编译的源程序并没有PI这个符号,自然PI也不会出现在符号表中。,简单变量是一种保存单个数据实体的数据区,该数据实体在程序中通常声明有指定的类型。遇到简单变量声明时,除了将其名字、类型、维数等信息填入符号表外,还要对变量分配存贮空间。对于实型、整型、逻辑型和固定长度字符串类型的变量,根据所声明的变量类型就可以确定在运行时必须为变量分配的存贮空间的大小。而对于动态数据类型,如可变长度的字符串、特殊类型、类数据类型等存贮空间大小不定的数据类型,则需要作特殊的考虑。,9.3 声明的处理,考虑下列简单变量声明的属性翻译说明: t,in svardeft,n,i ; t,irealt,i|intt,i|chart,i 动作符号svardeft,n,i:查询符号表,若没有,将类型t及变量名n存入符号表,为该变量名分配存储空间,并将存储空间地址i存入符号表中;若有,报告错误:变量重复定义。,9.3 声明的处理,符合上述文法的变量声明的例子如下: real x; int j;

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 其它办公文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号