文章目錄
語法分析器
語法分析器作用
- 從詞法分析器獲得單元的序列,確認該是否可以由語言文法生成
- 對於語法錯誤的程序,報告錯誤信息
- 對於語法正確的程序,生成分析樹 (簡稱語法樹 )
語法分析的任務
- 從詞法分析中獲得的每個屬性字(token)在語句或程序中是什麼作用
- 檢查語句或程序是否符合程序語言的語法
語法分析器分類
- 自頂向下語法分析器(處理LL文法):從語法分析樹的根部開始構語法分析樹,推導式
- 自底向上語法分析器(處理LR文法):從語法分析樹的葉子開始構語法分析樹,規約式
上下文無關文法與正則文法
一個上下文無關文法(CFG)包括四部分:
- 終結符號(terminal)的集合T
- 非終結符號(nonterminal)的集合N
- 唯一的開始符號S(S ∈ N)
- 若干以下形式的產生式(production):X
其中X∈N 且Y ∈
區別: 正則表達式無法定義 ,上下文無關語言可以:S 0 EXPR 1
EXPR 0 EXPR 1 | ε
NFA轉爲上下文無關文法
- 右線性文法:AaB , Aa ,A
- 左線性文法:ABa , Aa ,A
推導與規約
- 推導(derivation):從開始符號開始,每一步推導就是用一個產生式的右方取代左端的非終端符號
- 最左推導:每步推導都替換最左邊的非終結符號
- 最右推導:每步推導都替換最右邊的非終結符號
二義性
- 文法的二義性:如果對於一個文法,存在一個句子,對這個句子可以構造兩棵不同的分析樹,那麼我們稱這個文法爲二義的.
- 原因:最左推導和最右推導可以構造出兩顆不同的分析樹
- 消除二義性例子
遞歸下降分解
- 消除二義性(只有一棵分析樹),消除左遞歸(消除A=>Aa),提取左公因子
- 判斷是否爲LL(1),是則提取預測分析表,不是則不用遞歸下降來實現語法分析
自頂向下無法處理左遞歸的情況,需要消除左遞歸;自底向上可以處理左遞歸
消除左遞歸
- A可以消除爲: ,
- 例子
- 多步左遞歸:由於兩步或多步推導而產生的左遞歸(S->A, A->S),化爲只有一條左遞歸
提取左公因子
- 方法:對每個非終結符號A找出它的可選產生式的最長公共前綴
- 例子
LL(n)
當前面步驟完成後,利於A,我們仍然是不知道要選擇哪個產生式,但是我們可以偷看輸入符號,偷看後幫助我們來選定產生式,LL(1)表示偷看1位,LL(n)表示偷看n位
LL(1)
- FIRST集
-
first(a)表示可以從a推導得到的串的首符號的集合
-
求first(X)算法
- 如果X是終結符號,first(X)={X},否則
- X,first()也屬於first(X),如果i可能爲,first()也屬於first(X),也屬於first(X)
-
求右邊產生式的first()
- 加入first()非的符號
- 如果first()有, first()非的符號也加入以此類推
- FOLLOW集
- follow(A):跟在A右邊的終結符號的集合,可以幫助我們選擇恰當的產生式
- 求follow算法
- 將右端結束標記$加入follow集
- 如果存在產生式 , 那麼first(β)的所有非 都加入到follow(B)中
- 如果存在產生式 或 , 且first(β)包含 ,那麼follow(A)的所有符號 都加入到follow(B)中
- LL(1)定義:第一個L表示輸入字符串從最左開始掃描,第二個L表示得到的推導是最左推導
對文法的任意兩個不同的產生式,
- 不存在終結符號a可以使得α和β都可以推導出以a開頭的串,α和β最多隻有一個可以推導出空串
- 如果β可以推導出空串,那麼a不能推導出以follow(A)中讓你和終結符號開頭的串
等價於
- first(α) first(β)=
- ∈first(β),那麼first(α) follow(A)=
- 構造預測分析表M步驟: 對每個產生式Aa
- 對first(A)的每個終結符號a,將產生式Aa 加入到分析表M【A,a】
- 如果first(A)中有 ,那麼將follow(A)中的每個符號b都將產生式加入到預測分析表M【A,b】中
例子
判斷是不是LL(1)文法:LL(1)文法沒有二義性,如果是二義文法,預測分析表中【X,i】會出現兩個產生式
- 分析表驅動的預測分析表:輸入緩衝區爲w$,壓入棧中爲S$, 根據預測分析表M,S遇到每個w是進行匹配還是輸出產生式,直到棧中爲空或報錯
例子
自底向上語法分析(串w規約爲文法開始符號S)
- 歸約:一個與某產生式體相匹配的特定子串被替換爲該產生式頭部的非終結符號
- 句柄:如果S=>aAw=>aβw,那麼緊跟a之後的β就是Aβ的一個句)
如果文法沒有二義性,那麼每個句型都有且只有一個句柄
句柄右邊只有終結符號(小寫)
句子是不包含非終結符號的句型,句型可能包含非終結符號或終結符號或空串
- 移入規約分析
- 使用棧保存歸約/掃描移入的文法符號:開始是$——輸入w$——結束$S
- 移入:將下一個輸入符號移入到棧頂
- 歸約:將句柄歸約到對應的非終結符號(Aβ)
- 規約分析例子:
- id爲句柄,(F ),將id歸約
- 移入下一個輸入符號
LR(n)
- L表示最左掃描,R表示反向構造出最右推導,n表示最多向前看n個符號
LR語法分析表
- LR語法分析表的結構:
-
ACTION表項:狀態1,2,,,i; 終結符號a,,,
- 移入:移入狀態j,把j壓入棧,將a移入
- 歸約:將棧頂的β(句柄)歸約到A,根據GOTO表項壓入新狀態
- 接受:接受輸入完成分析acc
- 報錯:輸入發現語法錯誤err
-
GOTO表項:[,A]=,遇到非終結符號A從狀態i跳到狀態j
- 例子分析
- 輸入移入id(狀態0),根據分析表跳到狀態
- 根據歸約成F,根據GOTO[0,F]跳轉狀態3
- 根據歸約成T,根據GOTO[0,F]跳轉狀態2
- 繼續直到E$,acc
- LR(0)
(1)增廣文法
(2)項集閉包CLOSURE:如果表示希望接下來的串是由Bβ推導出的串,首先是B推導出來的字串,因此加上B的各個產生式對應的項
- 如果,B的產生式項有,但不在CLOSURE(I)中就加入CLOSURE(I)中
- 將I中的各個項加入到CLOSURE(I)中
(3)項集規範族構造例子
(4)根據規範LR(0)構造LR(0)自動機
- 初始狀態爲CLOSURE({})對應的項集
- 每個項集對應LR(0)自動機的一個狀態
- 如果GOTO(I,X)=J,那麼從I到J有一個標號爲X的轉換
(5)根據LR(0)自動機分析
- 從開始取id,根據自動機跳轉到狀態5()
- :,知道句柄歸約爲F,返回跳轉
- :,知道句柄歸約爲T,返回跳轉
- 移入下一個 ,根據中知道跳轉
- 移入下一個 ,根據中知道跳轉
- :,知道句柄歸約爲F,返回跳轉
- :,知道句柄歸約爲T,返回
- :,知道句柄歸約爲E,返回 接受
LR(0)中可能存在歸約與移入的衝突,所以引入SLR確定應該進行移入還是歸約,例如存在E’->E(應該進行歸約),E->E+n(應該進行移入),所以遇到E時應該進行歸約還是移入,引入SLR1當$的時候歸約,+時候移入,只是狀態表中多了個接受狀態acc
- SLR(1)
S表示簡單,L表示最左掃描,R表示反向構造出最右推導,n表示最多向前看n個符號
(1)基本思想:要把α歸約成A,後面必須是follow(A)中的終結符號,否則只能移入
(2)構造語法分析表算法:
(3)判斷是不是SLR(1):構造出來的項集是否有衝突(某個時刻存在兩個有效項要求執行不同的動作)
例子: S,一個遇到L後要求移入(爲空),一個要求歸約(不爲空)
有效項:存在一個推導過程S到αAw=>,那麼說是可行前綴的有效項
如果是可行前綴的有效項:
如果不爲空的時候說明句柄尚未出現在棧中,應該移入
如果等於空的時候說明句柄已經出現在棧中,應該歸約
SLR(1)引入LR(1)是因爲SLR中可能也存在着兩種衝突,第一種是移入與歸約的衝突(與LR(0)中的不一樣)例如I->if S ; I->S else S, 而且follow(I)={$,else},此時遇到else應該進行歸約,但是下面 I->S else S遇到else應該移入所以衝突;
另一種衝突是歸約與歸約衝突如A->a,B->a;此時應該歸約爲哪個
- LR(1):精確的說明了應該何時歸約
L表示最左掃描,R表示反向構造出最右推導,n表示最多向前看n個符號
(1) [A ]:a表示要向前看的符號,按照A $\rightarrow $αβ進行歸約