预测解析(Predictive Parsing)
预测解析有些类似递归下降解析但是通过以下方法可以预测使用规则中的哪一个Production:
- 通过观察后面几个tokens(Look ahead,只用于有限的语法)
- 不回退
预测解析支持 L L ( k ) ( l e f t − t o − r i g h t , l e f t − m o s t d e r i v a t i o n , k t o k e n s l o o k a h e a d ) LL(k)(left-to-right,left-most \ derivation,k\ tokens\ lookahead) LL(k)(left−to−right,left−most derivation,k tokens lookahead)语法。(我们只讨论 k = 1 k=1 k=1的情况)
在 L L ( K ) LL(K) LL(K)中:
- 每一步只有一种Production可以选择(对于语法的要求)
考虑我们前面见过的语法:
E − > T ∣ T + E E\ ->\ T\ |\ T\ +\ E E −> T ∣ T + E
T − > i n t ∣ i n t ∗ T ∣ ( E ) T\ ->\ int\ |\ int\ *\ T\ |\ (\ E\ ) T −> int ∣ int ∗ T ∣ ( E )
这个语法很难用于预测解析,因为:
- T T T有两个Production是以 i n t int int开始的
- E E E的预测不明确
所以我们需要 l e f t f a c t o r left\ factor left factor这个语法,即删除一个规则的相同前缀。对于上面的语法修改,可以将匹配决策推迟:
E − > T X E\ ->\ TX E −> TX
X − > + E ∣ ε X\ ->\ +E\ |\ ε X −> +E ∣ ε
T − > i n t Y ∣ ( E ) T\ ->\ intY\ |\ (E) T −> intY ∣ (E)
Y − > ε ∣ ∗ T Y\ ->\ ε\ |\ *T Y −> ε ∣ ∗T
解析表
根据上面left parsing之后的语法,可以生成一个 L L ( 1 ) LL(1) LL(1)的解析表:
int | * | + | ( | ) | $ | |
---|---|---|---|---|---|---|
E | TX | TX | ||||
X | +E | ε | ε | |||
T | intY | (E) | ||||
Y | *T | ε | ε | ε |
最左边一列是当前解析树中的非终端;第一行是下一个输入令牌;表格内的数据是当前终端遇到下一个令牌所采用的Production;表格中的空项都是表示错误输入。
- 先找最左的非终端 S S S
- 看下一个令牌 a a a
- 查看表选择Production表示的是 [ S , a ] [S,a] [S,a]
需要用一个堆栈保存解析树的边界:
- 还没有扩展的非终端
- 和输入匹配不成功的非终端
- 栈顶=最左将要匹配的终端或非终端
结束条件:
- 当遇到错误状态时,拒绝,结束
- 当输入结束并且栈为空,接收,结束
算法伪码:
initialize stack = <S $> and next //这里S是起始符号,$不是合法语法,而是标识输入结束,next初始指向当前字符串的第一个元素
repeat
case stack of
<X, rest> : //栈顶是一个非终端
if T[X, *next] = Y1...Yn //查看解析表T
then stack <- <Y1...Yn rest>; //将X取出堆栈,换成Y1...Yn(Y1成为新栈顶)
else error();
<t, rest> : //栈顶是一个终端
if t == *next++
then stack <- <rest>;
else error();
until stack == <>
举例:
First Sets
下面考虑如何构建解析表:
- 考虑一个非终端 A A A,有Production A − > α A\ ->\ α A −> α和输入的token t t t。
- 则 T [ A . t ] = α T[A.t]=α T[A.t]=α有两种情况:
- 有 α − > ∗ t β α\ ->^*\ tβ α −>∗ tβ,即α可以在第一个位置生成 t t t,那么就有 t ∈ F i r s t ( α ) t∈First(α) t∈First(α)、
- 有 A − > α , α − > ∗ ε , S − > ∗ β A t σ A\ ->\ α,\ α\ ->^*ε,\ S\ ->^*βAtσ A −> α, α −>∗ε, S −>∗βAtσ。这个情况适用于堆栈中有 A A A,输入为 t t t,并且 A A A不能演化成 t t t(即 t t t不输入 F i r s t ( α ) First(α) First(α)),这种情况下就是要消去 A A A(通过生成ε),但是这只适用于在至少一个演化后, t t t可以出现在 A A A后面,我们称 t ∈ F o l l o w ( A ) t∈Follow(A) t∈Follow(A)。
定义
举例:
还是考虑如下语法:
E − > T X E\ ->\ TX E −> TX
X − > + E ∣ ε X\ ->\ +E\ |\ ε X −> +E ∣ ε
T − > i n t Y ∣ ( E ) T\ ->\ intY\ |\ (E) T −> intY ∣ (E)
Y − > ε ∣ ∗ T Y\ ->\ ε\ |\ *T Y −> ε ∣ ∗T
左边是终端的 F i r s t S e t s First\ Sets First Sets,右边是非终端的。
Follow Sets
定义
-
算法框架:
-
举例:
还是考虑这个语法:
E − > T X E\ ->\ TX E −> TX
X − > + E ∣ ε X\ ->\ +E\ |\ ε X −> +E ∣ ε
T − > i n t Y ∣ ( E ) T\ ->\ intY\ |\ (E) T −> intY ∣ (E)
Y − > ε ∣ ∗ T Y\ ->\ ε\ |\ *T Y −> ε ∣ ∗T
分析:
- 从第一个可以推断出 F o l l w ( X ) Follw(X) Follw(X)是 F o l l o w ( E ) Follow(E) Follow(E)的子集,而从第而个可以推断出 F o l l w ( E ) Follw(E) Follw(E)是 F o l l o w ( X ) Follow(X) Follow(X)的子集,可以得出 F o l l o w ( E ) = F o l l o w ( X ) Follow(E)=Follow(X) Follow(E)=Follow(X)。
- 结合1、2可以得出 F o l l o w ( E ) Follow(E) Follow(E)是 F o l l o w ( T ) Follow(T) Follow(T)的子集。
- 从第四个可以得到 F o l l o w ( Y ) Follow(Y) Follow(Y)是 F o l l o w ( T ) Follow(T) Follow(T)的子集。从第三个可以得出 F o l l o w ( T ) Follow(T) Follow(T)是 F o l l o w ( Y ) Follow(Y) Follow(Y)的子集,因此 F o l l o w ( Y ) = F o l l o w ( T ) Follow(Y)=Follow(T) Follow(Y)=Follow(T)。
所以结果:
构建LL(1)解析表
目标:为上下文无关语法 G G G建立一个解析表 T T T。
对于 G G G中的每一个产生式 A − > α A\ ->\ α A −> α:
- 对与每一个终端 t ∈ F i r s t ( α ) t∈First(α) t∈First(α),有 T [ A , t ] = α T[A,t]\ =\ α T[A,t] = α。
- 如果 ε ∈ F i r s t ( α ) ε∈First(α) ε∈First(α),那么对于所有 t ∈ F o l l o w ( A ) t∈Follow(A) t∈Follow(A),有 T [ A , t ] = α T[A,t]\ =\ α T[A,t] = α。
- 如果 ε ∈ F i r s t ( α ) ε∈First(α) ε∈First(α)并且$ ε ∈ F o l l o w ( A ) ε∈Follow(A) ε∈Follow(A),那么有 T [ A , T[A, T[A, $ ] = α ]\ =\ α ] = α。
举例:
还是考虑如下语法:
E − > T X E\ ->\ TX E −> TX
X − > + E ∣ ε X\ ->\ +E\ |\ ε X −> +E ∣ ε
T − > i n t Y ∣ ( E ) T\ ->\ intY\ |\ (E) T −> intY ∣ (E)
Y − > ε ∣ ∗ T Y\ ->\ ε\ |\ *T Y −> ε ∣ ∗T
那么解析表有:
int | * | + | ( | ) | $ | |
---|---|---|---|---|---|---|
E | TX | TX | ||||
X | +E | ε | ε | |||
T | intY | (E) | ||||
Y | *T | ε | ε | ε |
接下来我们考虑为不是 L L ( 1 ) LL(1) LL(1)的语法构造一个 L L ( 1 ) LL(1) LL(1)表的情形:
语法:
S -> Sa | b
分析:
First(S) = {b}
Follow(S) = {$, a}
a | b | |
---|---|---|
S | b, Sa |
所以我们看到问题,某个表项有不止一个元素,被多次定义了。在构建 L L ( 1 ) LL(1) LL(1)表时,如果某个表项有多个元素,那么语法生成不是唯一的,则说明这个语法不是 L L ( 1 ) LL(1) LL(1)。
非 L L ( 1 ) LL(1) LL(1)的特点有:
- 不可左分解
- 左递归
- 不清晰(有歧义)
- 其他不是 L L ( 1 ) LL(1) LL(1)的语法
但是如果不满足上面的前三个条件,也不能说明是 L L ( 1 ) LL(1) LL(1),最终还是需要建立 L L ( 1 ) LL(1) LL(1)表判断。
自底而上解析
自底向上解析比(确定性的)自顶向下解析更常见。
+
参考资料:
《编译原理》龙书