最普通的自底向上算法稱做LR(1)分析( LR(1)parsing) ( L表示由左向右處理輸入,R表示生成了最右推導,而數字1則表示使用了先行的一個符號)。算法
自底向上的分析程序使用了顯式棧來完成分析,這與非遞歸的自頂向下的分析程序相相似。分析棧包括記號和非終結符,以及一些後面將討論到的其餘信息。自底向上的分析開始時棧是空的,在成功分析的末尾還包括了開始符號。閉包
自底向上的分析示意爲:spa
分析棧在左邊,輸入位於正中間,而分析程序的動做則在右邊。.net
自底向上的分析程序有兩種可能的動做(除「接受」以外):
1) 將終結符從輸入的開頭移進到棧的頂部。
2) 假設有BNF選擇A→α,將棧頂部的串α歸約爲非終結符A。設計
所以自底向上的分析程序有時稱做是移進-歸約分析程序。blog
移進動做是由書寫單詞s h i f t指出的。歸約動做則由書寫re d u c e單詞給出且指出在歸約中所用的B N F選擇。遞歸
另外一個特徵是(因爲技術緣由???):get
老是將文法與一個新的開始符號一同擴充 這就意味着若S是開始符號,那麼就將新的開始符號S’增長到文法中,同時還添加一個單元產生式到前面的開始符號中:it
S’ → Sio
例:
E’→ E
E → E + n | n
移進-歸約分析程序描繪出輸入串的最右推導,但推導步驟的順序倒是顛倒的。
在表5 - 2中,相對應的推導是E’ => E => E + n => n + n
這樣的推導中的終結符和非終結符的每一箇中間串都稱做右句型。
句柄:(參考:http://www.javashuo.com/article/p-cjrqmxak-cm.html)
直接短語中的最左直接短語爲該句型的句柄。
移進-歸約分析程序將終結符從輸入移進到棧直到它能執行一個歸約以獲得下一個右句子格式。它發生在位於棧頂部的符號串匹配用於下一個歸約的產生式的右邊。這個串、它在右句子格式中發生的位置以及用來歸約它的產生式被稱做右句型的句柄(handle) 。
例如,在右句子格式n + n 中,它的句柄是由最左邊的單個記號n 與用來歸約它以產生新的右句型E + n的產生式E→n 組成的串。這個新句型的句柄是整個串E + n (一個可行的前綴)以及產生式E→E + n。有時因爲表示法上的弊端,咱們要用串自己來做爲句柄。
判斷分析中的下一個句柄是移進-歸約分析程序的主要任務。
例:
S’→S
S→( S ) S | ε
這個文法存在着3個產生式選擇和8個項目:
S’→.S
S’→S.
S→. ( S ) S
S→( .S ) S
S→( S. )S
S→( S ) .S
S→( S )S.
定義:
上下文無關文法的LR (0)項(LR(0) item)(或簡寫爲項( item ) )是在其右邊帶有區分位置的產生式選擇。咱們可用一個句點(固然它就變成了元符號,而不會與真正的記號相混淆)來指出這個區分的位置。因此若A→α是產生式選擇,且若β和γ 是符號的任何兩個串(包括空串),且存在着βγ = α,那麼A→β.γ 就是LR(0)項。之因此稱做LR(0)項是因爲它們不包括先行的顯式引用。
項目概念的思想就是指項目記錄了特定文法規則右邊識別中的中間步驟。特別地,項目A→β.γ是由文法規則選擇A→α構成(其中 α = βγ ),這一點意味着早已看到了β,且可能從下一個輸入記號中獲取γ。從分析棧的觀點來看,這就意味着β必須出如今棧的頂部。項目A→.α 意味着將要利用文法規則選擇 A→α 識別A(將這樣的項目稱做初始項(initial item)。項目 A→α. 意味着α如今位於分析棧的頂部,並且若 A→α 在下一個歸約中使用的話,它有可能就是句柄(將這樣的項目稱做完整項(complete item))。
注:概念解釋:閉包:
參考:https://blog.csdn.net/n6323438/article/details/51996551
https://blog.csdn.net/woailuo453786790/article/details/51254124
LR (0)項可做爲一個保持有關分析棧和移進-歸約分析過程的信息的有窮自動機的狀態來使用。
對1.2.1的例子:
NFA:
文法有8個LR(0)項,因此這個NFA就有8個狀態。
DFA:
DFA的開始狀態是由項目S’→ .S 組成的集合的 ε- 閉包,即集合{S’→.S, S’→. ( S ) S, S’ →.}。因爲S有一個從S’→ .S到S’→ S.的轉換,因此也就存在着一個從開始狀態到DFA狀態{ S’→ S. } 的對應轉換(不存在從S’→ S.到任何其餘項目的轉換)。在(上也有一個從開始狀態到DFA狀態的轉換{ S→ (. S ) S, S→.( S ) S, S→. }({ S→ (.S ) S }的ε閉包。DFA狀態{ S→ (. S ) S, S→ . ( S ) S, S→ . }在(上有到其自身的轉換,在S上也有到{ S→ ( S.) S }的轉換。這個狀態在(上有到狀態{ S→ ( S ). S,S→ .( S ) S, S → . }的轉換。最後,這一最終狀態在(上有到前面所構造的狀態{ S→ (. S ) S, S→ .( S ) S, S → . }的轉換。圖是完整的D FA,爲了便於引用還給各個狀態編了號(按照慣例,狀態0是開始狀態)。
例:文法 A → (A)| a
LR(0)項:
狀態0:初始狀態
A’ → .A
A → .(A)
A → .a
狀態1:(識別狀態0 的第一個LR(0)項,接收A)
A’ → A.
狀態2:(識別狀態0的第三個項,接收a,較爲簡單)
A → a.
狀態3:接收" ( " (接收a,將轉到狀態2)
A → (.A)
A → .(A)
A → .a
狀態4:從狀態3接收A
A → (A.)
狀態5:從狀態4接收 )
A → (A).
構造DFA:
以((a))分析動做:將狀態數和字符都壓入棧中:
初步解釋,待驗證:
初始狀態0
終止狀態1
遇到狀態2和狀態5進行規約,狀態1也是規約狀態
分析表:
注:空白項表示的是識別出錯。
名詞解釋:https://blog.csdn.net/zuzhiang/article/details/79047743
經過使用輸入串中下一個記號來指導它的動做。首先,它在一個移進以前先考慮輸入記號以確保存在着一個恰當的DFA。其次,使用非終結符的Follow集合來決定是否應執行一個規約。
定義:SLR(1)分析算法(SLR(1) parsing algorithm)。令s 爲當前狀態(位於分析棧的頂部)。則動做可定義以下:
1. 若狀態s 包含了格式A→α.Xβ的任意項目,其中X是一個終結符,且X是輸入串
中的下一個記號,則動做將當前的輸入記號移進到棧中,且被壓入到棧中的新
狀態是包含了項目A→aX.b的狀態。
2. 若狀態s 包含了完整項目A→γ.,則輸入串中的下一個記號是在Follow(A)中,所
以動做是用規則A→γ 歸約。用規則S’→S歸約與接受等價,其中S是開始狀態;
只有當下一個輸入記號是$時,這纔會發生。在全部的其餘狀況中,新狀態都
是以下計算的:刪除串α和全部它的來自分析棧中的對應狀態。相對應地,
DFA回到α開始構造的狀態。經過構造,這個狀態必須包括格式B→γ. Aβ的一
個項目。將A壓入到棧中,並將包含了項目B→αA.β的狀態壓入。
3. 若下一個輸入記號都不是上面兩種狀況所提到的,則聲明一個錯誤。
SLR(1)文法判斷:
當且僅當對於任何狀態s,知足一下兩個條件
1) 對於在s 中的任何項目A→α.Xβ,當X是一個終結符,且X在Follow (B) 中時,s 中沒有完整的項目B→γ.。
2) 對於在s 中的任何兩個完整項目A→α.和B→β.,Follow (A)∩ Follow(B)爲空。
若第1個條件不知足,就表示這是一個移進-歸約衝突(shift-reduce conflict)。
若第2個條件不知足,就表示這是一個歸約-歸約衝突(reduce-reduce conflict)。
例:
接着1.2.2的例子進行,分析串()()
SLR(1)分析表:
分析動做
在移進-歸約衝突中,老是選取移進而不是歸約 ,解決移進-歸約衝突。
可是歸約-歸約衝突就要複雜一些了:這樣的衝突一般(但並非老是)指出文法設計中的一個錯誤(後面將給出這樣衝突的示例)。
//待補充吧
例:文法:
這個狀態在 id 上有一個狀態:
S → id.
V → id.
的轉換。如今有 Follow (S) = {$}和 Follow (V) = { : =, $ }(因爲有規則V→V := E因此有 :=,又因爲E能夠是V,因此有$ )。所以,SLR(1)分析算法要求在這個狀態中有一個在輸入符號$下的利用規則S→ id 和規則V→ id 實現的歸約(這是一個歸約-歸約衝突)。這個分析衝突其實是一個由SLR(1)方法的缺點所引發的「假冒」問題。實際上當輸入爲$時,用V → id 實現的歸約永遠也不該該在這個狀態中,這是因爲只有到看到記號:=和被移進後,變量纔會出如今語句的末端。
同其餘分析算法同樣, SLR(1)分析算法可被擴展爲 SLR(k)分析,其中的分析動做是基於k≥1個先行的符號之上。利用上一章定義的集合Firstk和
Followk,Slr(k)分析程序使用如下兩個規則:
1) 若狀態s 包含了格式A→α.Xβ(X是一個記號),且Xw∈ Firstk(Xβ)是輸入串中以後的k個記號,那麼該動做就是將當前輸入記號移進到棧中,並且被壓入到棧中的新狀態是包含了項目A→α.Xβ的狀態。
2) 若狀態s 包含了完整項目A→α.,且w ∈ Followk(A)是輸入串中以後的k 個記號,則動做用規則A→α歸約。
當k > 1時,SLR(k)分析比SLR(1)分析更強大,但因爲分析表的大小將按k的指數倍增加,因此它又要複雜許多。非SLR(1)的典型語言構造可利用LRLA(1)分析程序處理得更好一些,它可以使用標準的消除二義性的規則,或將文法重寫。
L R ( 1 )規範( canonical)分析。這種方法解決了上一節最後所提到的SLR(1)分析中出現的問題,但它卻複雜得多。實際上在絕大多數狀況下,一般地,通常的LR(1)分析太複雜以致於不能在大多數狀況下的分析程序的構造中使用。幸運的是,通常的LR(1)分析的一個修正——稱做LALR(1) (即「先行」 LR分析)在保留了LR (1)分析的大多數優勢以外還保留了SLR(1)方法的有效性。LALR(1)方法已成爲諸如用於諸如 Yacc 這樣的分析程序生成器所選用的方法。
LR(1)項應是由LR(0)項和一個先行記號組成的對。利用中括號將LR(1)項寫做
[A→α.β α ]
其中A→α.β是一個LR(0)項,而α 則是一個記號(先行)。
LR(1)分析的自動機的非 ε- 轉換:
定義: LR(1)轉換(第1部分)的定義(definition of LR(1) transitions (part 1))。假設有LR(1)項目[A→α.Xγ, a],其中X是任意符號(終結符或非終結符),那麼X就有一個到項目[A→αX.γ, a]的轉換。
請注意在這種情形下,兩個項目中都出現了相同的先行a,因此這些轉換並不會引發新的先行的出現。只有 ε- 轉換才「建立」新的先行,以下所示。
定義: LR(1)轉換(第2部分)的定義(definition of LR(1) transitions (part 2))。假設有LR(1)項目[A→α.Bγ,a],其中B是一個非終結符,那麼對於每一個產生式B→β和在 First(γa) 中的每一個記號β都有到項目[B→.β,b] 的 ε- 轉換。
例:
文法: A→ ( A ) | a
經過擴充文法以及構造初始的LR(1)項目[A’→.A, $]來構建它的LR(1)項目集合的DFA。
狀態0: [A’→.A, $]
[A→. ( A ), $]
[A→. a, $]
狀態1: [A’→A., $]
狀態2: [A→ ( .A ), $ ]
[A→. ( A ), ) ]
[A→. a, ) ]
狀態3: [A→a ., $ ]
狀態4: [A→ ( A. ), $]
狀態5: [A→ ( . A ), ) ]
[A→ . ( A ), ) ]
[A→. a, ) ]
狀態6: [A→a ., ) ]
狀態7: [A→ ( A ) ., $]
狀態8: [A→ ( A . ), ) ]
狀態9: [A→ ( A ) ., ) ]
DFA:
通常的LR(1)分析算法令s 爲當前狀態(位於分析棧的頂部),則動做定義以下:
① 若狀態s 包含了格式[A→a.Xb, a]的任意LR(1)項目,其中X是一個終結符且是輸入串中
的下一個記號,則動做就是將輸入記號移進到棧中,且被壓入到棧中的新狀態是包含了LR(1)
項目[A→aX.b, a]的狀態。
② 若狀態s 包含了完整的LR(1)項目[A→a., a],且輸入串中的下一個記號是a,則動做就是
用規則A→a歸約。用規則S’→S (其中S是開始狀態)實現的歸約等價於接受(只有當下一個輸入
記號是$時才發生)。在其餘狀況下,新狀態的計算以下:將串a以及與它對應的全部狀態從分
析棧中刪去。相應地DFA返回到a開始構造的狀態。經過構造,這個狀態必須包括格式[B→
a.Ab, b]的L R ( 1 )項目。將A壓入到棧中,並壓入包含了項目[B→aA.b, b] 的狀態。
③ 若下一個輸入記號不是上面所述的任何一種狀況,則聲明一個錯誤。
LR(1)文法判斷:
① 對於在s 中的任何項目[A→a.Xb, a],且X是一個終結符,則在s 中沒有格式[B→b., X ]的項目(不然就有一個移進-歸約衝突)。
② 在s 中沒有格式[A→a., a]和[B→b., a]的兩個項目(不然就有一個歸約-歸約衝突)。
可從表達通常L R ( 1 )分析算法的L R ( 1 )項目集合的D FA中構造出一個分析表。該表具備與S L R ( 1 )分析程序的表格徹底相同的格式,以下例所示。
例: 接1.4.2的例子
在歸約動做中的文法規則選擇使用瞭如下的編號:
(1) A→(A)
(2) A→a
所以在狀態3中帶有先行$的項r 2指出了規則A→a 實現的歸約。
通常LR(1)分析表
例:解決SLR(1)文法的先行問題
文法:
S→ i d | V : = E
V→ i d
E→ V | n
爲這個文法構造LR(1)項目集合的D FA。其開始狀態是項目[S’→.S, $]的閉包。
狀態0:
[S’→.S, $]
[S →.i d, $]
[S →.V : = E, $]
[V →.i d, : =]
狀態1:
[S’→.S, $]
狀態2:
[S→.i d, $]
[V →.i d, . = ]
狀態3:
[S →V . : = E, $]
狀態4:
[S →V : = .E, $]
[E →.V, $]
[E →.n, $]
[V →.i d, $]
。。。
LR(1)項目集合的DFA
狀態2,這是SLR(1)分析引發的衝突狀態。LR(1)項目可由它的先行清晰地區分出兩個規約。
LALR(1)分析算法代表了它使得標識全部這樣的狀態和組合它們的先行有意義。在這樣作時,咱們老是必須以一個與LR(0)項目中的DFA相同的DFA做爲結尾,可是每一個狀態都是以帶有先行集合的項目組成。在完整項目的狀況下,這些先行集合一般比相應的Follow集合小;所以,LRLA(1)分析保留了LR(1)分析優於SLR(1)分析的一些特徵,可是仍具備在LR(0)項目中的DFA尺寸較小的特色。
LALR(1)分析的原則:
(1) LALR(1)分析的第1個原則
L R ( 1 )項目的D FA的狀態核心是L R ( 0 )項目的D FA的一個狀態。
(2) LALR(1)分析的第2個原則
如有具備相同核心的LR(1)項目的DFA的兩個狀態s1 和s2,假設在符號X上有一個從s1 到狀
態t1 的轉換,那麼在X上就還有一個從狀態s2 到一個狀態t2 的轉換,且狀態t1 和t2 具備相同的
核心。
例:續上面的例子