对于一个文法,当给你一串终结符号时,怎样知道它是不是该文法的一个句子呢?在自上而下的语法分析里,就是要判断能否从文法的开始符号推导出这个输入串。语法分析这一部分在编译过程中承前启后,是核心部分。
自上而下分析就是对输入串通过试探,反复使用不同产生式谋求匹配输入串,为的是从文法开始符号(根节点)出发,自上而下的为输入串建立一棵语法树,即为输入串寻找一个最左推导。
在这种推导过程中,因为反复的试探,不对就回去重来,存在很多缺点:一是文法的左递归性,会使分析陷入无限循环。二是回溯,会导致白走很多错路。三是虚假匹配,像X**Y,比如把第一个*匹配了,但发现还有第二个*,其实是不能匹配的,就无法识别X**Y是一个句子。第四,如果出错也难以知道确切位置。
当然,我们要讨论的重点是解决了一些上述问题的LL(1)分析方法。
一、 左递归的消除
P是非终结符,这就是左递归的形式。
按这个方法扩展一下,假定P关于的全部产生式是P->Pα1|Pα2|...|Pαm|β1|β2|β3
这里的α、β不一定是终结符,只是式子的代称,每个α都不等于空字,每个β都不以P开头。那么消除P的直接左递归性就是改写为:
这个方法只能消除直接左递归,如果是间接左递归,要先变成直接左递归。
如 S->Qc|c
Q->Rb|b
R->Sa|a
含有隐含左递归,把R代入到Q的有关候选之后,我们把Q的规则变为Q->Sab|ab|b
再把Q代入S,得S->Sabc|abc|bc|c这样就可以按直接左递归处理了。
二、消除回溯
为了消除回溯就必须保证匹配不再是试探,假如非终结符A->α1|α1|...|αn,它面临的输入符号为a,这里A不再是让某个αi去试探匹配,而是根据所面临的输入符号的不同准确制定一个αi去匹配,若匹配到最后没能识别整个字串,则该字串一定不是该文法中的句子。αi的工作成败完全代表了A。
在不得回溯的前提下,文法不能有左递归。
令G是一个不含左递归的文法,对G的所有非终结符的每个候选α定义它的终结首符集FIRST(α)为
A→δβ1|δβ2|…|δβn|γ1| γ2|…|γm 其中每个γ不以δ开头
那么把这些产生式改写为:
A→δA’ |γ1| γ2|…|γm
A’→β1|β2|…|βn
工作做到这里还有一个问题:如果非终结符A面临符号1a,且a不属于A的任意候选首符集怎么办?我们可以这样,如果A的某个候选首符集包含空字,让它和A自动匹配,其条件是a是允许在文法的某个句型中跟在A后面的终结符。怎么表示这个条件呢?这里我们引入FOLLOW集:

特别是如果,则#∈FOLLOW(A),即FOLLOW(A)是所有句型中出现在紧接A之后的终结符或“#”。
三,LL(1)文法
(2)对于文法中每一个非终结符A的各个产生式的候选式的FIRST集两两不相交。即,若
A→α1|α2|…|αn
则 FIRST(αi)∩FIRST(αj)=Φ (i≠j)
(3)对于文法中的每个非终结符A,若它的某个候选首符集包含ε,则
FIRST(A)∩FOLLOW(A)=Φ
如果一个文法G满足以上条件,则称该文法G为LL(1)文法
A→α1|α2|…|αn
(2)若a不属于任何一个候选首符集,则:
①若ε属于某个 FIRST(αi)且a∈FOLLOW(A),则让A与ε自动匹配;
②否则,a的出现是一种语法错误
四,预测分析程序
预测分析表

