第8章语法制导翻译和中间代码生成_第1页
第8章语法制导翻译和中间代码生成_第2页
第8章语法制导翻译和中间代码生成_第3页
第8章语法制导翻译和中间代码生成_第4页
第8章语法制导翻译和中间代码生成_第5页
已阅读5页,还剩92页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章语法制导翻译

和中间代码生成语法分析的作用是判断一个输入是否为一个句子,并且同时获得该句子的语法结构,即语法树。例如在算术表达式的翻译中,不仅要知道表达式中各个运算的先后次序,即语法结构,而且还要知道该表达式中的各个变量和常量的内存地址或值,要知道计算过程的中间结果所存放的内存地址或值,甚至还要知道其数据类型。这些信息都被称为语义信息,而对语义信息进行相应的分析处理就叫做语义分析。因此,翻译是一个语法分析和语义分析综合在一起进行的过程。在编译程序中使用了这样的一种技术,就是在语法分析的同时进行语义分析的工作,并同步地完成相应语句的翻译。这种技术就称为语法制导翻译。第5章教学内容属性文法的概念;语法制导翻译的概念;常用的中间代码形式;程序设计语言的语法结构的自底向上的语法制导翻译方法。一、属性文法属性文法是在上下文无关文法的基础上为每个文法符号(终结符或非终结符)配备若干个相关的“值”(称为属性)。这些属性代表与文法符号相关的信息,例如它的类型、值、代码序列、符号表内容等等。属性和变量一样,可以进行计算和传递。属性一般分为两类:综合属性和继承属性。简单的说,综合属性用于“自下而上”传递信息,而继承属性用于“自上而下”传递信息。属性加工的过程即是语义处理的过程,对于文法的每一个产生式都配备了一组属性的计算规则,则称为语义规则。在一个属性文法中,对应于每个产生式A都有一套与之相关联的语义规则,每条语义规则的形式为:b:=f(c1,c2,…,ck)

这里f是一个函数,而且或者(1)b是A的一个综合属性并且c1,c2,…ck是产生式右边文法符号的属性;或者(2)b是产生式右边某个文法符号的一个继承属性并且c1,c2,…ck是A或产生式右边任何文法符号的属性。在这两种情况下,属性b依赖于属性c1,c2…,ck。要特别强掉的是:终结符只有综合属性,它由词法分析器提供;非终结符既可以有综合属性也可以有继承属性,文法开始符号的所有继承属性作为属性计算前的初始值。一般来讲,对出现在产生式右边的继承属性和出现在产生式左边的综合属性都必须提供一个计算规则,属性计算规则中只能使用相应产生式的文法符号的属性,这有利于产生式范围内“封装”属性的依赖性。然而,出现在产生式左边的继承属性和出现在产生式右边的综合属性不由所给的产生式的属性计算规则进行计算,它们由其它产生式的属性规则计算,由属性计算器的参数提供。语义规则所描述的工作可以包括属性计算、静态语义检查、符号表操作、代码生成等。语义规则可能产生副作用(如产生代码),也可能不是变元的严格函数(如某个规则给出可用的下一个数据单元的地址)。这样的语义规则通常写成过程调用,或过程段。综合属性在语法树中,一个结点的综合属性的值由其子结点的属性值确定。因此,通常使用自底向上的方法在每一个结点处使用语义规则计算综合属性的值。仅仅使用综合属性的属性文法称S—属性文法。继承属性在语法树中,一个结点的继承属性由此结点的父结点和/或兄弟结点的某些属性确定。用继承属性来表示程序语言结构中的上下文依赖关系很方便。属性文法的定义【定义】一个属性文法AG是一个四元组,即AG=(G,A,R,B),其中⑴G=(N,T,S,P)是一个前后文无关文法;⑵A=∪XN∪TA(X)是一个属性的有限集合;⑶R=∪pPR(p)是一个语义规则式的有限集合;⑷B=∪pPB(p)是一个条件的有限集合;属性文法的定义并且满足以下两个条件:1.对任意两个符号的X和Y,若X≠Y,则A(X)∩A(Y)=;2.对于任何在L(G)的句子所对应的语法树上出现的符号X,X的任意一个属性X.a的计算,至多只有一条语义规则式可以应用。

