はじめに
- Go 言語で generate を使ってコードを生成するツールを作ろうとしているが AST ノードの構成を見るためにいちいち ast.Print するのが面倒だったのでツールを作ってみた。
レポジトリ
できること
- オプション無しでファイルを指定すると、ルートノードの ast.Print を表示する。
-
-type
オプションでノードの型を指定すると、対象のノードの ast.Print とそのノードを取得するためのコマンドが表示される。- コマンドとは構造体のフィールド名や必要ならインターフェイスからのキャストを記述した文字列で、ルートノードに実行することで対象のノードを取得できるもの。
- 例えば一つ目の構造体宣言の取得コマンドは
Decls[0].(*ast.GenDecl).Specs[0]
-
-command
オプションでノードの取得コマンドを指定すると、対象のノードの ast.Print とそのノードを取得するためのコマンドが表示される。 -
-regex
オプションでノードの取得コマンドを正規表現で指定すると、対象のノードの ast.Print とそのノードを取得するためのコマンドが表示される。
実行例
型宣言のノードを型で検索して ast.Print を表示
検索対象
package main
type ST1 struct {
N int
B bool
}
type ST2 struct {
S string
IS []int
}
実行コマンドと結果
$ ast-walker -type *ast.TypeSpec file.go
Found!
COMMAND:
Decls[0].(*ast.GenDecl).Specs[0]
AST_PRINT:
0 *ast.TypeSpec {
1 . Name: *ast.Ident {
2 . . NamePos: test/file2.go:3:6
3 . . Name: "ST1"
4 . . Obj: *ast.Object {
5 . . . Kind: type
6 . . . Name: "ST1"
7 . . . Decl: *(obj @ 0)
8 . . }
9 . }
10 . Type: *ast.StructType {
11 . . Struct: test/file2.go:3:10
12 . . Fields: *ast.FieldList {
13 . . . Opening: test/file2.go:3:17
14 . . . List: []*ast.Field (len = 2) {
15 . . . . 0: *ast.Field {
16 . . . . . Names: []*ast.Ident (len = 1) {
17 . . . . . . 0: *ast.Ident {
18 . . . . . . . NamePos: test/file2.go:4:2
19 . . . . . . . Name: "N"
20 . . . . . . . Obj: *ast.Object {
21 . . . . . . . . Kind: var
22 . . . . . . . . Name: "N"
23 . . . . . . . . Decl: *(obj @ 15)
24 . . . . . . . }
25 . . . . . . }
26 . . . . . }
27 . . . . . Type: *ast.Ident {
28 . . . . . . NamePos: test/file2.go:4:4
29 . . . . . . Name: "int"
30 . . . . . }
31 . . . . }
32 . . . . 1: *ast.Field {
33 . . . . . Names: []*ast.Ident (len = 1) {
34 . . . . . . 0: *ast.Ident {
35 . . . . . . . NamePos: test/file2.go:5:2
36 . . . . . . . Name: "B"
37 . . . . . . . Obj: *ast.Object {
38 . . . . . . . . Kind: var
39 . . . . . . . . Name: "B"
40 . . . . . . . . Decl: *(obj @ 32)
41 . . . . . . . }
42 . . . . . . }
43 . . . . . }
44 . . . . . Type: *ast.Ident {
45 . . . . . . NamePos: test/file2.go:5:4
46 . . . . . . Name: "bool"
47 . . . . . }
48 . . . . }
49 . . . }
50 . . . Closing: test/file2.go:6:1
51 . . }
52 . . Incomplete: false
53 . }
54 }
COMMAND:
Decls[1].(*ast.GenDecl).Specs[0]
AST_PRINT:
0 *ast.TypeSpec {
1 . Name: *ast.Ident {
2 . . NamePos: test/file2.go:8:6
3 . . Name: "ST2"
4 . . Obj: *ast.Object {
5 . . . Kind: type
6 . . . Name: "ST2"
7 . . . Decl: *(obj @ 0)
8 . . }
9 . }
10 . Type: *ast.StructType {
11 . . Struct: test/file2.go:8:10
12 . . Fields: *ast.FieldList {
13 . . . Opening: test/file2.go:8:17
14 . . . List: []*ast.Field (len = 2) {
15 . . . . 0: *ast.Field {
16 . . . . . Names: []*ast.Ident (len = 1) {
17 . . . . . . 0: *ast.Ident {
18 . . . . . . . NamePos: test/file2.go:9:2
19 . . . . . . . Name: "S"
20 . . . . . . . Obj: *ast.Object {
21 . . . . . . . . Kind: var
22 . . . . . . . . Name: "S"
23 . . . . . . . . Decl: *(obj @ 15)
24 . . . . . . . }
25 . . . . . . }
26 . . . . . }
27 . . . . . Type: *ast.Ident {
28 . . . . . . NamePos: test/file2.go:9:5
29 . . . . . . Name: "string"
30 . . . . . }
31 . . . . }
32 . . . . 1: *ast.Field {
33 . . . . . Names: []*ast.Ident (len = 1) {
34 . . . . . . 0: *ast.Ident {
35 . . . . . . . NamePos: test/file2.go:10:2
36 . . . . . . . Name: "IS"
37 . . . . . . . Obj: *ast.Object {
38 . . . . . . . . Kind: var
39 . . . . . . . . Name: "IS"
40 . . . . . . . . Decl: *(obj @ 32)
41 . . . . . . . }
42 . . . . . . }
43 . . . . . }
44 . . . . . Type: *ast.ArrayType {
45 . . . . . . Lbrack: test/file2.go:10:5
46 . . . . . . Elt: *ast.Ident {
47 . . . . . . . NamePos: test/file2.go:10:7
48 . . . . . . . Name: "int"
49 . . . . . . }
50 . . . . . }
51 . . . . }
52 . . . }
53 . . . Closing: test/file2.go:11:1
54 . . }
55 . . Incomplete: false
56 . }
57 }
一つ目の型宣言をコマンドで検索して表示
検索対象
- 前の例と同じファイル
実行コマンドと結果
$ ast-walker -command Decls[0].(*ast.GenDecl).Specs[0] file.go
Found!
COMMAND:
Decls[0].(*ast.GenDecl).Specs[0]
AST_PRINT:
0 *ast.TypeSpec {
1 . Name: *ast.Ident {
2 . . NamePos: test/file2.go:3:6
3 . . Name: "ST1"
4 . . Obj: *ast.Object {
5 . . . Kind: type
6 . . . Name: "ST1"
7 . . . Decl: *(obj @ 0)
8 . . }
9 . }
10 . Type: *ast.StructType {
11 . . Struct: test/file2.go:3:10
12 . . Fields: *ast.FieldList {
13 . . . Opening: test/file2.go:3:17
14 . . . List: []*ast.Field (len = 2) {
15 . . . . 0: *ast.Field {
16 . . . . . Names: []*ast.Ident (len = 1) {
17 . . . . . . 0: *ast.Ident {
18 . . . . . . . NamePos: test/file2.go:4:2
19 . . . . . . . Name: "N"
20 . . . . . . . Obj: *ast.Object {
21 . . . . . . . . Kind: var
22 . . . . . . . . Name: "N"
23 . . . . . . . . Decl: *(obj @ 15)
24 . . . . . . . }
25 . . . . . . }
26 . . . . . }
27 . . . . . Type: *ast.Ident {
28 . . . . . . NamePos: test/file2.go:4:4
29 . . . . . . Name: "int"
30 . . . . . }
31 . . . . }
32 . . . . 1: *ast.Field {
33 . . . . . Names: []*ast.Ident (len = 1) {
34 . . . . . . 0: *ast.Ident {
35 . . . . . . . NamePos: test/file2.go:5:2
36 . . . . . . . Name: "B"
37 . . . . . . . Obj: *ast.Object {
38 . . . . . . . . Kind: var
39 . . . . . . . . Name: "B"
40 . . . . . . . . Decl: *(obj @ 32)
41 . . . . . . . }
42 . . . . . . }
43 . . . . . }
44 . . . . . Type: *ast.Ident {
45 . . . . . . NamePos: test/file2.go:5:4
46 . . . . . . Name: "bool"
47 . . . . . }
48 . . . . }
49 . . . }
50 . . . Closing: test/file2.go:6:1
51 . . }
52 . . Incomplete: false
53 . }
54 }
構造体宣言のフィールド定義部分をコマンドの正規表現で検索して表示
検索対象
- 前の例と同じファイル
実行コマンドと結果
$ ast-walker -regex 'Fields.List\[\d+\]$' file.go
Found!
COMMAND:
Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List[0]
AST_PRINT:
0 *ast.Field {
1 . Names: []*ast.Ident (len = 1) {
2 . . 0: *ast.Ident {
3 . . . NamePos: test/file2.go:4:2
4 . . . Name: "N"
5 . . . Obj: *ast.Object {
6 . . . . Kind: var
7 . . . . Name: "N"
8 . . . . Decl: *(obj @ 0)
9 . . . }
10 . . }
11 . }
12 . Type: *ast.Ident {
13 . . NamePos: test/file2.go:4:4
14 . . Name: "int"
15 . }
16 }
COMMAND:
Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List[1]
AST_PRINT:
0 *ast.Field {
1 . Names: []*ast.Ident (len = 1) {
2 . . 0: *ast.Ident {
3 . . . NamePos: test/file2.go:5:2
4 . . . Name: "B"
5 . . . Obj: *ast.Object {
6 . . . . Kind: var
7 . . . . Name: "B"
8 . . . . Decl: *(obj @ 0)
9 . . . }
10 . . }
11 . }
12 . Type: *ast.Ident {
13 . . NamePos: test/file2.go:5:4
14 . . Name: "bool"
15 . }
16 }
COMMAND:
Decls[1].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List[0]
AST_PRINT:
0 *ast.Field {
1 . Names: []*ast.Ident (len = 1) {
2 . . 0: *ast.Ident {
3 . . . NamePos: test/file2.go:9:2
4 . . . Name: "S"
5 . . . Obj: *ast.Object {
6 . . . . Kind: var
7 . . . . Name: "S"
8 . . . . Decl: *(obj @ 0)
9 . . . }
10 . . }
11 . }
12 . Type: *ast.Ident {
13 . . NamePos: test/file2.go:9:5
14 . . Name: "string"
15 . }
16 }
COMMAND:
Decls[1].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List[1]
AST_PRINT:
0 *ast.Field {
1 . Names: []*ast.Ident (len = 1) {
2 . . 0: *ast.Ident {
3 . . . NamePos: test/file2.go:10:2
4 . . . Name: "IS"
5 . . . Obj: *ast.Object {
6 . . . . Kind: var
7 . . . . Name: "IS"
8 . . . . Decl: *(obj @ 0)
9 . . . }
10 . . }
11 . }
12 . Type: *ast.ArrayType {
13 . . Lbrack: test/file2.go:10:5
14 . . Elt: *ast.Ident {
15 . . . NamePos: test/file2.go:10:7
16 . . . Name: "int"
17 . . }
18 . }
19 }