《編譯原理》LR 分析法與構造 LR(1) 分析表的步驟 - 例題解析

肖朋偉發表於2019-06-22

《編譯原理》LR 分析法與構造 LR(1) 分析表的步驟 - 例題解析

筆記

直接做題是有一些特定步驟,有技巧。但也必須先了解一些基本概念,本篇會通過例題形式解釋概念,會容易理解和記憶,以及解決類似問題。

如果只想做題可以直接下拉至習題部分。

(一)關於狀態

對於產生式 A→aBcD,就可以分解為下面幾個不同的識別狀態:

(1)A→.aBcD
(2)A→a.BcD
(3)A→aB.cD
(4)A→aBc.D
(5)A→aBcD.

“.” 的左部符號表示已被識別出來的那部分控制程式碼符號

狀態(1)表示:處於控制程式碼的頭
狀態(2)表示:已經識別出字元 a,等待 形成以 B 為產生式左部的右部
狀態(3)表示:剛剛進行了一次規約,即把關於 B 的產生式右部規約成 B
狀態(4)表示:已經識別出字元 c,等待 形成以 D 為產生式左部的右部
狀態(5)表示:已經到達控制程式碼的尾巴,可以把 aBcD 規約為產生式左部的符號 A

(二)什麼是 LR(k) 分析法?

字面意思理解:

字元 含義
L 表示 從左到右 掃描輸入串
R 表示利用 最右分析方法 來識別句子,即構造一個 最右推導的逆過程
k 表示向右檢視輸入串符號的個數

LR 分析過程是規範歸約的過程

規範規約是最右推導的逆過程,最右推導是規範推導,所以 最左規約是規範規約。

LR 分析法根據當前分析棧中的符號串和向右順序檢視輸入串的 k 個符號就可以唯一確定分析器的動作是移進還是歸約、利用那個產生式進行歸約。

當沒有指明 k 是幾的時候,預設為 1

(三)文法的拓廣?

文法的拓廣是對現有文法,新增一個 S',並對文法進行展開。

例如:

對於文法 G[E]:
E → E+T|T
T → T*F|F
F → i|(E)

可以把它拓廣為

