為什么我們不用詞法分析那一套方式(正則文法、有限狀態機等)來解決語法分析?
正則文法通常什么樣?
對於文法G=(V, T, S, P),如果產生式的形式如下:
A -> xB
A -> x其中A, B屬於V,x屬於T*,則稱為右線性文法;相似的,如果產生式的形式如下:
A -> Bx
A -> x則稱為左線性文法。右線性文法和左線性文法統稱為正則文法。
例子:
G(S):
S->aS|bS
S->aA
A->aB|bB
B->a|b
上下文無關文法通常什么樣?
對於文法G=(V, T, S, P),如果產生式的形式如下:
對於任何 0型文法: α -> β :α∈(VN∪VT)* , β∈(VN∪VT)* (可以是符號也可以是字) 1. 在0型文法的基礎上,約束滿足|β|>=|α|,即1型文法(上下文有關文法)。 2. 在上下文有關文法的基礎上,約束滿足所有的產生式左邊只有一個非終結符。 例子: G(S): S -> aSa|bSbA A -> bA
很顯然的上下文無關文法包括了正則文法,是一個更大的范圍。
所以由於正則文法的表達能力,它的表述能力有限,而高級語言的語法結構合適用上下文無關文法描述。所以我們需要一套全新的算法來進行語法分析。
自上而下的語法分析
什么是自上而下的語法分析?
對於任何一個上下文無關文法,我們可以構建一個類似於下圖的語法樹。
該語法樹(將無線遞歸下去)可以用來表示文法:
G(s):
自上而下分析所面臨的問題
回溯
假設有文法:
S -> xAy
A -> **|*
對於輸入串 x**y 匹配過程如下:
1.讀第1個字符x,展開S樹,發現S有唯一產生式S->xAy。且對於產生式的右邊確實有第一個節點是葉結點(終結符),且為x。所以x得到匹配。
2.讀第2個字符*,因為S的第一個節點已經處理完畢,於是嘗試第二個節點A。發現第二個節點A並不是葉結點。
3.因為A不是葉節點,於是展開A,發現產生式右邊有兩個項:*和**,他們都滿足第一個字符是*,故可能存在兩個選擇。當第一個選擇是錯誤的時候,就需要回溯。
回溯會帶來很多麻煩事:
1.因為語義和語法處理一般同時工作,所以當回頭的時候,語義部分的工作都白費了。
2.如果存在了虛假匹配(如題用A->*處理了**的第一個*,后面出錯)使得需要復雜的回溯處理。
3.如果分析不成功,我們難於知道輸入串出錯的確切位置。
左遞歸
如果存在左遞歸的產生式(如A->Abc),那么因為是處理過程的本質是樹的深度遍歷,會導致會無窮無盡的遞歸下去。
左遞歸的消除
直接左遞歸
P->Pα|β
這意味着 β(α)*
改寫成:
P->βP'
P'->αP'|ε
更通用的:
P→Pα1 / Pα2 /…/ Pαn / β1 / β2 /…/βm
其中,αi(I=1,2,…,n)都不為ε,而每個βj(j=1,2,…,m)都不以P開頭,將上述規則改寫為如下形式即可消除P的直接左遞歸:
P→β1 P’ / β2 P’ /…/βm P’
P’ →α1P’ / α2 P’ /…/ αn P’ /ε
間接左遞歸
對於類似於:
S->Qc|c
Q->Rb|b
R->Sa|a
把S->Qc|c中的Q替換成S的表達式,就變成了直接左遞歸,再按照上述方法處理。