[编译原理]Stanford斯坦福CS143第四周第七章

本文详细介绍了预测解析的概念,包括LL(1)解析表的构建过程,如FirstSets和FollowSets的定义,以及如何解决左递归和左因子化问题。通过具体的例子展示了如何为给定的文法生成LL(1)解析表,并分析了非LL(1)文法的特征。此外,还简要提到了自底向上的解析方法。
摘要由CSDN通过智能技术生成


预测解析(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)lefttoright,leftmost 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*+()$
ETXTX
X+Eεε
TintY(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]=α有两种情况:
  1. α   − > ∗   t β α\ ->^*\ tβ α > tβ,即α可以在第一个位置生成 t t t,那么就有 t ∈ F i r s t ( α ) t∈First(α) tFirst(α)
  2. 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) tFollow(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(α) tFirst(α),有 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) tFollow(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*+()$
ETXTX
X+Eεε
TintY(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}

ab
Sb, 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)表判断。


自底而上解析

自底向上解析比(确定性的)自顶向下解析更常见。
+


参考资料:
《编译原理》龙书

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值