此文略長。我也沒想到這寫起來這么多,但對構造過程絕對清楚,一步步慢慢看吧。
LR的第一個L和LL的第一個L含義相同,即從左到右掃描句子 ,第二個R表示Right most最右推導。
在通常的描述中,后面還有一個括號里面的數字如,LR(0)、LR(1)這樣,括號里面的數字表示用於決策所需的后續token分詞數。
首先看一下LR分析器的模型圖
可惜看出,LR分析器最關鍵的部分就是 LR分析表了,而LR分析表的構建是由已構造出的LR(0)項目集規范族來進行構造的。LR分析法貌似是不要求掌握的,而且這部分比我想象的還要復雜,今天看了好多。才勉強搞清楚這個項目集規范族的構造,但是用來鍛煉思維確實不錯啊。
項目集,那么字面上看就是項目的集合了,項目是什么呢。這個也確實不好說,書上是說在文法G中每個產生式的右部適當位置添加一個圓點構成LR(0)項目,舉個例子吧。
比如對於
A->xyz
這條產生式可以構造的LR(0)項目就有4個
A->.xyz A->x.yz A->xy.z A->xyz.
這樣很清楚了吧,就是用.分割。這個分割產生的四個項目在進行真正的語法分析的時候對應不同的操作,比如規約還是移位。這里不討論。重點是項目集規范族的構造,
在知道了LR(0)項目后,可以來看看項目集規范族的定義,
對於構成識別一個文法活前綴的DFA項目集(狀態)的全體我們稱之為這個文法的LR(0)項目集規范族。至於什么是活前綴呢,定義如下
對於任一文法G[S],若S’經過任意次推導得到αAω,繼續經過一次推導得到αβω,若γ是αβ的前綴,則稱γ是G的一個活前綴。
現在知道了LR(0)項目,了解了活前綴,和項目集規范族的定義,還須引入LR(0)項目集的閉包函數CLOSURE和狀態轉換函數GO兩個概念,先給出數學上的定義,如果你覺得麻煩可以跳過,后面會給出一道例題。
① 閉包函數CLOSURE(I)的定義如下:
a)I的項目均在CLOSURE(I)中。
b)若A→α·Bβ屬於CLOSURE(I),則每一形如B→·γ的項目也屬於CLOSURE(I)。
c)重復b)直到不出現新的項目為止。即CLOSURE(I)不再擴大。
② 轉換函數GO(I,X)的定義:
GO(I,X)=CLOSURE(J)
其中:I為包含某一項目的狀態,就是前面我們說的那四個了。,X為一文法符號,X∈(VN∪VT),J={任何形如A→αX·β的項目| A→α·Xβ屬於I}。
這樣就可以使用閉包函數和轉換函數構造文法G′的LR(0)項目集規范族,其步驟如下:
a)置項目S′→·S為初態集的核,然后對核求閉包,CLOSURE({S′→·S})得到初態的項目集。
b)對初態集或其它所構造的項目集應用轉換函數GO(I,X)=CLOSURE(J),求出新狀態J的項目集。
c)重復b)直到不出現新的項目為止。
開始拿個例題來說明,定義沒例題看起來看難了。
<strong><em>例題:對於下列文法,S→aS|bS|a,構造該文法的LR(0)項目集規范族</em></strong>
思路就是利用閉包函數CLOSURE和轉換函數GO來構造。通過計算函數CLOSURE和GO得到文法的LR(0)項目集規范族,而GO函數則把LR(0)項目集規范族連成一個識別該文法所產生的活前綴的DFA。DFA大家都知道,有窮自動機。
(1)將文法G(S)拓廣為G(S’)也就是該文法的增廣文法,目的是使語法分析器知道何時應該停止並接受該串,也就是說當使用S'->S進行規約的時候,就結束。
(0)S’→S
(1)S→aS
(2)S→bS
(3)S→a
構造該文法的LR(0)項目集規范族,I就是產生式,至於I0 I1就是往下遞增就可以了。沒什么特別的意思。:
I0=CLOSURE({S' →•S})={S’ →•S, S→•aS, S→•bS, S→•a}
//第一條是固定的,都是求S' →•S的閉包。而因為右側的S又可以推導出后面三個,所以后面三個式子是屬於該閉包的。在閉包的規則中可以看出,若A→α·Bβ屬於CLOSURE(I),此時S' →•S屬於閉包,S相當於規則中的B,則每一形如B→·γ的項目也屬於CLOSURE(I),此處相當於S->后面的三個推導式。加上.就可以了
I1=GO( I0 , a)=CLOSURE({S→a•S , S→a•})={S→a•S , S→a• , S→•aS, S→•bS, S→•a }
//第二條你可能已經看出來了,其實就是把轉換函數GO反過來用,在GO函數中,X為一文法符號,J={任何形如A→αX·β的項目| A→α·Xβ屬於I}。也就是在I0中,找到右側包含a的項目,然后將.右移一位來求他們的閉包函數,此處,I0中包含.a的項目為 S→•aS和 S→•a,.右移一位變成S→a•S , S→a•,求閉包函數的規則和上面那條是一樣的,繼續找推導式右邊可以推導出來的式子就可以了,此處,右邊同樣是S可以推導出三個式子,前面加上.就可以了。
I2=GO(I0 , b)=CLOSURE({S→b•S })={ S→b•S, S→•aS, S→•bS, S→•a }
//第三條仿照第二條進行推導,先在I0中找有.b的,然后右移一位,然后推導式右側的S繼續用題目給出的推導,然后前面加上.
I3=GO(I0 , S)=CLOSURE({S’ →S•})={ S’ →S•}
//第四條因為右側包含.S的只有一項。必須是.S。所以只有一個,右移后求閉包即可。因為S是右側的第一位,所以不用再向下推導了,規則是:A→α·Bβ屬於CLOSURE(I),此處是S’ →S•,則B→·γ的項目也屬於CLOSURE(I),此處S相當於規則中的α,無B。。。。
因為GO(I1, a)=CLOSURE({S→a•S , S→a•})=I1,
//第五條同理,在I1中找有右側有.a的項目,右移一位。進行求閉包,發現和I1求閉包的變量是一樣的。所以結果必然也和I1是一樣的。
GO(I1, b)=CLOSURE(S→b•S)=I2.
//第六條沒有新的I產生。
I4=GO(I1, S)=CLOSURE({S→aS•})={S→aS•}
//這第七條在I1找右側包含.S的項目,只有S→a•S,右移后求閉包,同第四條,無B,所以直接如此。
GO(I2, a)= CLOSURE({S→a•S , S→a•})=I1
GO(I2, b)=CLOSURE({S→b•S})=I2
I5=GO(I2, S)=CLOSURE({S→bS•})={S→bS•}
此時應該繼續求GO(I3, a),GO(I3, b)和,GO(I3, S),但顯然I3中沒有.a,沒有.b也沒有.S,所以不用多此一舉了,
繼續求GO(I4, a),GO(I4, b)和,GO(I4, S),顯然同上,也沒有。所以也不用求了,
繼續求GO(I5, a),GO(I5, b)和,GO(I5, S),理由同上,沒有任何必要了
最終,項目集I0,I1,I2,I3,I4和I5構成了該文法的LR(0)項目集規范族。
編譯原理真是博大精深啊。就一個簡單的三個推導式就得這么多步驟才構造了一個規范族,還要利用規范族來構造LR分析表,這。。手工果斷不是事啊。今天看了幾個小時才完全弄清楚LR(0)項目集規范族的構造。例題的步驟是我自己寫的。這篇文章寫了2個小時。。太費腦子了。。慢慢再寫產生活前綴的DFA和LR分析表吧。
我的網站中的原文鏈接:http://leaver.me/archives/548.html
參考:
http://metc.gdut.edu.cn/compile/nandian/n-7.htm