属性文法示例【例5.1】简单台式计算器的算术表达式的属性文法:产生式集G:语义规则式集R:LE{print(E.val)}EE1+T{E.val=E1.val+T.val}ET{E.val=T.val}TT1

*F{T.val=T1.val×F.val}TF{T.val=F.val}F(E){F.val=E.val}Fdigit{F.val=digit.lexval}

示例

在该描述中,每个非终结符都有一个属性:一个整数值的称作val的属性。按照语义规则对每个产生式来说,它的左部E,T,F的属性值的计算来自它右部的非终结符,这种属性称作综合属性。单词digit仅有综合属性,它的值是由词法分析程序提供的。和产生式LE相联的语义规则是一个过程,打印由E产生的表达式的值。我们可以理解为L的属性是空的或是虚的。设表达式为3*5+4,则语义动作打印数值19LE.val=19E.val=15T.val=4T.val=15F.val=4T.val=3F.val=3F.val=5digit.lexval=4digit.lexval=5digit.lexval=3+*3*5+4的带注释的分析树LR分析器可以改造为一个翻译器,在对输入串进行语法分析的同时对属性进行计算。LR分析器增加语义栈。#X1…XmI0I1…Im

语义栈

-X1.VAL…Xm

.VALSLR(1)分析表digit+*()#ETF0s5s41231s6acc2r2s7r2r23r4r4r4r44s5s48235r6r6r6r66s5s4937s5s4108s6s119r1s7r1r110r3r3r3r311r5r5r5r5状态ACTIONGOTO步骤分析栈栈顶状态,输入符剩余输入串10#-Action[0,2]=S5移进22+3*5#205#2--Action[5,+]=r6用F→digit归约,执行语义动作F.val=2+3*5#303#F-2Action[3,+]=r4用T→F归约,执行语义动作T.val=F.val+3*5#LR分析:增加语义栈归约时进行语义动作。给出2+3*5的分析和计值过程。步骤分析栈栈顶状态,输入符剩余输入串402#T-2Action[2,+]=r2用E→T归约,执行语义动作E.val=T.val+3*5#501#E-2Action[1,+]=S6移进++3*5#6016#E+-2-Action[6,3]=S5移进33*5#70165#E+3-2--Action[5,*]=r6用F→digit归约,执行语义动作F.val=3

*5#80163

#E+F-2-3Action[3,*]=r4用T→F归约,执行语义动作T.val=F.val

*5#90169#E+T-2-3Action[9,*]=S7移进*

*5#1001697#E+T*-2-3-Action[7,5]=S5移进5

5#11016975#E+T*5-2-3--Action[5,#]=r6用F→digit归约,执行语义动作F.val=5#120169710#E+T*F-2-3-5Action[10,#]=r3用T→T*F归约,执行语义动作T.val=T.val×F.val=3×5=15

#130169#E+T-2-15Action[9,#]=r1用E→E+T归约,执行语义动作E.val=E.val+T.val=2+15=17

#1401#E-17Action[1,#]=acc成功接收输入串#二.语义分析的任务

编译程序的任务是把源程序翻译成目标程序,这个目标程序必须和源程序的语义等同,也就是说,尽管它们的语法结构完全不同,但它们所表达的结果应完全相同。通常,在词法分析程序和语法分析程序对源程序的语法结构进行分析之后,要么由语法分析程序直接调用相应的语义子程序进行语义处理,要么首先生成语法树或该结构的某种表示,再进行语义处理。编译中的语义处理是指两个功能:审查每个语法结构的静态语义,即验证语法结构合法的程序是否真正有意义。有时把这个工作称为静态语义分析或静态审查。如果静态语义正确,语义处理则要执行真正的翻译,即,要么生成程序的一种中间表示形式(中间代码),要么生成实际的目标代码。

