From 68f96f9213e849d0d7c4cedf3edeaec99743ee27 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 24 Aug 2015 09:51:15 +0200 Subject: New variable architecture --- build/variable.cxx | 333 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 282 insertions(+), 51 deletions(-) (limited to 'build/variable.cxx') diff --git a/build/variable.cxx b/build/variable.cxx index eab77e5..d215b02 100644 --- a/build/variable.cxx +++ b/build/variable.cxx @@ -4,86 +4,317 @@ #include +#include // make_move_iterator() + #include +#include using namespace std; namespace build { - variable_set variable_pool; + // value + // + void + assign (value& v, const value_type* t, const variable& var) + { + if (v.type == nullptr) + { + v.type = t; + + if (v && t->assign != nullptr) + v.state_ = t->assign (v.data_, var) + ? value::state_type::filled + : value::state_type::empty; + } + 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:: + operator= (value&& v) + { + 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_); + + return *this; + } + + value& value:: + append (value v, const variable& var) + { + assert (type == v.type); + append (move (v.data_), var); + return *this; + } + + void value:: + append (names v, const variable& var) + { + // Treat append to NULL as assign. + // + if (!null () && type != nullptr && type->append != nullptr) + { + state_ = type->append (data_, move (v), var) + ? state_type::filled + : state_type::empty; + return; + } + + if (data_.empty ()) + data_ = move (v); + else + data_.insert (data_.end (), + make_move_iterator (v.begin ()), + make_move_iterator (v.end ())); - // value_proxy + state_ = (type != nullptr && type->assign != nullptr + ? type->assign (data_, var) + : !data_.empty ()) + ? state_type::filled + : state_type::empty; + } + + // bool value // - template <> - string& value_proxy:: - as () const - { - list_value& lv (as ()); - assert (lv.size () == 1); - name& n (lv.front ()); - assert (n.simple ()); - return n.value; + bool value_traits:: + assign (name& n) + { + if (n.simple ()) + { + const string& s (n.value); + + if (s == "true" || s == "false") + return true; + } + + return false; } - template <> - const string& value_proxy:: - as () const + static bool + bool_assign (names& v, const variable& var) { - const list_value& lv (as ()); - assert (lv.size () < 2); + // Verify the value is either "true" or "false". + // + if (v.size () == 1) + { + name& n (v.front ()); - if (lv.empty ()) - return empty_string; + if (value_traits::assign (n)) + return true; + } + + fail << "invalid bool variable '" << var.name << "' value '" << v << "'"; + return false; + } + + static bool + bool_append (names& v, names a, const variable& var) + { + // Translate append to OR. + // + bool_assign (a, var); // Verify "true" or "false". - const name& n (lv.front ()); + if (a.front ().value[0] == 't' && v.front ().value[0] == 'f') + v = move (a); - assert (n.simple ()); - return n.value; + return true; } - template <> - dir_path& value_proxy:: - as () const + const value_type value_traits::value_type { - list_value& lv (as ()); - assert (lv.size () == 1); - name& n (lv.front ()); - assert (n.directory ()); - return n.dir; + "bool", + &bool_assign, + &bool_append + }; + + const value_type* bool_type = &value_traits::value_type; + + // string value + // + bool value_traits:: + assign (name& n) + { + // Convert directory to string. + // + if (n.directory ()) + { + n.value = std::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 += '/'; + } + + return n.simple (); } - template <> - const dir_path& value_proxy:: - as () const + static bool + string_assign (names& v, const variable& var) { - const list_value& lv (as ()); - assert (lv.size () < 2); + // 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 (lv.empty ()) - return empty_dir_path; + if (value_traits::assign (n)) + return !n.value.empty (); + } + + fail << "invalid string variable '" << var.name << "' value '" << v << "'"; + return false; + } - const name& n (lv.front ()); + 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 (n.empty ()) - return empty_dir_path; + if (v.front ().value.empty ()) + v = move (a); + else + v.front ().value += a.front ().value; - assert (n.directory ()); - return n.dir; + return !v.front ().value.empty (); } - template <> - bool value_proxy:: - as () const + const value_type value_traits::value_type + { + "string", + &string_assign, + &string_append + }; + + const value_type* string_type = &value_traits::value_type; + + // dir_path value + // + bool value_traits:: + assign (name& n) { - const list_value& lv (as ()); - assert (lv.size () == 1); - const name& n (lv.front ()); - assert (n.simple ()); - if (n.value == "true") + if (n.directory ()) return true; - else if (n.value == "false") + + if (n.simple ()) + { + try + { + n.dir = n.empty () ? dir_path () : dir_path (move (n.value)); + n.value.clear (); + return true; + } + catch (const invalid_path&) {} // Fall through. + } + + return false; + } + + static bool + dir_path_assign (names& v, const variable& var) + { + // 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 ()); + + if (value_traits::assign (n)) + return !n.dir.empty (); + } + + fail << "invalid dir_path variable '" << var.name << "' " + << "value '" << v << "'"; + return false; + } + + static bool + dir_path_append (names& v, names a, const variable& var) + { + // 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 - assert (false); // Bool value should be 'true' or 'false'. + fail << "append of absolute path '" << d << "' to dir_path variable " + << var.name; + + return false; } + + const value_type value_traits::value_type + { + "dir_path", + &dir_path_assign, + &dir_path_append + }; + + const value_type* dir_path_type = &value_traits::value_type; + + // name value + // + static bool + name_assign (names& v, const variable& var) + { + // Verify the value is a single name. + // + if (v.size () == 1) + return v.front ().empty (); + + fail << "invalid string variable '" << var.name << "' value '" << v << "'"; + return false; + } + + static bool + name_append (names&, names, const variable& var) + { + fail << "append to name variable '" << var.name << "'"; + return false; + } + + const value_type value_traits::value_type + { + "name", + &name_assign, + &name_append + }; + + const value_type* name_type = &value_traits::value_type; + + // vector value + // + const value_type* strings_type = &value_traits::value_type; + const value_type* dir_paths_type = &value_traits::value_type; + const value_type* names_type = &value_traits::value_type; + + // variable_set + // + variable_set variable_pool; } -- cgit v1.1