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

フォールデッド・スタイルは、リテラル・スタイルに行の畳み込みを追加したものです。

enum {T_SINGLE_QUOTED, T_DOUBLE_QUOTED, T_LITERAL, T_FOLDED};

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

フロー・スカラーの行の畳み込みとの違いは、行末の空白列の扱いにあり、フローでは捨てていたのに対して、ブロック・スカラーであるフォールデッドでは文字列に取り込みます。その点、少し記述が楽になるはずなのですが、微妙な機能が盛り込まれているため、そうは簡単に済みません。フォールデッド・スタイルで行の折り畳みが働くのは、インデントの直後に空白ではない文字がある行の間に限ります。そうでない行では空白行含めて、改行をすべて文字列にとりこみます。

	{L"Example 8.10. Folded Lines",
	 T_FOLDED, -1, wjson::CTX_BLOCK_IN,
LR"__(>

 folded
 line

 next
 line
   * bullet

   * list
   * lines

 last
 line

# Comment
)__",
	L"\nfolded line\nnext line\n  * bullet\n\n  * list\n  * lines\n\nlast line\n"},

インデント・インジケータはリテラルと同じ働きをします。インデントがゼロの場合のふるまいもリテラルと同じです。

    {L"Example 8.2. explicit block indicator",
     T_FOLDED, 0, wjson::CTX_BLOCK_IN,
     L">1\n"
     L"  explicit\n",
     // expected
     L" explicit\n"},

    {L"detected block zero indentation",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">\n"
     L"detected\n"
     L"\n"
     L"...\n",
     // expected
     L"detected\n"},

チョンピング・インジケータもリテラルと同じふるまいをします。strip は文字列末尾の改行列を除去し、clip は 1 個にまとめ、keep はそのまま残します。

    {L"Example 8.5. strip trailing lines",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">-\n"
     L"  # text\n"
     L"  \n"
     L" # clip\n"
     L"  # comments:"
     L"\n",
     //expected
     L"# text"},

    {L"Example 8.5. clip trailing lines",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">\n"
     L"  # text\n"
     L"  \n"
     L" # clip\n"
     L"  # comments:"
     L"\n",
     //expected
     L"# text\n"},

    {L"Example 8.5. keep trailing lines",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">+\n"
     L"  # text\n"
     L"  \n"
     L" # clip\n"
     L"  # comments:"
     L"\n",
     //expected
     L"# text\n\n"},

内容が空のときのチョンピング・インジケータの働きもリテラルと同じです。strip と clip は空文字列、keep のときだけ改行を含む文字列が得られます。

   {L"Example 8.6. strip empty scalar",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">-\n"
     L"\n"
     L"\n",
     //expected
     L""},

    {L"Example 8.6. clip empty scalar",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">\n"
     L"\n"
     L"\n",
     //expected
     L""},

    {L"Example 8.6. keep empty scalar",
     T_FOLDED, -1, wjson::CTX_BLOCK_IN,
     L">+\n"
     L"\n"
     L"\n",
     //expected
     L"\n\n"},

実装では、インデントの直後の文字がブランクかそうでないかを調べて、直後がブランクでない行の間に限り、間の空行で数えた改行の個数がゼロかそれより多いかによって、空白か改行のどちらを文字列に追加するかを決めています (最新版では、リテラルの c_l_literal とこの c_l_folded は一つの関数でまとめて処理するように変更してあります)。

namespace wjson {

static bool c_l_folded (derivs_t& s0, int const n0, json& value)
{
    std::wstring lit;
    derivs_t s = s0;
    int m;
    int t;
    if (! (s.scan (L">") && c_b_block_header (s, n0, m, t)))
        return s0.fail ();
    int n = n0 + m;
    int nbreak = 0;
    bool clipable = false;
    bool foldable = false;
    for (;;) {
        derivs_t s1 = s;
        derivs_t s2 = s;
        derivs_t s3 = s;
        if (c_forbidden (s))
            break;
        if (s1.scan_indent (0, n) && s1.check (L"\n")) {
            s.match (s1);
            ++nbreak;
        }
        else if (s2.scan_indent (n, n) && s2.check (L"%s.{0,*}\n")) {
            if (nbreak > 0)
                lit.append (nbreak, '\n');
            lit.append (s2.cbegin (), s2.cend () - 1);
            s.match (s2);
            nbreak = 1;
            clipable = true;
            foldable = false;
        }
        else if (s3.scan_indent (n, n) && s3.check (L"%S.{0,*}\n")) {
            if (! foldable && nbreak > 0)
                lit.append (nbreak, '\n');
            else if (nbreak == 1)
                lit.push_back (' ');
            else if (nbreak > 1)
                lit.append (nbreak - 1, '\n');
            lit.append (s3.cbegin (), s3.cend () - 1);
            s.match (s3);
            nbreak = 1;
            clipable = true;
            foldable = true;
        }
        else
            break;
    }
    if ('+' == t && nbreak > 0)
        lit.append (nbreak, '\n');
    else if (' ' == t && clipable)
        lit.push_back ('\n');
    if (! l_trail_comments (s, n))
        return s0.fail ();
    value = json (lit);
    return s0.match (s);
}

}//namespace wjson