前言

「手把手教你構建 C 語言編譯器」 這一系列教程將帶你從頭編寫一個 C 語言的編譯器。希望通過這個系列,我們能對編譯器的構建有一定的瞭解,同時,我們也將構建出一個能用的 C 語言編譯器,儘管有許多語法並不支持。

手把手教你構建 C 語言編譯器系列共有10個部分:

手把手教你構建 C 語言編譯器(0)——前言
手把手教你構建 C 語言編譯器(1)——設計
手把手教你構建 C 語言編譯器(2)——虛擬機
手把手教你構建 C 語言編譯器(3)——詞法分析器
手把手教你構建 C 語言編譯器(4)——遞歸下降
手把手教你構建 C 語言編譯器(5)——變量定義
手把手教你構建 C 語言編譯器(6)——函數定義
手把手教你構建 C 語言編譯器(7)——語句
手把手教你構建 C 語言編譯器(8)——表達式
手把手教你構建 C 語言編譯器(9)——總結

在開始進入正題之前,本篇是一些閒聊,談談這個系列的初衷。如果你急切地想進入正篇,請跳過本章。

前言

為什麼要學編譯原理

如果要我說計算機專業最重要的三門課,我會說是《數據結構》、《算法》和《編譯原理》。在我看來,能不能理解「遞歸」像是程序員的第一道門檻,而會不會寫編譯器則是第二道。

(當然,並不是說是沒寫過編譯器就不是好程序員,只能說它是一個相當大的挑戰吧)

以前人們會說,學習了編譯原理,你就能寫出更加高效的代碼,但隨著計算機性能的提升,代碼是否高效顯得就不那麼重要了。那麼為什麼要學習編譯原理呢?

原因只有一個:裝B。

好吧,也許現在還想學習編譯原理的人只可能是因為興趣了。一方面想瞭解它的工作原理;另一方面希望挑戰一下自己,看看自己能走多遠。

理論很複雜,實現也很複雜?

我對編譯器一直心存敬佩。所以當學校開《編譯原理》的課程後,我是抱著滿腔熱情去上課的,但是兩節課後我就放棄了。原因是太複雜了,聽不懂。

一般編譯原理的課程會說一些:

  • 如何表示語法(BNF什麼的)
  • 詞法分析,用什麼有窮自動機和無窮自動機
  • 語法分析,遞歸下降法,什麼 LL(k),LALR 分析。
  • 中間代碼的表示
  • 代碼的生成
  • 代碼優化

我相信絕大多數(98%)的學生頂多學到語法分析就結束了。並且最重要的是,學了這麼多也沒用!依舊幫助不了我們學習編譯器!這其中最主要的原因是《編譯原理》試圖教會我們的是如何構造「編譯器生成器」,即構造一個工具,根據文法來生成編譯器(如 lex/yacc)等等。

這些理論試圖教會我們如何用通用的方法來自動解決問題,它們有很強的實際意義,只是對於一般的學生或程序員來說,它們過於強大,內容過於複雜。如果你嘗試閱讀 lex/yacc (或 flex/bison)的代碼,就會發現太可怕了。

然而如果你能跟我一樣,真正來實現一個簡單的編譯器,那麼你會發現,比起可怕的《編譯原理》,這點複雜度還是不算什麼的(因為好多理論根本用不上)。

項目的初衷

有一次在 Github 上看到了一個項目(當時很火的),名叫 c4,號稱用 4 個函數來實現了一個小的 C 語言編譯器。它最讓我震驚的是能夠自舉,即能自己編譯自己。並且它用很少的代碼就完成了一個功能相當完善的 C 語言編譯器。

一般的編譯器相關的教程要麼就十分簡單(如實現四則運算),要麼就是藉助了自動生成的工具(如 flex/bison)。而 c4 的代碼完全是手工實現的,不用外部工具。可惜的是它的代碼初衷是代碼最小化,所以寫得很亂,很難懂。所以本項目的主要目的:

實現一個功能完善的 C 語言編譯器 通過教程來說明這個過程。 c4 大致500+行。重寫的代碼歷時一週,總共代碼加註釋1400行。項目地址: Write a C Interpreter

聲明:本項目中的代碼邏輯絕大多數取自 c4 ,但確為自己重寫。

預警

在寫編譯器的時候會遇到兩個主要問題:

  • 麻煩,會有許多類似的代碼,寫起來很無聊。
  • 難以調試,一方面沒有很好的測試用例,另一方面需要對照生成的代碼來調試(遇到的時候就知道了)。

所以我希望你有足夠的耐心和時間來學習,相信當你真正完成的時候會像我一樣,十分有成就感。

PS. 第一篇完全沒有正題相關的內容也是希望你能有所心理準備再開始學習。

參考資料

最後想介紹幾個資料:

Let's Build a Compiler 很好的初學者教程,英文的。 Lemon Parser Generator,一個語法分析器生成器,對照《編譯原理》觀看效果更佳。 由於本人水平一般,文章、代碼難免會有錯誤,敬請批評指正!

最後祝你學得愉快。