小窺Go ast庫及初步使用

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

本文需要你有寫Golang代碼經驗,閱讀大概需要20分鐘。

最近一直在研究Go的依賴注入(dependencyinjection),方便日後寫比較容易測試的代碼(以便偷懶)。目前學到ast解析代碼,現拿出來跟大家分享一下:)

Tokenizer 和 Lexical anaylizer

如果你知道tokenizer和lexical anaylizer是什麼的話,請跳到下一章,不熟的話請看下面這個最簡單的go代碼

package mainfunc main() {     println("Hello, World!")}

這段go代碼做了什嗎?很簡單吧,package是main,定義了個main函數,main函數裡調用了println函數,參數是”Hello,World!“。好,你是知道了,可當你運行gorun時,go怎麼知道的?go先要把你的代碼打散成自己可以理解的構成部分(token),這一過程就叫tokenize。例如,第一行就被拆成了package和main。這個階段,go就像小嬰兒只會理解我、要、吃飯等詞,但串不成合適句子。因為”吃飯我要”是講不通的,所以把詞按一定的文法串起來的過程就是lexicalanaylize或者parse,簡單吧!和人腦不同的是,被程式理解的代碼,通常會以abstract syntaxtree(AST)的形式儲存起來,方便進行校正和尋找。

Go的AST

那我們來看看go的ast庫對代碼的理解程度是不是小嬰兒吧(可啟動並執行原始碼在此),其實就是token+parse剛才我們看到的上一章代碼,並且按AST的方式列印出來,結果在這裡

package mainimport (    "go/ast"    "go/parser"    "go/token")func main() {    // 這就是上一章的代碼.    src := `package mainfunc main() {    println("Hello, World!")}`    // Create the AST by parsing src.    fset := token.NewFileSet() // positions are relative to fset    f, err := parser.ParseFile(fset, "", src, 0)    if err != nil {        panic(err)    }    // Print the AST.    ast.Print(fset, f)}

為了不嚇到你,我先只列印前6行:

     0  *ast.File {     1  .  Package: 2:1     2  .  Name: *ast.Ident {     3  .  .  NamePos: 2:9     4  .  .  Name: "main"     5  .  }       // 省略之後的50+行

可見,go解析出了package這個關鍵詞在文本的第二行的第一個(2:1)。”main”也解析出來了,在第二行的第9個字元,但是go的解析器還給它安了一個叫法:ast.Ident,標示符 或者大家常說的ID,如所示:

Ident +------------+                   |                         Package +-----+    |              v    v           package main

接下來我們看看那個main函數被整成了什麼樣。

     6  .  Decls: []ast.Decl (len = 1) {     7  .  .  0: *ast.FuncDecl {     8  .  .  .  Name: *ast.Ident {     9  .  .  .  .  NamePos: 3:6    10  .  .  .  .  Name: "main"    11  .  .  .  .  Obj: *ast.Object {    12  .  .  .  .  .  Kind: func    13  .  .  .  .  .  Name: "main"    14  .  .  .  .  .  Decl: *(obj @ 7)

此處func main被解析成ast.FuncDecl(functiondeclaration),而函數的參數(Params)和函數體(Body)自然也在這個FuncDecl中。Params對應的是*ast.FieldList,顧名思義就是項列表;而由大括弧”{}”組成的函數體對應的是ast.BlockStmt(blockstatement)。如果不清楚,可以參考下面的圖:

                 FuncDecl.Params +----------+                                            |                   FuncDecl.Name +--------+ |                                          v v          +----------------------> func main() {          |                    +->FuncDecl ++    FuncDecl.Body +-+       println("Hello, World!")          |                    +->          +----------------------> }

而對於main函數的函數體中,我們可以看到調用了println函數,在ast中對應的是ExprStmt(ExpressStatement),調用函數的運算式對應的是CallExpr(CallExpression),調用的參數自然不能錯過,因為參數只有字串,所以go把它歸為ast.BasicLis (a literal of basictype)。如所示:

+-----+ ExprStmt +---------------+|                                ||    CallExpr   BasicLit         ||        +          +            ||        v          v            |+---> println("Hello, World!")<--+

還有什嗎?

    50  .  Scope: *ast.Scope {    51  .  .  Objects: map[string]*ast.Object (len = 1) {    52  .  .  .  "main": *(obj @ 11)    53  .  .  }    54  .  }    55  .  Unresolved: []*ast.Ident (len = 1) {    56  .  .  0: *(obj @ 29)    57  .  }    58  }

我們可以看出ast還解析出了函數的範圍,以及範圍對應的對象。

小結

Go將所有可以識別的token抽象成Node,通過interface方式組織在一起,它們之間的關係如示意:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.