柯里化的前生今世(四):编译器与解释器
關(guān)于
在上一篇中,我們提到了形式語(yǔ)言與文法,S表達(dá)式與M表達(dá)式,同像性。
本文將開始寫一個(gè)簡(jiǎn)單的解釋器,
通過(guò)具體實(shí)現(xiàn),我們來(lái)理解求值環(huán)境,動(dòng)態(tài)作用域和靜態(tài)作用域,還有閉包等概念。
當(dāng)然,一篇文章來(lái)寫完這些肯定是不夠的,我們可以慢慢來(lái),循序漸進(jìn)。
寫完了這個(gè)解釋器之后,我們會(huì)增加一些新的功能。
編譯器與解釋器
編譯器會(huì)將源代碼轉(zhuǎn)換成另一種語(yǔ)言的代碼,然后在支持后一種語(yǔ)言的機(jī)器上執(zhí)行。
而解釋器則不同,它會(huì)逐行分析源代碼,直接執(zhí)行分析結(jié)果。
值得一提的是,編譯和解釋是執(zhí)行代碼的兩種手段,
具體的語(yǔ)言實(shí)現(xiàn)很可能采用兩者的混合形式。
例如,一段Java程序,會(huì)首先經(jīng)過(guò)javac編譯為字節(jié)碼,
字節(jié)碼再交由Java虛擬機(jī)來(lái)解釋執(zhí)行。(JIT和RTSJ,略。。
編譯器包含以下三個(gè)部分,
編譯器前端:詞法分析,語(yǔ)法分析,最終生成抽象語(yǔ)法樹這種中間代碼。
編譯器優(yōu)化:中間代碼多次轉(zhuǎn)換,多種優(yōu)化,
編譯器后端:目標(biāo)代碼生成,優(yōu)化目標(biāo)代碼。
解釋器不包含目標(biāo)代碼生成階段,將優(yōu)化結(jié)果直接執(zhí)行。
前端和優(yōu)化,是編譯器和解釋器共有的。
抽象語(yǔ)法樹
編譯器前端會(huì)分析源代碼文本,生成一棵抽象語(yǔ)法樹。
假如,我們有如下源代碼,(1+2*3)*(4-5)。
使用ANTLR,我們得到了(具體)語(yǔ)法樹,
語(yǔ)法文件如下:
grammar Expr;expr: expr ('*'|'/') expr| expr ('+'|'-') expr| INT| '(' expr ')';INT: [0-9]+ ; WS: [ \t]+ -> skip ;我們看到語(yǔ)法樹包含了產(chǎn)生式的名稱,這在后續(xù)處理過(guò)程中是不需要的,
因此,編譯器前端會(huì)將具體語(yǔ)法樹轉(zhuǎn)換成一種中間形式——抽象語(yǔ)法樹。
這不就是S表達(dá)式嗎?
對(duì)的,編譯器前端會(huì)將任何語(yǔ)言的源代碼轉(zhuǎn)換成與具體語(yǔ)法無(wú)關(guān)的抽象語(yǔ)法樹,
而S表達(dá)式正是這種抽象語(yǔ)法樹的線性編碼。
(因此,你寫任何語(yǔ)言,本質(zhì)上都是在寫Lisp。。
格林斯潘第十定律:
任何C或Fortran程序復(fù)雜到一定程度之后,都會(huì)包含一個(gè)臨時(shí)開發(fā)的、不合規(guī)范的、充滿程序錯(cuò)誤的、運(yùn)行速度很慢的、只有一半功能的Common Lisp實(shí)現(xiàn)。
簡(jiǎn)化解釋器的實(shí)現(xiàn)
為了簡(jiǎn)化解釋器的實(shí)現(xiàn),我們會(huì)直接分析S表達(dá)式(抽象語(yǔ)法樹),并且略過(guò)優(yōu)化環(huán)節(jié)。我們也不解釋四則運(yùn)算表達(dá)式,因?yàn)檫@涉及到了操作符的定義問(wèn)題。
我們將直接實(shí)現(xiàn)lambda表達(dá)式和函數(shù)的調(diào)用。
和其他解釋器的教材不同的是,我沒(méi)有寫那么多的if-else,
而是把決策模式提取出來(lái)了,這樣會(huì)更清晰一些。
eval-exp會(huì)根據(jù)exp的具體形式,尋找相應(yīng)的處理方式,
而各個(gè)處理方式中,還有可能再用到eval-exp來(lái)處理子表達(dá)式。
因此,這是一個(gè)遞歸執(zhí)行的過(guò)程。
下文,我們會(huì)剖析這個(gè)簡(jiǎn)單的解釋器,
把每個(gè)處理分支都實(shí)現(xiàn)一下。
關(guān)于寫作意圖
本系列文章的寫作目的是想借著柯里化這個(gè)概念,
把函數(shù)式編程相關(guān)的知識(shí)點(diǎn)串聯(lián)起來(lái)。
為什么選擇柯里化呢,因?yàn)榭吕锘紫群透唠A函數(shù)相關(guān),
我可以借此來(lái)引入作用域的概念,
continuation本身就是一個(gè)單參函數(shù),順便就可以介紹了,
hygienic macro也涉及到了標(biāo)識(shí)符的查找,學(xué)了求值環(huán)境也容易理解了。
其次,帶參數(shù)的類型,可以類比函數(shù)的柯里化來(lái)理解,
要想理解帶參數(shù)的類型,我們就得學(xué)習(xí)類型,以及代數(shù)數(shù)據(jù)類型,
從而繼續(xù)深入下去,學(xué)習(xí)Functor,Applicative,Monad這些類型類。
這樣類型系統(tǒng)就揭開了神秘的面紗。
當(dāng)然,這些都是偏工業(yè)應(yīng)用的,并沒(méi)有涉及理論基礎(chǔ),
自動(dòng)機(jī)理論,可計(jì)算性理論,形式語(yǔ)義,也不適合在本系列中提及,
寫完本系列后,我會(huì)嘗試寫其他系列,希望能覆蓋掉某些點(diǎn),
以此來(lái)督促自己努力學(xué)習(xí),小心求證。
參考
程序設(shè)計(jì)語(yǔ)言:實(shí)踐之路
編程語(yǔ)言實(shí)現(xiàn)模式
The Definitive ANTLR 4 Reference
Lisp in Small Pieces
Java 是編譯型語(yǔ)言還是解釋型語(yǔ)言?
Abstract vs. Concrete Syntax Trees
總結(jié)
以上是生活随笔為你收集整理的柯里化的前生今世(四):编译器与解释器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux 双网卡绑定
- 下一篇: Widget开发中遇到的坑