From c0c85b67516653c181fbce7c61c2df3e31e4edd8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 18 Dec 2014 07:14:53 +0200 Subject: Initial support for loading dependency info from buildfiles Also a new iteration on the overall architecture. Now, for the first time, build can read the buildfile and build itself. g++-4.9 -std=c++14 -g -I.. -o bd bd.cxx algorithm.cxx scope.cxx parser.cxx lexer.cxx target.cxx prerequisite.cxx rule.cxx native.cxx cxx/target.cxx cxx/rule.cxx process.cxx timestamp.cxx path.cxx g++-4.9 -std=c++14 -g -I../../.. -o driver driver.cxx ../../../build/lexer.cxx g++-4.9 -std=c++14 -g -I../../.. -o driver driver.cxx ../../../build/lexer.cxx ../../../build/parser.cxx ../../../build/scope.cxx ../../../build/target.cxx ../../../build/native.cxx ../../../build/prerequisite.cxx ../../../build/path.cxx ../../../build/timestamp.cxx --- build/parser.cxx | 174 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 155 insertions(+), 19 deletions(-) (limited to 'build/parser.cxx') diff --git a/build/parser.cxx b/build/parser.cxx index 348d285..e300201 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -4,11 +4,16 @@ #include +#include // unique_ptr #include #include #include +#include +#include +#include + using namespace std; namespace build @@ -21,11 +26,12 @@ namespace build typedef token_type type; void parser:: - parse (istream& is, const path& p) + parse (istream& is, const path& p, scope& s) { lexer l (is, p.string (), diag_); lexer_ = &l; path_ = &p; + scope_ = &s; token t (type::eos, 0, 0); type tt; @@ -50,7 +56,7 @@ namespace build if (tt != type::name && tt != type::lcbrace) break; // Something else. Let our caller handle that. - names ns (parse_names (t, tt)); + names tns (parse_names (t, tt)); if (tt == type::colon) { @@ -60,7 +66,127 @@ namespace build // if (tt == type::name || tt == type::lcbrace) { - names ns (parse_names (t, tt)); + names pns (parse_names (t, tt)); + + // Prepare the prerequisite list. + // + target::prerequisites_type ps; + ps.reserve (pns.size ()); + + for (auto& pn: pns) + { + // Resolve prerequisite type. + // + //@@ TODO: derive type from extension, factor to common function + // + const char* tt (pn.type.empty () ? "file" : pn.type.c_str ()); + + auto i (target_types.find (tt)); + + if (i == target_types.end ()) + { + //@@ TODO name (or better yet, type) location + + error (t) << "unknown prerequisite type '" << tt << "'" << endl; + throw parser_error (); + } + + const target_type& ti (i->second); + + // We need to split the name into its directory part (if any) + // and the name part. We cannot assume the name part is a + // valid filesystem name so we will have to do the splitting + // manually. + // + path d; + string n; + + { + path::size_type i (path::traits::rfind_separator (pn.name)); + + if (i == string::npos) + n = move (pn.name); // NOTE: steal! + else + { + d = path (pn.name, i); + n.assign (pn.name, i + 1, string::npos); + d.normalize (); + } + } + + //cout << "prerequisite " << tt << " " << n << " " << d << endl; + + // Find or insert. + // + auto r (scope_->prerequisites.emplace ( + ti, move (n), move (d), *scope_)); + + ps.push_back (const_cast (*r.first)); + } + + for (auto& tn: tns) + { + path d; + string n; + + // The same deal as in handling prerequisites above. + // + { + path::size_type i (path::traits::rfind_separator (tn.name)); + + if (i == string::npos) + { + d = scope_->path (); // Already normalized. + n = move (tn.name); // NOTE: steal! + } + else + { + d = path (tn.name, i); + n.assign (tn.name, i + 1, string::npos); + + if (d.relative ()) + d = scope_->path () / d; + + d.normalize (); + } + } + + // Resolve target type. + // + //@@ TODO: derive type from extension + // + const char* tt (tn.type.empty () ? "file" : tn.type.c_str ()); + + auto i (target_types.find (tt)); + + if (i == target_types.end ()) + { + //@@ TODO name (or better yet, type) location + + error (t) << "unknown target type '" << tt << "'" << endl; + throw parser_error (); + } + + const target_type& ti (i->second); + + //@@ TODO would be nice to first check if this target is + // already in the set before allocating a new instance. + + //cout << "target " << tt << " " << n << " " << d << endl; + + // Find or insert. + // + auto r ( + targets.emplace ( + unique_ptr (ti.factory (move (n), move (d))))); + + target& t (**r.first); + + t.prerequisites = ps; //@@ TODO: move is last target. + + if (default_target == nullptr) + default_target = &t; + } if (tt == type::newline) next (t, tt); @@ -88,14 +214,14 @@ namespace build } // See if this is a directory or target scope. Different - // things can appear inside depending on which it is. + // things can appear inside depending on which one it is. // bool dir (false); - for (const auto& n: ns) + for (const auto& n: tns) { - if (n.back () == '/') + if (n.type.empty () && n.name.back () == '/') { - if (ns.size () != 1) + if (tns.size () != 1) { // @@ TODO: point to name. // @@ -110,9 +236,21 @@ namespace build next (t, tt); if (dir) + { + scope& prev (*scope_); + path p (tns[0].name); + + if (p.relative ()) + p = prev.path () / p; + + scope_ = &scopes[p]; + // A directory scope can contain anything that a top level can. // parse_clause (t, tt); + + scope_ = &prev; + } else { // @@ TODO: target scope. @@ -151,7 +289,7 @@ namespace build } void parser:: - parse_names (token& t, type& tt, names& ns) + parse_names (token& t, type& tt, names& ns, const string* tp) { for (bool first (true);; first = false) { @@ -160,7 +298,7 @@ namespace build if (tt == type::lcbrace) { next (t, tt); - parse_names (t, tt, ns); + parse_names (t, tt, ns, tp); if (tt != type::rcbrace) { @@ -176,20 +314,20 @@ namespace build // if (tt == type::name) { - string name (t.name ()); + string name (t.name ()); //@@ move? // See if this is a type name, that is, it is followed by '{'. // if (next (t, tt) == type::lcbrace) { - //cout << "type: " << name << endl; + if (tp != nullptr) + { + error (t) << "nested type name '" << name << "'" << endl; + throw parser_error (); + } - //@@ TODO: - // - // - detect nested typed name groups, e.g., 'cxx{hxx{foo}}'. - // next (t, tt); - parse_names (t, tt, ns); + parse_names (t, tt, ns, &name); if (tt != type::rcbrace) { @@ -201,9 +339,7 @@ namespace build continue; } - // This is a target, directory, or variable name. - //cout << "name: " << name << endl; - ns.push_back (name); + ns.emplace_back ((tp != nullptr ? *tp : string ()), move (name)); continue; } -- cgit v1.1