第六章
6.1属性文法
属性文法:是在上下文无关文法的基础上为每个文法符号(终结符或非终结符)配备若干个相关的“值”(称为属性)。
* 属性代表与文法符号相关的信息,和变量一样,可以进行计算和传递。
* 属性的加工过程即是语义的处理过程。
属性分为两种:继承属性和综合属性。
继承属性:用于“自上而下”传递信息。在语法树中,一个结点的继承属性由此结点的父结点和/或兄弟结点的某些属性确定。
例:继承属性的例子,说明语句的类型信息统计,说明语句的文法。
综合属性:用于“自下而上”传递信息。在语法树中,一个结点的综合属性的值,由其子结点的属性值确定。
S—属性文法:仅仅使用综合属性的属性文法
例:综合属性的例子,考虑如下属性文法,它用作台式计算器程序,对每个 非终结符E、T和F都有一个综合属性——val,是一个
整数值。每个产生式左边的综合属性val都是由右边的计算出来的。
语义规则:属性计算的过程即是语义处理的过程。对于文法的每一个产生式配备一组属性的计算规则,则称为语义规则。
6.2基于属性的文法处理过程
输入串—>语法树—>依赖图—>语义规则计算次序—>计算结果
这种由源程序的语法结构所驱动的处理办法就是语法制导翻译法。
语义规则的计算可能产生代码、在符号表中存放信息、给出错误信息或执行任何其它动作。对输入串的翻译也就是根据语义规则
进行计算得出结果。
依赖图
1. 在一颗语法树中的结点的继承属性和综合属性之间的相互依赖关系可以用称作依赖图的一个有向图来描述。
在为一棵语法树构造依赖图以前,我们为每一个包含过程调用的语义规则引入一个虚综合属性b,这样把每一个语义规则都写成
b:= f(c1,c2, …ck) 。
依赖图中为每一个属性设置一个结点,如果属性b依赖属性c,则从属性c的结点有一条有向边连到属性b的结点。
2.依赖图的构造方法
for分析树中每一个结点n{
for 结点的文法符号的每一个属性a{
为a在依赖图中建立一个结点;
}
}for分析树中每一个结点n{
for结点n所用产生式对应的每一个语义规则 b:=f(c1,c2,…ck){
for i :=1 to k
从ci结点到b结点构造一条有向边}
}
例:产生式 A—>XY的语义规则
(1)A.a:=f(X.x, Y.y)
(2)X.i:=g(A.a,Y.y)
分析:1)若有其语义规则:A.a:=f(X.x,Y.y)。这条语义规则确定了依赖于属性X.x和Y.y的综合属性A.a。
如果在语法树中应用这个产生式,那么在依赖图中会有三个节点A.a X.x Y.y。
由于A.a依赖于X.x,所以有一条有向边从X.x到A.a。由于A.a也依赖于Y.y,所以有一条有向边从Y.y连接到A.a。
2)若产生式A—>XY还有对应的语义规则:X.i:=g(A,a,Y,y)
那么图中还应有两条有向边,一条从A.a连接到 X.i,另一条从Y.y连接到X.i,因为X.i依赖于A.a和Y.y。
注意:
如果一属性文法不存在属性之间的循环依赖关系,那么该文法为良定义的。为了设计编译程序,我们只处理良定义的属性文法。
属性的计算次序:
1) 一个有向非循环图的拓扑序是图中结点的任何顺序m1,m2, …mk,使得边必须是从序列中前面的结点指向后面的结点。也就是
说,如果mi—>mj是mi到mj的一条边,那么在序列中mi必须出现在mj之前。
2)一个依赖图的任何拓扑排序都给出一个语法树中结点的语义规则计算的有效顺序。这就是说,在拓扑排序中,在一个结点
上,语义规则b:=f(c1,c2,…ck)中的属性c1,c2…ck在计算b以前都是可用的。
3)若依赖图中无环,则存在一个拓扑排序,它就是属性值的计算顺序。
4)从依赖图的任何拓扑排序中,我们可以得到计算语义规则的顺序
属性计算方法:
数遍历的属性计算方法:假设语法树已经建立起了,并且树中已带有开始符号的继承属性和终结符的综合属性。然后以某种次序
遍历语法树,直至计算出所有的属性。最常用的遍历方法是深度优先,从左到右的遍历方法。如果需要,可使用多次遍历。
一边扫描的处理方法:与树遍历的属性计算方法不同,一遍扫描的处理方法是在语法分析的同时计算属性值,而不是语法分析构
造语法树之后进行属性的计算,而且无需构造实际的语法树。因为一遍扫描的处理方法与语法分析器的相互作用,它与下面两个
因素密切相关:所采用的语法分析方法和属性的计算次序。
6.3 S-属性文法的自上而下计算
S—属性文法,它只含有综合属性。
* 综合属性可以在分析符号串的同时由自上而下的分析器来构造
* 分析器可以保存与栈中文法符号有关的综合属性值
* 每当进行归约时,新的属性值就由栈中正在归约的产生式右边符号的属性值来计算
* 可以通过扩充分析器中的栈来存放这些综合属性值
* S-属性文法的翻译器通常可借助于LR分析器实现例:
输入 3 * 5 + 4n
6.4 L-属性文法的自顶向下翻译
定义:如果每个产生式A —>X1 X2 … Xn 的每条语义规则计算的属性是A的综合属性;或者是Xj 的继承属性, 1 j n, 但它仅
依赖:
* 该产生式中Xj左边符号X1, X2, …, Xj-1的属性;
* A的继承属性
S属性文法包含于L属性文法。
第七章
中间代码的形成
中间代码:是源程序的一种内部表示,复杂性介于源语言和目标机语言之间。
中间代码的作用:
1)使编译程序的逻辑结构更加简单明确
2)利于进行与目标机无关的优化
3)利于在不同目标机上实现同一种语言
翻译方法:语法制导翻译
中间代码的形式:
1)逆波兰表示
2)图表示法(DAG和抽象语法树)
3)三地址代码(四元式、三元式、简介三元式)
例: 对于语句a:=b*-c+b*-c 的三种表示方法
四元式
三元式
间接三元式
注:四元式出现的顺序和表达式计值顺序一样,但四元式之间的联系与三元式不同(通过指示器),它是通过临时变量,所以改
动四元式很容易,这就为优化提供了方便,因为不牵扯到改变指示器的问题。
逆波兰表示法
波兰表示是一种既不须考虑优先关系、又不用括号的一种表示表达式的方法(前缀式)。
现在我们要介绍的刚好是另一种波兰表示形式,称为后缀式,即运算符在后。
例: a+b → ab+
a*(b+c) → abc+*
-a+b*c → a@bc*+图表示法
抽象语法树。
无循环有向图(DAG)
* DAG与抽象语法树基本上一样,对表达式中的每个子表达式,DAG中都有一个结点。一个内部结点表示一个操作符,它的孩子
表示操作数。
* 两者所不同的是,在一个DAG中代表公共子表达式的结点具有多个父结点,而在一棵抽象语法树中公共子表达式被表示为重复
的子树。如下例:
a:=b*-c+b*-c的图表示法