More Related Content Similar to マスター・オブ・goパッケージ (20) More from Takuya Ueda (18) マスター・オブ・goパッケージ1. The Go gopher was designed by Renée French.
The gopher stickers was made by Takuya Ueda.
Licensed under the Creative Commons 3.0 Attributions license.
マスター・オブ・goパッケージ
@会津大Go言語+仮想通貨勉強会
2017年12月10日
1
※ この資料はGolangUK 2017の発表資料を改良したものです
3. ソウゾウ エキスパートチーム
技術をアウトプットするところに技術は集まる
■ エキスパートチームとは?
● 50%以上の時間を技術コミュニティへの貢献に充てる
■ エキスパートチームの役割
● 社内に新しい技術を取り取り込む
● 社外のコミュニティなどを通じて社会へ還元する
■ エキスパートチームの活動
● カンファレンス・勉強会の開催/運営
● 対外的な講演活動
● 執筆、雑誌への寄稿、インタビュー
● 社内外での担当技術の普及推進
@tenntenn
担当:Go・GCP
@mhidaka
担当:Android
メンバー
3
7. "Gopher"を探せ!
type Gopher struct { Gopher string `json:"gopher"` }
func main() {
const gopher = "GOPHER"
gogopher := GOPHER()
gogopher.Gopher = gopher
fmt.Println(gogopher)
}
func GOPHER() (gopher *Gopher) {
gopher = &Gopher{ Gopher: "gopher" }
return
}
7
8. みんな大好きgrep
$ grep Gopher main.go
type Gopher struct { Gopher string `json:"gopher"` }
gogopher.Gopher = gopher
func GOPHER() (gopher *Gopher) {
gopher = &Gopher{ Gopher: "gopher" }
grepで"Gopher"を探す
8
9. "Gopher" 型を探せ!
type Gopher struct { Gopher string `json:"gopher"` }
func main() {
const gopher = "GOPHER"
gogopher := GOPHER()
gogopher.Gopher = gopher
fmt.Println(gogopher)
}
func GOPHER() (gopher *Gopher) {
gopher = &Gopher{ Gopher: "gopher" }
return
}
9
13. リフレクション
■ リフレクション
● 実行時に型や値 情報を解析する
■ Goにおけるリフレクション
● reflectパッケージを使う
● 実行時にstructタグにアクセスする唯一の方法
● 出力引数としてポインタを貰ってきた場合に値を入れるために使用
● JSONやXMLなどのエンコードに使用
13
16. go パッケージ
go/ast 抽象構文木(AST)を提供
go/build パッケージに関する情報を集める
go/constant 定数に関する型を提供
go/doc ドキュメントをASTから取り出す
go/format コードフォーマッタ機能を提供
go/importer コンパイラに適したImporterを提供
go/parser 構文解析 機能を提供
go/printer AST 表示機能を提供
go/scanner 字句解析 機能を提供
go/token トークンに関する型を提供
go/types 型チェックに関する機能を提供
16
20. 構文解析 - go/parser,go/ast
■ トークンを抽象構文木(AST)に変換
v + 1
IDENT ADD INT
ソースコード:
+
v 1
BinaryExpr
Ident BasicLit
トークン:
抽象構文(AST):
20
24. 式の抽象構文木を取得する
expr, err := parser.ParseExpr(`v + 1`)
if err != nil {
/* handling the error */
}
/* use expr */
■ 式単位でパースする
24
25. ファイル単位で抽象構文木を取得
const src = `
package main
var v = 100
func main() {
fmt.Println(v+1)
}`
fs := token.NewFileSet()
f, err := parser.ParseFile(fs, "my.go", src, 0)
if err != nil {
/* handling the error */
}
/* use f */
srcがnilの場合だと
ファイル名を元にファイルが開かれる
ソースコード
パースモード
25
28. 抽象構文木の探索- ast.Inspect
■ Using ast.Inspect
expr, _ := parser.ParseExpr(`v + 1`)
ast.Inspect(expr, func(n ast.Node) bool {
if n != nil { fmt.Printf("%Tn", n) }
return true
})
抽象構文木の探索
*ast.BinaryExpr
*ast.Ident
*ast.BasicLit
Playgroundで実行
ast.Walk はもっと複雑な処理ができる
+
v 1
BinaryExpr
Ident BasicLit
28
29. 抽象構文木の探索 - 再帰
func traverse(n ast.Node) {
switch n := n.(type) {
case *ast.Ident:
fmt.Println(n.Name)
case *ast.BinaryExpr:
traverse(n.X)
traverse(n.Y)
case *ast.UnaryExpr:
traverse(n.X)
default:
fmt.Println(n)
}
}
識別子:識別子名を出力
2項演算式:各項について再帰的に探索
型で処理を分岐
Playgroundで実行
単項演算式:項を再帰的に探索
29
31. 型チェック
/* 型チェックのためのConfigを初期化 */
cfg := &types.Config{Importer: importer.Default()}
info := &types.Info{
/* TODO: 結果を保持するためのmapを初期化 */
}
pkg, err := cfg.Check("main", fs, []*ast.File{f}, info)
if err != nil {
/* エラー処理 */
}
/* TODO: pkgやinfoを使う処理 */
■ (*types.Config).Check で型チェックを行う
31
32. types.Info で型チェックの結果を保持する
type Info struct {
// Types maps expressions to their types, and for constant
// expressions, also their values.
Types map[ast.Expr]TypeAndValue
// Defs maps identifiers to the objects they define.
Defs map[*ast.Ident]Object
// Uses maps identifiers to the objects they denote.
Uses map[*ast.Ident]Object
// Implicits maps nodes to their implicitly declared objects, if any.
Implicits map[ast.Node]Object
// Selections maps selector expressions (excluding qualified identifiers)
// to their corresponding selections.
Selections map[*ast.SelectorExpr]*Selection
// Scopes maps ast.Nodes to the scopes they define.
Scopes map[ast.Node]*Scope
// InitOrder is the list of package-level initializers in the order in which
// they must be executed.
InitOrder []*Initializer
}
32
37. ノードの情報を取得するメソッド
■ Node() ast.Node
● 現在注目のノード取得する
■ Parent() ast.Node
● 注目ノードの親ノードを取得
■ Name() string
● 親ノードにどういう名前でノードを管理されているか
○ 例:BinaryExprの場合、2つの項はXとY
■ Index() int
● 親ノードでスライスとして管理していた場合にそのインデックス
○ 例:関数の引数など
● Nameメソッドだけで区別できない場合に使う
37
38. ノードを変更するメソッド
■ Replace(n ast.Node)
● 現在注目のノードを指定したノードで入れ替える
■ InsertBefore(n ast.Node)
● スライスで管理されているノードが対象
● 現在注目のノードの前にノードを追加する
■ InsertAfter(n ast.Node)
● 現在注目のノードの後ろにノードを追加する
■ Delete()
● 現在注目のノードを削除する
38
39. ノードの更新の例
39
expr, err := parser.ParseExpr(`x+y`)
if err != nil { log.Fatal(err) }
n := astutil.Apply(expr, func(cr *astutil.Cursor) bool {
if _, ok := cr.Parent().(*ast.BinaryExpr); !ok { return true }
if cr.Name() == "Y" {
cr.Replace(&ast.BasicLit{Kind:token.INT, Value:"2"})
}
return true
}, nil)
if err := format.Node(os.Stdout, token.NewFileSet(), n); err != nil {
log.Fatalln("Error:", err)
}
fmt.Println()
41. ノードの削除の例(失敗例)
41
■ 第1引数だけを削除したい
expr, err := parser.ParseExpr(`func(x, y int){}(10, 20)`)
if err != nil { log.Fatal(err) }
n := astutil.Apply(expr, func(cr *astutil.Cursor) bool {
if cr.Name() == "Args" && cr.Index() == 0 {
cr.Delete()
}
return true
}, nil)
if err := format.Node(os.Stdout, token.NewFileSet(), n); err != nil {
log.Fatalln("Error:", err)
}
fmt.Println()
42. ノードの削除の例(修正版)
42
■ 第1引数を一度だけ削除する
expr, err := parser.ParseExpr(`func(x, y int){}(10, 20)`)
if err != nil { log.Fatal(err) }
var once sync.Once
n := astutil.Apply(expr, func(cr *astutil.Cursor) bool {
if cr.Name() == "Args" && cr.Index() == 0 {
once.Do(cr.Delete)
}
return true
}, nil)
if err := format.Node(os.Stdout, token.NewFileSet(), n); err != nil {
log.Fatalln("Error:", err)
}
fmt.Println()
45. リクエストとレスポンスのテスト
■ リクエストのテストからドキュメントを生成
● Request Bodyをリフレクションでテストしている
● 構造体の任意のフィールドを指定できる
validator.RequestBody(t, []httpdoc.TestCase{
{"Name", "tenntenn", "User Name"},
{"Attribute.Email", "tenntenn@example.com", "e-mail address"},
}, &createUserRequest{})
45
参考:go-httpdocのexample