From b1715878d50aa8a3c3e2404f3ded120329994aba Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 30 Mar 2015 14:16:54 +0200 Subject: Initial support for command line variables --- build/b.cxx | 44 ++++++++++++++-- build/buildfile | 7 +-- build/lexer | 3 ++ build/parser | 6 +++ build/parser.cxx | 156 ++++++++++++++++++++++++++----------------------------- build/token | 6 +++ build/token.cxx | 34 ++++++++++++ 7 files changed, 167 insertions(+), 89 deletions(-) create mode 100644 build/token.cxx 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 #include -#include //@@ TMP, for dump() #include #include @@ -32,6 +31,9 @@ #include #include #include + +#include +#include #include 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, ""); + 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 (*val).data = move (vns); - } - } - else - { - if (auto val = (*scope_)[var]) - { - //@@ TODO: assuming it is a list. - // - list_value* lv (&val.as ()); - - 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 (*val).data = move (vns); + } + } + else + { + if (auto val = (*scope_)[var]) + { + //@@ TODO: assuming it is a list. + // + list_value* lv (&val.as ()); + + 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 (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 << ""; break; - case token_type::newline: os << ""; break; - case token_type::pair_separator: os << ""; 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 +#include #include // size_t #include // uint64_t #include @@ -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 + +#include + +using namespace std; + +namespace build +{ + ostream& + operator<< (ostream& os, const token& t) + { + switch (t.type ()) + { + case token_type::eos: os << ""; break; + case token_type::newline: os << ""; break; + case token_type::pair_separator: os << ""; 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; + } +} -- cgit v1.1