diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2024-02-19 12:23:10 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2024-02-20 16:01:40 +0200 |
commit | 88640e677fa0695783eac68014d7d8d5bc42d117 (patch) | |
tree | b1de045dc2d8d290422aaebbfbdbe184e8074dfd | |
parent | bed6b6a9170253e010cbffd59202add4edfd1c2b (diff) |
Add string_set buildfile value type
This exposes the std::set<std::string> type to buildfiles.
New functions:
$size(<string-set>)
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
...
-rw-r--r-- | libbuild2/functions-string.cxx | 6 | ||||
-rw-r--r-- | libbuild2/parser.cxx | 1 | ||||
-rw-r--r-- | libbuild2/variable.cxx | 2 | ||||
-rw-r--r-- | libbuild2/variable.hxx | 22 | ||||
-rw-r--r-- | libbuild2/variable.ixx | 38 | ||||
-rw-r--r-- | libbuild2/variable.txx | 223 | ||||
-rw-r--r-- | tests/function/string/testscript | 3 | ||||
-rw-r--r-- | tests/type/set/buildfile | 4 | ||||
-rw-r--r-- | tests/type/set/testscript | 52 |
9 files changed, 348 insertions, 3 deletions
diff --git a/libbuild2/functions-string.cxx b/libbuild2/functions-string.cxx index b332efa..367923f 100644 --- a/libbuild2/functions-string.cxx +++ b/libbuild2/functions-string.cxx @@ -119,14 +119,16 @@ namespace build2 }; // $size(<strings>) + // $size(<string-set>) // $size(<string-map>) // $size(<string>) // - // First two forms: return the number of elements in the sequence. + // First three forms: return the number of elements in the sequence. // - // Third form: return the number of characters (bytes) in the string. + // Fourth form: return the number of characters (bytes) in the string. // f["size"] += [] (strings v) {return v.size ();}; + f["size"] += [] (set<string> v) {return v.size ();}; f["size"] += [] (map<string, string> v) {return v.size ();}; f["size"] += [] (string v) {return v.size ();}; diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index b68f009..bf806be 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -6101,6 +6101,7 @@ namespace build2 if (n[6] == '\0') return &value_traits<string>::value_type; if (n[6] == 's' && n[7] == '\0') return &value_traits<strings>::value_type; + if (n == "string_set") return &value_traits<set<string>>::value_type; if (n == "string_map") return &value_traits<map<string,string>>::value_type; } diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index 40eeccd..6f9f0fb 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -3292,6 +3292,8 @@ namespace build2 template struct LIBBUILD2_DEFEXPORT value_traits<vector<pair<string, optional<bool>>>>; + template struct LIBBUILD2_DEFEXPORT value_traits<set<string>>; + template struct LIBBUILD2_DEFEXPORT value_traits<map<string, string>>; diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index d67098e..b157806 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -1177,6 +1177,26 @@ namespace build2 static const pair_vector_value_type<K, V> value_type; }; + // set<T> + // + template <typename T> + struct set_value_type; + + template <typename T> + struct value_traits<set<T>> + { + static_assert (sizeof (set<T>) <= value::size_, "insufficient space"); + + static set<T> convert (names&&); + static void assign (value&, set<T>&&); + static void append (value&, set<T>&&); + static void prepend (value&, set<T>&&); + static bool empty (const set<T>& x) {return x.empty ();} + + static const set<T> empty_instance; + static const set_value_type<T> value_type; + }; + // map<K, V> // // Either K or V can be optional<T> making the key or value optional. @@ -1321,6 +1341,8 @@ namespace build2 extern template struct LIBBUILD2_DECEXPORT value_traits<vector<pair<string, optional<bool>>>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<set<string>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<map<string, string>>; diff --git a/libbuild2/variable.ixx b/libbuild2/variable.ixx index b8f80e3..a448cd8 100644 --- a/libbuild2/variable.ixx +++ b/libbuild2/variable.ixx @@ -853,6 +853,44 @@ namespace build2 new (&v.data_) vector<pair<K, V>> (move (x)); } + // set<T> value + // + template <typename T> + inline void value_traits<set<T>>:: + assign (value& v, set<T>&& x) + { + if (v) + v.as<set<T>> () = move (x); + else + new (&v.data_) set<T> (move (x)); + } + + template <typename T> + inline void value_traits<set<T>>:: + append (value& v, set<T>&& x) + { + if (v) + { + set<T>& p (v.as<set<T>> ()); + + if (p.empty ()) + p.swap (x); + else + // Keys (being const) can only be copied. + // + p.insert (x.begin (), x.end ()); + } + else + new (&v.data_) set<T> (move (x)); + } + + template <typename T> + inline void value_traits<set<T>>:: + prepend (value& v, set<T>&& x) + { + append (v, move (x)); + } + // map<K, V> value // template <typename K, typename V> 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<T> 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<vector<T>> () : *new (&v.data_) vector<T> ()); + 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<T> value + // + template <typename T> + set<T> value_traits<set<T>>:: + convert (names&& ns) + { + set<T> 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<T>::convert (move (n), r)); + } + + return s; + } + + template <typename T> + void + set_append (value& v, names&& ns, const variable* var) + { + set<T>& s (v ? v.as<set<T>> () : *new (&v.data_) set<T> ()); + + // 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<T>::value_type.name << " value " + << "'" << n << "'" << n.pair << "'" << *r << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + } + + try + { + s.insert (value_traits<T>::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 <typename T> + void + set_assign (value& v, names&& ns, const variable* var) + { + if (v) + v.as<set<T>> ().clear (); + + set_append<T> (v, move (ns), var); + } + + template <typename T> + names_view + set_reverse (const value& v, names& s, bool) + { + auto& sv (v.as<set<T>> ()); + s.reserve (sv.size ()); + + for (const T& x: sv) + s.push_back (value_traits<T>::reverse (x)); + + return s; + } + + template <typename T> + int + set_compare (const value& l, const value& r) + { + auto& ls (l.as<set<T>> ()); + auto& rs (r.as<set<T>> ()); + + 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<T>::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 <typename T> + 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<T> (move (sub)); + } + catch (const invalid_argument& e) + { + fail (sloc) << "invalid " << value_traits<set<T>>::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<set<T>> ()); + 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 <typename T> + struct set_value_type: value_type + { + string type_name; + + set_value_type (value_type&& v) + : value_type (move (v)) + { + // set<T> + // + type_name = "set<"; + type_name += value_traits<T>::type_name; + type_name += '>'; + name = type_name.c_str (); + } + }; + + // Convenience aliases for certain set<T> cases. + // + template <> + struct set_value_type<string>: value_type + { + set_value_type (value_type&& v) + : value_type (move (v)) + { + name = "string_set"; + } + }; + + template <typename T> + const set<T> value_traits<set<T>>::empty_instance; + + template <typename T> + const set_value_type<T> + value_traits<set<T>>::value_type = build2::value_type // VC14 wants =. + { + nullptr, // Patched above. + sizeof (set<T>), + nullptr, // No base. + true, // Container. + &value_traits<T>::value_type, // Element type. + &default_dtor<set<T>>, + &default_copy_ctor<set<T>>, + &default_copy_assign<set<T>>, + &set_assign<T>, + &set_append<T>, + &set_append<T>, // Prepend the same as append. + &set_reverse<T>, + nullptr, // No cast (cast data_ directly). + &set_compare<T>, + &default_empty<set<T>>, + &set_subscript<T>, + nullptr // Iterate. + }; + // map<K, V> value // template <typename K, typename V> diff --git a/tests/function/string/testscript b/tests/function/string/testscript index a363cc3..96f5c52 100644 --- a/tests/function/string/testscript +++ b/tests/function/string/testscript @@ -46,7 +46,8 @@ $* <'print $size([string] abc)' >'3' : basics $* <'print $size([string] )' >'0' : zero $* <'print $size([strings] a b c)' >'3' : strings - $* <'print $size([string_map] a@1 b@2 c@3)' >'3' : string_map + $* <'print $size([string_set] a b b)' >'2' : string-set + $* <'print $size([string_map] a@1 b@2 b@3)' >'2' : string-map } : find diff --git a/tests/type/set/buildfile b/tests/type/set/buildfile new file mode 100644 index 0000000..55b37bb --- /dev/null +++ b/tests/type/set/buildfile @@ -0,0 +1,4 @@ +# file : tests/type/set/buildfile +# license : MIT; see accompanying LICENSE file + +./: testscript $b diff --git a/tests/type/set/testscript b/tests/type/set/testscript new file mode 100644 index 0000000..3897220 --- /dev/null +++ b/tests/type/set/testscript @@ -0,0 +1,52 @@ +# file : tests/type/set/testscript +# license : MIT; see accompanying LICENSE file + +# See also tests in function/*/ (size()). + +.include ../../common.testscript + +: basics +: +$* <<EOI >>EOO +s = [string_set] a b a +print $s +s += c b +print $s +s =+ d b +print $s +EOI +a b +a b c +a b c d +EOO + +: type +: +$* <<EOI >>EOO +s = [string_set] +print $type($s) +EOI +string_set +EOO + +: subscript +: +$* <<EOI >>EOO +s = [string_set] a b c +print ($s[b]) +print ($s[z]) +EOI +true +false +EOO + +: iteration +: +$* <<EOI >>EOO +for s: [string_set] a b c + print $s +EOI +a +b +c +EOO |