编译原理之ll(1)文法判断和左递归问题
LL(1)文法名称中第一个L表示自顶向下分析是从左向右扫描输入串,
第二个L表示分析过程中将用最左推导,
括号中的1表示每进行一步推导,只需要向前查看一个输入符号便能确定当前所应选用的产生式。
文法G是LL(1)的,当且仅当对于G的每个非终结符Α的任何两个不同产生式
Α→α,Α→β均满足下面条件(其中α和β不能同时推出ε):
1、FIRST(α)∩FIRST(β)=Φ
2、假若β=>*ε,那么FIRST(α)∩FOLLOW(A)=Φ
SELECT集的定义
对于上下文无关文法产生式Α→α, A∈VN ,α∈V∗,
1、若α!⇒∗ε,则SELECT(Α→α)=FIRST(α)
2、若α⇒∗ε,则SELECT(Α→α)=(FIRST(α)-{ε})∪FOLLOW(Α)
上下文无关文法是LL(1)文法的充要条件可以描述为:
对每个非终结符A的两个不同产生式Α→α,Α→β
满足(SELECT(Α→α)∩SELECT(Α→β)=Φ
例:
- S→aA
- S→d
- A→bAS
- A→ε
Select(S→aA)={a};
Select(S→d)={d};
Select(A→bAS)={b};
Select( A→ε}={a, d, #};
Select(S→aA)∩ Select(S→d)=Φ
Select(A→bAS)∩ Select(A→ε)=Φ
故文法G是LL(1)文法。
判断步骤:
- 求出能推出ε的非终结符
- 计算FIRST集
- 计算FOLLOW集
- 计算SELECT集
- 根据LL(1)文法的等价条件进行判断
判断ll(1)文法的算法:
1 求出能推出ε的非终结符:首先建立一个以文法的非终结符个数为上界的一维数组,记录对应非终结符能否推出ε.
计算能推出ε的非终结符步骤如下:
- 将数组X[ ]中对应每一非终结符的标记置初值为“未定”。
- 扫描文法中的产生式。
- 删除所有右部含有终结符的产生式,若这使得以某一非终结符为左部的所有产生式都被删除,则将数组中对应该非终结符的标记值改为“否”,说明该非终结符不能推出ε。
- 若某一非终结符的某一产生式右部为ε ,则将数组中对应该非终结符的标志置为“是”,并从文法中删除该非终结符的所有产生式。
- 扫描产生式右部的每一符号
- 若所扫描到的非终结符号在数组中对应的标志是“是”则删去该非终结符,若这使产生式右部为空,则对产生式左部的非终结符在数组中对应的标志改为“是”,并删除该非终结符为左部的所有产生式。
- 若所扫描到的非终结符号在数组中对应的标志是“否”,则删去该产生式,若这使产生式左部非终结符的有关产生式都被删去,则把在数组中该非终结符对应的标志改成“否”。
- 重复3,直到扫描完一遍文法的产生式,数组中非终结符对应的特征再没有改变为止。
2 计算FIRST集
3 计算FOLLOW集
4 计算SELECT集
5 分析是否为ll(1)文法
当文法中含有左递归或左公共因子时,该文法肯定不是LL(1)文法。
提取左公共因子:
当文法中出现形如A→aβ|aγ的产生式,此时若对A进行语法分析,将会无法区分应该选择哪个分支,也就是出现了局部二义性问题。
解决方法:将产生式A→aβ, A→aγ变换为A→aB,B→β|γ
对文法提取左公共因子后,有可能产生无用产生式,所以需对文法进行重新化简。
也可能存在一些文法不能在有限步内提取完公共因子。
文法提取了左公共因子后,只解决了相同左部产生式右部的FIRST集不相交的问题,只有当改写后的文法不含空产生式,且无左递归时,改写后的文法才是LL(1)文法,否则还要用LL(1)文法的判别方法进行判断。
消除左递归:
- 直接左递归: A→Aβ,其中A ∈VN, β∈V*。
- 间接左递归: A→Bβ, B→ Aα,其中A,B ∈ VN, α,β∈V*
- 一般左递归: A⇒*Aα,其中A ∈ VN, α∈V*
消除直接左递归: 把直接左递归改为右递归
形如:P→Pα|β 其中α非ε, α,β不以P开头
改为:P→βQ,Q→αQ|ε
消除间接左递归的方法:
首先通过非终结符置换,变为直接左递归,然后再消除。
消除文法中一切左递归的方法:
(1) 以某种顺序将文法非终结符排列A1 ,A2 …An
(2)
for i:=1 to n do
begin
for j:=1 to i-1 do
begin
用Ai→α1r|α2r…|αkr 对形如Ai→Ajr的规则进行替代,
其中Aj→α1|α2…|αk是关于Aj的全部产生式;
end
消除Ai规则的直接左递归;
end;
(3) 化简由(2)得到的文法
消除一切左递归后要求文法满足:
- 无回路(A⇒+A)
- 无空产生式
对非终结符号的排序不同,最后得到的文法在形式上可能不同,但它们都是等价文法。消去左递归过程中,要注意保证文法的识别符号不变。