$30 off During Our Annual Pro Sale. View Details »

First getting started of SwiftSyntax

First getting started of SwiftSyntax

Yutaro Muta

April 19, 2019
Tweet

More Decks by Yutaro Muta

Other Decks in Programming

Transcript

  1. SwiftSyntax௒ೖ໳
    2019/04/19 @Mobile Act KYOTO #1
    Yutaro Muta @yutailang0119

    View Slide

  2. • Yutaro Muta @yutailang0119
    • Hatena Co., Ltd. @Kyoto
    • Conference Staff
    • builderscon 2017, 2018
    • try! Swift Tokyo 2019, 2020?
    • and more
    Who am I ?

    View Slide

  3. Goal
    • SwiftSyntaxΛ࢖͏͖͔͚ͬʹͳΔ

    View Slide

  4. apple/swift-syntax

    View Slide

  5. apple/swift-syntax
    • https://github.com/apple/swift-syntax
    > SwiftSyntax is a set of Swift bindings for the libSyntax library. It allows for
    Swift tools to parse, inspect, generate, and transform Swift source code.

    View Slide

  6. libSyntax vs SwiftSyntax
    • libSyntax
    • SwiftίϯύΠϥͰ࠾༻
    • API͸Swift ͱ C++Λࠞࡏͯ͠ఏڙ
    • SwiftSyntax
    • libSyntaxͷSwiftϥούʔ
    • SwiftPackageManagerͰ࢖༻Ͱ͖Δ
    • ։ൃஈ֊

    View Slide

  7. Some Example Users
    • Swift AST Explorer: a Swift AST visualizer.
    • Swift Stress Tester: a test driver for sourcekitd and Swift evolution.
    • SwiftRewriter: a Swift code formatter.
    • SwiftPack: a tool for automatically embedding Swift library source.
    • Periphery: a tool to detect unused code.
    • BartyCrouch: a tool to incrementally update strings files to help App localization.
    • Muter: Automated mutation testing for Swift
    • Swift Variable Injector: a tool to replace string literals with environment variables values.

    View Slide

  8. Some Example Users
    • Swift AST Explorer: a Swift AST visualizer.
    • Swift Stress Tester: a test driver for sourcekitd and Swift evolution.
    • SwiftRewriter: a Swift code formatter.
    • SwiftPack: a tool for automatically embedding Swift library source.
    • Periphery: a tool to detect unused code.
    • BartyCrouch: a tool to incrementally update strings files to help App localization.
    • Muter: Automated mutation testing for Swift
    • Swift Variable Injector: a tool to replace string literals with environment variables values.
    SwiftͷASTΛѻ͏্Ͱ͸ඞਢ!!!

    View Slide

  9. ͓୊

    View Slide

  10. ͓୊: UIColor
    • UIColor.init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
    • ௚ײతʹͲΜͳ৭ͳͷ͔͕Θ͔ΓͮΒ͍
    • HexColor (16ਐτϦϓϨοτ) ͰऔΓѻ͍͍ͨ
    => HexColorͰॳظԽ͢Δϝιουʹॻ͖׵͑Δ

    View Slide

  11. UIColor.init(hex string: String, alpha: CGFloat)
    https://gist.github.com/yutailang0119/bef7cbe591ba6f76318219ba8bb69946
    extension UIColor {
    convenience init(hex string: String, alpha: CGFloat) {
    let hex = string.replacingOccurrences(of: "#", with: "")
    let scanner = Scanner(string: hex)
    var color: UInt32 = 0
    if scanner.scanHexInt32(&color) {
    let r = CGFloat((color & 0xFF0000) >> 16) / 255.0
    let g = CGFloat((color & 0x00FF00) >> 8) / 255.0
    let b = CGFloat(color & 0x0000FF) / 255.0
    self.init(red: r, green: g, blue: b, alpha: alpha)
    } else {
    self.init(white: 1.0, alpha: 1)
    }
    }
    }

    View Slide

  12. yutailang0119/swift-color-detector

    View Slide

  13. Demo

    View Slide

  14. 0. SwiftSyntax΁ͷґଘ

    View Slide

  15. Package.swift
    // swift-tools-version:5.0
    // The swift-tools-version declares the minimum version of Swift required to build this package.
    import PackageDescription
    let package = Package(
    name: "swift-color-detector",
    products: [
    .executable(name:"swift-color-detector", targets: ["swift-color-detector"]),
    .library(name: "SwiftColorDetectKit", targets: ["SwiftColorDetectKit"]),
    ],
    dependencies: [
    .package(url: "https://github.com/apple/swift-syntax.git", from: Version(0, 50000, 0)),
    ...
    ],
    targets: [
    .target(name: "swift-color-detector", dependencies: ["SwiftColorDetectKit", ...]),
    .target(name: "SwiftColorDetectKit", dependencies: ["SwiftSyntax", ...]),
    .testTarget(name: "SwiftColorDetectKitTests", dependencies: ["SwiftColorDetectKit"]),
    ]
    )

    View Slide

  16. 1. UIColor.init(red: _,
    green: _,
    blue: _,
    alpha: _)
    ͷಛఆ

    View Slide

  17. 1.1 UIColor.initͷಛఆ
    func isUIColorInitializer(of node: FunctionCallExprSyntax) -> Bool {
    return
    (node.calledExpression.description == "UIColor"&& node.leftParen != nil)
    ||
    node.calledExpression.description == "UIColor.init"
    }

    View Slide

  18. 1.1 UIColor.initͷಛఆ
    func isUIColorInitializer(of node: FunctionCallExprSyntax) -> Bool {
    return
    (node.calledExpression.description == "UIColor"&& node.leftParen != nil)
    ||
    node.calledExpression.description == "UIColor.init"
    }
    ਫ਼౓Λ্͛Δ΂͘ɺݕূத

    View Slide

  19. 1.2 Label red, green, blue, alphaͷಛఆ
    func detectRGBColor(from node: FunctionCallExprSyntax) -> RGBColor? {
    guard isUIColorInitializer(of: node) else {
    return nil
    }
    var red, green, blue, alpha: CGFloat?
    node.argumentList.forEach { argumentSyntax in
    guard let label = argumentSyntax.label else {
    return
    }
    switch label.text {
    case "red":
    red = CGFloat(expression: argumentSyntax.expression)
    case "green":
    green = CGFloat(expression: argumentSyntax.expression)
    case "blue":
    blue = CGFloat(expression: argumentSyntax.expression)
    case "alpha":
    alpha = CGFloat(expression: argumentSyntax.expression)
    default:
    break
    }
    }
    guard let rgbColor = RGBColor(red: red, green: green, blue: blue, alpha: alpha) else {
    return nil
    }
    return rgbColor
    }
    }

    View Slide

  20. 2. UIColor.init(hex string: String,
    alpha: CGFloat)
    ΁ͷॻ͖׵͑

    View Slide

  21. 2.1Ҿ਺ϦετΛ૊ΈཱͯΔ
    import SwiftSyntax
    var hexInitializerArgumentListSyntax: FunctionCallArgumentListSyntax {
    let space = Trivia.init(arrayLiteral: TriviaPiece.spaces(1))
    let colon = SyntaxFactory.makeIdentifier(":").withTrailingTrivia(space)
    let hexArgument = FunctionCallArgumentSyntax { builder in
    let label = SyntaxFactory.makeIdentifier("hex")
    let value = SyntaxFactory.makeStringLiteral("\"\(hex)\"")
    let expression = SyntaxFactory.makeStringLiteralExpr(stringLiteral: value)
    let trailingComma = SyntaxFactory.makeIdentifier(",").withTrailingTrivia(space)
    builder.useLabel(label)
    builder.useColon(colon)
    builder.useExpression(expression)
    builder.useTrailingComma(trailingComma)
    }
    let alphaArgument = FunctionCallArgumentSyntax { builder in
    let label = SyntaxFactory.makeIdentifier("alpha")
    let value = SyntaxFactory.makeFloatingLiteral("\(alpha)")
    let expression = SyntaxFactory.makeFloatLiteralExpr(floatingDigits: value)
    builder.useLabel(label)
    builder.useColon(colon)
    builder.useExpression(expression)
    }
    let argumentList = SyntaxFactory.makeFunctionCallArgumentList([hexArgument, alphaArgument])
    return argumentList // hex: \"#8C0000\", alpha: 1.0
    }

    View Slide

  22. 2.2 UIColor.initΛॻ͖׵͑Δ
    import SwiftSyntax
    class RGBColorSyntaxRewriter: SyntaxRewriter {
    override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax {
    guard let rgbColor = detectRGBColor(from: node) else {
    return node
    }
    let colorInitializerSyntax =
    node.withArgumentList(rgbColor.hexInitializerArgumentListSyntax)
    // UIColor(hex: "#8C0000", alpha: 1.0)
    return colorInitializerSyntax
    }
    }

    View Slide

  23. 3. ࣮ߦ

    View Slide

  24. ࣮ߦ
    import SwiftSyntax
    public func rewrite() throws {
    let sourceFile = try SyntaxTreeParser.parse(path)
    let rewriter = RGBColorSyntaxRewriter(filePath: path)
    let syntax = rewriter.visit(sourceFile)
    // ϑΝΠϧͷॻ͖׵͑
    try syntax.description.write(to: path, atomically: true, encoding: .utf8)
    }

    View Slide

  25. ޙ͸ҰͭҰͭͷϑΝΠϧʹ
    ࣮ߦ͍͚ͯͩ͘͠

    View Slide

  26. One more thing...

    View Slide

  27. yutailang0119/FileScanKit

    View Slide

  28. yutailang0119/FileScanKit
    • ࢦఆύεҎԼͷϑΝΠϧύεΛ໢ཏతʹऔಘ͢ΔͨΊͷSwift޲͚Library
    • Support
    • Recursion
    • all
    • depth limit
    • File extension
    • Ignore paths

    View Slide

  29. &OKPZ4XJGU4ZOUBYMJGF 5IBOLT
    w NVUBZVUBSP!HNBJMDPN
    w IUUQTUXJUUFSDPNZVUBJMBOH
    w IUUQTHJUIVCDPNZVUBJMBOH

    View Slide