1. 源碼示例
package mainimport ("context"
)// Foo 結構體
type Foo struct {i int
}// Bar 接口
type Bar interface {Do(ctx context.Context) error
}// main方法
func main() {a := 1
}
2. Golang中的AST
golang官方提供的幾個包,可以幫助我們進行AST分析:
-
go/scanner:詞法解析,將源代碼分割成一個個token
-
go/token:token類型及相關結構體定義
-
go/ast:ast的結構定義
-
- ast的各種結構定義入口在
go/ast/ast.go
上
- ast的各種結構定義入口在
-
go/parser:語法分析,讀取token流生成ast
通過上述的四個庫,我們就可以實現golang代碼的語法樹分析
3. 使用main.go解析demo.go的AST樹
package mainimport ("go/ast""go/parser""go/token""log""path/filepath"
)func main() {fset := token.NewFileSet()// 這里取絕對路徑,方便打印出來的語法樹可以轉跳到編輯器path, _ := filepath.Abs("./demo.go")f, err := parser.ParseFile(fset, path, nil, parser.AllErrors)if err != nil {log.Println(err)return}// 打印語法樹ast.Print(fset, f)
}
3.1. 解析的結果如下
可在http://goast.yuroyoro.net/里貼上源代碼后查看
*ast.File {1 . Package: 1:12 . Name: *ast.Ident {3 . . NamePos: 1:94 . . Name: "main"5 . }6 . Decls: []ast.Decl (len = 4) {7 . . 0: *ast.GenDecl {8 . . . TokPos: 3:19 . . . Tok: import10 . . . Lparen: 3:811 . . . Specs: []ast.Spec (len = 1) {12 . . . . 0: *ast.ImportSpec {13 . . . . . Path: *ast.BasicLit {14 . . . . . . ValuePos: 4:215 . . . . . . Kind: STRING16 . . . . . . Value: "\"context\""17 . . . . . }18 . . . . . EndPos: -19 . . . . }20 . . . }21 . . . Rparen: 5:122 . . }23 . . 1: *ast.GenDecl {24 . . . TokPos: 8:125 . . . Tok: type26 . . . Lparen: -27 . . . Specs: []ast.Spec (len = 1) {28 . . . . 0: *ast.TypeSpec {29 . . . . . Name: *ast.Ident {30 . . . . . . NamePos: 8:631 . . . . . . Name: "Foo"32 . . . . . . Obj: *ast.Object {33 . . . . . . . Kind: type34 . . . . . . . Name: "Foo"35 . . . . . . . Decl: *(obj @ 28)36 . . . . . . }37 . . . . . }38 . . . . . Type: *ast.StructType {39 . . . . . . Struct: 8:1040 . . . . . . Fields: *ast.FieldList {41 . . . . . . . Opening: 8:1742 . . . . . . . List: []*ast.Field (len = 1) {43 . . . . . . . . 0: *ast.Field {44 . . . . . . . . . Names: []*ast.Ident (len = 1) {45 . . . . . . . . . . 0: *ast.Ident {46 . . . . . . . . . . . NamePos: 9:247 . . . . . . . . . . . Name: "i"48 . . . . . . . . . . . Obj: *ast.Object {49 . . . . . . . . . . . . Kind: var50 . . . . . . . . . . . . Name: "i"51 . . . . . . . . . . . . Decl: *(obj @ 43)52 . . . . . . . . . . . }53 . . . . . . . . . . }54 . . . . . . . . . }55 . . . . . . . . . Type: *ast.Ident {56 . . . . . . . . . . NamePos: 9:457 . . . . . . . . . . Name: "int"58 . . . . . . . . . }59 . . . . . . . . }60 . . . . . . . }61 . . . . . . . Closing: 10:162 . . . . . . }63 . . . . . . Incomplete: false64 . . . . . }65 . . . . }66 . . . }67 . . . Rparen: -68 . . }69 . . 2: *ast.GenDecl {70 . . . TokPos: 13:171 . . . Tok: type72 . . . Lparen: -73 . . . Specs: []ast.Spec (len = 1) {74 . . . . 0: *ast.TypeSpec {75 . . . . . Name: *ast.Ident {76 . . . . . . NamePos: 13:677 . . . . . . Name: "Bar"78 . . . . . . Obj: *ast.Object {79 . . . . . . . Kind: type80 . . . . . . . Name: "Bar"81 . . . . . . . Decl: *(obj @ 74)82 . . . . . . }83 . . . . . }84 . . . . . Type: *ast.InterfaceType {85 . . . . . . Interface: 13:1086 . . . . . . Methods: *ast.FieldList {87 . . . . . . . Opening: 13:2088 . . . . . . . List: []*ast.Field (len = 1) {89 . . . . . . . . 0: *ast.Field {90 . . . . . . . . . Names: []*ast.Ident (len = 1) {91 . . . . . . . . . . 0: *ast.Ident {92 . . . . . . . . . . . NamePos: 14:293 . . . . . . . . . . . Name: "Do"94 . . . . . . . . . . . Obj: *ast.Object {95 . . . . . . . . . . . . Kind: func96 . . . . . . . . . . . . Name: "Do"97 . . . . . . . . . . . . Decl: *(obj @ 89)98 . . . . . . . . . . . }99 . . . . . . . . . . }100 . . . . . . . . . }101 . . . . . . . . . Type: *ast.FuncType {102 . . . . . . . . . . Func: -103 . . . . . . . . . . Params: *ast.FieldList {104 . . . . . . . . . . . Opening: 14:4105 . . . . . . . . . . . List: []*ast.Field (len = 1) {106 . . . . . . . . . . . . 0: *ast.Field {107 . . . . . . . . . . . . . Names: []*ast.Ident (len = 1) {108 . . . . . . . . . . . . . . 0: *ast.Ident {109 . . . . . . . . . . . . . . . NamePos: 14:5110 . . . . . . . . . . . . . . . Name: "ctx"111 . . . . . . . . . . . . . . . Obj: *ast.Object {112 . . . . . . . . . . . . . . . . Kind: var113 . . . . . . . . . . . . . . . . Name: "ctx"114 . . . . . . . . . . . . . . . . Decl: *(obj @ 106)115 . . . . . . . . . . . . . . . }116 . . . . . . . . . . . . . . }117 . . . . . . . . . . . . . }118 . . . . . . . . . . . . . Type: *ast.SelectorExpr {119 . . . . . . . . . . . . . . X: *ast.Ident {120 . . . . . . . . . . . . . . . NamePos: 14:9121 . . . . . . . . . . . . . . . Name: "context"122 . . . . . . . . . . . . . . }123 . . . . . . . . . . . . . . Sel: *ast.Ident {124 . . . . . . . . . . . . . . . NamePos: 14:17125 . . . . . . . . . . . . . . . Name: "Context"126 . . . . . . . . . . . . . . }127 . . . . . . . . . . . . . }128 . . . . . . . . . . . . }129 . . . . . . . . . . . }130 . . . . . . . . . . . Closing: 14:24131 . . . . . . . . . . }132 . . . . . . . . . . Results: *ast.FieldList {133 . . . . . . . . . . . Opening: -134 . . . . . . . . . . . List: []*ast.Field (len = 1) {135 . . . . . . . . . . . . 0: *ast.Field {136 . . . . . . . . . . . . . Type: *ast.Ident {137 . . . . . . . . . . . . . . NamePos: 14:26138 . . . . . . . . . . . . . . Name: "error"139 . . . . . . . . . . . . . }140 . . . . . . . . . . . . }141 . . . . . . . . . . . }142 . . . . . . . . . . . Closing: -143 . . . . . . . . . . }144 . . . . . . . . . }145 . . . . . . . . }146 . . . . . . . }147 . . . . . . . Closing: 15:1148 . . . . . . }149 . . . . . . Incomplete: false150 . . . . . }151 . . . . }152 . . . }153 . . . Rparen: -154 . . }155 . . 3: *ast.FuncDecl {156 . . . Name: *ast.Ident {157 . . . . NamePos: 18:6158 . . . . Name: "main"159 . . . . Obj: *ast.Object {160 . . . . . Kind: func161 . . . . . Name: "main"162 . . . . . Decl: *(obj @ 155)163 . . . . }164 . . . }165 . . . Type: *ast.FuncType {166 . . . . Func: 18:1167 . . . . Params: *ast.FieldList {168 . . . . . Opening: 18:10169 . . . . . Closing: 18:11170 . . . . }171 . . . }172 . . . Body: *ast.BlockStmt {173 . . . . Lbrace: 18:13174 . . . . List: []ast.Stmt (len = 1) {175 . . . . . 0: *ast.AssignStmt {176 . . . . . . Lhs: []ast.Expr (len = 1) {177 . . . . . . . 0: *ast.Ident {178 . . . . . . . . NamePos: 19:2179 . . . . . . . . Name: "a"180 . . . . . . . . Obj: *ast.Object {181 . . . . . . . . . Kind: var182 . . . . . . . . . Name: "a"183 . . . . . . . . . Decl: *(obj @ 175)184 . . . . . . . . }185 . . . . . . . }186 . . . . . . }187 . . . . . . TokPos: 19:4188 . . . . . . Tok: :=189 . . . . . . Rhs: []ast.Expr (len = 1) {190 . . . . . . . 0: *ast.BasicLit {191 . . . . . . . . ValuePos: 19:7192 . . . . . . . . Kind: INT193 . . . . . . . . Value: "1"194 . . . . . . . }195 . . . . . . }196 . . . . . }197 . . . . }198 . . . . Rbrace: 20:1199 . . . }200 . . }201 . }202 . Scope: *ast.Scope {203 . . Objects: map[string]*ast.Object (len = 3) {204 . . . "Foo": *(obj @ 32)205 . . . "Bar": *(obj @ 78)206 . . . "main": *(obj @ 159)207 . . }208 . }209 . Imports: []*ast.ImportSpec (len = 1) {210 . . 0: *(obj @ 12)211 . }212 . Unresolved: []*ast.Ident (len = 3) {213 . . 0: *(obj @ 55)214 . . 1: *(obj @ 119)215 . . 2: *(obj @ 136)216 . }217 }
4. AST樹結構
// 該結構體位于標準包 go/ast/ast.go 中,有興趣可以轉跳到源碼閱讀更詳盡的注釋
type File struct {Doc *CommentGroup // 如果文件有文檔,則文檔會被存儲在這個結構體中,否則為 nilPackage token.Pos // "package"關鍵字,主要是所在的位置信息Name *Ident // package的名字Decls []Decl // 文件級別的聲明。它包含文件中所有變量、函數、類型聲明。如果文件中沒有聲明,則 decls 值為 nilScope *Scope // 包級作用域。它代表包級作用域,包含所有在包內聲明的變量和函數。它對當前文件有效Imports []*ImportSpec // imports in this fileUnresolved []*Ident // unresolved identifiers in this file。未使用的標識符Comments []*CommentGroup // 文件中的所有注釋。它包含文件中所有注釋的列表
}
4.1. Doc
如果文件有文檔,則文檔會被存儲在這個結構體中,否則為 nil
todo:目前沒找到什么樣的源代碼解析成AST樹后有Doc的
4.2. Package
*ast.File {1 . Package: 1:12 . Name: *ast.Ident {3 . . NamePos: 1:94 . . Name: "main"5 . }
Package: 1:1
, package關鍵字所在的位置
4.3. Name
type為ast.ident
,表示它是一個變量值,可以看到內容為"main"
4.4. Decls
文件級別的聲明。它包含文件中所有變量、函數、類型聲明。如果文件中沒有聲明,則 decls 值為 nil
4.5. Decls總共有三種類型
4.5.1. BadDecl
語法出錯的聲明
4.5.2. GenDecl
常規的聲明,包含以下部分
- import
- constant
- type
- variable
4.5.2.1. import
4.5.2.2. constant
4.5.2.3. type
4.5.2.4. variable
4.5.3. FunDecl
方法的聲明
4.6. Scope
包級作用域。它代表包級作用域,包含所有在包內聲明的變量和函數。它對當前文件有效
4.6.1. 示例如下
4.7. Imports
回顧以下File
結構體定義,其中Imports
為ImportSpec
類型數組
// 該結構體位于標準包 go/ast/ast.go 中,有興趣可以轉跳到源碼閱讀更詳盡的注釋
type File struct {Doc *CommentGroup // 如果文件有文檔,則文檔會被存儲在這個結構體中,否則為 nilPackage token.Pos // "package"關鍵字,主要是所在的位置信息Name *Ident // package的名字Decls []Decl // 文件級別的聲明。它包含文件中所有變量、函數、類型聲明。如果文件中沒有聲明,則 decls 值為 nilScope *Scope // 包級作用域。它代表包級作用域,包含所有在包內聲明的變量和函數。它對當前文件有效Imports []*ImportSpec // imports in this fileUnresolved []*Ident // unresolved identifiers in this file。未使用的標識符Comments []*CommentGroup // 文件中的所有注釋。它包含文件中所有注釋的列表
}
ImportSpec
結構體定義如下,一條import就是一個ImportSpec
// An ImportSpec node represents a single package import.ImportSpec struct {Doc *CommentGroup // associated documentation; or nilName *Ident // local package name (including "."); or nilPath *BasicLit // import pathComment *CommentGroup // line comments; or nilEndPos token.Pos // end of spec (overrides Path.Pos if nonzero)}
4.8. Unresolved
unresolved identifiers in this file。未使用的標識符
4.9. Comments
文件中的所有注釋。它包含文件中所有注釋的列表。實際上這塊有問題,并沒有注釋解析出來
5. AST數節點類型
6. 參考資料
- Golang AST語法樹使用教程及示例
- GoAst Viewer
- https://github.com/DrmagicE/ast-example
- [golang深入源代碼系列之一:AST的遍歷](