Skip to content

Latest commit

 

History

History
1062 lines (993 loc) · 53.6 KB

File metadata and controls

1062 lines (993 loc) · 53.6 KB

JSONLib (JSON generator and parser fo NX/NJ)

JSONLibは、OMRON社のNX/NJコントローラ向けのJSONジェネレータ/パーサです。JSONパーサはJSON構造のパースに専念し、値の検査についてはパースに必要とするだけに抑えています。これは、対象がPLC(NX/NJ)で処理能力に制約があるためです。JSONパーサは品質確認の為にJSONTestSuiteのテストデータによるテストを行っています。その他はこの記事を確認してください。

数値は、PLCが最も必要とする値であるため、パース時に検査を行っています。また、基本型では扱えない範囲の値であったとしても、フォーマットとして妥当である値は検査をパスします。独自の数値ライブラリがあれば使用可能であるためです。一方、文字列値に対する検査は、文字列処理が主となるアプリケーションを構築する可能性が小さいため、最低限度です。

文字列値を取得するFUNは、エスケープしたASCII及びUnicodeエスケープシーケンスのアンエスケープを行いません。文字列値の検査は、ユーザーに委譲しています。JSONパーサは、何れの型の値についてもユーザーからの要求無しにバイト列を文字列型に変換しません。これは、UTF-8バイト列とUnicodeエスケープシーケンス処理に係るリスクの軽減と、対象システムの主な用途で不要である処理の回避を目的としています。

キーについては、JSONのパス検索のためにパース時に文字列型に変換します。但し、アンエスケープは行いません。そのため、キーがエスケープ文字を含む場合、それをアンエスケースした値とは同値となりません。

JSONジェネレータ

JSONジェネレータは一連の出力FUNから成ります。いずれの出力FUNもKeyに空文字列を指定することで、値だけ出力します。オブジェクトのキーと値のペアを生成する場合、必ずKeyに空文字列ではない値を指定します。Keyの文字列はエスケープを行わないため、事前にエスケープする必要があります。

JSONジェネレータには以下の制約があります。

  • JSONのサイズは65534バイトが上限
  • コレクションのネストは16階層が上限

以下は、ルートが配列であるJSONを出力します。

iNow :=GetTime();
JSONContext_init(iJson);
JSON_ARRAY(Context:=iJson);
FOR i := 0 TO 2 DO
    JSON_OBJECT(Context:=iJson);
        JSON_STRING(Context:=iJson, Key:='product_id', Value:=UINT_TO_STRING(i));
        JSON_UINT(Context:=iJson, Key:='amount', Value:=(i + 1) * 100);
        // DTの出力は高コスト(50us以上)なので、可能ならエポック秒等が好ましい。
        JSON_DATE_AND_TIME(Context:=iJson, Key:='timestamp', Value:=iNow, Timezone:='+09:00');
    JSON_OBJECT_CLOSE(Context:=iJson);
END_FOR;
JSON_ARRAY_CLOSE(iJson);
// [{"product_id":0,"amount":100,"timestamp":"YYYY-MM-DDThh:mm:ss+09:00"},...]
JSONContext_toString(iJson, iJsonStr);

JSONパーサ

JSONパーサは、JSON文字列であるUTF-8バイト列をJSONとしてパースします。

JSONパーサには以下の特徴があります。

  • キー、値のいずれもエスケープしたASCIIとUnicodeエスケープシーケンスのアンエスケープをを行わない
  • 数値は、フォーマットに誤りが無ければ基本型の範囲外でも許容する(パースに成功する)
  • 値の評価は任意の型として取得するFUNの実行時に行こなう

JSONパーサには以下の制約があります。

  • JSONのサイズは65534バイトが上限
  • 要素(値、または、値とキーのペア)は4096個が上限
  • キーまたは、インデックスが構成するパスのサイズは63文字が上限
  • コレクションのネストは16階層が上限

以下は、オブジェクトであるJSONデータをパースして値を取得します。

