Upgrade to Pro — share decks privately, control downloads, hide ads and more …

fmt

398137161a861ee5412b84b234c65187?s=47 ktr
January 25, 2018

 fmt

398137161a861ee5412b84b234c65187?s=128

ktr

January 25, 2018
Tweet

Transcript

  1. fmt fmt 1

  2. $ whoami $ whoami (id:|@)ktr_0731 サーバサイドエンジニ ア STEINS;GATE / 響け!ユーフォニア

    ム 2
  3. 3

  4. ktr0731/evans ktr0731/evans more expressive gRPC client more expressive gRPC client

    4
  5. fmt? fmt? コードフォーマッタ ソースコードを 強制的 に整形してくれ る 5

  6. gofmt YAPF Prettier scalafmt etc. 6

  7. gofmt を読む gofmt を読む golang/go/src/cmd/gofmt 7

  8. 前提環境 前提環境 golang/go master (157d8cfbc13fbc4c849075e905b0001fb248b5e6) 8

  9. cmd/gofmt/gofmt.go cmd/gofmt/gofmt.go gofmtMain() gofmtMain() for i := 0; i <

    flag.NArg(); i++ { path := flag.Arg(i) switch dir, err := os.Stat(path); { case err != nil: report(err) case dir.IsDir(): walkDir(path) // NOTE; call processFil default: if err := processFile(path, nil, os.St report(err) } } } 9
  10. cmd/gofmt/gofmt.go cmd/gofmt/gofmt.go processFile() processFile() file, sourceAdj, indentAdj, err := parse(fileSet,

    filename, sr if err != nil { return err } 10
  11. cmd/gofmt/internal.go cmd/gofmt/internal.go parse() parse() parser.ParseFile file, err = parser.ParseFile(fset, filename,

    psrc, parserMode) if err == nil { sourceAdj = func(src []byte, indent int) []byte { src = src[indent+len("package p\n"):] return bytes.TrimSpace(src) } 11
  12. go/parser go/parser ParseFile() ParseFile() go/parser: 構文解析用パッケージ Go ソースコードから AST (

    抽象構文木) を生 成 12
  13. cmd/gofmt/gofmt.go cmd/gofmt/gofmt.go processFile() processFile() ast.SortImports(fileSet, file) if *simplifyAST { simplify(file)

    } res, err := format(fileSet, file, sourceAdj, indentAdj, src, p if err != nil { return err } 13
  14. cmd/gofmt/internal.go cmd/gofmt/internal.go format format var buf bytes.Buffer err := cfg.Fprint(&buf,

    fset, file) if err != nil { return nil, err } return buf.Bytes(), nil 14
  15. go/printer/printer.go go/printer/printer.go Fprint() → Fprint() → fprint() fprint() AST ノードを整形・出力するパッケージ

    var p printer p.init(cfg, fset, nodeSizes) if err = p.printNode(node); err != nil { return } 15
  16. go/printer/printer.go go/printer/printer.go printNode() printNode() switch n := node.(type) { case

    ast.Expr: p.expr(n) case ast.Stmt: if _, ok := n.(*ast.LabeledStmt); ok { p.indent = 1 } p.stmt(n, false) case ast.Decl: p.decl(n) case ast.Spec: p.spec(n, 1, false) case []ast.Stmt: for _, s := range n { if _, ok := s.(*ast.LabeledStmt); ok { 16
  17. printNode printNode AST を再帰的に辿り、 各ノードの持つ内容を整 形・出力する 17

  18. 例: 二項演算子のスペース 例: 二項演算子のスペース fmt.Println("Hello, "+"World") fmt.Println("Hello, " + "World")

    18
  19. AST AST の構造 の構造 0 *ast.CallExpr { 1 . Fun:

    *ast.SelectorExpr { 2 . . X: *ast.Ident { 3 . . . NamePos: 1 4 . . . Name: "fmt" 5 . . . Obj: *ast.Object { 6 . . . . Kind: bad 7 . . . . Name: "" 8 . . . } 9 . . } 10 . . Sel: *ast.Ident { 11 . . . NamePos: 5 12 . . . Name: "Println" 13 . . } 14 . } 19
  20. 20

  21. go/printer/printer.go go/printer/printer.go printNode() printNode() switch n := node.(type) { case

    ast.Expr: p.expr(n) func (p *printer) expr(x ast.Expr) { const depth = 1 p.expr1(x, token.LowestPrec, depth) } 21
  22. go/printer/nodes.go go/printer/nodes.go expr1() expr1() switch x := expr.(type) { case

    *ast.BinaryExpr: if depth < 1 { p.internalError("depth < 1:", depth) depth = 1 } p.binaryExpr(x, prec1, cutoff(x, depth), depth) 22
  23. go/printer/nodes.go go/printer/nodes.go binaryExpr() binaryExpr() ( 一部略) ( 一部略) // X

    評価 p.expr1(x.X, prec, depth+diffPrec(x.X, prec)) if printBlank { // 追加 p.print(blank) } // "+" p.print(x.OpPos, x.Op) if printBlank { // 追加 p.print(blank) } // Y 評価 p.expr1(x.Y, prec+1, depth+1) 23
  24. gofmt のフォーマットルール gofmt のフォーマットルール AST 生成までの過程で取り除かれるも の go/printer によって整形されるもの 24

  25. AST AST 生成までの過程で取り除かれるもの 生成までの過程で取り除かれるもの 改行 スペー ス etc. 25

  26. go/printer によって整形されるもの go/printer によって整形されるもの 二項演算子のスペース ( 前 述) 配列の最後の要素のカンマ etc.

    []string{"foo", "bar",} []string{"foo", "bar"} 26
  27. 似たようなのを実装してみる 似たようなのを実装してみる 27

  28. ktr0731/markdownfmt ktr0731/markdownfmt 28

  29. 29

  30. 30

  31. 31

  32. 整形ルール 整形ルール 余計な改行・空白は取り除く テキストの最大長を 80 文字 に 32

  33. 整形ルール 整形ルール テキストの単語間のスペースは1つ に this is a text! this is

    a text! 33
  34. 整形ルール 整形ルール 水平線 (hr) の上下に空 行 horizontal --- line horizontal

    --- line 34
  35. demo demo 35

  36. これはデモ用のテキストです これはデモ用のテキストです this text is an example text for demonstration.

    current version of markdownfmt accepts 80 charactors as the max width. so, maybe this text will be splitted to some lines. 36
  37. まとめ まとめ AST なんでもできてすごい 特に Go は取得できる情報が多い AST を作れれば、フォーマットはそこまで難しく ない

    意外と gofmt は読みやすい 37
  38. 参考 参考 printer - The Go Programming Language parser -

    The Go Programming Language Go のための Go GitHub Flavored Markdown Spec 38