本页使用了标题或全文手工转换

LL剖析器

维基百科,自由的百科全书
跳转至: 导航搜索

上下文无关文法
语法分析器
· LL剖析器
· 算符优先分析器
· LR剖析器
· SLR剖析器
· LALR剖析器

LL剖析器是一種處理某些上下文無關語法由上而下剖析器。因為它由Left)至右處理輸入,再由句中邊的優先推導出語法樹(Left derivation)(相對於LR剖析器)。能以此方法剖析的語法稱為LL語法

本文之中會討論以表格為主的剖析器,而非另一種通常是手工打造(非絕對;參考如ANTLR等的LL(*)遞迴深入剖析器產生器)遞迴深入剖析器

一個LL剖析器若被稱為LL(k)剖析器,表示它使用k語彙標記前瞻處理。若對某個語法而言,存在一個剖析器可以在不用回溯的情況下處理這個語法,則這個語法稱為LL(k)語法。在這些語法中較嚴格的LL(1)語法相當受到歡迎,由於它的剖析器只需要多看一個語彙符號就可以產生剖析結果。那些需要很大的k才能產生剖析結果的程式語言,在剖析時的需求也較高。

一般情況[编辑]

本剖析器可以處理特定的形式文法的字串。

本剖析器由以下組成

  • 一個輸入緩衝,存放輸入字串(由語法建立的)
  • 一個儲存語法中尚待處理的終結符與非终結符堆疊
  • 一個剖析表,標示是否有可以套用在目前堆疊與下一個輸入符號的語法規則

剖析器根據堆疊最上方的符號(行)以及目前輸入流中的符號(列)來決定套用哪一條規則。

當剖析器一開始執行的時候,堆疊中已經有兩個符號:

[ S, $ ]

'$'是一個特殊的終端符號,用來表示堆疊的底端或是輸入的結束;而'S'則是文法開始的符號。剖析器會嘗試根據它在輸入流中看到的符號來複寫堆疊中的資料,不過只會將還需要修改的資料存回堆疊中。

實際的例子[编辑]

設定[编辑]

為解釋LL剖析器的工作方式,我們創造了以下這個小語法:

  1. S → F
  2. S → ( S + F )
  3. F → 1

並處理以下輸入:

( 1 + 1 )

這個語法的剖析表如下:

1 + $
S 2 - 1 - -
F - - 3 - -

(注意到有一列特殊終端符號,在這裡表示為$,是用來標示輸入結束的。)

剖析流程[编辑]

剖析器先從輸入資料流中讀到第一個 '(',以及堆疊中的'S'。從表格中他發現必須套用規則 (2);它必須將堆疊中的'S'重寫為 '( S + F )',並將規則的號碼輸出。最後堆疊變成:

[ (, S, +, F, ), $ ]

再來它移除輸入及堆疊中的 '(':

[ S, +, F, ), $ ]

現在剖析器從輸入資料流中抓到一個'1',所以他知道必須套用規則 (1)與規則 (3),並將結果輸出。則堆疊變成:

[ F, +, F, ), $ ]
[ 1, +, F, ), $ ]

接下來的兩個步驟中,剖析器讀到'1'及 '+',因為他們跟堆疊中的資料一樣,所以從堆疊中移除。最後堆疊剩下:

[ F, ), $ ]

再接著的三個步驟中,堆疊中的'F'會'1'被取代,而規則 (3)會被輸出。再來堆疊與輸入資料流中的'1'與')'都會被移除。而剖析器看到堆疊與輸入資料流都只剩下'$'的時候,就知道自己的事情做完了。

在這個例子中,剖析器接受了輸入資料,並產生以下輸出(規則的代號):

[ 2, 1, 3, 3 ]

這的確是從輸入的左邊優先推導。我們可以看出由左至右的輸入順序為:

S → ( S + F ) → ( F + F ) → ( 1 + F ) → ( 1 + 1 )

备注[编辑]

