ヤルキデナイズド

Unclassified Articles on Software and IT

“[0,1]” は不正な YAML だった

発端はこの発言。

いわく、

# ruby
YAML.load('[0, 1]') == [0, 1]
YAML.load('[0,1]') == [1]

になるというもの。カンマの後ろにスペースがあるかないかでパース結果が異なる。馬鹿な。

調べた結果、Ruby 1.9.2 までデフォルトの YAML パーサであった Syck に問題があることが分かった。 Syck は、 いわゆる配列(YAML の用語ではフローシーケンスという)の要素を区切るカンマの後にスペースがないとき、カンマが存在しないものとしてパースする。つまり [0,1] という YAML[01] に等しく、8進数の“1”を要素とするシーケンスと解釈される。

これ、 Syck がタコなだけかと思いきやそうでもないらしい。YAML 1.0 の仕様によれば、フローシーケンスはカンマの後に1つ以上の空白トークンを必要とする。 Syck は YAML 1.0 に対応した古いパーサなので、 Syck にとってみれば [0,1] はそもそも不正な YAML なのである。不正な YAML を食ったらエラーのひとつも吐いてほしいところだが、未定義動作をしても仕方ないといえるだろう。

より新しい仕様である YAML 1.1YAML 1.2 ではカンマの後の空白トークンがオプショナルになったため、現在では [0,1] も正しい YAML である。よってこの記事のタイトルは、見てのとおり過去形になっている。

Ruby 1.9.3 からデフォルトの YAML パーサとなった Psych は libyaml のラッパーであり、 libyaml は YAML 1.1 に対応している。よって Ruby 1.9.3 ではこの問題は起こらない。さらに Psych は、不正な YAML(たとえば {0:1}——フローマッピングのコロンの後には依然として空白トークンが必要)をパースするとエラーを吐く。 Syck よりも Psych を使おう。