slide1
Download
Skip this Video
Download Presentation
第七章 语义分析和中间代码产生

Loading in 2 Seconds...

play fullscreen
1 / 82

第七章 语义分析和中间代码产生 - PowerPoint PPT Presentation


  • 102 Views
  • Uploaded on

第七章 语义分析和中间代码产生. 7.1 中间代码. 7.1.1 逆波兰表示法. 逆波兰表示法是波兰逻辑家 Lukasiewicz 发明的一种表示表达式的方法。该方法是将运算量写在前面,算符写在后面,用这种方法表示的表达式称为后缀式,如 a+b 可写成 ab+。 一般而言,若 θ 是 K 目算符,它对后缀式 e 1 ,e 2 ,...,e k 作用的结果将被表示为 e 1 e 2 ... e k θ。.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' 第七章 语义分析和中间代码产生' - kelton


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
7 1 1

7.1.1 逆波兰表示法

逆波兰表示法是波兰逻辑家Lukasiewicz发明的一种表示表达式的方法。该方法是将运算量写在前面,算符写在后面,用这种方法表示的表达式称为后缀式,如a+b可写成ab+。一般而言,若θ是K目算符,它对后缀式e1 ,e2 ,...,ek作用的结果将被表示为e1 e2... ekθ。

slide4
后缀式的计算:一个后缀式的计算过程是使用一个栈,然后自左向右扫描后缀式,每遇到运算量就将它推进栈中,每遇到K目运算符就把它作用于栈顶部的K个项,并用运算结果来替代这K个项。可以看出表达式的后缀式表示法对进行表达式的计算而言具有很强的方便性。后缀式的计算:一个后缀式的计算过程是使用一个栈,然后自左向右扫描后缀式,每遇到运算量就将它推进栈中,每遇到K目运算符就把它作用于栈顶部的K个项,并用运算结果来替代这K个项。可以看出表达式的后缀式表示法对进行表达式的计算而言具有很强的方便性。
slide5
后缀式的推广:后缀式这种表示法可以比较方便的推广到其它的描述地方,例如对条件算术表达式 if e then x else y (含义为,若e=0,此式为y,否则等于x)而言,可以将if-then-else看成一个三目算符@,则该条件表达式的后缀式可写为[email protected],考虑一下该表示法的缺点,和解决该缺点的方法。
slide6
语法制导生成后缀式:

E→E(1)+ E(2)

{ E.CODE := E(1).CODE|| E(2).CODE }

E→(E(1))

{ E.CODE := E(1).CODE}

E→i {E.CODE:=i}

7 1 2
7.1.2三元式和树
  • 三元式的形式为:(OP ,ARG1,ARG2);OP是运算符,ARG1、ARG2分别为第一运算量和第二运算量。表达式A+B*C可以表示为:

(1)(*,B,C)

(2)(+,A,(1))

  • 其中三元式(2)是表达式A+B*C的最终代表,三元式(2)中的(1)指第一个三元式的结果。
slide8
为产生中间代码,现定义几个相关的函数,这些函数主要是为使用符号表和保存中间代码而定义的。为产生中间代码,现定义几个相关的函数,这些函数主要是为使用符号表和保存中间代码而定义的。
  • LOOKUP(NAME):对NAME查找符号表。若此名出现在表中,则将其表项位置(入口)作为LOOKUP的值;否则,LOOKUP取值为null。
  • FILLSYM(NAME):在符号表中开辟一新项,项目名为NAME,把此项的入口作为FILLSYM的值。
slide9
TRIP(OP,ARG1,ARG2):产生一个新三元式(OP,ARG1,ARG2),该过程将新的三元式放入三元式代码区中,并返回在三元式表中的位置。TRIP(OP,ARG1,ARG2):产生一个新三元式(OP,ARG1,ARG2),该过程将新的三元式放入三元式代码区中,并返回在三元式表中的位置。
  • ERTRY(i):对i所代表的标识符查找符号表以获得它在表中的位置
slide10

通常的表达式翻译成三元式的语义动作如下:

(1)E→E(1)op E(2)

