在前一章分析了LL(1)文法是什么并且也知道了如何判断一个文法是不是LL(1)文法。那么我们肯定是很喜欢一个文法是一个LL(1)文法的,但是现实条件是无论是生活中还是工作中我们喜欢的往往是我们所无法得到的。
那么我们是不是可以将一些不是LL(1)文法的东西转换成LL(1)文法呢。
这里有两种方式:提取左公因子和消除左递归
(1)提取左公因子
含有左公共因子的文法
若文法中含有形如:
A→
α
β|
α
r
的产生式,称
文法含有左公共因子
显然
,
SELECT(A→αβ)∩SELECT(A→αr)
≠
Ø
, 文法不是
LL(1)
文法(很显然他们的select集合至少都含有α)
提取左公因子基本思想
通过改写产生式来推迟决定,读入足够多的输
入,获得足够的信息之后,再做出正确判断。
提取左公因子
有左因子的文法 A→αβ1 | αβ2 等价于 A →A’( β1 | β2)
提左因子 A →αA’ A→ β1 | β2
一般定义:
A→δβ1 |δβ 2 |…|δβ n |γ 1 |γ 2 |…|γ m (每个 γ不 以 δ 开头)
可改写为 A→δA’|γ1 |γ 2 |…|γ m A’→β1 |β 2 |…|β n
当然左公因子这个是有两种情况的,一种是显式的可以让我们直接看出来,还有一种隐式的不能直接看出来。下面举个例子就可以明白了
例4.7 文法 G[A]
A →
ad | Bc
B →
aA | bB
还有隐含左公因子
改写后
G[A] :A →
ad | aAc | bBc
B →
aA | bB
该文法不是LL(1)文法。
提公因子后
A →
aA’| bBc
A’ →
d| Ac
B →
aA | bB
提取左公因子之后的
文法是LL(1)文法
经过提取左公因子之后,有些文法从非
LL(1)
文法
变为
LL(1)
文法,有的仍不是
LL(1)
文法。因此,文
法不
含左公共因子只是
LL(1)
文法的必要条件而非充要条件
。
经过提取左公因子之后,有时会使某些规则变为多
余规则
,故必须检查提取左公因子之后
的文法,去除多余规则
2
消除左递归
含有左递归的文法
若文法中含有形如
a)
A
→
A
β(A
∈
V
N
, β∈
(V)*)
的规则
b)
A
→Bβ B→
A
α
的规则
那么
在
a)
中含有左递归的规则,称为
直接左递归
在
b)
中虽然没有含左递归的规则,
但
A=>
Bβ
=>
Aα
β
即
A
=>
+
A
…,
称为
间接左递归
为什么含有左递归的文法不是
LL(1)
文法
含有左递归的文法不是一个LL(1)文法证明:
以直接左递归为例,若有如下产生式
A→A α | A→β
其中 , α 和β 为任意语法符号串。
不难证明有下面关系式:
First( β )⊆ First( A α )⊆ Select( A→A α)
First( β )⊆ Select( A→ β )
由于 A→A α 和 A→ β 的 Select 集相交,故含有左递归的文法不是 LL(1) 文法
消除直接左递归一般定义
P →
Pα
1
|Pα
2
|…|Pα
m
|β
1
|β
2
|…|β
n
其中每个
α
均不等于
ε
,每个
β不
以
P
开头,则:
P→
β
1
P’|β
2
P’|…|β
n
P’
P’→
α
1
P’|α
2
P’|…|α
m
P’|ε
例:
S→
Sc | Sabc | abc | bc | c
取消其左递归
答案:
S →
abcS’ | bcS’| cS’
S’→
cS’ | abcS’ | ε
这种办法只能消除直接左递归而无法消除间接左递归当我们遇到间接左递归的时候我们需要首先将间接左递归转换成直接左递归
然后按照消除直接左递归的方式进行求解即可
消除左递归之后,有些文法从非
LL(1)
文法变为
LL(1)
文法,有的仍不是
LL(1)
文法。因此,文法不
含左公共因子只是
LL(1)
文法的必要条件而非充要条件
消除左递归之后,有时会使某些规则变为多余规则,
故必须检查消除左递归之后的文法,去除多余规则
消除左递归的文法在形式上可能不同,但均等价。
表驱动的LL(1)预测分析程序
系统维持一个
分析表
和一个
分析栈
,根据当
前扫描到的符号,选择当前
语法变量
(处于
栈顶)的候选式进行推导——希望找到相应
的输入符号串的最左推导。
一个通用的
控制算法
一个分析栈,#为栈底符号
一个输入缓冲区,#为输入串结束符
一个统一形式的分析表M
不同语言使用内容不同的分析表
即我们可以实现一个表来实现根据表来进行识别句子的时候利用这个表来进行一点点的判断实现,这个表告诉我们下一步该怎么办。
LL(1)分析法的原理
1)初始化分析栈,将
#
和
开始符号
分别入栈,输入指针
指向输入串的第一个字符。
2)比较当前分析栈栈顶符号与当前输入符号:
如果栈顶符号为非终结符号,则到LL(1)分析表中查
找
(X,a)
,若存在
可用产生式
,则使用该产生式替换栈
顶符号,转到(2);若为错误信息,则输出错误信息,
结束分析。
如果栈顶符号为
终结符号
,且不
输入符号
相同,则分
析栈弹出栈顶符号,输入指针
读入下一个输入符号
,
转到(2) ;
3)如果栈顶号为
#
且当前输入符号也为
#
,则分析成功,
结束分析
构造预测分析表
把文法转变为
LL(1)
文法
求出每条产生式的
SELECT
集
依照
SELECT
集把产生式填入分析表
列
——
终结符与‘#’
行
——
非终结符
交点
M[A,a]
(A→β
)
放入
M[A,a]
——
若
a∈
SELECT(A→β
)
无产生式的
M[A,a]
——
标记出错
依照选择集合把产生式填入分析表 例子:
通过这个就可以求出了预测分析表然后当我们输入一个句子进去后就进行判断了
利用分析表,预测分析器接受输入
i+i*i#
的步骤


通过上述分析:
i+i*i是文法的句子,其所代表的源程序是
语法上正确的源程序。该文法是一个表达式文法,该句子
是语法上正确的表达式。