通常包括:类型检查。控制流检查。控制流语句必须使控制转移到合法的地方。例如,在C语言中break语句使控制跳离包括该语句的最小while、for或switch语句。如果不存在包括它的语句,则报错。一致性检查。在很多场合要求对象只能被定义一次。例如Pascal语言规定同一标识符在一个分程序中只能被说明一次,同一case语句的标号不能相同,枚举类型的元素不能重复出现等等。相关名字检查。有时,同一名字必须出现两次或多次。例如,Ada语言程序中,循环或程序块可以有一个名字,出现在这些结构的开头和结尾,编译程序必须检查这两个地方用的名字是相同的。名字的作用域分析。三、中间代码翻译为中间语言的好处:(1)便于进行与机器无关的代码优化;(2)使编译程序改变目标机更容易;(3)使编译程序的结构在逻辑上更为简单明确,以中间语言为界面,编译前端和后端的接口更清晰。编译程序所使用的中间代码有多种形式。常见的有逆波兰式、三元式和树形、四元式表示。1.逆波兰式逆波兰式是最简单的一种中间代码表示形式,早在编译程序出现之前,它就用于表示算术表达式,是波兰逻辑学家卢卡西维奇发明的。这种表示法将运算对象写在前面,把运算符号写在后面,比如把a+b写成ab+,把a*b写成ab*,用这种表示法表示的表达式也称做后缀式。示例中缀算术表达式逆波兰式a+bab+a+b*cabc*+(a+b)*cab+c*a=b*c+d*eabc*de*+=a=b*(c+b)*d/eabcb+*d*e/=后缀表示法表示表达式,其最大的优点是易于计算机处理表达式。常用的算法是使用一个栈,自左至右扫描算术表达式(后缀表示),每扫描到运算对象,就把它推进栈;扫描到运算符,若该运算符是二目的,则对栈顶部的两个运算对象实施该运算,并将运算结果代替这两个运算对象而进栈;若是一目运算符,则对栈顶元素执行该运算,并以运算结果代替该元素进栈,最后的结果留在栈顶。由于后缀式表示上的简洁和计值的方便,特别适用于解释执行的程序设计语言的中间表示,也方便具有堆栈体系的计算机的目标代码生成。2.三元式和树形表示另一类中间代码形式是三元式。把表达式及各种语句表示成一组三元式。每个三元式由三个部分组成,分别是:(算符op,第一运算对象ARG1,第二运算对象ARG2)运算对象可能是源程序中的变量,也可能是某个三元式的结果,用三元式的编号表示。对于一目算符op,只需选用一个运算对象,不妨规定只用ARG1。至于多目算符,可用若干个相继的三元式表示。

示例【例如】a=b*c+d*e的三元式表示为:

(1)(*,b,c)

(2)(*,d,e)

(3)(+,(1),(2))

(4)(=,(3),a)树形表示树形表示是三元式表示的翻版。bc**+ade=3.四元式四元式是一种比较普遍采用的中间代码形式。四元式的四个组成成分是:算符op第一运算对象ARG1第二运算对象ARG2运算结果RESULT运算对象和运算结果有时指用户自己定义的变量,有时指编译程序引进的临时变量。示例四元式形式:

(op,arg1,arg2,result)【例如】a=b*c+d*e的四元式为:

(1)(*,b,c,T1)

(2)(*,d,e,T2)

(3)(+,

T1,T2,

T3)

(4)(=,T3,-,

a)四元式表示很类似于三地址指令,确实,有时把这类中间表示称为“三地址代码”,因为这种表示可看作一种虚拟三地址机的通用汇编码,即这种虚拟机的每条“指令”包含操作符和三个地址,两个是为运算对象的,一个是为结果的。这种表示对于代码优化和目标代码生成都较有利。示例为了更直观,也把四元式的形式写成简单赋值形式或更易理解的形式:result=arg1oparg2【例如】把上述四元式序列写成:(1)t1=b*c(2)t2=b*d

(3)t3=t1+t2

