From 4372f041bb7401c3adc2d5710566b13f64722102 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 27 Feb 2015 16:57:34 +0200 Subject: Variable assignment, appending support --- build/b.cxx | 2 + build/buildfile | 6 +-- build/diagnostics | 3 +- build/diagnostics.cxx | 3 ++ build/dump | 14 ++++++ build/dump.cxx | 72 ++++++++++++++++++++++++++++++ build/lexer.cxx | 15 ++++++- build/name | 43 ++++++++++++++++++ build/name.cxx | 51 +++++++++++++++++++++ build/parser | 15 +------ build/parser.cxx | 118 ++++++++++++++++++++++++++++++++++++++++++------- build/path | 2 +- build/prerequisite.cxx | 21 +++++++-- build/scope | 15 +++++-- build/scope.cxx | 34 +++++++++++--- build/target | 5 ++- build/target.cxx | 2 +- build/token | 4 +- build/variable | 110 +++++++++++++++++++++++++++++++++++++++++++-- build/variable.cxx | 12 +++++ 20 files changed, 493 insertions(+), 54 deletions(-) create mode 100644 build/dump create mode 100644 build/dump.cxx create mode 100644 build/name create mode 100644 build/name.cxx create mode 100644 build/variable.cxx (limited to 'build') diff --git a/build/b.cxx b/build/b.cxx index 492fdea..659eb40 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -182,6 +183,7 @@ main (int argc, char* argv[]) fail << "failed to read from " << bf; } + dump_scopes (); dump (); // Register rules. diff --git a/build/buildfile b/build/buildfile index 5ae6bf9..d8f683b 100644 --- a/build/buildfile +++ b/build/buildfile @@ -1,3 +1,3 @@ -exe{b1}: cxx{b algorithm scope parser lexer target prerequisite rule \ - native context search diagnostics cxx/target cxx/rule process timestamp \ - path utility mkdir} +exe{b1}: cxx{b algorithm parser lexer name scope variable target prerequisite \ + rule native context search diagnostics cxx/target cxx/rule process \ + timestamp path utility mkdir dump} diff --git a/build/diagnostics b/build/diagnostics index e61295a..270fa7a 100644 --- a/build/diagnostics +++ b/build/diagnostics @@ -24,7 +24,8 @@ namespace build class failed: public std::exception {}; // In addition to calling relative_work(), this function also uses - // shorter notations such as ~/. + // shorter notations such as '~/'. If the path is the same as work, + // it returns '.'. // std::string diag_relative_work (const path&); diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx index e2e8b95..34bbee8 100644 --- a/build/diagnostics.cxx +++ b/build/diagnostics.cxx @@ -18,6 +18,9 @@ namespace build { if (p.absolute ()) { + if (p == work) + return "."; + path rp (relative_work (p)); #ifndef _WIN32 diff --git a/build/dump b/build/dump new file mode 100644 index 0000000..0de7569 --- /dev/null +++ b/build/dump @@ -0,0 +1,14 @@ +// file : build/dump -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD_DUMP +#define BUILD_DUMP + +namespace build +{ + void + dump_scopes (); +} + +#endif // BUILD_DUMP diff --git a/build/dump.cxx b/build/dump.cxx new file mode 100644 index 0000000..4294e92 --- /dev/null +++ b/build/dump.cxx @@ -0,0 +1,72 @@ +// file : build/dump.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace std; + +namespace build +{ + static void + dump_scope (scope& p, scope_map::iterator& i, string& ind) + { + string d (diag_relative_work (p.path ())); + + if (d.back () != path::traits::directory_separator) + d += '/'; + + cerr << ind << d << ":" << endl + << ind << '{' << endl; + + ind += " "; + + for (const auto& e: p.variables) + { + const variable& var (e.first); + const value_ptr& val (e.second); + + cerr << ind << var.name << " = "; + + if (val == nullptr) + cerr << "[undefined]"; + else + { + //@@ TODO: assuming it is a list. + // + cerr << dynamic_cast (*val).data; + } + + cerr << endl; + } + + // Print nested scopes of which we are a parent. + // + for (auto e (scopes.end ()); i != e && i->second.parent () == &p; ) + { + scope& s (i->second); + dump_scope (s, ++i, ind); + } + + ind.resize (ind.size () - 2); + cerr << ind << '}' << endl; + } + + void + dump_scopes () + { + string ind; + auto i (scopes.begin ()); + scope& r (i->second); // Root scope. + assert (&r == root_scope); + dump_scope (r, ++i, ind); + } +} diff --git a/build/lexer.cxx b/build/lexer.cxx index ea11680..c2f98f2 100644 --- a/build/lexer.cxx +++ b/build/lexer.cxx @@ -39,6 +39,17 @@ namespace build { return token (token_type::rcbrace, ln, cn); } + case '=': + { + return token (token_type::equal, ln, cn); + } + case '+': + { + if (get () != '=') + fail (c) << "expected = after +"; + + return token (token_type::plus_equal, ln, cn); + } } // Otherwise it is a name. @@ -121,10 +132,12 @@ namespace build case ' ': case '\t': case '\n': + case '#': case ':': case '{': case '}': - case '#': + case '=': + case '+': { break; } diff --git a/build/name b/build/name new file mode 100644 index 0000000..db7b61c --- /dev/null +++ b/build/name @@ -0,0 +1,43 @@ +// file : build/name -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef BUILD_NAME +#define BUILD_NAME + +#include +#include +#include +#include // move() + +#include + +namespace build +{ + // A name is what we operate on by default. Depending on the context, + // it can be interpreted as a target or prerequisite name. A name + // without a type and directory can be used to represent any text. + // + struct name + { + explicit + name (std::string v): value (std::move (v)) {} + + name (std::string t, path d, std::string v) + : type (std::move (t)), dir (std::move (d)), value (std::move (v)) {} + + std::string type; + path dir; + std::string value; + }; + + typedef std::vector names; + + std::ostream& + operator<< (std::ostream&, const name&); + + std::ostream& + operator<< (std::ostream&, const names&); +} + +#endif // BUILD_NAME diff --git a/build/name.cxx b/build/name.cxx new file mode 100644 index 0000000..9de1695 --- /dev/null +++ b/build/name.cxx @@ -0,0 +1,51 @@ +// file : build/name.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include + +using namespace std; + +namespace build +{ + ostream& + operator<< (ostream& os, const name& n) + { + if (!n.type.empty ()) + os << n.type << '{'; + + if (!n.dir.empty ()) + { + string s (diag_relative_work (n.dir)); + + if (s != ".") + { + os << s; + + if (!n.value.empty () && + s.back () != path::traits::directory_separator) + os << path::traits::directory_separator; + } + } + + os << n.value; + + if (!n.type.empty ()) + os << '}'; + + return os; + } + + ostream& + operator<< (ostream& os, const names& ns) + { + for (auto b (ns.begin ()), i (b), e (ns.end ()); i != e; ++i) + os << (i != b ? " " : "") << *i; + + return os; + } +} diff --git a/build/parser b/build/parser index 4fea3db..4f099fe 100644 --- a/build/parser +++ b/build/parser @@ -6,13 +6,12 @@ #define BUILD_PARSER #include -#include #include -#include // std::move #include #include #include +#include #include namespace build @@ -33,17 +32,7 @@ namespace build // Recursive descent parser. // private: - struct name_type - { - name_type (std::string t, path d, std::string n) - : type (std::move (t)), dir (std::move (d)), name (std::move (n)) {} - - std::string type; // Empty if untyped. - path dir; - std::string name; - }; - - typedef std::vector names_type; + typedef build::names names_type; void clause (token&, token_type&); diff --git a/build/parser.cxx b/build/parser.cxx index 9ebf8d3..16be5da 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -7,6 +7,7 @@ #include // unique_ptr #include #include // move() +#include // make_move_iterator() #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -113,9 +115,9 @@ namespace build // ': foo' is equvalent to '{}: foo' and to 'dir{}: foo'. // - names_type tns (tt != type::colon - ? names (t, tt) - : names_type ({name_type ("", path (), "")})); + names_type ns (tt != type::colon + ? names (t, tt) + : names_type ({name ("")})); if (tt == type::colon) { @@ -138,11 +140,11 @@ namespace build // things can appear inside depending on which one it is. // bool dir (false); - for (const auto& n: tns) + for (const auto& n: ns) { - if (n.type.empty () && n.name.back () == '/') + if (n.type.empty () && n.value.back () == '/') { - if (tns.size () != 1) + if (ns.size () != 1) { // @@ TODO: point to name. // @@ -163,9 +165,9 @@ namespace build // Search for root_scope for details. // #ifdef _WIN32 - path p (tns[0].name != "/" ? path (tns[0].name) : path ()); + path p (ns[0].value != "/" ? path (ns[0].value) : path ()); #else - path p (tns[0].name); + path p (ns[0].value); #endif if (p.relative ()) p = prev.path () / p; @@ -230,14 +232,16 @@ namespace build const string* e (nullptr); { - path::size_type i (path::traits::rfind_separator (pn.name)); + string& v (pn.value); + + path::size_type i (path::traits::rfind_separator (v)); if (i == string::npos) - n = move (pn.name); // NOTE: steal! + n = move (v); // NOTE: steal! else { - d /= path (pn.name, i != 0 ? i : 1); // Special case: "/". - n.assign (pn.name, i + 1, string::npos); + d /= path (v, i != 0 ? i : 1); // Special case: "/". + n.assign (v, i + 1, string::npos); } // Handle '.' and '..'. @@ -289,7 +293,7 @@ namespace build ps.push_back (p); } - for (auto& tn: tns) + for (auto& tn: ns) { path d (tn.dir); string n; @@ -298,14 +302,16 @@ namespace build // The same deal as in handling prerequisites above. // { - path::size_type i (path::traits::rfind_separator (tn.name)); + string& v (tn.value); + + path::size_type i (path::traits::rfind_separator (v)); if (i == string::npos) - n = move (tn.name); // NOTE: steal! + n = move (v); // NOTE: steal! else { - d /= path (tn.name, i != 0 ? i : 1); // Special case: "/". - n.assign (tn.name, i + 1, string::npos); + d /= path (v, i != 0 ? i : 1); // Special case: "/". + n.assign (v, i + 1, string::npos); } // Handle '.' and '..'. @@ -382,6 +388,83 @@ namespace build fail (t) << "expected newline instead of " << t; } + // Variable assignment. + // + if (tt == type::equal || tt == type::plus_equal) + { + bool assign (tt == type::equal); + + // LHS should be a single, simple name. + // + if (ns.size () != 1 || !ns[0].type.empty () || !ns[0].dir.empty ()) + fail << "variable name expected before " << t; + + next (t, tt); + + names_type vns (tt != type::newline && tt != type::eos + ? names (t, tt) + : names_type ()); + + // Enter the variable. + // + string name; + if (ns[0].value.front () == '.') // Fully qualified name. + name.assign (ns[0].value, 1, string::npos); + else + //@@ TODO: append namespace if any. + name = move (ns[0].value); + + const variable& var (variable_pool.find (move (name))); + + if (assign) + { + value_ptr& val (scope_->variables[var]); + + if (val == nullptr) // Initialization. + { + val.reset (new list_value (*scope_, move (vns))); + } + else // Assignment. + { + //@@ TODO: assuming it is a list. + // + dynamic_cast (*val).data = move (vns); + } + } + else + { + //@@ TODO: assuming it is a list. + // + list_value* val (dynamic_cast ((*scope_)[var])); + + if (val == nullptr) // Initialization. + { + list_value_ptr nval (new list_value (*scope_, move (vns))); + scope_->variables.emplace (var, move (nval)); + } + else if (&val->scope != scope_) // Append to value from parent scope. + { + list_value_ptr nval (new list_value (*scope_, val->data)); + val = nval.get (); // Append. + scope_->variables.emplace (var, move (nval)); + } + + // Append. + // + if (val != nullptr) + val->data.insert (val->data.end (), + make_move_iterator (vns.begin ()), + make_move_iterator (vns.end ())); + } + + if (tt == type::newline) + next (t, tt); + else if (tt != type::eos) + fail (t) << "expected newline instead of " << t; + + continue; + } + fail (t) << "unexpected " << t; } } @@ -670,6 +753,7 @@ namespace build case token_type::colon: os << ":"; break; case token_type::lcbrace: os << "{"; break; case token_type::rcbrace: os << "}"; break; + case token_type::equal: os << "="; break; case token_type::name: os << t.name (); break; } diff --git a/build/path b/build/path index 9b3668d..6ce2172 100644 --- a/build/path +++ b/build/path @@ -418,7 +418,7 @@ namespace std struct hash>: hash> { size_t - operator() (const build::basic_path& p) const + operator() (const build::basic_path& p) const noexcept { return hash>::operator() (p.string ()); } diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index f2c72e5..feb6381 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -30,8 +30,15 @@ namespace build { string s (diag_relative_work (p.scope.path ())); - if (!s.empty () && s.back () != path::traits::directory_separator) - os << s << path::traits::directory_separator << ": "; + if (s != ".") + { + os << s; + + if (s.back () != path::traits::directory_separator) + os << path::traits::directory_separator; + + os << ": "; + } } // Print directory. @@ -40,8 +47,14 @@ namespace build { string s (diag_relative_work (p.dir)); - if (!s.empty () && s.back () != path::traits::directory_separator) - os << s << path::traits::directory_separator; + if (s != ".") + { + os << s; + + if (!p.name.empty () && + s.back () != path::traits::directory_separator) + os << path::traits::directory_separator; + } } os << p.name; diff --git a/build/scope b/build/scope index 6d96b0a..2746ff8 100644 --- a/build/scope +++ b/build/scope @@ -20,8 +20,17 @@ namespace build const path_type& path () const {return i_->first;} // Absolute and normalized. - scope& - parent () const {return *parent_;} + scope* + parent () const {return parent_;} + + // Variable lookup. + // + public: + value* + operator[] (const variable&); + + value* + operator[] (const std::string& name); private: friend class scope_map; @@ -45,7 +54,7 @@ namespace build scope* parent_; }; - class scope_map: path_map + class scope_map: public path_map { public: // Note that we assume the first insertion into the map is that diff --git a/build/scope.cxx b/build/scope.cxx index 7165663..9013e12 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -8,6 +8,30 @@ using namespace std; namespace build { + // scope + // + value* scope:: + operator[] (const string& name) + { + const variable& var (variable_pool.find (name)); + return (*this)[var]; + } + + value* scope:: + operator[] (const variable& var) + { + for (scope* s (this); s != nullptr; s = s->parent ()) + { + auto i (s->variables.find (var)); + if (i != s->variables.end ()) + return i->second.get (); + } + + return nullptr; + } + + // scope_map + // scope_map scopes; scope* root_scope; @@ -32,8 +56,8 @@ namespace build // between it and our parent. // if (p == nullptr) - p = &c; - else if (p != &c) // A scope with an intermediate parent. + p = c.parent (); + else if (p != c.parent ()) // A scope with an intermediate parent. continue; c.parent (s); @@ -44,7 +68,7 @@ namespace build // root scope). // if (p == nullptr && size () != 1) - p = &find (k); + p = &find (k.directory ()); s.init (er.first, p); } @@ -67,12 +91,12 @@ namespace build for (path d (k.directory ());; d = d.directory ()) { - auto i (base::find (k)); + auto i (base::find (d)); if (i != end ()) return i->second; - assert (d.empty ()); // We should have the root scope. + assert (!d.empty ()); // We should have the root scope. } } } diff --git a/build/target b/build/target index 881bfb7..0f5de4f 100644 --- a/build/target +++ b/build/target @@ -13,7 +13,7 @@ #include #include #include -#include // move +#include // move() #include #include @@ -49,6 +49,9 @@ namespace build class target { public: + virtual + ~target () = default; + target (path d, std::string n, const std::string* e) : dir (std::move (d)), name (std::move (n)), ext (e) {} diff --git a/build/target.cxx b/build/target.cxx index bb578c1..b4c4481 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -29,7 +29,7 @@ namespace build { string s (diag_relative_work (t.dir)); - if (!s.empty ()) + if (s != ".") { os << s; diff --git a/build/token b/build/token index 9f9b2b4..a071987 100644 --- a/build/token +++ b/build/token @@ -20,7 +20,9 @@ namespace build newline, colon, lcbrace, - rcbrace + rcbrace, + equal, + plus_equal }; class token diff --git a/build/variable b/build/variable index caceb2b..393ad47 100644 --- a/build/variable +++ b/build/variable @@ -5,15 +5,119 @@ #ifndef BUILD_VARIABLE #define BUILD_VARIABLE +#include +#include // unique_ptr +#include // move() +#include +#include + +#include #include namespace build { - // @@ TODO: - // - pool names? + class scope; + struct value; + + struct value_type + { + std::type_index id; + value* (*const factory) (); + }; + + // variable + // + // The two variables are considered the same if they have the same name. + // + struct variable + { + explicit + variable (std::string n): name (std::move (n)), type (nullptr) {} + + std::string name; + const value_type* type; // If NULL, then this variable has no fixed type. + }; + + inline bool + operator== (const variable& x, const variable& y) {return x.name == y.name;} + + typedef std::reference_wrapper variable_cref; + + // value + // + struct value + { + typedef build::scope scope_type; + + virtual + ~value () = default; + + value (scope_type& s): scope (s) {} + + scope_type& scope; // Scope to which this value belongs. + }; + typedef std::unique_ptr value_ptr; + + struct list_value: value + { + list_value (scope_type& s, names d): value (s), data (std::move (d)) {} + + names data; + }; + typedef std::unique_ptr list_value_ptr; +} + +namespace std +{ + template <> + struct hash: hash + { + size_t + operator() (const build::variable& v) const noexcept + { + return hash::operator() (v.name); + } + }; +} + +namespace build +{ + // variable_pool + // + struct variable_set: std::unordered_set + { + // @@ Need to check/set type? + // + const variable& + find (std::string name) {return *emplace (std::move (name)).first;} + }; + + extern variable_set variable_pool; + + // variable_map // + template <> + struct compare_prefix: compare_prefix + { + typedef compare_prefix base; + + explicit + compare_prefix (char d): base (d) {} + + bool + operator() (const variable& x, const variable& y) const + { + return base::operator() (x.name, y.name); + } + + bool + prefix (const variable& p, const variable& k) const + { + return base::prefix (p.name, k.name); + } + }; - typedef prefix_map variable_map; + typedef prefix_map variable_map; } #endif // BUILD_VARIABLE diff --git a/build/variable.cxx b/build/variable.cxx new file mode 100644 index 0000000..613cada --- /dev/null +++ b/build/variable.cxx @@ -0,0 +1,12 @@ +// file : build/variable.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +using namespace std; + +namespace build +{ + variable_set variable_pool; +} -- cgit v1.1