自顶向下分析法就是从文法的开始符号出发,不断建立直接推导,试图构造一个最左推导序列,最终由它推导出与输入符号串完全匹配(相同)的句子。
从语法树的角度看,自顶向下分析法就是以开始符号为根节点,试图向下构造一棵语法树,其端末结符号串与输入符号串相同。
能使用自顶向下分析技术的文法正是LL(1)文法.
LL(1)文法
LL(1)的含义:第1个L表明自顶向下分析是从左向右扫描输入串,第2个L表明分析过程中将用最左到推倒,1表明只需向右看一个符号便可决定如何推倒即选择哪个产生式(规则)进行推导,类似也可以有LL(k)文法,也就是需要向前查看k个符号才能确定选用哪个产生式。
若文法G[S]为
S->AB
S->bC
A->ε
A->b
B->ε
B->aD
C->AD
C->b
D->aS
D->c
判断别步骤
1.求出能推出ε的非终结符
非终结符 | S | A | B | C | D |
---|---|---|---|---|---|
初值 | - | - | - | - | - |
第一次扫描 | - | y | y | - | n |
第二次扫描 | y | y | y | n | n |
2.求first集,非终结符第一个的终结符集合
非终结符 | 中间集 | 最终集 |
---|---|---|
S | first(A)-{ε} U first(B) -{ε} U {b} U {ε} | {ε, b,a} |
A | {ε, b} | {ε, b} |
B | {ε, a} | {ε, a} |
C | first(A)-{ε} U first(D) U {b} | {a, b,c} |
D | {a,c} | {a,c} |
3.求follow集,非终结符跟着的第一个的终结符集合
(1) 对于文法的开始符号 S,置 # 到 FOLLOW(S) 中;
(2)若 A -> αBaβ 是一个产生式,a 为终结符,则把 a加至 FOLLOW(B) 中;
(3)若 A -> αBβ 是一个产生式,则把 FIRST(β) - {ε} 加至 FOLLOW(B) 中;
(4)若 A -> αB 是一个产生式,或 A -> αBβ 是一个产生式,而 β =*> ε, 则把 FOLLOW(A) 加至FOLLOW(B) 中
非终结符 | 规则 | 中间集 | 最终集 |
---|---|---|---|
S | (1),(4 D->aS) | {#} U follow(D) | {#} |
A | (3,4 S->AB), (3 C->AD) | first(B)-{ε} U follow(S) U first(D) | {a,#,c} |
B | (4 S->AB) | follow(S) | {#} |
C | (4 S->bC) | follow(S) | {#} |
D | (4 B->aD),(4 C->AD) | follow(B) U follow© | {#} |
4.求select集,每个式子对应的终结符
产生式 | 中间集 | 最终集 |
---|---|---|
S->AB | fisrt(AB) - {ε} U follow(S) | {a,b,#} |
S-bC | first(bC) | {b} |
A->ε | fisrt(ε) - {ε} U follow(A) | {a,#,c} |
A->b | fisrt(b) | {b} |
B->ε | fisrt(ε) - {ε} U follow(B) | {#} |
B->aD | fisrt(aD) | {a} |
C->AD | fisrt(AD) - {ε} | {a,b,c} |
C->b | fisrt(b) | {b} |
D->aS | fisrt(aS) | {a} |
D->c | fisrt© | {c} |
LL(1)文法的判别,从上表格最终集中得出
select(S->AB) ∩ select(S->bC) = {b} 不为空,当终结符为S时,讲入符号为b时,不知道选用哪个式子
select(A->ε) ∩ select(A->b) = {}为空
select(B->ε) ∩ select(B->aD)= {}为空
select(C->AD) ∩ select(C->b)= {b}不为空,同上
select(D->aS) ∩ select(D->c)= {}为空
从非LL(1)文法到LL(1)文法的等价转换
由LL(1)文法定义可知,若方法中有直接或间接左递归或者有左公共因子则肯定不是LL(1)文法,因而我们高潮消除文法中的左递归,撮左公共因子进行等价转换。
问题1:左递归问题
若采用自顶向下的语法分析,应消除文法中存在的左递归。
因为左递归的存在,有可能使推导不能结束,分析陷入循环状态。 例如:
A → Aβ (A∈VN , β∈VT*) 直接左递归
A → Bβ B->Aα (A,B∈VN , α,β∈VT*) 间接左递归
左递归还比较好理解,例如:我需要匹配的字符串是 bsd 就需要从左端 b 开始是被,推导时,先用 A -> Aa 推导 AAa,此时不符合条件,但因为还有非终结符,不能结束,而是继续推导。此时就会陷入死循环。
- 消除直接左递归
P→Pα1 / Pα2 /…/ Pαn / β1 / β2 /…/βm
其中,αi(I=1,2,…,n)都不为ε,而每个βj(j=1,2,…,m)都不以P开头,将上述规则改写为如下形式即可消除P的直接左递归:
P→β1 P’ / β2 P’ /…/βm P’
P’→α1P’ / α2 P’ /…/ αn P’ /ε
例:
S->Sa
S->b
可以改写为
S->bS’
S’->aS’|ε
- 消除间接左递归
消除间接左递归的方法是,把间接左递归文法改写为直接左递归文法,然后用消除直接左递归的方法改写文法。
消除左递归算法:
(1) 把文法G的所有非终结符按任一顺序排列,例如,A1,A2,…,An。
(2) for (i=1;i<=n;i++){
for (j=1;j<=i-1;j++){
把形如Ai→Ajγ的产生式改写成Ai→δ1γ /δ2γ /…/δkγ
其中Aj→δ1 /δ2 /…/δk是关于的Aj全部规则;
消除Ai规则中的直接左递归;
}
(3) 化简由(2)所得到的文法,即去掉多余的规则。
例:
A->Ba
A->aB
B->Ac
B->d
首先,令非终结符的排序为A,B。对于A,不存在直接左递归。把A代入到B中的相关规则中得
B->Bbc
B->d|aBc
B存在直接左递归。在消除了B的直接左递归后,得到整个文法为:
B->(aBc|d)B’
B->bcB’|ε
最终得:
A->Ba
A->aB
B->(aBc|d)B’
B->bcB’|ε
问题2:左公共因子问题
若采用自顶向下的语法分析,应消除文法中存在的左公因子。
因为左公因子的存在,使式子select存在不为空集的情况如
A → αβ | αγ
select(A → αβ) ∩ select(A → αγ) 不为空
提取公因式
A→αβ1 |αβ2|αβ3|…|αβn,提取左公因子后变为
A->α(β1|β2|β3|…|βn),再引出非终结符A’
A->αA’
A’->β1|β2|β3|…|βn
例:
S->aSb
S->aS
S->ε
提取左公因式
S->aS(b|ε)
S->ε
进一步变换
S->aSA
A->b|ε
S->ε