aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-09-25 13:40:11 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-09-25 13:40:11 +0200
commitabcd9e4dcd17fe4bd50bc1d48ceccf6a3894986d (patch)
tree38d24b2580b864af04a3b123b3d1448370cd563a /libbuild2
parent21bd200819d14f5bba18c2a87cccc18743cd7fde (diff)
Use type-aware iteration in script for-loop (GH issue #436)
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/parser.cxx7
-rw-r--r--libbuild2/script/parser.cxx111
-rw-r--r--libbuild2/variable.cxx9
-rw-r--r--libbuild2/variable.hxx9
-rw-r--r--libbuild2/variable.txx18
5 files changed, 110 insertions, 44 deletions
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<void (value&&, bool first)> iteration =
+ function<bool (value&&, bool first)> 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_function>& exec_set;
const function<exec_cmd_function>& exec_cmd;
const function<exec_cond_function>& exec_cond;
const function<exec_for_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<void (string&&)> 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<names> ());
- for (auto ni (ns.begin ()), ne (ns.end ()); ni != ne; ++ni)
+ names* ns (!iterate ? &val.as<names> () : nullptr);
+
+ // Similar to above.
+ //
+ struct loop_data
+ {
+ const function<exec_set_function>& exec_set;
+ const function<exec_cmd_function>& exec_cmd;
+ const function<exec_cond_function>& exec_cond;
+ const function<exec_for_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<bool (value&&, bool first)> 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<void (value&&, bool first)>& f)
+ const function<bool (value&&, bool first)>& 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<json_value>::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<void (value&&, bool first)>&);
+ bool (*const iterate) (const value&,
+ const function<bool (value&&, bool first)>&);
};
// 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<T> for efficiency.
//
template <typename T>
- void
+ bool
vector_iterate (const value& val,
- const function<void (value&&, bool first)>& f)
+ const function<bool (value&&, bool first)>& f)
{
const auto& v (val.as<vector<T>> ()); // 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<T> for efficiency.
//
template <typename T>
- void
+ bool
set_iterate (const value& val,
- const function<void (value&&, bool first)>& f)
+ const function<bool (value&&, bool first)>& f)
{
const auto& v (val.as<set<T>> ()); // 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