From f7cd640bcc59f64e37648487391989d245ed8f0f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 6 Feb 2024 15:35:07 +0200 Subject: Add support for nested subscript, use for json access --- libbuild2/parser.cxx | 184 +++++++++++++++++++++++---------------------- libbuild2/variable.cxx | 13 +++- tests/type/json/testscript | 29 +++++++ 3 files changed, 134 insertions(+), 92 deletions(-) diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 1ac159b..7cc01e3 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -8589,126 +8589,132 @@ namespace build2 // Handle value subscript. // - if (tt == type::lsbrace && mode () == lexer_mode::eval) + if (mode () == lexer_mode::eval) // Note: not if(sub)! { - location bl (get_location (t)); - next (t, tt); // `[` - mode (lexer_mode::subscript, '\0' /* pair */); - next (t, tt); - - location l (get_location (t)); - value v ( - tt != type::rsbrace - ? parse_value (t, tt, pattern_mode::ignore, "value subscript") - : value (names ())); - - if (tt != type::rsbrace) + while (tt == type::lsbrace) { - // Note: wildcard pattern should have `]` as well so no escaping - // suggestion. - // - fail (t) << "expected ']' instead of " << t; - } + location bl (get_location (t)); + next (t, tt); // `[` + mode (lexer_mode::subscript, '\0' /* pair */); + next (t, tt); - if (!pre_parse_) - { - // For type-specific subscript implementations we pass the - // subscript value as is. - // - if (auto f = (result->type != nullptr - ? result->type->subscript - : nullptr)) + location l (get_location (t)); + value v ( + tt != type::rsbrace + ? parse_value (t, tt, pattern_mode::ignore, "value subscript") + : value (names ())); + + if (tt != type::rsbrace) { - result_data = f (*result, &result_data, move (v), l, bl); + // Note: wildcard pattern should have `]` as well so no escaping + // suggestion. + // + fail (t) << "expected ']' instead of " << t; } - else + + if (!pre_parse_) { - uint64_t j; - try - { - j = convert (move (v)); - } - catch (const invalid_argument& e) + // For type-specific subscript implementations we pass the + // subscript value as is. + // + if (auto f = (result->type != nullptr + ? result->type->subscript + : nullptr)) { - fail (l) << "invalid value subscript: " << e << - info (bl) << "use the '\\[' escape sequence if this is a " - << "wildcard pattern" << endf; + result_data = f (*result, &result_data, move (v), l, bl); } - - // Similar to expanding an undefined variable, we return NULL if - // the index is out of bounds. - // - // Note that result may or may not point to result_data. - // - if (result->null) - result_data = value (); - else if (result->type == nullptr) + else { - const names& ns (result->as ()); + uint64_t j; + try + { + j = convert (move (v)); + } + catch (const invalid_argument& e) + { + fail (l) << "invalid value subscript: " << e << + info (bl) << "use the '\\[' escape sequence if this is a " + << "wildcard pattern" << endf; + } - // Pair-aware subscript. + // Similar to expanding an undefined variable, we return NULL + // if the index is out of bounds. // - names r; - for (auto i (ns.begin ()); i != ns.end (); ++i, --j) + // Note that result may or may not point to result_data. + // + if (result->null) + result_data = value (); + else if (result->type == nullptr) { - if (j == 0) + const names& ns (result->as ()); + + // Pair-aware subscript. + // + names r; + for (auto i (ns.begin ()); i != ns.end (); ++i, --j) { - r.push_back (*i); + if (j == 0) + { + r.push_back (*i); + if (i->pair) + r.push_back (*++i); + break; + } + if (i->pair) - r.push_back (*++i); - break; + ++i; } - if (i->pair) - ++i; + result_data = r.empty () ? value () : value (move (r)); } + else + { + // Similar logic to parse_for(). + // + const value_type* etype (result->type->element_type); - result_data = r.empty () ? value () : value (move (r)); - } - else - { - // Similar logic to parse_for(). - // - const value_type* etype (result->type->element_type); + value val (result == &result_data + ? value (move (result_data)) + : value (*result)); - value val (result == &result_data - ? value (move (result_data)) - : value (*result)); + untypify (val, false /* reduce */); - untypify (val, false /* reduce */); + names& ns (val.as ()); - names& ns (val.as ()); + // Pair-aware subscript. + // + names r; + for (auto i (ns.begin ()); i != ns.end (); ++i, --j) + { + bool p (i->pair); - // Pair-aware subscript. - // - names r; - for (auto i (ns.begin ()); i != ns.end (); ++i, --j) - { - bool p (i->pair); + if (j == 0) + { + r.push_back (move (*i)); + if (p) + r.push_back (move (*++i)); + break; + } - if (j == 0) - { - r.push_back (move (*i)); if (p) - r.push_back (move (*++i)); - break; + ++i; } - if (p) - ++i; - } - - result_data = r.empty () ? value () : value (move (r)); + result_data = r.empty () ? value () : value (move (r)); - if (etype != nullptr) - typify (result_data, *etype, nullptr /* var */); + if (etype != nullptr) + typify (result_data, *etype, nullptr /* var */); + } } + + result = &result_data; } - result = &result_data; + // See if we have another subscript. + // + enable_subscript (); + tt = peek (); } - - tt = peek (); } if (pre_parse_) diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index b0fe32a..1785e56 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -2080,9 +2080,16 @@ namespace build2 } } - return (jv != nullptr - ? json_subscript_impl (val, val_data, i, n, index) - : value ()); + value r (jv != nullptr + ? json_subscript_impl (val, val_data, i, n, index) + : value ()); + + // Typify null values so that we get called for nested subscripts. + // + if (r.null) + r.type = &value_traits::value_type; + + return r; } void json_iterate (const value& val, diff --git a/tests/type/json/testscript b/tests/type/json/testscript index d9827df..2663a22 100644 --- a/tests/type/json/testscript +++ b/tests/type/json/testscript @@ -329,6 +329,35 @@ {"two":2} EOO + : nested + : + $* <>EOO + o = [json] one@([json] 1 2 ([json] a@3 b@4)) 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]) + + a = [json] ([json] one@1 two@([json] 2 3)) ([json] 4 5) + print ($a[0][one]) + print ($a[0][two][1]) + print ($a[1][1]) + print ($a[1][123][junk]) + print ($a[1][123][junk][garbage]) + EOI + 2 + 4 + 6 + [null] + [null] + 1 + 3 + 5 + [null] + [null] + EOO + : reverse : $* <>EOO -- cgit v1.1