版權聲明:本文爲原創文章,未經博主允許不得用於商業用途。
語法分析
上下文無關文法(CFG)
1.1 基本定義
CFG包含如下四個組成部分:
- 終結符號:組成串的基本符號(詞法單元名,id,運算符)
- 非終結符號:表示串的集合的語法符號(如expr,stmt)
- 開始符號:某個被指定的非終結符(如expr)
- 產生式:定義了使用非終結符和終結符狗構造串的方法。
- 形式:頭(左)部→體(右)部
- 頭部是一個非終結符,右部是一個符號串
1.2 例子(c語言產生式)
![在這裏插入圖片描述]()
其中expression表示表達式,term表示術語(保留字,關鍵字)。
經過簡化,此文法可以表示成:
,其中’|'爲文法描述符,不是文法符號。
1.3 推導
推導可以看作是對產生式的解讀,如果產生式A→γ,則αAβ⇒αγβ表示“通過一步推導出”。因此推導可以看作是按照產生式規則替換。同理,定義⇒∗表示經過零步或多步推導出。
在進行推導時,如果每次都解析最左(右)邊的非終結符號,則稱爲最左(右)推導
- 句型:一個文法可以推導出的所有串的集合
- 句子:不包含非終結符號的句型
- 語言:所有句子的集合
1.4 語法分析樹
推導過程可以使用樹狀結構表示,其中:
- 根結點的標號是文法的開始符號
- 每個葉子結點的標號是非終結符號、終結符號或ε
- 每個內部結點的標號是非終結符號
- 每個內部結點表示某個產生式的一次應用
- 結點的標號爲產生式頭,其子結點從左到右是產生式的體
一棵語法分析樹可以對應多個推導序列,不過只具有一種最左(右)推導(即前序遍歷和後序遍歷唯一)。
1.5 上下文無關文法和正則表達式
上下文無關文法比正則表達式的表達能力更強:即CFG可以對兩個個體的計數(最多兩個個體)而RE(NFA)不能。
例如{anbn∣n≥1}(括號匹配)
- CFG:S→aSb∣ab
- 正則表達式:反證法,若可以表示,若存在一個具有2n個狀態的NFA可以表示此語言,則對於an+1bn+1,必有一個狀態s0有一條收到a時指向之前k個狀態的邊,因此其可以接受an+kbn+1,不在此語言中。矛盾。
設計文法
消除二義性
對於一些語法,同一個句型可以對應多個語法分析樹,如典型的if-else-then語法:
文法1:
stmt→if expr then stmt ∣ if expr then stmt else stmt ∣ other
對於if E1 then if E2 then S1 else S2有兩種解讀,即S2對應的層次可以是外層if語句,也可以是內層if語句。
可以通過規定else和最近的未匹配then匹配消除二義性,體現在文法層面如下:
文法2:
stmt→matched_stmt ∣ open_stmt
match_stmt→if expr then matched_stmt else matched_stmt ∣ other
open_stmt→if expr then stmt ∣ if expr then matched_stmt else open_stmt
消除左遞歸
- 左遞歸:文法中存在非終結符A使得A⇒+Aα
- 立即左遞歸:文法中存在非終結符A使得A→Aα
自頂向下的語法分析無法處理左遞歸,因爲在DFS時可以無限替換下去。
可以通過替換法消除左遞歸:
- 立即左遞歸變爲立即右遞歸:
- 輸入:A→Aα∣β
- 轉化爲:A→βA′,A′→αA′∣ϵ
- 多步左遞歸:
- 算法思路:按照順序替換產生式,直到立即左遞歸出現後,消除立即左遞歸。(相當於求了產生關係的閉包)
![在這裏插入圖片描述]()
提取左公因子
在CFG分析句型時,每次都通過查看下一個符號選擇產生式,因此如果兩個產生式具有相同的前綴時,則無法判斷應該選擇哪一個。
因此需要提取產生是的左公因子(相同前綴),並且將公因子轉化爲新的產生式消除相同前綴。
- 原語法:A→αβ1∣αβ2
- 提取左公因子後:A→αA′,A′→β1∣β2
自頂向下的語法分析
自頂向下語法分析按照先根次序深度優先的創建節點(對應最左推導)建立語法分析樹,一次讀入一個字符,知道完成整個輸入串的解析。
![在這裏插入圖片描述]()
此算法類似貪心算法,顯然當遇到一個錯誤時,不一定是語法錯誤,也可能是之前的產生式選擇錯誤,因此可以通過回溯改良算法。
FIRST和FOLLOW
通常在選擇產生式時使用FIRST和FOLLOW兩個函數。
- FIRST(α)定義爲可以從α推導出的首符號的集合。
- 若α爲終結符,則加入α
- 若α爲非終結符,且α→β1β2...βk
- 若θ∈FIRST(βi),ϵ∈FIRST(β1),FIRST(β2),...,FIRST(βi−1),加入θ
- 若ϵ∈FIRST(β1),FIRST(β2),...,FIRST(βk),加入ϵ
- 若α爲非終結符,且α→ϵ,加入ϵ
- FIRST(α1α2...αn)計算如下:
- 加入FIRST(α1)中所有非ϵ符號
- 若ϵ∈/FIRST(αi),ϵ∈FIRST(α1),FIRST(α2),...,FIRST(αi−1),加入FIRST(βi)
- 若ϵ∈FIRST(α1),FIRST(α2),...,FIRST(αn),加入ϵ
- FOLLOW(α)定義了可以跟隨在α右邊的終結符號集合
- 例如:S→aαbβ,則終結符b在FOLLOW(α)中
- 計算:將右端結束標記$加入FOLLOW(B)中,保證非空
- 存在S→αBβ,則將FIRST(β)中的非ϵ符號加入
- 存在A→αB or A→αBβ,ϵ∈FIRST(β),則將FOLLOW(A)中所有符號加入
LL(1)文法
LL(1)文法中的LL和1分別代表從左向右掃描輸入字符、最左推導、每一步只需向前看一個輸入字符。
對於任意一個產生式A→α∣β,滿足如下兩個條件:
- FIRST(α)∩FIRST(β)=Φ
- 若ϵ∈FIRST(β),FIRST(α)∩FOLLOW(A)=Φ,反之亦然
對於滿足上述條件的LL(1)文法,可以構造出不需要回溯的遞歸下降語法分析器。
預測分析表
爲了避免重複計算,爲語言構造預測分析表M,其中行表示非終結符號,列表示輸入符號(終結符號),每個單元記錄非終結符是否可以通過產生式推導出終結符。
構造算法:
- 對於文法G中的每個產生式A→α
- 對於FIRST(α)中的每個終結符號a,將A→α寫入M[A,a]單元中
- 如果ϵ∈First(α),對於FOLLOW(A)中的每個終結符號b,將A→α寫入M[A,b]單元中
- 算法執行後,在空白單元格填入error
由於LL(1)語法不存在二義性,因此在執行遞歸算法時對於每個輸入符號可以根據M唯一的選擇產生式。
特別的,如果出現衝突,則說明G不滿足LL(1)的條件,即具有二義性。
非遞歸預測分析
由於LL(1)文法時最左推導的,因此每次分析時只需記住尚未分析的產生式右側部分即可。
可以通過一個棧實現預測分析。具體過程如下:
![在這裏插入圖片描述]()