aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-02-19 12:23:10 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-02-20 16:01:40 +0200
commit88640e677fa0695783eac68014d7d8d5bc42d117 (patch)
treeb1de045dc2d8d290422aaebbfbdbe184e8074dfd
parentbed6b6a9170253e010cbffd59202add4edfd1c2b (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.cxx6
-rw-r--r--libbuild2/parser.cxx1
-rw-r--r--libbuild2/variable.cxx2
-rw-r--r--libbuild2/variable.hxx22
-rw-r--r--libbuild2/variable.ixx38
-rw-r--r--libbuild2/variable.txx223
-rw-r--r--tests/function/string/testscript3
-rw-r--r--tests/type/set/buildfile4
-rw-r--r--tests/type/set/testscript52
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