go/parserを調べる
参考にしているのはこちらのページです。このブログ見るより以下のページを見た方がわかりやすいかも。
https://motemen.github.io/go-for-go-book/#はじめに
go/parserとは
goの標準パッケージで構文解析を行っている部分です。
1 2 3 4 5 6 7 8 9 10 11 |
package main import ( "fmt" "go/parser" ) func main() { expr, _ := parser.ParseExpr("a * -1") fmt.Printf("%#v", expr) } |
a * -1をParseしてみます。以下の結果が得られました。
1 2 |
$ go run exam.go &ast.BinaryExpr{X:(*ast.Ident)(0xc42000a200), OpPos:3, Op:14, Y:(*ast.UnaryExpr)(0xc42000a240)} |
より詳細な結果を見るにはast.printを使います。
1 2 3 4 5 6 7 8 9 10 11 |
package main import ( "go/ast" "go/parser" ) func main() { expr, _ := parser.ParseExpr("a * -1") ast.Print(nil, expr) } |
結果は以下。どうparseされたのかわかりやすいですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ go run exam.go 0 *ast.BinaryExpr { 1 . X: *ast.Ident { 2 . . NamePos: 1 3 . . Name: "a" 4 . . Obj: *ast.Object { 5 . . . Kind: bad 6 . . . Name: "" 7 . . } 8 . } 9 . OpPos: 3 10 . Op: * 11 . Y: *ast.UnaryExpr { 12 . . OpPos: 5 13 . . Op: - 14 . . X: *ast.BasicLit { 15 . . . ValuePos: 6 16 . . . Kind: INT 17 . . . Value: "1" 18 . . } 19 . } 20 } |
今回Parserは2項演算(a * -1)を与えたので2項演算するためのExprであるBinaryExprを返しています。処理によってExprはいくつかあるようです。
ファイル全体の解析
ファイル全体の解析を行うにはparser.ParseFileを使えば良いようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package main import ( "fmt" "go/ast" "go/parser" "go/token" ) func main() { fset := token.NewFileSet() f, _ := parser.ParseFile(fset, "example.go", src, parser.Mode(0)) for _, d := range f.Decls { ast.Print(fset, d) fmt.Println() } } var src = `package p import _ "log" func add(n, m int) {} ` |
結果は以下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
$ go run exam.go 0 *ast.GenDecl { 1 . TokPos: example.go:2:1 2 . Tok: import 3 . Lparen: - 4 . Specs: []ast.Spec (len = 1) { 5 . . 0: *ast.ImportSpec { 6 . . . Name: *ast.Ident { 7 . . . . NamePos: example.go:2:8 8 . . . . Name: "_" 9 . . . } 10 . . . Path: *ast.BasicLit { 11 . . . . ValuePos: example.go:2:10 12 . . . . Kind: STRING 13 . . . . Value: "\"log\"" 14 . . . } 15 . . . EndPos: - 16 . . } 17 . } 18 . Rparen: - 19 } 0 *ast.FuncDecl { 1 . Name: *ast.Ident { 2 . . NamePos: example.go:3:6 3 . . Name: "add" 4 . . Obj: *ast.Object { 5 . . . Kind: func 6 . . . Name: "add" 7 . . . Decl: *(obj @ 0) 8 . . } 9 . } 10 . Type: *ast.FuncType { 11 . . Func: example.go:3:1 12 . . Params: *ast.FieldList { 13 . . . Opening: example.go:3:9 14 . . . List: []*ast.Field (len = 1) { 15 . . . . 0: *ast.Field { 16 . . . . . Names: []*ast.Ident (len = 2) { 17 . . . . . . 0: *ast.Ident { 18 . . . . . . . NamePos: example.go:3:10 19 . . . . . . . Name: "n" 20 . . . . . . . Obj: *ast.Object { 21 . . . . . . . . Kind: var 22 . . . . . . . . Name: "n" 23 . . . . . . . . Decl: *(obj @ 15) 24 . . . . . . . } 25 . . . . . . } 26 . . . . . . 1: *ast.Ident { 27 . . . . . . . NamePos: example.go:3:13 28 . . . . . . . Name: "m" 29 . . . . . . . Obj: *ast.Object { 30 . . . . . . . . Kind: var 31 . . . . . . . . Name: "m" 32 . . . . . . . . Decl: *(obj @ 15) 33 . . . . . . . } 34 . . . . . . } 35 . . . . . } 36 . . . . . Type: *ast.Ident { 37 . . . . . . NamePos: example.go:3:15 38 . . . . . . Name: "int" 39 . . . . . } 40 . . . . } 41 . . . } 42 . . . Closing: example.go:3:18 43 . . } 44 . } 45 . Body: *ast.BlockStmt { 46 . . Lbrace: example.go:3:20 47 . . Rbrace: example.go:3:21 48 . } 49 } |
構文木の探索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package main import ( "fmt" "go/ast" "go/parser" "go/token" ) func main() { fset := token.NewFileSet() f, _ := parser.ParseFile(fset, "example.go", src, parser.Mode(0)) ast.Inspect(f, func(n ast.Node) bool { if ident, ok := n.(*ast.Ident); ok { fmt.Println(ident.Name) } return true }) } var src = `package p import _ "log" func add(n, m int) {} |
コメント文の解析
ParseFileの第4引数にparser.ParseCommentsを入れると解析できるようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package main import ( "fmt" "go/parser" "go/token" ) func main() { fset := token.NewFileSet() f, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments) for _, c := range f.Comments { fmt.Printf("%s: %q\n", fset.Position(c.Pos()), c.Text()) } } var src = `// Package p provides Add function // ... package p // Add adds two ints. func add(n, m int) int { return n + m } ` |
1 2 |
example.go:1:1: "Package p provides Add function\n...\n" example.go:5:1: "Add adds two ints.\n" |