aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-02-27 16:57:34 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-02-27 16:57:34 +0200
commit4372f041bb7401c3adc2d5710566b13f64722102 (patch)
tree5f37f6e69e5529d3628b7611bb642dba15d885c0
parente1d2e3b63934c1e193429f1d6c4e04abc0e85d56 (diff)
Variable assignment, appending support
-rw-r--r--build/b.cxx2
-rw-r--r--build/buildfile6
-rw-r--r--build/diagnostics3
-rw-r--r--build/diagnostics.cxx3
-rw-r--r--build/dump14
-rw-r--r--build/dump.cxx72
-rw-r--r--build/lexer.cxx15
-rw-r--r--build/name43
-rw-r--r--build/name.cxx51
-rw-r--r--build/parser15
-rw-r--r--build/parser.cxx118
-rw-r--r--build/path2
-rw-r--r--build/prerequisite.cxx21
-rw-r--r--build/scope15
-rw-r--r--build/scope.cxx34
-rw-r--r--build/target5
-rw-r--r--build/target.cxx2
-rw-r--r--build/token4
-rw-r--r--build/variable110
-rw-r--r--build/variable.cxx12
20 files changed, 493 insertions, 54 deletions
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 <build/diagnostics>
#include <build/context>
#include <build/utility>
+#include <build/dump>
#include <build/lexer>
#include <build/parser>
@@ -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 <build/dump>
+
+#include <string>
+#include <cassert>
+#include <iostream>
+
+#include <build/scope>
+#include <build/variable>
+#include <build/diagnostics>
+
+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<list_value&> (*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 <string>
+#include <vector>
+#include <iosfwd>
+#include <utility> // move()
+
+#include <build/path>
+
+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<name> 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 <build/name>
+
+#include <ostream>
+
+#include <build/diagnostics>
+
+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 <string>
-#include <vector>
#include <iosfwd>
-#include <utility> // std::move
#include <unordered_set>
#include <build/path>
#include <build/token>
+#include <build/name>
#include <build/diagnostics>
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<name_type> 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 <memory> // unique_ptr
#include <fstream>
#include <utility> // move()
+#include <iterator> // make_move_iterator()
#include <iostream>
#include <build/token>
@@ -15,6 +16,7 @@
#include <build/scope>
#include <build/target>
#include <build/prerequisite>
+#include <build/variable>
#include <build/diagnostics>
#include <build/context>
@@ -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<list_value&> (*val).data = move (vns);
+ }
+ }
+ else
+ {
+ //@@ TODO: assuming it is a list.
+ //
+ list_value* val (dynamic_cast<list_value*> ((*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<build::basic_path<C>>: hash<basic_string<C>>
{
size_t
- operator() (const build::basic_path<C>& p) const
+ operator() (const build::basic_path<C>& p) const noexcept
{
return hash<basic_string<C>>::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<scope>
+ class scope_map: public path_map<scope>
{
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 <typeindex>
#include <ostream>
#include <cassert>
-#include <utility> // move
+#include <utility> // move()
#include <build/path>
#include <build/key-set>
@@ -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 <string>
+#include <memory> // unique_ptr
+#include <utility> // move()
+#include <typeindex>
+#include <unordered_set>
+
+#include <build/name>
#include <build/prefix-map>
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<const variable> 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> 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> list_value_ptr;
+}
+
+namespace std
+{
+ template <>
+ struct hash<build::variable>: hash<string>
+ {
+ size_t
+ operator() (const build::variable& v) const noexcept
+ {
+ return hash<string>::operator() (v.name);
+ }
+ };
+}
+
+namespace build
+{
+ // variable_pool
+ //
+ struct variable_set: std::unordered_set<variable>
+ {
+ // @@ 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<variable_cref>: compare_prefix<std::string>
+ {
+ typedef compare_prefix<std::string> 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<std::string, std::string, '.'> variable_map;
+ typedef prefix_map<variable_cref, value_ptr, '.'> 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 <build/variable>
+
+using namespace std;
+
+namespace build
+{
+ variable_set variable_pool;
+}