這不是為了技術本身而技術。這是為了能夠實現你的想法。
想像一下,你對程式設計一無所知,想學習如何做。你瀏覽 Amazon.com,看到一本評價很高的書,作者是 Knute 或類似名字,書名是《The Art of Computer Programming》,於是你買了。現在想像一下,這不只是一個糟糕的選擇,而是所有關於程式設計的書都寫到那個程度。
這就是關於編寫編譯器的書籍的狀況。
並不是說它們是壞書,只是它們的範圍太廣泛了,作者提供了太多資訊,讓人難以入門。有些書比其他書好,但仍然有關於將正規表達式轉換為可執行狀態機的厚重章節,以及不同類型的語法等等。在艱苦地讀完所有內容後,你的知識無疑會擴展,但離實際編寫一個可工作的編譯器卻更遠了。
毫不奇怪,這些書籍的晦澀難懂導致了編譯器很難寫的神話。
打破這個神話的最佳來源是 Jack Crenshaw 的系列文章「Let's Build a Compiler!」,該系列始於 1988 年。這是技術寫作中的一顆寶石,將一個被認為是複雜的主題,最終變得適合一年級程式設計課程。他專注於 Turbo Pascal 類型的編譯器:單一傳遞,解析和程式碼生成交織在一起,並且只對生成的程式碼應用最基本的優化。最初的教學使用 Pascal 作為實現語言,但也有 C 版本。如果你真的喜歡冒險,Marcel Hendrix 已經翻譯成 Forth 版本(由於 Forth 是一種互動式語言,因此比 C 或 Pascal 原始碼更容易實驗和理解)。
儘管 Crenshaw 的系列文章很好,但有一個主要的遺漏:完全沒有程式碼的內部表示。也就是說,沒有抽象語法樹。如果你願意犧牲靈活性,確實可以繞過這個步驟,但它沒有出現在教學中的主要原因是,在 Pascal 中操作樹與他提出的其餘程式碼的簡潔性不協調。如果你在更高階的語言(Python、Ruby、Erlang、Haskell、Lisp)中工作,那麼這個擔憂就會消失。創建和操作類似樹的資料表示非常容易。事實上,這正是 Lisp、Erlang 和 Haskell 的設計目的。
這讓我想到了 Sarkar、Waddell 和 Dybvig 的論文「A Nanopass Framework for Compiler Education [PDF]」。這篇論文的細節不如其總體概念重要:編譯器不過是一系列對程式碼內部表示的轉換。作者提倡使用數十甚至數百個編譯器傳遞,每個傳遞盡可能簡單。不要合併轉換;將它們分開。標題中提到的框架是指定每個傳遞的輸入和輸出的方法。程式碼是用 Scheme 編寫的,它是動態型別的,因此資料在執行時進行驗證。
寫了一兩個編譯器之後,再去花錢買臭名昭著的龍書或其中一個替代品。也許吧。或者你可能根本不需要它們。
我是 James Hague,一個正在康復的程式設計師,自 1980 年代以來一直從事電子遊戲設計。Programming Without Being Obsessed With Programming 和 Organizational Skills Beat Algorithmic Wizardry 是不錯的起點。對於更早期的內容,可以嘗試 2012 年的回顧。