文法 G[E']:
E' → E
E → E+T|T
T → T*F|F
F → i|(E)

此時可能會有疑問,不就是加了個開始符號,有什麼意義呢?為什麼要再加個開始符號呢?

加開始符號是為了狀態的表示,這樣原來的 S 會成為右部,可以表示 .S 和 S.

那同一非終結符的右部有多種情況為什麼不展開呢?

這裡是說拓廣文法,是新增開始符號,可以展開可以不展開,但是一般預設要展開,一般一道題不會只讓求拓廣文法,而是為了後面。一般題目中是說 “求該文法的拓廣文法並編號”,此時請一定要展開。展開後應該是這樣:

1.E'→E
2.E → E+T
3.E → T
4.T → T*F
5.T → F
6.F → i
7.F → (E)

(四)什麼是專案?專案有哪些分類?等價狀態?

上面提到拓廣文法,展開,以及編號。

先看例題:

對於文法 G[S]:
S → vI:T
I → I,i
I → i
T → r

可以把它拓廣並編號,如下:

文法 G[S']:
1.S' → S
2.S → vI:T
3.I → I,i
4.I → i
5.T → r

它的全部 LR(0) 專案,如下:

1.S' → .S
2.S' → S.
3.S → .vI:T
4.S → v.I:T
5.S → vI.:T
6.S → vI:.T
7.S → vI:T.
8.I → .I,i
9.I → I.,i
10.I → I,.i
11.I → I,i.
12.I → .i
13.I → i.
14.T → .r
15.T → r.

對上面 LR(0) 專案進行分類

型別 包含 特點
規約專案 2, 7, 11, 13, 15 . 在右部的末尾
接收專案 2 . 在開始符號的末尾
移進專案 3, 5, 9, 10, 12, 14 . 後面跟著終結符,表移進
待約專案 1, 4, 6, 8 . 後面跟著非終結符,表等待後面非終結符的規約,簡稱待約

誰和誰是等價狀態?

例如:

待約專案 4 即 S→v.I:T 它的含義是等待棧頂規約出 I,但尚未識別對應 I 的那些控制程式碼的任何符號;

專案 8 即 I→.I,i 和專案 12 即 I→.i 的含義也是期待棧頂形成 I 的控制程式碼,所以這三個專案的含義是一樣的,即 4, 8, 12 三個狀態是等價的。

同理:專案 6 即 S → vI:.T 和專案 14 即 T → .r 也是等價的

為什麼它們是等價狀態?怎麼判斷等價狀態?

上面有說因為他們表示的含義是一樣的,並且會發現等價肯定涉及至少一個待約專案,以及一個 . 在最左端的移進專案。

這是因為,待約專案是 . 後面跟非終結符,這個 . 是在非終結符的前面;當存在該非終結符的產生式時,且 . 在最左端的時候。因為 . 在最左端,其實也是相當於在該非終結符的前面。所以是一個等價的狀態。

(五)LR 分析表介紹

LR 分析器的關鍵部分是 分析表的構造。分析表有以下幾種:

規範的 LR 分析表:

  • LR(0),能力最弱,侷限性較大,但理論上最重要。
  • LR(1),它功能最強,但代價也最大。

簡單的 LR 分析表:

  • 簡稱 SLR ,最容易實現,但功能最弱。

向前看的 LR 分析表:

  • 簡稱 LALR,功能和代價處於前兩者之間,適用於絕大多數程式語言的文法

總結: LR(0) 功能最弱,功能弱是說當文法中產生式比較複雜,出現某些問題時,無法解決。這些問題一部分可以由 SLR 分析法解決。但還有一部分 SLR 解決不了,可以用 LR(1) 來解決。

(六)關於 “展望

在規範歸約過程中,一方面記住已移進和歸約出的整個符號串,即記住 “歷史”,另一方面根據所用的產生式推測未來可能碰到的輸入符號,即對未來進行 “展望”。

當一串貌似控制程式碼的符號串呈現於分析棧的頂端時,根據所記載的 “歷史” 和 “展望” 材料,來確定棧頂的符號串是否構成控制程式碼。

為了記住分析的 “歷史” 和彙集 “展望” 的資訊,LR 分析法這樣處理:

將歸約過程的 “歷史” 和 “展望” 材料綜合抽象成某些狀態,存放在一個狀態棧中,棧中每個狀態都概括了從分析開始直到某一歸約階段的全部“歷史”和“展望”材料。

LR(1) 分析法這樣處理:

首先,明白了在 LR(1) 分析法中展望是為了解決其他分析法解決不了的問題。簡單的說就是,狀態會出現衝突,我們不能只通過後 1 個輸入串符號,直接確定選用哪個產生式,這是嚴重的錯誤。

所以 展望 是通過展望後面的內容,所以展望對應的終結符,應該 屬於該非終結符的 FOLLOW 集(確切的說,屬於 FOLLOW 集中的具體哪個個終結符,應該根據產生式的推導過程確定,通過語法樹來分析,是最直觀的方法),來幫助唯一確定選擇產生式。

在 LR(1) 中,用

狀態, 終結符
例如:S' → # (#表示開始符號FOLLOW集會提到那個符號,有的地方用 $,是一樣的 )

這種形式是表式展望,終結符就是展望的後面的終結符,具體的下面例題中還會提到。

(七)終極例題 - LR(1) 分析表的構造

給定文法 G[S]:

S→L=R | R
L→*R | id
R→L

回答以下問題:

(1)文法的拓廣並編號
(2)LR(1) 專案集規範族所對應的識別活字首的 DFA
(3)構造 LR(1) 分析表

解析:

1)文法的拓廣並編號:

拓廣文法 G[S']:

(0)S'→S
(1)S→L=R
(2)S→R
(3)L→*R
(4)L→id
(5)R→L

2)LR(1) 專案集規範族所對應的識別活字首的 DFA*

這裡就涉及到 “展望” 這個知識點了

《編譯原理》LR 分析法與構造 LR(1) 分析表的步驟 - 例題解析

(圖片來源:中國大學慕課 -《編譯原理》哈爾濱工業大學 陳老師)

該 DFA 有窮自動機的解釋:

(1)這樣表示形式就是自動機,每個方框表示一個狀態,從 I0 到 I13 所以共有 14 個狀態。
(2)每個狀態中包含的多個專案,都是等價的。
(3)每個專案中逗號後面的終結符或者 # 表示展望的終結符。
(4)關於畫出 DFA 的步驟:

  • 以 I0 為例,首先對於 0 號產生式 S' → S,可知應該有 S' → .S 和 S' → S. 兩個狀態,因為 S' 是開始符號,展望是屬於 FOLLOW 集的,展望應該是 #,可以得出 S' → .S, #
  • 因為 .S 表示等待規約出 S 的狀態。並且 S→L=R,所以 .S 和 .L=R 是兩個等價的狀態。但需要注意的是此時的 FOLLOW 集應該 S 的 FOLLOW 集,而不是 L 的,也不 R 的
  • 同理,因為有 S→R,則 .S 和 .R 是兩個等價的狀態。
  • 有了 .R,應該繼續去找 R 為左部的產生式,因為有 R→L,所以 .S 和 .L 是兩個等價的狀態。
  • 注意: 在找 R 的展望終結符時,展望 是通過展望後面的內容,所以展望對應的終結符,應該 屬於該非終結符的 FOLLOW 集(確切的說,屬於 FOLLOW 集中的具體哪個個終結符,應該根據產生式的推導過程確定,通過語法樹來分析,是最直觀的方法)

《編譯原理》LR 分析法與構造 LR(1) 分析表的步驟 - 例題解析
(圖片來源:中國大學慕課 -《編譯原理》哈爾濱工業大學 陳老師)

可以看出來 R 的展望應該有兩種情況,一個是 =,一種是 #

但此時,我們通過 S → R 找到的 R,所以應該是 #

不斷迴圈通過,將 . 後移,判斷下一個狀態,找出等價狀態,直到判斷完成。

3)構造 LR(1) 分析表