有如下预测分析表:
构造FIRST集
3,若X->Y…是一个产生式,且Y为非终结符,则把FIRST (Y)-ε加入到FIRST(X)中;
若X->Y1Y2Y3….YK,是产生式, Y1Y2Y3….Yi-1是非终结符,而且ε属于 FIRST (Yj)(1<=j<=i-1),则把FIRST (Yj)-ε加入到FIRST(X)中;如果ε属于所有的FIRST (Yj),则ε加入到FIRST(X)中
构造FOLLOW集
1, 对于文法的开始符,置#于FOLLOW(S)中,
2,若A->αBβ, 则把FIRST (β)-ε加入到FOLLOW(B)中,
3,若A->αB 是一个产生式,或 A->αBβ是一个产生式,而β-> ε,则把FOLLOW(A)加入到FOLLOW(B)中
对上述文法:
从E开始,由规则1,得FOLLOW(E)={#},又F->(E)|i,由规则2,FOLLOW(E)={#, )}
对于E‘,由规则3和E->TE',得应将FOWLLOW(E)加入FOLLOW(E')中,FOLLOW(E’)={#, )}
对于T,有E'->+TE'|ε,由规则2,应将FIRST(E')-{ε}加到FOLLOW(T)中,FOLLOW(T)={+},又由规则3和E'->+TE',由于FIRST(E')包含 ε,应把FOLLOW(E')加入到FOLLOW(T)中,FOLLOW(T)={+, ), #}
对于T',由T->FT'和规则3,应把FOLLOW(T)包含到FOLLOW(T'),FOLLOW(T’)= FOLLOW(T)= {+,),#}
对于F,由T'->*FT'|ε和规则3,应把FOLLOW(T')包含到FOLLOW(F),同时由规则2,应把FIRST(T')-{ε}包含到FOLLOW(F),FOLLOW(F)={*, + , ), #}
对上述文法,还要知道FIRST集合不光是对单个非终结符,对符号串也能构造FIRST集,规则是:对符号串α=X1X2...Xn,首先置FIRST(α)=FIRST(X1)\{ε };
若对任何1<=j<=i-1,ε ∈FIRST(Xj),则把FIRST(Xi)\{ε }加入到FIRST(α);
特别是,若所有的FIRST(Xj)均含有ε,1<=j<=n,则把,ε 也加至FIRST(α);
显然,若α=ε则FIRST(α)={ε}
对上述文法,我们总结一下成果:
文法 E→TE’
E’→+TE’|ε
T→FT’
T’→*FT’|ε
F→(E)|i
FIRST(F)={ (,i }
FIRST(T’)={*,ε}
FIRST(T)={ (,i}
FIRST(E’)={+,ε}
FIRST(E)= {(,i}
FOLLOW(E)={#, )}
FOLLOW(E’)={#, )}
FOLLOW(T)={+, ), #}
FOLLOW(T’)= {+,),#}
FOLLOW(F)={*, + , ), #}
下面我们求符号串的FIRST集:
FIRST(TE’)=FIRST(T)-{ε}={(,i}
FIRST(+TE’)={+}
FIRST(FT’)= FIRST(F)-{ε}={(,i}
FIRST(*FT’)={*}
FIRST((E))={(}
FIRST(ε)={ε}
FIRST(i)={i}
到此,就可以构造预测分析表了,规则如下:对文法G的每个产生式A->α:对每个终结符a,如果a属于FIRST(α),则把该产生式写入到M[A,a];
若ε属于FIRST(α),则对任何b属于FOLLOW(A), 把该产生式加入到M[A,b]
对 E→TE’,因为FIRST(TE’)={(,i} ,则把该式写进M[E,i]和M[E,(]
对 E’→+TE’|ε ,因为FIRST(+TE’)={+} ,FIRST(ε)={ε} ,FOLLOW(E’)={#, )}则把E’→+TE’写进M[E',+];把E'->ε 写进M[E',)]和M[E',#]
对T→FT’,因为FIRST(FT’)={(,i} ,则把T→FT’写进M[T,(]和M[T,i]
对T’→*FT’|ε ,因为FIRST(*FT’)={*} ,FIRST(ε)={ε} ,FOLLOW(T’)= {+,),#} 则把T’→*FT’写进M[T',*],;把T'->ε 写进M[T',+],M[T',)]和M[T',#]
对 F→(E)|i,因为FIRST((E))={(} ,FIRST(i)={i},则把F->(E)写进M[F,(],把F->i写进M[F,i]
这样就构造了如上的预测分析表。
栈用于存放文法符号,栈底有一个‘#’,输入串之后也有一个'#'
预测分析程序任何时候都是按栈顶符号X和当前的输入符号a行事的。
分析程序按以下步骤:
(1)若X=a=#,分析成功,停止。E匹配输入串成功.
(2)若X=a≠#,把X推出栈,再读入下一个符号。
(3)若X∈Vn,查分析表M。
a) M[X,a]= X→UVW 则将X弹出栈,按WVU顺序进栈
注:U在栈顶 (最左推导)
b) M[X, a] = error 转出错处理
c) M[X, a] = X-〉ε ---a为X的后继符号
则将X弹出栈 (不读下一符号) 继续分析。
步骤 符号栈 读入符号 剩余符号串 使用规则
1. #E i +i*i#
2. #E’T i +i*i# E->TE’
3. #E’T’F i +i*i# T->FT’
8. #E’T i *i#
9. #E’T’F i *i # T->FT’
10. #E’T’ i i *i# F-> i
10. #E’T’ i i * i# F-> i
11. #E’T’ * i#
17. # # E’->ε



