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

fmt

ktr
January 25, 2018

 fmt

ktr

January 25, 2018
Tweet

More Decks by ktr

Other Decks in Technology

Transcript

  1. $ whoami
    $ whoami
    (id:|@)ktr_0731
    サーバサイドエンジニ

    STEINS;GATE /
    響け!ユーフォニア

    2

    View full-size slide

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

    View full-size slide

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

    5

    View full-size slide

  4. gofmt
    YAPF
    Prettier
    scalafmt
    etc.
    6

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  7. 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

    View full-size slide

  8. cmd/gofmt/gofmt.go
    cmd/gofmt/gofmt.go
    processFile()
    processFile()
    file, sourceAdj, indentAdj, err := parse(fileSet, filename, sr
    if err != nil {
    return err
    }
    10

    View full-size slide

  9. 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

    View full-size slide

  10. go/parser
    go/parser
    ParseFile()
    ParseFile()
    go/parser:
    構文解析用パッケージ
    Go
    ソースコードから AST (
    抽象構文木)
    を生

    12

    View full-size slide

  11. 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

    View full-size slide

  12. 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

    View full-size slide

  13. 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

    View full-size slide

  14. 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

    View full-size slide

  15. printNode
    printNode
    AST
    を再帰的に辿り、 各ノードの持つ内容を整
    形・出力する
    17

    View full-size slide

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

    View full-size slide

  17. 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

    View full-size slide

  18. 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

    View full-size slide

  19. 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

    View full-size slide

  20. 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

    View full-size slide

  21. gofmt
    のフォーマットルール
    gofmt
    のフォーマットルール
    AST
    生成までの過程で取り除かれるも

    go/printer
    によって整形されるもの
    24

    View full-size slide

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

    etc.
    25

    View full-size slide

  23. go/printer
    によって整形されるもの
    go/printer
    によって整形されるもの
    二項演算子のスペース (

    述)
    配列の最後の要素のカンマ
    etc.
    []string{"foo", "bar",}
    []string{"foo", "bar"}
    26

    View full-size slide

  24. 似たようなのを実装してみる
    似たようなのを実装してみる
    27

    View full-size slide

  25. ktr0731/markdownfmt
    ktr0731/markdownfmt
    28

    View full-size slide

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

    32

    View full-size slide

  27. 整形ルール
    整形ルール
    テキストの単語間のスペースは1つ

    this is a text!
    this is a text!
    33

    View full-size slide

  28. 整形ルール
    整形ルール
    水平線 (hr)
    の上下に空

    horizontal
    ---
    line
    horizontal
    ---
    line
    34

    View full-size slide

  29. これはデモ用のテキストです
    これはデモ用のテキストです
    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

    View full-size slide

  30. まとめ
    まとめ
    AST
    なんでもできてすごい
    特に Go
    は取得できる情報が多い
    AST
    を作れれば、フォーマットはそこまで難しく
    ない
    意外と gofmt
    は読みやすい
    37

    View full-size slide

  31. 参考
    参考
    printer - The Go Programming
    Language
    parser - The Go Programming Language
    Go
    のための Go
    GitHub Flavored Markdown Spec
    38

    View full-size slide