狠狠撸

狠狠撸Share a Scribd company logo
GoのASTをいじくって
新しいツールを作る
わかめ まさひろ
わかめ まさひろ @vvakame
TypeScript
Masahiro Wakame
De?nitelyTyped
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 (↑の?eld1個分)
? 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は敵
? 生成すべきコードがどんどん複雑に…
? ?eldの型が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開発者絶賛募集中!
骋辞に対する感想
疑問
? ライブラリのリビジョン?
? jwg 非互換な変更していいのかしら
? embedしたstructのメソッド呼び奴
? 外側のstructがreceiverになってほし
? Generics欲しい気持ちが抑えられない
怒り?
? stringのslice取ると[]byteなのやめて?
? 1文字=1バイトマンが作るライブラリ
? 再帰的なパッケージ参照許して?
? 1パッケージが際限なくでかくなる…
? err != nil 毎回やるのだるい?

More Related Content

GoCon 2015 Summer 骋辞の础厂罢をいじくって新しいツールを作る

  • 2. わかめ まさひろ @vvakame TypeScript Masahiro Wakame De?nitelyTyped appengine photo from golang.org/doc/gopher/
  • 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 -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 作った
  • 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" } 実行結果→
  • 13. 主張 ? コード生成 is 便利 ? GoだとGenericsないしコード増えがち ? コンパイル時チェックの恩恵! ? 文字列で指定とか時代遅れだよね~ ? 元コード→データ化→加工→生成! ? まずはソースコードを解析しないと!
  • 14. 正規表現で頑張る 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)? }
  • 16. ASTを活用する ? AST = Abstract Syntax Tree ? 本来はコンパイラ内部の中間表現 ? ソースコードをデータとして使える! ? コード解析はライブラリに任せよう! ? 解析後のコード組立に専念できる!
  • 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
  • 29. ツール開発の流れ 1. 処理対象(のstruct)を決める 2. コード生成結果を手書きする ?名前を機械的に考えてつけよう 3. 必要な俺形式のデータ構造を設計する ?ASTから取れるか?不足はないか? ?型情報取れなくて辛いパターンある 4. 頑張ってAST取って変換して生成処理書く
  • 30. 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
  • 31. 俺形式が必要な理由 // 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..
  • 32. 代表的な俺形式 ? Source (生成結果ソース全体) ? Struct (生成するstruct1個分) ? Field (↑の?eld1個分) ? Tag (↑に付属するtag情報)
  • 33. 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"`? }
  • 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は敵 ? 生成すべきコードがどんどん複雑に… ? ?eldの型がstructだと絶望 ? ASTだけでは型の詳細な情報がない ? 生成コードないとコンパイル通らん ? Printfの %[1]s 記法マジ便利
  • 38. I ? Pull Request ? よりGoらしい書き方できるよ! ? より効率の良い実装があるよ! ? Template使えよ! ? text/template は気に入らなかった… ? なんかないですかね? github.com/favclip
  • 40. We are hiring! ? 開発:テレビ朝日 ? jwg, genbase 他 爆誕! ? http://www.favclip.com/ ? appengine/go開発者絶賛募集中!
  • 42. 疑問 ? ライブラリのリビジョン? ? jwg 非互換な変更していいのかしら ? embedしたstructのメソッド呼び奴 ? 外側のstructがreceiverになってほし ? Generics欲しい気持ちが抑えられない
  • 43. 怒り? ? stringのslice取ると[]byteなのやめて? ? 1文字=1バイトマンが作るライブラリ ? 再帰的なパッケージ参照許して? ? 1パッケージが際限なくでかくなる… ? err != nil 毎回やるのだるい?