一、L属性的自上而下计算
1、边分析边计算
由于属性的计算次序受分析方法所限定的分析树节点建立次序(自左向右的)的限制,所以,只有当属性是自左向右流动时才能边分析边计算
2、L属性定义
(1) A—>X1X2...Xn
每条规则计算的属性是A的综合属性或者Xj的继承属性
而且Xj的继承属性只依赖于父节点和左兄弟节点
(2)S属性定义属于L属性定义
(3)非L属性定义:1.父节点依赖于子节点,2.某节点依赖于右兄弟节点
3、翻译方案
(1)区别于L属性和S属性的就是执行的时机
因为S属性是将产生式作为一个整体来看
而L属性定义与属性嵌入的位置有关
(2)定义:给出了使用语义规则进行计算的次序,体现实现细节
(3)表示:使用{ }
语法树
(4)启发方式
a、产生式右边的符号的继承属性必须先于这个符号的动作
b、一个动作不能引用这个动作右边的符号的综合属性
c、产生式左边非终结符的综合属性放在右边的末尾
这两条在写翻译方案的时候经常要注意到
(5)消除左递归
自上而下分析需要消除左递归,引起了继承属性
4、设计递归下降翻译器
每个继承属性:设置一个形参
每个综合属性:获取一个函数的返回值
每个属性都有一个局部变量
5、使用综合属性代替继承属性(改写文法)
这个方法的重点是,要所有的符号定义完了才表示出类型
二、L属性的自下而上计算
1、区别于自上而下计算
S属性的自上而下计算局限于LL(1)文法
L属性的自下而上计算基于任何LL(1)文法和许多LR(1)文法
两种特殊情况:
2、删除翻译方案中嵌入的动作
可以使用M—>ε解决问题
比如
修改为
3、分析栈上的继承属性
即:所依赖的属性在分析栈上的位置能静态确定(知道其相对位置)
对于不能确定的属性也可以通过修改文法来确定
4、模拟继承属性的计算
当规则不是简单的复写规则(如函数)时
方法:增加一个非终结符,将函数的计算移到该非终结符归约时进行
从而保证:每次使用继承属性时,刚好都在文法符号的正下方
原文法:
S——>aAC C.i = f(A.s) (这里C.i不等于复写规则)
C——>c C.c = g(C.i)
改写为:
S——>aANC C.i = N.s, N.i = A.s
N——>ε N.s = f(N.i)
C——>c C.c = g(C.i)
5、引进非终结符对基础文法的影响
1、基础文法是LL(1)文法
无影响:标记非终结符是唯一的,而且只有唯一一个的ε产生式
任何LL(1)文法一定是LR(1)的所以不会引起冲突
2、基础文法是LR(1)文法
可能使修改后的文法变成非LR(1)文法
eg. L—>Lb|a
修改为:L—>MLb|a
M—>ε
本来这个文法产生 abbbb...串
修改后,空归约和b的个数一样,在面临a后,后面有多少个b是不知道的
因此会产生移进-归约冲突
假设栈中有a,b此时不能判断是移进还是归约
在a进入栈之前,栈中是否应该已经有一个M?(造成了冲突)