LL1 文法分析的实现
EmilMatthew ([email protected]) 06/07/1 9
[ 类别 ]课程设计
[推荐指数]★★★
[ 摘要 ]实现了LL1文法分析的基本功能:消除左递归、构建文法的First、Follow及Select集、建立分析表并对句子进行分析。
[ 关键词 ]LL1文法, 文法分析
The implementation of LL1 grammar analyzer
[Classify] Curriculum Design
[ Level ] ★★★
[Abstract] In this article, I realize the basic function of LL1 grammar analyze : eliminate left recursive , construct the grammar’s first set , follow set and select set, and also construct the analyze table to parse the input sentence.
[Key Words]LL1 grammar, grammar analyze
[0引言]
LL1文法是一种简单易行的,自顶向下的,且较易实现的文法。它的原理在编译原理的书上已有详细叙述,本文着重于介绍其实现过程中的一些细节及思考。
[1总体设计思想]
1.1文法的表示
这里采用字符串数组的形式表示文法中的各个项,这样的表示好处在于操作相当直观,缺点是当项目较多时,需要较多的检索时间。
1.2集合类的设计
由于初期对集合的操作的复杂认识不足,直接采用其于一维字条符数组的形式来表示first, follow集及select集,使得细节处的代码编制较为繁锁,如能采用某类封装好的集合数据类型,如hash等,则可使得内部算法实现较为轻松。
1.3自动机的实现------GOTO语句的放光
由于在构建一些有较多情况出现的算法时,用在纸上画出算法对应的自动机是相当方便的。此时,要做的,只是将自动机转化成相应的代码即可。我个人认为,此时是GOTO语句真正有用武之地的一个地方,当自动机的状态多于三个时,用标号结合GOTO语句可以与自动机紧密结合,表达起来相当清晰和简单,如果为了避免用GOTO而改用其它的语句来实现自动机的功能,反而有些得不偿失了。
1.4图算法的应用
计算first集及follow集时,可以用基于集合扩张的迭代算法,直至没有集合在某次迭代计算中更新即意味着算法结束,过程有些繁锁。不过,用图算法就要轻松多了,不仅意图清晰,而且实现起来也相当容易,最后都会涉及到一个可达性计算,用Floyd算法(O(n^3))可轻松搞定。
[2核心算法]
注:空字符这里用z表示。
2.1消除左递归
消除左递归时主要经历以下步骤:
a)对文法按推导字母顺序的顺序排列,且将开始符置于数组最前部,这里采用冒泡算法。
b)查看文法是否含有左递归,如果没有,则终止。
c)准备两个字符串数组:tweenStrArr和tmpGrammerArr,tweenStrArr用以
存放每一个非终止符作为左侧推导项时临时分析结果,tmpGrammerArr则用以存放去除左递归后的文法。
接下来即可进行消除左递归的过程,核心算法框架如下:
For each vn in Grammer
For each item which started by vn’ (vn’ has been operated)
If has vn->vn’a then
Replace all vn’ with vn’->beta
Endif
End for
If(vn has left recursive)
Find new sign vnNew
Change Vn->VnB|A to
Vn->A vnNew
vnNew->B vnNew
vnNew->z //这里用z代表空
Else
Copy all vn->beta to tmpGrammerArr
End If
End for
2.2求first集,follow集及select集
2.2.1计算各非终止符是否可推得空
这个算法较易,框架如下:
While has un labeled node
Changed=0
For all the vn if not labled
If has vn->z* then
produceZ[vn’s pos]=1
replace all vn in grammer ‘s right side with z
Changed=0
End If
End for
If(changed==0)
Label all the unlabeled vn in produceZ with 0
End If
End While
2.2.2Frist集的计算
[a]First集采用基于图的算法:
对于形如A->aB的推导式,则连接A->a一条线;(注意a!=z)
对于形如A->B的推导式,则连A->B一条线,
对于形如A->BC的推导式,如果B->z,则连A->C一条线。
再则用Floyd算法计算图的各点间的可达性。
记录First集中的元素。若A->z,则应将z加入First(A)中
2.2.3Follow集的计算:
起始符连’#’一条线。
对于形如P->AB 的推导式,连A->First(B)中的所有元素一条线。
对于形如P->A的推导式,连A->P一条线。
对于形如P->Aa的推导式,连A->a一条线。
对于形如P->ABC的推导式,且B->z,则连A->First(C)中所有元素一条线。
再则用Floyd算法计算图的各点间的可达性。
记录Follow集中的元素。
2.2.4Select集的计算:
对于形如A->B的式子,且B->z,则加First(B)-z与Follow(A)中互异元素至Select(A)中。
对于形如A->aB的式子,则有Select(A)=a
记录Select(A)中的元素。
2.3符号分析表的构建及预测分析程序
符号分析表的构建较易,是一个填充二维字符串数组的过程。
而预测分析程序的主框架如下:
Push(start sign)to analyse Stack
While(analyse stack is not empty)
{
If top(analyse stack ==curChar)
{
Pop analyse stack.
Get new curChar
}
Else
{
find produce started in analyse table:
rowIndex: top(analyse stack)’s top
colIndex: curChar’s pos
if(not founded)
error
eles if ->z
pop analyse stack
else
if ->B1B2
push B2,B1 to analyse stack.
}
}
[3]算法实现结果
试验1:
文法规则为:(其中,S表示起始字符)
S
S->aH
H->aMd
H->d
M->Ab
M->z
A->aM
A->e
需要分析的句子为:
aaabd#
经分析程序得到如下结果:
==fisrt group condition==:
group S:a,
group A:a,e,
group H:a,d,
group M:a,e,z,
==follow group condition==:
group S:#
group A:b
group H:#
group M:bd
======================
===output of group select===:
SaH a,
AaM a,
Ae e,
HaMd a,
Hd d,
MAb a,e,
Mz b,d,
================
===output analyse table.===
a b d e z #
S aH
A aM e
H aMd d
M Ab z z Ab
==========================
===parse setence : aaabd# ===
s analyseStack unParseSetence op
0 #S aaabd# S->aH
1 #Ha aaabd# a matched.
2 #H aabd# H->aMd
3 #dMa aabd# a matched.
4 #dM abd# M->Ab
5 #dbA abd# A->aM
6 #dbMa abd# a matched.
7 #dbM bd# M->z
8 #db bd# b matched.
9 #d d# d matched.
parse successfully.
试验2
文法规则为:(E表示起始字符)
E
E->E+T
E->T
T->T*F
T->F
F->i
F->(E)
需要分析的句子为:
i*(i+i*i)#
则得到的分析表及其分析过程如下:
==output analyse table.==
( ) * + i z #
E TA TA
A z +TA z
B z *FB z z
F (E) i
T (E)B iB
==parse setence : i*(i+i*i)# ==
s analyseStack unParseSetence op
0 #E i*(i+i*i)# E->TA
1 #AT i*(i+i*i)# T->iB
2 #ABi i*(i+i*i)# i matched.
3 #AB *(i+i*i)# B->*FB
4 #ABF* *(i+i*i)# * matched.
5 #ABF (i+i*i)# F->(E)
6 #AB)E( (i+i*i)# ( matched.
7 #AB)E i+i*i)# E->TA
8 #AB)AT i+i*i)# T->iB
9 #AB)ABi i+i*i)# i matched.
10 #AB)AB +i*i)# B->z
11 #AB)A +i*i)# A->+TA
12 #AB)AT+ +i*i)# + matched.
13 #AB)AT i*i)# T->iB
14 #AB)ABi i*i)# i matched.
15 #AB)AB *i)# B->*FB
16 #AB)ABF* *i)# * matched.
17 #AB)ABF i)# F->i
18 #AB)ABi i)# i matched.
19 #AB)AB )# B->z
20 #AB)A )# A->z
21 #AB) )# ) matched.
22 #AB # B->z
23 #A # A->z
parse successfully.
以上两个试验从较大呈度上说明了程序运行的正确性及稳定性,当然,对程序本身还有待进行更进一步的严格测试,也欢迎诸位需要此程序的朋友们在抓到BUG时能告知于我,谢谢。
[参考文献与网站]
[1] 吕映芝,编译原理,清华大学出版社,2004.
[源码下载]
http://emilmatthew.51.net/EmilPapers/0626LL1Grammer/code.rar
若直接点击无法下载(或浏览),请将下载(或浏览)的超链接粘接至浏览器地( 推荐MYIE或GREENBORWSER)址栏后按回车.若不出意外,此时应能下载.
若下载中出现了问题,请参考:
http://blog.csdn.net/emilmatthew/archive/2006/04/08/655612.aspx