aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-30 14:16:54 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-30 14:16:54 +0200
commitb1715878d50aa8a3c3e2404f3ded120329994aba (patch)
tree85c9feb4556f7555f0acc0e2d012ee52b73c1b34
parent70256514a09e4692c6839f5c2b21b7ec9c1055bd (diff)
Initial support for command line variables
-rw-r--r--build/b.cxx44
-rw-r--r--build/buildfile7
-rw-r--r--build/lexer3
-rw-r--r--build/parser6
-rw-r--r--build/parser.cxx156
-rw-r--r--build/token6
-rw-r--r--build/token.cxx34
7 files changed, 167 insertions, 89 deletions
diff --git a/build/b.cxx b/build/b.cxx
index c676f8a..bd02052 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -12,7 +12,6 @@
#include <sstream>
#include <cassert>
-#include <iostream> //@@ TMP, for dump()
#include <typeinfo>
#include <system_error>
@@ -32,6 +31,9 @@
#include <build/context>
#include <build/utility>
#include <build/filesystem>
+
+#include <build/token>
+#include <build/lexer>
#include <build/parser>
using namespace std;
@@ -223,6 +225,40 @@ main (int argc, char* argv[])
//
reset ();
+ // Parse command line variables. They should come before the
+ // buildspec.
+ //
+ int argi (1);
+ for (; argi != argc; argi++)
+ {
+ const char* s (argv[argi]);
+
+ istringstream is (s);
+ is.exceptions (istringstream::failbit | istringstream::badbit);
+ lexer l (is, "<cmdline>");
+ token t (l.next ());
+
+ if (t.type () == token_type::eos)
+ continue; // Whitespace-only argument.
+
+ // Unless this is a name followed by = or +=, assume it is
+ // a start of the buildspec.
+ //
+ if (t.type () != token_type::name)
+ break;
+
+ token_type tt (l.next ().type ());
+
+ if (tt != token_type::equal && tt != token_type::plus_equal)
+ break;
+
+ parser p;
+ t = p.parse_variable (l, *root_scope, t.name (), tt);
+
+ if (t.type () != token_type::eos)
+ fail << "unexpected " << t << " in variable " << s;
+ }
+
// Parse the buildspec.
//
buildspec bspec;
@@ -235,10 +271,10 @@ main (int argc, char* argv[])
// which argument is invalid).
//
string s;
- for (int i (1); i != argc;)
+ for (; argi != argc;)
{
- s += argv[i];
- if (++i != argc)
+ s += argv[argi];
+ if (++argi != argc)
s += ' ';
}
diff --git a/build/buildfile b/build/buildfile
index 93f9179..95899fd 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,6 +1,7 @@
cxx = cxx/{target rule}
config = config/{operation module}
-exe{b1}: cxx{b algorithm parser lexer name operation spec scope variable \
- target prerequisite rule file module native context search diagnostics \
- process timestamp path utility filesystem dump $config $cxx}
+exe{b1}: cxx{b algorithm name operation spec scope variable target \
+ prerequisite rule file module native context search diagnostics \
+ token lexer parser process timestamp path utility filesystem dump \
+ $config $cxx}
diff --git a/build/lexer b/build/lexer
index 5f91dc4..81225e0 100644
--- a/build/lexer
+++ b/build/lexer
@@ -30,6 +30,9 @@ namespace build
public:
lexer (std::istream& is, const std::string& name): is_ (is), fail (name) {}
+ const std::string&
+ name () const {return fail.name_;}
+
// Note: sets mode for the next token. If mode is pairs, then
// the second argument specifies the separator character.
//
diff --git a/build/parser b/build/parser
index 94a5bdf..4423e25 100644
--- a/build/parser
+++ b/build/parser
@@ -33,6 +33,9 @@ namespace build
buildspec
parse_buildspec (std::istream&, const std::string& name);
+ token
+ parse_variable (lexer&, scope&, std::string name, token_type kind);
+
// Recursive descent parser.
//
private:
@@ -53,6 +56,9 @@ namespace build
void
using_ (token&, token_type&);
+ void
+ variable (token&, token_type&, std::string name, token_type kind);
+
names_type
names (token& t, token_type& tt)
{
diff --git a/build/parser.cxx b/build/parser.cxx
index 617f22b..e2b11f0 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -27,11 +27,6 @@ using namespace std;
namespace build
{
- // Output the token type and value in a format suitable for diagnostics.
- //
- ostream&
- operator<< (ostream&, const token&);
-
static location
get_location (const token&, const void*);
@@ -70,6 +65,21 @@ namespace build
process_default_target (t);
}
+ token parser::
+ parse_variable (lexer& l, scope& s, string name, token_type kind)
+ {
+ path_ = &l.name ();
+ lexer_ = &l;
+ scope_ = &s;
+
+ token t (type::eos, false, 0, 0);
+ type tt;
+ next (t, tt);
+
+ variable (t, tt, name, kind);
+ return t;
+ }
+
void parser::
clause (token& t, token_type& tt)
{
@@ -305,21 +315,11 @@ namespace build
//
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 (t) << "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);
@@ -327,48 +327,9 @@ namespace build
//@@ 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
- {
- if (auto val = (*scope_)[var])
- {
- //@@ TODO: assuming it is a list.
- //
- list_value* lv (&val.as<list_value&> ());
-
- if (&lv->scope != scope_) // Append to value from parent scope?
- {
- list_value_ptr nval (new list_value (*scope_, lv->data));
- lv = nval.get (); // Append to.
- scope_->variables.emplace (var, move (nval));
- }
-
- lv->data.insert (lv->data.end (),
- make_move_iterator (vns.begin ()),
- make_move_iterator (vns.end ()));
- }
- else // Initialization.
- {
- list_value_ptr nval (new list_value (*scope_, move (vns)));
- scope_->variables.emplace (var, move (nval));
- }
- }
+ type kind (tt);
+ next (t, tt);
+ variable (t, tt, move (name), kind);
if (tt == type::newline)
next (t, tt);
@@ -648,6 +609,61 @@ namespace build
}
void parser::
+ variable (token& t, token_type& tt, string name, token_type kind)
+ {
+ bool assign (kind == type::equal);
+
+ names_type vns (tt != type::newline && tt != type::eos
+ ? names (t, tt)
+ : names_type ());
+
+ // Enter the variable.
+ //
+ const auto& 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
+ {
+ if (auto val = (*scope_)[var])
+ {
+ //@@ TODO: assuming it is a list.
+ //
+ list_value* lv (&val.as<list_value&> ());
+
+ if (&lv->scope != scope_) // Append to value from parent scope?
+ {
+ list_value_ptr nval (new list_value (*scope_, lv->data));
+ lv = nval.get (); // Append to.
+ scope_->variables.emplace (var, move (nval));
+ }
+
+ lv->data.insert (lv->data.end (),
+ make_move_iterator (vns.begin ()),
+ make_move_iterator (vns.end ()));
+ }
+ else // Initialization.
+ {
+ list_value_ptr nval (new list_value (*scope_, move (vns)));
+ scope_->variables.emplace (var, move (nval));
+ }
+ }
+ }
+
+ void parser::
names (token& t,
type& tt,
names_type& ns,
@@ -830,7 +846,7 @@ namespace build
//@@ TODO: append namespace if any.
n = t.name ();
- const variable& var (variable_pool.find (move (n)));
+ const auto& var (variable_pool.find (move (n)));
auto val ((*scope_)[var]);
// Undefined namespaces variables are not allowed.
@@ -1289,28 +1305,4 @@ namespace build
const string& p (**static_cast<const string* const*> (data));
return location (p.c_str (), t.line (), t.column ());
}
-
- // Output the token type and value in a format suitable for diagnostics.
- //
- ostream&
- operator<< (ostream& os, const token& t)
- {
- switch (t.type ())
- {
- case token_type::eos: os << "<end-of-file>"; break;
- case token_type::newline: os << "<newline>"; break;
- case token_type::pair_separator: os << "<pair separator>"; break;
- 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::plus_equal: os << "+="; break;
- case token_type::dollar: os << "$"; break;
- case token_type::lparen: os << "("; break;
- case token_type::rparen: os << ")"; break;
- case token_type::name: os << t.name (); break;
- }
-
- return os;
- }
}
diff --git a/build/token b/build/token
index f7e45dc..7b0223b 100644
--- a/build/token
+++ b/build/token
@@ -6,6 +6,7 @@
#define BUILD_TOKEN
#include <string>
+#include <iosfwd>
#include <cstddef> // size_t
#include <cstdint> // uint64_t
#include <cassert>
@@ -61,6 +62,11 @@ namespace build
std::uint64_t l_;
std::uint64_t c_;
};
+
+ // Output the token value in a format suitable for diagnostics.
+ //
+ std::ostream&
+ operator<< (std::ostream&, const token&);
}
#endif // BUILD_TOKEN
diff --git a/build/token.cxx b/build/token.cxx
new file mode 100644
index 0000000..33d30a8
--- /dev/null
+++ b/build/token.cxx
@@ -0,0 +1,34 @@
+// file : build/token.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/token>
+
+#include <ostream>
+
+using namespace std;
+
+namespace build
+{
+ ostream&
+ operator<< (ostream& os, const token& t)
+ {
+ switch (t.type ())
+ {
+ case token_type::eos: os << "<end-of-file>"; break;
+ case token_type::newline: os << "<newline>"; break;
+ case token_type::pair_separator: os << "<pair separator>"; break;
+ 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::plus_equal: os << "+="; break;
+ case token_type::dollar: os << "$"; break;
+ case token_type::lparen: os << "("; break;
+ case token_type::rparen: os << ")"; break;
+ case token_type::name: os << t.name (); break;
+ }
+
+ return os;
+ }
+}