(一)LR(k)項目算法
LR(k)項目與以前SLR(1)中的項目有所不一樣,LR(k)項目是一個二元組[ 產生式,終結符 ]的形式閉包
定義:使得每一個項目都附帶有k個終結符,項目是二元組,通常形式是[ A->α· β ,a1 a2 ....ak],這樣的項目稱爲LR(k)項目。k越大,LR(k)項目越多。函數
-
-
- 顯然,從定義中咱們能得出A->α· β是一個LR(0)項目,由於它後邊二元組的終結符個數爲0。
- a1 a2 ..... ak是終結符,稱爲向前搜索符串(展望串)
- α處於棧頂位置
- 圓點後邊的輸入能夠匹配 β a1 a2 ..... ak
(二)LR(1)項目學習
定義:咱們只對k<=1的情形感興趣,經過向前搜索一個符號就能夠肯定移進或歸約,若是K=1,即LR(1)項目,[ A->α· β ,a ]。spa
-
-
- 對於任何移進或待約項目[ A->α· β ,a](β != ε),搜索符串a沒有任何做用。
- 向前搜索符a僅對歸約項目[ A->α β · ,a]有意義。
- 當它所屬的狀態呈如今棧頂且後續的輸入符號爲a時,才能夠把棧頂上的αβ歸約爲A。
- 當歸約A->αβ(第i個產生式)時,a時前看符號。把ri填入到ACITON[ s,a ]中。
LR(1)項目的構造:3d
-
-
- 對於項目[ A->α ·Bβ,a ],添加[ B->γ ,b ] 到項目集,b屬於First(βa)。
- 與LR(0)相比,僅有閉包的計算方法不一樣。
- 爲構造有效的LR(1)項目集族咱們須要兩個函數CLOSURE和GO。
CLOSEURE(I)的定義:blog
![](http://static.javashuo.com/static/loading.gif)
GO的定義:博客
![](http://static.javashuo.com/static/loading.gif)
(三)構造LR(1)文法分析表搜索
下圖中有一增廣文法,求出它的項目集。方法
![](http://static.javashuo.com/static/loading.gif)
老規矩,直接上答案圖,而後按步驟講解。
![](http://static.javashuo.com/static/loading.gif)
-
- 從S' -> ·S且項目集編號爲0開始,S'爲開始符號,二元組終結符部分是#,因此二元組爲[ S' -> ·S,# ]。
-
-
- 根據定義,圓點後爲S(非終結符),將S -> ·BB加入0號項目集,由於S->·BB中的S是從S'->S而來的,在二元組 [S'->·S,# ]求出Follow(S) = { # },因此得出二元組[ S->·BB,# ]
- 根據定義,圓點後爲B(非終結符),將B -> ·aB加入0號項目集,求出S -> ·BB圓點後第一個符號(第一個B)的Follow集,或求出圓點後從第二個符號開始的(第二個B開始)的First集。求出First(B,#)={a,b},(注意First(B)中的B是S -> ·BB中的第二個B),因此得出二元組[ B -> ·aB,a | b ]。
- 將B->B -> ·b加入0號項目集,(注意這裏產生式左部的B,也是來自於S->·BB中的第一個B),所以咱們只須要求出Follow(第一個B)或者First(第二個B,#)便可,得出First(B)={a,b},加入二元組[ B->·b, a | b ]
- 至此0號項目集已經完成
2.項目集0輸入符號B進入項目集2,從S->B·B開始,這裏的S來自於項目集0中的[S->·BB,#],而S->·BB來自於[ S'->·S,# ],所以項目集2中加入二元組[ S->B·B ,#]。
-
-
- 根據定義,S->B·B中,圓點後爲非終結符,加入B -> ·aB,而產生式左部的B來自於項目集2中[ S->B·B ,# ]中的第二個B,因此求出Follow(第二個B)={ # },或First(#)={ # },因此加入二元組[ B -> ·aB ,#]。
- 項目集2加入B->·aB以後還須要加入B->·b,這個B一樣來自於項目集2中[ S->B·B ,# ]中的第二個B,因此求出Follow(第二個B)={ # },或First(#)={ # },因此加入二元組[ B->·b ,#]。
- 至此2號項目集已經完成
3. 項目集0輸入符號a進入項目集3,從B->a·B開始,產生式左部的B來自於項目集0中的[ B->·aB,a | b],而B->·aB又來自於S->·BB,因此求出Follow(第一個B)={ a,b }或First(第二個B,#)={ a,b },因此項目集3中加入[ B->a·B ,a | b]
-
-
- 根據定義,B->a·B中圓點後爲非終結符,加入B->·aB,繼續找出產生式左部B的來源,來自於項目集3中[ B->a·B,a | b ]求出Follow(B)={a , b},First(a|b)={a,b},所以項目集3中加入二元組[B->·aB,a | b]。
- 一樣項目集3加入B->·b,繼續找出產生式左部B的來源,來自於項目集3中[ B->a·B,a | b ]求出Follow(B)={a , b},First(a|b)={a,b},所以項目集3中加入二元組[B->·b,a | b]。
- 至此3號項目集已經完成
由於項目集過多,這裏只選出具備表明性的三個項目集解釋,其餘項目集可按該思路得出。
總結:有一產生式 S -> xxx(x表明任意終結符或非終結符),就去查找該S的來源,而後找到源二元組[ E->x·SA,abc ]以後,求出Follow(S),或 First(Aabc)便可。
下圖是該文法的LR(1)分析表
![](http://static.javashuo.com/static/loading.gif)
能夠發現歸約項目集爲四、五、七、八、9,而再查看對應的的LR(1)項目集能夠看出來:
-
-
- 4號項目集終結符爲a,b,所以在ab所在列填入r3(Ri中的 i 爲文法編號,第一個圖)。
- 5號項目集終結符只有#,所以只在#所在列填寫r1。
- 7號項目集終結符只有#,所以只在#所在列填寫r3。
- 8號項目集終結符爲a,b,所以在ab所在列填入r2。
- 9號項目集終結符只有#,所以只在#所在列填寫r2。
表中其他內容與LR(0)和SLR(1)基本一致,這裏就再也不介紹。至此LR(1)分析表就構造完成。
對於該文法再給出LR(0)和SLR(1)分析表,能夠作一下對比理解,本身推下3個分析表如何構造:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
最後給出三個分析表算法的系統語言:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
到此爲止,就已經完成LR(0)、SLR(1)、LR(1)分析表的構造以及流程。
總結一下三個表流程(重點、重點、重點!!!):
-
-
-
- 構造增廣文法。
- 根據增廣文法列出項目集
- 構造NFA(該步驟能夠省略)
- 構造LR(0)DFA(這一步很是重要,若是DFA構造錯誤則分析表會出錯,LR(0)和SLR(1)的DFA同樣,LR(1)的DFA中產生式後須要計算終結符 )
- 判斷是否是LR(0)文法,若是存在衝突則下一步,若是不存在衝突則該文法是LR(0)文法。(是LR(0)文法則必定是SLR(1)和LR(1)文法)
- 判斷衝突可否用SLR(1)的解決方法消除,若是能消除則是SLR(1)文法,若是不是則下一步
- 根據LR(0)的DFA或SLR(1)的DFA(一元組形式)計算每一個產生式的展望串,從而得出LR(1)的DFA(二元組形式)。
- 根據衝突項目集中終結符去判斷可否消除衝突,若是S-R或R-R衝突的兩個二元組中的終結符沒有交集則視爲能夠消除衝突,若是不能消除至此則該文法不屬於上述3個文法的任意一個。
LALR文法就再也不介紹了,若是有興趣能夠查看一下其餘優秀的博客,至此自下而上分析法就已經介紹完畢(該博客爲我的學習總結,若是錯誤或異議歡迎指出,謝謝。)