From 9fb791e9fad6c63fc1dac49f4d05ae63b8a3db9b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 5 Jan 2016 11:55:15 +0200 Subject: Rename build directory/namespace to build2 --- build2/variable.cxx | 452 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 build2/variable.cxx (limited to 'build2/variable.cxx') diff --git a/build2/variable.cxx b/build2/variable.cxx new file mode 100644 index 0000000..27555ba --- /dev/null +++ b/build2/variable.cxx @@ -0,0 +1,452 @@ +// file : build2/variable.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include // make_move_iterator() + +#include +#include + +using namespace std; + +namespace build2 +{ + // 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; + } + + value& value:: + prepend (value v, const variable& var) + { + assert (type == v.type); + prepend (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 ())); + + state_ = (type != nullptr && type->assign != nullptr + ? type->assign (data_, var) + : !data_.empty ()) + ? state_type::filled + : state_type::empty; + } + + void value:: + prepend (names v, const variable& var) + { + // Reduce to append. + // + if (!null () && type != nullptr && type->append != nullptr) + { + state_ = type->append (v, move (data_), var) + ? state_type::filled + : state_type::empty; + swap (data_, v); + return; + } + + if (data_.empty ()) + data_ = move (v); + else + { + v.insert (v.end (), + make_move_iterator (data_.begin ()), + make_move_iterator (data_.end ())); + swap (data_, v); + } + + state_ = (type != nullptr && type->assign != nullptr + ? type->assign (data_, var) + : !data_.empty ()) + ? state_type::filled + : state_type::empty; + } + + // bool value + // + bool value_traits:: + assign (name& n) + { + if (n.simple ()) + { + const string& s (n.value); + + if (s == "true" || s == "false") + return true; + } + + return false; + } + + static bool + bool_assign (names& v, const variable& var) + { + // Verify the value is either "true" or "false". + // + if (v.size () == 1) + { + name& n (v.front ()); + + 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". + + if (a.front ().value[0] == 't' && v.front ().value[0] == 'f') + v = move (a); + + return true; + } + + const value_type value_traits::value_type + { + "bool", + &bool_assign, + &bool_append + }; + + const value_type* bool_type = &value_traits::value_type; + + // string value + // + bool value_traits:: + assign (name& n) + { + // The below code is quite convoluted because we don't want to + // modify the name until we know it good (if it is not, then it + // will most likely be printed by the caller in diagnostics). + + // Suspend project qualification. + // + const string* p (n.proj); + n.proj = nullptr; + + // 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 += '/'; + } + + if (!n.simple ()) + { + n.proj = p; // Restore. + return false; + } + + // Convert project qualification to its string representation. + // + if (p != nullptr) + { + string s (*p); + s += '%'; + s += n.value; + s.swap (n.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 (value_traits::assign (n)) + return !n.value.empty (); + } + + 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 (); + } + + 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) + { + if (n.directory ()) + return true; + + 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 + 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_pool var_pool; + + // variable_type_map + // + lookup variable_type_map:: + lookup (const target_type& type, + const string& name, + const variable& var) const + { + using result = build2::lookup; + + // Search across target type hierarchy. + // + for (auto tt (&type); tt != nullptr; tt = tt->base) + { + auto i (find (*tt)); + + if (i == end ()) + continue; + + // Try to match the pattern, starting from the longest values + // so that the more "specific" patterns (i.e., those that cover + // fewer characters with the wildcard) take precedence. See + // tests/variable/type-pattern. + // + const variable_pattern_map& m (i->second); + + for (auto j (m.rbegin ()); j != m.rend (); ++j) + { + const string& p (j->first); + + size_t nn (name.size ()); + size_t pn (p.size ()); + + if (nn < pn - 1) // One for '*'. + continue; + + size_t w (p.find ('*')); + assert (w != string::npos); + + // Compare prefix. + // + if (w != 0 && + name.compare (0, w, p, 0, w) != 0) + continue; + + ++w; // First suffix character. + pn -= w; // Suffix length. + + // Compare suffix. + // + if (pn != 0 && + name.compare (nn - pn, pn, p, w, pn) != 0) + continue; + + // Ok, this pattern matches. But is there a variable? + // + if (const value* v = j->second.find (var)) + { + //@@ TODO: should we detect ambiguity? 'foo-*' '*-foo' and + // 'foo-foo'? Right now the last defined will be used. + // + return result (v, &j->second); + } + } + } + + return result (); + } +} -- cgit v1.1