(4)a=t3(jump,-,-,L)写成gotoL(jrop,B,C,L)写成ifBropCgotoL例如:A+B*(C-D)+E/(C-D)^N四、简单赋值语句的翻译语义过程GEN表示产生一个四元式,并且填入四元式表中。语义过程Newtemp表示生成一个临时变量,每调用一次,生成一新的临时变量。语义变量E.place,表示存放E值的变量名在符号表的登录项或一整数码(若此变量是一个临时变量)。语义变量Entry(id)回送标识符id在符号表中的入口地址。简单赋值语句的属性文法产生式语义规则

S→id=E{GEN(=,E.Place,_,Entry(id))}E→E1+E2{E.place=Newtemp;GEN(+,E1.place,E2.place,E.place)}E→E1*E2{E.place=Newtemp;GEN(*,E1.place,E2.place,E.Place)}E→-E1{E.place=Newtemp;GEN(@,E1.place,_,E.place)}E→(E1){E.place=E1.place}E→id{E.place=Entry(id)}a=b*c+d*e的翻译过程a=b*c+d*e

E(c)EE(b)E(d)E(e)T11.(*,b,c,T1)ET22.(*,d,e,T2)ET33.(+,T1,T2,T3)S4.(=,T3,_,a)(1)(*,b,c,T1)

(2)(*,d,e,T2)

(3)(+,T1,T2,T3)

(4)(=,T3,_,a)a=b*c+d*e的翻译结果a=-b*(c+d)的翻译过程略。类型转换的语义处理实际上,在一个表达式中可能会出现各种不同类型的变量或常数。即编译程序还应执行这样的语义动作:对表达式中的运算对象应进行类型检查,如不能接受不同类型的运算对象的混合运算,则应指出错误;如能接受混合运算,则应进行类型转换的语义处理。例如,算术表达式可以进行实型量与整型量的混合运算,并且约定,当两个不同类型的量进行运算时,必须首先将整型量转换为实型量。语义变量语义变量E.type表示E的类型信息;为区别整型加(乘)和实型加(乘),把+(*)分别写作+i(*

i)和+r(*

r)。用一目算符itr表示将整型运算对象转换成实型。示例产生式:E→E1*E2语义动作:{E.place=Newtemp;

ifE1.type=intANDE2.type=int{

GEN(*i,E1.place,E2.place,E.place,);

E.type=int}

elseifE1.type=realANDE2.type=real{

GEN(*r,E1.place,E2.place,E.place);

E.type=real}示例elseifE1.type=int/*andE2.type=real*/{t=Newtemp;

GEN(itr,E1.place,_,t);

GEN(*r,t,E2.place,E.place,)

E.type=real}

else/*E1·type=realandE2.type=int*/

{t=Newtemp;

GEN(itr,E2.place,_,t);

GEN(*r,E1.place,t,E.place,) E.type=real}}五、布尔表达式的翻译程序设计语言中的布尔表达式有两个作用。一是计算逻辑值,二是用做改变控制流语句中的条件表达式,如在if-then,if-then-else,或是while-do语句中那样。布尔表达式是由布尔算符and,or和not施于布尔变量或关系表达式而成。即布尔表达式的形式为E1ropE2,其中E1和E2都是算术表达式,rop是关系符,如<=,<,=,〉=,≠等等。只考虑简单的布尔表达式文法:

E→EandE|EorE|notE|idropid|id并且按通常习惯,约定布尔算符的优先顺序(从高到低)为not、and、or,并且and和or服从左结合。布尔表达式的翻译方法通常,计算布尔表达式的值有两种办法,第一种办法,如同计算算术表达式一样,计算出各部分的真假值,最后计算出整个表达式的值。例如,用数值1表示true,用0表示false。那么布尔表达式1or(not0and0)or0的计算过程是:

1or(not0and0)or0

=1or(1and0)or0

=1or0or0

=1or0