由以上範例可以看出剖析器根據堆疊最上層為非終端符號、終端符號、還是特殊符號$來決定採取三種不同的步驟:

  • 若堆疊最上層為非終端符號,則根據輸入資料流中的符號對照剖析表,決定要用語法中的哪條規則來取代堆疊中的資料,順帶輸出規則的號碼。若表格中並沒有這麼個規則,則回報錯誤並終止執行。
  • 若堆疊最上層為終端符號,則與輸入資料流中的符號比較。若相同則移除,若不同則回報錯誤並終止執行。
  • 若堆疊最上層為'$',並且輸入資料流中也是'$',則表示剖析器成功的處理了輸入,否則將回報錯誤。不管怎樣,最後剖析器都將終止執行。

這些步驟會持續到輸入結束,然後剖析器成功處理了一則左邊優先推導,或者會回報錯誤。

建構LL(1)剖析表格[编辑]

為了要填滿剖析表格,我們必須決定剖析器在堆疊看到非終端符號A又在輸入資料流看到a的時候應該選用哪一條文法規則。我們可以輕鬆的發現到這種規則應該有Aw一類的格式,並且語言中的w應至少有一個字串由a開頭。為了這個目的,我們設定 第一個集合w,記作Fiw),表示可以在w中找到的所有字串的集合,如果空字串也屬於w的話還要再加上ε。而透過文法規則A1w1, ..., Anwn,就可以使用以下方法演算每條規則的Fi(wi)及Fi(Ai)了:

  1. 將每個Fi(wi)及Fi(Ai)初始成空集合
  2. Fi(wi)加入每條Aiwi規則中的Fi(Ai),Fi定義如下:
    • 所有的a皆為終端符號時,Fia w' )= { a }
    • FiA)不包含ε時,相對於每個非終端符號AFiA w' )= FiA
    • FiA)包含ε時,相對於每個非終端符號AFiA w' )= FiA)\ { ε } ∪ Fiw'
    • Fi(ε) = { ε }
  3. 針對每條Aiwi規則,將Fi(wi)加入Fi(Ai)
  4. 重複步驟2與步驟3,直到所有Fi集合固定下來。

不幸的是,第一集合還不夠用來產生出剖析表。由於規則中右手邊的w可能無限制的被覆寫成空字串,所以剖析器也在ε位於Fiw)並且輸入資料流中的符號可以符合A的時候套用Aw。所以還需要一個記作FoA)的A跟隨集合,表示可以由開始的符號衍生出αAaβ字串的終端符號a的集合。非終端符號的跟隨集合可以用以下方法得出:

  1. 將每個Fo(Ai)初始成空集合
  2. 若存在AjwAiw' 格式的規則,則
    • 若終端符號a存在Fiw' )中,則將a加入Fo(Ai)
    • 若ε存在Fiw' )中,則將Fo(Aj)加入Fo(Ai)
  3. 重複步驟2直到所有Fo集合固定下來

現在我們可以清楚定義每條規則要放在剖析表的哪裡了。若T[A,a]用以表示表格中代表非終端符號A及終端符號a的規則,則

T[A,a]包含Aw規則,若且唯若
aFiw)之中,或
ε在Fiw)之中,且aFoA)之中。

若表格的每格中都僅包含一個規則,則剖析器總是知道該套用什麼規則,所以可在不用回朔的前提下剖析字串。在此情形下,這個語法可以稱為LL(1)語法

建構LL(k)剖析表格[编辑]

剖析表格可能(一般來說,在最差狀況下)必須有k次的指數複雜度的觀念在1992年左右PCCTS發表後改觀,它示範了許多程式語言可以用LL(k)來有效率的處理,而不會觸發剖析器的最差狀況。再者,在某些必須無限前瞻的狀況下,LL剖析也是合理的。相反的,傳統剖析器產生器,如yacc使用LALR(1)剖析表格建立被限制的LR剖析器,這種剖析器只能向後看固定的一個語彙符號。

参见[编辑]

外部链接[编辑]