這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原文在此。
————翻譯分隔線————
編譯器(3)-編譯器設計概覽
第一部分:介紹
第二部分:編譯、轉譯和解釋
我們正在編寫什嗎?
簡單來說,是一個計算機。一個用於超級簡單的數學語言的編譯器。
至少當前來說,我們會盡量避免處理字串和字元這些複雜的東西,並且集中精力在數字上。當然也不是所有的數字,僅僅整數而已。當前來說系統中僅有的“類型”就是它了。
添加新類型並不困難,實際上是很簡單的,但是在這個遊戲中,這樣做會讓我們的設計變得更加複雜。
撰寫策略
無論你是構建解譯器還是編譯器,大多數步驟都是一樣的。通常來說,基本步驟如下:
我們每次只攻打一個步驟。
概覽:詞法分析
大家之前都寫過代碼。程式的行為通過“人類可讀”的方式進行表達。我們需要處理這些人類可讀的內容,讓電腦能夠明白其中的含義。第一步,就是詞法分析。
簡短來說,我們需要掃描整個文本,然後報告有什麼發現。也就是說,需要分揀出語言中所有不同的獨立部分,並賦予一個標識符(token)。每個標識符都與一個用字元表示的文法串(也叫詞位)相關聯。它會在下一個階段使用。文法(數字、字串等等)、關鍵字和操作符是在這個階段需要標識的樣本。
我們應當提供每個標識符發現的位置,以便錯誤報表使用。
概覽:解析
這個階段也叫做文法分析,為已經發現的標識符賦予各種含義。每個標識符都代表了樹形資料結構中的一個對象和位置。
語言的文法會在這個階段進行驗證。以確保接收到的順序是我們預期的順序。在 LISP 中,運算式的格式為左括弧,操作符或函數,接著是若干個參數,然後是右括弧。解析過程會確保輸入的內容是按照這個順序排列。
概覽:語義分析
接下來,將檢查語言的語義是正確的。如果一個變數定義為一個整數,但是賦值了一個字串,就會出問題。函數調用的參數與其聲明時的參數數量不一致也是一個語義錯誤。
語義分析還要考慮例如變數範圍,並在使用未定義的變數時產生錯誤。
概覽:最佳化
這個階段的名字表明了它要作的事情。它會最佳化輸出,通常是針對速度或大小(記憶體消耗)。在這個系列中,不會涵蓋太多關於此的內容。不過會有一個關於為變數賦值運算式結果,而不是運算式本身的簡單列子。
例如,如果為變數 A 指派陳述式“2 + 3”的話。產生將兩個常量數字相加的代碼可能需要很多步完成。這可以最佳化為預先計算結果,然後在編碼時進行賦值,這可以將語句簡化為“A = 5”。
概覽:編碼
這是最後一個步驟。它將輸出要發布的低級代碼。Java 編譯器會輸出 Java 位元組碼。C 編譯器會輸出彙編。彙編編譯器會輸出機器碼。
向前!向前!向前!
務必牢記這些步驟在你的編譯器裡可多可少。例如,C 編譯器還有先行編譯步驟。有一些步驟,例如解析和語義分析可以放在一起,成為一個步驟。有各種編譯器的設計。為了開闊視野,參閱這個維基百科的頁面:編譯器(英文)(譯註:關於編譯器的維基中文頁面並未包含編譯器前端的內容)。
現在立刻沉迷於編譯器的編寫當然很好。不幸的是,這是很愚蠢的行為。在不知道語言到底是什麼樣子的情況下,我們很快就會迷失於到底要寫什麼的問題之中。沒有規則去遵循,就沒有方向。
接下來:語言規則說明書!