=1布尔值的翻译方案E→E1orE2{E.place=Newtemp;GEN(or,E1.place,E2.place,E.place)}E→E1andE2{E.place=Newtemp;

GEN(and,E1.place,E2.place,E.place)}E→notE1{E.place=Newtemp;

GEN(not,E1.place,_,E.place)}E→(E1){E.place=E1.place}E→id1ropid2{E.place=Newtemp;GEN(jrop,Entry(id1),Entry(id2),E.place)}E→id{E.place=Entry(id)}另一种计算方法是采取某种优化措施,只计算部分表达式:AorB,若A的值为1,则无需计算B的值,因为不管B的值为何结果,AorB的值都为1。AandB,若A的值为0,则无需计算B的值,因为不管B的值为何结果,AandB的值都为0。控制语句中布尔表达式的翻译条件控制语句ifEthenS1elseS2的目标代码结构如下:E的代码S1的代码S2的代码真假“真”出口与“假”出口布尔表达式E的作用仅在于控制对S1和S2的选择,而无须最终保留E值,故作为转移条件的布尔表达式E,可赋予它两种出口:“真”出口E.TC→S1“假”出口E.FC→S2作为转移条件的布尔表达式E,可翻译为如下的四元式序列:(jnz,A,_,p)表示ifAgotop(jrop,A1,A2,p)表示ifA1ropA2gotop(j,_,_,p)表示gotop示例【例如】条件语句ifAorB<CthenS1elseS2

可翻译为:(1)(jnz,A,_,(5))(2)(j,_,_,(3))(3)(j<,B,C,(5))(4)(j,_,_,(p+1))(5)关于S1的四元式序列……(p)(j,_,_,(q))(p+1)关于S2的四元式序列……(q)如何确定一个表达式的真假出口考虑E为E1orE2的形式:若E1为真,则E为真,所以E1的真出口就是整个E的真出口。若E1为假,则必须计算E2,E2代码的第一个四元式标号就是E1的假出口,而E2的真出口和假出口就是整个E的真出口和假出口。如何确定一个表达式的真假出口考虑E为E1andE2的形式:若E1为假,则E为假,所以E1的假出口就是整个E的假出口。若E1为真,则必须计算E2,E2代码的第一个四元式标号就是E1的真出口,而E2的真出口和假出口就是整个E的真出口和假出口。考虑E为notE1的形式:只需调换E1的真假出口即可得到E的真假出口。拉链为了记录需回填地址的四元式,常采用一种“拉链”的办法。把需回填E.TC的四元式拉成一条链子,把需回填E.FC的四元式拉成一条链子,分别称做"真"链和"假"链。示例若有四元式序列:(10)…gotoE.TC…(20)…gotoE.TC…(30)…gotoE.TC则把需回填E.TC的四元式(10),(20)和(30)链成:(10)…goto(0)…(20)…goto(10)…(30)…goto(20)把地址(30)称作链首,0为链尾标志,即地址(10)为链尾。自底向上分析中的一种翻译方案E→E1orE2(1)EA→E1or{Backpatch(E1.

FC,NXQ);EA.TC

=E1.TC}(1’)E→EAE2{E

.FC

=E2.FC;

E

.

TC=Merg(EA.

TC,E1.TC)}【说明】Backpatch(p,t):将p链接的四元式的第四元都回填t;Merg(p1,p2):将以p1和p2为链首的两条链合并为一条链,返回时的函数值作为合并后的链首。E→E1andE2(2)EB→E1and{Backpatch(E1

.

TC,NXQ);EB.

FC

=E1.FC}(2’)E→EBE2{E

.

TC

=E2.TC;

E

.

FC=Merg(EB.FC,E1.FC)}(3)E→notE1{E.TC=E1.FC;E.FC=E1.TC}(4)E→(E1){E.TC=E1.TC; E.FC=E1.FC}(5)E→id1ropid2{E.TC=NXQ; E.FC=NXQ+1; GEN(jrop,Entry(id1),Entry(id2),0); GEN(j,_,_,0)}(6)E→id{E.TC=NXQ;E.FC=NXQ+1; GEN(jnz,Entry(id),_,0); GEN(j,_,_,0)}六、控制语句的翻译考虑ifthen,ifthenelse和whiledo语句的翻译。G[S]:S→ifEthenS

|ifEthenSelseS

|whileEdoS

|beginLend

|A

L→L;S

|S其中各非终结符号的意义是:S--语句

