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