aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-02-06 15:35:07 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-02-07 15:02:57 +0200
commitac2974380f1986480b3a974d97dd120f81c3e2af (patch)
tree336820da30501602ae7c27ef20e0cb39ea13acad
parent36d6b4e5549dc45baf890105de5ef487211f0144 (diff)
Add support for nested subscript, use for json access
-rw-r--r--libbuild2/parser.cxx184
-rw-r--r--libbuild2/variable.cxx13
-rw-r--r--tests/type/json/testscript29
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<uint64_t> (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<names> ());
+ uint64_t j;
+ try
+ {
+ j = convert<uint64_t> (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<names> ());
+
+ // 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> ());
- names& ns (val.as<names> ());
+ // 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<json_value>::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
+ :
+ $* <<EOI >>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
:
$* <<EOI >>EOO