GoのASTをいじくって
新しいツールを作る
わかめ まさひろ
わかめ まさひろ @vvakame
TypeScript
Masahiro Wakame
DefinitelyTyped
appengine
photo from golang.org/doc/gopher/
めんどいことはしたくない
誰だってそうする
俺だってそうする
encoding/json
play.golang.org/p/T9uO25D2xz
…


type Game struct {

ID int64 `json:"id"`

Title string `json:"title"`

Price int `json:"price"`

InDevelopment bool `json:"inDevelopment"`

ShippedAt time.Time `json:"shippedAt"`

}



func main() {

game := &Game{

ID: 1,

Title: "Splatoon",

Price: 5700,

InDevelopment: false,

}

b, _ := json.Marshal(game)

fmt.Println(string(b))

}
encoding/json
…


type Game struct {

ID int64 `json:"id"`

Title string `json:"title"`

Price int `json:"price"`

InDevelopment bool `json:"inDevelopment"`

ShippedAt time.Time `json:"shippedAt"`

}



func main() {

game := &Game{

ID: 1,

Title: "Splatoon",

Price: 5700,

InDevelopment: false,

}

b, _ := json.Marshal(game)

fmt.Println(string(b))

}
手書き!?
正気か!?!?
めんどい
“ 閉じるの忘れる
typoる
jwg 作った
//go:generate jwg -output model_json.go .

package sample



…


// +jwg

type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}



func main() {

game := &Game{

ID: 1, Title: "Splatoon", Price: 5700, InDevelopment: false,

}

jsonObj, _ := NewGameJsonBuilder().AddAll().Convert(game)

b, _ := json.Marshal(jsonObj)

fmt.Println(string(b))

}
jwg = Json Wrapper Generator
//go:generate jwg -output model_json.go .

package sample



…


// +jwg

type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}



