aboutsummaryrefslogtreecommitdiff
path: root/build2/variable.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-03-28 09:14:31 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-03-28 16:03:35 +0200
commit3ce44330cca9dbc4314feebb27403ebc3175b6c2 (patch)
tree8c1f27442f3b2dafaec3ba50baaca3d5fd63dca7 /build2/variable.cxx
parent9d0d078ff297138622cd2f3f1076f5984395e42b (diff)
New variable architecture
Diffstat (limited to 'build2/variable.cxx')
-rw-r--r--build2/variable.cxx604
1 files changed, 362 insertions, 242 deletions
diff --git a/build2/variable.cxx b/build2/variable.cxx
index ce06d3e..c15a773 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -4,6 +4,8 @@
#include <build2/variable>
+#include <cstring> // memcmp()
+
#include <build2/diagnostics>
using namespace std;
@@ -12,23 +14,50 @@ namespace build2
{
// value
//
- void
- assign (value& v, const value_type* t, const variable& var)
+ value& value::
+ operator= (nullptr_t)
{
- if (v.type == nullptr)
+ if (!null ())
{
- v.type = t;
+ if (type == nullptr)
+ as<names> ().~names ();
+ else if (type->dtor != nullptr)
+ type->dtor (*this);
- if (v && t->assign != nullptr)
- v.state_ = t->assign (v.data_, var)
- ? value::state_type::filled
- : value::state_type::empty;
+ state = value_state::null;
+ }
+
+ return *this;
+ }
+
+ value::
+ value (value&& v)
+ : type (v.type), state (v.state)
+ {
+ if (!null ())
+ {
+ if (type == nullptr)
+ as<names> () = move (v).as<names> ();
+ else if (type->copy_ctor != nullptr)
+ type->copy_ctor (*this, v, true);
+ else
+ data_ = v.data_; // Copy as POD.
+ }
+ }
+
+ value::
+ value (const value& v)
+ : type (v.type), state (v.state)
+ {
+ if (!null ())
+ {
+ if (type == nullptr)
+ as<names> () = v.as<names> ();
+ else if (type->copy_ctor != nullptr)
+ type->copy_ctor (*this, v, false);
+ else
+ data_ = v.data_; // Copy as POD.
}
- else
- fail << "variable '" << var.name << "' type mismatch" <<
- info << "value '" << v.data_ << "' is " << v.type->name <<
- info << (t == var.type ? "variable" : "new type") << " is "
- << (var.type != nullptr ? var.type->name : "untyped");
}
value& value::
@@ -36,152 +65,264 @@ namespace build2
{
assert (type == nullptr || type == v.type);
- // Since the types are the same, we don't need to call
- // the callbacks.
- //
- type = v.type;
- state_ = v.state_;
- data_ = move (v.data_);
+ if (this != &v)
+ {
+ // Prepare the receiving value.
+ //
+ if (type == nullptr && v.type != nullptr)
+ {
+ if (!null ())
+ *this = nullptr;
- return *this;
- }
+ type = v.type;
+ }
+
+ // Now our types are the same. If the receiving value is NULL, then call
+ // copy_ctor() instead of copy_assign().
+ //
+ if (type == nullptr)
+ as<names> () = move (v).as<names> ();
+ else if (auto f = null () ? type->copy_ctor : type->copy_assign)
+ f (*this, v, true);
+ else
+ data_ = v.data_; // Assign as POD.
+
+ state = v.state;
+ }
- value& value::
- append (value v, const variable& var)
- {
- assert (type == v.type);
- append (move (v.data_), var);
return *this;
}
value& value::
- prepend (value v, const variable& var)
+ operator= (const value& v)
{
- assert (type == v.type);
- prepend (move (v.data_), var);
+ assert (type == nullptr || type == v.type);
+
+ if (this != &v)
+ {
+ // Prepare the receiving value.
+ //
+ if (type == nullptr && v.type != nullptr)
+ {
+ if (!null ())
+ {
+ reinterpret_cast<names&> (data_).~names ();
+ state = value_state::null;
+ }
+
+ type = v.type;
+ }
+
+ // Now our types are the same. If the receiving value is NULL, then call
+ // copy_ctor() instead of copy_assign().
+ //
+ if (type == nullptr)
+ as<names> () = v.as<names> ();
+ else if (auto f = null () ? type->copy_ctor : type->copy_assign)
+ f (*this, v, false);
+ else
+ data_ = v.data_; // Assign as POD.
+
+ state = v.state;
+ }
+
return *this;
}
void value::
- append (names v, const variable& var)
+ assign (names&& ns, const variable& var)
{
- // Treat append to NULL as assign.
- //
- if (!null () && type != nullptr && type->append != nullptr)
+ assert (type == nullptr || type->assign != nullptr);
+
+ bool r;
+
+ if (type == nullptr)
{
- state_ = type->append (data_, move (v), var)
- ? state_type::filled
- : state_type::empty;
- return;
- }
+ names* p;
+
+ if (null ())
+ p = new (&data_) names (move (ns));
+ else
+ {
+ p = &as<names> ();
+ *p = move (ns);
+ }
- if (data_.empty ())
- data_ = move (v);
+ r = !p->empty ();
+ }
else
- data_.insert (data_.end (),
- make_move_iterator (v.begin ()),
- make_move_iterator (v.end ()));
-
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
+ r = type->assign (*this, move (ns), var);
+
+ state = r ? value_state::filled : value_state::empty;
}
void value::
- prepend (names v, const variable& var)
+ append (names&& ns, const variable& var)
{
- // Reduce to append.
- //
- if (!null () && type != nullptr && type->append != nullptr)
+ bool r;
+
+ if (type == nullptr)
{
- state_ = type->append (v, move (data_), var)
- ? state_type::filled
- : state_type::empty;
- swap (data_, v);
- return;
- }
+ names* p;
- if (data_.empty ())
- data_ = move (v);
+ if (null ())
+ p = new (&data_) names (move (ns));
+ else
+ {
+ p = &as<names> ();
+
+ if (p->empty ())
+ *p = move (ns);
+ else if (!ns.empty ())
+ {
+ p->insert (p->end (),
+ make_move_iterator (ns.begin ()),
+ make_move_iterator (ns.end ()));
+ }
+ }
+
+ r = !p->empty ();
+ }
else
{
- v.insert (v.end (),
- make_move_iterator (data_.begin ()),
- make_move_iterator (data_.end ()));
- swap (data_, v);
+ if (type->append == nullptr)
+ fail << type->name << " value in variable " << var.name
+ << " cannot be appended to";
+
+ r = type->append (*this, move (ns), var);
}
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
+ state = r ? value_state::filled : value_state::empty;
}
- // bool value
- //
- bool value_traits<bool>::
- assign (name& n, name* r)
+ void value::
+ prepend (names&& ns, const variable& var)
{
- if (r == nullptr && n.simple ())
+ bool r;
+
+ if (type == nullptr)
{
- const string& s (n.value);
+ names* p;
- if (s == "true" || s == "false")
- return true;
+ if (null ())
+ p = new (&data_) names (move (ns));
+ else
+ {
+ p = &as<names> ();
+
+ if (p->empty ())
+ *p = move (ns);
+ else if (!ns.empty ())
+ {
+ ns.insert (ns.end (),
+ make_move_iterator (p->begin ()),
+ make_move_iterator (p->end ()));
+ p->swap (ns);
+ }
+ }
+
+ r = !p->empty ();
+ }
+ else
+ {
+ if (type->prepend == nullptr)
+ fail << type->name << " value in variable " << var.name
+ << " cannot be prepended to";
+
+ r = type->prepend (*this, move (ns), var);
}
- return false;
+ state = r ? value_state::filled : value_state::empty;
}
- static bool
- bool_assign (names& v, const variable& var)
+ bool
+ operator== (const value& x, const value& y)
{
- // Verify the value is either "true" or "false".
- //
- if (v.size () == 1)
- {
- name& n (v.front ());
+ assert (x.type == y.type);
- if (assign<bool> (n))
- return true;
- }
+ if (x.state != y.state)
+ return false;
+
+ if (x.null ())
+ return true;
+
+ if (x.type == nullptr)
+ return x.as<names> () == y.as<names> ();
- fail << "invalid bool variable '" << var.name << "' value '" << v << "'";
- return false;
+ if (x.type->compare == nullptr)
+ return memcmp (&x.data_, &y.data_, x.type->size) == 0;
+
+ return x.type->compare (x, y) == 0;
}
- static bool
- bool_append (names& v, names a, const variable& var)
+ void
+ typify (value& v, const value_type& t, const variable& var)
{
- // Translate append to OR.
- //
- bool_assign (a, var); // Verify "true" or "false".
+ if (v.type == nullptr)
+ {
+ if (!v.null ())
+ {
+ // Note: the order in which we do things here is important.
+ //
+ names ns (move (v).as<names> ());
+ v = nullptr;
+ v.type = &t;
+ v.assign (move (ns), var);
+ }
+ else
+ v.type = &t;
+ }
+ else if (v.type != &t)
+ {
+ fail << "variable " << var.name << " type mismatch" <<
+ info << "value type is " << v.type->name <<
+ info << (&t == var.type ? "variable" : "new") << " type is " << t.name;
+ }
+ }
- if (a.front ().value[0] == 't' && v.front ().value[0] == 'f')
- v = move (a);
+ // bool value
+ //
+ bool value_traits<bool>::
+ convert (name&& n, name* r)
+ {
+ if (r == nullptr && n.simple ())
+ {
+ const string& s (n.value);
+
+ if (s == "true")
+ return true;
+
+ if (s == "false")
+ return false;
+
+ // Fall through.
+ }
- return true;
+ throw invalid_argument (string ());
}
const value_type value_traits<bool>::value_type
{
"bool",
- &bool_assign,
- &bool_append
+ sizeof (bool),
+ nullptr, // No dtor (POD).
+ nullptr, // No copy_ctor (POD).
+ nullptr, // No copy_assign (POD).
+ &simple_assign<bool, false>, // No empty value.
+ &simple_append<bool, false>,
+ &simple_append<bool, false>, // Prepend same as append.
+ &simple_reverse<bool>,
+ nullptr, // No cast (cast data_ directly).
+ nullptr // No compare (compare as POD).
};
- const value_type* bool_type = &value_traits<bool>::value_type;
-
// string value
//
- bool value_traits<string>::
- assign (name& n, name* r)
+ string value_traits<string>::
+ convert (name&& n, name* r)
{
// The goal is to reverse the name into its original representation. The
- // code is a bit convoluted because we try to avoid extra allocation for
+ // code is a bit convoluted because we try to avoid extra allocations for
// the common cases (unqualified, unpaired simple name or directory).
//
@@ -189,215 +330,194 @@ namespace build2
//
if (!(n.simple (true) || n.directory (true)) ||
!(r == nullptr || r->simple (true) || r->directory (true)))
- return false;
+ throw invalid_argument (string ());
+
+ string s;
if (n.directory (true))
{
- n.value = move (n.dir).string (); // Move string out of path.
+ s = move (n.dir).string (); // Move string out of path.
// Add / back to the end of the path unless it is already there. Note
// that the string cannot be empty (n.directory () would have been
// false).
//
- if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
- n.value += '/';
+ if (!dir_path::traits::is_separator (s[s.size () - 1]))
+ s += '/';
}
+ else
+ s.swap (n.value);
// Convert project qualification to its string representation.
//
if (n.qualified ())
{
- string s (*n.proj);
- s += '%';
- s += n.value;
- s.swap (n.value);
+ string p (*n.proj);
+ p += '%';
+ p += s;
+ p.swap (s);
}
// The same for the RHS of a pair, if we have one.
//
if (r != nullptr)
{
- n.value += '@';
+ s += '@';
if (r->qualified ())
{
- n.value += *r->proj;
- n.value += '%';
+ s += *r->proj;
+ s += '%';
}
if (r->directory (true))
{
- n.value += r->dir.string ();
+ s += r->dir.string ();
- if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
- n.value += '/';
+ if (!dir_path::traits::is_separator (s[s.size () - 1]))
+ s += '/';
}
else
- n.value += r->value;
- }
-
- return true;
- }
-
- static bool
- string_assign (names& v, const variable& var)
- {
- // Verify/convert the value is/to a single simple name.
- //
- if (v.empty ())
- {
- v.emplace_back (name ()); // Canonical empty string representation.
- return false;
- }
- else if (v.size () == 1)
- {
- name& n (v.front ());
-
- if (assign<string> (n))
- return !n.value.empty ();
+ s += r->value;
}
- fail << "invalid string variable '" << var.name << "' value '" << v << "'";
- return false;
- }
-
- static bool
- string_append (names& v, names a, const variable& var)
- {
- // Translate append to string concatenation.
- //
- string_assign (a, var); // Verify/convert value is/to string.
-
- if (v.front ().value.empty ())
- v = move (a);
- else
- v.front ().value += a.front ().value;
-
- return !v.front ().value.empty ();
+ return s;
}
const value_type value_traits<string>::value_type
{
"string",
- &string_assign,
- &string_append
+ sizeof (string),
+ &default_dtor<string>,
+ &default_copy_ctor<string>,
+ &default_copy_assign<string>,
+ &simple_assign<string, true>, // Allow empty strings.
+ &simple_append<string, true>,
+ &simple_prepend<string, true>,
+ &simple_reverse<string>,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<string>
};
- const value_type* string_type = &value_traits<string>::value_type;
-
// dir_path value
//
- bool value_traits<dir_path>::
- assign (name& n, name* r)
+ dir_path value_traits<dir_path>::
+ convert (name&& n, name* r)
{
- if (r != nullptr)
- return false;
-
- if (n.directory ())
- return true;
-
- if (n.simple ())
+ if (r == nullptr)
{
- try
+ if (n.directory ())
+ return move (n.dir);
+
+ if (n.simple ())
{
- n.dir = n.empty () ? dir_path () : dir_path (move (n.value));
- n.value.clear ();
- return true;
+ try
+ {
+ return dir_path (move (n.value));
+ }
+ catch (const invalid_path&) {} // Fall through.
}
- catch (const invalid_path&) {} // Fall through.
+
+ // Fall through.
}
- return false;
+ throw invalid_argument (string ());
}
- static bool
- dir_path_assign (names& v, const variable& var)
+ const value_type value_traits<dir_path>::value_type
{
- // Verify/convert the value is/to a single directory name.
- //
- if (v.empty ())
- {
- v.emplace_back (dir_path ()); // Canonical empty path representation.
- return false;
- }
- else if (v.size () == 1)
- {
- name& n (v.front ());
+ "dir_path",
+ sizeof (dir_path),
+ &default_dtor<dir_path>,
+ &default_copy_ctor<dir_path>,
+ &default_copy_assign<dir_path>,
+ &simple_assign<dir_path, true>, // Allow empty paths.
+ &simple_append<dir_path, true>,
+ &simple_prepend<dir_path, true>,
+ &simple_reverse<dir_path>,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<dir_path>
+ };
- if (assign<dir_path> (n))
- return !n.dir.empty ();
- }
+ // name value
+ //
+ name value_traits<name>::
+ convert (name&& n, name* r)
+ {
+ if (r != nullptr)
+ throw invalid_argument (string ());
- fail << "invalid dir_path variable '" << var.name << "' "
- << "value '" << v << "'";
- return false;
+ return move (n);
}
- static bool
- dir_path_append (names& v, names a, const variable& var)
+ static names_view
+ name_reverse (const value& v, names&)
{
- // Translate append to path concatenation.
- //
- dir_path_assign (a, var); // Verify/convert value is/to dir_path.
-
- dir_path& d (a.front ().dir);
- if (d.relative ())
- return !(v.front ().dir /= d).empty ();
- else
- fail << "append of absolute path '" << d << "' to dir_path variable "
- << var.name;
-
- return false;
+ return names_view (&v.as<name> (), 1);
}
- const value_type value_traits<dir_path>::value_type
+ const value_type value_traits<name>::value_type
{
- "dir_path",
- &dir_path_assign,
- &dir_path_append
+ "name",
+ sizeof (name),
+ &default_dtor<name>,
+ &default_copy_ctor<name>,
+ &default_copy_assign<name>,
+ &simple_assign<name, true>, // Allow empty names.
+ nullptr, // Append not supported.
+ nullptr, // Prepend not supported.
+ &name_reverse,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<name>
};
- const value_type* dir_path_type = &value_traits<dir_path>::value_type;
+ // variable_pool
+ //
+ variable_pool var_pool;
- // name value
+ // variable_map
//
- static bool
- name_assign (names& v, const variable& var)
+ const value* variable_map::
+ find (const variable& var) const
{
- // Verify the value is a single name.
+ auto i (m_.find (var));
+ const value* r (i != m_.end () ? &i->second : nullptr);
+
+ // First access after being assigned a type?
//
- if (v.size () == 1)
- return v.front ().empty ();
+ if (r != nullptr && var.type != nullptr && r->type != var.type)
+ typify (const_cast<value&> (*r), *var.type, var);
- fail << "invalid string variable '" << var.name << "' value '" << v << "'";
- return false;
+ return r;
}
- static bool
- name_append (names&, names, const variable& var)
+ value* variable_map::
+ find (const variable& var)
{
- fail << "append to name variable '" << var.name << "'";
- return false;
+ auto i (m_.find (var));
+ value* r (i != m_.end () ? &i->second : nullptr);
+
+ // First access after being assigned a type?
+ //
+ if (r != nullptr && var.type != nullptr && r->type != var.type)
+ typify (*r, *var.type, var);
+
+ return r;
}
- const value_type value_traits<name>::value_type
+ pair<reference_wrapper<value>, bool> variable_map::
+ assign (const variable& var)
{
- "name",
- &name_assign,
- &name_append
- };
+ auto r (m_.emplace (var, value (var.type)));
+ value& v (r.first->second);
- const value_type* name_type = &value_traits<name>::value_type;
-
- // vector<T> value
- //
- const value_type* strings_type = &value_traits<strings>::value_type;
- const value_type* dir_paths_type = &value_traits<dir_paths>::value_type;
- const value_type* names_type = &value_traits<names>::value_type;
+ // First access after being assigned a type?
+ //
+ if (!r.second && var.type != nullptr && v.type != var.type)
+ typify (v, *var.type, var);
- // variable_set
- //
- variable_pool var_pool;
+ return make_pair (reference_wrapper<value> (v), r.second);
+ }
// variable_type_map
//