L--语句串A--赋值句E--布尔表达式1.条件语句if的翻译考虑if语句的翻译。产生式S→ifEthenS

|ifEthenSelseS

两个问题布尔式E的"真、"假"出口尚待回填E.TC,E.FC。ifEthenS1elseS2elseS3在翻译完S2之后,S1后的无条件转移目标仍无法确定,因为它不仅要跨越S2还应跨越S3。即转移目标的确定和语句所处的环境密切相关。故也可象处理布尔表达式一样,让非终结符S(和L)含有一项语义值S.CHAIN(和L.CHAIN)。也是一条链,它把所有那些四元式串在一起,这些四元式期待在翻译完S(L)之后回填转移目标。真正的回填工作将在处理S的外层环境的某一适当时候完成。单分支条件语句的目标结构ifEthenS1的目标代码结构如下:E的代码S1的代码E.TCE.FCS1.CHAINS.CHAIN多分支条件语句的目标结构ifEthenS1elseS2的目标代码结构如下:E的代码S1的代码S2的代码E.TCE.FCjmp0S1.CHAINS2.CHAINS.CHAIN翻译方案ifEthenS1

C→ifEthen{Backpatch(E

.

TC,NXQ);C.CHAIN=E.FC}S→CS1{S

.

CHAIN=Merg(C.CHAIN,S1.CHAIN)}ifEthenS1elseS2C→ifEthen{Backpatch(E

.

TC,NXQ);C.CHAIN=E.FC}TP→CS1else{q=NXQ;GEN(j,_,_,0);Backpatch(C.CHAIN,NXQ);TP

.

CHAIN=Merg(S1.CHAIN,q)}S→TPS2{S

.

CHAIN=Merg(TP.CHAIN,S2.CHAIN)}2.循环语句的翻译whileEdoS1的目标代码结构如下:E的代码S1的代码E.TCE.FCS1.CHAINS.CHAINjmphead翻译方案W→while{W

.quad=NXQ}Wd→WEdo{Backpatch(E

.

TC,NXQ); Wd.CHAIN=E.FC; Wd.quad=W.quad}S→WdS1{Backpatch(S1.CHAIN,

Wd.quad);GEN(j,_,_,Wd.quad);S

.

CHAIN=Wd.quad}第一个四元式的地址示例while(A<B)doif(C<D)thenX=Y+Z将被翻译成如下的一串四元式:

100(j<,A,B,102)

101(j,_,_,107)

102(j<,C,D,104)

103(j,_,_,100)

104(+,Y,Z,T1)

105(=,T1,_,X)

106(j,_,_,100)1073.for循环语句fori=E1stepE2untilE3doS1为了简单起见,假定E2总是正的。在这种假定下,上述循环句的意义等价于:

i=E1;

gotoOVER;

AGAIN:i=i+E2;

OVER:ifi≤E3then

beginS1;gotoAGAINend;七、简单说明语句的翻译程序设计语言中的说明语句旨在定义各种形式的有名实体,如常量、变量、数组、记录(结构)、过程、子程序等等,说明语句的种类也多,对象说明、变量说明、类型说明、过程说明等等。编译程序把说明语句中定义的名字和性质登记在符号表中,用以检查名字的引用和说明是否一致。许多说明语句的翻译并不生成相应的目标代码。过程说明和动态数组的说明有相应的代码。简单说明句的文法程序设计语言中最简单的说明句的语法描述为:D→integer〈namelist〉|real〈namelist〉〈namelise〉→〈namelist〉,id|id即使用关键字integer和real定义一串名字的性质。对这种说明句的翻译是指在符号表中登录该名和性质。翻译方案语义变量D.type:用以记录说明句所引入的名字的性质(int还是real);语义过程enter(id,A):把名字id和类型A登录在符号表中。(1)D→integerid{enter(id,int);

D.type=int}

(2)D→realid{enter(id,real);

D.type=real}

