編譯原理筆記整理
編譯程式的邏輯結構
編譯程式邏輯結構上至少包含兩大階段
-
分析(Analysis)階段
理解源程式,挖掘源程式的語義
-
綜合(Synthesis)階段
生成與源程式語義上等價的目標程式
編譯程式的前端、中端和後端
-
前端(Front End)
實現主要的分析任務
通常以第一次生成中間程式碼為標誌
-
後端(Back End),與目標機相關
實現主要的綜合任務(目的碼生成和優化)
通常以從最後一級中間程式碼生成目的碼為標誌
-
中端(Middle End)
實現各級中間程式碼上的操作(中間程式碼生成與優化)
典型編譯程式的邏輯過程
詞法分析
詞法分析程式(Lexical Analyzer)或詞法掃描程式(Scanner)的作用
-
從左至右掃描構成源程式的字元流
-
識別出有詞法意義的單詞(Lexemes)
-
返回單詞記錄(由單詞記號(Token)和單詞的屬性值組成),或詞法錯誤資訊
-
除以上主要任務外,常伴有如下任務
濾掉空格,跳過註釋、換行符,追蹤換行標誌,複製出錯源程式,巨集展開,……也可能包含訪問符號表的操作
語法分析
判斷w是否是文法G的語言,如果是則給出語法分析樹。
語法分析過程主導語義分析以及翻譯的過程
語義分析
對語法分析後的程式進行語義分析,不符合語義規則時給出語義錯誤資訊
符號表
收集每個名字的各種屬性用於語義分析及後續各階段
出錯處理
-
檢查錯誤
報告出錯資訊(error reporting)
-
排錯
恢復編譯工作(error recovery)
中間程式碼生成
抽象語法樹 AST
三地址碼 TAC
目的碼生成
生成目標機程式碼
小結: 典型編譯程式的主要邏輯模組
文法基礎
1. 最左(右)推導
最左推導
最右推導
2. 0~3型文法分類及其包含關係
文法與語言的 Chomsky 分類方法
0 型文法
每個產生式左邊至少有一個非終結符
1型文法(上下文有關文法)
每個產生式右邊長度都 大於等於 左邊長度(除S→ε),且S不出現在右部
2型文法(上下文無關文法)
產生式左邊只有一個非終結符
3型文法(正規文法)
產生式左邊僅是一個非終結符
產生式右邊只由1個終結符(或ε)後面跟0到1個非終結符
能夠用3型文法定義的語言稱為3型語言或正規語言
文法之間的關係
\[3型文法 \subsetneq 2型文法 \subsetneq 1型文法 \subsetneq 0型文法 \]3. 畫出語法分析樹
語法分析樹的果實
歸約、推導與分析樹之間關係
4. 文法的二義性
二義文法(ambiguous grammars)舉例
二義文法概念
二義性的判定
消除二義性
正規式、正規文法、自動機
1. DFA 和 NFA 的等價性
定理: L 是某個 DFA 的語言, 當且僅當 L 也是某個 NFA 的語言.
ε - 閉包
個人理解:ε_CLOSURE(I)表示從 集合I 中的每一個元素出發經過 ε邊能夠到達的點(包括集合I)。
move函式
個人理解:狀態集合 I 中的元素經過a邊可以到達的點的集合
子集構造法
NFA\(\Rightarrow\)DFA
子集構造法之例
2. DFA 的化簡:通過合併等價的(或不可區別的)狀態
DFA 狀態集合上的一個等價關係定義
DFA 狀態集合上等價關係的另一種解釋
無關狀態
無用狀態:從開始狀態不能到達的狀態
死狀態:不能到達終止狀態的狀態
DFA的簡化演算法
3. 正規表示式
正規表示式算符優先順序
舉例
4. 正規式和正規文法的等價
正規式轉換為正規文法
tips:未在考試重點
個人理解:第二條可以簡化成如下
\[對於形如A\rightarrow x^*y 的正規產生式改寫為 A\rightarrow xA|y \]舉例
正規文法轉換為正規式
5. 正規式與有窮自動機的等價
從正規表示式構造等價的ε - NFA
典型正規表示式對應的狀態轉換圖
從有限自動機M構造等價的正規表示式r
舉例
6. 正規文法與構造有窮自動機的等價
有窮自動機M\(\Rightarrow\)正規文法G
正規文法G構造有窮自動機M
LL(1)自頂向下
1. 消除(直接)左遞迴
tips:間接左遞迴不在考點
左遞迴消除規則
舉例
2. 提取左公因子
提取左公因子規則
舉例
3. Select集合
First 集合
個人理解:經過任意次推導得到的句型中的第一個終結符
計算 First 集合
個人理解:
- 終結符或ε的first集合為自己
- 產生式右部為空,即\(A \rightarrow \varepsilon\) First(A)並上\(\varepsilon\)
- 產生式右部非空,則First(左部)需並上First(右部)
- First(\(Y_1Y_2...Y_k\))。找到第一個First集合不包含\(\varepsilon\)的元素\(Y_i\)。將前面所有元素的First集合並起來,減去\(\{\varepsilon\}\)。如果不存在則所有元素的First集合並起來
Follow 集合(後繼符號集)
個人理解:Follow(A)求A後面可能字串的First集合
計算 Follow 集合
個人理解:如果\(\beta\)為\(\varepsilon\),即A後面無內容,則Follow(A)需並上Follow(X)
Select集(預測集合)(Predictive Set)
4.判斷是否為LL(1)文法
舉例
5. 畫出預測分析表
表驅動 LL(1)分析程式
工作原理:藉助於預測分析表和一個下推棧
初始時,下推棧只包含#;首先將文法開始符號入棧;之後依如下步驟:
-
若棧頂為終結符,則判斷當前讀入的單詞是否與該終結符相匹配,若匹配,再讀取下一單詞繼續分析;不匹配,則進行出錯處理
-
若棧頂為非終結符,則根據該非終結符和當前輸入單詞查預測分析表,若相應表項中是產生式(唯一的),則將此非終結符出棧,並把產生式右部符號從右至左入棧;若表項為空,則進行出錯處理
-
重複(1)和(2),直到棧頂為 # 同時輸入也遇到結束符 # 時,分析結束
預測分析表
-
表驅動分析程式需要的二維表M
-
表的每一行A 對應一個非終結符
-
表的每一列a對應某個終結符或輸入結束符 #
-
表中的項 M(A,a) 表示棧頂為A,下一個輸入符號為a時,可選的產生式集合
-
對於LL(1)文法,可以構造出一個 M(A,a) 最 多隻包含一個產生式的預測分析表,可稱之為LL(1)分析表
-
M(A,a) 不含產生式時,對應一個出錯位置
預測分析表的構造演算法
舉例
6. 對輸入塊的分析過程
LR(0)
1. 增廣文法
增廣文法:使開始符號不會出現在產生式的右部
2. 畫出活字首的DFA
活字首
活字首與控制代碼的關係:一個活字首是某一右句型的字首,它不超過該右句型的某個控制代碼
-
活字首已含有該控制代碼的全部符號,表明該控制代碼對應的產生式 A→α的右部α已出現在棧頂
-
活字首只含該控制代碼的一部分符號,表明該控制代碼對應的產生式 A→α1α2 的右部子串α1 已出現在棧頂,期待從輸入串中看到α2 推匯出的符號串
-
活字首不含有該控制代碼的任何符號,此時期待從輸入串中看到該控制代碼對應的產生式 A→α的右部所推匯出的符號串
活字首集合的歸納定義:
LR(0)FSM 的狀態
LR(0)FSM 的狀態是一個特殊的 LR(0)專案(item)集
一個LR(0)專案是在右端某一位置有圓點的產生式
LR(0)專案解析
計算LR(0)專案集 I 的閉包 CLOSURE(I)的演算法
圓點在產生式右部字串的某個非終結符前,且該非終結符可以進行推導,則圓點也在該終結符推匯出的式子的前面
LR(0)FSM 的初態
例子
LR(0)FSM 的狀態轉移函式
計算 LR(0)FSM 的所有狀態的集合
LR(0)FSM 的構造說明
同一狀態的專案集中,若不同專案其後繼符號相同時, 後繼狀態也相同
不同狀態的專案集中,若出現相同的專案時,則後繼狀態也相同. (如後圖狀態I2和I4同有專案A→.cA故經c均到達狀態I2 ,狀態I2和I4也同有專案A→.d故經d均到達狀態I10 )
LR(0)FSM 的構造舉例
LR(0)FSM 的語言
3. 判斷是否為LR(0)
LR(0)文法判斷
4. action表,goto表
LR分析表
ACTION表
告訴分析引擎:在棧頂狀態為k, 當前輸入符號是 a 時做什麼
ACTION [k,a]=si | Shift:狀態 i 移進狀態棧頂,a移進符號棧頂 |
ACTION [k,a]=rj | Reduce:按第 j 條產生式A→β歸約,兩棧頂各彈出|β|個元素 |
ACTION [k,a]=acc | Accept :分析完成 |
ACTION [k,a]=err | Error :發現錯誤 (常標為空白) |
GOTO表
GOTO[i,A]=j 告訴分析引擎:在依產生式 A→β 歸約依Action完成動作後(兩棧頂各彈出|β|個元素),棧頂狀態為i 時,要將新狀態 j 移進狀態棧頂(同時: A 移入符號棧頂)
即:在棧頂狀態為k, 當前輸入符號是 A時做什麼。GOTO表就一個shift操作
LR(0)分析表的構造
LR(0)分析表的構造舉例
5. 對輸入串的分析過程
LR分析
帶符號棧的 LR 分析演算法
SLR(1)
LR(0)分析的侷限性
發現兩個狀態(專案集)存在移進-歸約衝突
向前檢視一個符號可解決衝突
SLR(1)分析思想
- 向前檢視一個符號來改進對LR(0)狀態(專案集)中移進-歸約和歸約-歸約衝突的解決
- 根據下一個輸入符號是否屬於要歸約的非終結符的Follow 集來決定是否進行歸約
- 如果LR(0)狀態(專案集)中的所有歸約項中要歸約的非終結符的 Follow 集互不相交,則可以解決歸約-歸約衝突
- 如果LR(0)狀態(專案集)中的所有歸約項中要歸約的非終結符的 Follow 集與所有移進專案要移進的符號集互不相交,則可以解決移進-歸約衝突
- SLR(0)分析表的構造也基於LR(0)FSM
- 只需對 LR(0)分析表進行簡單修改使得歸約表項只適用於相應非終結符Follow 集中的輸入符號
SLR(1)分析表的構造
SLR(1)文法
比較 LR(0)表和 SLR(1)表
在 LR(0)表的 ACTION 表中,歸約表項總是整行出現的,即一個歸約對於所有輸入符號都適用; 不會既有移進又有歸約
而在 SLR(1)表的ACTION 表中。歸約表項只適用於相應非終結符Follow 集中的輸入符號; 可以既有移進又有歸約
LR(1)
LR(1)專案
LR(1)FSM
LR(1)FSM的構造
LR(1)FSM的初態
LR(1)FSM的狀態轉移函式
計算 LR(1)專案集規範族
LR(1)FSM的構造舉例
LR(1)分析表的構造
LR(1)分析表的構造舉例
LR(1)文法
語義分析、中間程式碼
0. 語法制導的語義計算基礎(不在考點,前導概念)
概念
-
屬性文法(Attribute Grammar)在上下文無關文法的基礎上進行如下擴充套件
- 為每個文法符號關聯多個屬性(Attribute)
- 為文法的每個產生式關聯一個語義規則集合或稱為語義動作。
-
屬性
- 屬性(Attribute)可用來刻畫一個文法符號的任何我們所關心的特性,如:符號的值,符號的名字串,符號的型別,符號的偏移地址,符號被賦予的暫存器,程式碼片斷,等等…
-
記號
- 文法符號 X 關聯屬性 a 的屬性值可通過 X.a 訪問
-
語義規則(Semantic Rule)
- 在屬性文法中,每個產生式 \(A \rightarrow \alpha\) 都關聯一個語義規則的集合,用於描述如何計算當前產生式中文法符號的屬性值或附加的語義動作
-
屬性文法中允許如下語義規則
- 複寫(copy)規則,形如X.a := Y.b
- 基於語義函式(semantic function)的規則,形如
b:=f(c1, c2, …, ck) 或 f(c1, c2, …, ck)
其中,b,c1, c2, …, ck是該產生式中文法符號的屬性
-
綜合屬性(synthesized attribute)
-
用於“自下而上”傳遞資訊
- 對關聯於產生式 \(A \rightarrow \alpha\) 的語義規則 b:=f(c1, c2, …, ck) ,如果 b 是 A 的某個屬性, 則稱 b 是 A 的一個綜合屬性
-
綜合屬性的例子
-
-
繼承屬性(inherited attribute)
-
用於“自上而下”
-
傳遞資訊對關聯於產生式$$A \rightarrow \alpha$$ 的語義規則 b:=f(c1, c2, …, ck) ,如果 b 是產生式右部某個文法符號 X 的某個屬性,則稱 b 是文法符號 X 的一個繼承屬性
-
繼承屬性的例子
繼承屬性代表自上而下傳遞的資訊
-
基於屬性文法的語義計算
S-屬性文法
- 只包含綜合屬性
- 可以自下而上計算分析樹中節點的屬性
L-屬性文法
L-屬性文法的語義計算
-
採用自上而下的方式可以較方便地進行
-
可以採用下列基於深度優先後序遍歷的演算法
-
該演算法與自上而下預測分析過程對應. 因此,基於 LL(1) 文法的 L-屬性文法可以採用這種方法進行語義計算.隨後將結合翻譯模式的進一步討論分析程式的構造)
採用基於深度優先後序遍歷演算法進行 L-屬性文法的語義計算舉例
翻譯模式(Translation Scheme)概念
- 適合語法制導語義計算的另一種描述形式
- 可以體現一種合理呼叫語義動作的翻譯演算法
- 形式上類似於屬性文法,但允許由{ }括起來的語義規則集合出現在產生式右端的任何位置. 這樣做的好處是可以顯式地表達動作和屬性計算的次序,而在前述的屬性文法中不體現這種次序
受限的翻譯模式
- 在設計翻譯模式時,必須作某些限制,以確保每個屬性值在被訪問到的時候已經存在
- 本講僅討論兩類受限的翻譯模式
- S-翻譯模式,為僅需要綜合屬性的情形,建立一個語義規則集合,放在相應產生式右端的末尾,把屬性的計算規則加入其中即可。該模式類似與S-屬性文法,不重複討論。
- L-翻譯模式,對於既包含繼承屬性又包含綜合屬性的情形,但需要滿足:
- (1)產生式右端某個符號繼承屬性的計算必須位於該符號之前,其語義動作不訪問位於它右邊符號的屬性,只依賴於該符號左邊符號的屬性(對於產生式左部的符號,只能是繼承屬性);
- (2)產生式左部非終結符的綜合屬性的計算只能在所用到的屬性都已計算出來之後進行,通常將相應的語義動作置於產生式的尾部。
翻譯模式舉例
定點二進位制小數轉換為十進位制小數
1. 宣告語句的屬性文法
型別表示式(type expressions)
由基本型別,型別名字,型別變數,及型別構造子 (type constructor)歸納定義的表示式
分四類定義
-
基本資料型別表示式
-
純量型別表示式:bool, int, real
-
有界陣列型別表示式:array(I,T)
T∈{ bool, int, real };I 代表一個整數區間,如 1..10
-
指標資料型別表示式:pointer(T)
T∈{ bool, int, real }
-
-
積型別表示式
-
<T1, T2, …, Tn>
T1, T2, …, Tn 為上述資料型別表示式;若n=0,則表示為 < >
-
-
過程型別表示式
- fun(T)T 是上述積型別表示式
-
專用型別表示式
- type_error 專用於有型別錯誤的程式單元
- ok 專用於沒有型別錯誤的程式單元
語義函式
make_product_3 (V1.type, T.type, L.num) | 生成積型別表示式< t1,…,tm,T.type,…,T.type>T.type共L.num個,V1.type=<t1,…,tm> 。 |
num.lexval | 為詞法分析返回的單詞屬性值 |
id.entry | 指向當前識別符號對應於符號表中的表項 |
addtype(id.entry,L.in) | 表示將屬性值 L.in 填入當前識別符號在符號表表項中的type 域( 記錄識別符號的型別) |
id.name | 為當前識別符號的名字; |
lookup_type(id.name) | 從符號表中查詢名字為 id.name 的識別符號所對應的表項中 type 域的內容,若未查到該表項或表項中的 type 域無定義,則返回 nil。 |
type_error | 專用於有型別錯誤的程式單元 |
ok | 專用於沒有型別錯誤的程式單元 |
match( fun (type1), type2) | 返回 true,當且僅當 type1, type2 是完全相同的積型別表示式 |
id.place | id 對應的儲存位置 |
E.place | 用來存放 E 的值的儲存位置 |
E.code | E 求值的 TAC 語句序列(可不止一條四元式) |
S.code | 對應於 S 的 TAC 語句序列 |
newtemp | 在符號表中新建一個名字,返回該名字儲存位置 |
newlabel | 返回一個新的語句標號 |
gen | 生成一條 TAC 語句 |
|| | TAC 語句序列之間的連結運算 |
型別檢查程式的設計
語法制導的方法
- 將型別表示式作為屬性值賦給程式各個部分
- 設計恰當的翻譯模式
- 可實現相應語言的一個型別系統
語法制導的型別檢查程式 —— 舉例
處理宣告的翻譯模式
處理表達式的翻譯模式(不在考點)
處理語句、過程宣告及程式的翻譯模式(不在考點)
2. 寫出四元式序列(布林表示式,短路求值)
布林表示式的語法制導翻譯
直接對布林表示式求值
翻譯流程控制布林表示式至短路程式碼(L-翻譯模式)
語法分析樹
通過控制流體現布林表示式的語義
執行時儲存組織
1. 棧區、活動記錄、靜態鏈和動態鏈
棧式儲存分配
- 用於有效實現可動態巢狀的程式結構
- 如實現過程/函式,遞迴,塊層次結構
- 比較:靜態分配不宜實現遞迴過程/函式
- 執行的時候才能確定資料物件的儲存分配結果
- 過程/函式的實現中,執行棧中的資料單元是活動記錄(activation record,後面專門討論)
- 執行時每當進入一個過程/函式,就在棧頂為該過程/函式分配存放活動記錄的資料空間;工作完畢返回時,棧頂的活動記錄資料空間隨即釋放
- 在過程/函式的某一次執行中,其活動記錄中會存放生存期在該過程/函式本次執行中的資料物件,以及必要的控制資訊單元。
- 在編譯期間, 過程/ 函式以及巢狀程式塊的活動記錄大小(最大值) 應該是可以確定的,如果不滿足則應該使用堆式儲存管理
活動記錄
函式/過程呼叫或返回時,在執行棧上建立或從執行棧上消去的棧幀(frame)
包含區域性變數,函式實參,臨時值(用於表示式計算的中間單元)等資料資訊以及必要的控制資訊
過程活動記錄的棧式分配舉例
典型的過程活動記錄結構
活動過程記錄舉例
靜態鏈
Display 表的方法要用到多個儲存單元或多個暫存器。
一種可選的方法是採用靜態鏈,也稱訪問鏈、存取鏈
所有活動記錄都增加一個靜態鏈(如在offset 為 0 處)的域,指向定義該過程的直接外過程(或主程式)執行時最新的活動記錄
過程返回時, 當前活動記錄要被撤銷,為回( unwind)到呼叫過程的活動記錄(恢復FP),需要在被呼叫過程的活動記錄中有這樣一個域,即動態鏈,指向該呼叫過程的活動記錄(的基址),也稱控制鏈
個人理解:靜態鏈指向定義該過程的外過程,動態鏈指向呼叫該過程的外過程
靜態鏈動態鏈舉例
程式碼生成
1. 支配節點集、回邊、自然迴圈
基本塊(basic block)
概念
- 程式中一個順序執行的語句序列
- 只有一個入口語句和一個出口語句
- 除入口語句外其他語句均不可以帶標號
- 除出口語句外其他語句均不可能是轉移或停語句
入口語句
- 程式的第一個語句
- 條件轉移語句或無條件轉移語句的轉移目標語句
- 緊跟在條件轉移語句後面的語句
劃分基本塊的演算法
-
針對三地址碼(TAC)
-
步驟
- 出 TAC 程式之中各個基本塊的入口語句
- 對每一入口語句,構造其所屬的基本塊。它是由該語句到下一入口語句(不包括下一入口語句),或到一轉移語句(包括該轉移語句),或到一停語句(包括該停語句)之間的語句序列組成的
- 凡未被納入某一基本塊的語句,都是程式中控制流程無法到達的語句,因而也是不會被執行到的語句,可以把它們刪除
-
舉例
流圖(flow graph)
概念
- 可以為構成程式的基本塊增加控制流資訊,方法是構造一個有向圖,稱之為流圖或控制流圖(CFG,Control-Flow Graph)
流圖以基本塊集為結點集;第一個結點為含有程式第一條語句的基本塊;從基本塊 i 到基本塊 j 之間存在有向邊當且僅當
- 基本塊 j 在程式的位置緊跟在 i 後,且 i 的出口語句不是轉移 (可為條件轉移)語句、停語句或者返回語句;
- 或者i 的出口是 goto(S) 或 if …goto(S), 而 (S) 是 j 的入口語句
舉例
迴圈(loop)
支配結點集(dominators)
如果從流圖的首結點出發,到達 n 的任意通路都要經過 m,則稱 m 支配 n, 或 m 是 n 的支配結點,記為 m DOM n(∀a. a DOM a)
結點n 的所有支配結點的集合, 稱為結點 n 的支配結點集,記為D(n)
注意:支配結點也稱必經節點,支配節點集也稱必經節點集
支配結點集舉例
自然迴圈(natural loop)
假設 n→d 是流圖中的一條有向邊,如果 d DOM n 則稱 n→d 是流圖中的一條回邊(back edge)
有向邊 n→d 是回邊,它對應的自然迴圈是由結點 d ,結點 n 以及有通路到達 n 而該通路不經過 d 的所有結點組成,並且 d 是該迴圈的唯一入口結點
同時,因 d 是 n 的支配結點,所以 d 必可達該迴圈中任意結點
注:流圖中的任何結點都是從首結點可達的
自然迴圈舉例