tomlの複数行文字列内でのクオートの取り扱いに関するエッジケース

何が起きたのか

TOML規格レポジトリにこの記事のタイトルの通りのアップデートがあった。

github.com

基本的に、toml11ではTOMLのmasterブランチに入った変更は TOML11_USE_UNRELEASED_FEATURESdefine しないと有効にならないようにしている。だがこの手の"Clarification"の類は、もしtoml11がそれに則らない振る舞いをする場合、まだTOML側でリリースされていなくてもtoml11側の修正案件としている。これは「以前からこの振る舞いを想定していたが、これまで陽に書かれていなかっただけ」という解釈による。実際のところは最初から全てのエッジケースを想定していたと言うことはないだろうが。

このアップデートでは、以下のようなケースでの振る舞いが明確化された。

str = """"This," she said, "is just a pointless statement.""""
# 文字列の内容は"This," she said, "is just a pointless statement."
# になる(最初と最後の`"`は文字列の内容に含まれる)

何が問題だったのか

TOMLでは"""'''で始めた文字列はmultiline stringになる。そしてその場合は、"が文字列中に(エスケープなしで)現れることが許される。 ではこの場合、"""の直前や直後に"が来たらどうなるだろうか? 外側の"""をとるか内側の"""を取るかで、そのような文字列が文法的に正しいかが決まる。 以下に示すのは、文字列の最初と最後に"が来て欲しい比較的ありそうなケースだ。よく見ると、最初の"""が3つではなく4つになっている。

str = """"This," she said, "is just a pointless statement.""""

この場合のこの文字列の内容は "This," she said, "is just a pointless statement." が正しく、もっとも外側の """ デリミタが正しいデリミタの位置である。 このことが今までのspecだと曖昧だった。

toml11側の対応

これに今日気づいて、閉じる方の"""は最短マッチしてしまうと言うtoml11側の"不具合"を直した。"不具合"を"で括っているのは、v0.5.0時点の規格を厳密に参照するならここの動作は"処理系定義"に近いと感じているからだ。実際ClarificationはまだTOML仕様としてリリースされていないので。

あと、これに関する挙動を調べている時に、文字列のシリアライズ時にmultiline stringになるかどうかの場合分けとその場合のフォーマットがあまり美しくないなと感じたので、そこもあとで直す。

例えば、'で括られたliteral stringは一切のエスケープシーケンスを持たないので、literal stringの中で'を使おうとすると'''を使ってmultiline literal stringにするしかない。そこは正しく処理できるのだが、シリアライザで「multilineにするということは長いのだろう」という仮定がされており、開始即改行されてしまう。

str = '''
'That's still pointless', she said.'''

これはちょっと微妙だ。下のようになっていた方がカッコいい。

str = ''''That's still pointless', she said.'''

いやもしかしたら読みづらいかもな……?

あと、"で括られたbasic stringの場合は、"エスケープシーケンスを使うか全体を"""で括ってmultiline basic stringにするという2通りの選択肢がある。ここで常にエスケープシーケンスを使っていたが、バックスラッシュが多用されているとちょっと読みづらいので、少し考えるべきだろう。