(3)D→D1,id{enter(id,D1.type);

D.type=D1.type}八、数组元素访问的翻译讨论包括数组元素的表达式和赋值语句的翻译问题。一个数组是由同一类型数据所组成的某种n维矩形结构。沿着每一维的距离称为一个下标。每维的下标只能在该维的上、下限之内变动。数组的每个元素是矩形结构中的一个点,它的位置可通过给出每维的下标来确定。数组的存储数组的存储表示有多种形式,最简单的一种是把整个数组按行(或按列)存放在一片连续存储区中。例如,若A是一个2×3的二维数组,每个元素占一个单元,那么,所谓按行和按列的存储方式分别如图所示。有些程序语言,如FORTRAN,要求以列为序存放数组;另一些,如PL/l,要求以行为序;还有一些则取决于编译程序设计者的意愿。数组按列存放A[1,1]A[1,2]A[1,3]A[2,1]A[2,2]A[2,3]数组按行存放A[1,1]A[2,1]A[1,2]A[2,2]A[1,3]A[2,3]计算数组元素的地址数组元素的地址计算和数组的存储方式密切相关。在以行为序的情形下,如何计算数组元素的地址。例如,假定A是一个10×20的二维数组,各维的下限为1,每个元素占用一个机器字(令存储器是以字编址的),数组的首地址,即A[l,1]的地址为a,那么,数组元素A[i,j]的地址为a十(i—1)×20十(j—l)或等价地表示成(a-21)+(20×i+j)以行为序计算数组元素的地址设A是一个ALGOLn维数组arrayA[l1:u1,l2:u2,…,ln:un]令di=ui一1i十1,i=1,2,…,n,a为数组的首地址,即A[l1,l2,…,ln]的地址。元素A[i1,i2,…,in]的地址D为:D=a+(i1-l1)d2d3…dn+(i2-l2)d3d4…dn+…+(in-1-ln-1)dn+(in-ln)经因子分解后得D=CONSPART+VARPART其中CONSPART=a-CC=(…((l1d2+l2)d3+13)d4+…+ln-1)dn+lnVARPART=(…((i1d2+i2)d3+i3)d4+…+in-1)dn+in数组元素引用的中间代码1.将产生两组计算数组元素地址的四元式。一组计算VARPART,并将它放在某个临时单元T中;另一组计算CONSPART,并把它放在另一个临时单元T1中。同时用Ti[T]表示数组元素的地址。数组元素引用的中间代码2.对应“数组元素引用”(引用其值)和“对数组元素赋值”有两个相应的四元式:“变址取数”和“变址存数”。“变址取数”的四元式是:(=[],T1[T],_,X)即X=T1[T]“变址存数”的四元式是:([]=,X1,_,T1[T])即T1[T]=X赋值语句中数组元素的翻译含数组元素的赋值语句的文法为:A→V=EV→i[<elist>]|i<elist>→<elist>,E|EE→E+E|(E)|V为了产生计算VARPART的四元式序列,需要如下的语义变量和过程:elist.ARRAY:表示数组名在符号表中的位置,即数组名的符号表入口。elist.DIM:下标的个数(数组维数)计数器。elist.PLACE:记存业已形成的VARPART的中间结果单元名字在符号表中的位置,或是一个临时变量的整数码。LIMIT(ARRAY,k):这是一个函数过程,它给出数组ARRAY的第k维长度dk。其中,ARRAY是数组名在符号表中的位置。

说明每个变量V有两项语义值(属性),V.PLACE和V.OFFSET。若V是一个简单变量名i,则V.PLACE就是指此名的符号表入口,而V.OFFSET将为null。若V是一个下标变量名i,则V.PLACE就是指保存CONSPART的临时变量名的整数码,而V.OFFSET则指保存VARPART的临时变量名的整数码。注意,null是一个特殊值,它是区分简单变量名和下标变量名的标志。翻译方案(1)A→V=E{IF(V.OFFSET=null)THEN/*V是一个简单变量名*/GEN(=,E.PLACE,_,V.PLACE)ELSEGEN([]=,E.PLACE,_,V.PLACE[V.OFFSET])}此处,若V是一个下标变量,那么,我们产生“变址存数”的四元式。(2)E→E1+E2{T=Newtemp;GEN(+,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论