From 36d6b4e5549dc45baf890105de5ef487211f0144 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 6 Feb 2024 05:22:12 +0200 Subject: Add experimental support for JSON value types New types: json json_array json_object New functions: $json.value_type() $json.value_size() $json.member_{name,value}() $json.object_names() $json.array_size() $json.array_find(, ) $json.array_find_index(, ) $json.load() $json.parse() $json.serialize([, ]) For example, to load a JSON value from a file: j = $json.load($src_base/board.json) Or to construct it in a buildfile: j = [json] one@1 two@([json] 2 3 4) three@([json] x@1 y@-1) This can also be done incrementally with append/prepend: j = [json_object] j += one@1 j += two@([json] 2 3 4) j += three@([json] x@1 y@-1) Instead of using this JSON-like syntax, one can also specify valid JSON input text: j = [json] '{"one":1, "two":[2, 3, 4], "three":{"x":1, "y":-1}' Besides the above set of functions, other handy ways to access components in a JSON value are iteration and subscript. For example: for m: $j print $member_name($m) $member_value($m) print ($j[three]) A subscript can be nested: print ($j[two][1]) print ($j[three][x]) While a JSON value can be printed directly like any other value, the representation will not be pretty-printed. As a result, for complex JSON values, printing a serialized representation might be a more readable option: info $serialize($j) --- tests/function/builtin/testscript | 3 + tests/function/json/buildfile | 4 + tests/function/json/testscript | 224 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+) create mode 100644 tests/function/json/buildfile create mode 100644 tests/function/json/testscript (limited to 'tests/function') diff --git a/tests/function/builtin/testscript b/tests/function/builtin/testscript index 88f802a..04e8bd8 100644 --- a/tests/function/builtin/testscript +++ b/tests/function/builtin/testscript @@ -53,6 +53,9 @@ $* <'print $empty(abc)' >'false' : name $* <'print $empty(abc cxx{foo})' >'false' : names $* <'print $empty([bool] false)' >'false' : bool + $* <'print $empty([json] null)' >'true' : json-null + $* <'print $empty([json] "[]")' >'true' : json-array + $* <'print $empty([json] "{}")' >'true' : json-object } : first-second diff --git a/tests/function/json/buildfile b/tests/function/json/buildfile new file mode 100644 index 0000000..45c60d2 --- /dev/null +++ b/tests/function/json/buildfile @@ -0,0 +1,4 @@ +# file : tests/function/json/buildfile +# license : MIT; see accompanying LICENSE file + +./: testscript $b diff --git a/tests/function/json/testscript b/tests/function/json/testscript new file mode 100644 index 0000000..f5fc671 --- /dev/null +++ b/tests/function/json/testscript @@ -0,0 +1,224 @@ +# file : tests/function/json/testscript +# license : MIT; see accompanying LICENSE file + +# See also tests in type/json/. + +.include ../../common.testscript + +: type +: +$* <>EOO +print $value_type([json] ) +print $value_type([json] null) +print $value_type([json] true) +print $value_type([json] 123) +print $value_type([json] -123) +print $value_type([json] 123, true) +print $value_type([json] -123, true) +print $value_type([json] 1 2 3) +print $value_type([json] one@1 two@2 three@3) + +j = [json] one@1 two@2 three@3 +i = [uint64] 1 +m = ($j[$i]) +print $value_type($j[$i]) +print $value_type($m) +EOI +null +null +boolean +number +number +unsigned number +signed number +array +object +object +object +EOO + +: member +: +$* <>EOO +j = [json] one@1 two@2 three@3 +i = [uint64] 1 +m = ($j[$i]) +print $member_name($j[$i]) $member_value($j[$i]) +print $member_name($m) $member_value($m) +for m: $j + print $member_name($m) $member_value($m) +EOI +two 2 +two 2 +one 1 +two 2 +three 3 +EOO + +: size +: +$* <>EOO +print $size([json] null) +print $size([json] true) +print $size([json] 123) +print $size([json] abc) +print $size([string] ([json] abc)) # @@ Should be 3 (quoted, type hint). +print $size([json] 1 2 3) +print $size([json] one@1 two@2 three@3) +EOI +0 +1 +1 +1 +5 +3 +3 +EOO + +: find +: +$* <>EOO +j = [json] 1 ([json] one@1 two@2) 2 true 3 null 4 abc -5 null ([json] 1 2 3) +print $find_index($j, null) +print $find_index($j, true) +print $find_index($j, 3) +print $find_index($j, 0x4) +print $find_index($j, -5) +print $find_index($j, abc) +print $find_index($j, [json] 1 2 3) +print $find_index($j, [json] two@2 one@1) +print $find_index($j, [json] 1 2) +print $find_index($j, [json] one@1) +print $find_index($j, [json] one@1 two@2 three@3) +print $find_index($j, [json] one@1 TWO@3) +print $find_index($j, [json] one@1 two@3) +EOI +5 +3 +4 +6 +8 +7 +10 +1 +11 +11 +11 +11 +11 +EOO + +: parse +: +{ + : basics + : + $* <>EOO + print $json.parse('[123, "abc", {"one":1, "two":2}]') + EOI + [123,"abc",{"one":1,"two":2}] + EOO + + : diagnostics-invalid-input + : + $* <>EOE != 0 + print $json.parse('{"one":, "two":2}]') + EOI + error: invalid json input: unexpected byte ',' in value + info: line 1, column 8, byte offset 8 + :1:8: info: while calling json.parse() + EOE + + : diagnostics-duplicate-input + : + $* <>EOE != 0 + print $json.parse('{"one":1, "one":2}]') + EOI + error: invalid json input: duplicate object member 'one' + info: line 1, column 11, byte offset 15 + :1:8: info: while calling json.parse() + EOE +} + +: serialize +: +{ + : basics + : + $* <>EOO + j = [json] 123 abc ([json] one@1 two@2) + print $json.serialize($j) + print $json.serialize($j, 0) + EOI + [ + 123, + "abc", + { + "one": 1, + "two": 2 + } + ] + [123,"abc",{"one":1,"two":2}] + EOO + + : diagnostics + : + if false + { + # This is not easy to trigger normally so we have a normally-disabled + # special hack in the $json.serialize() implementation to trigger this. + # + $* <>EOE != 0 + print $json.serialize([json] deadbeef) + EOI + error: invalid json value: invalid UTF-8 text + info: while serializing string value + info: offending byte offset 4 + :1:8: info: while calling json.serialize(json) + EOE + } + +} + +: load +: +{ + : basics + : + cat <=input.json; + { + "str":"abc", + "num":123, + "arr":[1, 2, 3], + "obj":{"one":1, "two":2, "three":3} + } + EOI + $* <>EOO + j = $json.load(input.json) + for m: $j + print $member_name($m) $member_value($m) + EOI + str "abc" + num 123 + arr [1,2,3] + obj {"one":1,"two":2,"three":3} + EOO + + : diagnostics + : + cat <=input.json; + { + "str":"abc", + "num":, + "arr":[1, 2, 3], + "obj":{"one":1, "two":2, "three":3} + } + EOI + $* <>EOE != 0 + j = $json.load(input.json) + EOI + input.json:3:9: error: invalid json input: unexpected byte ',' in value + info: byte offset 26 + :1:6: info: while calling json.load() + EOE +} -- cgit v1.1