Fake YAML ローダ

Ruby 製ブログツール Jekyll の YAML フロントライナのように、 Wiki でも平文テキストにメタデータをくっつけてみようとしたのですが、 フルセットの YAML は循環参照を作れたり、 あらゆるコードポイントのユニコード文字を記述できたり、 ブレスした任意のオブジェクトを記述することも可能で、 そのままだと危なすぎて使いたくない上に、 機能制限をするために書き直すにしてもピュア Perl だと重いのが難点です。

ところで、 これまで Jekyll を使ってきて、 メタデータに何を書き込んできたか思い出してみたところ、 コンフィギュレーションファイル程度の内容にすぎません。 それなら、 構文だけ YAML っぽくみせかけておけばいいのではないかということで、 次のように思いっきり簡略にした Fake YAML にしてみることにしました。

  • ソース入力文字列は必ず改行で終わることにします。
  • トップノードを暗黙のブロック・マッピングに限定し、 キーの最初の文字を行頭から必ず書き始めることにします。
  • マッピングのキーを構成する文字列はコロンを含まず、 ダブル・クォート、 シングル・クォートで囲まない、インライン平文文字列に限定します。
  • マッピングの値をインライン平文テキストか、ブロック・シーケンスのどちらかに限定します。
  • ブロック・シーケンスのブロック要素の入れ子を許さず、シーケンスの要素をインライン平文テキストに限定します。
  • インライン・テキストに複数行を許し、 ダブル・クォート、 シングル・クォートで囲まない平文テキストに限定します。
  • インライン・テキストの行頭のスペースはすべて取り除きます。行末のスペースはそのまま残します。
  • YAML と異なり、インライン・テキストの行の自動結合をおこないません。改行は改行のままテキストに残します。
  • 値はテキスト限定とし、 型認識をおこないません。
  • コメントなしとします。

Jekyll のフロントライナは次のような簡単なものが大半なので、これで十分でしょう。

---
layout: default
title: Fake YAML ローダ
categories:
  - Perl
  - Jekyll
---
なんたらかんたら

この構文は、再帰的な入れ子をやめたので、正規文法で扱えます。 単純な while ループを 1 パスで回すだけなので、 高速にデータへ変換することができます。

https://gist.github.com/tociyuki/7836613 テスト込み版

sub fakeyaml_load {
    my($s) = @_;
    my $ls = qr/[ ]*\n/msxo;
    my $mapkey = qr/\G(\S+(?:[ ]+[^\s:]+)*)[ ]*:/msxo;
    my $mapvalue = qr/\G$ls*[ ]+((?!\-\s)\S[^\n]*(?:$ls+[ ]+\S[^\n]*)*)$ls+/msxo;
    my $blockseq = qr/\G$ls+(?=[ ]+\-\s)/msxo;
    my $seqvalue = qr/\G[ ]+\-$ls*[ ]+(\S[^\n]*(?:$ls+[ ]+(?!\-\s)\S[^\n]*)*)$ls+/msxo;
    my $data = {};
    $s =~ m/\G---[^\n]*\n/gcmsxo;
    while ($s =~ m/$mapkey/gcmsxo) {
        my $k = $1;
        if ($s =~ m/$mapvalue/gcmsxo) {
            (my $v = $1) =~ s/^[ ]+//gmsxo;
            $data->{$k} = $v;
        }
        elsif ($s =~ m/$blockseq/gcmsxo) {
            $data->{$k} = [];
            while ($s =~ m/$seqvalue/gcmsxo) {
                (my $v = $1) =~ s/^[ ]+//gmsxo;
                push @{$data->{$k}}, $v;            
            }
        }
    }
    return $data;
}