例2:有如下文法:
1) E->TE'
2) E'->+TE'
3) E'->c
4) T->FT'
5) T'->*FT'
6) T'->c
7) F->(E)
8) F->i
计算的各个非终结符的Follow集。
Follow(E)={ #,) } 开始符号得到#,(7)产生式右侧得到)
Follow(E')=Follow(E) 看左右,E'位于产生式最后,
Follow(T)={+} ???
自上而下的语法分析方法就是对任何输入串(由token串构成的源程序),试图用一切可能的办法,从文法开始符号(根结点)出发,自上而下的为输入串建立一棵语法树,或者说,为输入串寻找一个最左推导。
即当文法中存在形如A->αβ1|αβ2......的产生式,即某个非终结符存在多个候选式的前缀相同(也称为公共左因子),则可能造成虚假匹配(即当前的匹配可能是暂时的),即发现不能匹配后重新回溯重新选择一个候选式进行匹配,这样反复多次,使得在分析过程中可能需要大量的回溯。
即当文法中存在形如A->αβ1|αβ2......的产生式,即某个非终结符存在多个候选式的前缀相同(也称为公共左因子),则可能造成虚假匹配(即当前的匹配可能是暂时的),即发现不能匹配后重新回溯重新选择一个候选式进行匹配,这样反复多次,使得在分析过程中可能需要大量的回溯。
因此,自上而下的语法分析要消除回溯和左递归
回溯产生的根本原因在于某个非终结符的多个候选式存在公共左因子,如非终结符A的产生式如下:A->αβ1|αβ2
如果输入串中待分析的字前缀也为α,此时选择A的那个候选式就会不确定,可能就导致回溯。因此,要想进行确定的分析,必须保证文法G的每个非终结符的多个候选式均不包含公共左因子。
1>改造方法:
改造的方法是提取公共左因子。使得文法的每个非终结符号的各个候选式的首终结符集两两不相交。
2>样例:
例1:条件(if)语句的文法有两个候选式如下:
上面存在问题:当输入的符号是if时,就不能立刻确定选用那个候选式,提取公共因子后:
P -> else S2
例2:对A→δα1|δα2|…|δαn,提取公共左因子后为:
A->δA'
A'->α1|α2|…|αn
简而言之,就是将相同的产生式前缀提取出来,将不同的后缀作为一个新的产生式,加到后面
左递归包括直接左递归和间接左递归。
直接左递归 |
若文法G中有形如A->Aα的产生式,则称该产生式对A直接左递归。 |
间接左递归 | 若文法G中没有形如A->Aα的产生式,但是A经过有限步骤推导可以得到A->Aα,则称该产生式 对A间接左递归。 |
1>直接左递归的消除
原理上是把左递归变为右递归。做法没啥好说的,直接背公式就完了
假设文法中A的产生式如下:
A->Aα1|Aα2|Aα3|......|Aαm|β1|β2|β3......|βn
则消除直接左递归如下所示:
A->β1A'|β2A'|......|βnA'
A->α1A'|α2A'|......|αmA'|c
2>间接左递归的消除
首先要找到递归环:如
S->Ac|c
A->Bb|b
B->Sa|a
存在推导:S->Ac->Bbc->Sabc
然后依次带入,将其转换为直接左递归即可,然后消除直接左递归就可以了
对于文法如下消除左递归:
S->Ac|c
A->Bb|b
B->Sa|a
1)代入:
将产生式B->Sa|a代入A->Bb|b,有A->(Sa|a)b|b,即A->Sab|ab|b
将产生式A->Sab|ab|b代入A->Ac|c,有S->(Sab|ab|b)c|c,即S->Sabc|abc|bc|c
2)消除S的直接左递归,得到:
S->abcS'|bcS'|cS'
S'->abcS'|c
A->Bb|b
B->Sa|a
如下:
有文法如下所示:
S->Ac|Be
A->db|b
B->da|a
当输入字符串dbc,要求用S的候选式匹配d时,虽然S的两个候选式没有公共左因子,
但仍不能准确地选取S的候选式,不能进行确定的分析。
现在我们要解决不同产生式产生相同前缀的问题。
First(α)是文法G的符号串α的首终结符集,即由该候选式推导出的所有符号串中的第一个终结符或可能的c(空字)的集合,其中α可能是文法符号、c或者候选式,或候选式的一部分。定义为:First(α) = {a|α=>a....,a∈Vt},其计算的方式如下所示:
1)若产生式形如A->aα,则First(A->aα)={a};
2)若产生式形如A->c,则First(A->c)={c};
3)产生式形如A->Xα,则把First(X)中非c元素加入到First(A->Xα)中去
4)若有产生式形如A->X1X2X3...Xkα,则有
1.当X1X2X3...Xi=>c(有限步骤,1<=i<=k)时,
则把First(Xi+1....Xk)的所有非c元素加入到First(A->X1X2X3...Xkα)中
2.当X1X2X3...Xk=>c(有限步骤)时,则把First(α)加入到First(A->X1X2X3...Xkα)中
简单点来说:
若文法符号A为终结符(A∈Vt),则First(A)={A} |
若文法符号为非终结符A(A∈Vn),求First集的方法就是将非终结符A的每个候选式的First集都加入到First(A)中。 |
例题:
求下述各个候选式和非终结符的First集
E->TE'
E'->+TE'|c
T->FT'
T'->*FT'|c
F->(E)|i
-------------------------------
各个候选式的First集:
First(E->TE') = First(T) = First(F) = {(,i}
First(E'->+TE') = {+}
First(E'->c) = {c}
First(T->FT') = First(F) = {(,i)}
First(T'->*FT') = {*}
First(T'->c} = {c}
First(F->(E)) = {(}
First(F->i) = {i}
各个非终结符的First集:
First(E) = First(T) = First(F) = {(,i}
First(E') = {+,c}
First(T) = {(,i}
First(T') = {*,c}
First(F) = {(,i }
其实,First集还是比较好找的,下面的Follow集相比之下会困难一点,大家要多加练习,不要脑测。
表示非终结符A的后随符号集Follow(A),定义如下:
假设S是文法G的开始符号,对G的任何非终结符A:Follow(A)={a|S=>...Aa...,a∈Vt}
若S=>...A,则规定#∈Follow(A),
通俗来讲就是Follow(A)就是所有句型中出现在紧接A之后的终结符号或者#,#表示输入符号串的结束标记。
其算法如下:
输入:文法G
输出:非终结符A的Follow集
步骤:
1)如果A是开始符号,则#∈Follow(A)。
2)如果有产生式B->αAaβ,a∈Vt,把a加入到Follow(A)中。
3)如果有产生式B->αAXβ,X∈Vn,把First(Xβ)中非a元素加入到Follow(A)中。
4)如果B->αA,或者B->αAβ且β=>a,把Follow(B)加入到Follow(A)中。
5)对每一个非终结符,浏览每个产生式,连续使用上述规则,
直到A的Follow集不再增大为止。
案例如下:
例1:有如下文法:
S->Ap
S->Bq
A->a
A->cA
B->b
B->dB
则Follow集如下所示:
Follow(S)={#}(规则1,因为是开始符号)
Follow(A)={p}(规则2)
Follow(B)={q}(规则2)
可能看到这里还是云里雾里,我们不妨再看一道题
例2:有如下文法:
1) E->TE'
2) E'->+TE'
3) E'->c
4) T->FT'
5) T'->*FT'
6) T'->c
7) F->(E)
8) F->i
计算的各个非终结符的Follow集。
Follow(E)={#,)} //根据开始符(规则一),和产生式(7)(规则二)
Follow(E')=Follow{E} // ???
Follow(T)={+}+First(E')(规则三)={+,#,)}
Follow(T')=Follow(T)= {+,#,)}
Follow(F)=First(T')+Follow(T)
用人话讲:
计算所有非终结符号A的follow(A)集合时,不断应用下面的规则,直到再没有新的终结符号可以被加入到任意的follow集合中为止。
注意:当A是最右部的时候,将$加入到follow(A)中
(1)将 $ 放到follow(S)中,其中S是文法的开始符号。
(2)如果存在一个产生式A→αBβ,那么first(β)中除ε之外的所有符号都在follow(B)中。 【 follow(B)是求跟在B后的终结符或$组成的集合,因此对于跟在B后的β,它的first集合就是follow(B)的子集 】
(3)如果存在一个产生式A→αB,或存在产生式A→αBβ且first(β)包含ε,那么follow(A)中的所有符号都在follow(B)中。 【 对于A→αBβ,且β多步推导出ε ,那么可以用αB替换A, B后面紧跟的字符就是A后面紧跟的字符】
接下来,我们把上面那道题再做一遍:
例2:有如下文法:
1) E->TE'
2) E'->+TE'
3) E'->c
4) T->FT'
5) T'->*FT'
6) T'->c
7) F->(E)
8) F->i
计算的各个非终结符的Follow集。
Follow(E)={#,)} //根据开始符(规则一),和产生式(7)右侧有终止符
Follow(E')=Follow{E}
Follow(T)=follow(E')+(First(E')-c)={+,#,)}
Follow(T')=Follow(T)= {+,#,)}
Follow(F)=First(T')+Follow(T)
1.检查是否含有左递归
2.如果没有左递归,则计算其First集,判别其First集是否相交,即是否有回溯
3.若存在某个A=>ε,求A的 Follow集,并判别条件 (3) 是否满足(是否可以使用ε-产生式进行匹配)