diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2024-02-06 05:05:56 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2024-02-06 05:05:56 +0200 |
commit | a5acaba537dab8e06be1197916acff86699aa5a3 (patch) | |
tree | 3f34f1d3889f17937b170a032ebd60338cd71ae1 /libbuild2 | |
parent | 5b21820a4ad0f69290c6a20643640ff5fbf5021a (diff) |
Add support for value type-specific subscript and iteration
Diffstat (limited to 'libbuild2')
-rw-r--r-- | libbuild2/parser.cxx | 217 | ||||
-rw-r--r-- | libbuild2/variable.cxx | 64 | ||||
-rw-r--r-- | libbuild2/variable.hxx | 22 | ||||
-rw-r--r-- | libbuild2/variable.txx | 12 |
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 |