From fcc239ecdbd1467a4ac8b17a353e1b0ae7fd63a0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 6 Feb 2024 16:51:24 +0200 Subject: Map JSON null in subscript/iteration to [null] instead of empty This in fact feels more natural in the "for consumption" model and also helps with the nested subscript semantics. --- libbuild2/variable.cxx | 47 ++++++++++++++++++++++++++++------------------ tests/type/json/testscript | 20 ++++++++++++++------ 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index 1785e56..1c899f9 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -1905,9 +1905,10 @@ namespace build2 return l.as ().compare (r.as ()); } - // Return null value if the index/name is out of range. + // Return the value as well as the indication of whether the index/name is + // in range. // - static value + static pair json_subscript_impl (const value& val, value* val_data, uint64_t i, const string& n, bool index) { @@ -1918,13 +1919,12 @@ namespace build2 if (index) { if (i >= (jv.type == json_type::array ? jv.array.size () : - jv.type == json_type::object ? jv.object.size () : 1)) - return value (); + jv.type == json_type::object ? jv.object.size () : + jv.type == json_type::null ? 0 : 1)) + return make_pair (value (), false); switch (jv.type) { - case json_type::null: - return value (); // JSON null has no elements. case json_type::boolean: case json_type::signed_number: case json_type::unsigned_number: @@ -1963,6 +1963,8 @@ namespace build2 : json_member (m)); break; } + case json_type::null: + assert (false); } } else @@ -1975,7 +1977,7 @@ namespace build2 })); if (i == jv.object.end ()) - return value (); + return make_pair (value (), false); // Steal the member value if possible. // @@ -1996,20 +1998,29 @@ namespace build2 // @@ TODO: split this function into two (index/name) once get rid of this. // #if 1 + value r; switch (jr.type) { - case json_type::null: return value (names {}); - case json_type::boolean: return value (jr.boolean); - case json_type::signed_number: return value (jr.signed_number); + // Seeing that we are reversing for consumption, it feels natural to + // reverse JSON null to our [null] rather than empty. This, in + // particular, helps nested subscript. + // +#if 0 + case json_type::null: r = value (names {}); break; +#else + case json_type::null: r = value (); break; +#endif + case json_type::boolean: r = value (jr.boolean); break; + case json_type::signed_number: r = value (jr.signed_number); break; case json_type::unsigned_number: - case json_type::hexadecimal_number: return value (jr.unsigned_number); - case json_type::string: return value (move (jr.string)); + case json_type::hexadecimal_number: r = value (jr.unsigned_number); break; + case json_type::string: r = value (move (jr.string)); break; case json_type::array: - case json_type::object: break; + case json_type::object: r = value (move (jr)); break; } #endif - return value (move (jr)); + return make_pair (move (r), true); } static value @@ -2081,7 +2092,7 @@ namespace build2 } value r (jv != nullptr - ? json_subscript_impl (val, val_data, i, n, index) + ? json_subscript_impl (val, val_data, i, n, index).first : value ()); // Typify null values so that we get called for nested subscripts. @@ -2100,12 +2111,12 @@ namespace build2 // for (uint64_t i (0);; ++i) { - value e (json_subscript_impl (val, nullptr, i, {}, true)); + pair e (json_subscript_impl (val, nullptr, i, {}, true)); - if (e.null) + if (!e.second) break; - f (move (e), i == 0); + f (move (e.first), i == 0); } } diff --git a/tests/type/json/testscript b/tests/type/json/testscript index 2663a22..5295fbc 100644 --- a/tests/type/json/testscript +++ b/tests/type/json/testscript @@ -303,7 +303,7 @@ print ($j[4]) EOI 2 - + [null] [null] EOO @@ -316,7 +316,7 @@ print ($j[five]) EOI 2 - + [null] [null] EOO @@ -332,30 +332,38 @@ : nested : $* <>EOO - o = [json] one@([json] 1 2 ([json] a@3 b@4)) two@([json] x@x y@([json] 5 6)) + o = [json] one@([json] 1 2 ([json] a@3 b@4) null) two@([json] x@x y@([json] 5 6)) print ($o[one][1]) print ($o[one][2][b]) print ($o[two][y][1]) print ($o[two][bogus][junk]) print ($o[two][bogus][junk][garbage]) + print ($o[one][3][junk]) # JSON null + print ($o[one][3][junk][garbage]) - a = [json] ([json] one@1 two@([json] 2 3)) ([json] 4 5) + a = [json] ([json] one@1 two@([json] 2 3)) ([json] 4 5) null print ($a[0][one]) print ($a[0][two][1]) print ($a[1][1]) print ($a[1][123][junk]) print ($a[1][123][junk][garbage]) + print ($a[2][junk]) # JSON null + print ($a[2][junk][garbage]) EOI 2 4 6 [null] [null] + [null] + [null] 1 3 5 [null] [null] + [null] + [null] EOO : reverse @@ -368,7 +376,7 @@ print (([json] one@0xdecaf)[one]) print (([json] one@abc)[one]) EOI - + [null] true 123 -123 @@ -435,7 +443,7 @@ for v: ([json] null true 123 -123 0xdecaf abc) print $v EOI - + [null] true 123 -123 -- cgit v1.1