880 likes | 1.21k Views
语法分析 后的源程序. 语义处理. 中间代码. 第 8 章 语法制导翻译和中间代码生成. §8.1 概述 一、语义处理的任务:. 编译中的语义处理是指两个功能: 第一 静态语义分析或静态审查。. 静态语义分析通常包括: ① 类型检查。 ② 控制流检查。控制流语句必须使控制转移到合法 的地方。 ③ 一致性检查。 ④ 相关名字检查。有时,同一名字必须出现两次或 多次。例如, Ada 语言程序中,循环或程序块可 以有一个名字,出现在这些结构的开头和结尾,
E N D
语法分析 后的源程序 语义处理 中间代码 第8章 语法制导翻译和中间代码生成 §8.1概述 一、语义处理的任务:
编译中的语义处理是指两个功能: 第一 静态语义分析或静态审查。
静态语义分析通常包括: ① 类型检查。 ② 控制流检查。控制流语句必须使控制转移到合法 的地方。 ③ 一致性检查。 ④ 相关名字检查。有时,同一名字必须出现两次或 多次。例如,Ada 语言程序中,循环或程序块可 以有一个名字,出现在这些结构的开头和结尾, 编译程序必须检查这两个地方用 的名字是相同的。 ⑤ 名字的作用域分析
第二 动态语义处理:如果静态语义正确,语义处理则要执行真正的翻译,即,或者将源程序翻译成程序的一种中间表示形式(中间代码),或者将源程序翻译成目标代码。
二、属性文法: 1、属性: 所谓属性,其涉及的概念比较广泛,常用以描述事物或人的特征、性质,品质等等。比如,谈到一个 物体,可以用“颜色”描述它,谈起某人,可以使用“有幽默感“来形容他。对编译程序使用的语法树的结点,可以用"类型"、"值"或"存储位置"来描述它。
2、属性文法(也称属性翻译文法) 它是在上下文无关文法的基础上,为每个文法符号(终结符或非终结符)配备若干相关的"值"(称为属性)。这些属性代表与文法符号相关信息,例如它的类型、值、代码序列、符号表内容等等。 属性与变量一样,可以进行计算和传递。 属性加工的过程即是语义处理的过程。 对于文法的每个产生式都配备了一组属性的计算规则,称为语义规则。
假如语法分析方法是自下而上的。在用某一产生式进行归约的同时就执行相应的语义动作,在分析出一个句子时,这个句子的"值"也就同时产生了假如语法分析方法是自下而上的。在用某一产生式进行归约的同时就执行相应的语义动作,在分析出一个句子时,这个句子的"值"也就同时产生了
3、属性文法的定义: 一个属性文法是一个三元组: 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} 属性文法的主要思想有两点: 首先对于每个文法符号引进相关的属性符号; 其次对于每个产生式写出属性值计算的规则。
属性分为两种:继承属性和综合属性.综合属性:在语法树中,一个结点的综合属性由该结点 的子结点的属性值确定。它的计算规则由底 向上.继承属性:在语法树中,一个结点的继承属性由该结点的 父结点/兄弟结点的属性确定。即它 的计算规 则由顶向下。
例如定义表达式值的属性文法, 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
又例:一个简单台式计算器的定义 综合属性val 产 生 式 语 义 规 则 Print(E.val) L E E.val:=E1.val+T.val E E1+T 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 E.val=19 E.val=15 T.val=4 + T.val=15 F.val=4 F.val=5 T.val=3 * digit.lexval=4 F.val=3 digit.lexval=5 digit.lexval=3 3*5+4的带注释的分析树
产生式 语 义 规 则 D TL L.in:=T.type T int T.type=integer T real T.type:=real L L1,id addtype(id.entry,L.in) L id addtype(id.entry,L.in ) 继承属性 一个结点的继承属性值是由此结点的父结点和/或兄弟结点的某些属性来决定的。例,添加标识符类型的语义描述: 继承属性type L1.in:=L.in
与L产生式相联的语义规则中有一个过程调用addtype,过程addtype的功能是把每个标识符的类型信息登录在符号表的相关项中。与L产生式相联的语义规则中有一个过程调用addtype,过程addtype的功能是把每个标识符的类型信息登录在符号表的相关项中。
句子 int id1,id2的语法树,使用 表示属性的传递情况。
三、中间代码 • 是源程序的一种内部表示 • 复杂性介于源语言和目标机语言之间 中间代码的形式: 逆波兰式、四元式、三元式、间接三元式、 树
逆波兰表示 程序设计语言中的表示 逆波兰表示 a+ba+b*c(a+b)*ca:=b*c+b*d ab+abc*+ ab+c*abc*bd*+:= 1 逆波兰记号 逆波兰记号是最简单的一种中间代码表示形式。 这种表示法将运算对象写在前面,把运算符号写在后面,比如把a+b写成ab+,把a*b写成ab*,用这种表示法表示的表达式也称做后缀式。
后缀表示法表示表达式,其最大的优点是易于计算机处理表达式。常用的算法是使用一个栈,自左至右扫描算术表达式(后缀表示),每扫描到运算对象,就把它推进栈;扫描到运算符,若该运算符是二目的,则对栈顶部的两个运算对象实施该运算,并将运算结果代替这两个运算对象而进栈;若是一目运算符,则对栈顶元素执行该运算,并以运算结果代替该元素进栈,最后的结果留在栈顶。后缀表示法表示表达式,其最大的优点是易于计算机处理表达式。常用的算法是使用一个栈,自左至右扫描算术表达式(后缀表示),每扫描到运算对象,就把它推进栈;扫描到运算符,若该运算符是二目的,则对栈顶部的两个运算对象实施该运算,并将运算结果代替这两个运算对象而进栈;若是一目运算符,则对栈顶元素执行该运算,并以运算结果代替该元素进栈,最后的结果留在栈顶。
例如 B@CD*+(它的中缀表示为-B+C*D,使用@表示一目减)的计值过程为:1. B进栈;2. 对栈顶元素施行一目减运算,并将结果代替栈顶,即-B置于栈顶;3. C进栈; 4. D进栈;5. 栈顶两元素相乘,两元素退栈,相乘结果置栈顶;6. 栈顶两元素相加,两元素退栈,相加结果进栈,现在栈顶存放的是整个表达式的值。
逆波兰表示很容易扩充到表达式以外的范围。 只要遵守运算对象后直接紧跟它们的运算符的规 则即可。 比如把转语句GOTO L写为“L jump ", 运算对象L为语句标号,运算符jump表示转到 某个标号。再比如条件语句if E then S1 else S2 可表示为:ES1S2¥,把if then else看成三目运 算符,用¥来表示。
生成后缀式的属性文法 注释: || 表示代码序列的连接
例:给出下列表达式的逆波兰式: (1) a*(-b+c) (2) a+b*(c+d/e) 解 (1) ab@c+* (2) abcde/+*+ 练习:P188 1
2 三元式和树形表示 另一类中间代码形式是三元式。把表达式及各种语句表示成一组三元式。每个三元式由三个部分组成,分别是:算符op,第一运算对象ARG1和第二运算对象ARG2。运算对象可能是源程序中的变量,也可能是某个三元式的结果,用三元式的编号表示。 例如a∶ =b*c+b*d的表示为:(1)(*, b,c)(2)(*, b,d)(3)(+ (1), (2))(4)(∶= (3), a)
树形表示是三元式表示的翻版。 例:a∶ =b*c+b*d可表示成下面的树表示:
将赋值语句变换为语法结构树 • 基本函数——结点构造 • Mknode(op,left,right) 建标记为op的算符结点,结点有两个域,分别为left和right. • Mkleaf(id,entry) 建标记为id的叶结点,有一个entry域,它是指向符号表的入口。 • Mkleaf(num,val)建标记为id的叶结点,有一个val域,是表示该数的值。
+ - id 指向c的入口 id num 4 指向a 的入口 构造 a-4+c的语法树: (1) p1:=mkleaf(id,entry a); (2) p2:=mkleaf(num, 4); (3) p3:=mknode(‘-’,p1,p2) (4) p4:=mkleaf(id, entry c) (5) p5:=mknode(‘+’,p3,p4)
语法制导定义(属性文法) E.p 是语法结构树指针
四元式和三元式的主要不同在于,四元式对中间结果的引用必须通过给定的名字,而三元式是通过产生中间结果的三元式编号。也就是说,四元式之间的联系是通过临时变量实现的。四元式和三元式的主要不同在于,四元式对中间结果的引用必须通过给定的名字,而三元式是通过产生中间结果的三元式编号。也就是说,四元式之间的联系是通过临时变量实现的。 有时,为了更直观,也把四元式的形式写成简单赋值形式或更易理解的形式。比如把上述四元式序列写成: (1)t1∶=b*c (2)t2∶=b*d (3)t3∶=t1+t2 (4)a∶=t3 把(jump,-,-,L)写成goto L把(jrop,B,C,L)写成if B rop C goto L
请将表达式-(a+b)*(c+d)-(a+b)分别表示成三元式、间接三元式和四元式序列。请将表达式-(a+b)*(c+d)-(a+b)分别表示成三元式、间接三元式和四元式序列。 三元式 (1) (+ a, b) (2) (+ c, d) (3) (* (1), (2)) (4) (- (3), /)(5) (+ a, b)(6) (- (4), (5)) 间接三元式间接三元式序列 间接码表(1) (+ a, b)(1)(2) (+ c, d)(2) (3) (* (1), (2))(3) (4) (- (3), /) (4)(5) (- (4), (1))(1) (5) 四元式(1) (+, a, b, t1)(2) (+, c, d, t2)(3) (*, t1, t2, t3) (4) (-, t3, /, t4) (5) (+, a, b, t5)(6) (-, t4, t5, t6)
实际上,在一个表达式中可能出现各种不同 类型的变量,而不象前面假定为同一类型。 因此,上例应改为P163 图8.10 的形式。
采用语法制导翻译思想,表达式E的"值"的描述如下: 产生式 语义动作 (0) S′→E{print E.VAL}(1) E→E1+E2 {E.VAL∶=E1.VAL+E2.VAL} (2) E→E1*E2{E.VAL∶=E1.VAL*E2.VAL} (3) E→(E1) {E.VAL∶=E1.VAL}(4) E→n {E.VAL∶=n.LEXVAL} 假如终结符n可以是整数或实数,算符+和*的运算对象类型一致,语义处理增加"类型匹配检查",请给出相应的语义描述。
(0) S′→E { if error≠1 then print E.VAL} • E→E1+E2 { if E1.TYPE=int AND E2.TYPE=int thenbeginE.VAL:=E1.VAL + E2.VAL;E.YTPE:=int;end else if E1.TYPE=real AND E2.TYPE=real • then begin E.VAL:=E1.VAL + E2.VAL;E.YTPE:=real; end else error=1 }
(2) E→E1*E2 { if E1.TYPE=int AND E2.TYPE=int then beginE.VAL:=E1.VAL * E2.VAL;; E.YTPE:=int; end else if E1.TYPE=real AND E2.TYPE=real thenbeginE.VAL:=E1.VAL * E2.VAL;; E.YTPE:=real;end else error=1}
(3) E→(E1) { E.VAL:=E1.VAL; E.TYPE:=E1.TYPE } (4) E→n { E.VAL:=n.LEXVAL;E.TYPE:=n.LEXTYPE }
8.3 布尔表达式的翻译 程序设计语言中的布尔表达式有两个作用。 一是计算逻辑值 更多的情况是二:用做改变控制流语句中的 条件表达式,如在if-then,if-then-else, 或是while-do语句中那样。
布尔表达式是由布尔算符and,or和not施于布尔变量或关系表达式而成。布尔表达式是由布尔算符and,or和not施于布尔变量或关系表达式而成。 即布尔表达式的形式为E1 rop E2,其中E1和E2都是算术表达式,rop是关系符,如<=,<,=,>=,≠等等。
布尔表达式的翻译方法 通常,计算布尔表达式的值有两种办法: 第一种办法,如同计算算术表达式一样,步步 计算出各部分的真假值,最后计算出整个表 达式的值。 用数值1表示true,用0表示false。 那么布尔表达式1 or(not 0 and 0)or 0的计算过程是: 1 or(not 0 and 0)or 0 =1 or(1 and 0)or 0 =1 or 0 or 0 =1 or 0 =1
另一种计算方法是采取某种优化措施,只计算部分表达式.另一种计算方法是采取某种优化措施,只计算部分表达式. 例如要计算A or B,若计算出A的值为1,那么B的值就无需再计算了,因为不管B的值为何结果,A or B的值都为1。
控制语句中布尔表达式的翻译 现在讨论出现在 if then;if then else和while do等语句中的布尔表达式E的翻译。 这三种语句的语法为: S→if E then S1|if E then S1 else S2| while E do S1
E的代码 . 。 begin 真 E 的代码 . 。 E的代码 . 。 假 S1的代码 S1 的代码 S1的代码 Jump out Jump begin S2的代码 out 控制语句的代码结构 (a) if E then S1 (b)|if E then S1 else S2 (c)while E do S1
翻译的基本思路:对于E为a rop b,形成代码为: If a rop b goto E.true goto E.false 对于E为 E1 or E2 的形式: 1、E1为真,则E为真,即E的真出口为E1的真 出口。 2、E1为假,计算E2,即E1的假出口应 为E2的第一个四元式标号,E2的真、假出 口与E的真、假出口一样。
我们使用E.true和E.false分别表示整个表达式a<b or c<d and e<f的真、假出口,而E.true和E.false的值并不能在产生四元式的同时就知道。为了看清这一点,我们把该表达式放在条件语句中考虑,如语句 If ( a<b or c<d and e<f )then S1 else S2 的四元式序列见下页:
If ( a<b or c<d and e<f )then S1 else S2 的四元式序列为 (1) ifa<bgoto (7) /*(7)是整个布尔表达式的真出口*/ (2) goto (3) (3) ifc<dgoto (5) (4) goto(p+1) /*(p+1)是整个布尔表达式的假出口*/ (5) ife<fgoto (7) (6) goto (p+1) (7) (关于S1的四元式) … (p) goto (q) (p+1) (关于S2的四元式) … (q)