如何學習編譯原理?

最近想在工作之餘學習編譯原理,因為感覺編譯器的編寫很提高綜合技能。大牛們能否給些建議?這樣不至於自己瞎摸索,因為網上一般都推薦一些所謂的經典教材,如龍書、虎書等等。但我感覺自己啃這些書本有些乏味且大部分都是理論,不利於實踐。


我覺得對普通的程序員來說,編譯原理裡面有實際用途的,是parser和codegen,但是因為這兩個領域,到了2016年都沒什麼好研究的了,而且也被搞PLT的人所鄙視,所以你們看到的那些經典的教材,都沒有好好講。

在這裡我隆重推薦,一行代碼、一句公式都沒有,但是卻什麼都講明白了的:《Parsing Techniques》。第一版官網免費下載,第二版多出來的東西你們用不上不用看了。全書只講parsing,沒有後端的任何廢話(逃

人們可能會說,現在做parser的工具這麼多,學這些有用嗎?當然有用。數據結構都被封裝好了,你們不還是要學過一遍,才能把別人的庫用得高效。做parser也一樣,沒有受過訓練的人,很容易語法總結成屎,或者因為想像力不夠而無法實現自己的需求。

至於要是哪天真的需要做後端了,多半還是隨便看點資料(如Engineering a Compiler),知道點優化的概念和架構的知識,然後直接用LLVM幹了。


//昨晚用手機打的字錯誤很多現重新修改了一下 update2015.1.2

分享一下學習路線。

一開始上編譯原理課就聽不大懂,課下找來龍虎鯨翻了遍,發現詞法分析主要還是正則表達式和自動機。感覺開了一個新世界大門後找了本《自動機理論、語言和計算導論》來啃,啃完後很過癮的同時,詞法分析也就可以理解了。

之後就是語法分析,語法分析有幾個要點:語法樹、二義性、句柄。具體操作碰上了LL和LR分析。LL分析法比較簡單,主要思想就是可以向前讀入一個(常用的LL(1))詞素來決定選擇哪個語法推導規則。這種分析法可以手動實現但限制較多,比如說要讓兩個並列的規則FIRST集不相交。LR語法比LL普適,但分析過程要建立狀態轉移手寫略費事,一般用工具生成如yacc。LR中優化了狀態數後有LALR分析法,是最常用的一種分析法。語法分析理論比較繁瑣,工程中具體分析法也難實現,初學理解了基本的幾種語法分析理論後,選擇實現最簡單的LL即可。

這裡開始大三時的我已經雲里霧裡了,加上課程緊張就僅稍微了解一下中間代碼生成基本思路和公共子表達式消除規則和mips彙編的規範就開始寫PL0(精簡版的Pascal)編譯器。這個大作業讓我學到了很多東西,書上的大部分理論應用到真實工程場景都需要重新設計,比如說嵌套作用域的符號表處理和運行時訪問鏈、PL0里函數調用的引用參數會影響數據流、還有中間代碼四元式的設計取捨。總而言之在理論知識理解不深的情況下終是用比較曲折的設計完成了從PL0源碼到mips的彙編的編譯器。完成一個從詞法分析到目標代碼生成都手寫的編譯器是一件很爽的事情。

最近有時間重新看了一遍之前看的時候一知半解的龍書,發現許多不甚理解的東西都容易許多了。包括運行時棧設計和訪問鏈,實際上書上的解決方案已經說得很詳細了,不過當時缺乏感性認識而理解不了罷。在稀里糊塗實現一遍編譯器之後,會很容易理解編譯器劃分這麼多階段所要解決的問題以及工程取捨。在重看龍書之前讀了一些JVM的規範以及學了幾個函數式語言像scheme、haskell和js,看了SICP、On Lisp、The Little Scheme等奇怪的書(當然不都看完),這些學習過程都有助於理解編譯原理。

當然,語法分析依舊是比較難的地方,比如我還是沒看懂LALR分析法,所以打算去啃輪子哥推薦的《parsing techniques》,順便看一看具體工程中的編譯器實現比如準備啃的ART虛擬機…

總結一下我大概是這麼走的…先是大概理解了一些編譯理論皮毛,然後一知半解地寫個編譯器,之後大量涉獵了各種編程語言,再回來重讀理論。這樣就能建立起一個比較完備的知識圖譜,再往後就是對知識圖的不斷完善和與現實工程作結合了。


其實編譯原理並不僅僅是用來寫編譯器的,還可以在許多意想不到的領域進行應用。

比如,作為一名律師和玩票的碼農,我就用yacc來對法律文本、案例文本這種semi-structured的文本進行自動格式分析和整理,覺得yacc好有用,比hand craft一個解析器強多了。

說說我這個純外行接近編譯原理的「心路歷程」:

1、看到很多法律文本、法院文書雖然是自然語言文本,但是存在一定的結構性,所以想要寫程序去分析、整理這種結構。

2、根據自己總結的文本中發現的規則hand craft分析程序。

3、發現這樣寫出來的東西又臭又長,而且裡面有一些模式經常出現,就是一個state machine,在某種state下對應適用某個規則,而某些輸入又能使state發生切換。隱約覺得應該有什麼理論已經為這些模式而存在了。

4、同時我又覺得這些semi-structured的文本雖然不像計算機程序一樣有嚴整明確的規則,但是也不同於完全散文式的自然語言文本,能不能把它當做一個語法規則不太嚴格的程序源代碼來看待?那麼,用編譯原理中的東東來修理它?

5、在The Art of Unix Programming的一章中讀到了介紹lexer和parser的一段,有一些領悟,撿起龍書看了一陣兒,把最重要的前四五章看了。

6、在自己喜歡的Python語言中找到了一個lexyacc式的工具PLY,玩熟了,用它來實現了一個自動解析符合《立法法》第54條格式法律文本的小程序。儘管簡單,但是挺有用,可以把許多純文本的法條轉化為一個樹狀結構,這樣可以很精緻的展示這些法條文本。

有了這個成功經驗後,興趣更大了,努力在現實世界中發現更多這種似乎與實用領域相距甚遠的理論工具還能應用的領域。


如何學習編譯原理?個人不太建議一上手就拿起龍書、虎書等等來看。

學過編譯原理課程的同學應該有體會,各種文法、各種詞法語法分析演算法,非常消磨人的耐心和興緻;中間代碼生成和優化,其實在很多應用場景下並不重要(當然這一塊對於「編譯原理」很重要);語義分析要處理很多很多細節,特別對於比較複雜的語言;最後的指令生成,可能需要讀各種手冊,也比較枯燥。

編譯原理是用來做什麼的?從源語言提取需要的信息;把源語言翻譯成目標語言;自動生成滿足一定規範的文本...

有個東西叫DSL(領域專用語言):

從各種格式的數據中提取信息:XML/JSON/CSV/Excel;...

各種格式文本的轉換:word/markdown...生成到pdf/html/..

寫爬蟲從HTML中抓感興趣的內容;

做網站,實現/修改模板引擎,ORM..

自定義描述測試用例規範的腳本,然後自動生成測試用例...

心理學實驗,按一個簡單規範(語法)寫描述性句子,就能生成相關的場景圖;

xx畢業論文答謝詞生成模板,這算不算呢。。;

發揮想像力,能做的還有很多,如上面 @董成良 就提到用yacc解析法律文本...

推薦兩本書:《編程語言實現模式》,主要用ANTLR講解各種例子。《領域專用語言實戰》用Ruby、Groovy等語言講解。(這類書我感覺都寫得不怎樣,找不到很合適的。。)

現在你開始動手了:

最簡單的應用,可以放下詞法、語法分析等概念,直接用你會的語言去實現;

有時你會發現寫得很」繞「,雖然有了正則表達式會方面不少;

然後你可以試著用各種工具:yacc/lex, ANTLR, flex/bison, parsec, ply..用什麼無需計較,順手、達到目標就行。這些工具通常只是方便詞法、語法分析, 語義上的要自己處理;

不滿於處理簡單的文本,想分析真正意義上的程序語義,或者希望深入理解自己所用工具的原理,這時候你可以去看那些理論的書了;

建議從解釋器做起:

基本的解釋器,很多應用場景下,熟悉下面幾個就夠了。

  • 行解釋器:對於非常簡單的」語言「,其實可以逐行解析,邊解析邊做語義的處理。如我做的一個幾何題命題的DSL,就是按規範一行行給題目條件,然後會自動生成解題方程組;

  • 在詞法、語法解析的過程中做語義處理,經典的例子」計算器解釋器「一般都是這樣做的。

  • 先通過詞法和語法分析,生成抽象語法樹(AST),然後基於AST做語義分析。

如果對編程語言設計和實現感興趣,解釋器是弄清楚很多概念的竅門,推薦從Scheme學

  • 推薦The little schemer,另外R5R標準也才30多頁。注意多動手。
  • 然後可以看Essential of Programming Language,玩上面的各種解釋器。
  • 一兩本書是不夠的,你看的時候很可能還是搞不清很多概念。要善用網路資源,要多動手,選擇看更多書也是可以的。

就想做編譯器

當然,你可以跳過上面的步驟,事實上也沒有步驟一說。

找一本實踐性比較強的,一定要多動手(可以和另外一本理論性比較強的結合者看)

比如虎書+龍書(PS:虎書其實有3個版本,分別用Java、C、Ocaml描述,目標是實現Tiger語言

比如 @馮東 提到的Compiler Construction: Principles and Practice

其他答案答得挺好了..


這是個好問題, 我光是發現怎麼學習編譯原理就花了不少時間, 也買了不少書, 但每本書的實作都不同, 讓學習更難了。

最後我想到一個方法:

我要實作 c 語言編譯器, 畢竟書上寫的 pascal 實作我一點都不感興趣, 我又沒在用 pascal, 我在使用的是 c/c++ 語言, 實作一個自己沒在用的語言實在是沒有動力。

再來是 c 很複雜, 我要想辦法簡化它, 所以只實作 if/else, while, function call, type 只支援 char, int, 當然還有指標。c 語言沒實作指標還能算是 c 嗎?

再來是 lex, parser 都一定要自己實作, 絕對不用 flex, bison, 要練習怎麼可以偷懶呢? 這些都要自己做。

再來我選擇要產生 AST, 不用產生 AST 的作法也有, 但我要實作 AST, 這部份我在《兩周自製腳本語言》獲得此技能, 雖然我是用 c++ 實作, 但這本書的 java code 給了我很好的參考, 讓我得以突破 lexer, parser, AST 的關卡。

實作出以下的 AST 時, 那種興奮感一定很多人都可以體會。

再來的 interpreter 我就沒靠相關的 compiler 書籍了, 由於在 SICP 我實作了 4.1 的 scheme (一樣用 c++ 實作), 所以接下來的 eval 我可以靠自己的能力就完成了。在編譯實作上還缺什麼呢?

  1. 產生 asm

  2. 實作 assembler

  3. 實作 linker

  4. 實作 virtaul machine

  5. 實作 jit

我目前在實作 1, 還有很多部份需要努力。

我並不會那些編譯原理的理論, 也沒用上很複雜的資料結構, 我的 c++ 也不厲害, 只用到 if/else, while, function call, 一點 class, vector, stirng, map, 也沒用到多型, 但這樣的武器就已經足夠完成一個玩具版的練習了。

把這些都簡化實作過一次, 就相當於走過一輪了, 有興趣的部份就去深入。

source code

GitHub - descent/simple_compiler: simple compiler

至於學習的書呢? 我想你得自己找, 每個人推薦的都是好書, 但適不適合你只有你自己知道了。

編譯原理那本我覺得可以不用看, 初學者絕對沒辦法看這本書而實作出來編譯器, 連程式碼都沒有, 初學者怎麼可能自己完成所有的部份。

紙上得來終覺淺,絕知此事要躬行。


虎書非常利於實踐,強烈推薦


不二法門是讀一本好書。還沒學會是因為沒有好書。我不知道為什麼這本書被低估,在我看來遠好過其它經常被提到的「經典」:

Compiler Construction: Principles and Practice: Kenneth C. Louden: 9780534939724: Amazon.com: Books

當年我有兩個問題,直接寫信給作者,每次解答都很及時詳細。

國內有影印版,很便宜。


虎書的確有利於實踐,而且比較薄。如果真的說如何學習編譯原理,我想最好的辦法就是練習、實踐。雖然編譯原理被冠以原理二字,但是我認為這也是一個高度實踐的課,而且編寫的代碼是每一步都需要小心處理的,如你編寫的Parser的產生式,只要修改一個小地方,都會如同蝴蝶效應般影響非常大。總的來說,我認為學習編譯原理的一個辦法就是安靜下來,耐耐心心的讀一本經典的編譯原理教材,然後做完每一道習題,隨後再自己編寫一個玩具編譯器,你可以藉助LLVM的力量輕易達到這一點兒。同時不推薦使用FLEX和BISON自動生成詞法與語法分析,這樣你也沒有多少事情可以幹了. :-)


看了一下現有的答案,分幾個部分來回答這個問題。

1. 知乎上對於新手推薦的(高票)答案通常都存在很大問題。

倒不是說推薦的東西不夠經典,而是推薦的東西確實很好,走的路太彎太崎嶇,不太適合新手。像類似於『看龍書』、隨手扔一本純英文的書、沒有公式也能講清楚、『動手開始寫』這類答案看似逼格很高,分分鐘體現答題人的情懷,其實對新手毫無幫助,因為對於一個『不知道從何下手』的人來說,無論是理論經典還是直接實戰,都是不切實際的(居然還讓我直接讀英文?這得多少功底才能從讀下去到讀懂了?都說了『不知道從何下手』了)。且不去揣測這些推薦的真正目的是什麼,但有理由懷疑這些推薦其實希望新手把自己以前踩過的坑再踩一遍,或者壓根就不希望讓新手入門,甚至單純處於一個目的: **

2. 在回答『如何學習編譯原理』問題之前。

編譯原理並非隨隨便便就能入門的,一定要正視這個問題。編譯原理的學習和實踐通常基於對計算機編譯過程、計算機基本工作原理、甚至一定的數學知識有一定積累,這些知識分別分布並應用在了編譯原理的不同階段。沒有這些基本知識的積累,很快就會在某個階段由於功底不夠而無法再繼續後面的學習。適合(完整, 前後端)學習編譯原理的必要不充分條件是能夠回答下面的所有問題,這些問題實在是太過於簡單,如果這都答不上來真的別學了,好好補補基礎:

a. 編譯型語言和解釋型語言的區別是什麼?

b. 編譯經歷了哪幾個基本過程? 每個過程主要幹了什麼事情?

c. 詳細描述一下你最熟悉的語言中,賦值號的左邊可以由哪些部分組成? 右邊可以由哪些部分組成?

d. 什麼是遞歸? 如何終止一個遞歸行為?

e. 什麼是貪心過程?

f. 聽說過 KMP 嗎? 介紹一下它的基本思想.

g. 生成樹是什麼?

h. 你覺得編程語言和自然語言最重要的區別有哪些?

i. 什麼是有限狀態自動機? 你知道幾種不同的自動機類型? 它們之間的區別和聯繫?

j. 內存被人為的劃分成了哪幾種不同的類型, 它們之間的區別和聯繫?

k. 什麼是中斷? CPU 在響應中斷時會做什麼事情?

l. 你最熟悉的語言有垃圾回收機制嗎? 若有描述它進行垃圾回收的原理, 若沒有則描述你在編寫程序時是如何管理內存的?

3. 回答『如何學習編譯原理』。

a. 學習 C 語言, 不要求熟悉, 但至少要弄明白指針的思想.

b. 學習數據結構, 尤其是對字元串/樹/圖的相關基本處理手段要非常熟悉.

c. 學習離散數學, 對樹和圖的相關理論要比較心中有數

d. 學習彙編語言, 不要求熟悉這門語言, 但至少要弄明白彙編指令、數據在CPU和存儲器之間的交互機制.

e. 著手學習編譯原理, 推薦先找一本國內高校普遍使用的教材(比如我本科學校用的是胡元義的一本編譯原理教程, 很一般, 但很適合先入門), 入門後(搞明白編譯原理到底是要幹嘛, 解決什麼樣的需求)馬上扔掉轉龍書, 此法最佳.

f. 這裡討論的"學習"包含了編譯器前後端.

最後提示:不要忽視理論的重要性, 因為編譯原理本身就是很理論的;邊學邊做。


建議先從ANTLR開始,比較簡單,馬上就可以開始寫,也可以從一些簡單的編譯器開始學,工業級的編譯器源碼,要考慮太多其他問題,反而不大好讀。

我專欄里就有一個簡易的編譯器的源碼解讀:

SSCLI示常式序MYC編譯器源碼 - 知乎專欄MYC編譯器源碼分析之程序入口 - 知乎專欄MYC編譯器源碼之詞法分析 - 知乎專欄

語法分析和代碼生成也在專欄里,但是知乎專欄出了點問題,暫時還沒有上傳上去。

ANTLR我之前也寫過一些文章:

寫一個編譯器 - 知平軟體 - 博客園


看完&<編譯原理與實踐&>和&<計算機系統要素&>, 就可以寫一個像我這樣的編譯器了

https://github.com/xiang1993/jack-compiler


xtlisk的專欄

以前做這塊東西的資料,盡量寫得通俗點


理論:編譯原理_中科大_網易雲課堂 編譯原理_哈工大_中國大學MOOC

實踐:自己動手用java寫編譯器


我最近在網易雲課堂學習《編譯原理》的在線課程,是中科大一老師講的。我已經快學完詞法分析了。

鏈接:

http://mooc.study.163.com/course/USTC-1000002001

視頻很短,幾分鐘到十幾分鐘不等。我一般沒事兒的時候看幾段,充分利用碎片時間,比如上班的路上、去食堂的路上。但我看的非常仔細,一定要把講到的所有知識點都搞懂才會進入下一課。

視頻的好處不用多說了,比如能學到書本上沒有的東西。

作為一個從來沒有學過編譯原理的非科班出身的IT從業者,我感覺很適合入門。準備學完這個課程再買一本書看看。


目前 網易雲課堂在開編譯原理的MOOC課程, 可以跟著學 編譯原理 - 網易雲課堂。

斯坦福也曾經開過這門課, 但目前只有課程, 老師沒有在跟 Log into your Stanford Online Account。

ps:MOOC編譯原理學習群 69754380


我覺得恩,其實我寫的時候是大作業寫的,根本沒看過什麼龍書虎書,我覺得其實只要把詞法分析,語法分析,語義分析,中間代碼,最終代碼生成理解了,自己yy就能寫出來。找個簡單的書,然後看看上面這些是個啥意思要用什麼方法,還是挺簡單的吧。不過建議樓主找個簡單的語言,例如pascal的,別搞太複雜的先。


可以試試編程語言實現模式這本書,我覺得寫的比較平易近人,龍書雖然偏理論,但是搞懂了看著很帶感的,可以自己一步步去實現個小語言來練習


是反正我是不推薦龍書的,它寫給懂的人看的,呵呵。


自己開始做了之後感覺需要多方面配合才能比較快速得理解和掌握。首先龍書,雖然內容比較晦澀,但卻是一本詳盡的,從理論上說明編譯器的實現演算法以及架構,有點類似於我們通信專業的&<&<數字圖像處理&>&>一類的純理論書籍,雖然難懂,但讀完可以說收貨很大,很有點方法論的感覺。其次&<&<程序語言設計:實踐之路&>&>,這本書感覺總體難度不大,適合入門,從層次上個人感覺更高,但大多是比較簡單的基礎概念性講解。第三,網易雲課堂,編譯原理課程。這個自我感覺不用每個視頻都在,反正我是在看龍書卡殼的時候,去翻一翻,畢竟看活人講解比較好理解。龍書概念太多,看得頭疼。

最重要的當然是實踐了,寫一個C子集的編譯器 一步步來還是很重要的。能讓自己的理解更加透徹


我就是直接看龍書,每天還要上班,就上下班路上看,周末看,現在看了一年,已經快看完第11章了,全部看懂理解,而且總結了書中一些小錯誤,中文整體翻譯還是很好的,有些地方還有譯者仔細思考後的小注釋,但是建議在看不懂時和英文原版對照一下。看書最重要的是理解演算法的本質,我有時一個演算法會想幾天才真正理解。另外第11章要求線性代數的知識,否則看不懂。


推薦閱讀:

JVM里的符號引用如何存儲?
計算機專業書籍興趣閱讀,應該做習題嗎?
大學期間應不應該讀計算機專業大部頭呢?
如何評價5月28日LeCun等人刊發於Nature的Deep Learning這一論文?
大學裡面,怎樣有效的學習知識?理論和實踐怎麼權衡?

TAG:計算機科學 | 編譯原理 | 編譯器 |