編譯抽象語法樹
對大多數開發人員來說,編譯就意謂著產生本地**,給人感覺就是乙個字,難。但是,並不一定要產生本地**,對於 dsl,通常產生其他更加通用的程式語言。.net 框架提供幾個把抽象語法樹編譯成程式的功能。
技術的選擇取決於幾個因素。例如,如果語言針對的是開發人員,那麼,生成文字檔案就足夠了,內容可以是 f#,也可以是其他語言,或者是編譯過的程式集,能在程式中使用;然而,如果針對的是終端使用者,那麼幾乎可以肯定,必須編譯然後動態[ on the fly ]執行。表12-1 彙總了各種可用選項。
表12-1 .net 的**生成技術 技術
描述microsoft.csharp
.csharpcodeprovider
這個類支援動態產生的 c# 編譯檔案,既可以通過簡單地字串連線,也可以通過使用 system.codedom 命名空間。一旦**編譯成程式集,就可以動態載入到記憶體,通過反映執行。這個操作相對來說是昂貴的,因為它要求寫到磁碟,並使用反映去執行方法。
system.codedom
這是一組類,目標是在不同語言的可用操作之間進行抽象。其思想是,用在這個命名空間下可用的類來描述操作,然後,用提供程式把它編譯成選擇的語言。.net 為 c# 和 visual basic 發布了提供程式。
fsharp.compiler.codedom.dll
這是隨 f# 發行的 codedom 提供程式,它可以用於動態編譯 f#,方法與 c# 的 codedom 提供程式相似。
system.reflection.emit
有了這個命名空間,可以用中間語言建立程式集。由於中間語言提供了比 f#、c#,system.codedom 更多的功能,因此,更具靈活性。然而,由於是低階語言,就需要更多的耐心,為了獲得正確的結果,要花更多的時間。
mono.cecil
這個庫廣泛用於 mono 框架,既可以解析程式集,也可以動態建立程式集。
我們system.reflection.emit.dynamicmethod 類,並不是因為我們特別需要中間語言的靈活性,而是因為中間語言內建了浮點運算的指令,可以很適合實現我們的小語言。dynamicmethod 還提供了快捷、容易的方法可以呼叫產生的程式。
createdynamicmethod 方法才真正編譯抽象語法樹,通過掃瞄抽象語法樹和生成**。首先,建立dynamicmethod 類的例項,來操作這個中間語言,是定義來表示這個方法的:
let temp = new dynamicmethod("",(type float), paramstypes, meth.module)
然後,createdynamicmethod 開始遍歷樹。遇到識別符號,輸出的**是載入動態方法的引數:
| ident name ->
il.emit(opcodes.ldarg, paramnames.indexof(name))
當遇到文字時,輸出的中間語言**是載入這個文字的值:
| val x ->il.emit(opcodes.ldc_r8, x)
當遇到操作時,必須遞迴評估計算表示式的兩項,然後輸出的是表示要求操作的指令:
| multi (e1 , e2) ->
generateilinner e1
generateilinner e2
il.emit(opcodes.mul)
注意,如何運算是最後輸出的,在表示式的兩項遞迴評估之後。這樣做原因是中間語言基於堆疊,因此,來自其他運算的資料,在評估運算子之前,必須壓棧。
清單 12-7 是完整的編譯器。
清單 12-7 編譯從命令列輸入生成的抽象語法樹
opensystem.collections.generic
open system.reflection
open system.reflection.emit
openstrangelights.expressionparser
// get a list of all the parameter names
letrec getparamlist e =
letrecgetparamlistinner e names =
match e with
| identname ->
if not (list.exists (funs -> s = name) names) then
name :: names
else
names
| multi(e1 , e2) ->
names
|> getparamlistinner e1
|> getparamlistinner e2
| div(e1 , e2) ->
names
|> getparamlistinner e1
|> getparamlistinner e2
| plus(e1 , e2) ->
names
|> getparamlistinner e1
|> getparamlistinner e2
| minus(e1 , e2) ->
names
|> getparamlistinner e1
|> getparamlistinner e2
| _ -> names
getparamlistinner e
// create the dynamic method
letcreatedynamicmethod e (paramnames: string list) =
let generateil e (il : ilgenerator) =
letrecgenerateilinner e =
match e with
|ident name ->
let index = list.find_index (fun s -> s = name)paramnames
il.emit(opcodes.ldarg, index)
|val x -> il.emit(opcodes.ldc_r8, x)
|multi (e1 , e2) ->
generateilinner e1
generateilinner e2
il.emit(opcodes.mul)
|div (e1 , e2) ->
generateilinner e1
generateilinner e2
il.emit(opcodes.div)
| plus (e1 , e2) ->
generateilinner e1
generateilinner e2
il.emit(opcodes.add)
|minus (e1 , e2) ->
generateilinner e1
generateilinner e2
il.emit(opcodes.sub)
generateilinner e
il.emit(opcodes.ret)
let paramstypes = array.create paramnames.length (typeof
)let meth = methodinfo.getcurrentmethod()
let temp = newdynamicmethod("", (typeof
), paramstypes, meth.module)
let il = temp.getilgenerator()
generateile il
temp
// function to read the arguments fromthe command line
let collectargs(paramnames : string list) =
paramnames
|> seq.map
(fun n ->
printf "%s: " n
box(float(console.readline())))
|>array.ofseq
// the expression to be interpreted
let e = multi(val 2.,plus(val 2., ident "a"))
// get a list of all the parameters fromthe expression
let paramnames =getparamlist e
// compile the tree to a dynamic method
let dm =createdynamicmethod e paramnames
// print collect arguments from the user
let args =collectargs paramnames
// execute and print out final result
printf "result:%o" (dm.invoke(null, args))
執行結果如下:
編譯清單 12-7 的**產生下面的結果:
a: 4
result: 32
python抽象語法樹的遍歷
遍歷語法樹獲取資訊 語法樹node for node in ast.walk tree if isinstance node,ast.functiondef print node.name class codevisitor ast.nodevisitor def generic visit sel...
編譯原理 語法推導樹
語法推導樹必須具有的特徵 首先理解此處和文法有關,文法公式g 1.每個結點都有乙個標記,此標記是v的乙個符號。這個標記必須是終結符或者非終結符中的乙個 2.根的標記是s 開始符 3.若一結點n至少有乙個它自己除外的子孫,並且有標記a,則a肯定在vn中。如果乙個結點有子孩子,則這個結點肯定不是終結符 ...
bebel抽象語法樹(AST)中js節點名稱
functiondeclaration 函式宣告 function a functionexpression 函式表示式 var a function arrowfunctionexpression 箭頭函式表示式 此處可以思考 為什麼沒有箭頭函式宣告,以及declaration和expressio...