diff options
Diffstat (limited to 'build/parser.cxx')
-rw-r--r-- | build/parser.cxx | 180 |
1 files changed, 94 insertions, 86 deletions
diff --git a/build/parser.cxx b/build/parser.cxx index 1893b70..ad17ae9 100644 --- a/build/parser.cxx +++ b/build/parser.cxx @@ -16,9 +16,6 @@ #include <build/utility> #include <build/version> -#include <build/token> -#include <build/lexer> - #include <build/scope> #include <build/target> #include <build/prerequisite> @@ -309,93 +306,101 @@ namespace build tt == type::equal_plus || tt == type::plus_equal) { + token at (t); + type att (tt); + string v (variable_name (move (pns), ploc)); - // Enter the target/scope and set it as current. + // If we have multiple targets/scopes, then we save the value + // tokens when parsing the first one and then replay them for + // the subsequent. We have to do it this way because the value + // may contain variable expansions that would be sensitive to + // the target/scope context in which they are evaluated. // - if (ns.size () != 1) - fail (nloc) << "multiple names in scope/target-specific " - << "variable assignment"; - - name& n (ns[0]); + replay_guard rg (*this, ns.size () > 1); - if (n.qualified ()) - fail (nloc) << "project name in scope/target " << n; - - if (n.directory ()) + for (name& n: ns) { - // The same code as in directory scope handling code above. - // - dir_path p (move (n.dir)); + if (n.qualified ()) + fail (nloc) << "project name in scope/target " << n; - if (p.relative ()) - p = scope_->out_path () / p; + if (n.directory ()) + { + // The same code as in directory scope handling code above. + // + dir_path p (move (n.dir)); - p.normalize (); + if (p.relative ()) + p = scope_->out_path () / p; - scope* ors (root_); - scope* ocs (scope_); - switch_scope (p); + p.normalize (); - variable (t, tt, move (v), tt); + scope* ors (root_); + scope* ocs (scope_); + switch_scope (p); - scope_ = ocs; - root_ = ors; - } - else - { - // Figure out if this is a target or type/pattern specific - // variable. - // - size_t p (n.value.find ('*')); + variable (t, tt, v, att); - if (p == string::npos) - { - target* ot (target_); - target_ = &enter_target (move (n)); - variable (t, tt, move (v), tt); - target_ = ot; + scope_ = ocs; + root_ = ors; } else { - // See tests/variable/type-pattern. + // Figure out if this is a target or type/pattern specific + // variable. // - if (!n.dir.empty ()) - fail (nloc) << "directory in target type/pattern " << n; + size_t p (n.value.find ('*')); - if (n.value.find ('*', p + 1) != string::npos) - fail (nloc) << "multiple wildcards in target type/pattern " - << n; + if (p == string::npos) + { + target* ot (target_); + target_ = &enter_target (move (n)); + variable (t, tt, v, att); + target_ = ot; + } + else + { + // See tests/variable/type-pattern. + // + if (!n.dir.empty ()) + fail (nloc) << "directory in target type/pattern " << n; - // Resolve target type. If none is specified, use the root - // of the hierarchy. - // - const target_type* ti ( - n.untyped () - ? &target::static_type - : scope_->find_target_type (n.type)); + if (n.value.find ('*', p + 1) != string::npos) + fail (nloc) << "multiple wildcards in target type/pattern " + << n; - if (ti == nullptr) - fail (nloc) << "unknown target type " << n.type; + // Resolve target type. If none is specified, use the root + // of the hierarchy. + // + const target_type* ti ( + n.untyped () + ? &target::static_type + : scope_->find_target_type (n.type)); - if (tt == type::equal_plus) - fail (t) << "prepend to target type/pattern-specific " - << "variable " << v; + if (ti == nullptr) + fail (nloc) << "unknown target type " << n.type; - if (tt == type::plus_equal) - fail (t) << "append to target type/pattern-specific " - << "variable " << v; + if (att == type::equal_plus) + fail (at) << "prepend to target type/pattern-specific " + << "variable " << v; - const auto& var (var_pool.find (move (v))); + if (att == type::plus_equal) + fail (at) << "append to target type/pattern-specific " + << "variable " << v; - // Note: expand variables in the value in the context of - // the scope. - // - names_type vns (variable_value (t, tt, var)); - value& val ( - scope_->target_vars[*ti][move (n.value)].assign (var).first); - val.assign (move (vns), var); + const auto& var (var_pool.find (v)); + + // Note: expand variables in the value in the context of + // the scope. + // + names_type vns (variable_value (t, tt, var)); + value& val (scope_->target_vars[*ti][move (n.value)].assign ( + var).first); + val.assign (move (vns), var); + } } + + rg.play (); // Replay. } } // Dependency declaration. @@ -500,7 +505,7 @@ namespace build // The rest should be a list of buildfiles. Parse them as names // to get variable expansion and directory prefixes. // - lexer_->mode (lexer_mode::value); + mode (lexer_mode::value); next (t, tt); const location l (get_location (t, &path_)); names_type ns (tt != type::newline && tt != type::eos @@ -581,7 +586,7 @@ namespace build // The rest should be a list of buildfiles. Parse them as names // to get variable expansion and directory prefixes. // - lexer_->mode (lexer_mode::value); + mode (lexer_mode::value); next (t, tt); const location l (get_location (t, &path_)); names_type ns (tt != type::newline && tt != type::eos @@ -746,7 +751,7 @@ namespace build ? &scope_->assign (*var) : &scope_->append (*var); next (t, tt); // Consume =/=+/+=. - lexer_->mode (lexer_mode::value); + mode (lexer_mode::value); next (t, tt); } } @@ -797,7 +802,7 @@ namespace build // The rest is a value. Parse it as names to get variable expansion. // build::import() will check the names, if required. // - lexer_->mode (lexer_mode::value); + mode (lexer_mode::value); next (t, tt); if (tt != type::newline && tt != type::eos) @@ -822,7 +827,7 @@ namespace build // The rest should be a list of module names. Parse them as names // to get variable expansion, etc. // - lexer_->mode (lexer_mode::pairs, '@'); + mode (lexer_mode::pairs, '@'); next (t, tt); const location l (get_location (t, &path_)); names_type ns (tt != type::newline && tt != type::eos @@ -1060,8 +1065,7 @@ namespace build // to the variable value lexing mode so that we don't treat special // characters (e.g., ':') as the end of the names. // - lexer_->mode (lexer_mode::value); - + mode (lexer_mode::value); next (t, tt); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) @@ -1120,9 +1124,9 @@ namespace build variable_value (token& t, type& tt, const variable_type& var) { if (var.pairs != '\0') - lexer_->mode (lexer_mode::pairs, var.pairs); + mode (lexer_mode::pairs, var.pairs); else - lexer_->mode (lexer_mode::value); + mode (lexer_mode::value); next (t, tt); return (tt != type::newline && tt != type::eos @@ -1133,7 +1137,7 @@ namespace build parser::names_type parser:: eval (token& t, type& tt) { - lexer_->mode (lexer_mode::eval); + mode (lexer_mode::eval); next (t, tt); names_type ns (tt != type::rparen ? names (t, tt) : names_type ()); @@ -1490,7 +1494,7 @@ namespace build // it on and switch to the eval mode if what we get next // is a paren. // - lexer_->mode (lexer_mode::variable); + mode (lexer_mode::variable); next (t, tt); loc = get_location (t, &path_); @@ -1499,7 +1503,7 @@ namespace build n = t.value; else if (tt == type::lparen) { - lexer_->expire_mode (); + expire_mode (); names_type ns (eval (t, tt)); // Make sure the result of evaluation is a single, simple name. @@ -1723,7 +1727,7 @@ namespace build count = 1; } - ns.back ().pair = lexer_->pair_separator (); + ns.back ().pair = t.pair; tt = peek (); continue; } @@ -1802,6 +1806,7 @@ namespace build bool parser:: keyword (token& t) { + assert (replay_ == replay::stop); // Can't be used in a replay. assert (t.type == type::name); // The goal here is to allow using keywords as variable names and @@ -1873,7 +1878,7 @@ namespace build // Turn on pairs recognition with '@' as the pair separator (e.g., // src_root/@out_root/exe{foo bar}). // - lexer_->mode (lexer_mode::pairs, '@'); + mode (lexer_mode::pairs, '@'); token t (type::eos, false, 0, 0); type tt; @@ -1915,7 +1920,7 @@ namespace build if (tt != type::name && tt != type::lcbrace && // Untyped name group: '{foo ...' tt != type::dollar && // Variable expansion: '$foo ...' - !(tt == type::lparen && lexer_->mode () == lexer_mode::quoted) && + !(tt == type::lparen && mode () == lexer_mode::quoted) && tt != type::pair_separator) // Empty pair LHS: '@foo ...' fail (t) << "operation or target expected instead of " << t; @@ -2164,13 +2169,16 @@ namespace build type parser:: next (token& t, type& tt) { - if (!peeked_) - t = lexer_->next (); - else + if (peeked_) { t = move (peek_); peeked_ = false; } + else + t = (replay_ == replay::play ? replay_next () : lexer_->next ()); + + if (replay_ == replay::save) + replay_data_.push_back (t); tt = t.type; return tt; @@ -2181,7 +2189,7 @@ namespace build { if (!peeked_) { - peek_ = lexer_->next (); + peek_ = (replay_ == replay::play ? replay_next () : lexer_->next ()); peeked_ = true; } |