前言 本来这篇文章打算考试完之后写的,但是我懒了一下,就等到了答辩结束。
这次我用的Python写的,主要是以前只是用过Python,但是没有它做过项目,这次就用Python来试一下。感觉用Python开发还是蛮舒服的,短小精悍的代码,还有跨平台。
任务要求 Create a phaser analysis program, which uses LL(1) method, Operator precedence analyzing or LR(1) method. The inputs of the program are a text document including a set of productions of the 2º grammar (context free grammar) and the token table generated by Task 1. The out of the program is YES or NO. i.e., the source code string accords with this 2º grammar or the source code string doesn’t accord with this 2º grammar.
要求是用LL(1)或者LR(1)方法,这次读入的是2型文法(词法分析器是3型文法),也就是上下文无关文法。 源代码符合输入的文法,就输出YES,否则就输出NO,并给出错误的位置。
任务分析 在语法分析中,主要完成以下几个操作:
First函数:用来求解FIRST集
Closure函数:用来求解某个项目的闭包
Go函数:将LR(1)的小圆点向后移动一位
GetItemSet函数:用来根据一个项目集和一个符号(终结符或非终结符)生成另一个项目集
MakeTable函数:用来生成ACTION表和GOTO表
其实这几个函数,在课本中都有叙述,你只需要将课本上用文字描述的语言转换成C++、Java或Python就可以了。下面就一个一个的看看这些函数怎么转换。
First函数 LR(1)使用FIRST集作为向前搜索符号集,取代了SLR(1)中的FOLLOW集。 First集的求解方法是在LL(1)中讲到的。 $$FIRST(\alpha)=\lbrace\alpha|\alpha\buildrel*\over\Rightarrow\alpha\beta,\alpha\in V_T,\alpha,\beta\in V^*\rbrace$$ 其实简单来说,$FIRST(\alpha)$就是求的$\alpha$可以推导出的句子的第一个终结符,当$\alpha\buildrel*\over\Rightarrow\epsilon$,则规定$\epsilon\in FIRST(\alpha)$。 因此,当$\alpha\buildrel*\over\Rightarrow\epsilon$时,$FIRST(\alpha\beta)=FIRST(\beta)$ 因此,FIRST函数相当于一个DFS ,但是注意要标记 走过的路径,否则可能无限递归到爆栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 def first (self, items, visit ): res = [] if items[0 ] in visit: return (res, False ) empty = True for item in items: if empty == False : return (res, False ) if item not in self.vn: res.append(item) return (res, False ) empty = False for der in self.grammar[item]: if len (der) == 0 : empty = True continue visit.add(item) (ans, flag) = self.first(der, visit) res += ans temp = set (res) res = list (temp) visit.remove(item) if flag == True : empty = True return (res, empty)
Closure函数 闭包定义:
假定$I$是一个项目集,$I$的任何项目都属于$Closure(I)$
若有项目$A\Rightarrow\alpha∙B\beta, \alpha$,属于$Closure(I)$,$B\Rightarrow\gamma$是文法中的产生式,$\beta\in V^*$,$b\in FIRST(\beta\alpha)$,则$B\Rightarrow∙\gamma, b$ 在这里,我的Closure函数并没有计算一个项目集的闭包,而是计算的单个项目的闭包,对于整个项目集的操作,我放到了GetItemSet 这个函数中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 def closer (self, derivation, visit ): res = [] if len (derivation) == 0 or \ len (derivation['data' ]) == 0 or \ hash (derivation['data' ]) in visit : return res id = self.getItemHash(derivation) if id not in visit: visit.add(id ) res.append(derivation) if len (derivation['data' ]) <= derivation['pointPosition' ]: return res if derivation['data' ][derivation['pointPosition' ]] in self.vn: for item in self.grammar[derivation['data' ][derivation['pointPosition' ]]]: newItem = [derivation['data' ][derivation['pointPosition' ]]] + list (item) firstSet = [] for flag in derivation['flag' ]: firstSet += self.first(tuple (list (derivation['data' ][derivation['pointPosition' ] + 1 :]) + [flag]), set ())[0 ] firstSet = set (firstSet) firstSet = list (firstSet) item = { 'data' :tuple (newItem), 'pointPosition' :1 , 'flag' :firstSet } id = self.getItemHash(item) newRes = [] if id not in visit: visit.add(id ) res.append(item) newRes = self.closer(item, visit) res += newRes return res
GO函数 Go操作就是将一个项目集的所有小圆点向后移动一位(如果小圆点不在最后面的话)。 具体定义如下: $GO(I, X)=CLOSURE(J)$ 其中,$I$是LR(1)中的项目集,$X$是文法符号(终结符和非终结符)。 $J=\{任何形如[A\Rightarrow\alpha X∙\beta, \alpha]的项目|[A\Rightarrow\alpha∙X\beta, \alpha\in I\}$ 文法的初始项目集为$[S’\Rightarrow∙S, \#]$,然后由初始项目集通过闭包函数和转移函数,生成所有的项目集。
1 2 3 4 5 6 7 8 9 10 11 def go (self, item, v ): item = copy.copy(item) res = [] if item['pointPosition' ] < len (item['data' ]): if item['data' ][item['pointPosition' ]] != v: return res item['pointPosition' ] += 1 newItems = self.closer(item, set ()) res += newItems return res
GetItemSet函数 GetItemSet就是对一个项目集的所有项目,做一个转移和闭包操作(闭包操作在GO函数中)。
1 2 3 4 5 def getItemSet (self, items, v ): newItems = [] for item in items: newItems += self.go(item, v) return newItems
MakeTable函数 通过这个函数来生成Action表和Goto表,程序很长,但是并不复杂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 def makeTable (self ): self.grammar.setdefault('begin-1' , [(self.begin,)]) for vn, exp in self.grammar.items(): for item in exp : temp = [vn] temp += list (item) self.hashSet.setdefault(hash (tuple (temp)), tuple (temp)) initItem = { 'data' :('begin-1' , self.begin), 'pointPosition' :1 , 'flag' :['$end' ] } initItemSet = { 'id' :self.getItemSetHash(self.closer(initItem, set ())), 'data' :self.closer(initItem, set ()) } self.accflag = hash (initItem['data' ]) self.parserBegin = initItemSet['id' ] self.c = [] self.c.append(initItemSet) visit = set () visit.add(initItemSet['id' ]) for items in self.c: for v in self.v: res = self.getItemSet(items['data' ], v) if len (res) == 0 : continue newItemSet = { 'id' :self.getItemSetHash(res), 'data' :res } if v in self.vn: self.goto.setdefault(items['id' ], {}) self.goto[items['id' ]].setdefault(v, newItemSet['id' ]) if v not in self.vn: self.action.setdefault(items['id' ], {}) self.action[items['id' ]].setdefault(v, {}) self.action[items['id' ]][v] = { 'flag' :'s' , 'data' :newItemSet['id' ] } if newItemSet['id' ] not in visit: visit.add(newItemSet['id' ]) self.c.append(newItemSet) for itemSet in self.c: for item in itemSet['data' ]: if item['pointPosition' ] >= len (item['data' ]): id = hash (item['data' ]) self.action.setdefault(itemSet['id' ], {}) for v in item['flag' ]: self.action[itemSet['id' ]].setdefault(v, {}) self.action[itemSet['id' ]][v] = { 'flag' :'r' , 'data' :id }
语法分析过程 分析过程跟课本上说的一样,构造状态栈、符号栈,通过Action表和GOTO表进行状态转移和归约。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 def parser (self ): self.makeTable() signal = [] stateStack = [] stateStack.append(self.parserBegin) self.tokens.append([0 , 0 , '$end' , 'EOF' ]) while True : token = self.tokens[0 ][2 ] if token == '$end' and signal[-1 ] == self.begin: break if token in self.action[stateStack[-1 ]].keys(): thisAction = self.action[stateStack[-1 ]][token] if thisAction['flag' ] == 'r' and thisAction['data' ] != self.accflag: length = len (self.hashSet[thisAction['data' ]]) - 1 if length != 0 : stateStack = stateStack[0 :-length] token = self.hashSet[thisAction['data' ]][0 ] if token in self.goto[stateStack[-1 ]].keys(): thisGoto = self.goto[stateStack[-1 ]][token] stateStack.append(thisGoto) if length != 0 : signal = signal[0 :-length] signal.append(token) else : signal.append(token) self.tokens.pop(0 ) stateStack.append(thisAction['data' ]) else : print "Error:%s:%s %s" %(self.tokens[0 ][0 ], self.tokens[0 ][1 ], self.tokens[0 ][3 ]) break if signal[-1 ] == self.begin: return True else : return False
后记 整个语法分析最复杂的,感觉是闭包函数和First函数,需要递归操作,经常无限递归,然后程序就死了。其次就是第一次写完程序运行时,发现程序的运行速度太慢了!通过做性能分析,发现是copy.deepcopy函数的原因,果断放弃,换了copy.copy。 还有就是,深入了解了一下Python的参数传递的方式。
整个项目的源码在这里:点击查看
这个项目里不仅仅有语法分析程序,还有词法分析程序,主要是因为要统一语言,所以我把上次的词法分析器又重写了一边。。。