自底向上分析方法,也稱移進-歸約分析法,粗略地說它的實現思想是對輸入符號串自左向右進行掃描,並將輸入符逐個移入一個後進先出棧中,邊移入邊分析,一旦棧頂符號串形成某個句型的句柄時,就用該產生式的左部非終結符代替相應右部的文法符號串,這稱爲歸約。重複這一過程直到歸約到棧頂中只剩文法的開始符號時則爲分析成功,也就確認輸入串是文法的句子。
概述
設文法G[S]爲:
(1)S→aAcBe
(2)A→b
(3)A→Ab
(4)B→d
對輸入串abbcde#進行分析,檢查該符號串是否是G[S]的句子
由於自底向上分析的移進-歸約過程是自頂向下最右推導的逆過程,而最右推導爲規範推導,自左向右的歸約過程也稱爲規範歸約。
步驟 | 符號棧 | 輸入符串 | 動作 |
---|---|---|---|
1 | # | abbcde# | 移進 |
2 | #a | bbcde# | 移進 |
3 | #ab | bcde# | 歸約(A->b) |
4 | #aAb | cde# | 歸約(A->Ab) |
5 | #aAc | de# | 移進 |
6 | #aAcd | e# | 歸約(B->d) |
7 | #aAcBe | # | 歸約(S→aAcBe) |
9 | #S# | 接受 |
在上述移進-歸約或自底向上構造語法樹的過程中,考慮幾個問題:
- 何時移進?
- 何時歸約?
- 將哪個字符串歸約?
當 一個文法無二義性時,那麼它對一個句子的規範推導是唯一的,規範規約也必然是唯一的。因而每次歸約時要找當前句型的句柄,也就是說,任何時候棧中的符號串和剩餘的輸入串組成一個句型,當句柄出現在棧頂符號串中時,則可用句柄歸約,這樣一直歸約到輸入串只剩結束符,文法符號棧中只剩開始符號。
由此可見,自底向上分析的關鍵問題是在分析過程中如何確定句柄,即如何知道何時在棧頂符號串中已形成某句型的句柄。然而自底向上的分析算法很多,目前常用的算符優先分析和LR類分析法。
直觀算符優先分析法
給定文法G[E]:
E → E + E
E -> E - E
E -> E * E
E -> E / E
E -> E ↑ E 表示次方2↑3↑2 = 2↑9 = 512
E -> ( E )
E -> i
1.規定算法的優先關係如下:
1)↑ 優先級最高,具有右結合性( ↑ < ↑ )【注:表示次方2↑3↑2 = 2↑9 = 512】
2)*和/優先級低於↑,具有左結合性
3)+和-優先級最低,具有左結合性
4)(和),括號的優先級小於括號內運算符,大於括號外運算符優先級;內括號的優先級大於外括號的優先級
5)任何於#相鄰的符號的優先級都大於#
2.算符優先關係表如下
+ | - | * | / | ↑ | ( | ) | i | # | |
---|---|---|---|---|---|---|---|---|---|
+ | > | > | < | < | < | < | > | < | > |
- | > | > | < | < | < | < | > | < | > |
* | > | > | > | > | < | < | > | < | > |
/ | > | > | > | > | < | < | > | < | > |
↑ | > | > | > | > | < | < | > | < | > |
( | < | < | < | < | < | < | = | < | |
) | > | > | > | > | > | > | > | ||
i | > | > | > | > | > | > | > | ||
# | < | < | < | < | < | < | < | = |
3.輸入字符串i + i * i
步驟 | 符號棧 | 當前輸入符 | 輸入符串 | 動作 |
---|---|---|---|---|
1 | # | i | +i*i# | 移進(#<i) |
2 | #i | + | i*i# | 歸約(i>+) |
3 | #E | + | i*i# | 移進(#<+) |
4 | #E+ | i | *i# | 移進(+<i) |
5 | #E+i | * | i# | 歸約(i>*) |
6 | #E+E | * | i# | 移進(+>*) |
7 | #E+E* | i | # | 移進(*<i) |
8 | #E+E*i | # | 歸約(i>#) | |
9 | #E+E*E | # | 歸約(*>#) | |
10 | #E+E | # | 歸約(+>#) | |
11 | #E | # | acc |
算符優先分析法
算符優先分析法(Operator Precedence
Parse)是仿效四則運算的計算過程而構造的一種語法分析方法。算符優先分析法的關鍵是比較兩個相繼出現的終結符的優先級而決定應採取的動作。
給文法一點的限制,把符號優先線順序反應在文法中,再用方法求出其算符優先關係表。詳情(略)
LR(0)分析
給定文法G[E]:
(1) E -> aA|bB
(2) A -> cA|d
(3) B->cB|d
G[E]的拓廣文法,得到G [S’]:
(0) S’ -> E
(1) E -> aA|bB
(2)A -> cA|d
(3) B->cB|d
構造G[S’] 的 LR(0) FSM
LR(0)分析表
- 移進項目:如A->α•aβ,圓點後面爲終結符的項目爲移進項目
- 待約項目:如A->α•Bβ,圓點後面爲非終結符的項目爲待約項目
- 歸約項目:如A->α•,圓點在最右端的項目爲歸約項目
- 接受項目:如S’->α•,S’->α爲拓廣文法,S’爲左部的產生式只有一個,因此爲特殊的歸約項目,爲接受項目
ACTION 表項和 GOTO表項可按如下方法構造(lk表示FSM圖中的狀態,中可以0,1,2…):
若項目A ->α • aβ屬於 Ik 且 GO (Ik, a)= Ij, 期望字符a 爲終結符,則置ACTION[k, a] =sj(j表示新狀態Ij);
若項目A ->α • Aβ屬於Ik,且GO (Ik, A)= Ij,期望字符 A爲非終結符,則置GOTO(k, A)=j (j表示文法中第j個產生式);
若項目A ->α •屬於Ik, 那麼對任何終結符a, 置ACTION[k, a]=rj;其中,假定A->α爲文法G 的第j個產生式;
若項目S’ ->S • 屬於Ik, 則置ACTION[k, #]爲“acc”;
分析表中凡不能用上述規則填入信息的空白格均置上“出錯標誌”
狀態 | ACTION | GOTO | ||||||
---|---|---|---|---|---|---|---|---|
a | b | c | d | # | E | A | B | |
0 | S2 | S3 | 1 | |||||
1 | acc | |||||||
2 | S4 | S10 | 6 | |||||
3 | S5 | S11 | 7 | |||||
4 | S4 | S10 | 8 | |||||
5 | S5 | S11 | 9 | |||||
6 | r1 | r1 | r1 | r1 | r1 | |||
7 | r2 | r2 | r2 | r2 | r2 | |||
8 | r3 | r3 | r3 | r3 | r3 | |||
9 | r5 | r5 | r5 | r5 | r5 | |||
10 | r4 | r4 | r4 | r4 | r4 | |||
11 | r6 | r6 | r6 | r6 | r6 |
文法 G 是 LR(0) 文法,當且僅當它的LR(0)FSM中的每個狀態都滿足:
①不同時含有移進項目和歸約項目,即不存在移進-歸約衝突。
②不含有兩個以上歸約項目,即不存在歸約-歸約衝突。
對輸入串bccd#的分析過程
步驟 | 狀態棧 | 符號棧 | 輸入符串 | ACTION | GOTO |
---|---|---|---|---|---|
1 | 0 | # | bccd# | S3 | |
2 | 03 | #b | ccd# | S5 | |
3 | 035 | #bc | cd# | S5 | |
4 | 0355 | #bcc | d# | S11 | |
5 | 0355(11) | #bccd | # | r6 | 9 |
6 | 03559 | #bccB | # | r5 | 9 |
7 | 0359 | #bcB | # | r5 | 7 |
8 | 037 | #bB | # | r2 | 1 |
9 | 01 | #E | # | acc |
碰到r的時候,狀態棧出棧【r式子右部長度】然後根據符號棧入棧【r式子左邊字符】
SLR(1)分析
LR(0)文法不能有移進-歸約衝突及歸約-歸約衝突的限制。大多程序設計的時候不能滿足。爲了解決衝突,我們當衝突發生的時候向前查看一個符號,以確定哪個動作,這種分析方法爲簡單的LR(1),稱爲SLR(1).
詳情(略)…
LR(1)分析
SLR(1)分析法只是簡單地考察下一個輸入符號a是否屬於歸約項目A→α相關聯的FOLLOW(A),就採用產生式A->α歸約,但 a∈FOLLOW(A) 只是歸約的一個必要條件,而非充分條件。如果棧裏的符號串是βα,歸約成βA,再移當前符a,棧裏變爲βAa,而實際上βAa不一定是文件的活前綴。
A→α規約棧頂的符號α時,不僅應該查看棧中符號串δα,還應向前掃視一個輸入符號a,只有當δAa確實構成文法某一規範句型的前綴時,才能用此規則進行規約。
給定文法G[S’]:
S’->S
S->aAd
S->bAc
S->aec
S->bed
A->e
LR(1)項目集族的構造
以S′→·S,#屬於初始項目集中,把’#‘號作爲向前搜索符,表示活前綴爲γ(若γ是有關S產生式的某一右部)要歸約成S時,必須面臨輸入符爲’#'號才行。我們對初始項目S′→·S,# 求閉包後再用轉換函數逐步求出整個文法的LR(1)項目集族。具體構造步驟如下:
a)假定I是一個項目集, I 的任何項目都屬於CLOSURE(I)。
b) 若有項目 A→α·Bβ,a屬於CLOSURE(I),B→γ是文法中的產生式,β∈V*,b∈FIRST(βa), 則 B→·γ,b 也屬於CLOSURE(I)中。
c) 重複b)直到CLOSURE(I)不再增大爲止。
1.我們對初始項目S′→·S,#
2.活前綴爲γ(若γ是有關S產生式的某一右部)要歸約成S時,必須面臨輸入符爲’#'號才行
3.go(I0,a)
若有項目 S->a.Ad,#屬於I2,A→e是文法中的產生式,{d}∈V*,d∈FIRST({d#}), 則 A→·e,d 也屬於I2中
4.go(I0,b)
若有項目 S->b.Ac,#屬於I3,A→e是文法中的產生式,{c}∈V*,c∈FIRST({c#}), 則 A→·e,c 也屬於I2中
5.最終集
LR(1)分析表構造
狀態 | ACTION | GOTO | ||||||
---|---|---|---|---|---|---|---|---|
a | b | c | d | e | # | S | A | |
0 | S2 | S3 | 1 | |||||
1 | acc | |||||||
2 | S5 | 4 | ||||||
3 | S7 | 6 | ||||||
4 | S8 | |||||||
5 | S9 | r5 | ||||||
6 | S10 | |||||||
7 | r5 | S11 | ||||||
8 | r1 | |||||||
9 | r3 | |||||||
10 | r2 | |||||||
11 | r4 |
LALR(1)分析
LR(1)分析表的構造對搜索符的計算方法比較確切,對文法放寬了要求,可以解決SLR(1)方法中解決不了的問題,介是,由於它的構造對某些同心集的分裂對狀態數目引起劇烈增長,從而導致存儲容量增加,爲了克服LR(1)的這種缺點,我們可採用對LR(1)項目集規範族合併同心集的方法,若合併的同心集不產生衝突,則爲LALR(1)項目集
詳情(略)
二義性文法在LR分析中的應用
我們已經知道任何一個二義性文法絕不是LR類文法,也不是一個算符優先文法或者LL(K)文法,任何一個二義性文法不存在與其相應的確定的語法分析器,但是對於基些二義性文法,我們可以人爲地給出優先性和結合性的規定,從而可以構造出比相應非二義性文法更優越的LR分析器
給定文法G[E’]:
E’->E
E -> E + E
E ->E * E
E -> (E)
E -> i
LR(0)項目集構造
結合follow(E)={+,*,),#}得分析表
狀態 | ACTION | GOTO | |||||
---|---|---|---|---|---|---|---|
+ | * | ( | ) | i | # | E | |
0 | S2 | S3 | 1 | ||||
1 | S4 | S5 | acc | ||||
2 | S2 | S3 | 6 | ||||
3 | r4 | r4 | r4 | r4 | |||
4 | S2 | S3 | 7 | ||||
5 | S2 | S3 | 8 | ||||
6 | S4 | S5 | S9 | ||||
7 | r1,S4 | r1,S5 | r1 | r1 | |||
8 | r2,S4 | r2.S5 | r2 | r2 | |||
9 | r3 | r3 | r3 | r3 |
從圖中狀態可以看出I7和I8中存在移進-歸約衝突。但用SLR(1)和LR(K)仍不能解決。然而我們可以用優先關係和結合性可以解決。
1.規定算法的優先關係如下:
1)*優先級最高,具有左結合性
2)+優先級最低,具有左結合性
3)(和),括號的優先級小於括號內運算符,大於括號外運算符優先級;內括號的優先級大於外括號的優先級
4)任何於#相鄰的符號的優先級都大於#
2.算符優先關係表如下
+ | * | ( | ) | i | # | |
---|---|---|---|---|---|---|
+ | > | < | < | > | < | > |
* | > | > | < | > | < | > |
( | < | < | < | < | ||
) | > | > | > | > | ||
) | > | > | > | > | ||
# | < | < | < | < | = |
3.結合算符優先關係表改造得
在i7中,前算符爲+,
1.因算符優先關係表中[+>+]進行歸約
2.因算符優先關係表中[+<]進行移進
在i8中,前算符爲
1.因算符優先關係表中[>+]進行歸約
2.因算符優先關係表中[>*]進行歸約
狀態 | ACTION | GOTO | |||||
---|---|---|---|---|---|---|---|
+ | * | ( | ) | i | # | E | |
0 | S2 | S3 | 1 | ||||
1 | S4 | S5 | acc | ||||
2 | S2 | S3 | 6 | ||||
3 | r4 | r4 | r4 | r4 | |||
4 | S2 | S3 | 7 | ||||
5 | S2 | S3 | 8 | ||||
6 | S4 | S5 | S9 | ||||
7 | r1 | S5 | r1 | r1 | |||
8 | r2 | r2 | r2 | r2 | |||
9 | r3 | r3 | r3 | r3 |
4.輸入字符串i + i * i
步驟 | 狀態棧 | 符號棧 | 輸入符串 | 動作 |
---|---|---|---|---|
1 | 0 | # | i+i*i# | 移進S2 |
2 | 03 | #i | +i*i# | 歸約r4轉到【1】 |
3 | 01 | #E | +i*i# | 移進S4 |
4 | 014 | #E+ | i*i# | 移進S3 |
5 | 0143 | #E+i | *i# | 歸約r4轉到【7】 |
6 | 0147 | #E+E | * i# | 移進S5 |
7 | 01475 | #E+E* | i# | 移進S3 |
8 | 014753 | #E+E*i | # | 歸約r4轉到【8】 |
9 | 014758 | #E+E*E | # | 歸約r2轉到【7】 |
10 | 0147 | #E+E | # | 歸約r1轉到【1】 |
11 | 01 | #E | # | acc |