func main() {

game := &Game{

ID: 1, Title: "Splatoon", Price: 5700, InDevelopment: false,

}

jsonObj, _ := NewGameJsonBuilder().AddAll().Convert(game)

b, _ := json.Marshal(jsonObj)

fmt.Println(string(b))

}
go generate 使う!
コメントにタグ書く(標準仕様などない!
生成したコード利用だ!
jwg 作った
自動生成!
type GameJson struct {

ID int64 `json:"id,omitempty"`

Title string `json:"title,omitempty"`

Price int `json:"price,omitempty"`

InDevelopment bool `json:"inDevelopment,omitempty"`

ShippedAt time.Time `json:"shippedAt,omitempty"`

}
楽
その他!
type GameJson
func (orig *GameJson) Convert() (*Game, error)
type GameJsonBuilder
func NewGameJsonBuilder() *GameJsonBuilder
func (b *GameJsonBuilder) Add(info *GamePropertyInfo) *GameJsonBuilder
func (b *GameJsonBuilder) AddAll() *GameJsonBuilder
func (b *GameJsonBuilder) Convert(orig *Game) (*GameJson, error)
func (b *GameJsonBuilder) ConvertList(orig []*Game) (GameJsonList, error)
func (b *GameJsonBuilder) Marshal(orig *Game) ([]byte, error)
func (b *GameJsonBuilder) Remove(info *GamePropertyInfo) *GameJsonBuilder
type GameJsonList
func (jsonList GameJsonList) Convert() ([]*Game, error)
type GamePropertyDecoder
type GamePropertyEncoder
type GamePropertyInfo *JsonBuilder
*Property(De|En)coder
*PropertyInfo
Web API作成用
play.golang.org/p/5wYA62Njvn
func (b *GameJsonBuilder) AddSite() *GameJsonBuilder {

b.AddAll()

b.Remove(b.ID) // IDは内部情報なのでいらない

b.Price.Encoder = func(src *Game, dest *GameJson) error {

if !src.InDevelopment {

dest.Price = src.Price // 開発中じゃない時だけ価格を出すよ!

}

return nil

}



return b

}



func main() {

game := &Game{

ID: 2, Title: "Secret of Yaba", Price: 9999, InDevelopment: true,

}

jsonObj, _ := NewGameJsonBuilder().AddSite().Convert(game)

b, _ := json.Marshal(jsonObj)

fmt.Println(string(b))

}
{
"title":"Secret of Yaba”,
“inDevelopment":true,
“shippedAt":"0001-01-01T00:00:00Z"
}
実行結果→
この間公開しました!
生成コードは特に依存なし
みんなも作ろう!
主張
• コード生成 is 便利
• GoだとGenericsないしコード増えがち
• コンパイル時チェックの恩恵!
• 文字列で指定とか時代遅れだよね∼
• 元コード→データ化→加工→生成!
• まずはソースコードを解析しないと!
正規表現で頑張る
http://play.golang.org/p/fsOl7CcjgB
package main



import (

"fmt"

"regexp"

)



func main() {

code := `

type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}

`



re:=regexp.MustCompile(`s*types+([a-zA-Z]+)s+structs+{n(?:s*([a-zA-Z0-9]+)s+([a-zA-Z0-9.]+)s*n)*s*}`)

result := re.FindAllStringSubmatch(code,-1)

fmt.Printf("%#v", result)

}
copyright @shati_ko
ASTを活用する
• AST = Abstract Syntax Tree
• 本来はコンパイラ内部の中間表現
• ソースコードをデータとして使える!
• コード解析はライブラリに任せよう!
• 解析後のコード組立に専念できる!
copyright @shati_ko
AST? コード生成??
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
type (

A struct {

Foo string

}

B struct {

Bar string

}

)
こういう記法もある(怖い
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
ast.Ident
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
ast.Ident
ast.StructType
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
ast.Ident
ast.StructType
ast.FieldList
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
ast.Ident
ast.StructType
ast.FieldList
ast.Field type A struct {

Foo, Bar string

}
こういう記法もある(怖い
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
ast.Ident
ast.StructType
ast.FieldList
ast.Field
ast.Ident
Game struct → AST
type Game struct {

ID int64

Title string

Price int

InDevelopment bool

ShippedAt time.Time

}
ast = go/ast package
ast.GenDecl
ast.TypeSpec
ast.Ident
ast.StructType
ast.FieldList
ast.Field
ast.Ident
ast.Ident
AST→コード生成
_人人人人人人人人人人人人_
> 文字列組み立て頑張る <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
まじか
まじだ
開発のコツと構造
ここでは、struct読み取り→
ラッパ生成の流れに絞って解説する
ツール開発の流れ
1. 処理対象(のstruct)を決める
2. コード生成結果を手書きする
•名前を機械的に考えてつけよう
3. 必要な俺形式のデータ構造を設計する
•ASTから取れるか?不足はないか?
•型情報取れなくて辛いパターンある
4. 頑張ってAST取って変換して生成処理書く
Goコードの構造
// generated by jwg -output model_json.go .; DO NOT EDIT



package sample



import (

"encoding/json"

"time"

)



// for Game

type GameJson struct {

ID int64 `json:"id,omitempty"`

Title string `json:"title,omitempty"`

Price int `json:"price,omitempty"`

InDevelopment bool `json:"inDevelopment,omitempty"`

ShippedAt time.Time `json:"shippedAt,omitempty"`

}

PackageClause
ImportDecl
TopLevelDecl
俺形式が必要な理由
// generated by jwg -output model_json.go .; DO NOT EDIT



package sample



import (

"encoding/json"

"time"

)



// for Game

type GameJson struct {

ID int64 `json:"id,omitempty"`

Title string `json:"title,omitempty"`

Price int `json:"price,omitempty"`

InDevelopment bool `json:"inDevelopment,omitempty"`

ShippedAt time.Time `json:"shippedAt,omitempty"`

}

正しいPackageClauseの生成には、
TopLevelDecl生成結果の把握が必要! etc..
代表的な俺形式
• Source (生成結果ソース全体)
• Struct (生成するstruct1個分)
• Field (↑のfield1個分)
• Tag (↑に付属するtag情報)
jwgの場合
// BuildStruct represents source code of assembling..

type BuildSource struct {

g *genbase.Generator

pkg *genbase.PackageInfo

typeInfos genbase.TypeInfos



Structs []*BuildStruct

}



// BuildStruct represents struct of assembling..

type BuildStruct struct {

parent *BuildSource

typeInfo *genbase.TypeInfo



Fields []*BuildField

}



// BuildField represents field of BuildStruct.

type BuildField struct {

parent *BuildStruct

fieldInfo *genbase.FieldInfo



Name string

Embed bool

Tag *BuildTag

}



// BuildTag represents tag of BuildField.

type BuildTag struct {

field *BuildField



Name string

Ignore bool // e.g. Secret string `json:"-"`

DoNotEmit bool // e.g. Field int `json:",omitempty"`

String bool // e.g. Int64String int64 `json:",string"`

}
genbaseのご紹介
• 3つほどコード生成ツール作った
• 定形処理の存在に気がつく
• AST読み込み
• 指定されたorタグ付きstructの収集
• import句の管理
• コード組み立て・フォーマット
• その他便利関数とか
github.com/favclip/genbase
参考:typewriter
そして気合
func (st *BuildStruct) emit(g *genbase.Generator) error {

g.Printf("// for %sn", st.Name())



// generate FooJson struct from Foo struct

g.Printf("type %sJson struct {n", st.Name())

for _, field := range st.Fields {

if field.Tag.Ignore {

continue

}

postfix := ""

if field.WithJWG() {

postfix = "Json"

}

tagString := field.Tag.TagString()

if tagString != "" {

tagString = fmt.Sprintf("`%s`", tagString)

}

if field.Embed {

g.Printf("%s%s %sn", field.fieldInfo.TypeName(), postfix, tagString)

} else {

g.Printf("%s %s%s %sn", field.Name, field.fieldInfo.TypeName(), postfix, tagString)

}

}

g.Printf("}nn")



g.Printf("type %[1]sJsonList []*%[1]sJsonnn", st.Name())



// generate property builder

g.Printf("type %[1]sPropertyEncoder func(src *%[1]s, dest *%[1]sJson) errornn", st.Name())

g.Printf("type %[1]sPropertyDecoder func(src *%[1]sJson, dest *%[1]s) errornn", st.Name())



// generate property info

g.Printf(`

type %[1]sPropertyInfo struct {

name string

Encoder %[1]sPropertyEncoder

Decoder %[1]sPropertyDecoder

}



`, st.Name())



// generate json builder

↓ざっくり500行続く
デカさ
• genbase
• ざっくり580行くらい
• jwg
• ざっくり850行くらい
• 生成後コード読んでから読めば理解る
• …んじゃないかな多分
Tips
• 埋め込みstructは敵
• 生成すべきコードがどんどん複雑に…
• fieldの型がstructだと絶望
• ASTだけでは型の詳細な情報がない
• 生成コードないとコンパイル通らん
• Printfの %[1]s 記法マジ便利
I ♥ Pull Request
• よりGoらしい書き方できるよ!
• より効率の良い実装があるよ!
• Template使えよ!
• text/template は気に入らなかった…
• なんかないですかね?
github.com/favclip
宣伝
We are hiring!
• 開発:テレビ朝日
• jwg, genbase 他 爆誕!
• http://www.favclip.com/
• appengine/go開発者絶賛募集中!
Goに対する感想
疑問
• ライブラリのリビジョン?
• jwg 非互換な変更していいのかしら
• embedしたstructのメソッド呼び奴
• 外側のstructがreceiverになってほし
• Generics欲しい気持ちが抑えられない
怒り💢
• stringのslice取ると[]byteなのやめて💢
• 1文字=1バイトマンが作るライブラリ
• 再帰的なパッケージ参照許して💢
• 1パッケージが際限なくでかくなる…
• err != nil 毎回やるのだるい💢

GoCon 2015 Summer GoのASTをいじくって新しいツールを作る

  • 1.
  • 2.
    わかめ まさひろ @vvakame TypeScript MasahiroWakame DefinitelyTyped appengine photo from golang.org/doc/gopher/
  • 3.
  • 4.
    encoding/json play.golang.org/p/T9uO25D2xz … 
 type Game struct{
 ID int64 `json:"id"`
 Title string `json:"title"`
 Price int `json:"price"`
 InDevelopment bool `json:"inDevelopment"`
 ShippedAt time.Time `json:"shippedAt"`
 }
 
 func main() {
 game := &Game{
 ID: 1,
 Title: "Splatoon",
 Price: 5700,
 InDevelopment: false,
 }
 b, _ := json.Marshal(game)
 fmt.Println(string(b))
 }
  • 5.
    encoding/json … 
 type Game struct{
 ID int64 `json:"id"`
 Title string `json:"title"`
 Price int `json:"price"`
 InDevelopment bool `json:"inDevelopment"`
 ShippedAt time.Time `json:"shippedAt"`
 }
 
 func main() {
 game := &Game{
 ID: 1,
 Title: "Splatoon",
 Price: 5700,
 InDevelopment: false,
 }
 b, _ := json.Marshal(game)
 fmt.Println(string(b))
 } 手書き!? 正気か!?!? めんどい “ 閉じるの忘れる typoる
  • 6.
    jwg 作った //go:generate jwg-output model_json.go .
 package sample
 
 … 
 // +jwg
 type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 }
 
 func main() {
 game := &Game{
 ID: 1, Title: "Splatoon", Price: 5700, InDevelopment: false,
 }
 jsonObj, _ := NewGameJsonBuilder().AddAll().Convert(game)
 b, _ := json.Marshal(jsonObj)
 fmt.Println(string(b))
 } jwg = Json Wrapper Generator
  • 7.
    //go:generate jwg -outputmodel_json.go .
 package sample
 
 … 
 // +jwg
 type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 }
 
 func main() {
 game := &Game{
 ID: 1, Title: "Splatoon", Price: 5700, InDevelopment: false,
 }
 jsonObj, _ := NewGameJsonBuilder().AddAll().Convert(game)
 b, _ := json.Marshal(jsonObj)
 fmt.Println(string(b))
 } go generate 使う! コメントにタグ書く(標準仕様などない! 生成したコード利用だ! jwg 作った
  • 8.
    自動生成! type GameJson struct{
 ID int64 `json:"id,omitempty"`
 Title string `json:"title,omitempty"`
 Price int `json:"price,omitempty"`
 InDevelopment bool `json:"inDevelopment,omitempty"`
 ShippedAt time.Time `json:"shippedAt,omitempty"`
 } 楽
  • 9.
    その他! type GameJson func (orig*GameJson) Convert() (*Game, error) type GameJsonBuilder func NewGameJsonBuilder() *GameJsonBuilder func (b *GameJsonBuilder) Add(info *GamePropertyInfo) *GameJsonBuilder func (b *GameJsonBuilder) AddAll() *GameJsonBuilder func (b *GameJsonBuilder) Convert(orig *Game) (*GameJson, error) func (b *GameJsonBuilder) ConvertList(orig []*Game) (GameJsonList, error) func (b *GameJsonBuilder) Marshal(orig *Game) ([]byte, error) func (b *GameJsonBuilder) Remove(info *GamePropertyInfo) *GameJsonBuilder type GameJsonList func (jsonList GameJsonList) Convert() ([]*Game, error) type GamePropertyDecoder type GamePropertyEncoder type GamePropertyInfo *JsonBuilder *Property(De|En)coder *PropertyInfo
  • 10.
    Web API作成用 play.golang.org/p/5wYA62Njvn func (b*GameJsonBuilder) AddSite() *GameJsonBuilder {
 b.AddAll()
 b.Remove(b.ID) // IDは内部情報なのでいらない
 b.Price.Encoder = func(src *Game, dest *GameJson) error {
 if !src.InDevelopment {
 dest.Price = src.Price // 開発中じゃない時だけ価格を出すよ!
 }
 return nil
 }
 
 return b
 }
 
 func main() {
 game := &Game{
 ID: 2, Title: "Secret of Yaba", Price: 9999, InDevelopment: true,
 }
 jsonObj, _ := NewGameJsonBuilder().AddSite().Convert(game)
 b, _ := json.Marshal(jsonObj)
 fmt.Println(string(b))
 } { "title":"Secret of Yaba”, “inDevelopment":true, “shippedAt":"0001-01-01T00:00:00Z" } 実行結果→
  • 11.
  • 12.
  • 13.
    主張 • コード生成 is便利 • GoだとGenericsないしコード増えがち • コンパイル時チェックの恩恵! • 文字列で指定とか時代遅れだよね∼ • 元コード→データ化→加工→生成! • まずはソースコードを解析しないと!
  • 14.
    正規表現で頑張る http://play.golang.org/p/fsOl7CcjgB package main
 
 import (
 "fmt"
 "regexp"
 )
 
 funcmain() {
 code := `
 type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 }
 `
 
 re:=regexp.MustCompile(`s*types+([a-zA-Z]+)s+structs+{n(?:s*([a-zA-Z0-9]+)s+([a-zA-Z0-9.]+)s*n)*s*}`)
 result := re.FindAllStringSubmatch(code,-1)
 fmt.Printf("%#v", result)
 }
  • 15.
  • 16.
    ASTを活用する • AST =Abstract Syntax Tree • 本来はコンパイラ内部の中間表現 • ソースコードをデータとして使える! • コード解析はライブラリに任せよう! • 解析後のコード組立に専念できる!
  • 17.
  • 18.
  • 19.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl
  • 20.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec type (
 A struct {
 Foo string
 }
 B struct {
 Bar string
 }
 ) こういう記法もある(怖い
  • 21.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec ast.Ident
  • 22.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec ast.Ident ast.StructType
  • 23.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec ast.Ident ast.StructType ast.FieldList
  • 24.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec ast.Ident ast.StructType ast.FieldList ast.Field type A struct {
 Foo, Bar string
 } こういう記法もある(怖い
  • 25.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec ast.Ident ast.StructType ast.FieldList ast.Field ast.Ident
  • 26.
    Game struct →AST type Game struct {
 ID int64
 Title string
 Price int
 InDevelopment bool
 ShippedAt time.Time
 } ast = go/ast package ast.GenDecl ast.TypeSpec ast.Ident ast.StructType ast.FieldList ast.Field ast.Ident ast.Ident
  • 27.
  • 28.
  • 29.
    ツール開発の流れ 1. 処理対象(のstruct)を決める 2. コード生成結果を手書きする •名前を機械的に考えてつけよう 3.必要な俺形式のデータ構造を設計する •ASTから取れるか?不足はないか? •型情報取れなくて辛いパターンある 4. 頑張ってAST取って変換して生成処理書く
  • 30.
    Goコードの構造 // generated byjwg -output model_json.go .; DO NOT EDIT
 
 package sample
 
 import (
 "encoding/json"
 "time"
 )
 
 // for Game
 type GameJson struct {
 ID int64 `json:"id,omitempty"`
 Title string `json:"title,omitempty"`
 Price int `json:"price,omitempty"`
 InDevelopment bool `json:"inDevelopment,omitempty"`
 ShippedAt time.Time `json:"shippedAt,omitempty"`
 }
 PackageClause ImportDecl TopLevelDecl
  • 31.
    俺形式が必要な理由 // generated byjwg -output model_json.go .; DO NOT EDIT
 
 package sample
 
 import (
 "encoding/json"
 "time"
 )
 
 // for Game
 type GameJson struct {
 ID int64 `json:"id,omitempty"`
 Title string `json:"title,omitempty"`
 Price int `json:"price,omitempty"`
 InDevelopment bool `json:"inDevelopment,omitempty"`
 ShippedAt time.Time `json:"shippedAt,omitempty"`
 }
 正しいPackageClauseの生成には、 TopLevelDecl生成結果の把握が必要! etc..
  • 32.
    代表的な俺形式 • Source (生成結果ソース全体) •Struct (生成するstruct1個分) • Field (↑のfield1個分) • Tag (↑に付属するtag情報)
  • 33.
    jwgの場合 // BuildStruct representssource code of assembling..
 type BuildSource struct {
 g *genbase.Generator
 pkg *genbase.PackageInfo
 typeInfos genbase.TypeInfos
 
 Structs []*BuildStruct
 }
 
 // BuildStruct represents struct of assembling..
 type BuildStruct struct {
 parent *BuildSource
 typeInfo *genbase.TypeInfo
 
 Fields []*BuildField
 }
 
 // BuildField represents field of BuildStruct.
 type BuildField struct {
 parent *BuildStruct
 fieldInfo *genbase.FieldInfo
 
 Name string
 Embed bool
 Tag *BuildTag
 }
 
 // BuildTag represents tag of BuildField.
 type BuildTag struct {
 field *BuildField
 
 Name string
 Ignore bool // e.g. Secret string `json:"-"`
 DoNotEmit bool // e.g. Field int `json:",omitempty"`
 String bool // e.g. Int64String int64 `json:",string"`
 }
  • 34.
    genbaseのご紹介 • 3つほどコード生成ツール作った • 定形処理の存在に気がつく •AST読み込み • 指定されたorタグ付きstructの収集 • import句の管理 • コード組み立て・フォーマット • その他便利関数とか github.com/favclip/genbase 参考:typewriter
  • 35.
    そして気合 func (st *BuildStruct)emit(g *genbase.Generator) error {
 g.Printf("// for %sn", st.Name())
 
 // generate FooJson struct from Foo struct
 g.Printf("type %sJson struct {n", st.Name())
 for _, field := range st.Fields {
 if field.Tag.Ignore {
 continue
 }
 postfix := ""
 if field.WithJWG() {
 postfix = "Json"
 }
 tagString := field.Tag.TagString()
 if tagString != "" {
 tagString = fmt.Sprintf("`%s`", tagString)
 }
 if field.Embed {
 g.Printf("%s%s %sn", field.fieldInfo.TypeName(), postfix, tagString)
 } else {
 g.Printf("%s %s%s %sn", field.Name, field.fieldInfo.TypeName(), postfix, tagString)
 }
 }
 g.Printf("}nn")
 
 g.Printf("type %[1]sJsonList []*%[1]sJsonnn", st.Name())
 
 // generate property builder
 g.Printf("type %[1]sPropertyEncoder func(src *%[1]s, dest *%[1]sJson) errornn", st.Name())
 g.Printf("type %[1]sPropertyDecoder func(src *%[1]sJson, dest *%[1]s) errornn", st.Name())
 
 // generate property info
 g.Printf(`
 type %[1]sPropertyInfo struct {
 name string
 Encoder %[1]sPropertyEncoder
 Decoder %[1]sPropertyDecoder
 }
 
 `, st.Name())
 
 // generate json builder
 ↓ざっくり500行続く
  • 36.
    デカさ • genbase • ざっくり580行くらい •jwg • ざっくり850行くらい • 生成後コード読んでから読めば理解る • …んじゃないかな多分
  • 37.
    Tips • 埋め込みstructは敵 • 生成すべきコードがどんどん複雑に… •fieldの型がstructだと絶望 • ASTだけでは型の詳細な情報がない • 生成コードないとコンパイル通らん • Printfの %[1]s 記法マジ便利
  • 38.
    I ♥ PullRequest • よりGoらしい書き方できるよ! • より効率の良い実装があるよ! • Template使えよ! • text/template は気に入らなかった… • なんかないですかね? github.com/favclip
  • 39.
  • 40.
    We are hiring! •開発:テレビ朝日 • jwg, genbase 他 爆誕! • http://www.favclip.com/ • appengine/go開発者絶賛募集中!
  • 41.
  • 42.
    疑問 • ライブラリのリビジョン? • jwg非互換な変更していいのかしら • embedしたstructのメソッド呼び奴 • 外側のstructがreceiverになってほし • Generics欲しい気持ちが抑えられない
  • 43.
    怒り💢 • stringのslice取ると[]byteなのやめて💢 • 1文字=1バイトマンが作るライブラリ •再帰的なパッケージ参照許して💢 • 1パッケージが際限なくでかくなる… • err != nil 毎回やるのだるい💢