💨

Swift Parsing を使うときにKuuがつまづいたところ

に公開

Swift Parsing を使うときにKuuがつまづいたところ

Swift-Parsing (と XCTest) で「トークナイザ」を作ろうとした時につまづいたり、これははまると思ったなってトコをまとめます。


つまづいたこと & 実際の対策

○1. Ambiguous use of 'init(_:) といわれる

.map(String.init) // ← これだと不明義

原因

  • String.initSubstring, Character など複数の型に対応していて、どの型を使いたいのか Swift が判断できずエラーになる。
  • 特に Prefix(...) のような文字列系パーサと組み合わせた時に起きやすい。

対策

  • クロージャで型を明示することで、意図をコンパイラに伝える。
.map { (sub: Substring) -> String in String(sub) }
  • .map(String.init) が使えるのは、型が完全に明確な時だけ。

○2. Cannot convert value of type '(A, B) -> Token' to expected argument type 'Token'

Parse(Token.variable) { ... } // ← よくやりたくなるやつ

原因

  • Parse(Token.variable)(A, B) -> Token のような unlabeled な関数しか受け取れない。
  • Token.variable(type: DataType, name: String) のようにラベル付きだと、それが合わずにエラーになる。

対策

  • map を使って明示的に組み立てるのが安全。
Parse {
  dataTypeParser
  identifier
}.map { type, name in
  Token.variable(type: type, name: name)
}
  • または case variable(DataType, String) のようにラベルなしにして、Parse(Token.variable) を使えるようにする。

○3. Attempt が見つからない

原因

  • swift-parsing のバージョンによって、バックトラック用のパーサの名前が違う
  • 0.9.x: BacktrackingParser
  • 0.10〜0.11: Attempt
  • 0.12以降: Backtrack

対策

  • バージョンに合わせて正しいものを使う。
Backtrack { ... } // for 0.12+
  • Attempt {} がないと言われたら Backtrack {} に切り替える。
  • SwiftPM の指定を最新版 (from: "0.12.0") にすることで解消できることも多い。

○4. Inheritance from non-protocol type 'Parser'

原因

  • ParserParsing モジュールの中にあるプロトコル。
  • Foundation や標準ライブラリには存在しない。

対策

import Parsing

struct MyParser: Parsing.Parser { ... }
  • Parsing.Parser のようにモジュール名つきで明示してあげるとエラーが消える。

○5. 変数は static let にしているのに Cannot use instance member えられる

原因

  • static let の初期化時点では instance が存在しないため、self.identifier のようなインスタンスプロパティを参照できない。

対策

  • 全部 static に統一する。
  • もしくは identifier をファイルスコープ定数にして、どこからでも参照できるようにする。
  • インスタンスメンバが必要なら var parser: ... { ... } のように計算プロパティとして定義する。

体系化すると...

  • 「パーサの構成は全部 static let にして、.erasetoAnyParser() する」
  • 「OneOf の順序は気を付ける (特定なものは先に)」
  • 「Parse().map の形は .map { x, y in Token(x, y) } にする」
  • 「Backtrack が必要な場所は明示的に書く」
  • 「型推論に期待せず、必要なら型注釈で補助する」

結論。

SwiftParsingは真面目に使えばむしろ保守性ある文法で、実践的にも使える。けどコンパイラの次元解析に依存してる部分が大きいので、一歩づつコンパイラが何を言ってるかに対応できる感覚は必要です。

ある種の「Swiftの型システム」と「ResultBuilderの癖」を両方読める力が問われる感あります。

Discussion