590 likes | 719 Views
第八章 语法制导翻译法. 语法制导翻译概述. 语法制导翻译是继词法分析和语法分析后 , 对编译程序进行语义分析和翻译 ( 产生中间代码 ) 。 在语法分析过程中 , 随着分析的步步进展 , 根据每个产生式所对应的语义子程序 ( 或语义规则描述的语义动作 ) 进行翻译的办法称为语法制导翻译。. 本章学习的主要内容. 本章我们将主要讨论语法制导翻译的基本思想及其在在中间代码生成中的应用。主要包括:属性文法、各种常见中间语言形式、赋值语句的翻译,布尔表达式的翻译,控制语句的翻译、说明语句的翻译等。通过本章的学习,要求掌握语法制导翻译的基本思想和翻译方案。. 语义处理程序的两个功能.
E N D
语法制导翻译概述 语法制导翻译是继词法分析和语法分析后,对编译程序进行语义分析和翻译(产生中间代码)。 在语法分析过程中,随着分析的步步进展,根据每个产生式所对应的语义子程序(或语义规则描述的语义动作)进行翻译的办法称为语法制导翻译。
本章学习的主要内容 • 本章我们将主要讨论语法制导翻译的基本思想及其在在中间代码生成中的应用。主要包括:属性文法、各种常见中间语言形式、赋值语句的翻译,布尔表达式的翻译,控制语句的翻译、说明语句的翻译等。通过本章的学习,要求掌握语法制导翻译的基本思想和翻译方案。
语义处理程序的两个功能 第一,审查每个语法结构的静态语义,即检查语法结构合法的程序是否真正有意义。也称静态语义分析。(类型检查、控制流的检查、一致性检查、相关名字的检查) 第二,如果静态语义正确,语义处理则要执行真正的翻译,要么生成中间代码,要么生成实际的目标代码。(说明性语句:填符号表;可执行性语句:生成中间代码)
属性文法 • 属性文法主要用来描述和说明程序设计语言语义的。 • 属性:一般用来描述客观存在的事物或人的特征,编译技术中用属性来描述计算机要处理的对象的特征。 • 属性文法:每个上下文无关文法的产生式,配以相关联的属性,属性的处理过程也就是语义的处理过程,为文法的每条规则配以相应的语义规则构成的文法称为属性文法。
属性文法 • 属性文法的形式定义: • AG: AG=(G,V,E) • G:上下文无关文法; • V:属性的有穷集合;每一个属性与某一个文法符号相关联;用文法符号.属性表示 • E:表示属性的断言或谓词的有穷集合(语义规则);每一个断言与文法的某个产生式相关联) • 属性:综合属性(自下而上)、继承属性(自上
语法制导翻译的基本思想 • 对文法中的每个产生式都附加一个语义动作或语义子程序,在执行语法分析的过程中,每当使用一条产生式进行推导或规约时,就执行相应产生式的语义动作。 • 这些动作不仅指明了该产生式所生成的符号串的意义,而且还根据这种意义规定了对应的加工动作.(如查填各类表格、改变编译程序的某些变量之值、打印各种错误信息以及生成中间代码等)从而完成预定的工作。
算术表达式求值 • 语法分析分析的语法成分是简单算术表达式,所完成的语义的处理不是将它翻译成中间代码或目标代码,而是计算表达式的值。
算术表达式的属性文法 • 简单算术表达式求值的语义描述。 • 产生式 语义规则 • (0) L→ E Print(E.val) • (1)E→E1+T E.val=E1.val+T.val • (2)E→T E.val= T.val • (3)T→T1*F T.val=T1.val * F.val • (4)T→ F T.val=F.val • (5)F→ (E) F.val=E.val • (6)F→digit F.val=digit.lexval(0~9)
翻译步骤 • 首先,为文法的每条规则设计相应的语义子程序; • 增加一个语义信息栈; • 在语法分析的同时做语义处理:即在用某一个产生式进行规约的同时,调用相应的语义子程序完成所用规则的语义动作,并将每次动作后的值保存在语义栈中
G[E]: 1 E→E+T 2 E→T 3 T→T*F 4 T→F 5 F→(E) 6 F→i i+i*i
注意 • 句柄归约在先,语义动作调用在后 • 归约时,栈内句柄各符号的语义信息与句柄及同时出栈,整个句柄所表示的语义信息则赋给相应产生式左部非终结符号的语义变量,并让该非终结符号及语义信息同时入栈
中间代码 • 中间代码是介于源程序和目标程序之间,用来表述源程序并与之等效的一种编码方式. • 生成中间代码的作用 • (1)中间代码与具体机器无关,便于语法分析和语义加工,便于编译程序移植. • (2)易于对中间语言进行优化,有利于提高中间代码的质量. • (3)有利于编译程序的设计与实现,使编译各阶段的开发复杂性降低. • 常用的中间代码形式:逆波兰式、三元式、四元式、树代码等
逆波兰表示法(后缀式) 特点:运算符直接写在其运算对象之后。 • 不再有括号 • 运算对象出现的次序未变 • 求值过程简单,宜于用栈实现 • 例: a+b ab+ a*b+c ab*c+ a*(b+c/d) abcd/+* a*b+c*d ab*cd*+ a:=b*c+d*e abc*de*+:=
三元式 一般形式: (编号)〈运算符〉〈运算对象1〉〈运算对象2〉 i op arg1 arg2 • 出现的次序是对应语法成分的计算次序 • 无需引进临时工作变量,用三元式的编号代替之,节省了存贮空间 • 其中的编号代表某个三元式的位置编号和代表某个三元式的值 • 之间的联系靠编号进行,因此,三元式的移动较困难,不便于优化
表达式a+(-b)*c的三元式 • (1) (@,b,_);单目运算,运算对象2为空 • (2) (*,(1),c) • (3) (+,a,(2))
X=a+b*c Y=d-b*c 三元式表 (1)(*,b,c) (2)(+,a,(1)) (3)(=,x,(2)) (4)(_,d,(1)) (5)(=,y,(4)) 三元式
四元式(三地址代码) 一般形式: 〈运算符〉〈运算对象1〉〈运算对象2〉〈结果〉 (op , arg1, arg2 , result) 例: X=a*b+c*d的四元式序列 三地址代码 (1)(* ,a,b,T1) (1)T1=a*b (2)(*, c,d,T2) (2)T2=c*d (3)(+,T1,T2,T3) (3)T3=T1+T2 (4)(=,T3,_,X) (4)X=T3
属性翻译文法的应用 综合属性与自下而上定值 每个非终结符的属性值都是根据位于其下面那些符号的属性值来确定的,即按一种自下而上的方式来确定的。 继承属性和自上而下定值 非终结符的属性值或者根据其上层非终结符的属性来确定或者根据产生式右部其它符号的属性来确定。这种属性值是根据自上而下方式确定的。
自底向上的语法制导翻译 • 自底向上的语法制导翻译方法是在自底向上的语法分析过程中逐步实现语义规则的翻译方法。在实现时注意以下几点: (1)自底向上的翻译的特点,栈的操作,对产生式的要求等 (2)各种程序语句的目标结构 (3)从源结构到目标结构的变换方法(包括对产生式的改造等)
算术表达式和赋值语句的翻译 • 翻译步骤: • (1)分析文法的特点; • (2)设计一系列的语义变量、定义语义过程、语义函数; • (3)设计每一条规则的语义子程序; • (4)增加一个语义信息栈,构造LR分析表; • (5)语法分析同时语义处理.
例: 有文法G[A]: • 1. A→i:=E • 2. E →E+T∣T • 3. T →T*F∣F • 4.F →-P • 5.P →i ∣(E) 简单算术表达式的计值顺序与四元式出现的顺序相同,因此目标代码的顺序(结构)为: (1)先生成E的代码(一系列顺序执行的四元式) (2)把E的值赋给变量i(表达式的结果赋给赋值语句的左变量)
引入语义变量,语义过程和语义函数 • (1)E.place:表示存放E值的变量名或在符号表中的入口地址; • (2)函数newtemp():申请一个临时单元,存放中间结果; • (3)过程emit(T=arg1 op arg2):产生一条四元式,并添入四元式表中; • (4)lookup(i.name):审查i.name是否出现在符号表中,在:返回地址,不在:返回NULL; • (5)函数entry(i):回送标识符i在符号表中的入口地址.
表达式的语义动作设计 • 1. A→i:=E{emit(entry(i)=E.place} • 2. E →E1+T{E.place=newtemp(), emit(E.place)=E1.place+T.place} • 3.E →T {E.place=newtemp(), emit(E.place=T.place)} • 4. T →T1*F{T.place=newtemp(), emit(T.place)=T1.place*F.place} • 5. T →F {T.place=newtemp(), emit(T.place=F.place)}
6.F →_P {F.place=newtemp(), emit(F.place)=@P.place} • 7.P →(E) {P.place=newtemp(), emit(P.place=E.place)} • 8.P →i{P.place=newtemp(), emit(P.place=Entry(i))}
K+1: T1:=c-d K+2: T2=b*T1 K+3: T3:=a+T2 K+4: X:=T3 (-,c,d,T1) (*,b,T1,T2) (+,a,T2,T3) (:=,T3,_,X) 例如:表达式X:=a+b*(c-d)的翻译
布尔表达式的翻译 • 布尔表达式 是由布尔运算符(and,or,not)作用于布尔变量(或常数)或关系表达式而形成的。 • 布尔表达式的作用: • 用作控制语句中的条件:if-then、 while、repeat等 • 逻辑赋值句中的逻辑运算
布尔表达式到四元式的翻译 • 文法:EEEEEE(E)idE rop E 其中,(and)、(or)、(not)为布尔(逻辑)运算符;rop为关系运算符(>,≥,,≤,=,≠);id为布尔(逻辑)变量或布尔(逻辑)常数;E rop E为关系表达式。 • 布尔表达式的求值方法: ① 通过逐步计算出各部分的值来计算整个表达式。 ② 利用布尔运算符的性质计算其值
布尔表达式作为控制语句中的条件式 • 作用是用于选择下一个执行点 • while E do S1 • E的作用是选择S1执行,还是跳过S1语句,执行S1后面的语句 • E有两个出口: • 真:S1语句 • 假:S1后面的语句
whille W.head 假 E的代码 真 do S1的代码 JMP W.head While语句的目标结构
布尔初等量(布尔变量和关系表达式)的目标结构布尔初等量(布尔变量和关系表达式)的目标结构 • 由两个转移四元式构成,一个表示真出口Li,另一个表示假出口Lj,设E是一布尔变量, 它的目标结构为: • (1) if E goto Li (jumpt , E,_, Li ) • (jnz,A,_, Li) • (2) if E1 rop E2 goto Li (jumpθ ,E1 ,E2 ,Li) (jrop,E1,E2, Li) 例:(j<,a,b,p) • (3) goto Lj (jump Lj ) (j,_,_, Lj )
if a<b goto Li (真出口) goto Lj (假出口) Li:S的第一个四元式 … S的最后一个四元式 Lj:S 后面的语句 (1) if a<b goto (3) (2) goto (5) (3)T1:=x+y (4)A:=T1 (5) 举例:if a<b then A:=x+y的四元式序列
多因子组成的布尔表达式的翻译 • 例: if a<b ∨c then S1 else S2 • 分析结果如下: • 当a<b为真,执行S1,不需要计算c的值 • 当a<b为假,计算c的值,c为真:执行S1,为假:执行S2 • 当a<b与c分别为真时,程序转向一致,真出口相同(S1) • 当a<b与c分别为假时,程序转向一致,假出口相同(S2)
(1) if a<b goto S1的第一条语句 (5) (2) goto (3) (3) if c goto S1的第一条语句 (5) (4) goto S2的第一条语句 ( p+1 (5) 关于S1的四元式序列 … (p) Goto q (p+1)关于S2的四元式序列 … (q)后继四元式 目标结构 if a<b ∨c then S1 elseS2的四元式序列 E的代码 T F S1的代码 JUMP S S2的代码 S(后继语句)
if E then S1 else S2的四元式序列 • (1) if E goto (3) • (2) goto (S2的第一个四元式) (p+1) • (3)关于S1的四元式序列 • …… • (p) goto r (执行完S1后转出) • (p+1)关于S2的四元式序列 • …… • (r) 后继四元式
while E do S1的四元式序列 • (1) if E goto (3) • (2)goto ( S1后面的语句) (p+1) • (3)关于S1的四元式序列 • …… • (p) goto (1) • (p+1) 后继四元式
真出口链、假出口链、回填(Backparch) • 在多个因子组成的布尔表达式中,可能有多个因子的真出口或假出口的转移去向相同,但又不能立刻知道具体转向位置。 • 把转移方向相同的四元式链在一起,一旦发现具体转移目标,就回填。 • 真出口用Etrue来表示。 • 假出口用Efalse来表示。 • 回填Backparch(g,t)将t填入由g所指向的四元式的结果部分
if a<b ∨c then S1 elseS2的真假出口回填描述 (1) if a<b goto 0 /E的真出口Etrue有待回填 (2) goto (3) /*回填E的第一个假出口 (3) if c goto (1)/* (3)是E的真出口链 (4) goto 0 /*E的假出口Efalse有待回填 (5) 关于S1的四元式序列/*此时回填真出口Etrue … (p) goto 0 /*有待回填q的位置 (p+1)关于S2的四元式序列/*此时回填假出口Efalse … (q)后继四元式/*此时回填q
语法制导翻译中使用的公共变量、过程和函数 • guad:四元式指针 (表示四元式的编号或地址) • GEN(X):生成一条四元式,把它放到四元式表的尾部,(和前面介绍的emit功能相同) • Nextguad:指向下一个即将形成的四元式的编号,其初值为1,执行一次GEN(X),Nextguad自动加1; • merg(p1,p2)函数将以 p1,p2为链首的两个四元式合并为一,返回合并后的链首
条件语句的翻译 基本思路: • ① 先对定义它的文法进行改造,以便能在相应处添加上语义子程序; • ② 根据它的语义,设计出相应语义子程序的动作。
if语句的文法 • S→ if E then S1 • S→ if E then S1 else S2 (E是转移条件) • 对if E then S1 else S2,其Etrue直到扫描到then,产生S1的第一个四元式序号时,才能将该标号作为E的真链填入到Etrue,而Efalse 直到扫描到else,产生S2的第一个四元式序号时,才得到Efalse的值。
(1) C→ if E then (2) T→ C S1 else (3) S →T S2 (4) S → C S1 目标结构如右图 If E.true E.false E的代码 then C.chain S1.chain S1的代码 JUMP 0 T.chain else S2.chain S2的代码 S.chain if语句文法的改造
if语句的语义动作 • (1) C→ if E then • {backpatch(Etrue,Nextguad),C.chain=Efalse} 当用产生式if E then 进行归约时,生成了条件E的代码,E的假出口不能回填,通过非终结符C向后传递,所以引入C.chain链.
if语句的语义动作 • (2) T→ C S1 else • {q:=Nextguad,emit(goto 0) • Backpatch(C.chain,Nextguad), • T.chain=merg(S1.chain,q)} • 当用产生式T→ C S1 else 归约时,S1的代码已经形成,回填E的 假出口即C.chain。此时S2的代码尚未形成,S1的转出链和JMP转向相同,合并为一,通过T向后传递.
if语句的语义动作 • (3)S →T S2 • {S.chain:=merg(T.chain,S2.chain)} • (4) S → C S1 • {S.chain:=merg(C.chain,S1.chain)
采用then 与else的最近匹配原则(三地址指令) (1)if A goto (3) (2)goto 0 (13) (3)if B goto (5) (4)goto (2) (13) (5) if C>D goto (7) (6)goto (4) (13) (7)if A<B goto (9) (8)goto 0 (11) (9)F:=1 (10) goto 0 (15) (11) F:=0 (12) goto (10) (15) (13) T:=G+1 (14) G:=T (15) If A∧B ∧C>D then if A<B then F:=1 else F:=0 else G:=G+1
四元式形式代码 (1)(jnz,A,_,(3)) (2)(j,_,_,0) (13) (3)(jnz,B,_,(5) (4)(j,_,_,(2)) (13) (5)(j>,C,D,(7)) (6)(j,_,_,(4)) (13) (7)(j<,A,B,(9)) (8)(j,_,_,0) (11) (9)(:=,1,_,F) (10)(j,_,_,0) (15) (11)(:=,0,_,F) (12)(j,_,_,(10)) (15) (13)(+,G,1,T) (14)(:=,T,_,G) (15) If A∧B ∧C>D then if A<B then F:=1 else F:=0 else G:=G+1