“手把手教你構建 C 語言編譯器” 這一系列教程將帶你從頭編寫一個 C 語言的編譯器。希望通過這個系列,我們能對編譯器的構建有一定的瞭解,同時,我們也將構建出一個能用的 C 語言編譯器,儘管有許多語法並不支援。
在開始進入正題之前,本篇是一些閒聊,談談這個系列的初衷。如果你急切地想進入正篇,請跳過本章。
前言
為什麼要學編譯原理
如果要我說計算機專業最重要的三門課,我會說是《資料結構》、《演算法》和《編譯原理》。在我看來,能不能理解“遞迴”像是程式設計師的第一道門檻,而會不會寫編譯器則是第二道。
(當然,並不是說是沒寫過編譯器就不是好程式設計師,只能說它是一個相當大的挑戰吧)
以前人們會說,學習了編譯原理,你就能寫出更加高效的程式碼,但隨著計算機效能的提升,程式碼是否高效顯得就不那麼重要了。那麼為什麼要學習編譯原理呢?
原因只有一個:裝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,一個語法分析器生成器,對照《編譯原理》觀看效果更佳。
祝你學得愉快。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式