編譯原理 第四章 語法分析
構造預測分析程序、狀態轉換圖、預測分析表
例1 給出 文法 G1:
- E → E + T | T
- T → T * F | F
- F → ( E ) | id
- 首先,改寫文法,消除左遞歸(只有LL(1)文法需要消除左遞歸),得到 文法 G1’:
- E → T E ’
- E ’ → + T E ’ | ε
- T → F T ’
- T ’ → * F T ’ | ε
- F → ( E ) | id
- 構造狀態轉換圖:
- 狀態轉換圖的化簡:
- 預測分析程序:
- E 的過程:
void procE(void) {
procT();
if (char == '+') {
forward pointer;
procE();
}
}
- T 的過程:
void procT(void) {
procF();
if (char == '*') {
forward pointer;
procT();
}
}
- F 的過程:
void procF(void) {
if (char == '(') {
forward pointer;
procE();
if (char == ')')
forward pointer;
else error();
};
else if (char == 'id')
forward pointer;
else error();
}
- 作預測分析表:
- 對於 E → T E ’:由於 FIRST(T E ’) = FIRST(T) = { (,id },故應把 E → T E ’ 放入表項 M [ E,( ] 和 M [ E,id ] 中
- 對於 E ’ → + T E ’:由於 FIRST(+ T E ’) = { + },故應把 E ’ → + T E ’ 放入表項 M [ E ’,+ ] 中
- 對於 E ’ → ε:由於FOLLOW(E ’) = {$,) },故應把 E ’ → ε 放入表項 M [ E ’,$] 和 M [ E ’,) ] 中
- 依次檢查其餘產生式,得到表 M
構造FIRST集合、FOLLOW集合、LL(1)文法
- FIRST集合
- FOLLOW集合
- LL(1)文法
規範歸約、LR分析程序的工作過程、LR(0)項目
- 規範歸約(最左歸約)過程是最右推導(規範推導)的逆過程,由最右推導得到的右句型也稱爲規範句型
- 例2 具有如下產生式集合的 文法 G2 的 LR 分析表 如圖所示,利用該分析表分析輸入符號串 id + id * id
(1)E → E + T (2)E → T (3)T → T * F
(4)T → F (5)F → ( E ) (6)F → id
(Si 中的 S 表示“移進”,即把當前輸入符號和狀態 i 壓入棧,i 成爲新的棧頂;Rj 中的 R 表示“歸約”,即用第 j 個產生式進行歸約;ACC 表示“接受”;空白表示“出錯”)
- 分析動作:
- 常用的 LR 分析表有 SLR(1) 分析表、LR(1) 分析表、LALR(1) 分析表
- 產生式 A → X Y Z 對應有 4 個 LR(0) 項目:
(1)A → • X Y Z
(2)A → X • Y Z
(3)A → X Y • Z
(4)A → X Y Z •- 歸約項目:圓點在產生式最右端的 LR(0) 項目,如 A → X Y Z •
- 接受項目:對文法開始符號的歸約項目,如 S ’ → S •
- 移進項目:圓點後第一個符號爲終結符號的 LR(0) 項目,如 A → α • a β
- 待約項目:圓點後第一個符號爲非終結符號的 LR(0) 項目,如 A → α • B β
構造SLR(1)分析表
例3 構造如下 文法 G3 的 LR(0) 項目集規範族 及 識別所有活前綴的 DFA,並構造其 SLR(1) 分析表
- S → a A | b B
- A → c A | d
- B → c B | d
- 構造文法 G3 的 拓廣文法 G3’:
- S ’ → S
- S → a A | b B
- A → c A | d
- B → c B | d
- 首先,構造活前綴 ε 的 LR(0) 有效項目集,記爲 I0:
I0 = closure( { S ’ → • S } ) = { S ’ → • S,S → • a A,S → • b B }
現在,從 I0 出發構造其他活前綴的 LR(0) 有效項目集
從 I0 出發的轉移有:
I1 = go( I0,S ) = closure( { S ’ → S • } ) = { S ’ → S • }
I2 = go( I0,a ) = closure( { S → a • A } ) = { S → a • A,A → • c A,A → • d }
I3 = go( I0,b ) = closure( { S → b • B } ) = { S → b • B,B → • c B,B → • d }
由於 I0 是活前綴 ε 的 LR(0) 有效項目集,所以 I1、I2、I3 分別是活前綴 S、a、b 的 LR(0) 有效項目集;由於 I1 中唯一的元素爲接受項目 S ’ → S •,故沒有從 I1 出發的轉移
從 I2 出發的轉移有:
I4 = go( I2,A ) = closure( { S → a A • } ) = { S → a A • }
I5 = go( I2,c ) = closure( { A → c • A } ) = { A → c • A,A → • c A,A → • d }
I6 = go( I2,d ) = closure( { A → d • } ) = { A → d • }
I4、I5、I6 分別是活前綴 aA、ac、ad 的 LR(0) 有效項目集
從 I3 出發的 • • • • • •
至此,不再有新的有效項目集出現,上面構造的 LR(0) 有效項目集 I0、I1、I2、I3、• • • 、I11 組成的 集合 C = { I0、I1、• • • 、I11 } 就是文法 G3 的 LR(0) 項目集規範族 - 識別文法 G3 的所有活前綴的 DFA:
- 構造 SLR(1) 分析表,並判斷該文法是否爲 SLR(1) 文法:
考察 I0 = { S ’ → • S,S → • a A,S → • b B }
對項目 S ’ → • S,有 go( I0,S ) = I1,所以置 goto[ 0,S ] = 1
對項目 S → • a A,有 go( I0,a ) = I2,所以置 action[ 0,a ] = S2
對項目 S → • b B,有 go( I0,b ) = I3,所以置 action[ 0,b ] = S3
考察 I1 = { S ’ → S • }
項目 S ’ → S • 是接受項目,所以置 action[ 1,$] = ACC
考察 I2 = { S → a • A,A → • c A,A → • d }
對項目 S → a • A,有 go( I2,A ) = I4,所以置 goto[ 2,A ] = 4
對項目 A → • c A,有 go( I2,c ) = I5,所以置 goto[ 2,c ] = S5
對項目 A → • d,有 go( I2,d ) = I6,所以置 goto[ 2,d ] = S6
考察 I3 = { S → b • B,B → • c B,B → • d }
對項目 S → b • B,有 go( I3,B ) = I7,所以置 goto[ 3,B ] = 7
對項目 B → • c B,有 go( I3,c ) = I8,所以置 goto[ 3,c ] = S8
對項目 B → • d,有 go( I3,d ) = I9,所以置 goto[ 3,d ] = S9
考察 I4 = { S → a A • }(假設產生式 S → a A 的編號爲 1)
項目 S → a A • 是歸約項目,因爲 FOLLOW( S ) = {$},所以置 action[ 4,$] = R1
考察 I5 • • • • • •
最終可得到 SLR(1) 分析表 如下
由於表中不存在任何衝突,所以 文法 G3 是 SLR(1) 文法
LR(0)文法、SLR(1)文法
構造LR(1)分析表
-
例4 構造如下 文法 G4 的 LR(1) 項目集規範族 及 識別所有活前綴的 DFA,並構造其 LR(1)分析表
- S → C C
- C → c C | d
- 構造文法 G4 的 拓廣文法 G4’:
(0)S ’ → S
(1)S → C C
(2)C → c C
(3)C → d - 構造其 LR(1) 項目集規範族 及 識別所有活前綴的 DFA:
- 構造 LR(1)分析表:
-
例5 構造如下 文法 G5 的 LR(1) 項目集規範族 及 識別所有活前綴的 DFA,並構造其 LR(1)分析表
- S → L = R
- S → R
- L → * R
- L → id
- R → L
- 拓廣文法得到 G5’:
(0)S ’ → S
(1)S → L = R
(2)S → R
(3)L → * R
(4)L → id
(5)R → L - 構造 文法 G5 的 LR(1) 項目集規範族 及 識別所有活前綴的 DFA:
- 構造 LR(1)分析表(由於表中不含有多重定義的表項,所以該文法是 LR(1) 文法):
LR(1)項目集特徵
- 同心集:
如果 兩個 LR(1) 項目集 去掉向前看符號之後是相同的,即它們具有相同的心(core),則稱這兩個項目集是同心集
LR(1) 項目集 的心就是一個 LR(0) 項目集 - 項目集的核:
除初態項目集外,一個項目集的核(kernel)是由該項目集中那些圓點不在最左邊的項目組成的集合
LR(1) 初態項目集 的核中有且只有項目 [ S ’ → • S,$]
構造LALR(1)分析表
- 思想:
(1)合併 LR(1) 項目集規範族 中的 同心集(,以減少分析表的狀態數)
(2)用 項目集的核 代替 項目集(,以減少項目集所需的存儲空間)
注:同心集合並後的項目集中 只可能 出現 歸約—歸約衝突,絕不可能 出現 移進—歸約衝突 - 步驟:
(1)構造 LR(1) 項目集規範族,如果 不存在衝突,說明該文法是 LR(1) 文法
(2)檢查 LR(1) 項目集規範族中有沒有 同心集,若沒有,則該 LR(1) 分析表 就是 LALR(1) 分析表,若有,則 合併同心集
(3)檢查合併同心集後的項目集規範族是否有 歸約—歸約衝突,若有,則 不存在 LALR(1) 分析表,若沒有,則根據它可以 構造文法的 LALR(1) 分析表 - 例6 構造 文法 G4 的 LALR(1) 分析表
- 在 例4 中,已經構造出該文法的 LR(1) 項目集規範族,也給出了 識別所有活前綴的 DFA,並且構造出了該文法的 LR(1) 分析表,說明 文法 G4 是 LR(1) 文法
- 構造 LALR(1) 分析表:
(i) 其 LR(1) 項目集規範族 有三對 同心集 可以合併,即
(ii) 計算 轉移函數 go
先看 go( I36,C ),存在 go( I3,C ) = I8、go( I6,C ) = I9,因 I8、I9 都是 I89 的一部分,故有 go( I36,C ) = I89
再看 go( I2,c ),由於原來的 LR(1) 項目集規範族 中存在 go( I2,c ) = I6,因 I6 都是 I36 的一部分,因此 go( I2,c ) = I36(action[ 2,c ] = S36)
(iii) 作出分析表,可以看出該表不存在衝突的表項,因此 文法 G4 是 LALR(1) 文法