版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
语义分析和中间代码生成第一页,共一百一十八页,2022年,8月28日本章在编译程序中的地位表格管理词法分析器语法分析器语义分析与中间代码产生优化器目标代码生成器源程序单词符号语法单位中间代码中间代码目标代码出错处理第二页,共一百一十八页,2022年,8月28日6.1概述6.2属性文法6.3几种常见的中间语言(*四元式)6.4表达式及赋值语句的翻译6.5控制语句的翻译 6.6数组元素的翻译6.7过程或函数调用语句的翻译*6.8说明语句的翻译内容安排第三页,共一百一十八页,2022年,8月28日6.1概述
6.1.1语义分析的概念一个源程序经过词法分析、语法分析之后,表明该源程序在书写上是正确的,并且符合程序语言所规定的语法。但是语法分析并未对程序内部的逻辑含义加以分析,因此编译程序接下来的工作是语义分析,即审查每个语法成分的静态语义。如果静态语义正确,则生成与该语言成分等效的中间代码,或者直接生成目标代码。
第四页,共一百一十八页,2022年,8月28日直接生成目标代码
直接生成机器语言或汇编语言形式的目标代码的优点是编译时间短且无需中间代码到目标代码的翻译。生成中间代码
生成中间代码的优点是使编译结构在逻辑上更为简单明确,特别是使目标代码的优化比较容易实现。第五页,共一百一十八页,2022年,8月28日语义分析时语义检查的分类:动态语义检查需要生成相应的目标代码,它是在运行时进行的;例如:除零溢出错误。静态语义检查在编译时完成的,它涉及以下几个方面:
(1)类型检查
(2)控制流检查
(3)一致性检查第六页,共一百一十八页,2022年,8月28日各种条件表达式的类型不是布尔类型;运算符的分量类型不相容;赋值语句左右类型不相容;形、实参类型不相容;函数说明和函数返回类型不相容;……intx;floatf();x=f();符合变量声明的语法、语义符合函数声明的语法、语义符合赋值语句的语法、不符合语义(1)类型检查第七页,共一百一十八页,2022年,8月28日
(2)控制流检查用以保证控制语句有合法的转向点。如C语言中不允许goto语句转入case语句流;break语句需寻找包含它的最小switch、while或for语句方可找到转向点,否则出错。
(3)一致性检查如在相同作用域中标识符只能说明一次、case语句的标号不能相同、函数调用参数个数要相同等。
第八页,共一百一十八页,2022年,8月28日常见的语义错误声明和使用相关的语义错误标识符没有声明;重复声明;如何检查?每当遇到新声明的标识符,查符号表如果当前有效的所有标识符中有相同名字的,则是重复声明错误;否则生成它的属性信息,保存到符号表中;每当遇到标识符的使用,查符号表如果没有找到,说明该标识符没有声明;否则,得到该标识符的属性,进行进一步分析;第九页,共一百一十八页,2022年,8月28日语义分析阶段只产生中间代码而不生成目标代码的方法使编译程序的开发变得较为容易,但语义分析不像词法分析和语法分析那样可以分别用正规文法和上下文无关文法描述。
由于语义是上下文有关的,因此语义的形式化描述是非常困难的,目前较为常见的是用属性文法作为描述程序语言语义的工具,并采用语法制导翻译的方法完成对语法成分的翻译工作。第十页,共一百一十八页,2022年,8月28日
语法制导翻译的方法就是为每个规则配上一个翻译子程序(称语义动作或语义子程序),并在语法分析的同时执行这些子程序。
语义动作是为规则赋予具体意义的手段,它一方面指出了一个规则所产生的符号串的意义,另一方面又按照这种意义规定了生成某种中间代码应做哪些基本动作。
在语法分析过程中,当一个规则获得匹配(对于自上而下分析)或用于归约(对于自下而上分析)时,此规则相应的语义子程序就进入工作,完成既定的翻译任务。语法制导翻译方法第十一页,共一百一十八页,2022年,8月28日
语法制导翻译分为自下而上语法制导翻译和自上而下语法制导翻译,我们重点介绍自下而上语法制导翻译。假定有一个自下而上的LR分析器,我们可以把这个LR分析器的能力加以扩大,使它能在用某个规则进行归约的同时调用相应的语义子程序进行有关的翻译工作;每个规则的语义子程序执行之后,某些结果(语义信息)必须作为此规则的左部符号的语义值暂时保存下来,以便以后语义子程序引用这些信息。第十二页,共一百一十八页,2022年,8月28日
此外,原LR分析器的分析栈也加以扩充,以便能够存放与文法符号相对应的语义值。这样,分析栈可以存放三类信息:分析状态、文法符号及文法符号对应的语义值。扩充后的分析栈如图6–1所示。
第十三页,共一百一十八页,2022年,8月28日图6–1扩充后的LR分析栈第十四页,共一百一十八页,2022年,8月28日
作为一个例子,我们考虑下面的文法及语义动作所执行的程序:规则 语义动作
(0)S'→E printval[TOP](1)E→E(1)+E(2) val[TOP]=val[TOP]+val[TOP+2](2)E→E(1)*E(2) val[TOP]=val[TOP]*val[TOP+2](3)E→(E(1))val[TOP]=val[TOP+1](4)E→i val[TOP]=lexval(注:lexval为i的整型内部值)第十五页,共一百一十八页,2022年,8月28日
我们扩充分析栈工作的总控程序功能,使其在完成语法分析的同时也能完成语义分析工作(这时的语法分析栈已成为语义分析栈);即在用某一个规则进行归约之后,调用相应的语义子程序完成与所用规则相应的语义动作,并将每次工作后的语义值保存在扩充后的“语义值”栈中。图6–2表示算术表达式7+9*5#的语法树及各结点值,而表6.1则给出了根据分析表用LR语法制导翻译方法得到的该表达式的语义分析和计值过程。第十六页,共一百一十八页,2022年,8月28日图6–2语法制导翻译计算表达式
7+9*5#的语法树第十七页,共一百一十八页,2022年,8月28日表6.1表达式7+9*5#的语义分析和计值过程步骤状态栈符号栈语义栈输入串动作10#_7+9*5#s3203#7__+9*5#r4301#E_7+9*5#s44014#E+_7_9*5#s350143#E+9_7__*5#r460147#E+E_7_9*5#s5701475#E+E*_7_9_5#s38014753#E+E*5_7_9__#r49014758#E+E*E_7_9_5#r2100147#E+E_7_45#r11101#E_52#acc第十八页,共一百一十八页,2022年,8月28日6.2属性文法
6.2.1文法的属性
属性是指与文法符号的类型和值等有关的一些信息,在编译中用属性描述处理对象的特征。随着编译的进展,对语法分析产生的语法树进行语义分析,且分析的结果用中间代码描述出来。对于一棵等待翻译的语法树,它的各个结点都是文法中的一个符号X,该X可以是终结符或非终结符。根据语义处理的需要,在用规则A→αXβ进行归约或推导时,应能准确而恰当地表达文法符号X在归约或推导时的不同特征。第十九页,共一百一十八页,2022年,8月28日
例如:
判断变量X的类型是否匹配,要用X的数据类型来描述;判断变量X是否存在,要用X的存储位置来描述;而对X的运算,则要用X的值来描述;
因此,语义分析阶段引入X的属性,如X.type、X.place、X.val等来分别描述变量X的类型、存储位置以及值等不同的特征。
文法符号属性分:继承属性综合属性第二十页,共一百一十八页,2022年,8月28日
继承属性
用于“自上而下”传递信息。继承属性由相应语法树中结点的父结点属性计算得到,即沿语法树向下传递,由根结点到分枝(子)结点,它反映了对上下文依赖的特性。继承属性可以很方便地用来表示程序语言上下文的结构关系。
综合属性
用于“自下而上”传递信息。综合属性由相应语法分析树中结点的分枝结点(即子结点)属性计算得到,其传递方向与继承属性相反,即沿语法分析树向上传递,从分枝结点到根结点。第二十一页,共一百一十八页,2022年,8月28日
属性文法是一种适用于定义语义的特殊文法,即在语言的文法中增加了属性的文法,它将文法符号的语义以“属性”的形式附加到各个文法的符号上(如上述与变量X相关联的属性X.type、X.place和X.val等),再根据规则所包含的含义,给出每个文法符号属性的求值规则,从而形成一种带有语义属性的上下文无关文法,即属性文法。属性文法也是一种翻译文法,属性有助于更详细地指定文法中的代码生成动作。6.2.2属性文法第二十二页,共一百一十八页,2022年,8月28日例如,简单算术表达式求值的属性文法如下:规则 语义规则(1) S→E print(E.val)(2) E→E(1)+T
E.val=E(1).val+T.val(3) E→T E.val=T.val(4) T→T(1)*FT.val=T(1).val*F.val(5) T→T(1) T.val=T(1).val(6) F→(E) F.val=E.val(7) F→i F.val=i.lexval第二十三页,共一百一十八页,2022年,8月28日上面的一组规则中,每一个非终结符都有一个属性val来表示整型值,如E.val表示E的整型值,而i.lexval则表示i的整型内部值。与规则关联的每一个语义规则的左部符号E、T、F等的属性值的计算由其各自相应的右部符号决定,这种属性也称为综合属性。与规则S→E关联的语义规则是一个函数print(E.val),其功能是打印E规则的值。S’在语义规则中没有出现,可以理解为其属性是一个虚属性。第二十四页,共一百一十八页,2022年,8月28日简单变量类型说明的文法G[D]如下:G[D]:D→intL∣floatLL→L,id∣id其对应的属性文法为:规则语义规则(1) D→TL L.in=T.type(2) T→int
T.type=int(3) T→float T.type=float(4) L→L(1),idL(1).in=L.in;addtype(id.entry,L.in)(5) L→id addtype(id.entry,L.in)注意到与文法G[D]相应的说明语句形式可为intid1,id2,…,idn
或者floatid1,id2,…,idn第二十五页,共一百一十八页,2022年,8月28日
非终结符T有一个综合属性type,其值为int或float。语义规则L.in=T.type表示L.in的属性值由相应说明语句指定的类型T.type决定;属性L.in被确定后将随语法树的逐步生成而传递到下边的有关结点使用,这种结点属性称为继承属性。由此可见,标识符的类型可以通过继承属性的复写规则来传递。例如,对输入串inta,b,根据上述的语义规则,可在其生成的语法树中看到用“→”表示的属性传递情况,如图6–3所示。第二十六页,共一百一十八页,2022年,8月28日图6–3属性信息传递情况示意第二十七页,共一百一十八页,2022年,8月28日属性翻译文法(属性文法)
如语法分析方法是自下而上的,在用某一规则进行规约的同时就执行相应的语义,在分析出一个句子时,这个句子的“值”也就同时产生了。对于文法的每个规则都配备了一组属性的计算规则,称为语义规则。属性与变量一样,可以进行计算和传递。属性加工的过程即是语义处理的过程。第二十八页,共一百一十八页,2022年,8月28日6.3几种常见的中间语言
抽象语法树6.3.2逆波兰表示法6.3.3三地址代码第二十九页,共一百一十八页,2022年,8月28日6.3.1抽象语法树语法制导翻译既可以基于语法分析树也可以基于抽象语法树进行,采用的基本方法是一样的。现在对语法树进行改造,去掉那些对翻译不必要的信息,将语法树进行抽象---抽象语法树。在表达式的抽象语法树中,运算符、关键字不作叶子结点而作为内部结点,叶子结点只是运算量。语法制导翻译以语法树作基础,实际上,语法树可以作为一种合适的中间语言形式。抽象语法树也可以属性化,给结点加上属性变成带属性的抽象语法树。第三十页,共一百一十八页,2022年,8月28日当把语法规则中本质部分抽象出来而将非本质部分去掉后,便得到抽象语法规则。这种去掉不必要信息的做法可以获得高效的源程序中间表示。如赋值语句x=a−b*c的抽象语法树如图6–4(a)所示,而图6–4(b)则是该赋值语句的普通语法树。图6–4x=a−b*c的语法树第三十一页,共一百一十八页,2022年,8月28日
抽象语法树的一个显著特点是结构紧凑,容易构造且结点数较少。图6–4(b)所示的普通语法树的结点为14个;而图6–4(a)所示的抽象语法树的结点仅有7个,且每个内部结点最多只有两个分支,因此可以将每个赋值语句或表达式表示为一棵二叉树。对于含有多元运算的更为复杂的语法成分,相应的抽象语法树则为一棵多叉树,但我们总可以将其转变为一棵二叉树。第三十二页,共一百一十八页,2022年,8月28日为下面文法的句子a-4+c
建立抽象语法树。
EE+T|E-T|TT(E)Tid|num为每个运算量或运算符号都建立一个结点。可以根据表达式的运算顺序自下而上的构造---手工构造。a-+c4抽象语法树运算符作内部结点id(a)EE-TE+TTnum(4)id(c)语法树第三十三页,共一百一十八页,2022年,8月28日抽象语法树的实现抽象语法树中的每一个结点可以由包含几个域的记录来实现;有向边用指针实现。在一个运算量对应的结点(叶结)中,一个域标识运算量,另一个域是该运算量的属性值(或指针)。在一个运算符对应的结点中,一个域标识运算符,其它域包含指向运算分量的结点的指针。运算符通常叫做这个结点的标号。进行翻译时,抽象语法树中的结点可能会用附加域来存放结点的属性值(或指向属性的指针)。numvalid
.op
.
.Toentryofid右子树根结左子树根结第三十四页,共一百一十八页,2022年,8月28日建立表达式的抽象语法树使用的函数,这些函数返回新建立结点的指针。1.mknode(op,left,right)
建立一个运算符结点,标号是op,两个域left和right指向左右运算分量结点的指针。2.mkleaf(id,entry)
建立一个“标识符”叶子结点,由标号id标识,一个域entry指向标识符在符号表中相应的项。3.mkleaf(num,val)
建立一个“数”叶子结点,标号为num,域val用于存放数的值。抽象语法树的构造函数
第三十五页,共一百一十八页,2022年,8月28日调用上述函数建立表达式a-4+c
的抽象语法树。建立顺序自下而上是:
p1,p2,p3,p4,p5抽象语法树构造idtoentryap1num4p2
–p3
idtoentrycp4
+p5第三十六页,共一百一十八页,2022年,8月28日规则语义规则
EE1+TE.nptr:=mknode('+',E1.nptr,T.nptr)EE1-TE.nptr:=mknode('-',E1.nptr,T.nptr)ETE.nptr:=T.nptrT(E)T.nptr:=E.nptrTidT.nptr:=mkleaf(id,id.entry)TnumT.nptr:=mkleaf(num,num.val)建立抽象语法树的语义规则属性文法,nptr是综合属性第三十七页,共一百一十八页,2022年,8月28日手工构造表达式a+b*(c-d)-e/f的抽象语法树,根据表达式的运算顺序自下而上的构造。练习
+a-–dc*b/fe第三十八页,共一百一十八页,2022年,8月28日
逆波兰表示法是波兰逻辑学家卢卡西维奇(Lukasiewicz)发明的一种表示表达式的方法,这种表示法把运算量(操作数)写在前面,把运算符写在后面,因而又称后缀表示法。例如,把a+b写成ab+,把a*(b+c)写成abc+*。逆波兰表示法第三十九页,共一百一十八页,2022年,8月28日
1.表达式的逆波兰表示
表达式E的后缀表示的递归定义如下:
(1)如果E是变量或常数,则E的后缀表示即E自身。
(2)如果E为E1 opE2形式,则它的后缀表示为E1'E2'op;其中op是二元运算符,而E1'、E2'分别又是E1和E2的后缀表示。若op为一元运算符,则视E1和E1'为空。
(3)如果E为(E1)形式,则E1的后缀表示即为E的后缀表示。第四十页,共一百一十八页,2022年,8月28日
上述递归定义的实质是:后缀表示中,操作数出现的顺序与原来一致,而运算符则按运算先后的顺序放入相应的操作数之后(即运算符先后的顺序发生了变化)。这种表示已不再需要用括号来规定运算的顺序了。第四十一页,共一百一十八页,2022年,8月28日写表达式的后缀式要点:1.后缀式中运算量的顺序与中缀式的相同;2.算符出现的次序即表达式的运算次序;3.不使用括号。例:a+bab+a*(b+c)abc+*(a+b)*(c+d)ab+cd+*-a+b*cabc*+a/b**c-d*eabc**/de*-
(A=0∧B≠0)A0=B0≠∧用
表示取负算符(uminus)第四十二页,共一百一十八页,2022年,8月28日练习写出下列各式的逆波兰表示:(1)-a-(b*c/(c-d)+(-b)*a)(2)-A+B*C↑(D/E)/F(3)x:=a-b/(c+d)解:(1)abc*cd-/ba*+-(2)ABCDE/↑*F/+(3)xabcd+/-:=
用
表示取负算符(uminus):=表示赋值算符(assign)第四十三页,共一百一十八页,2022年,8月28日后缀表示法表示表达式,其最大优点是易于计算机处理表达式。常用的算法是使用一个栈,自左至右扫描算术表达式(后缀表示),每扫描到运算对象,就把它推进栈;扫描到运算符,若该运算符是二目的,则对栈顶部的两个运算对象实施该运算,并将运算结果代替这两个运算对象而进栈;若是一目运算符,则对栈顶元素执行该运算,并以运算结果代替该元素进栈,最后的结果留在栈顶。例如
-B+C*D的计算过程:第四十四页,共一百一十八页,2022年,8月28日
-B+C*D的后缀表示为BCD*+:第四十五页,共一百一十八页,2022年,8月28日把表达式翻译为后缀式的
语义规则(属性文法)规则语义规则
E→E1opE2
E.code:=E1.code||E2.code||op
E→(E1)
E.code:=E1.code
E→id
E.code:=id属性E.code:是E的后缀式代码属性op:二元算符‖:后缀式的连接运算算符第四十六页,共一百一十八页,2022年,8月28日
1.三地址代码的形式三地址代码语句的一般形式为
x=yopz
其中,x、y和z为变量、结果或编译时产生的临时变量;op为运算符。
三地址代码的每条语句通常包含三个地址,两个用来存放运算对象,一个用来存放运算结果。6.3.3三地址代码第四十七页,共一百一十八页,2022年,8月28日在实际实现中,用户定义的变量将由指向符号表中该变量项的指针所取代。
由于三地址语句只含有一个运算符,因此多个运算符组成的表达式必须用三地址语句序列来表示,如表达式x+y*z的三地址代码为:
t1=y*zt2=x+t1
其中,t1和t2是编译时产生的临时变量。第四十八页,共一百一十八页,2022年,8月28日例:赋值语句a:=(-b)*(c+d)-(c+d)的三地址码如下所示。t1:=minusbt2:=c+dt3:=t1*t2t4:=c+dt5:=t3-t4a:=t5第四十九页,共一百一十八页,2022年,8月28日
2.三地址代码的具体实现三地址代码是中间代码的一种抽象形式。在编译程序中,三地址代码语言的具体实现通常有三种表示方法:四元式、三元式和间接三元式。
1)四元式四元式是具有四个域的记录结构,这四个域为
(op,arg1,arg2,result)
其中,op为运算符,arg1、arg2及result为指针,它们可指向有关变量在符号表中的登记项或一临时变量(也可空缺)。第五十页,共一百一十八页,2022年,8月28日
常用的三地址语句与相应的四元式对应如下:
x=yopz对应(op,y,z,x)x=−y 对应(-,y,_,x)x=y 对应(=,y,_,x)callP 对应(call,_,_,P)gotoL 对应(j,_,_,L)ifxropygotoL对应(jrop,x,y,L)第五十一页,共一百一十八页,2022年,8月28日例如,赋值语句a=b*(c+d)相应的四元式代码为:①(+,c,d,t1)②(*,b,t1,t2)③(=,t2,_,a)
约定:凡只需一个运算量的算符一律使用arg1;如果op是一个算术或逻辑运算符,则result总是一个新引进的临时变量,它用来存放运算结果。由上例也可看出,四元式出现的顺序与表达式计值的顺序是一致的,四元式之间的联系是通过临时变量实现的。四元式由于其表示更接近程序设计的习惯而成为一种普遍采用的中间代码形式。第五十二页,共一百一十八页,2022年,8月28日
2)三元式三元式是具有三个域的记录结构,这三个域为
(op,arg1,arg2)
其中,op为运算符,arg1、arg2既可指向有关名字在符号表中的登记项或临时变量,也可以指向三元式表中的某一个三元式。
第五十三页,共一百一十八页,2022年,8月28日例如对于表达式:A+B*(C-D)+E/(C-D)^N的三元式代码为:由上例可知,三元式出现的先后顺序和表达式各部分的计值顺序是一致的。第五十四页,共一百一十八页,2022年,8月28日
3)间接三元式在三元式代码表的基础上另设一张表,该表按运算的次序列出相应三元式在三元式表中的位置,这张表称为间接码表。三元式表只记录不同的三元式语句,而间接码表则表示由这些语句组成的运算次序。例如对于表达式:A+B*(C-D)+E/(C-D)^N的三元式与间接码表为:每生成一条指令,先检查已生成的间接三元式序列,若已有,不再生成,只把序号列入间接码表中。第五十五页,共一百一十八页,2022年,8月28日
在三元式表示中,每个语句的位置同时有两个作用:一是可作为该三元式的结果被其它三元式引用;二是三元式位置顺序即为运算顺序。在代码优化阶段,需要调整三元式的运算顺序时会遇到困难,这是因为三元式中的arg1、arg2也可以是指向某些三元式位置的指针,当这些三元式的位置顺序发生变化时,含有指向这些三元式位置指针的相关三元式也需随之改变指针值。因此,变动一张三元式表是很困难的。第五十六页,共一百一十八页,2022年,8月28日
对四元式来说,引用另一语句的结果可以通过引用该语句的result(通常是一个临时变量)来实现,而间接三元式则通过间接码表来描述语句的运算次序。这两种方法都不存在语句位置同时具有两种功能的现象,代码调整时要做的改动只是局部的,因此,当需要对中间代码表进行优化处理时,四元式与间接三元式都比三元式方便得多。第五十七页,共一百一十八页,2022年,8月28日写中间语言:练习写出表达式:A+B*(C-D)-E/F↑G
的逆波兰表示、三元式表示、四元式表示。解:四元式表示:①(-,C,D,T1)②(*,B,T1,T2)③(+,A,T2,T3)④(↑,F,G,T4)⑤(/,E,T4,T5)⑥(-,T3,T5,T6)解:三元式表示:①(-,C,D)②
(*,B,①)③
(+,A,②)④(↑,F,G)⑤(/,E,④)⑥(-,③,⑤)解:逆波兰表示:ABCD-*+EFG↑/-第五十八页,共一百一十八页,2022年,8月28日语法制导翻译
翻译的任务:首先是语义分析和正确性检查,若正确,则翻译成中间代码或目标代码。第五十九页,共一百一十八页,2022年,8月28日语法制导翻译法的基本思想对文法中的每个规则都附加上一个语义动作或语义子程序,在执行语法分析的过程中,每当使用一条规则进行推导或归约时,就执行相应规则的语义动作,从而完成翻译工作。第六十页,共一百一十八页,2022年,8月28日什么是语法制导翻译法在语法分析过程中,随着分析的逐步进展,根据相应文法的每一规则所对应的语义子程序进行翻译的方法(即随语法分析的进展,识别出一个语法结构,就对它的语义进行分析和翻译)。第六十一页,共一百一十八页,2022年,8月28日
语法制导翻译技术分为自底向上语法制导翻译和自顶向下语法制导翻译。第六十二页,共一百一十八页,2022年,8月28日自底向上语法制导翻译自底向上语法制导翻译方法是在自下而上的语法分析过程中逐步实现语义规则的方法。自底向上语法制导翻译的特点:(1)当栈顶形成句柄执行归约时,调用相应的语义动作。(2)语法分析栈和语义分析栈同步操作。第六十三页,共一百一十八页,2022年,8月28日
以LR语法制导翻译为例,说明如何实现语法制导翻译:
1、为文法G的每一个规则设计相应的语义子程序。
2、构造文法G的LR分析表。
3、将原LR语法分析栈扩充,以便存放文法符号对应的语义值。
4、在用某一规则进行归约的同时,调用相应的语义子程序,完成所用规则式相应的语义动作。第六十四页,共一百一十八页,2022年,8月28日中间语言为了使编译程序在逻辑上更为简单明确,特别是为了使目标代码的优化比较容易实现,许多编译程序都采用了某种复杂性介于源程序语言和机器语言之间的中间语言,并且首先把源程序翻译成这种中间语言程序(中间代码)。第六十五页,共一百一十八页,2022年,8月28日常见的中间语言形式逆波兰式三元式四元式第六十六页,共一百一十八页,2022年,8月28日
四元式是一种比较普遍采用的中间代码形式。四元式的四个成分是:算符OP、第一运算量ARG1、第二运算量ARG2以及运算结果RESULT。其中,运算量和运算结果有时指用户自定义的变量,有时指编译程序引进的临时变量。第六十七页,共一百一十八页,2022年,8月28日赋值语句A:=-B*(C+D)的四元式序列:序号OPARG1ARG2RESULT注释(1)
B_T1T1
为临时变量(2)+CDT2T2
为临时变量(3)*T1T2T3T3
为临时变量(4):=T3_A赋值运算表中:“”是为了区别“-”而表示的求负运算符。凡只需一个运算量的算符,使用ARG1。第六十八页,共一百一十八页,2022年,8月28日6.4表达式及赋值语句的翻译
6.4.1简单算术表达式和赋值语句的翻译
简单算术表达式是一种仅含简单变量的算术表达式;简单变量是指普通变量和常数,但不含数组元素及结构引用等复合型数据结构。
简单算术表达式的计值顺序与四元式出现的顺序相同,因此很容易将其翻译成四元式形式。第六十九页,共一百一十八页,2022年,8月28日实现简单算术表达式和赋值语句到四元式的翻译一般采取下列步骤:(1)分析文法的特点。(2)设置一系列语义变量,定义语义过程、语义函数。(3)修改文法,写出每一条规则的语义子程序。(4)扩充LR分析栈,构造LR分析表。第七十页,共一百一十八页,2022年,8月28日
考虑以下文法G[A]:A→i=E E→E+E∣E*E∣−E∣(E)∣i
为了实现由表达式到四元式的翻译,需要给文法加上语义子程序,以便在进行归约的同时执行对应的语义子程序。第七十一页,共一百一十八页,2022年,8月28日
语义子程序所涉及的语义变量、语义过程及函数说明如下:
(1)对非终结符E定义语义变量E.place,即用E.place表示存放E值的变量名在符号表中的入口地址或临时变量名的整数码。
(2)定义语义函数newtemp( ),即每次调用newtemp( )时都将回送一个代表新临时变量的整数码;临时变量名按产生的顺序可设为T1、T2、……。
(3)定义语义过程emit(op,arg1,arg2,result),emit的功能是产生一个四元式并填入四元式表中。第七十二页,共一百一十八页,2022年,8月28日
(4)定义语义函数lookup(),其功能是检查是否出现在符号表中,是则返回在符号表的入口指针,否则返回NULL。
使用上述语义变量、过程和函数,可写出文法G[A]中的每一个规则的语义子程序。
(1) A→i=E{p=lookup();if(p==NULL)error( );elseemit(=,E.place,_,p);}(2) E→E(1)+E(2){E.place=newtemp( );emit(+,E(1).place,E(2).place,E.place);}第七十三页,共一百一十八页,2022年,8月28日(3) E→E(1)*E(2){E.place=newtemp( );
emit(*,E(1).place,E(2).place,E.place);}(4) E→−E(1){E.place=newtemp( );
emit(uminus,E(1).place,_,E.place);}(5) E→(E(1)) {E.place=E(1).place;}(6) E→i
{p=lookup();if(p!=NULL)E.place=p;/*另一种表示为E.place=entry(i)*/elseerror( );}第七十四页,共一百一十八页,2022年,8月28日运算合法性检查利用符号表保存的名字类型类型自动转换填加专用指令临时变量空间的统计了解需求、及时释放表达式翻译中的其他问题第七十五页,共一百一十八页,2022年,8月28日若考虑到变量的类型检查和转换,则第(3)条产生式及其有关语义描述如下:{E.place∶=newtemp;ifE(1).type=intANDE(2).type=intthen
beginemit(*,E(1).place,E(2).place,E.place);
E.type∶=int
endelseifE(1).type=realANDE(2).type=realthen
beginemit(*,E(1).place,E(2).place,E.place);
E.type∶=real
end第七十六页,共一百一十八页,2022年,8月28日elseifE(1).type=intandE(2).type=real
then
begint∶=newtemp;
emit(itr,E(1).Place,_,t);
emit(*,t,E(2).place,E.place)
;
E.type∶=real
end
else
E(1)·type=realandE(2).type=intbegint∶=newtemp;
emit(itr,E(2).Place,_,t)
;
emit(*,t,E(1).place,E.place)
;
E.type∶=real
end;
}
第七十七页,共一百一十八页,2022年,8月28日
6.4.2布尔表达式的翻译
在程序语言中,布尔表达式一般由运算符与运算对象组成。布尔表达式的运算符为布尔运算符,即┐、∧、∨,或为not、and和or(注:C语言中为!、&&和| |),其运算对象为布尔变量,也可为常量或关系表达式。关系表达式的运算对象为算术表达式,其运算符为关系运算符<、<=、==、!=、>=、>等。关系运算符的优先级相同但不得结合,其运算优先级低于任何算术运算符。第七十八页,共一百一十八页,2022年,8月28日布尔表达式文法:
EE^E|EE|E|(E)|iropi|i计算布尔表达式值的两种表示方法:数值表示法真:E.place=1;假:E.place=0;真假出口表示法(作为其它语句的条件改变控制流程)真出口:E.true
假出口:E.false第七十九页,共一百一十八页,2022年,8月28日仿照计算算术表达式的思想,按照布尔表达式的运算顺序,一步一步计算出其真假值。设逻辑值true用1表示,false用0表示,布尔表达式1(0^0)^1的计值过程是:1(0^0)^1=1(0^1)^1=10^1=10=1数值表示法
第八十页,共一百一十八页,2022年,8月28日用数值表示布尔值的翻译方案
E→E1
orE2
{E.place∶=newtemp;
emit(E.Place'∶=′E1.place′or′E2.place)}
E→E1
andE2
{E.place∶=newtemp;
emit(E.place′∶=′E1.place′and′E2.place)}
E→notE1
{E.place∶=newtemp:;
emit(E.place′∶=′not′E1.place)}
E→(E1)
{E.place∶=E1.place}
E→EorE|EandE|notE|(E)|iropi|true|false第八十一页,共一百一十八页,2022年,8月28日
E→i1
relopi2
{E.place∶=newtemp;
emit(′if′i1.placerelopi2.place
′goto′
quad+3);
emit(E.Place′∶=′′0′);
emit(′goto′quad+2);
emit(E.Place′∶=′′1′)}
E→true
{E.place∶=newtemp;
emit(E.Place′∶=′′1′)}
E→false
{E.place∶=newtemp;
emit(E.Place′∶=′′0′)}
第八十二页,共一百一十八页,2022年,8月28日布尔表达式a<b可用等价的条件语句ifa<bthen1else0翻译,翻译成的四元式序列为:(1)if
a<bgoto(4)(2)t:=0(3)goto(5)(4)t:=1(5)…其中用临时变量t存放布尔表达式a<b的值,(5)为后续的四元式序号。第八十三页,共一百一十八页,2022年,8月28日
根据布尔运算的特点实施某种优化,即不必一步一步地计算布尔表达式中所有运算对象的值,而是省略不影响运算结果的运算。例如,在计算A∨B时,若计算出的A值为1,则B值就无需再计算了;因为不管B的结果是什么,A∨B的值都为1。同理,在计算A∧B时若发现A值为0,则B值也无需计算,A∧B的值一定为0。优化求值第八十四页,共一百一十八页,2022年,8月28日图6–5E(1)∨E(2)和E(1)∧E(2)的翻译图(a) E(1)∨E(2);(b) E(1)∧E(2)
考虑表达式E=E(1)∨E(2),若E(1)为真,则立即知道E也为真,因此,E(1)的真出口也就是整个E的真出口;若E(1)为假,则E(2)必须被计值。当然,E(2)的真假出口也就是整个E的真假出口。类似的考虑适用于对E=E(1)∧E(2)的翻译。我们将E(1)∨E(2)和E(1)∧E(2)的翻译用图6–5表示,而对形如┐E(1)的表达式则只需调换E(1)的真假出口就可得到该表达式E的真假出口。如何确定一个表达式的真假出口呢?真假出口表示法第八十五页,共一百一十八页,2022年,8月28日对于a<b的四元式翻译:
ifa<bgotop(j<,a,b,p)gotoq(j,_,_,q)
一般情况,每一个关系表达式对应一真一假两个四元式,其格式也是固定的,即
(jrop,X,Y,0)(j,_,_,0)/*X、Y为关系运算符两侧的变量或值*/
每一个布尔变量a也对应一真一假两个四元式,并且格式是固定的,即
(jnz,a,_,0) /*a为布尔变量*/(j,_,_,0)布尔表达式到四元式的翻译第八十六页,共一百一十八页,2022年,8月28日若将其作为条件表达式,例如:ifa<bthens1elses2(J<,a,b,p)p为s1的第一个代码序号
(j,,,q)q为s2的第一个代码序号第八十七页,共一百一十八页,2022年,8月28日例IF
A∨B<D
THENS1ELSES2E的结构从整体上,E对外只能转向两个目标{E}转向E为假时的目标转向E为真时的目标(1)(jnz,A,_,5)(2)(j,_,_,3)(3)(j<,B,D,5)(4)(j,_,_,p+1)(5)(p)(p+1)(q){S1}(j,_,_,q){S2}{下一语句}IF语句的四元式结构第八十八页,共一百一十八页,2022年,8月28日翻译的困难和解决办法
解决办法
一般地讨论:凡是先有目标应用的出现,后有目标的定义,如何处理?设p、q、r三条四元式均要转向t四元式第八十九页,共一百一十八页,2022年,8月28日p(2)回填
p(_,_,_,0)
..
q(_,_,_,_)q
..
q(_,_,_,p)
..r(_,_,_,_)r
..r(_,_,_,q)
..t(_,_,_,_)0为链尾标志第九十页,共一百一十八页,2022年,8月28日(2)回填
p(_,_,_,0)
..q(_,_,_,p)
..q(_,_,_,t)
..r(_,_,_,q)r
..r(_,_,_,t)
..t(_,_,_,_)qp
p(_,_,_,t)qp第九十一页,共一百一十八页,2022年,8月28日L1((L2(L3(
..L4(
..L5(..Li(..Lj(
..Ln(
LiE.trueLnE.false)))))))))000000
00L5L2L1LjL4L300第九十二页,共一百一十八页,2022年,8月28日什么是“回填”?(1)先产生暂时没有填写目标标号的四元式。(2)对于每一条这样的四元式作适当的记录,即可用“拉链”的方法将这些四元式链接起来(同值合并)。(3)一旦目标标号(即将要转移的目标四元式的地址)被确定下来,再将它“回填”到相应的指令(四元式的第四个区段)中。第九十三页,共一百一十八页,2022年,8月28日
为了处理E.true和E.false这两项语义值,我们需要引入如下的语义变量和函数:
(1) nxq:始终指向下一条将要产生的四元式的地址(序号),其初值为1。每当执行一次emit语句后,nxq自动增1。
(2) merge(p1,p2):把以p1和p2为链首的两条链合并为一条以p2为链首的链(即返回链首值p2)。
(3) Backpatch(p,t):把链首p所链接的每个四元式的第四区段(即result)都改写为地址t。第九十四页,共一百一十八页,2022年,8月28日
merge( )函数如下:
merge(p1,p2){if(p2==0)return(p1);else{p=p2;while(四元式p的第四区段内容不为0)p=四元式p的第四区段内容;把p1填进四元式p的第四区段;
return(p2);}}第九十五页,共一百一十八页,2022年,8月28日Backpatch( )函数如下:
Backpatch(p,t){Q=p;while(Q!=0){q=四元式Q的第四区段内容;把t填进四元式Q的第四区段;
Q=q;}}第九十六页,共一百一十八页,2022年,8月28日
为了便于实现布尔表达式的语法制导翻译,并在扫描到“∧”与“∨”时能及时回填一些已经确定了的待填转移目标,我们将前述文法G[E]改写为下面的文法G‘[E],以利于编制相应的语义子程序:
G'[E]:E→EAE∣EBE∣┐E∣(E)∣i∣iropiEA→E∧EB→E∨
这时,文法G'[E]的每个规则和相应的语义子程序如下:第九十七页,共一百一十八页,2022年,8月28日(1) E→i{E.true=nxq;E.false=nxq+1;
emit(jnz,entry(i),_,0);emit(j,_,_,0);}(2) E→i(1)ropi(2){E.true=nxq;E.false=nxq+1;
emit(jrop,entry(i(1)),entry(i(2)),0);emit(j,_,_,0);}(3) E→(E(1)){E.true=E(1).ture;E.false=E(1).false;}(4) E→┐E(1){E.true=E(1).false;E.false=E(1).ture;}第九十八页,共一百一十八页,2022年,8月28日(5) EA→E(1)∧
{Backpatch(E(1).ture,nxq);EA.false=E(1).false;}(6) E→EAE(2){E.true=E(2).ture;
E.false=merge(EA.false,E(2).false);}(7) EB→E(1)∨
{Backpatch(E(1).false,nxq);EB.ture=E(1).ture;}(8) E→EBE(2){E.false=E(2).false;
E.true=merge(EB.ture,E(2).ture);}第九十九页,共一百一十八页,2022年,8月28日生成的四元式序列[例]将下面语句翻译成四元式:ifc<5
z=x+1elsex=y102105107100:(j<,c,5,?)101:(j,-,-,?)102:(+,x,1,t1)103:(=,t1,-,z)104:(j,-,-,?)105:(=,y,-,t2)107:106:(=,t2,-,z)第一百页,共一百一十八页,2022年,8月28日6.5控制语句的翻译在源程序中,控制语句用于实现程序流程的控制。文法:S→ifEthenS1|ifEthenS1elseS2|whileEdoS1第一百零一页,共一百一十八页,2022年,8月28日
6.5.1条件语句if的翻译
1.条件语句if的代码结构我们按下面的条件语句if的模式进行讨论:
ifEthenS1elseS2
条件语句ifEthenS1elseS2中布尔表达式E的作用仅在于控制对S1和S2的选择,因此可将作为转移条件的布尔式E赋予两种“出口”:一是“真”出口,出向S1;一是“假”出口,出向S2。于是,条件语句可以翻译成如图6–9所示的代码结构。第一百零二页,共一百一十八页,2022年,8月28日E.codeS1.codeE.true:S2.codeE.false:gotoS.next下一语句S.next:E.trueE.false图6-9ifEthenS1elseS2语法制导定义目标代码结构第一百零三页,共一百一十八页,2022年,8月28日
E.true只有在扫描到then时才能知道,而E.false则需要处理过S1之后并且到else时才能明确,需把E.false的值传下去,以便到达相应的else时才进行回填。S1语句执行完就意味着整个if-else语句也已执行完毕,因此,在S1的编码之后应产生一条无条件转移指令,这条转移指令将导致程序控制离开整个if-else语句。但是,在完成S2的翻译之前,这条无条件转移指令的转移目标是不知道的,甚至在翻译完S2之后仍无法确定,这种情形是由语句的嵌套性所引起的。
第一百零四页,共一百一十八页,2022年,8月28日
例如下面的语句:
IfE1
ifE2S1elseS2
elseS3
在S1代码之后的那条无条件转移指令不仅应跨越S2,而且应跨越S3。这也就是说,转移目标的确定和语句所处的环境密切相关。第一百零五页,共一百一十八页,2022年,8月28日
2.条件语句if的文法和语义子程序的设计条件语句if的文法G[S]如下:
G[S]:S→ifES(1)S→ifES(1)elseS(2)
为了在扫描条件语句过程中不失时机地处理和回填有关信息,可将G[S]改写为如下的G'[S]:
G'[S]:(1) S→CS(1)(2) C→ifE(3) S→TpS(2)(4) TP→CS(1)else第一百零六页,共一百一十八页,2022年,8月28日IFETHENS(1)ELSES(2)CTPS规约示例S第一百零七页,共一百一十八页,2022年,8月28日根据程序语言的处理顺序,首先用规则(2)C→ifE进行归约,这时E的真出口即为E所生成四元式序列后的下一个地址。一旦碰到THEN将nxq回填至E的真出口,E的假出口地址则作为待填信息放在C的语义变量C.chain中,即:
C→ifE{Backpatch(E.true,nxq);C.chain=E.false;}
第一百零八页,共一百一十八页,2022年,8月28日接下来用规则(1)S→CS(1)继续向上归约。这时已经处理到S→ifEthenS(1),由于归约时E的真出口已经处理,而E的假出口(即语句S的出口)同时是语句S(1)的出口,但此时语句S的出口地址未定,故将C.chain和S(1).chain一起作为S的待填信息链用函数merge链在一起保留在S的语义值S.chain中,即有
S→CS(1){S.chain=merge(C.chain,S(1).chain)}
如果此时条件语句为不含else的条件句,则在规则(1)、(2)归约为S后即可以用下一个将要产生的四元式地址(即S的出口地址)来回填S的出口地址链(即S.chain);如果此时条件语句为if-then-else形式,则继续用规则(4)TP→CS(1)else归约。第一百零九页,共一百一十八页,2022年,8月28日
用Tp→CS(1)else归约时首先产生S(1)语句序列之后的一个无条件转移四元式(以便跳过S(2),见图6–9的结构框图),该四元式
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《复活(节选)》说课课件 【知识精研】 统编版高中语文选择性必修上册
- 2024至2030年中国渐进多焦点树脂镜片行业投资前景及策略咨询研究报告
- 2024至2030年中国引擎盖行业投资前景及策略咨询研究报告
- 2024至2030年中国全玻推拉门行业投资前景及策略咨询研究报告
- 2024年中国诊断床市场调查研究报告
- 2024至2030年非标工业控制柜项目投资价值分析报告
- 2024至2030年焊接式锥螺纹端直角接头项目投资价值分析报告
- 2024至2030年冻干香葱项目投资价值分析报告
- 2024年狗屋项目可行性研究报告
- 医学教程 常见眼眶内肿瘤的影像诊断分析 课件
- 人物访谈类栏目课件
- 尊师重道-尊敬师长主题班会ppt
- 联名请愿书(通用11篇)
- 上海市各县区乡镇行政村村庄村名居民村民委员会明细
- 项目在线审批监管平台备案承诺函
- 医院食堂管理服务方案
- ABS申报材料清单要求
- 认知行为治疗CBT培训课件
- 农民工工资一金三制制度汇编
- 浅谈我院高压氧舱的建设
- 化工基础简答题
评论
0/150
提交评论