aboutsummaryrefslogtreecommitdiff
path: root/build2/variable
diff options
context:
space:
mode:
Diffstat (limited to 'build2/variable')
-rw-r--r--build2/variable831
1 files changed, 831 insertions, 0 deletions
diff --git a/build2/variable b/build2/variable
new file mode 100644
index 0000000..6304979
--- /dev/null
+++ b/build2/variable
@@ -0,0 +1,831 @@
+// file : build2/variable -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_VARIABLE
+#define BUILD2_VARIABLE
+
+#include <map>
+#include <vector>
+#include <cstddef> // nullptr_t
+#include <utility> // pair, make_pair()
+#include <iterator>
+#include <functional> // hash
+#include <type_traits> // conditional, is_reference, remove_reference, etc.
+#include <unordered_set>
+
+#include <butl/prefix-map>
+
+#include <build2/types>
+#include <build2/utility>
+
+#include <build2/target-type>
+
+namespace build2
+{
+ struct variable;
+
+ // If assign is NULL, then the value is assigned as is. If append
+ // is NULL, then the names are appended to the end of the value
+ // and assign is called, if not NULL. Variable is provided primarily
+ // for diagnostics. Return true if the resulting value is not empty.
+ //
+ struct value_type
+ {
+ const char* name;
+ bool (*const assign) (names&, const variable&);
+ bool (*const append) (names&, names, const variable&);
+ };
+
+ enum class variable_visibility
+ {
+ scope, // This scope (no outer scopes).
+ project, // This project (no outer projects).
+ normal // All outer scopes.
+ };
+
+ // variable
+ //
+ // The two variables are considered the same if they have the same name.
+ //
+ struct variable
+ {
+ std::string name;
+ const value_type* type; // If NULL, then not (yet) typed.
+ variable_visibility visibility;
+ char pairs; // Pair symbold or '\0' if not used.
+ };
+
+ inline bool
+ operator== (const variable& x, const variable& y) {return x.name == y.name;}
+
+ typedef std::reference_wrapper<const variable> variable_cref;
+
+ // value
+ //
+ class value
+ {
+ public:
+ // By default we create NULL value.
+ //
+ explicit value (const value_type* t = nullptr)
+ : type (t), state_ (state_type::null) {}
+
+ value (value&&) = default;
+
+ value&
+ operator= (std::nullptr_t)
+ {
+ data_.clear ();
+ state_ = state_type::null;
+ return *this;
+ }
+
+ value&
+ operator= (value&&);
+
+ value&
+ operator= (const value& v)
+ {
+ if (&v != this)
+ *this = value (v);
+ return *this;
+ }
+
+ value&
+ operator= (reference_wrapper<value> v) {return *this = v.get ();}
+
+ value&
+ operator= (reference_wrapper<const value> v) {return *this = v.get ();}
+
+ value&
+ append (value, const variable&); // Aka operator+=().
+
+ value&
+ prepend (value, const variable&); // Aka operator=+().
+
+ // Forwarded to the representation type's assign()/append() (see below).
+ //
+ template <typename T> value& operator= (T);
+ value& operator= (const char* v) {return *this = std::string (v);}
+
+ template <typename T> value& operator+= (T);
+ value& operator+= (const char* v) {return *this += std::string (v);}
+
+ private:
+ explicit value (const value&) = default;
+
+ public:
+ const value_type* type; // NULL means this value is not (yet) typed.
+
+ bool null () const {return state_ == state_type::null;}
+ bool empty () const {return state_ == state_type::empty;}
+
+ explicit operator bool () const {return !null ();}
+ bool operator== (std::nullptr_t) const {return null ();}
+ bool operator!= (std::nullptr_t) const {return !null ();}
+
+ // Raw data read interface.
+ //
+ using const_iterator = names::const_iterator;
+
+ const_iterator begin () const {return data_.begin ();}
+ const_iterator end () const {return data_.end ();}
+
+ // Raw data write interface. Note that it triggers callbacks for
+ // typed values. Variable is passed for diagnostics.
+ //
+ void
+ assign (names, const variable&);
+
+ void
+ append (names, const variable&);
+
+ void
+ prepend (names, const variable&);
+
+ public:
+ // Don't use directly except in the implementation of representation
+ // types, in which case take care to update state.
+ //
+ enum class state_type {null, empty, filled} state_;
+ names data_;
+ };
+
+ //@@ Right now we assume that for each value type each value has a
+ // unique representation. This is currently not the case for map.
+ //
+ inline bool
+ operator== (const value& x, const value& y)
+ {
+ return x.state_ == y.state_ && x.data_ == y.data_;
+ }
+
+ inline bool
+ operator!= (const value& x, const value& y) {return !(x == y);}
+
+ // lookup
+ //
+ // A variable can be undefined, NULL, or contain a (potentially
+ // empty) value.
+ //
+ struct variable_map;
+
+ template <typename V>
+ struct lookup
+ {
+ V* value;
+ const variable_map* vars;
+
+ bool
+ defined () const {return value != nullptr;}
+
+ // Note: returns true if defined and not NULL.
+ //
+ explicit operator bool () const {return defined () && !value->null ();}
+
+ V& operator* () const {return *value;}
+ V* operator-> () const {return value;}
+
+ // Return true if this value belongs to the specified scope or target.
+ // Note that it can also be a target type/pattern-specific value.
+ //
+ template <typename T>
+ bool
+ belongs (const T& x) const {return vars == &x.vars;}
+
+ lookup (): value (nullptr), vars (nullptr) {}
+ lookup (V* v, const variable_map* vs)
+ : value (v), vars (v != nullptr ? vs : nullptr) {}
+
+ template <typename T>
+ lookup (V& v, const T& x): lookup (&v, &x.vars) {}
+ };
+
+ // Representation types.
+ //
+ template <typename T> struct value_traits;
+
+ // Assign value type to the value.
+ //
+ template <typename T>
+ void assign (value&, const variable&);
+ void assign (value&, const value_type*, const variable&);
+
+ template <typename T> typename value_traits<T>::type as (value&);
+ template <typename T> typename value_traits<T>::const_type as (const value&);
+
+ // "Assign" simple value type to the value stored in name. Return false
+ // if the value is not valid for this type.
+ //
+ template <typename T> bool assign (name&);
+
+ template <typename T> typename value_traits<T>::type as (name&);
+ template <typename T> typename value_traits<T>::const_type as (const name&);
+
+ // bool
+ //
+ template <typename D>
+ struct bool_value
+ {
+ explicit
+ operator bool () const {return d->value[0] == 't';}
+
+ bool_value&
+ operator= (bool v)
+ {
+ d->value = v ? "true" : "false";
+ return *this;
+ }
+
+ bool_value&
+ operator+= (bool v)
+ {
+ if (!*this && v)
+ d->value = "true";
+ return *this;
+ }
+
+ // Implementation details.
+ //
+ public:
+ explicit bool_value (D& d): d (&d) {}
+
+ bool_value (const bool_value&) = delete;
+ bool_value& operator= (const bool_value&) = delete; // Rebind or deep?
+
+ bool_value (bool_value&&) = default;
+ bool_value& operator= (bool_value&&) = delete;
+
+ D* d; // name
+ };
+
+ template <>
+ struct value_traits<bool>
+ {
+ using type = bool_value<name>;
+ using const_type = bool_value<const name>;
+
+ static type as (name& n) {return type (n);}
+ static const_type as (const name& n) {return const_type (n);}
+
+ static type as (value&);
+ static const_type as (const value&);
+
+ static bool assign (name&);
+ static void assign (value&, bool);
+ static void append (value&, bool);
+
+ static const build2::value_type value_type;
+ };
+
+ extern const value_type* bool_type;
+
+ // string
+ //
+ template <>
+ struct value_traits<std::string>
+ {
+ using type = std::string&;
+ using const_type = const std::string&;
+
+ static type as (name& n) {return n.value;}
+ static const_type as (const name& n) {return n.value;}
+
+ static type as (value&);
+ static const_type as (const value&);
+
+ static bool assign (name&);
+ static void assign (value&, std::string);
+ static void append (value&, std::string);
+
+ static const build2::value_type value_type;
+ };
+
+ extern const value_type* string_type;
+
+ // dir_path
+ //
+ template <>
+ struct value_traits<dir_path>
+ {
+ using type = dir_path&;
+ using const_type = const dir_path&;
+
+ static type as (name& n) {return n.dir;}
+ static const_type as (const name& n) {return n.dir;}
+
+ static type as (value&);
+ static const_type as (const value&);
+
+ static bool assign (name&);
+ static void assign (value&, dir_path);
+ static void append (value&, dir_path);
+
+ static const build2::value_type value_type;
+ };
+
+ extern const value_type* dir_path_type;
+
+ // name
+ //
+ template <>
+ struct value_traits<name>
+ {
+ using type = name&;
+ using const_type = const name&;
+
+ static type as (name& n) {return n;}
+ static const_type as (const name& n) {return n;}
+
+ static type as (value&);
+ static const_type as (const value&);
+
+ static bool assign (name&) {return true;}
+ static void assign (value&, name);
+ static void append (value&, name) = delete;
+
+ static const build2::value_type value_type;
+ };
+
+ extern const value_type* name_type;
+
+ // vector<T>
+ //
+ template <typename T, typename D>
+ struct vector_value
+ {
+ using size_type = typename D::size_type;
+
+ using value_type = typename value_traits<T>::type;
+ using const_value_type = typename value_traits<T>::const_type;
+
+ template <typename I, typename V, typename R>
+ struct iterator_impl: I
+ {
+ using value_type = V;
+ using pointer = value_type*;
+ using reference = R;
+ using difference_type = typename I::difference_type;
+
+ iterator_impl () = default;
+ iterator_impl (const I& i): I (i) {}
+
+ // Note: operator->() is unavailable if R is a value.
+ //
+ reference operator* () const {return as<T> (I::operator* ());}
+ pointer operator-> () const {return &as<T> (I::operator* ());}
+ reference operator[] (difference_type n) const
+ {
+ return as<T> (I::operator[] (n));
+ }
+ };
+
+ // Make iterator the same as const_iterator if our data type is const.
+ //
+ using const_iterator =
+ iterator_impl<names::const_iterator, const T, const_value_type>;
+ using iterator = typename std::conditional<
+ std::is_const<D>::value,
+ const_iterator,
+ iterator_impl<names::iterator, T, value_type>>::type;
+
+ public:
+ vector_value&
+ operator= (std::vector<T> v) {assign (std::move (v)); return *this;}
+
+ vector_value&
+ assign (std::vector<T>);
+
+ template <typename D1>
+ vector_value&
+ assign (const vector_value<T, D1>&);
+
+ template <typename D1>
+ vector_value&
+ append (const vector_value<T, D1>&);
+
+ public:
+ bool empty () const {return d->empty ();}
+ size_type size () const {return d->size ();}
+
+ value_type operator[] (size_type i) {return as<T> ((*d)[i]);}
+ const_value_type operator[] (size_type i) const {return as<T> ((*d)[i]);}
+
+ iterator begin () {return iterator (d->begin ());}
+ iterator end () {return iterator (d->end ());}
+
+ const_iterator begin () const {return const_iterator (d->begin ());}
+ const_iterator end () const {return const_iterator (d->end ());}
+
+ const_iterator cbegin () const {return const_iterator (d->begin ());}
+ const_iterator cend () const {return const_iterator (d->end ());}
+
+ // Implementation details.
+ //
+ public:
+ explicit vector_value (D& d): d (&d) {}
+
+ vector_value (const vector_value&) = delete;
+ vector_value& operator= (const vector_value&) = delete; // Rebind or deep?
+
+ vector_value (vector_value&&) = default;
+ vector_value& operator= (vector_value&&) = default; //@@ TMP
+
+ explicit vector_value (std::nullptr_t): d (nullptr) {} //@@ TMP
+
+ D* d; // names
+ };
+
+ template <typename T>
+ struct value_traits<std::vector<T>>
+ {
+ using type = vector_value<T, names>;
+ using const_type = vector_value<T, const names>;
+
+ static type as (value&);
+ static const_type as (const value&);
+
+ template <typename V> static void assign (value&, V);
+ template <typename V> static void append (value&, V);
+
+ static const std::string type_name;
+ static const build2::value_type value_type;
+ };
+
+ template <typename T, typename D>
+ struct value_traits<vector_value<T, D>>: value_traits<std::vector<T>> {};
+
+ using strings_value = vector_value<std::string, names>;
+ using const_strings_value = vector_value<std::string, const names>;
+
+ extern const value_type* strings_type; // vector<string> aka strings
+ extern const value_type* dir_paths_type; // vector<dir_path> aka dir_paths
+ extern const value_type* names_type; // vector<name> aka names
+
+ // map<K, V>
+ //
+ template <typename K, typename V, typename D>
+ struct map_value
+ {
+ template <typename F, typename S>
+ struct pair
+ {
+ using first_type = typename std::conditional<
+ std::is_reference<F>::value,
+ std::reference_wrapper<typename std::remove_reference<F>::type>,
+ F>::type;
+
+ using second_type = typename std::conditional<
+ std::is_reference<S>::value,
+ std::reference_wrapper<typename std::remove_reference<S>::type>,
+ S>::type;
+
+ first_type first;
+ second_type second;
+ };
+
+ template <typename I, typename T>
+ struct iterator_impl
+ {
+ using value_type = T;
+ using pointer = value_type*;
+ using reference = value_type; // Note: value.
+ using difference_type = typename I::difference_type;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ iterator_impl () = default;
+ iterator_impl (const I& i): i_ (i) {}
+
+ pointer operator-> () const = delete;
+ reference operator* () const
+ {
+ return value_type {as<K> (*i_), as<V> (*(i_ + 1))};
+ }
+
+ iterator_impl& operator++ () {i_ += 2; return *this;}
+ iterator_impl operator++ (int) {auto r (*this); operator++ (); return r;}
+
+ iterator_impl& operator-- () {i_ -= 2; return *this;}
+ iterator_impl operator-- (int) {auto r (*this); operator-- (); return r;}
+
+ bool operator== (const iterator_impl& y) const {return i_ == y.i_;}
+ bool operator!= (const iterator_impl& y) const {return i_ != y.i_;}
+
+ private:
+ I i_;
+ };
+
+ using size_type = typename D::size_type;
+
+ using value_type = pair<typename value_traits<K>::const_type,
+ typename value_traits<V>::type>;
+
+ using const_value_type = pair<typename value_traits<K>::const_type,
+ typename value_traits<V>::const_type>;
+
+ // Make iterator the same as const_iterator if our data type is const.
+ //
+ using const_iterator =
+ iterator_impl<names::const_iterator, const_value_type>;
+ using iterator = typename std::conditional<
+ std::is_const<D>::value,
+ const_iterator,
+ iterator_impl<names::iterator, value_type>>::type;
+
+
+ public:
+ map_value&
+ operator= (std::map<K, V> m) {assign (std::move (m)); return *this;}
+
+ map_value&
+ assign (std::map<K, V>);
+
+ bool empty () const {return d->empty ();}
+ size_type size () const {return d->size ();}
+
+ iterator find (const K&);
+ const_iterator find (const K&) const;
+
+ iterator begin () {return iterator (d->begin ());}
+ iterator end () {return iterator (d->end ());}
+
+ const_iterator begin () const {return const_iterator (d->begin ());}
+ const_iterator end () const {return const_iterator (d->end ());}
+
+ // Implementation details.
+ //
+ public:
+ explicit map_value (D& d): d (&d) {}
+
+ map_value (const map_value&) = delete;
+ map_value& operator= (const map_value&) = delete; // Rebind or deep?
+
+ map_value (map_value&&) = default;
+ map_value& operator= (map_value&&) = delete;
+
+ D* d; // names
+ };
+
+ template <typename K, typename V>
+ struct value_traits<std::map<K, V>>
+ {
+ using type = map_value<K, V, names>;
+ using const_type = map_value<K, V, const names>;
+
+ static type as (value&);
+ static const_type as (const value&);
+
+ template <typename M> static void assign (value&, M);
+ template <typename M> static void append (value&, M);
+
+ static const std::string type_name;
+ static const build2::value_type value_type;
+ };
+
+ template <typename K, typename V, typename D>
+ struct value_traits<map_value<K, V, D>>: value_traits<std::map<K, V>> {};
+}
+
+namespace std
+{
+ template <>
+ struct hash<build2::variable>: hash<string>
+ {
+ size_t
+ operator() (const build2::variable& v) const noexcept
+ {
+ return hash<string>::operator() (v.name);
+ }
+ };
+}
+
+namespace butl
+{
+ template <>
+ struct compare_prefix<build2::variable_cref>: compare_prefix<std::string>
+ {
+ typedef compare_prefix<std::string> base;
+
+ explicit
+ compare_prefix (char d): base (d) {}
+
+ bool
+ operator() (const build2::variable& x, const build2::variable& y) const
+ {
+ return base::operator() (x.name, y.name);
+ }
+
+ bool
+ prefix (const build2::variable& p, const build2::variable& k) const
+ {
+ return base::prefix (p.name, k.name);
+ }
+ };
+}
+
+namespace build2
+{
+ // variable_pool
+ //
+ using variable_pool_base = std::unordered_set<variable>;
+ struct variable_pool: private variable_pool_base
+ {
+ const variable&
+ find (string name, const build2::value_type* t = nullptr, char p = '\0')
+ {
+ return find (name, nullptr, t, p);
+ }
+
+ const variable&
+ find (string name,
+ variable_visibility v,
+ const build2::value_type* t = nullptr,
+ char p = '\0')
+ {
+ return find (name, &v, t, p);
+ }
+
+ using variable_pool_base::clear;
+
+ private:
+ const variable&
+ find (string name,
+ const variable_visibility* vv,
+ const build2::value_type* t,
+ char p)
+ {
+ auto r (
+ insert (
+ variable {
+ std::move (name),
+ t,
+ vv != nullptr ? *vv : variable_visibility::normal,
+ p}));
+ const variable& v (*r.first);
+
+ // Update type?
+ //
+ if (!r.second && t != nullptr && v.type != t)
+ {
+ assert (v.type == nullptr);
+ const_cast<variable&> (v).type = t; // Not changing the key.
+ }
+
+ // Change visibility? While this might at first seem like a bad idea,
+ // it can happen that the variable lookup happens before any values
+ // were set, in which case the variable will be entered with the
+ // default visibility.
+ //
+ if (!r.second && vv != nullptr && v.visibility != *vv)
+ {
+ assert (v.visibility == variable_visibility::normal); // Default.
+ const_cast<variable&> (v).visibility = *vv; // Not changing the key.
+ }
+
+ return v;
+ }
+ };
+
+ extern variable_pool var_pool;
+
+ // variable_map
+ //
+ struct variable_map
+ {
+ using map_type = butl::prefix_map<variable_cref, value, '.'>;
+ using size_type = map_type::size_type;
+
+ template <typename I>
+ struct iterator_adapter: I
+ {
+ iterator_adapter () = default;
+ iterator_adapter (const I& i): I (i) {}
+ typename I::reference operator* () const;
+ typename I::pointer operator-> () const;
+ };
+
+ using const_iterator = iterator_adapter<map_type::const_iterator>;
+
+ const value*
+ find (const variable& var) const
+ {
+ auto i (m_.find (var));
+ const 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)
+ build2::assign (const_cast<value&> (*r), var.type, var);
+
+ return r;
+ }
+
+ value*
+ find (const variable& var)
+ {
+ 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)
+ build2::assign (*r, var.type, var);
+
+ return r;
+ }
+
+ lookup<const value>
+ operator[] (const variable& var) const
+ {
+ return lookup<const value> (find (var), this);
+ }
+
+ lookup<const value>
+ operator[] (const std::string& name) const
+ {
+ return operator[] (var_pool.find (name));
+ }
+
+ // Non-const lookup. Only exposed on the map directly.
+ //
+ lookup<value>
+ operator[] (const variable& var)
+ {
+ return lookup<value> (find (var), this);
+ }
+
+ lookup<value>
+ operator[] (const std::string& name)
+ {
+ return operator[] (var_pool.find (name));
+ }
+
+ // The second member in the pair indicates whether the new
+ // value (which will be NULL) was assigned.
+ //
+ std::pair<std::reference_wrapper<value>, bool>
+ assign (const variable& var)
+ {
+ auto r (m_.emplace (var, value (var.type)));
+ value& v (r.first->second);
+
+ // First access after being assigned a type?
+ //
+ if (!r.second && var.type != nullptr && v.type != var.type)
+ build2::assign (v, var.type, var);
+
+ return std::make_pair (std::reference_wrapper<value> (v), r.second);
+ }
+
+ std::pair<std::reference_wrapper<value>, bool>
+ assign (const std::string& name)
+ {
+ return assign (var_pool.find (name));
+ }
+
+ std::pair<const_iterator, const_iterator>
+ find_namespace (const std::string& ns) const
+ {
+ auto r (m_.find_prefix (var_pool.find (ns)));
+ return std::make_pair (const_iterator (r.first),
+ const_iterator (r.second));
+ }
+
+ const_iterator
+ begin () const {return m_.begin ();}
+
+ const_iterator
+ end () const {return m_.end ();}
+
+ bool
+ empty () const {return m_.empty ();}
+
+ size_type
+ size () const {return m_.size ();}
+
+ private:
+ map_type m_;
+ };
+
+ // Target type/pattern-specific variables.
+ //
+ // @@ In quite a few places we assume that we can store a reference
+ // to the returned value (e.g., install::lookup_install()). If
+ // we "instantiate" the value on the fly, then we will need to
+ // consider its lifetime.
+ //
+ using variable_pattern_map = std::map<std::string, variable_map>;
+
+ struct variable_type_map: std::map<std::reference_wrapper<const target_type>,
+ variable_pattern_map>
+ {
+ build2::lookup<const value>
+ lookup (const target_type&, const string& name, const variable&) const;
+ };
+}
+
+#include <build2/variable.ixx>
+#include <build2/variable.txx>
+
+#endif // BUILD2_VARIABLE