我們需要實現一門簡單的計算器語言,可以支援簡單的加減法,比如類似js的語法
var id = 123執行完這段**之後,myid的值會變成579,那麼如何來實現這個簡單的語言呢?我們需要實現乙個簡單的編譯器來完成這個事情。id = id + 456
var myid = id
乙個編譯器的編譯過程一般包括以下幾個步驟:
詞法分析
詞法分析的階段就是需要識別乙個乙個的詞,比如var id = 123裡面就有3個var、id、=、123這些識別符號,這些詞我們一般稱為token,詞法分析階段就是解析token的過程。我們需要乙個乙個的讀取字元,比如讀到v的時候,我們需要繼續讀下乙個字元,如果下乙個字元是空格或者=,那麼v就是變數名。如果下乙個字元不是空格或者=,那麼需要繼續讀下乙個字元。整體思路就是讀取的當前字元可以判斷之前的字串是乙個token的話,那麼就解析出token,否則就需要繼續往下讀。我們可以乙個乙個字元解析然後判斷每一種情況來實現詞法解析的過程。一般常用的做法是用狀態機來實現,每個字元是一種狀態。比如乙個能識別出var、id、=、+、123的狀態機就像下面一樣:
用**實現狀態機就可以判斷每個token了,詞法分析的階段就完成了
語法分析
語法分析的階段就是根據詞法分析產生的token,形成乙個抽象語法樹。比如關鍵字var不能單獨使用,後面得跟著乙個變數名id,「var id」在一起是有意義的,就是宣告乙個變數,後面還可以跟=形成賦值,也是可以的。「var var」就是乙個錯誤的語法。所以根據定義好的語法規則,就可以形成語法樹。語法樹是一棵樹狀結構,整個**片段都可以形成乙個棵樹,還是以最上面的語句為例,可以形成這樣一棵樹:
程式執行的過程就是執行一條一條的語句,每一條語句對應的是一棵語法樹,那麼如何處理這棵語法樹呢?
(圖中ε表示空轉換)
但是如果表示式中引用了乙個上下文未宣告的或者未初始化的變數,類似這種錯誤需要等到所有語法樹生成之後才能判斷,這些判斷就是語義分析階段的工作
語義分析
語義分析就是分析程式語法含義的過程。程式語法看上去沒問題,但是真實含義有問題,比如有:
類似這種錯誤需要在所有語法樹生成之後,遍歷所有語法樹的節點才能判斷。除了發現語義錯誤之外,還可以計算一些附加屬性,比如表示式的語法樹的最大層數,變數的型別,變數的作用域等。除此之外,還可以做一些優化,比如(1+2)+(3+4)表示式對應的語法樹,可以把左子樹(1+2)計算出來是3,右子樹(3+4)計算出來是7,再繼續計算出來是10,經過常量計算,語法樹就直接求出值了,這樣就不需要程式執行期間再求值了
生成中間**和生成目標**
生成中間**和生成目標**一般指後端語言的編譯過程。像j**ascript語言,是解釋執行的,不需要編譯成中間**或者機器碼。比如j**a語言,編譯之後會生成位元組碼檔案,位元組碼就是一種中間**,然後由j**a虛擬機器解釋執行位元組碼或者編譯成機器碼執行。假如要實現一門後端編譯型語言,生成中間**的過程可以生成llvm ir的中間表示形式,然後由llvm生成機器碼。生成中間**的過程是為了生成跨平台的統一的語言表達形式,就比如j**a位元組碼,每個平台的位元組碼是一樣的,由各個平台的j**a虛擬機器適配作業系統和底層硬體架構。llvm ir也是一種跨平台的統一的語言表達形式,再由llvm框架適配作業系統和底層硬體架構。
為了方便,這裡我們生成j**a位元組碼。j**a位元組碼是屬於jvm規範的一種(位元組碼的檔案格式如下:
classfile由類js語法翻譯成位元組碼的思路是:
示例**如
如何學習一門新的技術
作為乙個從事軟體開發工作多年的程式設計師,技術是在不斷變化,我們在做乙個專案的過程中,會遇到很多新的技術,我們可以看到那些多年前的技術大牛,現在依然是技術大牛,那麼原因是什麼呢?原因很簡單,因為他們從來沒有間斷過學習,遇到新的技術,他們會按照自己的方法,及時去學習,實戰。那麼問題來了,對於我們這些技...
如何快速高效的學習一門新技術
這個問題網上有很多不錯的blog,每篇或長或短,我看了幾篇評分較高的文章,認為有幾個核心方法和思想 1 以未來某個階段的目標和現階段自身的需求出發,選擇學習一門新技術。以目標為導向,確定近期的學習方向,選擇學習哪門技術。比如,現在遇到某個專案,該專案是你目前技術無法搞定的,那你就得學習該專案所需要的...
用構建者的角度去學習一門技術
通常,我們得知了一種新技術新領域,所做出的第一反應就是去了解它,學習它的使用方法,最後再借助相關文件和google來邊實踐邊熟悉這項技術。但是這樣真的是把技術學到家了嗎?我們或許只是把知識簡單的搬運到了大腦中。讓我們來看乙個簡單的生活中的例子。在玩具店中,擺放了乙隻 會說話的小黃鴨 這只會念兒歌 會...