1、第六章 属性文法和语法制导翻译,内容,属性文法 基于属性文法的处理方法 S-属性文法的自下而上计算 L-属性文法和自顶向下翻译 自下而上计算继承属性,第六章 属性文法和语法制导翻译,语义:一组规则,用它可以定义一个程序的意义。 描述方法: 自然语言描述:隐藏错误、二义性和不完整性 形式描述:操作语义(PL/1)指称语义(ADA)代数语义(PASCAL) 属性文法,第六章 属性文法和语法制导翻译,编译中的语义处理包括两个功能: (1)审查每个语法结构的静态语义,即验证语法结构合法的程序是否真正有意义。也称为静态语义分析或静态审查; (2)如果静态语义正确,则执行真正的翻译,即生成中间代码或生成实
2、际的目标代码。 以上工作普遍基于属性文法和语法制导翻译方法。,6.1 属性文法,属性文法(也称属性翻译文法) Knuth在1968年提出 在上下文无关文法的基础上,为每个文法符号(终结符或非终结符)配备若干相关的“值”(称为属性)。 属性代表与文法符号相关信息,如类型、值、代码序列、符号表内容等 属性可以进行计算和传递 语义规则:对于文法的每个产生式都配备了一组属性的计算规则,6.1 属性文法,属性 综合属性:“自下而上”传递信息 继承属性:“自上而下”传递信息 在一个属性文法中,对应于每个产生式A都有一套与之相关联的语义规则,每条规则的形式为: b:=f(c1, c2, , ck)这里,f是
3、一个函数,而且或者 1. b是A的一个综合属性并且c1,c2,ck是产生式右边文法符号的属性,或者 2. b是产生式右边某个文法符号的一个继承属性并且c1,c2,ck 是A或产生式右边任何文法符号的属性。在两种情况下,属性b依赖于属性c1,c2,ck。,6.1 属性文法,说明 终结符只有综合属性,由词法分析器提供 非终结符既可有综合属性也可有继承属性,文法开始符号的所有继承属性作为属性计算前的初始值 对出现在产生式右边的继承属性和出现在产生式左边的综合属性都必须提供一个计算规则。属性计算规则中只能使用相应产生式中的文法符号的属性 出现在产生式左边的继承属性和出现在产生式右边的综合属性不由所给的
4、产生式的属性计算规则进行计算,它们由其它产生式的属性规则计算或者由属性计算器的参数提供,6.1 属性文法,语义规则所描述的工作可以包括属性计算、静态语义检查、符号表操作、代码生成等等。 例,考虑非终结符A,B和C,其中,A有一个继承属性a和一个综合属性b,B有综合属性c,C有继承属性d。产生式ABC可能有规则C.d:=B.c+1A.b:=A.a+B.c 而属性A.a和B.c在其它地方计算,属性文法的例子:简单算术表达式求值的语义描述。 非终结符E、T及F都有一个综合属性val, 符号digit有一个综合属性lexval,它的值由词法分析器提供。 与产生式LE对应的语义规则仅仅是打印由E产生的算
5、术表达式的值的一个过程,我们可认为这条规则定义了L的一个虚属性。 某些非终结符加下标是为了区分一个产生式中同一非终结符多次出现,语 义 规 则,L E,E E1+T,E T,T T1 * F,T F,F (E),F digit,Print(E.val),E.val:=E1.val+T.val,E.val:=T.val,T.val:=T1.val F.val,T.val:=F.val,F.val:=E.val,F.val:=digit.lexval,产生式,6.1 属性文法,综合属性 在语法树中,一个结点的综合属性的值由其子结点的属性值确定。 使用自底向上的方法在每一个结点处使用语义规则计算综合
6、属性的值 仅仅使用综合属性的属性文法称S属性文法 结点属性值的计算正好和自底向上分析建立分析树结点同步进行。,表达式3*5+4n的带注释的语法树 (语义动作打印数值19),digit.lexval=3,F.val=3,T.val=3,*,digit.lexval=5,F.val=5,T.val=15,E.val=15,+,digit.lexval=4,F.val=4,T.val=4,E.val=19,n,L,6.1 属性文法,继承属性 在语法树中,一个结点的继承属性由此结点的父结点和/或兄弟结点的某些属性确定 用继承属性来表示程序设计语言结构中的上下文依赖关系很方便,6.1 属性文法,产 生
7、式 语 义 规 则 DTL L.in := T.type Tint T.type := integer Treal T.type := real LL1, id L1.in :=L.in addtype(id.type, L.in) Lid addtype(id.type, L.in),句子real id1,id2,id3的带注释的语法树,id1,L,id2,L,id3,L,real,T,D,T.type=real,L.in=real,L.in=real,L.in=real,6.2 基于属性文法的处理方法,语法制导翻译即基于属性文法的处理过程通常是这样的:对单词符号串进行语法分析,构造语法分析
8、树,然后根据需要遍历语法树并在语法树的各结点处按语义规则进行计算。 由源程序的语法结构所驱动的处理办法就是语法制导翻译法 语义规则的计算可能产生代码、在符号表中存放信息、给出错误信息或执行其他动作。对输入符号串的翻译就是根据语义规则进行计算的结果。,6.2.1 依赖图,在一棵语法树中的结点的继承属性和综合属性之间的相互依赖关系可以由称作依赖图的一个有向图来描述 为每一个包含过程调用的语义规则引入一个虚综合属性b,这样把每一个语义规则都写成 b:=f(c1,c2,ck)的形式 依赖图中为每一个属性设置一个结点,如果属性b依赖于属性c,则从属性c的结点有一条有向边连到属性b的结点。,6.2.1 依
9、赖图,依赖图的构造算法: for 语法树中每一结点n dofor 结点n的文法符号的每一个属性a do为a在依赖图中建立一个结点;for 语法树中每一个结点n dofor 结点n所用产生式对应的每一个语义规则b:=f(c1,c2,ck ) dofor i:=1 to k do从ci结点到b结点构造一条有向边;,EE1E2 E.val:=E1.val+E2.val,E1,+,E2,E,val,val,val,句子real id1,id2,id3的带注释的语法树的依赖图,id1,L,id2,L,id3,L,real,T,D,4 type,5 in,6,7 in,8,9 in,10,1 entry,
10、2 entry,3 entry,6.2.1 依赖图,如果一属性文法不存在属性之间的循环依赖关系,那么称该文法为良定义的,6.2.1 依赖图,属性的计算次序 一个依赖图的任何拓扑排序都给出一个语法树中结点的语义规则计算的有效顺序 属性文法说明的翻译是很精确的 基础文法用于建立输入符号串的语法分析树 根据计算语义规则建立依赖图 从依赖图的拓扑排序中,我们可以得到计算语义规则的顺序,句子real id1,id2,id3的带注释的语法树的依赖图,a4:=real; a5:=a4 addtype (id3.entry,a5); a7:=a5; addtype (id2.entry,a7); a9:=a7
11、 addtype (id1.entry,a9);,6.2.2 树遍历的属性计算方法,通过树遍历的方法计算属性的值 假设语法树已经建立起了,并且树中已带有开始符号的继承属性和终结符的综合属性 以某种次序遍历语法树,直至计算出所有属性 深度优先,从左到右的遍历,无循环的属性文法计算算法:,While 还有未被计算的属性 doVisitNode(S) /*S是开始符号*/procedure VisitNode (N:Node) ; beginif N是一个非终结符 then/*假设它的产生式为NX1Xm*/for i :=1 to m doif not XiVT then /*即Xi是非终结符*/b
12、egin计算Xi的所有能够计算的继承属性;VisitNode (Xi)end;计算N的所有能够计算的综合属性 end,产 生 式 语 义 规 则 SXYZ Z.h := S.aX.c := Z.gS.b := X.d -2Y.e := S.bXx X.d :=2*X.cYy Y.f := Y.e*3Zz Z.g :=Z.h+1,例:考虑属性的文法G。其中,S有继承属性a,综合属性b;X有继承属性c、综合属性d;Y有继承属性e、综合属性f;Z有继承属性h、综合属性g,假设S.a的初始值为0,输入串为xyz,S:a=0,X,Y,Z,x,y,z,Z:h=0g=1,X:c=1d=2,S:a=0, b=
13、0,Y:e=0f=0,6.2.3 一遍扫描的处理方法,一遍扫描的处理方法是在语法分析的同时计算属性值 所采用的语法分析方法 属性的计算次序 L属性文法适合于一遍扫描的自上而下分析 S属性文法适合于一遍扫描的自下而上分析,6.2.3 一遍扫描的处理方法,所谓语法制导翻译法,直观上说就是为文法中每个产生式配上一组语义规则,并且在语法分析的同时执行这些语义规则 语义规则就被计算的时机 在自上而下语法分析中,一个产生式匹配输入串成功时 在自下而上分析中,当一个产生式被用于进行归约时,6.2.4 抽象语法树,在语法树中去掉那些对翻译不必要的信息,从而获得更有效的源程序中间表示。这种经变换后的语法树称之为
14、抽象语法树(Abstract Syntax Tree),Sif B then S1 else S2,3*5+4,建立表达式的抽象语法树,mknode (op,left,right) 建立一个运算符号结点,标号是op,两个域left和right分别指向左子树和右子树。 mkleaf (id,entry) 建立一个标识符结点,标号为id,一个域eutry指向标识符在符号表中的入口。 mkleaf (num,ral) 建立一个数结点,标号为num,一个域ral用于存放数的值。,建立抽象语法树的语义规则,产 生 式 语 义 规 则 EE1+T E.nptr := mknode( +, E1.nptr,
15、 T.nptr ) EE1-T E.nptr := mknode( -, E1.nptr, T.nptr ) ET E.nptr := T.nptr T (E) T.nptr := E.nptr Tid T.nptr := mkleaf( id, id.entry ) Tnum T.nptr := mkleaf( num, num.val ),a4c的抽象语法树,E nptr,T nptr,E nptr,To entry for a,E,T nptr,id,-,T nptr,id,To entry for c,6.3 S-属性文法的自下而上计算,S-属性文法:只含有综合属性 综合属性可以在分析
16、输入符号串的同时由自下而上的分析器来计算。 分析器可以保存与栈中文法符号有关的综合属性值,每当进行归约时,新的属性值就由栈中正在归约的产生式右边符号的属性值来计算。,6.3 S-属性文法的自下而上计算,在分析栈中使用一个附加的域来存放综合属性值 假设语义规则A.a:=f(X.x,Y.y,Z.z)是对应于产生式AXYZ的,产生式 代 码 段 LEn print(valtop) EE1+T valntop := valtop-2+valtop ET TT1*F valntop := valtop-2*valtop TF F (E) valntop :=valtop-1Fdigit,翻译输入3*5+
17、4n所做的移动,输入 state val 用到的产生式3*5+4n *5+4n 3 3 *5+4n F 3 Fdigit*5+4n T 3 TF5+4n T* 3 - +4n T*5 3 - 5 +4n T*F 3 - 5 Fdigit+4n T 15 TT*F,翻译输入3*5+4n所做的移动,输入 state val 用到的产生式+4n E 15 ET4n E+ 15- n E+4 15- 4 n E+F 15- 4 Fdigitn E+T 15- 4 TFn E 19 EE+TEn 19- L 19 LEn,6.4 L-属性文法和自顶向下翻译,通过深度优先的方法对语法树进行遍历,计算属性文
18、法的所有属性值 LL(1):自上而下分析方法,深度优先建立语法树,6.4 L-属性文法和自顶向下翻译,一个属性文法称为L-属性文法,如果对于每个产生式AX1X2Xn,其每个语义规则中的每个属性或者是综合属性,或者是Xj(1jn)的一个继承属性且这个继承属性仅依赖于: (1) 产生式Xj的左边符号X1,X2,Xj-1的属性; (2) A的继承属性。 S-属性文法一定是L-属性文法,6.4 L-属性文法和自顶向下翻译,产 生 式 语 义 规 则 ALM L.i := l(A.i) M.i :=m(l.s) AQR R.i := r(A.i) Q.i :=q(R.s) A.s :=f(Q.s) 上述
19、文法不是L-属性文法,Q的继承属性Q.i依赖于它右边的文法符号的属性R.s,6.4.1 翻译模式,翻译模式:给出了使用语义规则进行计算的次序,这样就可把某些实现细节表示出来 在翻译模式中,和文法符号相关的属性和语义规则(这里我们也称语义动作),用花括号 括起来,插入到产生式右部的合适位置上,翻译模式示例:把带加号和减号的中缀表达式翻译成相应的后缀表达式,ETRRaddop T print(addop.lexeme) R1 | Tnum print(num.val),-,E,T,R,9,print(9),T,R,5,print(5),print(-),+,T,2,print(2),R,print
20、(+),输入串9-5+2 后缀表达式95-2+,6.4.1 翻译模式,设计翻译模式时,必须保证当某个动作引用一个属性时它必须是有定义的。 L-属性文法本身就能确保每个动作不会引用尚未计算出来的属性。,6.4.1 翻译模式,建立翻译模式: 当只需要综合属性时:为每一个语义规则建立一个包含赋值的动作,并把这个动作放在相应的产生式右边的末尾产生式 语义规则TT1*F T.val:=T1.valF.val建立产生式和语义动作:TT1*F T.val:=T1.valF.val,6.4.1 翻译模式,建立翻译模式: 如果既有综合属性又有继承属性,在建立翻译模式时就必须保证: 1. 产生式右边的符号的继承属
21、性必须在这个符号以前的动作中计算出来。 2. 一个动作不能引用这个动作右边的符号的综合属性。 3. 产生式左边非终结符的综合属性只有在它所引用的所有属性都计算出来以后才能计算。计算这种属性的动作通常可放在产生式右端的末尾。 如下列翻译模式不满足条件1:SA1A2 A1.in:=1; A2.in:=2Aa print(A.in),6.4.1 翻译模式,基于数学格式语言EQN 给定输入E sub 1 .val,识别输入并进行格式安放的L-属性文法,产 生 式 语 义 规 则SB B.ps :=10S.ht :=B.htBB1B2 B1.ps :=B.psB2.ps :=B.psB.ht := ma
22、x(B1.ht, B2.ht)BB1 sub B2 B1.ps :=B.psB2.ps :=shrink(B.ps)B.ht :=disp(B1.ht, B2.ht)Btext B.ht :=text.hB.ps,翻译模式,S B.ps:=10B S.ht:=B.htB B1.ps:=B.psB1 B2.ps:=B.ps B2 B.ht:=max(B1.ht,B2.ht)B B1.ps:=B.psB1sub B2.ps:=shrink(B.ps)B2 B.ht:=disp(B1.ht,B2.ht)B textB.ht:=text.hB.ps,6.4.2 自顶向下翻译,动作是在处于相同位置上的符
23、号被展开(匹配成功)时执行的。 为了构造不带回溯的自顶向下语法分析,必须消除文法中的左递归 当消除一个翻译模式的基本文法的左递归时同时考虑属性适合带综合属性的翻译模式,6.4.2 自顶向下翻译,关于算术表达式的左递归文法相应的翻译模式EE1+T E.val:=E1.val+T.valEE1-T E.val:=E1.val-T.valET E.val:=T.valT(E) T.val:=E.valTnum T.val:=num.val,消除左递归,构造新的翻译模式 E T R.i:=T.valR E.val:=R.sR +T R1.i:=R.i+T.valR1 R.s:=R1.sR -T R1.
24、i:=R.i-T.valR1 R.s:=R1.sR R.s:=R.iT (E) T.val:=E.valT num T.val:=num.val,计算表达式952,E,T,R,num,num.val=9,T.val=9,R.i=9,-,T,R,num,num.val=5,T.val=5,R.i=4,+,T,R,num,num.val=2,T.val=2,R.i=6,R.s=6,R.s=6,R.s=6,E.s=6,自顶向下分析一般方法,假设有翻译模式:AA1Y A.a:=g(A1.a,Y.y)AX A.a:=f(X.x)它的每个文法符号都有一个综合属性,用小写字母表示,g和f是任意函数。,消除左
25、递归:AXRRYR | ,翻译模式变为 :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.sR R.s:=R.i,XYY,X,A,A.a=f(X.x),Y1,A,A.a=g(f(X.x),Y1.y),Y2,A,A.a=g(g(f(X.x),Y1.y), Y2.y),XYY,A,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),R.s= g(g(f(X.x),Y1.y), Y2.y),R.s= g(g(f(X.x),Y1.y),
26、Y2.y),R.s= g(g(f(X.x),Y1.y), Y2.y),A.a= g(g(f(X.x),Y1.y), Y2.y),构造抽象语法树的属性文法定义转化成翻译模式,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.nptrR E.nptr:=R.sR +T R1.i:=mknode(+,R.i,T.nptr)R1 R.s:=R1.sR T R1.i:=mknode(,R.i,T.n
27、ptr)R1 R.s:=R1.sR R.s:=R.iT (E) T.nptr:=E.nptrT id T.nptr:=mkleaf(id,id.entry)T num T.nptr:=mkleaf(num,num.val),使用继承属性构造a4c的抽象语法树,E,T,R,id,T.nptr,-,T,num,T.nptr,R. i,R,+,T,R,id,T.nptr,R. i,R. i,R. s,R. s,R. s,E.nptr,6.4.3 递归下降翻译器的设计,如何在递归下降分析中实现翻译模式,构造递归下降翻译器 设计递归下降翻译器的方法,6.4.3 递归下降翻译器的设计,设计递归下降翻译器的
28、方法: 1. 对每个非终结符A构造一个函数过程,对A的每个继承属性设置一个形式参数,函数的返回值为A的综合属性(作为记录,或指向记录的一个指针,记录中有若干域,每个属性对应一个域)。为了简单,我们假设每个非终结只有一个综合属性。A对应的函数过程中,为出现在A的产生式中的每一个文法符号的每一个属性都设置一个局部变量。 2. 非终结符A对应的函数过程中,根据当前的输入符号决定使用哪个产生式候选。,6.4.3 递归下降翻译器的设计,设计递归下降翻译器的方法: 3. 每个产生式对应的程序代码中,按照从左到右的次序,对于单词符号(终结符)、非终结符和语义动作分别作以下工作: i) 对于带有综合属性x的终
29、结符X,把x的值存入为X.x设置的变量中。然后产生一个匹配X的调用,并继续读入一个输入符号。 ii) 对于每个非终结符B,产生一个右边带有函数调用的赋值语句c=B(b1,b2,bk),其中,b1,b2,bk是为B的继承属性设置的变量,c是为B的综合属性设置的变量。 iii) 对于语义动作,把动作的代码抄进分析器中,用代表属性的变量来代替对属性的每一次引用。,构造抽象语法树的属性文法定义转化成翻译模式,E T R.i:=T.nptrR E.nptr:=R.sR +T R1.i:=mknode(+,R.i,T.nptr)R1 R.s:=R1.sR T R1.i:=mknode(,R.i,T.npt
30、r)R1 R.s:=R1.sR R.s:=R.iT (E) T.nptr:=E.nptrT id T.nptr:=mkleaf(id,id.entry)T num T.nptr:=mkleaf(num,num.val),递归下降翻译器实现,非终结符E、R、T的函数及其参数类型 function E:AST-node; function R(in:AST-node): AST-node; function T: AST-node;用oddop代表和R oddopT R1.i:=mknode(addop,lexme, R.i,T.nptr)R1 R.s:=R1.sR R.s:=R.i,递归下降翻译
31、器实现,产生式Raddop TR|的分析过程procedare R;beginif sym=addop then beginadvance;T; Rendelse begin /*do nothing*/endend;,function R (in:AST-node): AST-node;var nptr, i1,s1,s: AST-node;addoplexeme: char;beginif sym=addop then begin/*产生式 Raddop TR */addoplexeme:=lexval;advance;nptr:=T;i1:=mknode (addoplexeme, in
32、, nptr);s1:=R (i1)s:=s1endelse s:=in;return send;,6.5 自下而上计算继承属性,在自下而上的分析过程中实现L-属性文法的方法 可实现任何基于LL(1)文法的L-属性文法,还可以实现许多(不是所有)基于LR(1)文法的L-属性文法,6.5.1从翻译模式中去掉嵌入在产生式中间的动作,要求把所有的语义动作都放在产生式的末尾 转换方法,它可以使所有嵌入的动作都出现在产生式的末尾 加入新的产生式M 把嵌入在产生式中的每个语义动作用不同的标记非终结符M代替,并把这个动作放在产生式M的末尾,6.5.1从翻译模式中去掉嵌入在产生式中间的动作,翻译模式 E TR
33、 R T print () R | T print ()R| T num print (num.val)转换为 E TR R TMR | TNR | T num print (num.val) M print () N print (),6.5.2 分析栈中的继承属性,属性位置能预测 L归约时,T恰在右部下面 例: int p, q, r D T L.in := T.typeL T int T. type := integer T real T. type := real L L1.in := L.in L1, id addtype (id.entry, L.in ) L id addtype
34、 (id.entry, L.in ),6.5.2 分析栈中的继承属性,6.5.3 模拟继承属性的计算,属性的位置不能预测S aAC C.i := A.sS bABC C.i := A.sC c C.s := g(C.i)增加标记非终结符S aAC C.i := A.sS bABMC M.i := A.s; C.i := M.sC c C.s := g(C.i)M M.s := M.i,6.5.3 模拟继承属性的计算,继承属性是某个综合属性的一个函数S aAC C.i := f (A.s)C c C.s := g(C.i)增加标记非终结符,把f(A.s)的计算移到对标记非终结符归约时进行。S a
35、ANC N.i := A.s; C.i := N.sN N.s := f (N.i)C c C.s := g(C.i),6.5.4用综合属性代替继承属性,有时,改变基础文法可能避免继承属性。例如,一个Pascal的说明由一标识符序列后跟类型组成,如, m, n: integer。这样的说明的文法可由下面形式的产生式构成 DL:T (左边L的类型属性要继承右边T的类型属性) Tinteger|char LL, id|id 如果非终结符L从第一个产生式中它的右边T中继承了类型,则我们得到的属性文法就不是L属性的,因此,基于这个属性文法的翻译工作不能在语法分析的同时进行。,6.5.4用综合属性代替继
36、承属性,一个解决的方法是重新构造文法 使类型作为标识符表的最后一个元素: Did L L, id L|: T Tinteger|char 改成从右向左归约 这样,类型可以通过综合属性L.type进行传递,当通过L产生每个标识符时,它的类型就可以填入到符号表中。,练习,例1:有文法: S(L)|a LL,S|S给此文法配上语义动作子程序(或者说为此文法写一个语法制导定义),它输出配对括号的个数。如对于句子(a,(a,a),输出是2。 解:加入新开始符号S和产生式SS,设num 为综合属性,代表值属性,则语法制导定义如下:产生式 语义规则SS print(S.num)S(L) S.num:=L.num+1Sa S.num:=0LL1,S L.num:=L1.num+S.numLS L.num:=S.num,练习,例2:构造属性文法,能对下面的文法,只利用综合属性获得类型信息。D L,id | L L T id T int | real 解:属性文法(语法制导)定义:产生式 语义规则D L,id D.type:=L.typeaddtype(id.entry,L.type)D L D.type:=L.typeL T id L.type:=T.typeaddtype(id.entry,T.type)T int T.type:=integerT real T.type:=real,