《五章语法制导的翻译.ppt》由会员分享,可在线阅读,更多相关《五章语法制导的翻译.ppt(64页珍藏版)》请在三一办公上搜索。
1、第五章 语法制导的翻译,赵建华南京大学计算机系2010年3月,介绍,使用上下文无关文法引导语言的翻译CFG的非终结符号代表了语言的某个构造程序设计语言的构造由更小的构造组合而成一个构造的语义可以由小构造的含义综合而来比如:表达式x+y的类型由x、y的类型和运算符+决定。也可以从附近的构造继承而来比如:声明int x;中x的类型由它左边的类型表达式决定。,语法制导定义和语法制导翻译,语法制导定义:将文法符号和某些属性相关联,并通过语义规则来描述如何计算属性的值EE1+TE.code=E1.code|T.code|+属性code代表中缀表达式的逆波兰表示,规则说明加法表达式的逆波兰表示由两个分量的
2、逆波兰表示并置,然后加上+得到。语法制导翻译:在产生式体中加入语义动作,并在适当的时候执行这些语义动作EE1+Tprint+;,语法制导的定义(SDD),SDD是上下文无关文法和属性/规则的结合;属性和文法符号相关联,按照需要来确定各个文法符号需要哪些属性规则和产生式相关联对于文法符号X和属性a,我们用X.a表示分析树中的某个标号为X的结点的值。一个分析树结点和它的分支对应于一个产生式规则,而对应的语义规则确定了这些结点上的属性的取值。,分析树和属性值(1),假设我们需要知道一个表达式的类型,以及对应代码将它的值存放在何处,我们就需要两个属性:type,place;产生式规则:EE1+T 语义
3、规则:(假设只有int/float类型)E.type=if(E1.type=T.type)T.type else floatE.place=newTempPlace();/返回一个新的内存位置;产生式规则:FidF.type=lookupIDTable(id.lexValue)-type;F.place=lookupIDTable(id.lexValue)-address;,分析树和属性值(2),a+b*c的语法分析树以及属性值,E,E,T,+,T,F,id,T,F,*,F,id,id,id.lexValue=a,F.Type=FLOATF.Place=&a,T.Type=FLOATT.Pla
4、ce=&a,E.Type=FLOATE.Place=&a,T.Type=INTT.Place=&tmp,F.Type=INTF.Place=&c,id.lexValue=c,id.lexValue=b,假设a,b,c是已经声明的全局变量,a的类型为FLOAT,b,c的类型为INT中间未标明的T和F的type和address都是INT和,E.Type=FLOATE.Place=&tmp2,继承属性和综合属性,综合属性(synthesized attribute):在分析树结点N上的非终结符号A的属性值由N对应的产生式所关联的语义规则来定义。通过N的子结点或N本身的属性值来定义继承属性(inher
5、ited attribute):结点N的属性值由N的父结点所关联的语义规则来定义。依赖于N的父结点、N本身和N的兄弟结点上的属性值。不允许N的继承属性通过N的子结点上的属性来定义,但是允许N的综合属性依赖于N本身的继承属性。终结符号有综合属性(由词法分析获得),但是没有继承属性。,SDD的例子,目标:计算表达式行L的值(属性val)计算L的val值需要E的val值E的val值又依赖于E和T的val值终结符号digit有综合属性lexval。,S属性的SDD,只包含综合属性的SDD称为S属性的SDD。每个语义规则都根据产生式体中的属性值来计算头部非终结符号的属性值。S属性的SDD可以和LR语法分
6、析器一起实现栈中的状态可以附加相应的属性值在进行归约时,按照语义规则计算归约得到的符号的属性值。语义规则不应该有复杂的副作用要求副作用不影响其它属性的求值没有副作用的SDD称为属性文法。,语法分析树上的SDD求值(1),实践中很少先构造语法分析树再进行SDD求值但在分析树上求值有助于翻译方案的可视化,便于理解。注释语法分析树包含了各个结点的各属性值的语法分析树步骤:对于任意的输入串,首先构造出相应的分析树。给各个结点(根据其文法符号)加上相应的属性值按照语义规则计算这些属性值即可,语法分析树上的SDD求值(2),按照分析树中的分支对应的文法产生式,应用相应的语义规则计算属性值计算顺序问题:如果
7、某个结点N的属性a为f(N1.b1,N2.b2,Nk.bk),那么我们需要先算出N1.b1,N2.b2,Nk.bk的值。如果我们可以给各个属性值排出计算顺序,那么这个注释分析树就可以计算得到。S属性的SDD一定可以按照自底向上的方式求值。下面的SDD不能计算ABA.s=B.i;B.i=A.s+1;,注释分析树的例子,适用于自顶向下分析的SDD,前面的表达式文法存在直接左递归,因此无法直接用自顶向下方法处理。消除左递归之后,我们无法直接使用属性val进行处理:比如规则:TFTT*FTT对应的项中,第一个因子对应于F,而运算符却在T中。需要继承属性来完成这样的计算,相同表达式的不同文法的比较,输入
8、串:3*4*5请观察左边的T对应的部分,和右边的T对应部分Ti和Ti恰好互补计算方法:把T之外部分的值继承给T。,T3,F,*,digit:4,digit:5,T2,T1,F,digit:3,F,*,T,F,*,T3,F,F,*,digit:3,digit:4,T2,T1,digit:5,适用于自顶向下分析的SDD,注意:T的属性inh实际上继承了相应的*号的左运算分量。,3*5的注释分析树,请观察inh属性是如何传递的。,消直接左递归时语义规则的处理,假设:AA1YA.a=g(A1.a,Y.a)AXA.a=f(X.x)那么A XRR.i=f(X.x);A.a=R.sR YR1R1.i=g(R
9、.i,Y.y);R.s=R1.sR R.s=R.i新文法中R对应的部分和原文法中A对应的部分互补;对于AXY1Y2Yn;如果R对应于YYiYn;互补的A对应于AY1Yi-1R.i等于互补的A的A.s,SDD的求值顺序,在对SDD的求值过程中,如果结点N的属性a依赖于结点M1的属性a1,M2的属性a2,。那么我们必须先计算出Mi的属性,才能计算N的属性a。使用依赖图来表示计算顺序。显然,这些值的计算顺序应该形成一个偏序关系。如果依赖图中出现了环,表示属性值无法计算,依赖图,描述了某棵特定的分析树上各个属性实例之间的信息流(计算顺序)从实例a1到实例a2的有向边表示计算a2时需要a1的值。(必须先
10、计算a2,再计算a1)对于标号为X的分析树结点N,和X关联的每个属性a都对应依赖图的一个结点N.a。结点N对应的产生式的语义规则通过X.c计算了A.b的值,且在分析树中X和A分别对应于N1和N2,那么从N1.c到N2.b有一条边。N1和N2可以等于/不等于N。,依赖图的例子,3*2的注释分析树;TFT T.val=T.syn;T.inh=F.val;边e1、e2。可能的计算顺序:1,2,3,4,5,6,7,8.91,3,5,2,4,6,7,8,9,属性值的计算顺序,各个属性的值需要按照依赖图的拓扑顺序计算。如果依赖图中存在环,则属性计算无法进行。给定一个SDD,很难判定是否存在一棵分析树,其对
11、应的依赖图包含环。但是特定类型的SDD一定不包含环,且有固定的排序模式S属性的SDDL属性的SDD对于这些类型的SDD,我们可以确定属性的计算顺序,且可以把不需要的属性(及分析树结点)抛弃以提高效率,S属性的SDD,每个属性都是综合属性都是根据子构造的属性计算出父构造的属性。在依赖图中,总是通过子结点的属性值来计算父结点的属性值。可以和自顶向下、自底向上的语法分析过程一起计算自底向上:在构造分析树的结点的同时计算相关的属性(此时其子结点的属性必然已经计算完毕)自顶向下:递归子程序法中,在过程A()的最后计算A的属性(此时A调用的其他过程(对应于子结构)已经调用完毕),在分析树上计算SDD,按照
12、后序遍历的顺序计算属性值即可postorder(N)for(从左边开始,对N的每个子结点C)postorder(c);/递归调用返回时,各子结点的属性计算完毕对N的各个属性求值;在LR分析过程中,我们实际上不需要构造分析树的结点。,L属性的SDD,每个属性要么是综合属性,要么是继承属性,且产生式AX1X2Xn中计算Xi.a的规则只能使用A的继承属性Xi左边的文法符号Xj的继承属性或综合属性。Xi自身的继承或综合属性。且这些属性之间的依赖关系不形成环。特点:依赖图的边:继承属性从左到右,从上到下。综合属性从下到上在扫描过程中,计算一个属性值时,和它相关的依赖属性都已经计算完毕。,L属性SDD和自
13、顶向下语法分析,在递归子程序法中实现L属性对于每个非终结符号A,其对应的过程的参数为继承属性,返回值为综合属性在处理规则AX1X2Xn时,在调用Xi()之前计算Xi的继承属性值,然后以它们为参数调用Xi();在产生式对应代码的最后计算A的综合属性注意:如果所有的文法符号的属性计算按上面的方式进行,计算顺序必然和依赖关系一致。,L属性SDD的例子,非L属性的例子:ABCA.s=B.b;B.i=f(C.c,A.s),各子程序的类型,intT();intT1(int inh);intF();,考虑一下:如果有多个继承属性/多个综合属性是如何处理,递归子程序法中实现L属性SDD,int T()if(c
14、urToken=digit)/digit是first(FT)中唯一的符号/处理规则TFT。intfval=F();/F的综合属性Value;intt1inh=fval;/计算T的继承属性intt1syn=T1(t1inh);/计算得到T的综合属性inttval=t1syn;/计算得到T的综合属性returntval;/返回T的综合属性elseerror();/报错,注意:if(curToken=)肯定不对,因为不是一个符号,对规则中某个文法符号X的处理:1、计算X的所有继承属性的值2、用这些值调用子函数X;返回值保存在某个变量中。,具有受控副作用的语义规则,属性文法没有副作用,但增加了描述的复
15、杂度比如语法分析时如果没有副作用,标识符表就必须作为属性传递。可以把标识符表作为全局变量,然后通过副作用函数来添加新标识符;受控的副作用不会对属性求值产生约束,即可以按照任何拓扑顺序求值,不会影响最终结果。或者对求值过程添加简单的约束。,受控副作用的例子,LE nprint(E.val)通过副作用打印出E的值总是在最后执行,而且不会影响其它属性的求值变量声明的SDD中的副作用addType将标识符的类型信息加入到标识符表中。只要标识符不被重复声明,标识符的类型信息总是正确的。,语法制导翻译的应用例子,抽象语法树的构造基本类型和数组类型的L属性定义,构造抽象语法树的SDD,抽象语法树每个结点代表
16、一个语法结构;对应于一个运算符;结点的每个子结点代表其子结构;对应于运算分量;表示这些子结构按照特定方式组成了较大的结构。可以忽略掉一些标点符号等非本质的东西。语法树的表示方法每个结点用一个对象表示对象有多个域叶子结点中只存放词法值;内部结点中存放了op值和参数(通常指向其它结点);,构造简单表达式的语法树的SDD,属性E.node指向E对应的语法树的根结点;,表达式语法树的构造过程,输入:a-4+c步骤:p1=new Leaf(id,entry_a)p2=new Leaf(num,4);p3=new Node(-,p1,p2);p4=new Leaf(id,entry_c);p5=new N
17、ode(+,p3,p4);,自顶向下方式处理的L属性定义(1),在消左递归时,按照规则得到此SDD,自顶向下方式处理的L属性定义(2),对于这个SDD,各属性值的计算过程实际上和原来S属性定义中的计算过程一致。继承属性可以把值从一个结构传递到另一个并列的结构;也可把值从父结构传递到子结构。抽象语法树和分析树不一致时,继承属性很有用。,类型结构,简化的类型表达式的语法TB CBint|floatCnumC|生成类型表达式的SDD,类型的含义,类型包括两个部分:TB C基本类型B分量C分量形如34表示3X4的二维数组int34数组构造算符arrayarray(3,array(4,int)表示抽象的
18、3X4的二维数组,类型表达式的生成过程,输入:int 23,语法制导的翻译方案,语法制导的翻译方案(SDT)是在产生式体中嵌入程序片断(语义动作)的上下文无关文法SDT的基本实现方法:建立语法分析树;将语义动作看作是虚拟的结点;从左到右、深度优先地遍历分析树,在访问虚拟结点时执行相应动作用SDT实现两类重要的SDD基本文法是LR的,SDD是S属性的基本文法是LL的,SDD是L属性的,例子,语句3*4*5的分析树如右DFS可知动作执行顺序A71,A5,A72,A41,A73,A42,A2注意,一个动作的不同实例所访问的属性值属于不同的结点,T3,F,*,digit:4,digit:5,T2,T1
19、,F,digit:3,F,*,E,A7,A71,A72,A5,A41,A42,A2,A73,可在语法分析过程中实现的SDT,实现SDT时,实际上并不会真的构造语法分析树,而是在分析过程中执行语义动作即使基础文法可以应用某种分析技术,仍可能因为动作的缘故导致此技术不可应用判断是否可在分析过程中实现将每个语义动作替换为一个独有的标记非终结符号;每个标记非终结符号M的产生式为M。如果新的文法可以由某种方法进行分析,那么这个SDT就可以在这个分析过程中实现。注意:这个方法没有考虑变量值的传递等要求。,判断SDT可否用特定分析技术实现例子,LE n M1M1EE+T M2M2ET M3M3,后缀翻译方案
20、,文法可以自底向上分析且SDD是S属性的,比然可以构造出后缀SDT后缀SDT:所有动作都在产生式最右端的SDT构造方法:将每个语义规则看作是一个赋值语义动作将所有的语义动作放在规则的最右端,后缀翻译方案的例子,实现桌上计算器的后缀SDT,注意动作中对属性值的引用:我们允许语句引用全局变量,局部变量,文法符号的属性。文法符号的属性只能被赋值一次;,后缀SDT的语法分析栈实现,可以在LR语法分析的过程中实现归约时执行相应的语义动作定义用于记录各文法符号的属性的union结构栈中的每个文法符号(或者说状态)都附带一个这样的union类型的值;在按照产生式AXYZ归约时,Z的属性可以在栈顶找到,Y的属
21、性可以在下一个位置找到,X的属性可以在再下一个位置找到。,分析栈实现的例子,假设语法分析栈存放在一个被称为stack的记录数组中,下标top指向栈顶;stacktop是这个栈的栈顶;stacktop-1指向栈顶下一个位置;如果不同的文法符号有不同的属性集合,我们可以使用union来保存这些属性值。归约时能够知道栈顶向下的各个符号分别是什么;因此我们也能够确定各个union中究竟存放了什么样的值,后缀SDT的栈实现,这个SDT中没有局部变量,不会产生和局部变量有关的问题,注意:stacktop-i和文法符号的对应,产生式内部带有语义动作的SDT,动作左边的所有符号(以及动作)处理完成后,就立刻执
22、行这个动作BXaY自底向上分析时,在X出现在栈顶时执行动作a自顶向下分析时,在试图展开Y或者在输入中检测到Y的时刻执行a不是所有的SDT都可以在分析过程中实现但是后缀SDT以及L属性对应的SDT可以在分析时完成。对于一般的SDT,都可以先建立分析树(语义动作作为虚拟的结点),然后进行前序遍历并执行动作。,消左递归时SDT的转换,如果动作不涉及属性值,可以把动作当作终结符号进行处理,然后消左递归原始的产生式EE1+T print(+);ET转换后得到E T RR+T print(+);RR,L属性的SDT,从L属性到SDT的转换将每个语义规则看作是一个赋值语义动作将赋值语义动作放到相应产生式的适
23、当位置计算A的继承属性的动作插入到产生式体中对应的A的左边如果A的继承属性之间具有依赖关系,则需要对计算动作进行排序计算产生式头的综合属性的动作在产生式的最右边。,L属性的SDT的例子,SDD继承属性:next:语句结束后应该跳转到的标号true、false:C为真/假时应该跳转到的标号综合属性code表示代码,转换,语义动作(a)L1=new()和L2=new():计算临时值(b)C.false=S.next;C.true=L2:计算C的继承属性(c)S1.next=L1:计算S1的继承属性(d)S.code=:计算S的综合属型根据放置语义动作的规则得到如下SDTb在C之前;c在S1之前;d
24、在最右端a可以放在最前面,L属性的SDD的实现,使用递归下降的语法分析器每个非终结符号对应一个函数函数的参数接受继承属性返回值包含了综合属性在函数体中首先选择适当的产生式使用局部变量来保存属性对于产生式体中的终结符号,读入符号并获取其(经词法分析得到的)综合属性对于非终结符号,使用适当的方式调用相应函数,并记录返回值。,递归下降法实现L属性SDD的例子,边扫描边生成属性(1),当属性值的体积很大时,对属性值进行运算的效率很低比如code(代码)可能是一个上百K的串,对其进行并置等运算会比较低效可以逐步生成属性的各个部分,并增量式添加到最终的属性值中条件:存在一个主属性,且主属性是综合属性在各产
25、生式中,主属性是通过产生式体中各个非终结符号的主属性连接(并置)得到的。同时还会连接一些其它的元素。各非终结符号的主属性的连接顺序和它在产生式体中的顺序相同,边扫描边生成属性(2),基本思想只需要在适当的时候“发出”非主属性的元素,即把这些元素拼接到适当的地方举例说明假设我们在扫描一个非终结符号对应的语法结构时,调用相应的函数,并生成主属性;S while(C)S1 S.code=Label|L1|C.code|Label|L2|S1.code处理S时,先调用C,再调用S(对应于S1)如果各个函数把主属性打印出来,我们处理while语句时,只需要先打印Label L1,再调用C(打印了C的代码
26、),再打印Label L2,再调用S(打印S1的代码)对于这个规则而言,只需要打印Label L1和Label L2。当然,我们要求C和S的语句在相应情况下跳转到L1和L2。,Swhile(L1=new();L2=new();C.false=S.next;C.true=L2;print(“label”,L1);C)S1.next=L1;print(“label”,L2);S1前提是所有的非终结符号的SDT规则都这么做,边扫描边生成属性的例子,L属性的自底向上实现(1),以LL文法为基础的L属性SDD可以在LR语法分析过程中实现方法:首先构造出L属性SDD的SDT,即在非终结符号前计算其继承属性
27、对于A的规则中的语义动作a,引入标记非终结符号MMa,其中a的构造方法如下:将a中需要的A或者其它属性作为M的继承属性进行拷贝按照a中的规则计算各个属性,作为M的综合属性但是:a必须设法找到相应的属性,因为产生式M中没有A等符号。,L属性的自底向上实现(2),AB.i=f(A.i);B C引入标记非终结符号M后A M B CMM.i=A.i;M.s=f(M.i);如何找到A.i?设法使得在即将把BC归约到A时,A的继承属性存放在分析栈中BC的下方。当执行到M的归约时,A.i的值存放在M的下方。(如果产生式体为K M B C,那么M的下方为K,K的下方存放A.i)M.s即B.i,存放在M所在的位
28、置,即将来归约到B时,B.i存放在归约位置的下方。,自底向上实现L属性SDD的例子,SDT转换后得到Swhile(M C)N S1M N 按照此产生式归约时S.next位于栈中右部的下方C的继承属性true、false位于栈中紧靠C的下方S1的next紧靠S1的下方,即N的栈记录中。S和S1的综合属性code出现在相应的栈记录中。,自底向上实现L属性SDD的例子(2),stacktop-3访问了S.nextC.true、C.false存放在M的记录中,自底向上实现L属性SDD的例子(3),S1.next被存放在N的栈记录中,它恰巧存放于S1的栈记录之下S1.next=stacktop-3.L1,