第8章 语法制导翻译与中间代码生成
8.1 属性文法 语法分析后的源程序==>语义处理 静态语义是对程序约束的描述,这些约束无法通过抽象语法规则来妥善地描述,实质上就是语法规则的良形式条件,它可以分为类型规则和作用域/可见性规则两大类 动态语义 程序单位描述的计算 编译程序的语义处理工作 1 静态语义审查,即验证语法结构合法的程序是否有意义 2 生成中间代码
静态语义审查 (1)类型检查。根据类型相容性要求,验证程序中执行的每个操作是否遵守语言的类型系统的过程,编译程序必须报告不符合类型系统的信息。 (2)控制流检查。控制流语句必须使控制转移到合法的地方。例如,在C语言中break语句使控制跳离包括该语句的最小while、for或switch语句。如果不存在包括它的这样的语句,则就报错。
(3)一致性检查。在很多场合要求对象只能被定义一次。例如Pascal语言规定同一标识符在一个分程序中只能被说明一次,同一case语句的标号不能相同,枚举类型的元素不能重复出现等等。 (4)上下文相关性检查。比如,变量名字必须先声明后引用; (5)名字的作用域分析 解释执行动态语义 (计算)生成代码(中间代码或目标代码)
E → T1+T2 | T1 or T2 T → num|true|false 对输入串 2+6 语法树如图: 例:有文法G[E]: E E{T1.t=T2.t} {T1.t=int} T T T{T2.t=int} + T + 2 6 2 6
类型检查的属性文法: E → T1+T2 {T1.t=int AND T2.t=int} E → T1 or T2 {T1.t=bool AND T2.t=bool} T → num {T.t:=int} T → true {T.t:=bool} T → false {T.t:=bool}
属性文法A(attribute grammar)是一个三元组:A=(G,V,F),其中 G:是一个上下文无关文法, 属性文法,语法制导翻译 属性文法A(attribute grammar)是一个三元组:A=(G,V,F),其中 G:是一个上下文无关文法, V:有穷的属性集,每个属性与文法的一终结符或非终结符相连, F:关于属性的属性断言或谓词集.每个断言与一个产生式相联.而此断言只引用该产生式左端或右端的终结符或非终结符相联的属性
例如:定义表达式的文法如下: EE+E E(E) En 给出定义表达式值的属性文法。 我们为文法符号E引进属性符号val,用E.val表示E的值,属性计算规则以赋值语句的形式给出,附在每个产生式后,并用大括号括起来。为了明确E的不同出现位置,用上角标区别。终结符n的值是词法分析程序提供的,这里用n.lex表示。下面给出属性文法: EE1+E2 {E.val := E1.val +E2.val} E(E1) {E.val := E1.val } En {E.val := n.lex} 属性文法的主要思想有两点: 首先对于每个文法符号引进相关的属性符号; 其次对于每个产生式写出属性值计算的规则。
属性文法:允许为每个终结符和非终结符配备一些属性的文法.它既能描述程序设计语言的语法,又为其语义描述提供了手段. 属性文法由D.E.Knuth于1968年引进.后来才被用于编译程序的设计。 属性有不同的类型,可以象变量一样地被赋值。赋值规则附加于语法规则之上。赋值与语法同时进行,赋值过程就是语义处理过程。在推导语法树的时候,诸属性的值被计算并通过赋值规则层层传递。有的从语法规则左边向右边传,有的从右边向左边传。语法推导树最后完成时,就得到开始符号的属性值。也就是整个程序的语义.
例如定义表达式值的属性文法, E.val是一个综合属性的例子: 属性分为两种:继承属性和综合属性. inherited and synthesized(derived)attribute 继承属性的计算规则由顶向下, 综合属性的计算规则由底向上. 例如定义表达式值的属性文法, E.val是一个综合属性的例子: EE1+E2 {E.val := E1.val +E2.val} E(E1) {E.val := E1.val } En {E.val := n.lex} 考虑句子2+(3+1)的求值顺序,将2+(3+1)的语法树结点改为有附加属性的结点(这样的树称为带注释的语法树): E.val=6 E.val=2 + E.val=4 n.lex=2 ( E.val=4 ) E.val=3 + E.val=1 n.lex=3 n.lex=1
例8.1 一个简单台式计算器的定义 综合属性val 产 生 式 语 义 规 则 Print(E.val) E E1+T E T 例8.1 一个简单台式计算器的定义 综合属性val 产 生 式 语 义 规 则 L E Print(E.val) E E1+T E.val:=E1.val+T.val E T E.val:=T.val T T1 * F T.val:=T1.val F.val T F T.val:=F.val F (E) F.val:=E.val F digit F.val:=digit.lexval
3*5+4的带注释的分析树 只使用综合属性. L 3*5+4的带注释的分析树 E.val=19 E.val=15 T.val=4 F.val=4 F.val=5 T.val=3 * digit.lexval=4 F.val=3 digit.lexval=5 digit.lexval=3 3*5+4的带注释的分析树
继承属性 一个结点的继承属性值是由此结点的父结点和/或兄弟结点的某些属性来决定的。 例8.2 添加标识符类型的语义描述: 继承属性type 例8.2 添加标识符类型的语义描述: 继承属性type 产生式 语 义 规 则 D TL L.type:=T.type T int T.type=integer T real T.type:=real L L1,id L1.type:=L.type addtype(id.entry,L.type) L id addtype(id.entry,L.type)
. . 继承属性(续) Real id1,id2,id3的带注释的语法树 D T.type=real L.type= real real ,
8.2 语法制导概论 属性文法:描述语义规则。一个属性文法包含一个上下文无关文法和一系列语义规则,这些语义规则附在文法的每一个产生式上。 语法制导翻译:在语法分析的同时,执行语义子程序: 1 检查静态语义 2 翻译(生成)中间(目标)代码
基于属性文法的处理过程即语法制导翻译是这样的: 对符号串进行语法分析,构造语法树,然后根据需要构造属性依赖图,遍历语法树并在语法树的各结点按语义规则进行计算。 8.2.1 计算语义规则 属性依赖图是一个有向图,用于描述分析树中的属性和属性间的互相依赖关系。 对于编译程序来讲,在单遍扫描中完成语义翻译工作非常重要。
8.2.2 S-属性文法和自下而上翻译 一般的属性文法的翻译器很难建立,然而L-属性文法的翻译器很容易建立。 L-属性文法的一个特例叫S-属性文法。 S-属性文法是只含有综合属性的属性文法。 8.2.3 L-属性文法在自下而上分析中实现 L-属性文法允许一次遍历就计算出所以的属性值。
8.3 中间代码的形式 编译程序的总任务是把源语言的程序代码(源代码)翻译成目标语言的程序代码(目标代码)。 有些编译程序直接把源代码翻译目标代码,而有些编译程序首先把源代码翻译成一种中间语言的程序代码(中间代码),再生成目标代码。
中间代码的特点: ① 中间代码与机器无关,编译程序易于移植。 ② 中间代码级进行优化较为容易。 常见的中间代码形式有逆波兰式,三元式,四元式,树等。 语法制导 翻译方法可分为 非语法制导
语法制导方法是一种形式化方法。它严格依赖于产生式结构。 在产生语法制导翻译程序时,完全根据文法的产生式来生成的,有时为了达到语法制导的目的,不得不对现有产生式做一些修改,这也是语法制导方法的特点。
中间代码 概述 何谓中间代码( Intermediate code) (Intermediate representation) (Intermediate language) 是源程序的一种内部表示 复杂性介于源语言和目标机语言之间 中间代码的作用: 使编译程序的逻辑结构更加简单明确 利于进行与目标机无关的优化 利于在不同目标机上实现同一种语言 中间代码的形式: 逆波兰式、四元式、三元式、间接三元式、树
中间代码按照其与高级语言和机器语言的接近程度,可以分成以下三个层次: 中间代码的层次 中间代码按照其与高级语言和机器语言的接近程度,可以分成以下三个层次: 高级:最接近高级语言,保留了大部分源语言的结构。 中级:介于二者之间,与源语言和机器语言都有一定差异。 低级:最接近机器语言,能够反映目标机的系统结构,因而经常依赖于目标机。
不同层次的中间代码举例 源语言 (高级语言) 中间代码(高级) 中间代码(中级) 中间代码(低级) float a[10][20]; a[i][j+2]; t1 = a[i, j+2] t1 = j + 2 t2 = i * 20 t3 = t1 + t2 t4 = 4 * t3 t5 = addr a t6 = t5 + t4 t7 = *t6 r1 = [fp - 4] r2 = [r1 + 2] r3 = [fp - 8] r4 = r3 * 20 r5 = r4 + r2 r6 = 4 * r5 r7 = fp – 216 f1 = [r7 + r6]
8.3.1 逆波兰式 运算符跟在所有运算对象的后面的表示法写出的式子称为后缀法或逆波兰法。 例子: 中缀表示:a+b 后缀表示:ab+ 前缀表示:+ab 若用post(E)表示中缀式E的逆波兰式 则当E=E1T时有:
POS(E)=POS(E1)||POS(T)|| 其中“||”表示串的“捻接”。 POS(F)=POS(E) F=(E) POS(F)=I F=I POS(T)=POS(T(1))||POS(F)||/ T=T(1)/F POS(T)=POS(T(1))||POS(F)||* T=T(1)*F POS(T)=POS(F) T=F POS(E) =POS(E(1))||POS(T)||- E=E(1)-T POS(E)=POS(E(1))||POS(T)||+ E=E(1)+T POS(E)=POS(T) E=T 逆波兰式 中缀式
例子: pos(A+B*C)=pos(A)||pos(B*C)||+=ABC*+ pos(A*B+c)=pos(A*B)||pos(C)||+=AB*C+ 处理原则: 运算对象出现的顺序与原来的相同 运算符按实际运算顺序出现。 运算符紧跟在运算对象的后面出现,并且没有括号。
逆波兰式的优点:转换为逆波兰式的语言中间形式后,容易实现中间代码的翻译或目标指令。 逆波兰式的生成: ① 运算对象向左移动 ② 运算符与栈顶比较优先数 ③ 括号处理:左括号进栈,起间隔作用;右括号与左括号匹配抵消。
运算对象 波兰表达式 表达式 . 运算符 . 退栈 进栈 . . 运算符栈
例子:a*(b+c/d)abcd/+*的推导 . # .
a *(b+c/d)# . # .
a (b+c/d)# . * # .
a b+c/d)# . ( * # .
ab +c/d)# . ( * # .
ab c/d)# . + ( * # .
abc /d)# . + ( * # .
abc d)# . / + ( * # .
abcd )# . / + ( * # .
abcd/ )# . + ( * # .
abcd/+ )# . ( * # .
abcd/+ # . * # .
abcd/+* # . # .
abcd/+* . . .
动画演示
8.3.2 表达式的三元式和树 一、三元式 三元式的一般形式: i:(ω,OPR1,OPR2) i是三元式编号,不同三元式不能有相同编号。 ω是运算符部分。 OPR1和OPR2是运算对象部分。
(*, b, c) b*c (*, b, d) b*d (+, (1),(2)) b*c+b*d 例子: a:=b*c+b*d的相应三元组 (*, b, c) b*c (*, b, d) b*d (+, (1),(2)) b*c+b*d (:=,(3), a) a:=b*c+b*d 例子:tri(A*B+C) =tri(A*B)||TRI(c)||2:(+,①≥,C) =1:(*, A,B) A*B 2:(+,①,C) A*B+C
tri(A*B+C/D)= 1:(*, A, B) A*B 2:(/, C, D) C/D 3:(+,①,②) A*B+C/D tri(A∨B∧X≠Y+1∨(X≥0∨B)∧D)=1:(+, Y, 1) Y+1 2:(≠,X,①) X≠Y+1 3:(∧,B,②) B∧X≠Y+1 4:(∨,A,③) A∨B∧X≠Y+1
5:(≥, X, 0) X≥0 6:(∨,⑤, B) X≥0∨B 7:(∧,⑥, D) (X≥0∨B)∧D 8:(∨,④,⑦) 二、树 二目运算对应二叉树,多目运算对应多叉树。三元式可以用二叉树表示。
(-,c, d ) c-d (*,b,(1)) b*(c-d) (+,a,(2)) a+b*(c-d) (/,e, f ) e/f (-,(3),(4)) 该题的树结构如下:
c d - + / * a b e f 1 2 3 4 5 该树的根后序为:abcd-*+ef/-,为该式的逆波兰式。
8.3.3 四元式 四元式的一般形式是: (ω,OPR1,OPR2,RESULT) 其中ω是运算符。 OPR1和OPR2是第一,二分量, RESULT是运算结果变量名。 例: 求a:=b*c+b*d 的四元式
FOUR(T) RES(E)=RES(T) 1)(*,b,c,T1) b*c 2)(*,b, d,T2)b*d 3)(+,T1,T2,T3)b*c+b*d 4)(:=,T3,-,a) 下面是表达式四元式的形式定义。 FOUR(T) RES(E)=RES(T) 1.E=T 四元式 中缀式 FOUR(E1) 2.E=E1+T
FOUR(T) (+,RES(E1),RES(T),TEMP) RES(E)=TEMP(临时变量) 空 RES(F)=ID 7.F=I 类似于2 6.T=T1/F 5.T=T1*F FOUR(F) RES(T)=RES(F) 4.T=F 3.E=E1-T
(-,A,B,T1)A-B (*,C,T1,T2)C*(A-B) (+,B,T2,T3)B+C*(A-B) (*,A,T3,T4) FOUR(E) RES(F)=RES(E) 8.F=(E) 例:设有表达式A*(B+C*(A-B))则有 (-,A,B,T1)A-B (*,C,T1,T2)C*(A-B) (+,B,T2,T3)B+C*(A-B) (*,A,T3,T4)
引进一过程GENQT: GENQT(ω):BEGIN RESULT:=NEWTEMP; QT[J]:=(ω,SEM[S-2],SEM[S-1],RESULT); SEM[S-2]:=RESULT; J:=J+1; S:=S-1 END 语法制导翻译算法如下:
空 F->(E) SEM[s]:=EADDR(id);s:=s+1 F->I GENQT(/) T->T/F GENQT(*) T->T*F T->F GENQT(-) E->E-T GENQT(+) E->E+T E->T 语义子程序 产生式
8.4 类型检查与类型转换 例:a+b 3+5=8 3.2+5=3.2+5.0=8.2 3+’T’=? 例:设有一表达式X*2+a*(i+1)/(j+1)其中i和j为整形变量,其它为实型变量,则产生的四元式如下:
1.(tran,2,—, T1) 2.(r*, x,T1, T2) x*2 3.(i+, i, 1, T3) i+1 4.(tran,T3,—,T4) 5.(r*, a, T4,T5) a*(i+1) 6.(i+, j, 1, T6) j+1 7.(tran,T6,—, T7) 8.(r/, T5, T7, T8)a*(i+1)/(j+1) 9.(r+, T2, T8, T9)
8.5 语句的中间代码及其语法制导生成 循环语句只考虑While型循环语句。所要考虑的语句文法如下: Gs:S→i:=E | if E then S | if E then S else S | while E do S | begin B end | goto l | l:S B→S | B; S
下面是语句四元式的形式定义: four(E) (then, res(E),—,—) four(S1) (ifend,—,—,—) S≡if E then S1 (=:,res(E), —,i) S≡i:=E 四元式 源代码
(while,—,—,—) Four(E) (do res(E),—,—) four(S1) S≡while E do S1 four(E) (then,res(E),—,—) (else,—,—, —) four(S2) (ifend,—,—,—) S≡if E then S1 else S2
four(S) B≡S (label,—,—,l) four(S1) S≡l:S1 four(B1) Four(S) B≡B1;S (goto,—,—,l) S≡goto l four(B) S≡begin B end whend(,—,—,—)
例:设有语句 if X=Y+1 then X:=X*Y else while X≠0 do begin X:=X-1;Y:=Y+2 end 则其四元式如下: 1.(+, Y, 1, T1) Y+1 2.(=, X, T1, T2) X=Y+1 3.(then,T2, — ,—) 4.(*, X, Y, T3) X*Y 5.(=:, T3, —, X ) X:=X*Y 6.(else,—, —, —)
8.(≠, X , 0, T4) X≠0 9.(do, T4, —, —) 10.(-, X, 1, T5) X-1 7.(while,—, —, —) 8.(≠, X , 0, T4) X≠0 9.(do, T4, —, —) 10.(-, X, 1, T5) X-1 11.(=:, T5, —, X) X:=X-1 12.(+, Y, 2, T6) Y+2 13.(=:, T6, —, Y) Y:=Y+2 14.(whend, —, —, —) 15.(ifend, —, —, —)
语法制导用的新文法可设计如下: Gs‘:S→Assig E | Ifthen S | Ifelse S | Whido S | begin B end | goto l | Label S Assigi:= Ifthenif E then
Ifelse Ifthen S else Whido While E do While while Label l: B→S| B; S
8.6 复合变量的中间代码 及其语法制导生成 在Pascal中,变量形式定义是: V→id | V[E] | V.id ClASS POINT a tp: L tp’ u l LOW UP CTP ClEN
下面考虑域选择变量V.id情形。 其中tp’是成分类型的TYPEL地址,L是成分类型的长度。 若用typ(V)和addr(V)表示变量V的类型(TYPEL地址)和V的始地址,则有: addr(V[E])=addr(V)+(E-l)*L 其中 l=AINFL[TYPEL[tp].TPOINT].LOW L=AINFL[TYPEL[tp].TPOINT].CLEN 下面考虑域选择变量V.id情形。
addr(V.id)=addr(V)+off(typ(V),id) 设tpy(V)=tp,且TYPEL[tp].TCLASS=d.这是tp指向一个记录类型的内部表示: ClASS POINT d tp: RINFL 若用V.id中的id去查RINFL部分可得到id关于该记录的区距off。若用off (tp,id)表示id 关于tp记录的区距,则有: addr(V.id)=addr(V)+off(typ(V),id)
例:设有PASCAL说明: TYPE at=ARRAY[1..10]OF[1..5]OF integer; rt=RECORD d:real; a:at; b:at END; VAR c,g:at;r,u:rt; 则有:addr(c[i])=cº+(i-1)*5 addr(c[i][j])=cº+(i-1)*5+(j-1)*1 addr(u.a)=uº+1 addr(u.a[i])=uº+1+(i-1)*5
vfour(V): addr(V)=>T 下面考虑V[E]和V.id情形的四元式。变量目标的任务是计算变量的地址,于是其四元式可描述如下: vfour(V): addr(V)=>T 其中vfour表示变量的四元式。 变量的目标代码不一定要彻底计算出变量的地址并将它存于临时变量中。如果没有方便的目标代码,则计算X:=V[E]的过程大致是:
1) Addr(V)=>T1 2) Value(E)T2 3) T1+ T2T3 4) @ T3X 但如果有方便的目标代码,则计算过程可以是: 1) Addr(V)T1 2) Value(E)T2 3) T2[T1]T3
V[E]的四元式结构可设计如下: vfour(V[E]):vfour(V) efour(E) (—,eres(E),l,T1) (*,T1,L,T2) ([],vres(V),T2,T) eres(E)是表示E的结果变量。 vres(V)是表示V变量的地址所在的变量 l和L分别为数组的下界和成分类型长度。
域选择变量V.id的四元式可设计如下: vfour(V.id) : vfour(V) (., vres(V),off,T) 用efour(E)形式表示现在表达式的四元式,eres(E)也类似。其中的vres(v)和efour(E)分别为SEM[s-2]和SEM[s-1],而l和L则可按下法求出: tp:=SYMBL[SEM[S-2]].TYPE l:=AINFL[TYPEL[tp]].LOW L:=AINFL[TYPEL[tp].CLEN 域选择变量V.id的四元式可设计如下: vfour(V.id) : vfour(V) (., vres(V),off,T)
例:假定有前例的说明,则有: vfour(c[i]) : 1.(—, i, 1, T1) 2.(*, T1,5, T2) 3.([], c, T2,T3) vfour(c[i][j]): 1. vfour(c[i]) 4.(—, j, 1,T4) 5.(*, T4,1,T5) 6.([], T3,T5,T6) vfour(u.a[i]): 1.(., u,off,T1) 2.(—,i, 1, T2) 3.(*, T2,5, T3) 4.([],T1, T3,T4)
例:设有说明部分 VAR x,i,j:integer; B:boolean a:ARRAY[1...10]OF[1...5]OF integer; b:ARRAY[1...5] OF integer 则下列语句 a[i][j]:=b[b[i]] a[i]:=b 的四元式部分如下: I.1.(—, i, l, T1) 2.(*, T1,5, T2) 3.([], a, T2,T3) a[i]
4.(—, j, 1, T4) 5.(*, T4, 1, T5)可省 6.([], T3, T5, T6) a[i][j] 7.(—, i, 1, T7) 8.(*, T7, 1, T8)可省 9.([], b, T8, T9) b[i] 10.(—, T9, 1, T10) 11.(*, T10,1, T11)可省 12.([], b, T11, T12) b[b[i]] 13.(=:, T12,—, T6)a[i][j]:=b[b[i]]
II. 1.(—,i, 1, T1) 2.(*, T1,5, T2) 3.([],a, T2,T3) a[i] 4.(=:,b, —,T3) a[i]:=b
(act,eres(En), βn,OFF(Xn)) 8.7 过程语句的中间代码及其语法制导生成 过程语句调用的四元式结构: g(E1,E2,…En) efour(E1) efour(E2) . efour(En) (act,eres(E1), β1,OFF(X1)) (act,eres(E2), β2,OFF(X2)) (act,eres(En), βn,OFF(Xn)) (call,EADDR(g),—,—)
当Xi为赋值形参时,βi部分为1,当Xi为引用型形参时, βi部分为0 。 如果是函数调用,那么最后一条为: (call,EADDR(g),—,NEWT) 在上述四元式中,Xi(i=1,2,..,n)为g的形参名。OFF(Xi)表示形参Xi的off值。
例:设有如下说明部分 则有:f(x,g(a[i])*3,y+2.5) TYPE arr=ARRAY[1...10] OF integer; VAR x,y:real; i:integer; a:arr; FUNCTION f(VAR.Y1:real; Y2:integer;Y3:real):real; BEGIN…………………………………END; FUNCTION g(VAR Z:integer):integer; BEGIN…………………………………END 则有:f(x,g(a[i])*3,y+2.5)
1.(—, i, 1, T1) 2.(*. T1, 1, T2) 3.([], a, T2, T3) a[i] 4.(act, T3, 0, 4) 5.(call,g, —, T4) g(a[i]) 6.(*, T4, 3, T5) g(a[i])*3 7.(+, y, 2.5,T6) y+2.5 8.(act, x, 0, 4) f(x,g(a[i]*3, y+2.5) 9.(act, T5, 1, 5) 10.(act, T6, 1, 6) 11.(call,f, —, T7)
例:原文法为i(E,E……E) S i(L) L E|L,E 用语法制导把文法改写如下: S List) list Head E list list,E Head i(
8.8 说明的中间代码及其语法制导生成 标号说明,常量定义,类型定义,变量说明部分不产生目标代码,因此不用产生中间代码。 设有过程说明:PROCEDURE f( ); LABELDECPART CONSTDEFPART TYPEDEFPART VARDECPART PFDEC1;……PFDECn; PFBODY
则其四元式结构可设计如下: (proc,f,Noff,Moff) dfour(PFDEC1) dfour(PEDEC2) ……… dfour(PEDECn) sfour(PFBODY) (procend,—,—,—) 其中Noff和Moff为f的NOFF和MOFF值,并且Moff部分以后还会变。最终它表示过程所需单元个数+1。
(proc,f,Noff,Moff1) 例:设有过程说明 PROCEDURE f(VAR x,y:real); CONST pai=3.14; TYPE arr=ARRAY[1..10]OF real; VAR x1:reaL;a:arr; FUNCTION h(X0:integer) :real; BEGIN h:=x0/2 END BEGIN x:=y+pai; a[2]:=1.2*x END 则其四元式如下: (proc,f,Noff,Moff1)
(func, h, Noff2,Moff2) (/, x0, 2, T1) x0/2 (=:, T1 ,—, h) h:=x0/2 (funcend, —, —, —) (+, y, pai, T2) y+pai (=:, T2, —, x) x:=y+pai (—, 2, 1, T3) (*, T3, 1, T4) ([], a, T4, T5) a[2] (*, 1.2, x, T6) 1.2*x (=: T6, —, T5) a[2]:=1.2*x (procend,—, —, —)