這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
好文不多說,系列長文,大家耐心等吧。原文在此。
————翻譯分隔線————
編譯器(1)-使用 Go 開發編譯器
概述
我對編譯器的工作原理已經感興趣很久了。神秘的指令和奇異的行為總是會令我迷惑不已。也從未真正理解最佳化是如何進行的,以及編譯器又是如何知道我到底做錯了什麼。
當我決定學習如何編寫編譯器的時候,我發現有許多關於這個領域的術語和縮寫。什麼是 SLR 或 LALR 解析器?什麼是該死的詞位(lexeme)或有限自動機(finite automata)?什麼是遞迴下降分析(recursive-descent parsing)?什麼是 AST?
最開始的時候,這真是鋪天蓋地。
我發現,最大的障礙是網路上的線上教程、指南和其他學習材料並沒有教你如何編寫編譯器或解譯器。更正一下,它們沒有教你如何從零開始,而只提供了一個概覽,自頂向下的依賴已有工具來完成這個任務。由於構建一個編譯器確實是一個龐大的任務,在一個學期的時間裡想要涵蓋所有的內容放幾乎是不可能的。對此我也無法指責任何人。
即便如此,我覺得這些工具跳過了一些我覺得是有價值的、希望瞭解更多的課程和資訊。
因此,我希望通過我個人學習如何編寫編譯器的旅程,為大家帶來一個指引。
務必記得,我不是一個專家程式員,我也沒有把所有的東西琢磨清楚。只是實在忍不住想分享給大家,我發現的如此好的內容,希望對大家尋找自己的學習道路有所協助。畢竟,這也是你在這裡的原因,不是嗎?
實至名歸
計算機是最初的工作成果,而關於這個項目所有的代碼都是手工編寫的,但不可否認其與 Go 和 Go 標準庫中解譯器包的相似之處。我在學習如何編寫編譯器時,每當卡住了,我常常會借鑒它們的工作。對於任何與他們共同的工作成果相似的任何代碼所帶來的榮耀都應當歸功於 Go 開發人員。你可以在golang.org找到 Go 編譯器的原始碼。
先決條件
這個系列文章並不十分適合初級程式員。它假設你已經有一些編程經驗,並且已經熟悉 Go 語言。因此,目標讀者至少應當是中級以上的程式員。
雖然我認為不少初級程式員也可以跟得上文章,不過我不會去解釋那些不與編譯直接相關的 Go 及其他任何代碼。我會提供一些工具函數,不過由於它們與編譯並沒有明確的關係,我不會對它們進行解釋。
工具
編譯器的設計是語言設計的一部分,至少有著關聯。這是個廣泛的話題,而對於編譯器的每一個步驟都可以是一個課程。
已經有許多工具可以用於加速處理。Flex 和 Bison 多半是最常用的開源工具。Flex 基於叫做 Lex 的程式,這是 Lexical Analysis(詞法分析)的簡稱。Bison 基於 YACC,是 Yet Anther Compiler Compiler(另一個編譯器的編譯器)的縮寫。
提供一系列的規則,你可以建立一個 GCC 的前端,或產生一個你自己的編譯器。當然,如果你明白你在幹什麼的話。如果你不清楚,那就準備好受打擊吧。
事實上,我發現跳過這些工具來學習基礎的內容會另人更加開心。我認為通過手工的方式來構建整個編譯器棧對於長期來說是更好的方案,不過這僅僅是我個人的觀點。你還是得自己拿主意。
對於我來說,使用這些工具的問題在於,在編譯的各個階段之間,最終會變得支離破碎,導致失控。同時心頭總會縈繞著一個問題:“這到底是怎麼工作的?”
解決方案
這也就是為什麼在這個系列裡,我會涵蓋每個獨立的組件,並且從一點一滴開始編寫代碼。這可能會花更長的時間,不過我相信到最後你會有一個深刻的理解。如果在我們完成之後,你決定用前面提到的工具,你也會感到更加輕鬆,並且做出更加明確的決定。
開始進入第二部分,準備好下地幹活吧!