“[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.1 と YAML 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 を使おう。