根據自動機即可構造 LL(1) 分析表:

《編譯原理》LR 分析法與構造 LR(1) 分析表的步驟 - 例題解析
(圖片來源:中國大學慕課 -《編譯原理》哈爾濱工業大學 陳老師)

LL(1) 分析表解釋補充:

(1)內容 LL(1) 分析表 = 動作表 (ACTION) + 狀態轉移表(GOTO)

(2)動作表 中的每一個元素 ACTION[S,a] 規定了當 棧頂狀態 為 S,且面臨輸入符號 a 時應採取的動作。根據自動機中的終結符邊可判斷。

(3)狀態轉換表 中的每一個元素 GOTO[S,x] 規定了當狀態 S 面對文法符號位 x 時的下一個狀態。根據自動機中的非終結符邊可判斷。

(4)動作表 的列對應所有終結符加上 #

(5)狀態轉換表 的列對應所有非終結符,不包括 S',因為 S 就是開始符號,S' 是為了使 “接收狀態” 易於識別,所引入的。

(6)動作表 中例如:

  • ACTION[0, *] 的 S4 表示移進,入棧,就是當前狀態為 0,當輸入串為 ,則將狀態 4 移進狀態棧,將 移進文法符號棧
  • ACTION[5, =] 的 r4 表示符合產生式 4,將棧頂符號 = 規約為產生式左部
  • acc 表示接收

(7)狀態轉換表 中例如:

  • GOTO[0, S] 的 數字為 1 表示轉入 1 狀態,置當前文法符號棧頂為 S,棧頂狀態為 1

總結

易錯點:

  • 展望對應的終結符 是通過展望後面的內容,所以展望對應的終結符,應該 屬於該非終結符的 FOLLOW 集(確切的說,屬於 FOLLOW 集中的具體哪個個終結符,應該根據產生式的推導過程確定,通過語法樹來分析,是最直觀的方法)
  • 各教材描述可能存在差異,但思想是相同的
    • 比如 $ 和 #
    • 比如展望終結的表示方法,有的分開寫,有的直接用或

相關文章