自製編譯器+自製腳本語言+自製編程語言 三書比較?

請問知友有人看過這三本書嗎?

本人看過 《自製腳本語言》 《自製編程語言》

自製腳本語言比較好吧,是用java從頭寫的。可以看到一個完整的實現過程。

自製編程語言這本書用到了工具,和預期的有出入。

《自製編譯器》這本書本人沒有看過,圖靈在翻譯。看過的同學希望說說自己的看法。謝謝~


這三本書的作用是入門。你看完了之後,大概對怎麼自己弄一個編譯器,就有概念上的理解了。當然你現在還是什麼都做不出來——因為凡是編程基礎紮實的,不需要看這些書也能自己鼓搗一個垃圾編譯器——所以你看完了之後,最終還是要去看parsing techniques和鯨書,還有 @RednaxelaFX聚聚推薦的垃圾收集巨著。

當然這三本書的共同特點就是,很難買(逃


基本上讀完兩周自製腳本語言了 也依照書上所說實現了一個解釋器和編譯器 git@github.com:woodgear/Dust.git

這是一本好書 對於我來說 醍醐貫頂 地位等同於編碼 讓人有種自己編譯原理總算入門的踏實感

其實感覺最重要的還是前面幾章 到第十天 就能實現一個支持類 閉包 函數 數組 的語言 但是總共的代碼量很少很少

總計13小時 爆肝的話一天就能完成一門編程語言 而且我在看之前並沒有任何編譯原理的基礎 可以想像是多麼的面對初學者

------------------------------------------------------------------------------------------------------------------------

詞法分析 句法分析 函數作用域 閉包 基本表達式 基本控制語句 閉包 通過預先計算變數保存位置用數組環境代替hash環境來優化性能 設計一個虛擬機 將代碼翻譯成二進位指令來執行(這裡真的很有趣 就像彙編一下 每個函數調用前保存恢復寄存器 通過棧指針在指令中跳來跳去) 類型推斷 類型檢查

這麼多的內容 但是不難 只要慢慢的敲的話 就能大致理解每一步的原理

總而言之 在我看來這一本是入門編譯原理的最佳選擇 它可以讓你很輕鬆的搞懂編譯過程的每一步

-----------------------------------------------------------------------------------------------------------

下面來說一說槽點 (這裡只是不吐不快 我對它是真愛)

上面列舉了許多名詞 例如詞法分析器 閉包 類型推斷 性能優化 什麼的 然而為了便於理解 作者寫的時候可謂是怎麼簡單怎麼寫 詞法分析直接用正則表達式 句法分析直接用的是作者另外寫的一個組合子庫(401行)閉包實現感覺完全就是順手為之 簡單粗暴的重新創建一個新的環境 重新執行一遍代碼 掛在作用域的最下面 太簡單了吧 書中充斥著諸如

A:如果只有4個寄存器 並且都處於佔用狀態 那麼就會崩潰

C:嗯 就會崩潰

A:這裡應該將ASTree抽象為介面更好

C: 這個。嘛 ~~

槽點滿滿

而且作者在編譯時採用了自己編寫的一個工具 GluonJ 來達到面向切面編程的效果 作者表示這是為了使Java語言實現類型與Ruby中open class的功能 (在我看來 就是一個補丁摞上另一個補丁 然後通過切換補丁來切換不同的版本 我直接使用git來記錄 這樣比較直觀)

而且我認為作者採用所謂的面向切面編程的最大原因就在這本書並不是線性發展的 前面實現 類 閉包 而後面使用虛擬機優化性能時是不支持閉包的 通過面向切面(也就是切換補丁)來講述優化應該如何實現

第14天講採用類型推斷類優化性能時 我完全沒能理解為什麼類型推斷就能優化性能了 在我看來 因為作者把推斷出來的類型用於以字元串拼接的形式轉化成java代碼

stone語言

def f(n:Int):Int{ n+1 } f(10)

通過類型轉換成java語言的字元串

public static int m(com.misakimei.stone.ArrayEnv env,int v0){
int res;
res=(v0+1);
return res;
}

通過javassist(竟然也是作者寫的)來動態執行java代碼

額 這就是為什麼類型推斷能帶來性能優化?感覺有點扯啊

---------------------------------------------------------------------------------------

編譯原理就是將字元串識別成樹,然後在樹上花式eval

這本書實際上並沒有很深入很詳細的講編譯的每個過程 但帶我在每個過程都轉了一圈

收益很大

PS:如果打分的百分制我打90分 剩下十分是因為「兩周」極大的拉低了逼格


《兩周自製腳本語言》實作的 parser 就會產生一棵 ast, 這是我比較有興趣的實作方式。

本書雖然說是用兩周的時間來學習, 但實際上需要的時間絕對是超過兩周的, 把它想成總共大概要 14 個章節來介紹會適合點。


可惜本書是用 java 來實作這個腳本語言, 我對 java 非常不熟, 連要編譯書中的範常式式都讓我大傷腦筋, 臨時惡補了一些 java 的知識。


我一直以為我會用《自己動手寫編譯器、鏈接器》來學習 compiler, 《自己動手寫編譯器、鏈接器》有建立符號表, 不過目前是《兩周自製腳本語言》幫了我最大的忙。讓我得以突破 lexer, parser, ast 的困難。

chapter 3 做了一個 lexer, 使用了 regular expression 的 library 來實作。而 chapter 15 會說明如何手工打造 lexer。


chapter 5 實作出產生 ast 的 parser。可惜我還是不知道那棵 ast 長什麼樣。這章並沒有提
到實作細節, 而是解說 parser library 怎麼使用(這其等於沒解釋 parser 怎麼實作), parser library
的原理則是 chapter 17 才說明, 使用的是一種 combinator 的作法, 我就沒特別研究了。我最後沒能看懂程式, 而是參考
chapter 16 的作法。


一般談到 ast 都會有類似 fig 1 的圖, 表示 13 + x * 2,

fig 1


不過若是

if (x&>1)
1+2;
else
5*3;


的 ast 應該長的怎麼樣呢? 我相信應該難倒你了。


長的像 fig 2 這樣。

fig 2


如果 if/else 難不倒你, 那 function declare 的 ast 應該長怎麼樣呢? 我不信還難不倒你。list 1 是我的表示方法, 我也不知道是不是對的。

list 1
root
|
prog
_____|_____
| |
var func
_|__ ____|_____
| | | |
i j para func_body
| |
x var
_|__
| |
a b

有了 ast 之後該怎麼辦呢?


chapter 6 則是實作 eval 來對那棵 ast 求值。


讓每個 node 執行 eval(), 求出每個 node 該有的值就可以了, if node 則是在 eval() predicate 後,
選擇 then node, 或是 else node 來 eval(), 層層下去後, 就可以計算出這個 expression 了。while
node 也是一樣的道理。


這章用到了 gluonj,
對於一本教學書來說, 這無疑是大大的進入門檻, 我要學的是 compiler 技術, 你卻導入了很多 java 的額外特殊功能,
我要看懂這些程式碼, 還需要去看這些東西, 大大增加了學習曲線。我覺得這不是很好的教學方式。對於不懂 java 的人來說,
額外負擔太大了。我花了點時間終於編譯出使用 gluonj 的 java 程式。又花了一點時間才知道怎麼執行。


這裡有個困難點是《環境》, 就是 sicp 4.1 講的東西, 這是變數名稱和變數直對應的東西, 由於我已經在 sicp 4.1 痛苦過了, 這部份就沒覺得那麼難了。用 std::map 來搞定《環境》吧!


最後發現 chapter 18 就談到這些東西了, 害我花了大量時間找資料, 應該要寫在同一章的。

chapter 15 講解了自動機程式, 手工寫出 lexer, 不像 chapter 3 那樣使用 regular library 那麼好寫,
但我很容易就可以看著 fig3 就寫出程式, 這是寫出 lexer 的方法之一, 很好用, lexer 雖然沒有 parser 那麼難寫,
但也不是那麼容易, 我覺得是很有趣的程式。


lexer.cpp L38 就是實作 fig 3 的 lexer。


fig 3

chapter 16 手工打造 parser, 不是使用原本的 combinator 作法, 使用 recursive descent 來做示範, 這是我最想知道的作法, recursive descent 是由上到下的作法, 以下面的 BNF 來說

expression: term {("+" | "-") term}
term: factor { ("*" | "/") factor}
factor: NUMBER | "(" expression ")"


就是從 expression 開始往 factor 去 match。由 factor 去對應到 expression 就是由下到上, yacc 產生的 parser 就是這樣的行為。

operator precedence parsing 是運算符號的優先順序處理方法, 一般都是使用 BNF 規則來定義運算符號的優先順序, 不過每增加一個規則, 程式就要重寫, 有點麻煩, 所以才有了這個方法, 我喜歡這個作法, 可以省下不少 bnf rule, 這對於手工撰寫程式的我來說, 可以省下撰寫大量的 bnf function, 所以我努力的把它看懂, 事實上也不算是太難。

expr 可以省略到這樣。

expr : factor {OP factor}

然後有段 recursive 的 code 需要努力看懂, 看懂之後得到的回報絕對值得。


對照其 java code, 我打造了 c++ 實作的 simple c interpreter, GitHub - descent/simple_compiler: simple compiler 這是我買過的書中幫助我最大的。


這三本書都不算大部頭,都是以輕量級的語言為背景講解編譯的流程。三本書中,《自製編譯器》是我「讀」起來最舒服的,但是由於Cb是基於Linux的,Windows或者Mac用戶會費寫事。

我學習編譯原理最開始讀的是龍書,讀到第四章「語法分析」我就受不了了,因為龍書里概念太多,對概念的解釋又太抽象,一個初學者讀到後續章節、甚至中間章節(比如我)就會感到混亂。比如,書中第二章提到了「終結符號」和「非終結符號」,按字面意思理解我是可以推斷出這兩個名詞的意思的,但是我還是往後讀了整個第二章才確定我的推斷是正確的。另一方面,龍書是以偽代碼作為實現講解的,但是這終究是另一層抽象,初學者不一定會理解得好。於是,我買了題目提到的三本書(和一些編譯原理「名著」)。

在龍書之後我讀的是《兩周自製腳本語言》,因為它是三本裡面最薄的。讀了 @吳聰 同學的回答,我很同意他對《兩周自製腳本語言》的評價。在練習時,我也是用Git代替了GluonJ來一步步實現功能。作者用的那個Parser庫其實思想還是挺有意思的,最起碼它的實現和在書中的使用讓我對編譯原理有了更清晰的認識。我要額外補充的評價就是「兩周」這本書的寫作方式不是我所習慣的。我更習慣先總體介紹,再細節描述的文字,而「兩周」就是一條直線的講,這讓我個人讀著不舒服。

《自製編譯器》的寫法是符合我喜好的,每到一個新章節、概念,總是先講結構,再講細節。比如,第十章「類型定義的檢查」里首先有一個「問題概要」,說「本節將對類型定義的下面3個問題進行檢查」,每個類型都有代碼實例;之後的一個小節才是「實現的概要」。這樣先講明問題(做好限定)再講內容,如起來自然清晰。

《自製編程語言》是三本書里出版最早的,因此犯了個「錯誤」。「自製」系列的目的是讓讀者快速概覽一個事物的實現,因此很多理論基礎都被作為冗餘去除了,取而代之的是總體實現的各個步驟和細節。而這類書的讀者往往是第一次接觸書中所講的事物,所以循序漸進的講解會讓讀者更好地理解。《自製編程語言》從開頭就使用lex和yacc,正則表達式幾頁帶過(為了用lex),剝奪了讀者手寫lexer的過程。後來,到了最後第八章、第九章講了類和閉包,講得中規中矩,起碼實現的各個步驟都在了。不過這本書倒是一個lex和yacc的很好的例子。

所以,我的答案很明顯《自製編譯器》是三本書里我最喜歡的,《兩周自製腳本語言》排第二。但是,就像 @ddss 說的,這類書也就是可以讓我們了解 lexer, parser, ast,最挫的優化等等。這三本書其實沒什麼好評價的,就是為學習編譯原理開個頭而已,而這個頭是一個好老師幾十分鐘就能講明白的。


看過其中一本,忘記是哪本了,跟我預想的有差距,parser部分使用的是Yacc等工具,我個人建議還是看&<&<編譯原理與實踐&>&>更好,裡面實現的語言所有東西都是手寫的.


看過《自製編程語言》和《自製編譯器》。語法方面《自製編程語言》用yacc,《自製編譯器》用javacc。實現過《自製編程語言》的crowbar GitHub - chenzl25/crowbar: implement language crowbar with self-lexer and self-parser 和《自製編譯器》的c-flat GitHub - chenzl25/cbc-in-js: implement a c-flat compiler(cbc) in javascript 。(README沒怎麼寫)

個人感覺編譯原理應該搞多幾本書一起來讀:編譯原理自學要看哪本書好? - 陳梓麟的回答

才不至於局限在某本書上。

回到題目上來,雖說《自製編程語言》和《自製編譯器》的前端部分都是用工具來實現的,但我感覺就不錯呀,至少可讀。你要是想自己實現詞法分析器和語法分析器的話可以看《龍書》的前端部分,裡面的演算法完全足夠實現一個用於詞法分析的正則表達式引擎,和一個簡單的基於SLR的類似yacc的generator。我用c++實現的crowbar就是自己寫正則表達式引擎和自己DIY的yacc。同樣c-flat編譯器cbc我也是把原來書上的javacc改成遞歸下降來實現。

《自製編程語言》是更像是一門教你寫解釋器的書,crowbar就是直接在抽象語法樹上解釋執行的。而另外一門語言則是轉為位元組碼再執行。而《自製編譯器》是直接生產彙編,再通過彙編器as和連接器ld來生產可執行的代碼。所以《自製編譯器》的實現更接近真實的編譯器。

後端進階部分就像輪子哥說的一樣看《鯨書》吧。我也嘗試把《鯨書》的優化演算法搬到cbc中,結果要覆蓋的edge case太多,失敗~~~

所以題主說的書上用到了工具,其實沒什麼問題,自己實現就是了。實現完還可以復用來應付下其它課程的作業嘛。例如:人工智慧作業的命題邏輯推理,就可以寫個小小的DSL。 GitHub - chenzl25/propositional-logic-inference: A propositional-logic-inference engine. support model-checking, resolution, forward-chaining algorithm.

還有就是《自製編譯器》裡面的代碼還有有些bug的,例如:for循環就有bug。自己實現過程中也可以抓下bug,pull request到絕雲大大這裡 GitHub - leungwensen/cbc-ubuntu-64bit: Cb (C flat) compiler. Cb is simplified C.


粗略看過《自製編程語言》這本書各部分內容寫得還算詳細,看完算是可以基本擼一個出來。個人感覺不是很過癮的就是裡面大量使用了第三方工具比如yacc。 個人對編譯器前端比較感興趣,所以看了下龍書,但是龍書理論比較多,如果想自己擼Lexer 和Parser的話,還是要看一下Esprima之類的代碼。此外《自製編程語言》的內容還涵蓋了GC、類型、鏈接等等等等,內容比《自製腳本語言》多太多。即時是後端、GC等等方面,能研究的方面也很多,這本書講得都不夠詳細,但是從領進門的角度來看,這本書算是寫得還可以,更多的是我覺得受益匪淺的是去看看Lua的代碼,不一定看官方實現,看JVM的實現也可以。

很詳細地看過《自製腳本語言》 說實話這本書在隨便翻翻就夠了,沒必要認真看。可能作者更注重實現,原理並沒有講得很清楚,實現中作者用了自己的庫,各種實現方式我也不是很喜歡,比如用正則表達式實現Lexer,CodeGen用的也是作者的,自己也沒法學到東西,所以實在不推薦。但是如果說對編譯原理沒有什麼了解,但是又想寫個解釋器玩玩的話,跟著這本教程走一遍也是可以的。

編譯原理方面個人覺得沒有哪本書是寫得很全很好的,還是要各種不同的書結合看,同時還是要多看優秀的源碼,自己擼一遍就好了。


從自製到放棄


以前看過操作系統和編程語言兩本書,我還記得自製操作系統的隨書光碟用紅傘掃出一堆毒


好吧,我覺得自製腳本語言是最差的,糅合的亂七八糟,又是組合子,又是dsl,又是自己的庫,最最基礎的理論都沒講,自製編程語言好一些,自製編譯器沒出版不好評論。


《自製編譯器》、《自製編程語言》我都看過了(快速的翻閱),我說下我看到的。

其實都不是從0開始製作一個編譯器(或編程語言),都是利用了現有的開源框架工具(yacc、lex、javacc)的基礎上做出來的,這種框架工具稱為「編譯器的編譯器(Compiler"s Compiler)」,其實就是幫助方便寫編譯器的一種框架,而不需要從0開始寫,而是寫一堆正則表達式。

《自製編譯器》

實現一種類似C語言的自製編程語言(自製編程語言=自製編譯器),不是從0開始寫編譯器,而是用javacc來做的,正如其名,是用java來寫編譯器

《自製編程語言》

這本書寫了兩種自製語言:

一種類似Perl、PHP、Ruby的解釋型語言,無類型變數(弱類型變數)、有垃圾回收,用yacc、lex框架寫,基於C語言

一種類似Java編譯生成有中間碼的語言,強類型變數(有int、double等變數類型)。

兩本書內容重疊的部分也比較少,兩本書都可以讀。

其實還是老老實實回去看《龍虎鯨》吧,這類書其實應該算是程序員趣味科普讀物,但還是值得讀的,如果想了解計算機底層編譯器內部原理,而又不想看《龍虎鯨》那樣枯燥的書,就可以看這個。


看了自製編程語言,知識點有講到,都是片段知識點代碼講解,感覺這就夠了。看之前最好先去了解yac和lex,然後,你會搗鼓這兩貨的話,那書就可以不看了。


看了自製編程語言這本書後,不推薦看!完全浪費時間。


推薦閱讀:

編寫PC單機遊戲的遊戲腳本需要哪些技術?
什麼是腳本語言?
Lua 這個腳本語言一般都用來幹什麼,有什麼優點?
類似 Lua 的腳本語言為什麼不用編譯和連接呢?
腳本類語言真的方便嗎?

TAG:編程語言 | 腳本語言 | 編譯原理 | 編譯器 |