編譯抽象語法樹

2021-06-22 09:02:32 字數 4549 閱讀 5884

編譯抽象語法樹

對大多數開發人員來說,編譯就意謂著產生本地**,給人感覺就是乙個字,難。但是,並不一定要產生本地**,對於 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...