在第一篇文章中,我們介紹了如何用上下文無關文法描述一種語言的語法,和如何使用推導和規約構造一棵語法分析樹,以及如何對文法進行轉換使之能夠更適用於語法分析。在本篇文章中,我們將介紹如何使用自頂向下的方法進行語法分析,進一步的,我們將介紹一種更高效的**分析方法。
為了下文需要和減少重複,我們先給出在下文中用到的乙個表示式文法和一些符號約定。
下面是需要用到的表示式文法,稱其為文法g:
e→te'
e'→+te'|ε
t→ft'
t'→*ft'|ε
f→(e)|id
同樣的,我們在此給出每個下文出現的符號的約定:
自頂向下的語法分析可以看作為乙個輸入的詞素序列構造語法分析樹的問題,它從語法分析樹的根結點開始,按照前序遍歷順序建立這棵語法分析樹的各個結點。實際上,前序遍歷的建立各個結點的過程就是乙個最左推導過程。
使用自頂向下方法構造一棵語法分析樹的關鍵問題是,每次必須確定應用哪乙個產生式對乙個非終結符號進行推導。對一組產生式和乙個非終結符號a,如果只有乙個形如a→α的產生式,那麼每次對a的推導只需使用這乙個產生式;但是,如果有多個左部為a的產生式如a→α|β|γ,那麼每次對a的推導就不得不決定使用哪乙個產生式了。
遞迴下降的語法分析是自頂向下語法分析的通用方法,這種方法可能需要進行回溯。
考慮文法:
s→cad
a→ab|a
我們嘗試對串cad構造語法分析樹:
可以發現,在第一次對非終結符號a進行推導後,得到的語法分析樹的句子cabd不匹配輸入cad,因此需要進行回溯;在第二次對a進行推導後,得到的語法分析樹的句子cad匹配輸入cad,到此,成功構造了串cad的語法分析樹,因此我們說串cad是符合該文法的。
遞迴下降的語法分析方法是不高效的。如果乙個文法有大量的以同乙個非終結符號作為左部的產生式,在使用遞迴下降的語法分析方法構造一棵語法分析樹時,就可能進行大量的回溯。如果我們能夠唯一確定每個非終結符號在每次推導時使用的產生式,那麼就可以高效的構造語法分析樹。
first(α)是可以從α推導得到的串的第乙個符號的集合,如果可以從α推導出ε,那麼ε也在first(α)中。
計算各個文法符號x的first(x)時,不斷應用下列規則,直到沒有新的終結符號或ε被加入first(x)中為止:
對於非終結符號a,follow(a)被定義為可能在某些句型中緊跟在a右邊的終結符號的集合,如果a是某些句型的最右符號,那麼$也在follow(a)中。
計算各個非終結符號b的follow(b)時,不斷應用下列規則,直到沒有新的終結符號被加入follow(b)中為止:
對文法g應用上述規則得到的first和follow集合分別為:
上一節介紹了如何計算乙個文法的first和follow集合,但是,我們還不清楚first和follow集合對構建語法分析樹有什麼作用。本節將如何用first和follow集合構造**分析表,這張表將幫助我們實現無回溯的遞迴下降的語法分析。
為了消除遞迴下降的語法分析過程中的回溯,我們必須確定對每個非終結符號應用哪乙個產生式而不是一次次地嘗試。假設有一組產生式a→α|β,其中α、β的首符號不同並且α、β不能都生成空串ε,對輸入符號a,我們必須確定應用哪乙個產生式:
如果a是α的首符號,那麼可以應用產生式a→α;如果a是β的首符號,那麼可以應用產生式a→β;
如果α可能生成空串ε,並且a可能緊跟在a之後,那麼也可以應用產生式a→α;如果β可能生成空串ε,並且a可能緊跟在a之後,那麼也可以應用產生式a→β;
如果前兩步都沒有找到匹配的產生式,那麼從a推導得到的串的字首都不能匹配a。
實際上,步驟1就是first(α)或者first(β),步驟2就是follow(a),步驟3報告了乙個語法錯誤,經過上述步驟後,我們可以唯一確定乙個產生式(或者發現乙個語法錯誤)。到此,我們知道了如何根據first和follow集合實現無回溯的遞迴下降的語法分析。
為了方便我們檢視每對非終結符號和輸入符號應該應用哪個產生式,我們把first和follow集合「組合」成一張表,這張表記為**分析表m[a, a],它的行頭a表示所有輸入符號和終結符號$,列頭a表示所有非終結符號,表項是零個或多個產生式:
對於first(α)中的每個非終結符號a,將a→α加入到m[a, a]中;
如果ε在first(α)中,那麼對於follow(a)中的每個終結符號b,將a→α加入到m[a, b]中;如果ε在first(α)中且$在follow(a)中,也將a→α加入到m[a,$
]中。
完成上面的操作後,如果m[a, a]中沒有產生式,那麼m[a, a]表示乙個語法錯誤。
注意,上面的規則適用於任何文法,但是左遞迴和二義性的文法構建的**分析表的某些表項可能有不止乙個產生式。
對文法g應用上面的規則,得到其**分析表如下:
對於某些文法,我們可以構造出向前看k個輸入符號的**分析器,這類文法也稱為ll(k)文法類,其中,第乙個「l」表示從左到右掃瞄輸入,第二個「l」表示產生最左推導,「k」表示在每一步中只需要向前看k個輸入符號來決定語法分析動作。通常情況下,我們只需要向前看乙個符號,因此這裡我們只介紹ll(1)文法。
乙個文法是ll(1)的,當且僅當該文法不是左遞迴和二義性的。ll(1)文法的**分析表的每個表項最多只有乙個產生式,正因如此,我們才能唯一確定對每對非終結符號和輸入符號應用的產生式。
該節的內容實質上和上文的**分析技術一樣,只不過更加系統的闡述了如何使用**分析技術從乙個輸入串得到乙個最左推導。
乙個由分析表驅動的語法分析器由輸入緩衝區、文法符號棧、**分析表和結果輸出流組成,如下圖所示:
其中的**分析過程如下:
對文法g,在上文已經得到了其**分析表,考慮輸入串id+id*id,其**分析過程如下:
上圖中棧的棧頂在左邊,把輸出組合起來就得到串id+id*id的乙個最左推導。
編譯原理 語法分析 二
在第一篇文章中,我們介紹了如何用上下文無關文法描述一種語言的語法,和如何使用推導和規約構造一棵語法分析樹,以及如何對文法進行轉換使之能夠更適用於語法分析。在本篇文章中,我們將介紹如何使用自頂向下的方法進行語法分析,進一步的,我們將介紹一種更高效的 分析方法。文法 約定 為了下文需要和減少重複,我們先...
編譯原理(二) 語法分析(二)
自底向上即從輸入字串w產生文法開始符號s的過程,相當於從葉子節點反向構造語法分析樹。對輸入從左到右 自底向上的語法分析可以最終反向構造出乙個最右推導。歸約 將與某個產生式匹配的子串替換為產生式頭部的非終結符號。控制代碼 最右句型中和某個產生式匹配的子串,即若s r m aw rm ws unders...
語法分析 編譯原理
實驗目的 對迴圈語句和條件判斷語句編寫詞法分析編譯程式,只能通過一遍掃瞄完成。用c 實現 實驗要求 1 關鍵字 for if then else while do 所有關鍵字都是小寫。2 運算子和分隔符 3 其他識別符號 id 和整型常數 num 通過以下正規式定義 id letter letter...