iJsonStr := '{"command":10, "payload":{"product_id":"xxxxx-xxx", "amount":500, "start_at":"2025-02-25T13:00:00+09:00"}}';
iJsonBinSize := StringToAry(In:=iJsonStr, AryOut:=iJsonBin[0]);
JSONContext_init(iJson);
// JSONParserは、1サイクルでパースを終わらせる。
// そのため、サイズの大きなJSONはタスク時間を圧迫する可能性がある。
iParser(Execute:=TRUE,
        Context:=iJson,
        Data:=iJsonBin,
        // Dataの開始位置を指定する。
        Head:=0,
        // Dataのサイズを指定する。
        Size:=iJsonBinSize);

// 取得FUNは、キーが存在し、値の型が取得FUNの型に対して妥当であるときTRUEを返す。
// また、取得FUNは、デフォルト値を指定できる。
// デフォルト値は、キーが存在しないか、nullであるときにValueとして出力する。
JSON_TO_STRING(Context:=iJson,
               Key:='payload.product_id',
               Value=>iProductId,
               Default:='');
JSON_TO_UINT(Context:=iJson,
             Key:='payload.amount',
             Value=>iAmount,
             Default:=0);
JSON_TO_DATE_AND_TIME(Context:=iJson,
                      Key:='payload.start_at',
                      Value=>iStartAt,
                      Default:=DT#1970-01-01-00:00:00);	

イテレータによるコレクション操作

コレクション操作として、イテレータを使用できます。イテレーションで取得するJsonElementは、値をSTRING型として保持するため、値をSTRING型に変換する副作用があります。将来的に、値をSTRING型に変換しないイテレーションを標準とします。

iJsonStr := '{"command":10, "payload":{"product_id":"xxxxx-xxx", "amount":500, "start_at":"2025-02-25T13:00:00+09:00"}}';
iJsonBinSize := StringToAry(In:=iJsonStr, AryOut:=iJsonBin[0]);
JSONContext_init(iJson);
iParser(Execute:=TRUE,
        Context:=iJson,
        Data:=iJsonBin,
        Head:=0,
        Size:=iJsonBinSize);

IF JSON_TO_UINT(Context:=iJson,
                Key:='command',
                Value=>iCommand,
                Default:=1000)
THEN
    CASE iCommand OF
        // Schedule production.
        10:
            IF JSON_ITERATOR(Context:=iJson, Iterator:=iIterator, Key:='payload') THEN
                iOk := TRUE;
                Clear(iProductId); Clear(iAmount); Clear(iStartAt);
                WHILE JSON_ITERATOR_NEXT(Context:=iJson,
                                         Iterator:=iIterator,
                                         Element:=iElement)
                DO
                    IF iElement.Key = 'product_id' THEN
                        iProductId := iElement.Value;
                    ELSIF iElement.Key = 'amount' THEN
                        iAmount := STRING_TO_UINT(iElement.Value);
                    ELSIF iElement.Key = 'start_at' THEN
                        // DTのユーティリティーは未実装。
                        JSON_TO_DATE_AND_TIME(Context:=iJson,
                                              Key:=iElement.Path,
                                              Value=>iStartAt);
                    ELSE
                        iOk := FALSE;
                        EXIT;
                    END_IF;
                END_WHILE;
                // Validate parameters.
                // ...
                // Do schedule procedure.
                // ...
            ELSE
                // Invalid payload.
                iError := TRUE;
            END_IF;
    ELSE
        // Invalid command.
        iError := TRUE;
    END_CASE;
ELSE
    // Unknown json.
    iError := TRUE;
END_IF;

環境

使用環境

このプロジェクトの使用には、次の環境が必要です。

コントローラ NX1またはNX5
Sysmac Studio 最新版を推奨します。

構築した環境

このプロジェクトは、次の環境で構築しました。

コントローラ NX102-9000 Ver 1.64
Sysmac Studio Ver.1.61

JSONLib.slr

JSONLib.slrは、ライブラリ本体です。プロジェクトでこのライブラリを参照することで、JSONジェネレータ/パーサを使用できるようになります。使い方は、JSONLibExmaple.smc2を確認してください。JSONLibExample.smc2の単体テストを確認することで、各POUの大まかな使い方が把握できると思います。ドキュメントの整備はこれからです。

JSONLibExample.smc2

JSONLibExample.smc2は、以下のPOUを含みます。また、JSONLib.slrとSTUnit.slrを含めてあるため、ライブラリの参照は不要です。

  • POU/プログラム/Test_UnitTest JSONLibの単体テストと、JSONTestSuite用のテストです。シミュレータで実行します。

  • POU/プログラム/Main JSONLibの使用例です。シミュレータでも実行できますが、実機での実行を推奨します。

テストの実行手順

テストは次の手順で実行します。

  1. テストデータの準備
    このリポジトリのsdディレクトリ以下を仮想SDカードフォルダ(C:\OMRON\Data\SimulatorData\CARD\Memory001)にコピーします。

  2. Sysmacシミュレータを実行
    Sysmac Studioでプロジェクトを開きシミュレータを実行、テストが完了するのを待ちます。テストには5分程かかります。テストは、Test_JSONTestSuiteのDoneがTrueになりファイルへのテスト出力が終わったら(JSONTestSuite_Test.txtの作成、または更新)完了です。

  3. テスト出力の確認
    テストが完了したら、仮想SDカードフォルダ内のテスト出力を確認します。出力ファイルが多いので、"❌ Test suite"を含むファイルをgrepします。該当するファイルが無ければ、全てのテストをパスしています。簡単なスクリプトを組んでフォルダ監視しても良いです。

使用例の実行手順

使用例は次の手順で実行します。

  1. Sysmacプロジェクトの変更
    Sysmacプロジェクトのコントローラの型式を使用予定のコントローラの型式に変更します。

  2. Sysmacプロジェクトをコントローラに転送
    Sysmacプロジェクトをコントローラに転送、コントローラを運転モードにしてプログラムを動作させます。

  3. ウォッチウィンドウで確認
    Sysmac Studioでコントローラに接続し、ウォッチウィンドウで出力を確認します。iStateの値を変更して実行する使用例を切り替えられます。

JSONTestSuiteのテストデータによるテスト

JSONパーサは、独自のテストデータとJSONTestSuiteのテストデータでテストを行っています。一連のテストは、JSONLibExample.smc2に含めてあるためテストデータを用意することでテストを実行し、結果を確認することができます。JSONTestSuiteのテストデータによるテスト結果は以下です。

🟩 意図した結果を返す (n_*は失敗、y_*は成功)
🟦 パースに成功する
🟪 パースに失敗する
⬛ パースの結果は不定
🟥 ハングする

test json result note
i_number_double_huge_neg_exp 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_huge_exp 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_neg_int_huge_exp 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_pos_double_huge_exp 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_real_neg_overflow 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_real_pos_overflow 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_real_underflow 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_too_big_neg_int 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_too_big_pos_int 🟦 独自の数値ライブラリがあれば使用可能なため。
i_number_very_big_negative_int 🟦 独自の数値ライブラリがあれば使用可能なため。
i_object_key_lone_2nd_surrogate 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_1st_surrogate_but_2nd_missing 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_1st_valid_surrogate_2nd_invalid 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_UTF-16LE_with_BOM 🟪 UTF-16はサポートしない。
i_string_UTF-8_invalid_sequence 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_UTF8_surrogate_U+D800 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_incomplete_surrogate_and_escape_valid 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_incomplete_surrogate_pair 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_incomplete_surrogates_escape_valid 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_invalid_lonely_surrogate 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_invalid_surrogate 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_invalid_utf-8 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_inverted_surrogates_U+1D11E 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_iso_latin_1 🟪 Latin-1はサポートしない。
i_string_lone_second_surrogate 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_lone_utf8_continuation_byte 🟪 JSON構造を読み取れなくなる。
i_string_not_in_unicode_range 🟦 文字列の厳格な妥当性検査を使用者に委譲するため。
i_string_overlong_sequence_2_bytes 成否は値による。
i_string_overlong_sequence_6_bytes 成否は値による。
i_string_overlong_sequence_6_bytes_null 成否は値による。
i_string_truncated-utf-8 🟪 JSON構造を読み取れなくなる。
i_string_utf16BE_no_BOM 🟪 UTF-16はサポートしない。
i_string_utf16LE_no_BOM 🟪 UTF-16はサポートしない。
i_structure_500_nested_arrays 🟪 ネストは16階層が上限。
i_structure_UTF-8_BOM_empty_object 🟪 UTF-16はサポートしない。
n_array_1_true_without_comma 🟩
n_array_a_invalid_utf8 🟩
n_array_colon_instead_of_comma 🟩
n_array_comma_after_close 🟩
n_array_comma_and_number 🟩
n_array_double_comma 🟩
n_array_double_extra_comma 🟩
n_array_extra_close 🟩
n_array_extra_comma 🟩
n_array_incomplete 🟩
n_array_incomplete_invalid_value 🟩
n_array_inner_array_no_comma 🟩
n_array_invalid_utf8 🟩
n_array_items_separated_by_semicolon 🟩
n_array_just_comma 🟩
n_array_just_minus 🟩
n_array_missing_value 🟩
n_array_newlines_unclosed 🟩
n_array_number_and_comma 🟩
n_array_number_and_several_commas 🟩
n_array_spaces_vertical_tab_formfeed 🟩
n_array_star_inside 🟩
n_array_unclosed 🟩
n_array_unclosed_trailing_comma 🟩
n_array_unclosed_with_new_lines 🟩
n_array_unclosed_with_object_inside 🟩
n_incomplete_false 🟩 確実に"false"のみ受容する。
n_incomplete_null 🟩 確実に"null"のみ受容する。
n_incomplete_true 🟩 確実に"true"のみ受容する。
n_multidigit_number_then_00 🟩
n_number_++ 🟩
n_number_+1 🟩
n_number_+Inf 🟩
n_number_-01 🟩
n_number_-1.0. 🟩
n_number_-2. 🟩
n_number_-NaN 🟩
n_number_.-1 🟩
n_number_.2e-3 🟩
n_number_0.1.2 🟩
n_number_0.3e+ 🟩
n_number_0.3e 🟩
n_number_0.e1 🟩
n_number_0_capital_E+ 🟩
n_number_0_capital_E 🟩
n_number_0e+ 🟩
n_number_0e 🟩
n_number_1.0e+ 🟩
n_number_1.0e- 🟩
n_number_1.0e 🟩
n_number_1_000 🟩
n_number_1eE2 🟩
n_number_2.e+3 🟩
n_number_2.e-3 🟩
n_number_2.e3 🟩
n_number_9.e+ 🟩
n_number_Inf 🟩
n_number_NaN 🟩
n_number_U+FF11_fullwidth_dig 🟩
n_number_expression 🟩
n_number_hex_1_digit 🟩
n_number_hex_2_digits 🟩
n_number_infinity 🟩
n_number_invalid+- 🟩
n_number_invalid-negative-real 🟩
n_number_invalid-utf-8-in-bigger-int 🟩
n_number_invalid-utf-8-in-exponent 🟩
n_number_invalid-utf-8-in-int 🟩
n_number_minus_infinity 🟩
n_number_minus_sign_with_trailing_garbag 🟩
n_number_minus_space_1 🟩
n_number_neg_int_starting_with_zero 🟩
n_number_neg_real_without_int_part 🟩
n_number_neg_with_garbage_at_end 🟩
n_number_real_garbage_after_e 🟩
n_number_real_with_invalid_utf8_after_e 🟩
n_number_real_without_fractional_part 🟩
n_number_starting_with_dot 🟩
n_number_with_alpha 🟩
n_number_with_alpha_char 🟩
n_number_with_leading_zero 🟩
n_object_bad_value 🟩
n_object_bracket_key 🟩
n_object_comma_instead_of_colon 🟩
n_object_double_colon 🟩
n_object_emoji 🟩
n_object_garbage_at_end 🟩
n_object_key_with_single_quotes 🟩
n_object_lone_continuation_byte_in_key_and_trailing_comma 🟩
n_object_missing_colon 🟩
n_object_missing_key 🟩
n_object_missing_semicolon 🟩
n_object_missing_value 🟩
n_object_no-colon 🟩
n_object_non_string_key 🟩
n_object_non_string_key_but_huge_number_instead 🟩
n_object_repeated_null_null 🟩
n_object_several_trailing_commas 🟩
n_object_single_quote 🟩
n_object_trailing_comma 🟩
n_object_trailing_comment 🟩
n_object_trailing_comment_open 🟩
n_object_trailing_comment_slash_open 🟩
n_object_trailing_comment_slash_open_incomplete 🟩
n_object_two_commas_in_a_row 🟩
n_object_unquoted_key 🟩
n_object_unterminated-value 🟩
n_object_with_single_string 🟩
n_object_with_trailing_garbage 🟩
n_single_space 🟩
n_string_1_surrogate_then_escape 🟩
n_string_1_surrogate_then_escape_u 🟩
n_string_1_surrogate_then_escape_u1 🟩
n_string_1_surrogate_then_escape_u1x 🟩
n_string_accentuated_char_no_quotes 🟩
n_string_backslash_00 🟩
n_string_escape_x 🟩
n_string_escaped_backslash_bad 🟩
n_string_escaped_ctrl_char_tab 🟩
n_string_escaped_emoji 🟩
n_string_incomplete_escape 🟩
n_string_incomplete_escaped_character 🟩
n_string_incomplete_surrogate 🟩
n_string_incomplete_surrogate_escape_invalid 🟩
n_string_invalid-utf-8-in-escape 🟩
n_string_invalid_backslash_esc 🟩
n_string_invalid_unicode_escape 🟩
n_string_invalid_utf8_after_escape 🟩
n_string_leading_uescaped_thinspace 🟩
n_string_no_quotes_with_bad_escape 🟩
n_string_single_doublequote 🟩
n_string_single_quote 🟩
n_string_single_string_no_double_quotes 🟩
n_string_start_escape_unclosed 🟩
n_string_unescaped_ctrl_char 🟩
n_string_unescaped_newline 🟩
n_string_unescaped_tab 🟩
n_string_unicode_CapitalU 🟩
n_string_with_trailing_garbage 🟩
n_structure_100000_opening_arrays 🟩 ネストは16階層が上限。
n_structure_U+2060_word_joined 🟩
n_structure_UTF8_BOM_no_data 🟩 BOM有UTF-8はサポートしない。
n_structure_angle_bracket_. 🟩
n_structure_angle_bracket_null 🟩
n_structure_array_trailing_garbage 🟩
n_structure_array_with_extra_array_close 🟩
n_structure_array_with_unclosed_string 🟩
n_structure_ascii-unicode-identifier 🟩
n_structure_capitalized_True 🟩
n_structure_close_unopened_array 🟩
n_structure_comma_instead_of_closing_brace 🟩
n_structure_double_array 🟩
n_structure_end_array 🟩
n_structure_incomplete_UTF8_BOM 🟩
n_structure_lone-invalid-utf-8 🟩
n_structure_lone-open-bracket 🟩
n_structure_no_data 🟩
n_structure_null-byte-outside-string 🟩
n_structure_number_with_trailing_garbage 🟩
n_structure_object_followed_by_closing_object 🟩
n_structure_object_unclosed_no_value 🟩
n_structure_object_with_comment 🟩
n_structure_object_with_trailing_garbage 🟩
n_structure_open_array_apostrophe 🟩
n_structure_open_array_comma 🟩
n_structure_open_array_object 🟩
n_structure_open_array_open_object 🟩
n_structure_open_array_open_string 🟩
n_structure_open_array_string 🟩
n_structure_open_object 🟩
n_structure_open_object_close_array 🟩
n_structure_open_object_comma 🟩
n_structure_open_object_open_array 🟩
n_structure_open_object_open_string 🟩
n_structure_open_object_string_with_apostrophes 🟩
n_structure_open_open 🟩
n_structure_single_eacute 🟩
n_structure_single_star 🟩
n_structure_trailing_# 🟩
n_structure_uescaped_LF_before_string 🟩
n_structure_unclosed_array 🟩
n_structure_unclosed_array_partial_null 🟩
n_structure_unclosed_array_unfinished_false 🟩
n_structure_unclosed_array_unfinished_true 🟩
n_structure_unclosed_object 🟩
n_structure_unicode-identifier 🟩
n_structure_whitespace_U+2060_word_joiner 🟩
n_structure_whitespace_formfeed 🟩
y_array_arraysWithSpaces 🟩
y_array_empty-string 🟩
y_array_empty 🟩
y_array_ending_with_newline 🟩
y_array_false 🟩
y_array_heterogeneous 🟩
y_array_null 🟩
y_array_with_1_and_newline 🟩
y_array_with_leading_space 🟩
y_array_with_several_null 🟩
y_array_with_trailing_space 🟩
y_number 🟩
y_number_0e+1 🟩
y_number_0e1 🟩
y_number_after_space 🟩
y_number_double_close_to_zero 🟩
y_number_int_with_exp 🟩
y_number_minus_zero 🟩
y_number_negative_int 🟩
y_number_negative_one 🟩
y_number_negative_zero 🟩
y_number_real_capital_e 🟩
y_number_real_capital_e_neg_exp 🟩
y_number_real_capital_e_pos_exp 🟩
y_number_real_exponent 🟩
y_number_real_fraction_exponent 🟩
y_number_real_neg_exp 🟩
y_number_real_pos_exponent 🟩
y_number_simple_int 🟩
y_number_simple_real 🟩
y_object 🟩
y_object_basic 🟩
y_object_duplicated_key 🟩
y_object_duplicated_key_and_value 🟩
y_object_empty 🟩
y_object_empty_key 🟩
y_object_escaped_null_in_key 🟩
y_object_extreme_numbers 🟩
y_object_long_strings 🟩
y_object_simple 🟩
y_object_string_unicode 🟩
y_object_with_newlines 🟩
y_string_1_2_3_bytes_UTF-8_sequences 🟩
y_string_accepted_surrogate_pair 🟩 バイト数の確認のみ。
y_string_accepted_surrogate_pairs 🟩 バイト数の確認のみ。
y_string_allowed_escapes 🟩
y_string_backslash_and_u_escaped_zero 🟩
y_string_backslash_doublequotes 🟩
y_string_comments 🟩
y_string_double_escape_a 🟩
y_string_double_escape_n 🟩
y_string_escaped_control_character 🟩
y_string_escaped_noncharacter 🟩
y_string_in_array 🟩
y_string_in_array_with_leading_space 🟩
y_string_last_surrogates_1_and_2 🟩
y_string_nbsp_uescaped 🟩
y_string_nonCharacterInUTF-8_U+10FFFF 🟩
y_string_nonCharacterInUTF-8_U+FFFF 🟩
y_string_null_escape 🟩
y_string_one-byte-utf-8 🟩
y_string_pi 🟩
y_string_reservedCharacterInUTF-8_U+1BFFF 🟩
y_string_simple_ascii 🟩
y_string_space 🟩
y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF 🟩
y_string_three-byte-utf-8 🟩
y_string_two-byte-utf-8 🟩
y_string_u+2028_line_sep 🟩
y_string_u+2029_par_sep 🟩
y_string_uEscape 🟩
y_string_uescaped_newline 🟩
y_string_unescaped_char_delete 🟩
y_string_unicode 🟩
y_string_unicodeEscapedBackslash 🟩
y_string_unicode_2 🟩
y_string_unicode_U+10FFFE_nonchar 🟩
y_string_unicode_U+1FFFE_nonchar 🟩
y_string_unicode_U+200B_ZERO_WIDTH_SPACE 🟩
y_string_unicode_U+2064_invisible_plus 🟩
y_string_unicode_U+FDD0_nonchar 🟩
y_string_unicode_U+FFFE_nonchar 🟩
y_string_unicode_escaped_double_quote 🟩
y_string_utf8 🟩
y_string_with_del_character 🟩
y_structure_lonely_false 🟩
y_structure_lonely_int 🟩
y_structure_lonely_negative_real 🟩
y_structure_lonely_null 🟩
y_structure_lonely_string 🟩
y_structure_lonely_true 🟩
y_structure_string_empty 🟩
y_structure_trailing_newline 🟩
y_structure_true_in_array 🟩
y_structure_whitespace_array 🟩