aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-02-06 05:05:56 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-02-06 05:05:56 +0200
commita5acaba537dab8e06be1197916acff86699aa5a3 (patch)
tree3f34f1d3889f17937b170a032ebd60338cd71ae1 /libbuild2
parent5b21820a4ad0f69290c6a20643640ff5fbf5021a (diff)
Add support for value type-specific subscript and iteration
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/parser.cxx217
-rw-r--r--libbuild2/variable.cxx64
-rw-r--r--libbuild2/variable.hxx22
-rw-r--r--libbuild2/variable.txx12
4 files changed, 205 insertions, 110 deletions
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index eb78059..043cd10 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -5219,12 +5219,17 @@ namespace build2
value val (parse_value_with_attributes (t, tt, pattern_mode::expand));
- // If this value is a vector, then save its element type so that we
+ // 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 && val.type != nullptr)
+ if (!iterate && val && val.type != nullptr)
{
etype = val.type->element_type;
@@ -5284,33 +5289,45 @@ namespace build2
if (!val)
return;
- names& ns (val.as<names> ());
-
- if (ns.empty ())
- return;
+ names* ns (nullptr);
+ if (!iterate)
+ {
+ ns = &val.as<names> ();
+ if (ns->empty ())
+ return;
+ }
istringstream is (move (body));
- for (auto i (ns.begin ()), e (ns.end ());; )
+ struct data
{
- // Set the variable value.
- //
- bool pair (i->pair);
- names n;
- n.push_back (move (*i));
- if (pair) n.push_back (move (*++i));
- value v (move (n));
+ const variable& var;
+ const attributes& val_attrs;
+ uint64_t line;
+ bool block;
+ value& lhs;
+ istringstream& is;
- if (etype != nullptr)
- typify (v, *etype, &var);
+ } d {var, val_attrs, line, block, lhs, is};
+
+ function<void (value&&, bool first)> iteration =
+ [this, &d] (value&& v, bool first)
+ {
+ // Rewind the stream.
+ //
+ if (!first)
+ {
+ d.is.clear ();
+ d.is.seekg (0);
+ }
// Inject element attributes.
//
- attributes_.push_back (val_attrs);
+ attributes_.push_back (d.val_attrs);
- apply_value_attributes (&var, lhs, move (v), type::assign);
+ apply_value_attributes (&d.var, d.lhs, move (v), type::assign);
- lexer l (is, *path_, line);
+ lexer l (d.is, *path_, d.line);
lexer* ol (lexer_);
lexer_ = &l;
@@ -5318,7 +5335,7 @@ namespace build2
type tt;
next (t, tt);
- if (block)
+ if (d.block)
{
next (t, tt); // {
next (t, tt); // <newline>
@@ -5326,20 +5343,33 @@ namespace build2
parse_clause (t, tt);
- if (tt != (block ? type::rcbrace : type::eos))
- fail (t) << "expected name " << (block ? "or '}' " : "")
+ if (tt != (d.block ? type::rcbrace : type::eos))
+ fail (t) << "expected name " << (d.block ? "or '}' " : "")
<< "instead of " << t;
lexer_ = ol;
+ };
- if (++i == e)
- break;
+ if (!iterate)
+ {
+ for (auto b (ns->begin ()), i (b), e (ns->end ()); i != e; ++i)
+ {
+ // Set the variable value.
+ //
+ bool pair (i->pair);
+ names n;
+ n.push_back (move (*i));
+ if (pair) n.push_back (move (*++i));
+ value v (move (n));
- // Rewind the stream.
- //
- is.clear ();
- is.seekg (0);
+ if (etype != nullptr)
+ typify (v, *etype, &var);
+
+ iteration (move (v), i == b);
+ }
}
+ else
+ iterate (val, iteration);
}
void parser::
@@ -8506,88 +8536,97 @@ namespace build2
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().
- //
- // @@ Maybe we should invent type-aware subscript? Could also
- // be used for non-index subscripts (map keys etc).
- //
- 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;
diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx
index 392c9bb..0ad63b1 100644
--- a/libbuild2/variable.cxx
+++ b/libbuild2/variable.cxx
@@ -460,7 +460,7 @@ namespace build2
m += "name '" + to_string (n) + '\'';
}
- throw invalid_argument (m);
+ throw invalid_argument (move (m));
}
// names
@@ -506,7 +506,9 @@ namespace build2
&simple_reverse<bool>,
nullptr, // No cast (cast data_ directly).
nullptr, // No compare (compare as POD).
- nullptr // Never empty.
+ nullptr, // Never empty.
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// int64_t value
@@ -564,7 +566,9 @@ namespace build2
&simple_reverse<int64_t>,
nullptr, // No cast (cast data_ directly).
nullptr, // No compare (compare as POD).
- nullptr // Never empty.
+ nullptr, // Never empty.
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// uint64_t value
@@ -622,7 +626,9 @@ namespace build2
&simple_reverse<uint64_t>,
nullptr, // No cast (cast data_ directly).
nullptr, // No compare (compare as POD).
- nullptr // Never empty.
+ nullptr, // Never empty.
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// string value
@@ -717,7 +723,9 @@ namespace build2
&simple_reverse<string>,
nullptr, // No cast (cast data_ directly).
&simple_compare<string>,
- &default_empty<string>
+ &default_empty<string>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// path value
@@ -785,7 +793,9 @@ namespace build2
&simple_reverse<path>,
nullptr, // No cast (cast data_ directly).
&simple_compare<path>,
- &default_empty<path>
+ &default_empty<path>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// dir_path value
@@ -853,7 +863,9 @@ namespace build2
&simple_reverse<dir_path>,
nullptr, // No cast (cast data_ directly).
&simple_compare<dir_path>,
- &default_empty<dir_path>
+ &default_empty<dir_path>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// abs_dir_path value
@@ -909,7 +921,9 @@ namespace build2
&simple_reverse<abs_dir_path>,
nullptr, // No cast (cast data_ directly).
&simple_compare<abs_dir_path>,
- &default_empty<abs_dir_path>
+ &default_empty<abs_dir_path>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// name value
@@ -948,7 +962,9 @@ namespace build2
&name_reverse,
nullptr, // No cast (cast data_ directly).
&simple_compare<name>,
- &default_empty<name>
+ &default_empty<name>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// name_pair
@@ -1031,7 +1047,9 @@ namespace build2
&name_pair_reverse,
nullptr, // No cast (cast data_ directly).
&simple_compare<name_pair>,
- &default_empty<name_pair>
+ &default_empty<name_pair>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// process_path value
@@ -1186,7 +1204,9 @@ namespace build2
&process_path_reverse,
nullptr, // No cast (cast data_ directly).
&simple_compare<process_path>,
- &default_empty<process_path>
+ &default_empty<process_path>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// process_path_ex value
@@ -1385,7 +1405,9 @@ namespace build2
&process_path_ex_reverse,
nullptr, // No cast (cast data_ directly).
&simple_compare<process_path>, // For now compare as process_path.
- &default_empty<process_path_ex>
+ &default_empty<process_path_ex>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// target_triplet value
@@ -1427,7 +1449,9 @@ namespace build2
&simple_reverse<target_triplet>,
nullptr, // No cast (cast data_ directly).
&simple_compare<target_triplet>,
- &default_empty<target_triplet>
+ &default_empty<target_triplet>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// project_name value
@@ -1472,7 +1496,9 @@ namespace build2
&simple_reverse<project_name>,
nullptr, // No cast (cast data_ directly).
&simple_compare<project_name>,
- &default_empty<project_name>
+ &default_empty<project_name>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// cmdline
@@ -1529,7 +1555,7 @@ namespace build2
new (&v.data_) cmdline (move (x));
}
- void
+ static void
cmdline_assign (value& v, names&& ns, const variable*)
{
if (!v)
@@ -1542,7 +1568,7 @@ namespace build2
make_move_iterator (ns.end ()));
}
- void
+ static void
cmdline_append (value& v, names&& ns, const variable*)
{
if (!v)
@@ -1557,7 +1583,7 @@ namespace build2
make_move_iterator (ns.end ()));
}
- void
+ static void
cmdline_prepend (value& v, names&& ns, const variable*)
{
if (!v)
@@ -1605,7 +1631,9 @@ namespace build2
&cmdline_reverse,
nullptr, // No cast (cast data_ directly).
&cmdline_compare,
- &default_empty<cmdline>
+ &default_empty<cmdline>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// variable_pool
diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx
index a91a7e0..9d7b001 100644
--- a/libbuild2/variable.hxx
+++ b/libbuild2/variable.hxx
@@ -99,6 +99,28 @@ namespace build2
// If NULL, then the value is never empty.
//
bool (*const empty) (const value&);
+
+ // Custom subscript function. If NULL, then the generic implementation is
+ // used.
+ //
+ // Note that val can be NULL. If val_data points to val, then it can be
+ // moved from. The sloc and bloc arguments are the subscript and brace
+ // locations, respectively.
+ //
+ // Note: should normally be consistent with iterate.
+ //
+ value (*const subscript) (const value& val,
+ value* val_data,
+ value&& subscript,
+ const location& sloc,
+ const location& bloc);
+
+ // Custom iteration function. It should invoked the specified function for
+ // each element in order. If NULL, then the generic implementation is
+ // used. The passed value is never NULL.
+ //
+ void (*const iterate) (const value&,
+ const function<void (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 2c1265a..bc4132f 100644
--- a/libbuild2/variable.txx
+++ b/libbuild2/variable.txx
@@ -664,7 +664,9 @@ namespace build2
&vector_reverse<T>,
nullptr, // No cast (cast data_ directly).
&vector_compare<T>,
- &default_empty<vector<T>>
+ &default_empty<vector<T>>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// vector<pair<K, V>> value
@@ -817,7 +819,9 @@ namespace build2
&pair_vector_reverse<K, V>,
nullptr, // No cast (cast data_ directly).
&pair_vector_compare<K, V>,
- &default_empty<vector<pair<K, V>>>
+ &default_empty<vector<pair<K, V>>>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// map<K, V> value
@@ -998,7 +1002,9 @@ namespace build2
&map_reverse<K, V>,
nullptr, // No cast (cast data_ directly).
&map_compare<K, V>,
- &default_empty<map<K, V>>
+ &default_empty<map<K, V>>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
};
// variable_cache