aboutsummaryrefslogtreecommitdiff
path: root/build/parser.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-12-18 07:14:53 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-12-18 07:14:53 +0200
commitc0c85b67516653c181fbce7c61c2df3e31e4edd8 (patch)
tree535467b029b27b190e35064e4babd62825eba6a1 /build/parser.cxx
parent835ed5f7080a98e9ee80ac08d5585ccdbb63fe0e (diff)
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
Diffstat (limited to 'build/parser.cxx')
-rw-r--r--build/parser.cxx174
1 files changed, 155 insertions, 19 deletions
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 <build/parser>
+#include <memory> // unique_ptr
#include <iostream>
#include <build/token>
#include <build/lexer>
+#include <build/scope>
+#include <build/target>
+#include <build/prerequisite>
+
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<prerequisite&> (*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<target> (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;
}