編譯器實現之旅——第一章 編譯器概觀
編譯器,近在咫尺卻又遠在天邊。當我們寫下任何非機器語言程式碼後,我們都需要藉助編譯器將這些程式碼變為通過電腦可運行的狀態。但是,就是這樣一個使用率極高的程式,我們對其卻知之甚少。什麼是編譯器?編譯器對我們的程式碼做了什麼?又是怎麼做的呢?如果你也懷有這些疑問,想要深入編譯器內部一探究竟的話,那就隨我一起踏上這趟編譯器實現的旅程吧。
1. 什麼是編譯器
廣義上,編譯器是這樣一個程式:其讀入A語言程式碼,並輸出B語言程式碼。如下圖所示:
+--------+
A語言程式碼 -> | 編譯器 | -> B語言程式碼
+--------+
僅從定義上看,A、B可以是同一種語言。也就是說,如果我們寫了一個只是具有「複製粘貼」功能的程式,其也可以被稱為是一個編譯器。但顯然,這樣的編譯器是無意義的。在實際中,編譯器的輸入一般是高級語言程式碼,如C語言、Python語言等,而編譯器的輸出一般是低級語言程式碼,如彙編語言、各種位元組碼等。彙編語言程式碼經由彙編語言編譯器繼續編譯,最終產生機器語言,以供電腦執行;而位元組碼可由能夠執行此位元組碼的虛擬機執行。這樣,就完成了一個程式從編寫到執行的過程。
2. 編譯器的結構組成
編譯器的內部並不是一個整體,而是由多個組件分工合作,共同完成編譯功能。這些組件總體上可被分為兩個部分:編譯器前端和編譯器後端。如下圖所示:
+------------+ +------------+
A語言程式碼 -> | 編譯器前端 | -> 中間程式碼 -> | 編譯器後端 | -> B語言程式碼
+------------+ +------------+
由於我們寫下的高級語言程式碼並不是編譯器比較喜歡的形式,故編譯器通過編譯器前端讀取、檢查並重新組織源程式碼,使之等價變換為編譯器喜歡的形式,即中間程式碼;一般來說,語法錯誤也由編譯器前端負責檢查。接下來,編譯器後端就拿著中間程式碼進行進一步的檢查、優化,最終生成目標程式碼。
事實上,編譯器前後端又分別可以進一步細分為多個組件,這些組件將在我們接下來的旅程中逐一講述。
3. 我們將要實現什麼
在這次旅程的終點,我們將實現一個名為CMM(即C Minus Minus)語言的編譯器,這個編譯器的輸出將是由我們自己設計的一套指令集中的指令所構成的指令文件。所以,我們還將實現一套虛擬機程式,以運行編譯器輸出的指令文件。
CMM語言是一門將C語言的語法進行縮減後得到的語言。其主要特點如下:
- 只有一種類型:int
- 支援賦值、四則運算與比較運算
- 支援if、while語句
- 支援函數
- 支援數組
- 區分全局作用域與局部作用域
接下來,就讓我們深入編譯器前端一探究竟吧。請看下一章:《編譯器前端概觀》。


