Goの抽象構文木の利用例を読んだ
goにはコードの自動生成ツールが付属しているらしい。
Go と reflect と generate
http://qiita.com/naoina/items/7966f73f3a807b3d25d6
ここで「AST からコード生成する方法」という節の内容に興味を持った。
文字列テンプレートからコードを生成するのではなく、抽象構文木から生成する方法である。
IdentとかFuncDecとかいう概念がよくわからないので抽象構文木の使い方でぐぐったらt2yの書いた記事がヒットしてしまった。
Python の ast モジュール入門 (抽象構文木を辿る)
http://qiita.com/t2y/items/0964d01bf3db0233e3c1
めぼしい記載は無かったが、これを読んでいて、astに特定の決まった構成要素が定められているわけではないことは理解できた。
つまりGoのastのドキュメントを読まなければ自分の知りたいことはわからない、と。
そして、ひとまずGoのastのドキュメントに戻った。
type Ident An Ident node represents an identifier. type Ident struct { NamePos token.Pos // identifier position Name string // identifier name Obj *Object // denoted object; or nil }
type FuncDecl A FuncDecl node represents a function declaration. type FuncDecl struct { Doc *CommentGroup // associated documentation; or nil Recv *FieldList // receiver (methods); or nil (functions) Name *Ident // function/method name Type *FuncType // function signature: parameters, results, and position of "func" keyword Body *BlockStmt // function body; or nil (forward declaration) }
type BasicLit A BasicLit node represents a literal of basic type. type BasicLit struct { ValuePos token.Pos // literal position Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o` }
type CompositeLit A CompositeLit node represents a composite literal. type CompositeLit struct { Type Expr // literal type; or nil Lbrace token.Pos // position of "{" Elts []Expr // list of composite elements; or nil Rbrace token.Pos // position of "}" }
問題は以下のコードを理解することだ。
case *ast.Ident: if strings.Contains(aType.Name, "PlaceHolder") { aType.Name = strings.Replace(aType.Name, "PlaceHolder", structName, 1) } case *ast.FuncDecl: if aType.Recv == nil { break } switch aType.Name.Name { case "columns": aType.Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.BasicLit).Value = strconv.Quote(strings.Join(columns, ", ")) case "fields": recvName := aType.Recv.List[0].Names[0].Name clit := aType.Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.CompositeLit) clit.Elts = clit.Elts[:0] for _, c := range columns { clit.Elts = append(clit.Elts, &ast.UnaryExpr{ Op: token.AND, X: &ast.SelectorExpr{ X: ast.NewIdent(recvName), Sel: ast.NewIdent(c), }, }) } } }
Identがメソッドのレシーバーや関数の引数に相当するらしい。
その識別子の名前が "PlaceHolder" を含めば、所定の名前に書き換える、という処理が最初のif節。
次が、FuncDeclは関数の定義を示すらしい。
その関数がレシーバーを持っていなければbreak。メソッドしか書き換えないから普通の関数が来たら処理を打ち切る。
そして、引数の名前が "columns" または "fields" ならばチョメチョメする。
"columns" が来たとき
最初の行をreturnステートメントとみなし、その文に含まれる先頭のリテラル値を書き換える。
"fields"が来たとき
returnステートメントからコンポジット型のリテラルを取り出して内容を書き換える。
だいたいこんな感じのことをやっているんだろう。
どうもGoの名前の略し方が慣れないのだが(そもそもPythonはあまり名前を省略しないので)、
- Identはidentifierの略
- FuncDeclはFunction declarationの略
- BasicLitはBasic type literal(基本データ型のリテラル, intとかstring)の略
- CompositeLitはComposite type literal(コンポジット型のリテラル, mapとかスライス)の略
ということらしい。
次の目標
次の目標は
https://github.com/azihsoyn/IDDD_go_sample
ここに書いてある
$ go build +build=mock
この "+build=mock" の意味を理解すること。
やっていることはモックと本物のデータソースを置き換えることらしい。