JSON 記述用 YAML 1.2 サブセットのローダ (その10)

当初はベア・ドキュメント一つだけを扱い、ドキュメント・エンド・マーカー、ディレクティブ・エンド・マーカーも無視しようとしていたのですが、インデントがゼロの平文スタイル、リテラル・スタイル、フォールデッド・スタイルの末尾にこれらのマークを使わざるをえないことに気がついたので、形だけでも YAML ドキュメントを受け付けるようにします。ただし、一度に解釈するドキュメントは一つに限ります。

enum {
    T_SEPARATE, T_S_L_COMMENT,
    T_SIGLE_QUOTED, T_DOUBLE_QUOTED, T_PLAIN, T_NOT_PLAIN,
    T_FLOW_SEQ, T_FLOW_MAP, T_FLOW_NODE,
    T_BLOCK_SEQ, T_BLOCK_MAP, T_BLOCK_NODE, T_LITERAL, T_FOLDED,
    T_DOCUMENT};

//@<T_DOCUMENT のフィルタ@>=
        case T_DOCUMENT:
            res = wjson::l_document (s, node);
            ts.ok (res && node.dump () == spec[i].expected, spec[i].name);
            break;

ドキュメントはコメントで始まってかまいません。

    {L"Example 9.1. Document Prefix",
     T_DOCUMENT, -1, wjson::CTX_BLOCK_IN,
     LR"(# Comment)" L"\n"
     LR"(# lines)" L"\n"
     LR"(Document)" L"\n",
     LR"("Document")"},

ドキュメントにはディレクティブ・エンド・マーカーとドキュメント・エンド・マーカーがあります。

    {L"Example 9.2. Document Markers",
     T_DOCUMENT, -1, wjson::CTX_BLOCK_IN,
     LR"(%YAML 1.2)" L"\n"
     LR"(---)" L"\n"
     LR"(Document)" L"\n"
     LR"(... # Suffix)" L"\n",
     LR"("Document")"},

ディレクティブとディレクティブ・エンド・マーカーのないものはベア・ドキュメントです。

    {L"Example 9.3. Bare Documents",
     T_DOCUMENT, -1, wjson::CTX_BLOCK_IN,
     LR"(Bare)" L"\n"
     LR"(Document)" L"\n"
     LR"(...)" L"\n"
     LR"(No document)" L"\n",
     LR"("Bare Document")"},

ディレクティブがなく、ディレクティブ・エンド・マーカーだけがあるものは明示ドキュメントです。

    {L"Example 9.4. Explicit Documents",
     T_DOCUMENT, -1, wjson::CTX_BLOCK_IN,
     LR"(---)" L"\n"
     LR"({ matches)" L"\n"
     LR"(% : 20 })" L"\n"
     LR"(...)" L"\n",
     LR"({"matches %": "20"})"},

ディレクティブが指定してあるものはディレクティブ・ドキュメントです。ディレクティブが指定してあるときは、ディレクティブ・エンド・マーカーは必須です。ディレクティブは行頭がパーセントで始めます。この実装ではコメントと同じ扱いでディレクティブは行末まで読み飛ばします。

    {L"Example 9.5. Directives Documents",
     T_DOCUMENT, -1, wjson::CTX_BLOCK_IN,
     LR"(%YAML 1.2)" L"\n"
     LR"(--- |)" L"\n"
     LR"(%!PS-Adobe-2.0)" L"\n"
     LR"(...)" L"\n",
     LR"("%!PS-Adobe-2.0\n")"},

明示ドキュメントとディレクティブ・ドキュメントでは空のドキュメントを表現できます。

    {L"Example 9.5. Directives Documents empty",
     T_DOCUMENT, -1, wjson::CTX_BLOCK_IN,
     LR"(%YAML 1.2)" L"\n"
     LR"(---)" L"\n"
     LR"(# Empty)" L"\n"
     LR"(...)" L"\n",
     LR"(null)"},

ドキュメントの構文解析では、入力文字列の先頭以外から始めるときはディレクティブ・エンド・マーカーかドキュメント・エンド・マーカーが必要です。入力文字列の先頭では制限がなく、いきなりベア・ドキュメントが始まってもかまいません。ディレクティブを読み飛ばすのは、入力文字列の先頭から始まるときか、そうでないときはドキュメント・エンド・マーカーがあるときだけです。ディレクティブがあったときはディレクティブ・エンド・マーカーも指定してあったかどうかをチェックします。この後はブロック・ノードをインデントをマイナス 1 でとりこみます。ブロック・ノードの後は、文字列末尾か、ディレクティブ・エンド・マーカーかドキュメント・エンド・マーカーのいずれかでないとエラーにします。

namespace wjson {

static bool l_document (derivs_t& s0, json& value)
{
    derivs_t s = s0;
    bool first_document = s.check_bos ();
    if (! first_document && ! c_forbidden (s))
        return s0.fail ();
    if (first_document)
        s_l_comment (s);
    int docend = false;
    while (s.scan (L"^%.%.%.%b")) {
        docend = true;
        if (! s_l_comment (s))
            return s0.fail ();
    }
    int ndirective = 0;
    if (first_document || docend)
        while (c_directive (s))
            ++ndirective;
    bool dirend = false;
    if (s.scan (L"^---%b"))
        dirend = true;
    if (ndirective > 0 && ! dirend)
        return s0.fail ();
    if (! s_l_block_node (s, -1, CTX_BLOCK_IN, value)) {
        if (! s_l_comment (s))
            return s0.fail ();
        if (! first_document && ! dirend) // fix s/docend/dirend/
            return s0.fail ();
        value = json ();
    }
    if (! s.check_eos () && ! c_forbidden (s))
        return s0.fail ();
    return s0.match (s);
}

}//namespace wjson

ディレクティブはコメントが付属していてもかまわないので、ディレクティブらしき記述を読み飛ばした後に、s-l-comments でコメントを読み飛ばします。

namespace wjson {

static bool c_directive (derivs_t& s0)
{
    derivs_t s = s0;
    if (s.scan (L"%%.{0,*}") && s_l_comment (s))
        return s0.match (s);
    return s0.fail ();
}

}//namespace wjson