From abcd9e4dcd17fe4bd50bc1d48ceccf6a3894986d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 25 Sep 2024 13:40:11 +0200 Subject: Use type-aware iteration in script for-loop (GH issue #436) --- libbuild2/parser.cxx | 7 ++- libbuild2/script/parser.cxx | 111 ++++++++++++++++++++++++++++++++------------ libbuild2/variable.cxx | 9 ++-- libbuild2/variable.hxx | 9 ++-- libbuild2/variable.txx | 18 ++++--- 5 files changed, 110 insertions(+), 44 deletions(-) (limited to 'libbuild2') diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index e417d39..53f808c 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -5607,7 +5607,7 @@ namespace build2 } d {var, val_attrs, line, block, lhs, is}; - function iteration = + function iteration = [this, &d] (value&& v, bool first) { // Rewind the stream. @@ -5645,12 +5645,15 @@ namespace build2 << "instead of " << t; lexer_ = ol; + return true; }; if (!iterate) { for (auto b (ns->begin ()), i (b), e (ns->end ()); i != e; ++i) { + bool first (i == b); + // Set the variable value. // bool pair (i->pair); @@ -5662,7 +5665,7 @@ namespace build2 if (etype != nullptr) typify (v, *etype, &var); - iteration (move (v), i == b); + iteration (move (v), first); } } else diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx index 84d2afc..a82ccb8 100644 --- a/libbuild2/script/parser.cxx +++ b/libbuild2/script/parser.cxx @@ -2460,19 +2460,19 @@ namespace build2 // struct loop_data { - lines::const_iterator i; - lines::const_iterator e; const function& exec_set; const function& exec_cmd; const function& exec_cond; const function& exec_for; + lines::const_iterator i; + lines::const_iterator e; const iteration_index* ii; size_t& li; variable_pool* var_pool; decltype (fcend)& fce; lines::const_iterator& fe; - } ld {i, e, - exec_set, exec_cmd, exec_cond, exec_for, + } ld {exec_set, exec_cmd, exec_cond, exec_for, + i, e, ii, li, var_pool, fcend, @@ -2558,7 +2558,6 @@ namespace build2 const location& ll; size_t fli; iteration_index& fi; - } d {ld, env, vname, attrs, ll, fli, fi}; function f ( @@ -2676,12 +2675,19 @@ namespace build2 if (val) { - // If this value is a vector, then save its element type so + // If the value type provides custom iterate function, then + // use that (see value_type::iterate for details). + // + auto iterate (val.type != nullptr + ? val.type->iterate + : nullptr); + + // If this value is a container, then save its element type so // that we can typify each element below. // const value_type* etype (nullptr); - if (val.type != nullptr) + if (!iterate && val.type != nullptr) { etype = val.type->element_type; @@ -2693,37 +2699,84 @@ namespace build2 size_t fli (li); iteration_index fi {1, ii}; - names& ns (val.as ()); - for (auto ni (ns.begin ()), ne (ns.end ()); ni != ne; ++ni) + names* ns (!iterate ? &val.as () : nullptr); + + // Similar to above. + // + struct loop_data + { + const function& exec_set; + const function& exec_cmd; + const function& exec_cond; + const function& exec_for; + lines::const_iterator i; + lines::const_iterator e; + const location& ll; + size_t& li; + variable_pool* var_pool; + const variable& var; + const attributes& val_attrs; + decltype (fcend)& fce; + lines::const_iterator& fe; + iteration_index& fi; + + } ld {exec_set, exec_cmd, exec_cond, exec_for, + i, e, + ll, li, + var_pool, *var, val_attrs, + fcend, fe, fi}; + + function iteration = + [this, &ld] (value&& v, bool) { - li = fli; + ld.exec_for (ld.var, move (v), ld.val_attrs, ld.ll); - // Set the variable value. + // Find the construct end, if it is not found yet. // - bool pair (ni->pair); - names n; - n.push_back (move (*ni)); - if (pair) n.push_back (move (*++ni)); - value v (move (n)); // Untyped. + if (ld.fe == ld.e) + ld.fe = ld.fce (ld.i, true, false); + + if (!exec_lines ( + ld.i + 1, ld.fe, + ld.exec_set, ld.exec_cmd, ld.exec_cond, ld.exec_for, + &ld.fi, ld.li, + ld.var_pool)) + return false; + + ld.fi.index++; + return true; + }; - if (etype != nullptr) - typify (v, *etype, var); + if (!iterate) + { + for (auto nb (ns->begin ()), ni (nb), ne (ns->end ()); + ni != ne; + ++ni) + { + bool first (ni == nb); - exec_for (*var, move (v), val_attrs, ll); + li = fli; - // Find the construct end, if it is not found yet. - // - if (fe == e) - fe = fcend (i, true, false); + // Set the variable value. + // + bool pair (ni->pair); + names n; + n.push_back (move (*ni)); + if (pair) n.push_back (move (*++ni)); + value v (move (n)); // Untyped. - if (!exec_lines (i + 1, fe, - exec_set, exec_cmd, exec_cond, exec_for, - &fi, li, - var_pool)) - return false; + if (etype != nullptr) + typify (v, *etype, var); - fi.index++; + if (!iteration (move (v), first)) + return false; + } + } + else + { + if (!iterate (val, iteration)) + return false; } } diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index fb9e840..0ec23d3 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -2122,9 +2122,9 @@ namespace build2 return r; } - static void + static bool json_iterate (const value& val, - const function& f) + const function& f) { // Implement in terms of subscript for consistency (in particular, // iterating over simple values like number, string). @@ -2136,8 +2136,11 @@ namespace build2 if (!e.second) break; - f (move (e.first), i == 0); + if (!f (move (e.first), i == 0)) + return false; } + + return true; } const json_value value_traits::empty_instance; diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index eebb767..a14c52b 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -124,12 +124,13 @@ namespace build2 const location& sloc, const location& bloc); - // Custom iteration function. It should invoked the specified function for + // Custom iteration function. It should invoke the specified function for // each element in order. If NULL, then the generic implementation is - // used. The passed value is never NULL. + // used. The passed value is never NULL. If the specified function returns + // false, then stop the iteration and return false. Otherwise return true. // - void (*const iterate) (const value&, - const function&); + bool (*const iterate) (const value&, + const function&); }; // The order of the enumerators is arranged so that their integral values diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx index a1ee340..6e00f89 100644 --- a/libbuild2/variable.txx +++ b/libbuild2/variable.txx @@ -686,16 +686,19 @@ namespace build2 // Provide iterate for vector for efficiency. // template - void + bool vector_iterate (const value& val, - const function& f) + const function& f) { const auto& v (val.as> ()); // Never NULL. for (auto b (v.begin ()), i (b), e (v.end ()); i != e; ++i) { - f (value (*i), i == b); + if (!f (value (*i), i == b)) + return false; } + + return true; } // Make sure these are static-initialized together. Failed that VC will make @@ -1071,16 +1074,19 @@ namespace build2 // Provide iterate for set for efficiency. // template - void + bool set_iterate (const value& val, - const function& f) + const function& f) { const auto& v (val.as> ()); // Never NULL. for (auto b (v.begin ()), i (b), e (v.end ()); i != e; ++i) { - f (value (*i), i == b); + if (!f (value (*i), i == b)) + return false; } + + return true; } // Make sure these are static-initialized together. Failed that VC will make -- cgit v1.1