1. 程式人生 > >現代編譯原理——第二章:語法分析之LL(K)

現代編譯原理——第二章:語法分析之LL(K)

  轉自: http://www.cnblogs.com/BlackWalnut/p/4472122.html

  LL(K)語法分析技術是建立在預測分析的技術之上的。我們先來了解預測分析技術。考慮以下文法:

  

     當使用該文法對(1*2-3)+4和(1*2-3)進行分析,前者因該呼叫E->E+T,而後者應該呼叫E->T,怎麼確定到底使用哪個產生式呢?這就要使用預測分析技術來構建預測分析語法分析器,LL(k)是其一種。預測分析技術的關鍵是構建一個無衝突的預測分析表。所謂預測分析表就是程式可以根據當前的狀態來查詢該表,然後確定下一步使用哪個產生式。

  構建預測分析表要要用到兩個集合,分別是first集合和follow集合。γ是終結符和非終結符組成的字串,first(γ)是從γ中可以推到出的任意字串中所包含的開頭終結符所組成的集合。A是一個非終結符,follow(A)的意思可以直接跟在A後面的所有終結符的集合。這兩個集合的求法可以描述為如下:

    First集合的求法:

    First集合最終是對產生式右部的字串而言的,但其關鍵是求出非終結符的First集合,由於終結符的First集合就是它自己,所以求出非終結符的First集合後,就可很直觀地得到每個字串的First集合。

1.  直接收取:對形如U-a…的產生式(其中a是終結符),把a收入到First(U)中
2.  反覆傳送:對形入U-P…的產生式(其中P是非終結符),應把First(P)中的全部內容傳送到First(U)中。       Follow集合的求法:
    Follow集合是針對非終結符而言的,Follow(U)所表達的是句型中非終結符U所有可能的後隨終結符號的集合,特別地,“#”是識別符號的後隨符。 1.反覆傳送:對形如U-…P的產生式(其中P是非終結符),應把Follow(U)中的全部內容傳送到Follow(P)中
2.  直接收取:注意產生式右部的每一個形如“…Ua…”的組合,把a直接收入到Follow(U)中。 3.直接收取:對形如“…UPA…”(P是非終結符)的組合,把First(P)直接收入到Follow(U)中。如果P的first集合包含由空(ε),則first(A)也要放入Follow(U)中。
  需要注意的是,空是隻能在First集合中不能在follow集合中。
  從上面的求法中可以知道,其實first集合就是一個非終結符的等價終結符的可選集合,也就是說A可以直接推到出這些終結符,如果first集合可以為空,則說明A可以直接忽略,這個時候,為了預測A為空後的情形,我們構建了follow集合。可見,者兩個完全是為了預測分析而產生的集合。   預測分析表是一個二維表,用非終結符符標註每行,用終結符好標註每一列,根據這first和follow兩個集合,我們使用如下規則構建一個預測分析表:從產生式集合中取出一個產生式A->γ,如果終結符a在first(γ)中,則講A->γ放入(A,a)確定的位置中,如果γ可以為空且a在follow(A)中,也則講A->γ放入(A,a)確定的位置中。這樣我們根據文法以下文法得到其相應的預測分析表:                            
                            如何使用這個分析表呢?要知道,我們所分析的資料是由詞法分析其產生的,對於語法分析器而言,詞法分析器產生的資料都是終結符。我們利用堆疊(用到堆疊的地方一般也可以用遞迴來解決,可以參考虎書英文版的第47頁程式碼)來記錄當前正在分析的非終結符,從詞法分析器得到一個終結符a,以及棧頂的資料來查表,然後確定使用的產生式,如果產生式的右面有非終結符,將其從右到左壓棧(保證每次從對產生式的最左面的非終結符開始推到,稱為左推到),然後繼續使用使用a對棧頂元素分析。直到找到一個產生式的右只包含終結符a,然後將a拋棄,重新讀取新的終結符,繼續分析。如果在過程中無法得到只包含終結符a的產生式,那麼說明有語法錯誤。   上面的演算法過程就是一個預測分析過程我們稱為LL(1)演算法,第一個L是left to right parse,第二個L是leftmost derivation ,1是1-symbol lookahead.意思就是從左到右的分析詞法分析器產生的資料,採用最左推到原則,每次只看超前檢視一個終結符來確定後續動作。   但是就對上面這張表而言,(Z,d)的位置有兩個產生式,那麼說明該文法是二義的,說明它是不是LL(K)文法。就不能使用預測分析技術來解析該語言,要使用功能更強大的LR(k)技術。    這裡介紹兩種可能產生二義性的例子,第一個為左遞迴。檢視下左圖,因為first(T)正好是first(E+T)的子集合,所以,一定會產生衝突。我們使用右圖來替換原來的產生式,稱為消除左遞迴。                                                              還有一種情況,我們解決的辦法稱為提取右因子。                                                              以上就是LL(K)文法的全部,相比LR(k)文法,它的功能不夠強大(我們在LR(K)中進行比較),但是想要構造一個預測分析表卻十分的簡單。對於某些LR(k)處理不了的情況,我們可以快速的建立相應的LL(K)分析表來解決。