From 88640e677fa0695783eac68014d7d8d5bc42d117 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 19 Feb 2024 12:23:10 +0200 Subject: Add string_set buildfile value type This exposes the std::set type to buildfiles. New functions: $size() Subscript returns true if the value is present and false otherwise (so it is mapped to std::set::contains()). For example: set = [string_set] a b c if ($set[b]) ... Note that append (+=) and prepend (=+) have the same semantics (std::set::insert()). For example: set = [string_set] a b set += c b # a b c set =+ d b # a b c d Example of iteration: set = [string_set] a b c for k: $set ... --- libbuild2/variable.txx | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) (limited to 'libbuild2/variable.txx') diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx index 485f9dc..501e103 100644 --- a/libbuild2/variable.txx +++ b/libbuild2/variable.txx @@ -479,6 +479,7 @@ namespace build2 convert (names&& ns) { vector v; + v.reserve (ns.size ()); // Normally there won't be any pairs. // Similar to vector_append() below except we throw instead of issuing // diagnostics. @@ -511,6 +512,8 @@ namespace build2 ? v.as> () : *new (&v.data_) vector ()); + p.reserve (p.size () + ns.size ()); // Normally there won't be any pairs. + // Convert each element to T while merging pairs. // for (auto i (ns.begin ()); i != ns.end (); ++i) @@ -834,6 +837,226 @@ namespace build2 nullptr // Iterate. }; + // set value + // + template + set value_traits>:: + convert (names&& ns) + { + set s; + + // Similar to set_append() below except we throw instead of issuing + // diagnostics. + // + for (auto i (ns.begin ()); i != ns.end (); ++i) + { + name& n (*i); + name* r (nullptr); + + if (n.pair) + { + r = &*++i; + + if (n.pair != '@') + throw invalid_argument ( + string ("invalid pair character: '") + n.pair + '\''); + } + + s.insert (value_traits::convert (move (n), r)); + } + + return s; + } + + template + void + set_append (value& v, names&& ns, const variable* var) + { + set& s (v ? v.as> () : *new (&v.data_) set ()); + + // Convert each element to T while merging pairs. + // + for (auto i (ns.begin ()); i != ns.end (); ++i) + { + name& n (*i); + name* r (nullptr); + + if (n.pair) + { + r = &*++i; + + if (n.pair != '@') + { + diag_record dr (fail); + + dr << "unexpected pair style for " + << value_traits::value_type.name << " value " + << "'" << n << "'" << n.pair << "'" << *r << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + } + + try + { + s.insert (value_traits::convert (move (n), r)); + } + catch (const invalid_argument& e) + { + diag_record dr (fail); + + dr << e; + if (var != nullptr) + dr << " in variable " << var->name; + + dr << info << "while converting "; + if (n.pair) + dr << " element pair '" << n << "'@'" << *r << "'"; + else + dr << " element '" << n << "'"; + } + } + } + + template + void + set_assign (value& v, names&& ns, const variable* var) + { + if (v) + v.as> ().clear (); + + set_append (v, move (ns), var); + } + + template + names_view + set_reverse (const value& v, names& s, bool) + { + auto& sv (v.as> ()); + s.reserve (sv.size ()); + + for (const T& x: sv) + s.push_back (value_traits::reverse (x)); + + return s; + } + + template + int + set_compare (const value& l, const value& r) + { + auto& ls (l.as> ()); + auto& rs (r.as> ()); + + auto li (ls.begin ()), le (ls.end ()); + auto ri (rs.begin ()), re (rs.end ()); + + for (; li != le && ri != re; ++li, ++ri) + if (int r = value_traits::compare (*li, *ri)) + return r; + + if (li == le && ri != re) // l shorter than r. + return -1; + + if (ri == re && li != le) // r shorter than l. + return 1; + + return 0; + } + + // Map subscript to set::contains(). + // + template + value + set_subscript (const value& val, value*, + value&& sub, + const location& sloc, + const location& bloc) + { + // Process subscript even if the value is null to make sure it is valid. + // + T k; + try + { + k = convert (move (sub)); + } + catch (const invalid_argument& e) + { + fail (sloc) << "invalid " << value_traits>::value_type.name + << " value subscript: " << e << + info (bloc) << "use the '\\[' escape sequence if this is a " + << "wildcard pattern"; + } + + bool r (false); + if (!val.null) + { + const auto& s (val.as> ()); + r = s.find (k) != s.end (); + } + + return value (r); + } + + // Make sure these are static-initialized together. Failed that VC will make + // sure it's done in the wrong order. + // + template + struct set_value_type: value_type + { + string type_name; + + set_value_type (value_type&& v) + : value_type (move (v)) + { + // set + // + type_name = "set<"; + type_name += value_traits::type_name; + type_name += '>'; + name = type_name.c_str (); + } + }; + + // Convenience aliases for certain set cases. + // + template <> + struct set_value_type: value_type + { + set_value_type (value_type&& v) + : value_type (move (v)) + { + name = "string_set"; + } + }; + + template + const set value_traits>::empty_instance; + + template + const set_value_type + value_traits>::value_type = build2::value_type // VC14 wants =. + { + nullptr, // Patched above. + sizeof (set), + nullptr, // No base. + true, // Container. + &value_traits::value_type, // Element type. + &default_dtor>, + &default_copy_ctor>, + &default_copy_assign>, + &set_assign, + &set_append, + &set_append, // Prepend the same as append. + &set_reverse, + nullptr, // No cast (cast data_ directly). + &set_compare, + &default_empty>, + &set_subscript, + nullptr // Iterate. + }; + // map value // template -- cgit v1.1