Swiftに組み込みの正規表現がやってくる…かも？

# はじめに

これまでSwiftで正規表現というと[`NSRegularExpression`](https://developer.apple.com/documentation/foundation/nsregularexpression)を使うという選択肢しかなかったかと思います。(…もしくはサードパーティ製ライブラリ？)

そこへきて、2021年9月末、Swift ForumsのEvolutionカテゴリに["Declarative String Processing Overview"](https://forums.swift.org/t/declarative-string-processing-overview/52459)というスレッドが立ち、一同騒然。
内容としては「2つの新しい“宣言可能な”文字列処理のAPIを導入したい」というもので、一つは`Pattern`、もう一つが`Regex`です。`Regex`はもちろん正規表現のことです。
スレッドのauthorは[Michael Ilseman氏](https://forums.swift.org/u/michael_ilseman)。有名なところで言えば、Swift 5で文字列の内部保持データをUTF-16からUTF-8に刷新した立役者です[^string-utf-8]。

[^string-utf-8]: 「[[Swift] string.data(using: .utf8)ってnilになるの？](https://qiita.com/YOCKOW/items/530b95cfbcb22eac22f2)」も読んでね。

そんな、Swiftの`String`ひいてはUnicode全般に精通している(と思われる)Ilseman氏の提案する文字列処理のAPIとは…？
この記事では正規表現(`Regex`)にスポットを当ててみましょう。


## 注意点

- これらのAPIはまだ「議論しましょう」という段階で何ら正式な決定はされていません。
- 私の個人的な意見としては、「“Swiftらしからぬ正規表現”を導入することには反対」という立場です(理由は当該スレッドの私のコメントを参照してください)。



# Swiftで宣言できるようになる正規表現の案

では、正規表現の案を当該スレッドから引用してみましょう：

```Swift
let regex = /([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s*;\s(\w+).*/
print(type(of: regex))
// Prints Regex<(Substring, Substring?, Substring)>
```

PerlやRubyなどのスクリプト言語でよくみる形ですね。
でも、Swiftではいくつか問題になるであろう点が指摘されています。
そのうちのいくつかを紹介しましょう。


## どうやってパースするの？

正規表現リテラルについて議論するための別スレッド["[Pitch] Regular Expression Literals"](https://forums.swift.org/t/pitch-regular-expression-literals/52820)でも議論されていますが、`/`はもともとSwiftでよく使う記号です。従って、正規表現を書いたつもりが別の文や式として解釈される可能性があります。

たとえば空の正規表現`//`は、コメントの開始記号として解釈されることになってしまうでしょう。
さらにSwiftでは独自にoperatorを定義できるので、`/hoge/`は`((/hoge)/)`(prefixとしての`/`が付いた`hoge`という識別子にsuffixとしての`/`がさらに付いている)とパースされる可能性があります。

というわけで、そこらへんをどうするの？という議論がなされています。
`/`を特別に扱うLexerをどう実装するかとか、そもそも`/hoge/`ではなく`#/hoge/#`や`#regex(hoge)`はどうだ、とか。


## そもそも正規表現はSwiftに馴染むの？

正規表現の導入自体に反対する意見が少数なのは残念な限りですが、実際に反対意見は存在はします[^opposition]。私も前述のとおりどちらかというと反対側の立場です。

[^opposition]: https://forums.swift.org/t/pitch-regular-expression-literals/52820/3 とか https://forums.swift.org/t/pitch-regular-expression-literals/52820/63 とか

そもそも正規表現の起源は1950年代で、UNIXで広まったのも1960年代と、かなり古びた技術です[^regex-wikipedia]。

[^regex-wikipedia]: [Wikipedia](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE)で得た浅い知識

当初は、多言語対応などという概念すらおそらくなかったでしょう。ちなみに、PerlがUnicodeに対応したのは5.6(本格的には5.8)とつい最近[^perl-unicode]です。

[^perl-unicode]: 2000年〜2002年を「つい最近」と言っちゃうところがオジサン。

Unicodeに対応した正規表現といっても、基本的には、書記素クラスタ(grapheme cluster)単位ではなく文字(character)単位というところに注意が必要です。

と、ここで混乱しないように用語の整理をしておきましょう。
Unicodeにおけるcharacterは、Swiftにおける`Unicode.Scalar`のことです。文字だけでなく“文字の部品”も含む概念です。
Swiftにおける`Character`は、Unicodeでいうところのgrapheme cluster(書記素クラスタ)で、1個以上の`Unicode.Scalar`からなる「人間から見た一文字」を表します。
即ち、UnicodeのcharacterとSwiftの`Character`は全くの別物というところは注意が必要です。
詳しくは、『[[Swift] CharacterSetはCharacterのsetではありませんよ？](https://qiita.com/YOCKOW/items/13360d7245c491932673)』も読んでみてください。

ところで、Swiftの`String`は`BidirectionalCollection`に準拠していますが、`Element`は`Character`であって、`Unicode.Scalar`ではありません。
正規表現をSwiftで使おうと思った時に、そこが問題となってくるはずです。


### Swiftの正規表現は「何」にマッチするの？

当該スレッドの[私のコメント](https://forums.swift.org/t/declarative-string-processing-overview/52459/117)を元に考えてみましょう。

`Character("A\u{20DD}")`は、2個の`Unicode.Scalar`(`LATIN CAPITAL LETTER A` + `COMBINING ENCLOSING CIRCLE`)からなる1個の`Character`です。

そして

```Swift
print((Character("A")...Character("B")).contains("A\u{20DD}"))
// -> true
```

というように、Swiftでは`"A\u{20DD}"`は`"A"`以上`"B"`以下の値と判定されます。

ここで`/([A-B])/`という正規表現を使って、`"ZZA\u{20DD}ZZ"`という文字列からマッチする「部分」を取り出すことを考えましょう。
Ilseman氏の案では次のような構文になります：

```Swift
let regex = /([A-B])/

switch "ZZA\u{20DD}ZZ" {
case let something <- regex: // 正規表現のマッチする部分を`something`に代入する構文
  // `something`は何になる?
}
```

もし、`Character`の`Collection`としての`String`にマッチさせたという解釈なら、`something`は`Character("A\u{20DD}")`になるはずです。
しかし、往年のPerlプログラマ(?)にはその結果は違和感を抱かせることになるでしょう。Perlの正規表現はUnicodeのcharacter、即ちSwiftの`Unicode.Scalar`単位でマッチすることになるので、`something `は`Unicode.Scalar("A")`であることを期待してしまうでしょう。

それでも「Swift的には`Character`にマッチするんだよ！」と言ってくれれば、それはそれで「そういうものか…」とも思えるのですが、Ilseman氏は「`Character`にマッチする方法も`Unicode.Scalar`にマッチする方法も選べるようにする」という旨の発言をしています。
どういうデザインになるか不明ですが、他の言語から入ってきたSwift初心者が混乱しそうな匂いがプンプンします…。



# おわりに: 「僕たちの議論はまだ始まったばかりだ！」

最初にも述べたとおり、`Pattern`も`Regex`もまだ議論は始まったばかり。議論次第で、どうにでも転ぶ可能性があります。
少なくとも、東アジア圏の文字を無視してUnicodeを始めようとした失敗[^unicode-failure]と同様のことが、Swiftの`Regex`で起きないようにしていけるといいなと思います。

[^unicode-failure]: 彼奴等は16 bitで全世界の文字が収まると思ってたんだからびっくりだね。

もしくは、今から議論に参加しておけば、自分好みの正規表現をSwiftで手に入れられる可能性も？！

Ilseman氏によればこれから関連するスレッドを細かく立てていく方針のようなので、自分の気になるスレッドだけでもチェックしておくといいかもしれません。

これからも素敵なSwift.Stringライフを送れますように！


# ●関連スレッド

## 概要

https://forums.swift.org/t/declarative-string-processing-overview/52459


## 正規表現リテラルについて

https://forums.swift.org/t/pitch-regular-expression-literals/52820

https://forums.swift.org/t/pitch-2-regex-literals/56736

https://forums.swift.org/t/se-0354-regex-literals/57037

https://forums.swift.org/t/se-0354-second-review-regex-literals/57367


## 「文字クラス」について

https://forums.swift.org/t/pitch-character-classes-for-string-processing/52920


## 強く型付けされた正規表現のキャプチャについて

https://forums.swift.org/t/pitch-strongly-typed-regex-captures/53391


## 正規表現の文法について

https://forums.swift.org/t/pitch-regex-syntax/55711

https://forums.swift.org/t/pitch-2-regex-syntax-and-run-time-construction/56624

https://forums.swift.org/t/se-0355-regex-syntax-and-runtime-construction/57038


## 正規表現による文字列処理のアルゴリズム

https://forums.swift.org/t/pitch-regex-powered-string-processing-algorithms/55969

https://forums.swift.org/t/se-0357-regex-string-processing-algorithms/57225


##  正規表現ビルダドメイン固有言語

https://forums.swift.org/t/pitch-regex-builder-dsl/56007

https://forums.swift.org/t/se-0351-regex-builder-dsl/56531

https://forums.swift.org/t/returned-for-revision-se-0351-regex-builder-dsl/57224

https://forums.swift.org/t/se-0351-second-review-regex-builder-dsl/58721


## 正規表現型と概要

https://forums.swift.org/t/pitch-regex-type-and-overview/56029

https://forums.swift.org/t/se-0350-regex-type-and-overview/56530

https://forums.swift.org/t/se-0352-second-review-regex-type-and-overview/56886



## 文字列処理のためのユニコード

https://forums.swift.org/t/pitch-unicode-for-string-processing/56907

https://forums.swift.org/t/se-0363-unicode-for-string-processing/58520


# ●関連レポジトリ

## 

https://github.com/apple/swift-experimental-string-processing