{ E.VAL:=TRIP(op,E(1).VAL,E(2).VAL }

(2)E→-E(1)

{ E.VAL := TRIP(@,E(1).VAL,-) }

(3)E→(E(1))

{E.VAL:= E(1).VAL}

slide11
(4)E→i {E.VAL:=ENTRY(i)}

试将下面语句序列给出其相应的三元式代码序列:

X:=(A+B)*C;

Y:=D↑(A+B);

slide12
三元式序列如下:

<1>(+,A,B)

<2>(*,①,C)

<3>(:=,X,②)

<4> (+,A ,B)

<5> (↑,D,④)

<6> (:=,Y,⑤)

slide13
可以看出,该三元式序列是可以优化的,但是优化过程需要调整运算顺序,例如三元式(4)与(1)是相同的,可去掉(4),此时三元式(5)和(6)都需要修改,为解决三元式优化修改的困难,可辅助以一张间接码表加以解决,这种表示法称为间接三元式。可以看出,该三元式序列是可以优化的,但是优化过程需要调整运算顺序,例如三元式(4)与(1)是相同的,可去掉(4),此时三元式(5)和(6)都需要修改,为解决三元式优化修改的困难,可辅助以一张间接码表加以解决,这种表示法称为间接三元式。
slide14
<1>(+,A,B)

<2>(*,①,C)

<3>(:=,X,②)

<4> (↑,D,①)

<5> (:=,Y,④)

  • 间接码表:①②③①④⑤
7 1 3
7.1.3 四元式
  • 四元式有四个部分:(OP,ARG1,ARG2,RESULT)OP一般代表确定运算符的整数码,ARG1,ARG2,RESULT或者是一个指向符号表的某一个入口的指示器,或者是一个代表临时变量的整数码,
slide16

临时变量的表达可有两种处理方法:

(1)将临时变量与用户自定义的变量同等看待添入符号表中,在四元式的运算量或运算结果位置通过指向符号表该临时变量的入口指示器来使用该临时变量。

(2)使用某种整数编码来表示临时变量,这样就不用将其放入符号表中。请思考一下三元式与四元式在中间代码优化时哪种代码形式便于优化?以下各节有关翻译的讨论均基于四元式代码的翻译

slide17
7.2 简单算术表达式和赋值语句到四元式的翻译

增加几个翻译中需要的几个函数:NEWTEMP:是一个函数过程,每次调用回送一个代表新临时变量名的整数码。以下我们用Ti表示相应产生的临时变量。

ENTRY(i):定义同前 。

gen op arg1 arg2 result op arg1 arg2 result e place e e
GEN(OP,ARG1,ARG2,RESULT):将四元式(OP,ARG1,ARG2,RESULT)填入四元式表中,并将添入的位置返回。GEN(OP,ARG1,ARG2,RESULT):将四元式(OP,ARG1,ARG2,RESULT)填入四元式表中,并将添入的位置返回。

E.PLACE :它是和非终结符E相联系的语义变量,表示存放E值的变量在符号表的入口或整数码(若此变量是一个临时变量)。

slide20
翻译算法由如下的语义动作描述:(1) A→i:=E

{GEN(:=,E.PLACE, ,ENTRY(i))}(2) E→E(1)+ E(2)

{E.PLACE:=NEWTEMP;GEN(+,E(1).PLACE,E(2).PLACE,E.PLACE)} (3) E→E(1)* E(2) {E.PLACE:=NEWTEMP;GEN (*,E(1).PLACE,E(2).PLACE,E.PLACE)}

slide21
(4) E→(E(1))

{ E.PLACE:= E(1).PLACE}

(5) E→-E(1)

{ E.PLACE:=NEWTEMP; GEN(@,E(1).PLACE,,E.PLACE)}

(6) E→i {E.PLACE:=ENTRY(i)}

slide22
7.3 布尔表达式的翻译
  • 布尔表达式的翻译可以仿照算术表达式的翻译过程,但是布尔表达式的运算有自己的特点,即布尔表达式的运算结果可能只需要运算一部分即可得到,这就是短路运算。对于布尔表达式这种计算方法可以用条件语句的形式加以表示。
slide23
例如:A∨B 对应于 if A then true else BA∧B 对应于 if A then B else false╗A 对应于 if A then false else trueA∨(B∧(╗C∨D))可按此法翻译成:if A then true else if B then if C then D else true else false
slide24
布尔表达式E一般出现在控制语句中,我们不需要保留布尔表达式的结果,而只需要在计算E的代码中,当发现E的结果为真时(称为E的真出口)就转向到某个地方,或发现为假时(称为E的假出口)跳转到另一个地方继续执行控制体中相应的计算。可以看出这种想法也可以运用到布尔表达式E的计算过程中。布尔表达式E一般出现在控制语句中,我们不需要保留布尔表达式的结果,而只需要在计算E的代码中,当发现E的结果为真时(称为E的真出口)就转向到某个地方,或发现为假时(称为E的假出口)跳转到另一个地方继续执行控制体中相应的计算。可以看出这种想法也可以运用到布尔表达式E的计算过程中。
slide25
例如对于产生式E→E1∨E2 ,E1为真,则E必为真,E1的真出口为E的真出口,E1为假则E的真假出口取决于E2的真假出口,此时E1的假出口应转向到E2的相应四元式代码的第一条四元式。现定义几种转移四元式: (jnz,A, ,P) A为真(非0)时转到第P条四元式。 (jrop,A1,A2,P)A1ropA2成立时,转到第P条四元式。 (j,,,P)无条件转向到第P条四元式。
slide26
例7.1:if A∨B〈C then S1 ELSE S2可翻译成如下四元式序列:1.(jnz,A,,5)2.(j,,,3)3.(j<,B,C,5)4.(j,,,P+1).....S1的代码序列p. (j,,,q)P+1....S2的代码序列q.
slide27
为描述布尔表达式的翻译现定义:

NXQ:表示将要产生的下一条四元式的地址。

GEN(op,arg1,arg2,result):同前面的描述,GEN每调用一次NXQ增加1。

slide28
MERGE(P1,P2):P1,P2两条链和并,返回链头P2。MERGE(P1,P2):P1,P2两条链和并,返回链头P2。

BACKPATCH(P,T):将四元式序号T填入以P为链头的四元式链中的所有四元式的RESULT域。

slide29
设布尔表达式的文法为:

E→E∨E|E∧E|╗E|(E)|i|i rop i

为便于构造语义子程序将该文法加以改造:

E→E∨E2

{E.FC:= E2 .FC; E.TC:=MERGE(E∨.TC,E2.TC)}

E→E∧E2 {E.TC:= E2 .TC; E.FC:=MERGE(E∧.FC,E2.FC)}

slide30
E→╗E1

{E.TC:= E1 .FC; E.FC:= E1.TC}

E→(E1)

{E.TC:= E1 .TC; E.FC:= E1.FC}

E→i

{E.TC:=NXQ; E.FC:=NXQ+1; GEN(jnz,ENTRY(i), ,0);GEN(j,,,0);}

slide31
E→i1 rop i2

{E.TC:=NXQ; E.FC:=NXQ+1; GEN(jrop,ENTRY(i1), ENTRY(i2 ),0); GEN(j,,,0);}

E∨→E1∨

{BACKPATCH(E1.FC,NXQ); E∨.TC:= E1.TC}

slide32
E∧→E1∧

{BACKPATCH(E1.TC,NXQ); E∧.TC:= E1.TC}

slide33
7.4 控制语句的翻译

7.4.1 语句标号与GOTO语句的翻译

  • 标号在程序中分定义性出现和使用性出现。

标号定义性出现的形式为:L:S; L是标号,S是语句。

标号使用性出现的形式为:GOTO L;

标号可先定义后使用,也可先使用后定义。

slide34
GOTO语句可翻译成无条件转移四元式,其转移地址为GOTO语句中出现的标号的定义性出现位置。GOTO语句可翻译成无条件转移四元式,其转移地址为GOTO语句中出现的标号的定义性出现位置。
  • 为在翻译中记载标号定义的位置以供翻译GOTO语句时使用,应将标号和其定义位置存放入符号表中。翻译GOTO语句时应该用标号查找符号表得到定义位置,若没有查到该标号,应该将标号存入符号表中注明为未定义并将使用该标号的四元式的地址记载下来。
  • 问题: 多个GOTO语句使用该标号,而该标号还没定义,那么这多个GOTO语句的地址该如何保留。
name type def addr l i i i def addr nxq
符号表的形式为:

NAME TYPE......DEF ADDR

设标号定义性出现的文法描述为:

L->i:

使用该产生式归约时的动作为:

若i不在符号表中则将i添入符号表并将DEF填为未定义。地址栏ADDR填写NXQ的值。

slide36
若i已在符号表中但类型不为标号或虽然是标号但DEF栏填的是已定义,则程序语义有错误. 若i已在符号表中但类型不为标号或虽然是标号但DEF栏填的是已定义,则程序语义有错误.

若i已在符号表中但DEF栏填的是未定义,则将DEF栏改为已定义,ADDR栏填NXQ,并执行BACKPATCH(q,NXQ).q为ADDR栏原保留的需要回填同一转移地址的四元式链的链头.

slide37
设GOTO 语句的产生式为S-> GOTO L;
  • 语义动作为:

{ lookup:=LOOKUP(L);

if lookup=null then

begin

ENTRY(L);

ENTER.TYPE:=L;

ENTER.DEF:=0;

ENTER.ADDR:=NXQ;

GEN(j,,,0);

END

slide38
ELSE if lookup.def=1

then

GEN(j,,,lookup.ADDR) else

begin

n:=NXQ;

GEN(j,,,lookup.ADDR)

lookup.ADDR:=n;

end

  • 如果考虑标号的作用域,语义动作应该如何表示?
7 4 2
7.4.2用于组织控制流程的一些语句的翻译
  • 考虑if语句,while语句和复合语句的翻译.
slide40
一般使用如下的文法描述这些语句:

G[S]:

1.S-> if E then S|if E then S else S

|while E do S|begin L end|A

L->L;S|S

各非终结符的含义是:S--语句, L--语句串,

A--赋值句,E--布尔表达式

slide41
为方便翻译现将文法改造如下:

S->C S1

{S.CHAIN:=MERGE(C.CHAIN, S1.CHAIN)}

S-> TP S2

{S.CHAIN:=MERGE(TP .CHAIN, S2 .CHAIN)}

S-> WD S3

{BACKPATCH(S3 .CHAIN, WD .QUAD)

GEN(J,,, WD .QUAD );S.CHAIN:= WD .CHAIN }

slide42
S->begin L end

{S.CHAIN:=L.CHAIN}

S->A

{S.CHAIN:=0}

L-> LS S1

{L.CHAIN:= S1.CHAIN}

L->S

{L.CHAIN:=S.CHAIN}

slide43
C-> if E then {BACKPATCH(E.TC,NXQ);C.CHAIN:=E.FC}

TP->C S1 ELSE {q:=NXQ;GEN(J,,,0);BACKPATCH(C.CHAIN,NXQ);TP .CHAIN=MERGE(S1.CHAIN,q) }

W->while {W.QUAD:=NXQ}

WD->W E do

{BACKPATCH(E.TC,NXQ);WD .CHAIN:=E.FC;

WD .QUAD:=W.QUAD}

LS ->L; {BACKPATCH(L.CHAIN,NXQ)}

slide44
例7.2: 按上述语义动作将语句

while (A<B) do

if (C<D) then X:=Y+Z

翻译成四元式序列。

7 4 3
7.4.3循环语句的翻译
  • 循环语句的文法描述形式为:

S->for i:= E1 step E2 until E3 do S1

slide46
假定步长为正,则上述循环语句等价与下列程序片段:假定步长为正,则上述循环语句等价与下列程序片段:

i:= E1;

gotoOVER;

AGAIN:i:=i+ E2 ;

OVER :if i<= E3 then

begin S1 ; goto AGIAN end;

按循环语句的这个实现模型进行翻译,我们改写文法并写出各产生式相应的语义动作如下:

slide47
F1 ->for i:=E1

{GEN(:=,E1 .PLACE, ,ENTRY(i));

F1 .PLACE:=ENTRY(i);

F1 .CHAIN:=NXQ;

GEN(j,,,0);

F1 .QUAD:=NXQ;}

slide48
F2 ->F1 step E2

{ F2 .QUAD:= F1 .QUAD; F2 .PLACE:= F1 .PLACE;

GEN(‘+’,F1 .PLACE, E2 .PLACE ,F1 .PLACE);

BACKPATCH(F1 .CHAIN,NXQ);}

slide49
F3 -> F2 until E3

{ F3.QUAD:= F2 .QUAD;

q:=NXQ;

GEN(j<=, F2 .PLACE, E3 .PLACE ,q+2);

F3.CHAIN:=NXQ

GEN(j,,,0)}

slide50
S-> F3 do S1

{GEN(j, , , F3.QUAD); BACKPATCH(S1 .CHAIN, F3.QUAD); S.CHAIN:= F3.CHAIN}

slide51
7.5数组元素的引用
  • 假定每个数组元素只占用一个机器字并以行为序存放,目标机器以字编址,数组说明的形式为:array A [l1:u1,l2:u2,...,ln:un]。
  • A数组的某个元素的引用为A[i1,i2,..., in]。
slide52
令di = ui- li+1则,i=1,2,...,n。则该元素的存储地址为:D=CONSPART+VARPART,其中:CONSPART=a-C,a为数组存储区域的起点地址,即A[l1,l2,...,ln]的地址。
  • C=(...(( l1d2 +l2)d3+l3)d4+...+ln-1)dn +ln
  • VARPART==(...(( i1d2 +i2)d3+i3)d4+...+in-1)dn +in
slide53
CONSPART部分在数组说明时就可得到,我们可以在数组说明时将其算出并放入符号表中。CONSPART部分在数组说明时就可得到,我们可以在数组说明时将其算出并放入符号表中。
  • VARPART部分则需要程序运行时才可算出,也就是我们在翻译时要翻译出执行计算VARPART的代码。

我们可以将VARPART的结果放入某个临时变量T中而CONSPART的结果放入另一个临时变量T1中,同时用T1[T]表示数组元素的地址。对应“数组元素引用”和“对数组元素赋值”有两个相应的四元式:

slide54
“变址取数”->(=[], T1[T], ,X) /*相当于X:= T1[T]*/;
  • “变址存数”->([]=,X, ,T1[T]) /*相当于T1[T]:=X */。
slide55
设描述赋值语句中数组元素使用的文法如下:

A->V:=E

V->i[elist]|i

elist->elist,E|E

E->E+E|(E)|V

为便于翻译,文法改写:

V->elist]|i elist->elist,E|i[E

slide56
定义几个语义变量与过程:

elist.ARRAY:保留数组在符号表中的位置.

eLIST.PLACE:保留已形成的VARPART的中间结果单元位置.

elist.DIM:数组维数计数器.

LIMIT(ARRAY,K):从符号表中取数组ARRAY的第K维的长度.ARRAY为符号表中的某个地址.

slide57
V.PLACE, V.OFFSET:

若V为简单变量名i归约所的,则V.PLACE存放i在符号表中的地址, V.OFFSET则为NULL; 若V为下标变量名,则V.PLACE存放保存CONSPART的临时变量名的整数码,V.OFFSET则指存放保存VARPART的临时变量名的地址.

slide58
A->V:=E

{if (V.OFFSET=NULL)

then GEN(:=,E.PLACE, ,V.PLACE)

ELSE GEN([]=,E.PLACE,V.PLACE[V.OFFSET])}

E->E1+E2 {T:=NEWTEMP;GEN(+,E1.PLACE,E2.PLACE,T);E.PLACE:=T}

slide59
E->( E1 ) {E.PLACE:= E1.PLACE}

E->V

{if (V.OFFSET=NULL) then E.PLACE:=V.PLACE

else begin T:=NEWTEMP; GEN(=[],V.PLACE[V.OFFSET], ,T) E.PLACE:=T; end }

slide60

V->elist]

{ T:=NEWTEMP; GEN(-,elist.ARRAY,C,T);

V.PLACE:=T;

V.OFFSET:=elist.PLACE

} /*假设通过elist.ARRAY可以获得相应数组的常数C*/

V->i {V.OFFSET:=NULL; V.PLACE:=ENTRY(i)}

slide61
elist->elist1,E

{T:=NEWTEMP; K:=elist.DIM+1; dk:=LIMIT(elist1.ARRAY,K); GEN(*,elist1.PLACE,dk,T) GEN(+,E.PLACE,T,T); elist.ARRAY:=elist1.ARRAY; elist.PLACE:=T; elist.DIM:=K }

elist->i[E

{elist.PLACE:=E.PLACE; elist.DIM:=1;

elist.ARRAY:=ENTRY(i); }

slide62
7.6过程调用
  • 设过程调用形实参结合采用传地址法

如果实在参数是一个变量或数组元素,就直接传递它的地址;

如果实参是其它表达式,那么就将它的值计算出来并防在某个临时变量T中,然后传送T的地址,所有实在参数的地址应存放在被调用的子程序能够取得到的地方。

slide63
被调用的子程序中,每一个形式参数都有一个单元(称为形式单元)用来存放相应的实在参数的地址,在子程序中对形式参数的任何引用都当作是对形式单元的间接访问。当通过转子指令进入子程序后,子程序的第一步工作就是把实在参数的地址取到对应的形式单元中,然后再开始执行本段中的语句。被调用的子程序中,每一个形式参数都有一个单元(称为形式单元)用来存放相应的实在参数的地址,在子程序中对形式参数的任何引用都当作是对形式单元的间接访问。当通过转子指令进入子程序后,子程序的第一步工作就是把实在参数的地址取到对应的形式单元中,然后再开始执行本段中的语句。
slide64
设过程调用CALL S(A+B,Z)将被翻译成:

计算A+B置于T的代码 /*T:=A+B */

par T /*第一个实参地址 */

par Z /*第二个实参地址 */

call S /*转子指令 */

  • 按上述关于过程调用的目标结构的模式,考虑过程调用的翻译。
slide65
描述过程调用语句的文法和相应语义子程序如下:描述过程调用语句的文法和相应语义子程序如下:

S->call (arglist)

{for 队列arglist.QUEUE的每一项p DO

GEN(par, , ,p);

GEN(call, , ,ENTRY(i))}

slide66
arglist->arglist1 ,E

{把E.PLACE排在arglist1.QUEUE的末端;

arglist.QUEUE:=arglist1.QUEUE}

arglist->E

{建立一个arglist.QUEUE,它只包含一项E.PLACE}

ad