apple/swift-syntax超々入門 UIColor編

これは Swift Advent Calendar 2018 24日目の記事です。
昨日23日目は @uounɹɐʇの「Conditional Conformanceで遊ぼう」でした。

前説

先日福岡で行われた 第5回 HAKATA.swift ~福岡でSwiftの勉強会~ でLTとして話した内容です。

apple/swift-syntax とは

github.com

READMEの冒頭には

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.

とあり、Swiftの

  • 解析
  • 検査
  • 生成
  • 変換

に用いることができ、所謂、メタプログラミングに使用できます。

メタプログラミングについてはこちらがおすすめ

speakerdeck.com

お題

iOSで色を扱う場合にお馴染み、UIKitデフォルトAPIとして提供されるイニシャライザ

UIColor.init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)

ですが、これには以下のような不満点があります。

  • RGB1~255表現を0~1の少数値に変換して表現するため、直感的ではない
  • 特にデザイナーはHexColor (16進トリプレット) で知りたい

ということで、swift-syntaxをを用いて、コメントでHexColorを入れてみようと思います。

想定はこんなコード

Contents.swift

import UIKit

let string = "ABCDE"
let color1 = UIColor(red: 0.55, green: 0.0, blue: 0.0, alpha: 1.0)
let array = [1, 2, 3, 4, 5]
let color2 = UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
let dictionary = ["foo": 1,
                  "bar": 2,
                  "baz": 3]
let color3 = UIColor.init(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)

用意したツールは こちら

github.com

$ swift run swift-color-detector help
dump \(path): Dump code
rewrite \(path): Add HexColor Comments and Save

まずは使ってみる

dump

これをdumpしてみると

$ swift run swift-color-detector dump Contents.swift
import UIKit

let string = "ABCDE"
let color1 = UIColor(red: 0.55, green: 0.0, blue: 0.0, alpha: 1.0) /* #8C0000 */
let array = [1, 2, 3, 4, 5]
let color2 = UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0) /* #00FF00 */
let dictionary = ["foo": 1,
                  "bar": 2,
                  "baz": 3]
let color3 = UIColor.init(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0) /* #0000FF */

と出力されます。

rewrite

$ swift run swift-color-detector rewrite Contents.swift

とすると、与えたpathのコードの書き換えも行います。

swift-syntaxを扱う主要なコード

今回のサンプルでswift-syntaxをメインで扱う部分は、以下ファイルの60行強で実現されています。

swift-color-detector/ColorSyntaxRewriter.swift at master · yutailang0119/swift-color-detector · GitHub

段階としては

  1. ColorSyntaxRewriter
    • イニシャライザに該当するコードを検査
  2. ColorInitializerSyntaxRewriter
    • UIColor.init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) を検査
  3. ColorLiteralSyntaxRewriter
    • Trivial (コメント) を追加

という3ステップで実現しています。

まとめ

いかがでしたでしょうか?
簡単に扱えそうな気がしてきませんか?

そもそも自分もメタプログラミングは挑戦を始めたばかりで、 apple/swift-syntax も手探りなため、最適解とは限りません。
ぜひ、アドバイスをもらえるとありがたいです。