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

SwiftSyntax で便利を実現する基礎 / How to Use SwiftSyntax for Better Productivity (Japanese version)

SwiftSyntax で便利を実現する基礎 / How to Use SwiftSyntax for Better Productivity (Japanese version)

SwiftSyntax を使うと、Swift のコード生成や書き換え、静的検査などを実現できます。しかし、SwiftSyntax の使い方はほとんど知られていません。この方法をダイジェストで紹介します。

try! Swift Tokyo 2019
https://www.tryswift.co/events/2019/tokyo/jp/

Kuniwak

March 22, 2019
Tweet

More Decks by Kuniwak

Other Decks in Programming

Transcript

  1. 4XJGU4ZOUBYͰ

    ศརΛ࣮ݱ͢Δجૅ
    Kuniwak - DeNA Co.,Ltd.
    2019.03.22 try! Swift Tokyo 2019

    View full-size slide

  2. 2
    Some code will be presented in the slides,

    you can see the slides and the code here:
    ൃදதʹίʔυ͕ग़͖ͯ·͕͢ɺಡΈͮΒ͍৔߹͸

    ҎԼ͔ΒεϥΠυΛ͝ཡ͍ͩ͘͞ɿ
    speakerdeck.com/orgachem

    View full-size slide

  3. ,VOJXBL
    w ॴଐˠ
    • github.com/Kuniwak
    • qiita.com/Kuniwak
    w ߏจղੳ΍୯ମςετ͕޷෺
    w "QQ$PEF࢖͍
    4

    View full-size slide

  4. ෼ؒͰ఻͍͑ͨ͜ͱ

    View full-size slide

  5. 4XJGU4ZOUBYΛ࢖͏ͱɺ4XJGUͷ

    ੩తݕࠪ΍ίʔυੜ੒Λ؆୯ʹͰ͖Δʂ
    6

    View full-size slide

  6. 4XJGU4ZOUBYͱ͸

    View full-size slide

  7. 4XJGU4ZOUBY͸4XJGUͷެࣜϥΠϒϥϦ
    github.com/apple/swift-syntax
    8

    View full-size slide

  8. 4XJGU4ZOUBYͰ

    Ͱ͖Δ͜ͱ

    View full-size slide

  9. ಡΈॻ͖͠΍͍͢σʔλ
    ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ
    ಡΈॻ͖͠΍͍͢σʔλΛɺίʔυʹ͢Δ


    4XJGUͷίʔυ
    ಡΈॻ͖͠΍͍͢σʔλ 4XJGUͷίʔυ
    10

    View full-size slide

  10. ಡΈॻ͖͠΍͍͢σʔλ
    ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ
    ಡΈॻ͖͠΍͍͢σʔλΛɺίʔυʹ͢Δ


    4XJGUͷίʔυ
    ಡΈॻ͖͠΍͍͢σʔλ 4XJGUͷίʔυ
    11

    View full-size slide

  11. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    12
    આ໌ʹ࢖͏αϯϓϧίʔυ

    View full-size slide

  12. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    JGจ
    13
    ͜Ε͸ʮJGจʯͱݺ͹ΕΔ

    View full-size slide

  13. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    ৚݅
    14
    JGจ͸ʮ৚݅ʯͱʮෳจʯͰ

    ߏ੒͞Ε͍ͯΔ

    View full-size slide

  14. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ৚݅
    15
    ࠓճͷ৚݅෦෼ʹ͸ɺ

    ʮ0QUJPOଋറ৚݅ʯͱ

    ݺ͹Ε͍ͯΔ΋ͷ͕ೖΔ

    View full-size slide

  15. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ৚݅
    ม਺ࣜ
    ม਺ύλʔϯ
    if let self = self {}
    16
    0QUJPOଋറ৚݅͸ɺ୅ೖઌͷ

    ʮม਺ύλʔϯʯͱ୅ೖ͢Δ஋ͷ

    ʮม਺ࣜʯͰߏ੒͞Ε͍ͯΔ

    View full-size slide

  16. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    if let self = self {}
    if let self = self {}
    17
    ͜ͷΑ͏ʹɺίʔυΛ෼ղͯ͠

    ߏ੒ཁૉΛḷ͍ͬͯ͘ͱɺ

    ίʔυΛʮ໦ߏ଄ʯͱͯ͠

    ѻ͍ͬͯΔ͜ͱʹؾͮ͘

    View full-size slide

  17. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    if let self = self {}
    if let self = self {}
    18
    ෳจ
    JGจ
    ෳจ
    ม਺ύλʔϯ ม਺ࣜ
    0QUJPOଋറ৚݅

    View full-size slide

  18. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ

    if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ
    ม਺ࣜ
    ม਺ύλʔϯ
    if let self = self {}
    JGจ
    ෳจ
    ม਺ύλʔϯ ม਺ࣜ
    ͜ΕΛʮߏจ໦ʯͱΑͿ
    19
    0QUJPOଋറ৚݅

    View full-size slide

  19. 4XJGU4ZOUBYͰ

    ʮߏจ໦ʯΛಡΉ

    View full-size slide

  20. 4XJGU4ZUOBYͰߏจ໦ΛಡΉ

    if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ
    ม਺ࣜ
    ม਺ύλʔϯ
    if let self = self {}
    ม਺໊ΛಡΜͰΈΑ͏
    21

    View full-size slide

  21. import SwiftSyntax
    let url = URL(fileURLWithPath: "example.swift")
    let source = try String(contentsOf: url)
    let syntax = try SyntaxParser.parse(source: source)
    4XJGU4ZOUBYͰ
    ಡΈॻ͖͠΍͍͢σʔλʢߏจ໦ʣʹ͢Δ
    4XJGUͷίʔυΛ
    22

    View full-size slide

  22. if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ
    ม਺ࣜ
    ม਺ύλʔϯ
    if let self = self {}
    ifStmt
    4XJGU4ZUOBYͰߏจ໦ΛಡΉ

    JGจ·ͰͷऔΓํ͸

    গ͠໘౗ͳͷͰޙड़
    23

    View full-size slide

  23. if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ
    ม਺ࣜ
    ม਺ύλʔϯ
    if let self = self {}
    ifStmt
    .conditions[0]
    .condition

    4XJGU4ZUOBYͰߏจ໦ΛಡΉ

    24

    View full-size slide

  24. if let self = self {}
    if let self = self {}
    JGจ
    ෳจ
    0QUJPOଋറ
    ม਺ࣜ
    ม਺ύλʔϯ
    if let self = self {}
    "self"
    ifStmt
    .conditions[0]
    .condition
    .pattern

    .identifier
    .text
    4XJGU4ZUOBYͰߏจ໦ΛಡΉ

    25

    View full-size slide

  25. let name = ifStmt
    .conditions[0]
    .condition
    .pattern

    .identifier
    .text
    if name == "this" {
    print("Oops!")
    }
    ྫ͑͹ɺif let self = ͷม਺໊͕

    ౷Ұ͞Ε͍ͯͳ͍৔߹ʹܯࠂͰ͖Δ
    ߏจ໦ΛಡΊΕ͹ɺΦϨΦϨ4XJGU-JOU͕ॻ͚Δ

    26

    View full-size slide

  26. import SwiftSyntax
    let url = URL(fileURLWithPath: "example.swift")
    let source = try String(contentsOf: url)
    let syntax = try SyntaxParser.parse(source: source)

    let stmts = Array(syntax.statements)

    guard let ifStmt = stmts.first?.item as? IfStmtSyntax else {
    print("If statement not found at the position.")
    return
    }
    JGจ͸গ͠ख͕ؒͩ͜ͷΑ͏ʹऔΕΔɻ࣮ࡍͷߏจ໦ΛEVNQ͢ΔͱΘ͔Δ
    JGจ·ͰͷऔΓํ
    27


    Ͱ
    ͸


    View full-size slide

  27. ಡΈॻ͖͠΍͍͢ߏจ໦
    ίʔυΛɺಡΈॻ͖͠΍͍͢ߏจ໦ʹ͢Δ
    ಡΈॻ͖͠΍͍͢ߏจ໦Λɺίʔυʹ͢Δ


    4XJGUͷίʔυ
    ಡΈॻ͖͠΍͍͢σʔλ 4XJGUͷίʔυ
    DONE
    28

    View full-size slide

  28. ίʔυΛɺಡΈॻ͖͠΍͍͢σʔλʹ͢Δ
    ಡΈॻ͖͠΍͍͢ߏจ໦Λɺίʔυʹ͢Δ


    4XJGUͷίʔυ
    4XJGUͷίʔυ
    ಡΈॻ͖͠΍͍͢ߏจ໦
    ಡΈॻ͖͠΍͍͢ߏจ໦
    DONE
    29

    View full-size slide

  29. if let this = self {}
    4XJGU4ZUOBYͰमਖ਼ͨ͠ίʔυΛੜ੒͢Δ

    if let self = self {}
    30

    View full-size slide

  30. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    31

    View full-size slide

  31. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    4XJGU4ZOUBYΛ࢖͏
    32

    View full-size slide

  32. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    4XJGU4ZOUBYͰಡΈॻ͖͠΍͍͢ߏจ໦ʹ͢Δ
    33

    View full-size slide

  33. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    ߏจ໦Λॻ͖׵͑ͯɺ݁ՌΛදࣔ͢Δ
    34

    View full-size slide

  34. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    ߏจ໦Λॻ͖׵͑ΔͨΊͷΫϥε
    35

    View full-size slide

  35. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    ॻ͖׵͍͑ͨߏจ໦ͷWJTJUؔ਺ΛPWFSSJEF͢Δ
    ࠓճ͸JGMFU9ͷ9෦෼͕ର৅

    ʢܕͷ໊લ͸EVNQͰௐ΂ΔͱΘ͔Δʣ
    36

    View full-size slide

  36. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    JGMFU9ͷ9෦෼͕UIJTͳΒॻ͖׵͑Δ
    37

    View full-size slide

  37. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    9෦෼ΛTFMGʹॻ͖׵͑Δ
    38

    View full-size slide

  38. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    39

    View full-size slide

  39. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax)) "if let self= self {}"
    40
    ίʔυΛੜ੒Ͱ͖ͨʂ

    View full-size slide

  40. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax)) "if let self= self {}"
    41
    Α͘ݟΔͱۭന͕

    ফ͑ͯ͠·͍ͬͯΔ

    View full-size slide

  41. import SwiftSyntax
    class RenameRewriter: SyntaxRewriter {
    override func visit(_ node: IdentifierPatternSyntax) -> PatternSyntax {
    if node.identifier.text == "this" {
    var modifiedNode = node
    let originalIdentifier = node.identifier
    modifiedNode.identifier = SyntaxFactory
    .makeIdentifier("self")
    .withTrailingTrivia(originalIdentifier.trailingTrivia)
    return modifiedNode
    }
    return node
    }
    }
    let syntax = try SyntaxParser.parse(source: String(contentsOf: url))
    print(RenameRewriter().visit(syntax))
    TFMGޙͷۭന͕ͳ͘ͳΔ໰୊͸5SJWJBͰௐ੔Ͱ͖Δ


    Ͱ
    ͸


    42

    View full-size slide

  42. if let this = self {}
    4XJGU4ZUOBYͰमਖ਼ͨ͠ίʔυΛੜ੒͢Δ

    if let self = self {}
    43
    DONE

    View full-size slide

  43. ίʔυΛɺಡΈॻ͖͠΍͍͢ߏจ໦ʹ͢Δ
    ಡΈॻ͖͠΍͍͢ߏจ໦Λɺίʔυʹ͢Δ


    4XJGUͷίʔυ
    4XJGUͷίʔυ
    ಡΈॻ͖͠΍͍͢ߏจ໦
    ಡΈॻ͖͠΍͍͢ߏจ໦
    DONE
    44
    DONE

    View full-size slide

  44. 4XJGU4ZOUBYΛ࢖͏ͱɺ

    4XJGUͷߏจ໦ͷݕࠪ΍

    मਖ਼ͨ͠ίʔυΛੜ੒Ͱ͖ͨʂ
    ·ͱΊ
    45

    View full-size slide