1、递归下降语法分析(RDP)
我们知道,对于一个CFG而言,不管规则多么复杂,规则集中总是会有非终结符到终结符的简单推导,就像递归中的出口一样。例如:
这样的特点是递归下降法能够顺利执行递归的基本条件。
RDP做的事情就是把每个非终结符看作是函数,从这个非终结符推导出的规则是函数体。例如:
可以看到,函数体内部的书写范式是:
case 终结符{eat(终结符);处理下一个;}
RDP是一种预测分析,预测的意思是:接下来要做什么可以通过case后面那个终结符知道。如果一条规则中,->后面的非终结符是不明确的,RDP就失效了,例如:
A->A+B
A->A-B
A->a
B->b
函数A应写作:
void A(void){
switch(tok){
case ?:A();eat(+);B();
case ?:A();eat(-);B();
case a:eat(a)
}
}
2、Nullable、FIRST和FOLLOW
为方便后面的讨论(例如,将递归下降法表示成一张表),引入三个定义:
(1)Nullable的定义:
若非终结符A可以推导出空串,则Nullable(A)为真,表示A是"可空的"。
(2)FIRST的定义:
FIRST(A)表示非终结符A推导的规则中,->后面的第一个终结符;
FIRST(ABC...)=FIRST(A)∪FIRST(B)∪FIRST(C)∪...,如果ABC是可空的。
(3)FOLLOW的定义:
对于非终结符A,如果在任意推导中出现At,其中t是终结符,那么t∈FOLLOW(A),如果是ABCt这种情况,但BC是可空的,那么也有t∈FOLLOW(A)
例如:
Nullable |
FIRST |
FOLLOW |
|
X |
yes |
a c |
a c d |
Y |
yes |
c |
a c d |
Z |
no |
a c d |
虽然可以通过观察法找到答案但是形式化的计算更不容易出错,在知道了
Nullable以后,我们完全可以从定义出发,列式计算,例如:因为X,Y都是可空的,则FIRST(Z)={d}∪FIRST(X)∪FIRST(Y)。
计算FOLLOW同样有一些捷径,例如X的FOLLOW,因为观察到规则集中有XYZ,那么Y的FIRST一定是X的FOLLOW的子集,又因为Y是可空的,所以Z的FIRST也一定是X的FOLLOW的子集,于是可毫不费力地写出{a c d}
3、构造分析预测表
由此可以得到2中文法对应的表:
A |
c |
d |
|
X |
X->a X->Y |
X->Y |
X->Y |
Y |
Y-> |
Y-> Y->c |
Y-> |