980 likes | 1.18k Views
第六章. 属性文法和语法制导翻译. 介绍有关语义分析及翻译的问题。 语义描述和语义处理的方法主要是属性文法和语法制导翻译方法。 属性文法:综合属性、继承属性 基于属性文法的处理方法:依赖图、属性的计算次序、树遍历的属性计算方法、一遍扫描的处理方法、抽象语法树 S- 属性文法的自下而上计算 L- 属性文法和自顶向下翻译 : 翻译模式、自顶向下翻译、递归下降翻译器的设计 自下而上计算继承属性. 6.1 属性文法. 属性 是指与文法符号的类型和值等有关的一些信息,在编译中用属性描述处理对象的特征。
E N D
第六章 属性文法和语法制导翻译
介绍有关语义分析及翻译的问题。 • 语义描述和语义处理的方法主要是属性文法和语法制导翻译方法。 • 属性文法:综合属性、继承属性 • 基于属性文法的处理方法:依赖图、属性的计算次序、树遍历的属性计算方法、一遍扫描的处理方法、抽象语法树 • S-属性文法的自下而上计算 • L-属性文法和自顶向下翻译:翻译模式、自顶向下翻译、递归下降翻译器的设计 • 自下而上计算继承属性
6.1 属性文法 属性是指与文法符号的类型和值等有关的一些信息,在编译中用属性描述处理对象的特征。 随着编译的进展,对语法分析产生的语法树进行语义分析,且分析的结果用中间代码描述出来。 对于一棵等待翻译的语法树,它的各个结点都是文法中的一个符号X,该X可以是终结符或非终结符。根据语义处理的需要,在用产生式AαXβ进行归约或推导时,应能准确而恰当地表达文法符号X在归约或推导时的不同特征。
例如: 判断变量X的类型是否匹配,要用X的数据类型来描述; 判断变量X是否存在,要用X的存储位置来描述: 而对X的运算,则要用X的值来描述; 因此,语义分析阶段引入X的属性,如 X.type 描述变量X的类型 X.place 描述变量X的存储位置 X.val 描述变量X的值
属性文法是在上下文无关文法的基础上为每个文法符号(终结符或非终结符)配备若干个相关的属性。即上下文无关文法+属性+属性计算规则属性文法是在上下文无关文法的基础上为每个文法符号(终结符或非终结符)配备若干个相关的属性。即上下文无关文法+属性+属性计算规则 属性一般分为两类: 综合属性用于“自下而上”传递信息; 继承属性用于“自上而下”传递信息。 属性加工的过程即是语义处理的过程,对于文法的每一个产生式都配备的一组属性的计算规则,称为语义规则。
在一个属性文法中,A→P都有与之相关联的一套语义规则,规则形式为在一个属性文法中,A→P都有与之相关联的一套语义规则,规则形式为 b:= f(c1,c2,…,ck), f是一个函数,而且 1.b是A的一个综合属性并且c1,c2,…,ck是中的文法符号的属性,或者 2.b是中的文法符号的一个继承属性并且c1,c2,…,ck是A或中的任何文法符号的属性。 在两种情况下,都说 属性b依赖于属性c1,c2,…,ck。
要特别强调的是: (1)终结符只有综合属性,它由词法分析器提供; (2)非终结符既可以有综合属性也可以有继承属性,文法开始符号的所有继承属性作为属性计算前的初始值。 语义规则所描述的工作可以包括属性计算、静态 语义检查、符号表操作、代码生成等。
例6.1 台式计算器程序的属性文法(表6.1) 产生式 语义规则 LEn 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
综合属性: 在语法树中,一个结点的综合属性的值由其子结点的属性值确定。 通常使用自下而上的方法在每一个结点处使用语义规则计算综合属性的值。 仅仅使用综合属性的属性文法称S—属性文法。 使用语义规则来计算 在建立 每一个 结点处 综合 属性值 即在 用哪个产生式进行归约后,就执行那个产生式的语义规则计算属性的值,从叶结点到根结点进行计算。
例 :输入3*5+4n L Eval:=19 n Eval:=15 + Tval:=4 Tval:=15 Fval:=4 digitlexval:=4 Tval:=3 * Fval:=5 LEn E E1+T E T T T1*F T F F (E) F digit Fval:=3 digitlexval:=5 digitlexval:=3
继承属性: 在语法树中,一个结点的继承属性由此结点的父结点和/或兄弟结点的某些属性确定。 例:变量说明的类型定义 表6.2 带有继承属性L.in的属性文法 产生式 语义规则 DTL L.in:=T type T int T .type :=integer T real T . type :=real L L1,id L1 .in :=L .in addtype(id .entry,L .in) L id addtype(id .entry,L .in)
D T.type=real L.in=real real , id3 L.in=real L.in=real , id2 id1 real id1,id2,id3的带注释的语法树
6.2 基于属性文法的处理方法 从概念上讲,基于属性文法的处理过程通常是这样的:对单词符号串进行语法分析,构造语法分析树,然后根据需要遍历语法树,并在语法树的各结点处按语义规则进行计算。 这种由源程序的语法结构所驱动的处理办法就是语法制导翻译法。 输入串语法树依赖图语义规则计算次序 语义规则的计算可能产生代码、在符号表中存放信息、给出错误信息或执行任何其它动作。对输入串的翻译也就是根据语义规则进行计算出的结果。
6.2.1 依赖图 如果在一棵语法树中,一个结点的属性b依赖于属性c,那么这个结点处计算b的属性规则必须在确定c的语义规则之后使用。 在一颗语法树中,结点的继承属性和综合属性之间的相互依赖关系可以用称作依赖图的一个有向图来描述。 为每一个包含过程调用的语义规则引入一个虚综合属性b,这样把每一个语义规则都写成如下形式 b:= f(c1,c2, …ck) 依赖图中为每一个属性设置一个结点,如果属性b依赖属性c,则从属性c的结点有一条有向边连到属性b的结点。
依赖图的构造方法 for 语法树中每一结点n do for 结点n的文法符号的每一个属性a do 为a在依赖图中建立一个结点; for 语法树中每一个结点n do for 结点n所用产生式对应的每一条 语义规则 b:=f(c1,c2,…,ck ) do for i:=1 to k do 从结点ci到结点b构造一条有向边;
A A.a X Y X.x Y.y 例如,产生式 AXY的语义规则是: A.a:=f( X.x, Y.y ) 该语义规则确定了综合属性A.a依赖于属性X.x和Y.y的。 • 在依赖图中会有三个结点A.a,X.x,和Y.y。 • 由于A.a依赖于X.x,所以有一条有向边从X.x到A.a.由于A.a也依赖于Y.y,所以还有一条有向边从Y.y连到A.a. • 如果与产生式AXY对应的语义规则还有: X.i:=g(A.a,Y.y) 那么,图中还应有两条有向边,一条从A.a连到X.i,另一条从Y.y连到X.i,因为X.i依赖于A.a和Y.y. X.i
例:下图是书中图6.2(P141)带注释语法树的依赖图。例:下图是书中图6.2(P141)带注释语法树的依赖图。 D 图6. 5 4 type T in 5 L 6 real , in 7 id3 3 entry L 8 in 9 L 10 , id2 2 entry 如果一属性文法不存在属性之间的循环依赖关系,那么该文法为良定义的。 id1 1 entry
属性的计算次序 ◆拓扑排序 一个有向非循环图的拓扑排序是图中结点的任何顺序m1,m2,…,mk,使得边必须是从序列中前面的结点指向后面的结点,也就是说,如果mi→mj是mi到mj的一条边,那么在 序列中mi必须出现在mj的前面。 若依赖图中无环,则存在一个拓扑排序,它就是属性值的计算顺序。
例6. 5 图6. 5的拓扑排序是: 1,2,3,4,5,6,7,8,9,10 a4:=real; a5:=a4; addtype(id3entry,a5); a7:=a5; addtype(id2entry,a7); a9:=a7; addtype(id1entry,a9);
例:无符号数文法: numbernumber digit|digit digit0|1|2|3|4|5|6|7|8|9 属性文法 产生式 语义规则 number1number2 digit number1.val=number2.val*10+digit.val number digit number.val=digit.val digit0 digit.val=0 digit.val=1 digit1 digit.val=2 digit2 digit.val=3 digit3 … …
例 :输入345 number.val:=345 number.val:=34 digit.val:=5 number.val:=3 digit.val:=4 digit.val:=3
6.2.2 树遍历的属性计算方法 • 假设语法树已经建立好了,并且树中已带有开始符号的继承属性和终结符的综合属性。 • 然后以某种次序遍历语法树,直至计算出所有属性。 • 最常用的遍历方法是深度优先,从左到右的遍历方法。如果需要的话,可使用多次遍历(或称遍)。
下面算法可对任何无循环的属性文法进行计算 While 还有未被计算的属性 do VisitNode(S) /*S是开始符号*/ procedure VisitNode(N: Node); begin if N是一个非终结符 then /* 假设它的产生式为N→X1,X2,…Xm */ for i:=1 to m do if not Xi∈VT then/* 即Xi是非终结符 * / begin 计算 Xi的所有能够计算的继承属性; VisitNode (Xi) end; 计算 N 的所有能够计算的综合属性 end
例:S有继承属性a,综合属性b;X有继承属性c,综合属性d;Y有继承属性e、综合属性f;Z有继承属性h、综合属性g。例:S有继承属性a,综合属性b;X有继承属性c,综合属性d;Y有继承属性e、综合属性f;Z有继承属性h、综合属性g。 产生式 语义规则 SXYZ Z.h:=S.a X.c:=Z.g S.b:=X.d-2 Y.e:=S.b X x X.d:=2*X.c Y y Y.f:=Y.e*3 Z z Z.g:=Z.h+1 While 还有未被计算的属性 do VisitNode(S) /*S是开始符号*/ procedure VisitNode(N: Node); begin if N是一个非终结符 then /* 假设它的产生式为N→X1,X2,…Xm */ for i:=1 to m do if not Xi∈VT then/* 即Xi是非终结符 * / begin 计算 Xi的所有能够计算的继承属性; VisitNode (Xi) end; 计算 N 的所有能够计算的综合属性 end
6.2.3 一遍扫描的处理方法 一遍扫描的方法是在语法分析的同时计算属性值。 因为一遍扫描的处理方法与语法分析器的相互作用,它与下面两个因素密切相关: (1)所采用的语法分析方法; (2)属性的计算次序。 所谓语法制导翻译法,直观上说是为文法中每个产生式配上一组语义规则,并且在语法分析的同时执行这些语义规则。 在自上而下的语义分析中,若一个产生式匹配输入串成功,或者在自下而上分析中,当一个产生式被用于进行归约时,此产生式相应的语义规则就被计算,完成有关语义分析和代码生成的工作。
6.2.4 抽象语法树 • 通过语法分析可以很容易构造出语法分析树,然后对语法树进行遍历完成属性的计算。 • 语法树可以作为一种合适的中间语言形式。 • 在语法树中去掉那些对翻译不必要的信息,从而获得更有效的源程序中间表示。这种经变换后的语法树称之为抽象语法树。 产生式S→if B then S1 else S2的语法树 if-then-else / | \ B S1 S2 在语法树中,操作符和关键字都不在叶结点,而是在内部结点中出现。
建立表达式的抽象语法树 使用的函数: 1. mknode(op,left,right) 建立一个运算符号结点,标号是op,两个域left和right分别指向左子树和右子树。 2.mkleaf(id,entry) 建立一个标识符结点,由标号id标识,一个域entry指向标识符在符号表中的入口。 3. mkleaf(num,val) 建立一个数结点,标号为num,域val用于存放数的值。
例:下面一系列函数调用建立了表达式a-4+c的抽象语法树。在这个序列中P1,P2,…,P5是指向结点的指针,entrya和entryc分别是指向符号表中的标识符a和c的指针。例:下面一系列函数调用建立了表达式a-4+c的抽象语法树。在这个序列中P1,P2,…,P5是指向结点的指针,entrya和entryc分别是指向符号表中的标识符a和c的指针。 (1)P1:=mkleaf(id,entrya); (2)P2:=mkleaf(num,4); (3)P3:=mknode(‘-’,P1,P2); (4) P4:=mkleaf(id,entryc); (5)P5:=mknode(‘+’,P3,P4);
+ – id to entry c id num 4 to entry a 图6.7 a-4+c的抽象语法树
为表达式建立抽象语法树的属性文法 产生式 语义规则 EE1+T E.nptr:=mknode('+',E1.nptr,T.nptr) E E1-T E.nptr:=mknode('-',E1.nptr,T.nptr) E T E.nptr:=T.nptr T (E) T.nptr:=E.nptr T id T.nptr:=mkleaf(id,id.entry) T num T.nptr:=mkleaf(num,num.val)
6.4 L—属性文法和自顶向下翻译 ◆定义 一个语法制导定义是L-属性定义,如果A→X1X2…XnP,其每一个语义规则中的每一个属性都是一个综合属性,或是Xj(1j n)的一个继承属性,这个继承属性仅依赖于 1. 产生式中Xj的左边符号X1,X2,…Xj-1 的属性; 2.A的继承属性。 1 2
A的继承属性 A.b A的继承属性 A X1.c1 X2.c2 … Xn.cn X1.c1 X2.c2 Xk.b … Xn.cn 继承属性Xk.b的计算 综合属性A.b的计算
表6.7 是否为L-属性的语法制导定义? 产生式 语义规则 ALM A QR L.i:=l(A.i) M.i:=m(L.s) A.s:=f(M.s) R.i:=r(A.i) Q.i:=q(R.s) A.s:=f(Q.s) 解: 表6.7的语法制导定义不是L-属性定义 文法符号Q的继承属性依赖于它右边文法符号R的属性
6.4.1 翻译模式 ◆定义 翻译模式是语法制导翻译的一种便于翻译的书写形式。其中属性与文法符号相对应,语义规则或语义动作用花括号{ }括起来,可被插入到产生式右部的合适位置上。 这是一种语法分析和语义动作交错的表示法,它表达在按深度优先遍历分析树的过程中何时执行语义动作。 翻译模式给出了使用语义规则进行计算的顺序。可看成是分析过程中翻译的注释。
例:一个简单的翻译模式 E→TR R→addop T {print(addop.lexeme)}R1|ε T→num{print(num.val)} 把语义动作看成终结符号,输入9-5+2, 当按深度优先遍历它,执行遍历中访问的语义动作,将输出 9 5 - 2 + 它是输入表达式9-5+2的后缀式。
图6.10 9-5+2的带语义动作的分析树 E→TR R→addop T {print(addop.lexeme)}R1|ε T→num {print(num.val)} E R T R - T 9 Pt(´-´) Pt(´9´) R T + Pt(´+´) 5 Pt(´5´) 2 Pt(´2´)
◆设计翻译模式 条件:语法制导翻译是L-属性文法 保证语义动作不会引用还没有计算的属性值。 1. 只需要综合属性的情况: 为每一个语义规则建立一个包含赋值的动作,并把这个动作放在相应的产生式右边的末尾。 例如:语法和语义规则如下,请写出其翻译模式: TT1*F T.val:=T1 .val*F.val 解:TT1*F {Tval:=T1 val*F val}
S A A a a • 既有综合属性又有继承属性 • ● 产生式右边的符号的继承属性必须在这个符号以前的动作中计算出来。 • ● 一个动作不能引用这个动作右边符号的综合属性。 • ● 产生式左边非终结符号的综合属性只有在它所引用的所有属性都计算出来以后才能计算。计算这种属性的动作通常可放在产生式右端的未尾。 例:下面的翻译模式满足要求吗 ? SA1A2 {A1.in:=1; A2.in:=2} A a { print(A.in } A1.in:=1; A2.in:=2 Print(A.in) Print(A.in) 解:不满足
6.4.2 自顶向下的翻译 用翻译模式构造自顶向下翻译。 1 从翻译模式中消除左递归 对于一个翻译模式,若采用自顶向下分析,必须消除左递归和提取左公因子,在改写基本文法时考虑属性值的计算。
例6.14 带左递归的文法的翻译模式构造方法 E E1+T{E.val:=E1.val+T.val} E E1-T {E.val:=E1.val-T.val} E T {E.val:=T.val} T (E) {T.val:=E.val} T num {T. val:=num.val} 图6.13 带左递归的文法的翻译模式
EE1+T E E1-T E T T (E) T num E→T {R.i:=T.val} R{E.val:=R.s} R→+ T{R1.i:=R.i+T.val} R1{R.s:=R1.s} R→- T{R1.i:=R. i-T.val} R1{R .s:=R1 .s} R→ε{R .s:=R.i} T→( E ) {T.val:=E.val} T→num {T.val:=num.val} E TR R(+T|-T)R| ε T (E) T num 继承属性i 综合属性s 图6.14 经过转换的带有右递归文法的翻译模式
E→T {R.i:=T.val} R{E.val:=R.s} R→+ T{R1.i:=R.i+T.val} R1{R.s:=R1.s} R→- T{R1.i:=R. i-T.val} R1{R .s:=R1 .s} R→ε{R .s:=R.i} T→( E ) {T.val:=E.val} T→num {T.val:=num.val} 图6.15 表达式9-5+2的计算 E val=6 E val=6 R i=9 R i=9; R s= 6 R s= 6 Tval=9 Tval=9 – T val=5 T val=5 R i=4 R i=4; R s= 6 R s= 6 9 + T val=2 T val=2 Ri=6; R s= 6 R i=6 5 R s= 6 2
◆关于左递归翻译模式更一般化的讨论 • 左递归翻译模式 • A→A1Y{A.a:=g(A1.a,Y.y)} • A→X {A.a:=f(X.x)} (6.1) • 每一个文法符号都有一个综合属性,用相应的小写字母表示,g和f是任意函数。 • 消除左递归,文法转换成 • A→X R • R→Y R|ε (6.2)
再考虑语义动作,翻译模式变为: • A→X {R.i:=f(X.x)} • R {A.a:=R.s} • R→Y {R1 .i:=g(R.i,Y.y)} • R1{R.s:=R1.s} • R→ε{R .s:=R.i} (6.3) • 经过转换的翻译模式,使用R的继承属性i和综合属性s。
A→A1Y{A.a:=g(A1.a,Y.y)} A→X {A.a:=f(X.x)} 例输入:XY1Y2 采用左递归翻译模式 A A.a=g(g(f(X.x),Y1.y),Y2.y) A A.a=g(f(X.x),Y1.y) Y2 A A.a=f(X.x) Y1 X (a)
例输入:XY1Y2 消除左递归后翻译模式 A A→X {R.i:=f(X.x)} R {A. a:=R. s} R→Y {R1. i:=g(R.i,Y.y)} R1{R.s:=R1.s} R→ε{R.s:=R.i} X R R.i=f(X.x) Y1 R R.i=g(f(X. x),Y1.y) Y2 R R.i=g(g(f(X.x),Y1.y),Y2.y) (b) ε
例6. 15 表达式建立语法树的翻译模式: EE1+T {Enptr :=mknode(´+´,E1 nptr,T nptr)} EE1-T {Enptr :=mknode(´-´,E1 nptr,T nptr)} ET {Enptr :=T nptr} 从翻译模式中消除左递归,变成图6.17的翻译模式。
图6.17 EE1+T {E.nptr :=mknode(´+´, E1 . nptr,T .nptr)} EE1-T {E.nptr :=mknode(´-´, E1 . nptr,T .nptr)} ET {E.nptr :=T .nptr} E→T {R.i:=T . nptr} R {E . nptr:=R . s} R→+ T {R1. i:=mknode('+',R . i,T . nptr)} R1{R . s:=R1. s} R→- T {R1. i:=mknode('-',R . i,T . nptr)} R1 {R . s:=R1. s} R→ε {R . s:=R . i} T→( E ) {T . nptr:=E . nptr} T→id {T . nptr:=mkleaf(id, id . entry)} T→num {T . nptr:=mkleaf(num,num . val)}
使用继承属性构造语法树 E nptr R T i i R - nptr id T nptr + T i R s num id ε + - id to entry for c id num 4 to entry for a