From 8d2e541ab1aa24140eb680fb046e49a4a3f0bbd2 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 12 Oct 2016 14:51:27 +0200 Subject: Various design/implementation cleanups --- build2/bin/init.cxx | 4 +- build2/config/utility | 6 +- build2/context.cxx | 2 +- build2/dist/operation.cxx | 2 +- build2/file.cxx | 4 +- build2/lexer | 91 ++++++++---- build2/lexer.cxx | 342 ++++++++++++++++++++++------------------------ build2/parser | 10 +- build2/parser.cxx | 64 ++++----- build2/scope | 8 +- build2/target | 12 +- build2/target.cxx | 25 ++-- build2/token | 76 ++++++++--- build2/utility | 5 +- build2/variable | 166 +++++++++++++--------- build2/variable.cxx | 23 ++-- build2/variable.ixx | 29 +++- 17 files changed, 503 insertions(+), 366 deletions(-) diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx index 59d9ce6..498118f 100644 --- a/build2/bin/init.cxx +++ b/build2/bin/init.cxx @@ -179,7 +179,7 @@ namespace build2 // config.bin.target // { - const variable& var (var_pool.find ("config.bin.target")); + const variable& var (var_pool["config.bin.target"]); // We first see if the value was specified via the configuration // mechanism. @@ -255,7 +255,7 @@ namespace build2 // config.bin.pattern // { - const variable& var (var_pool.find ("config.bin.pattern")); + const variable& var (var_pool["config.bin.pattern"]); // We first see if the value was specified via the configuration // mechanism. diff --git a/build2/config/utility b/build2/config/utility index aa7dc7e..de75de4 100644 --- a/build2/config/utility +++ b/build2/config/utility @@ -51,7 +51,7 @@ namespace build2 uint64_t save_flags = 0) { return required ( - root, var_pool.find (name), default_value, override, save_flags); + root, var_pool[name], default_value, override, save_flags); } inline pair, bool> @@ -77,7 +77,7 @@ namespace build2 inline pair omitted (scope& root, const string& name) { - return omitted (root, var_pool.find (name)); + return omitted (root, var_pool[name]); } // Set, if necessary, an optional config.* variable. In particular, @@ -93,7 +93,7 @@ namespace build2 inline const value& optional (scope& root, const string& var) { - return optional (root, var_pool.find (var)); + return optional (root, var_pool[var]); } // Check whether there are any variables specified from the config diff --git a/build2/context.cxx b/build2/context.cxx index 1c50610..d1d9b54 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -158,7 +158,7 @@ namespace build2 c == '%' ? variable_visibility::project : variable_visibility::normal); - const variable& var (var_pool.find (n)); + const variable& var (var_pool[n]); const char* k (tt == token_type::assign ? ".__override" : tt == token_type::append ? ".__suffix" : ".__prefix"); diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx index e2d283c..cb3a6f3 100644 --- a/build2/dist/operation.cxx +++ b/build2/dist/operation.cxx @@ -193,7 +193,7 @@ namespace build2 // entered. // action_targets files; - const variable& dist_var (var_pool.find ("dist")); + const variable& dist_var (var_pool["dist"]); for (const auto& pt: targets) { diff --git a/build2/file.cxx b/build2/file.cxx index eaf503d..7e94e03 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -290,7 +290,7 @@ namespace build2 return make_pair (value (), false); } - const variable& var (var_pool.find (move (t.value))); + const variable& var (var_pool[t.value]); parser p; temp_scope tmp (*global_scope); @@ -559,7 +559,7 @@ namespace build2 // NULL value indicates that we found no subprojects. // { - const variable& var (var_pool.find ("subprojects")); + const variable& var (var_pool["subprojects"]); auto rp (root.vars.insert (var)); // Set NULL by default. value& v (rp.first); diff --git a/build2/lexer b/build2/lexer index 827d141..570b753 100644 --- a/build2/lexer +++ b/build2/lexer @@ -21,7 +21,7 @@ namespace build2 // characters (e.g., '+', '=') as special so that we can use them in the // variable values, e.g., 'foo = g++'. In contrast, in the variable mode, we // restrict certain character (e.g., '/') from appearing in the name. The - // eval mode is used in the evaluation context. Quoted is an internal mode + // eval mode is used in the evaluation context. Quoted are internal modes // and should not be set explicitly. // // Note that the normal, value, and eval modes split names separated by the @@ -31,7 +31,29 @@ namespace build2 // automatically reset after the end of the line. The variable mode is reset // after the name token. And the eval mode is reset after the closing ')'. // - enum class lexer_mode {normal, variable, value, eval, quoted}; + + // Extendable/inheritable enum-like class. + // + struct lexer_mode + { + enum + { + normal, + variable, + value, + eval, + single_quoted, + double_quoted, + + value_next + }; + + using value_type = uint16_t; + + lexer_mode (value_type v = normal): v_ (v) {} + operator value_type () const {return v_;} + value_type v_; + }; class lexer: protected butl::char_scanner { @@ -44,26 +66,17 @@ namespace build2 const path& name, const char* escapes = nullptr, void (*processor) (token&, const lexer&) = nullptr) - : char_scanner (is), - fail (name), - escapes_ (escapes), - processor_ (processor), - sep_ (false) - { - mode (lexer_mode::normal); - } + : lexer (is, name, escapes, processor, true) {} const path& name () const {return fail.name_;} - // Note: sets mode for the next token. For the value mode the second - // argument can be used to specify an alternative separator character. + // Note: sets mode for the next token. The second argument can be used + // to specify an alternative separator character (if the mode supports + // pair separators). // - void - mode (lexer_mode m, char pair_separator = '@') - { - state_.push (state{m, pair_separator}); - } + virtual void + mode (lexer_mode, char pair_separator = '@'); // Expire the current mode early. // @@ -74,7 +87,7 @@ namespace build2 mode () const {return state_.top ().mode;} char - pair_separator () const {return state_.top ().pair_separator;} + pair_separator () const {return state_.top ().sep_pair;} // Scanner. Note that it is ok to call next() again after getting eos. // @@ -88,8 +101,11 @@ namespace build2 pair peek_char (); - private: - token + protected: + // If you extend the lexer and add a custom lexer mode, then you must + // override next_impl() and handle the custom mode there. + // + virtual token next_impl (); token @@ -110,7 +126,7 @@ namespace build2 // Diagnostics. // - private: + protected: struct fail_mark_base: build2::fail_mark_base { fail_mark_base (const path& n): name_ (n) {} @@ -122,17 +138,44 @@ namespace build2 }; typedef diag_mark fail_mark; - private: fail_mark fail; + // Lexer state. + // + protected: + lexer (istream& is, + const path& n, + const char* e, + void (*p) (token&, const lexer&), + bool sm) + : char_scanner (is), + fail (n), + escapes_ (e), + processor_ (p), + sep_ (false) + { + if (sm) + mode (lexer_mode::normal); + } + const char* escapes_; void (*processor_) (token&, const lexer&); - struct state { lexer_mode mode; - char pair_separator; + + char sep_pair; + bool sep_space; // Are whitespaces separators (see skip_spaces())? + + // Name separator characters. For two-character sequence put the first + // one in sep_first and the second one in the corresponding position of + // sep_second. If it's a single-character sequence, then put space in + // sep_second. If there are multiple sequences that start with the same + // character, then repeat the first character in sep_first. + // + const char* sep_first; + const char* sep_second; }; std::stack state_; diff --git a/build2/lexer.cxx b/build2/lexer.cxx index a5a4a3a..7b39623 100644 --- a/build2/lexer.cxx +++ b/build2/lexer.cxx @@ -10,7 +10,7 @@ using namespace std; namespace build2 { - typedef token_type type; + using type = token_type; token lexer:: next () @@ -24,13 +24,57 @@ namespace build2 pair lexer:: peek_char () { - // In the quoted mode we don't skip spaces. - // - sep_ = state_.top ().mode != lexer_mode::quoted && skip_spaces (); + sep_ = skip_spaces (); xchar c (peek ()); return make_pair (eos (c) ? '\0' : char (c), sep_); } + void lexer:: + mode (lexer_mode m, char ps) + { + const char* s1 (nullptr); + const char* s2 (nullptr); + char p ('\0'); + bool s (true); + + switch (m) + { + case lexer_mode::normal: + { + s1 = ":=+ $(){}[]#\t\n"; + s2 = " = "; + p = ps; + break; + } + case lexer_mode::value: + { + s1 = " $(){}[]#\t\n"; + s2 = " "; + p = ps; + break; + } + case lexer_mode::eval: + { + s1 = ":<>=! $(){}[]#\t\n"; + s2 = " == "; + p = ps; + break; + } + case lexer_mode::single_quoted: + case lexer_mode::double_quoted: + s = false; + // Fall through. + case lexer_mode::variable: + { + // These are handled in an ad hoc way in name(). + break; + } + default: assert (false); // Unhandled custom mode. + } + + state_.push (state {m, p, s, s1, s2}); + } + token lexer:: next_impl () { @@ -40,9 +84,12 @@ namespace build2 // switch (m) { + case lexer_mode::normal: + case lexer_mode::variable: + case lexer_mode::value: break; case lexer_mode::eval: return next_eval (); - case lexer_mode::quoted: return next_quoted (); - default: break; + case lexer_mode::double_quoted: return next_quoted (); + default: assert (false); // Unhandled custom mode. } bool sep (skip_spaces ()); @@ -56,13 +103,13 @@ namespace build2 // Handle pair separator. // if ((m == lexer_mode::normal || m == lexer_mode::value) && - c == state_.top ().pair_separator) + c == state_.top ().sep_pair) return token (type::pair_separator, sep, ln, cn); switch (c) { - // NOTE: remember to update name(), next_eval() if adding new - // special characters. + // NOTE: remember to update mode(), next_eval() if adding new special + // characters. // case '\n': { @@ -88,8 +135,8 @@ namespace build2 { switch (c) { - // NOTE: remember to update name(), next_eval() if adding new - // special characters. + // NOTE: remember to update mode(), next_eval() if adding new special + // characters. // case ':': return token (type::colon, sep, ln, cn); case '=': @@ -136,7 +183,7 @@ namespace build2 // Handle pair separator. // - if (c == state_.top ().pair_separator) + if (c == state_.top ().sep_pair) return token (type::pair_separator, sep, ln, cn); // Note: we don't treat [ and ] as special here. Maybe can use them for @@ -144,7 +191,7 @@ namespace build2 // switch (c) { - // NOTE: remember to update name() if adding new special characters. + // NOTE: remember to update mode() if adding new special characters. // case '\n': fail (c) << "newline in evaluation context"; case ':': return token (type::colon, sep, ln, cn); @@ -214,73 +261,87 @@ namespace build2 token lexer:: name (bool sep) { + lexer_mode m (state_.top ().mode); + xchar c (peek ()); assert (!eos (c)); uint64_t ln (c.line), cn (c.column); - string lexeme; - lexer_mode m (state_.top ().mode); - char ps (state_.top ().pair_separator); - bool quoted (m == lexer_mode::quoted); + string lexeme; + bool quoted (m == lexer_mode::double_quoted); for (; !eos (c); c = peek ()) { + // First handle escape sequences. + // + if (c == '\\') + { + // In the variable mode we treat the beginning of the escape sequence + // as a separator (think \"$foo\"). + // + if (m == lexer_mode::variable) + break; + + get (); + xchar p (peek ()); + + if (escapes_ == nullptr || + (!eos (p) && strchr (escapes_, p) != nullptr)) + { + get (); + + if (eos (p)) + fail (p) << "unterminated escape sequence"; + + if (p != '\n') // Ignore if line continuation. + lexeme += p; + + continue; + } + else + unget (c); // Treat as a normal character. + } + bool done (false); - // Handle the pair separator. + // Next take care of the double-quoted mode. This one is tricky since + // we push/pop modes while accumulating the same lexeme for example: // - if ((m == lexer_mode::normal || - m == lexer_mode::value || - m == lexer_mode::eval) && c == ps) - break; - - // The following characters are only special in the normal and - // variable name modes. + // foo" bar "baz // - if (m == lexer_mode::normal || m == lexer_mode::variable) + if (m == lexer_mode::double_quoted) { switch (c) { - case ':': - case '=': + // Only these two characters are special in the double-quoted mode. + // + case '$': + case '(': { done = true; break; } - case '+': + // End quote. + // + case '\"': { get (); - done = (peek () == '='); - unget (c); - break; + state_.pop (); + m = state_.top ().mode; + continue; } } - - if (done) - break; } - - // These extra characters are treated as the name end in the variable - // mode. + // We also handle the variable mode in an ad hoc way. // - if (m == lexer_mode::variable) + else if (m == lexer_mode::variable) { - //@@ Maybe we should rather test for allowed characeters (e.g., - // alnum plus '_' and '.')? - // - switch (c) + if (!alnum (c) && c != '_') { - case '/': - case '-': - case '"': - case '\'': - case '\\': - { + if (c != '.') done = true; - break; - } - case '.': + else { // Normally '.' is part of the variable (namespace separator) // unless it is trailing (think $major.$minor). @@ -289,161 +350,84 @@ namespace build2 xchar p (peek ()); done = eos (p) || !(alnum (p) || p == '_'); unget (c); - break; - } - } - - if (done) - break; - } - - // These extra characters are treated as the name end in the eval mode. - // - if (m == lexer_mode::eval) - { - switch (c) - { - case ':': - case '<': - case '>': - { - done = true; - break; - } - case '=': - case '!': - { - get (); - done = (peek () == '='); - unget (c); - break; } } - - if (done) - break; } - - // Handle escape sequences. - // - if (c == '\\') + else { - get (); - xchar e (peek ()); - - if (escapes_ == nullptr || - (!eos (e) && strchr (escapes_, e) != nullptr)) - { - get (); - - if (eos (e)) - fail (e) << "unterminated escape sequence"; - - if (e != '\n') // Ignore. - lexeme += e; - - continue; - } + // First check if it's a pair separator. + // + const state& st (state_.top ()); + if (c == st.sep_pair) + done = true; else - unget (c); // Treat as a normal character. - } - - // If we are quoted, these are ordinary characters. - // - if (m != lexer_mode::quoted) - { - switch (c) { - case ' ': - case '\t': - case '\n': - case '#': - case '{': - case '}': - case '[': - case ']': - case ')': - { - done = true; - break; - } - case '\'': + // Then see if this character or character sequence is a separator. + // + for (const char* p (strchr (st.sep_first, c)); + p != nullptr; + p = done ? nullptr : strchr (p + 1, c)) { - // If we are in the variable mode, then treat quote as just - // another separator. + char s (st.sep_second[p - st.sep_first]); + + // See if it has a second. // - if (m == lexer_mode::variable) + if (s != ' ') { - done = true; - break; + get (); + done = (peek () == s); + unget (c); } else + done = true; + } + } + + // Handle single and double quotes unless they were considered + // separators. + // + if (!done) + { + switch (c) + { + case '\'': { - get (); + // Enter the single-quoted mode in case the derived lexer needs + // to notice this. + // + mode (lexer_mode::single_quoted); + get (); for (c = get (); !eos (c) && c != '\''; c = get ()) lexeme += c; if (eos (c)) fail (c) << "unterminated single-quoted sequence"; + state_.pop (); + quoted = true; continue; } - } - } - - if (done) - break; - } - - switch (c) - { - case '$': - case '(': - { - done = true; - break; - } - case '\"': - { - // If we are in the variable mode, then treat quote as just - // another separator. - // - if (m == lexer_mode::variable) - { - done = true; - break; - } - else - { - get (); - - if (m == lexer_mode::quoted) - state_.pop (); - else + case '\"': { - mode (lexer_mode::quoted); + get (); + mode ((m = lexer_mode::double_quoted)); quoted = true; + continue; } - - m = state_.top ().mode; - continue; } } - default: - { - get (); - lexeme += c; - continue; - } } - assert (done); - break; + if (done) + break; + + get (); + lexeme += c; } - if (m == lexer_mode::quoted && eos (c)) + if (eos (c) && m == lexer_mode::double_quoted) fail (c) << "unterminated double-quoted sequence"; // Expire variable mode at the end of the name. @@ -452,6 +436,7 @@ namespace build2 state_.pop (); return token (lexeme, sep, quoted, ln, cn); + } bool lexer:: @@ -460,6 +445,11 @@ namespace build2 bool r (sep_); sep_ = false; + // In some modes we don't skip spaces. + // + if (!state_.top ().sep_space) + return r; + xchar c (peek ()); bool start (c.column == 1); diff --git a/build2/parser b/build2/parser index cc28695..49bf59c 100644 --- a/build2/parser +++ b/build2/parser @@ -99,7 +99,7 @@ namespace build2 // Note: calls attributes_push() that the caller must pop. // value - variable_value (token&, token_type&); + variable_value (token&, token_type&, lexer_mode = lexer_mode::value); void variable_attributes (const variable_type&); @@ -178,7 +178,7 @@ namespace build2 : value (nullptr); } - // Return true if the parsed value is NOT NULL. + // Append names and return true if the parsed value is NOT NULL. // bool names (token& t, token_type& tt, names_type& ns, bool chunk = false) @@ -247,6 +247,12 @@ namespace build2 // Lexer. // protected: + location + get_location (const token& t) const + { + return build2::get_location (t, *path_); + } + token_type next (token&, token_type&); diff --git a/build2/parser.cxx b/build2/parser.cxx index 05a2594..0853c04 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -21,10 +21,7 @@ using namespace std; namespace build2 { - static location - get_location (const token&, const void*); - - typedef token_type type; + using type = token_type; static const dir_path root_dir ("/"); @@ -337,7 +334,7 @@ namespace build2 // // @@ I think we should make ': foo' invalid. // - const location nloc (get_location (t, &path_)); + const location nloc (get_location (t)); names_type ns (tt != type::colon ? names (t, tt) : names_type ({name ("dir", string ())})); @@ -446,7 +443,7 @@ namespace build2 tt == type::newline || tt == type::eos) { - const location ploc (get_location (t, &path_)); + const location ploc (get_location (t)); names_type pns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -459,7 +456,7 @@ namespace build2 type att (tt); const variable_type& var ( - var_pool.find ( + var_pool.insert ( variable_name (move (pns), ploc))); // Handle variable attributes. @@ -720,7 +717,7 @@ namespace build2 if (tt == type::assign || tt == type::prepend || tt == type::append) { const variable_type& var ( - var_pool.find (variable_name (move (ns), nloc))); + var_pool.insert (variable_name (move (ns), nloc))); // Handle variable attributes. // @@ -770,7 +767,7 @@ namespace build2 // mode (lexer_mode::value); next (t, tt); - const location l (get_location (t, &path_)); + const location l (get_location (t)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -846,7 +843,7 @@ namespace build2 // mode (lexer_mode::value); next (t, tt); - const location l (get_location (t, &path_)); + const location l (get_location (t)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -1046,7 +1043,7 @@ namespace build2 size_t p (t.value.find ('=')); if (p != string::npos) - var = &var_pool.find (split (p)); + var = &var_pool.insert (split (p)); // // This could still be the 'foo =...' case. // @@ -1061,7 +1058,7 @@ namespace build2 (v[p = 0] == '=' || (n > 1 && v[0] == '+' && v[p = 1] == '='))) { - var = &var_pool.find (t.value); + var = &var_pool[t.value]; next (t, tt); // Get the peeked token. split (p); // Returned name should be empty. } @@ -1089,7 +1086,7 @@ namespace build2 // The rest should be a list of projects and/or targets. Parse // them as names to get variable expansion and directory prefixes. // - const location l (get_location (t, &path_)); + const location l (get_location (t)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -1177,7 +1174,7 @@ namespace build2 // mode (lexer_mode::value); next (t, tt); - const location l (get_location (t, &path_)); + const location l (get_location (t)); names_type ns (tt != type::newline && tt != type::eos ? names (t, tt) : names_type ()); @@ -1251,7 +1248,7 @@ namespace build2 << "definition"; string dn (move (t.value)); - const location dnl (get_location (t, &path_)); + const location dnl (get_location (t)); if (next (t, tt) != type::colon) fail (t) << "expected ':' instead of " << t << " in target type " @@ -1317,7 +1314,7 @@ namespace build2 // Parse as names to get variable expansion, evaluation, etc. // - const location nsl (get_location (t, &path_)); + const location nsl (get_location (t)); names_type ns (names (t, tt)); // Should evaluate to 'true' or 'false'. @@ -1471,9 +1468,9 @@ namespace build2 } value parser:: - variable_value (token& t, type& tt) + variable_value (token& t, type& tt, lexer_mode m) { - mode (lexer_mode::value); + mode (m); next (t, tt); // Parse value attributes if any. Note that it's ok not to have anything @@ -1600,7 +1597,7 @@ namespace build2 if (type != nullptr) { if (var != nullptr && var->type != nullptr && var->type != type) - fail (l) << "confliction variable " << var->name << " type " + fail (l) << "conflicting variable " << var->name << " type " << var->type->name << " and value type " << type->name; if (kind == token_type::assign) @@ -1618,7 +1615,7 @@ namespace build2 else if (v.type == nullptr) typify (v, *type, var); else if (v.type != type) - fail (l) << "confliction original value type " << v.type->name + fail (l) << "conflicting original value type " << v.type->name << " and append/prepend value type " << type->name; } } @@ -1700,7 +1697,7 @@ namespace build2 // while (tt != type::rparen) { - const location l (get_location (t, &path_)); + const location l (get_location (t)); // Remember to update parse_value above if adding any new name // separators here. @@ -1815,7 +1812,7 @@ namespace build2 attributes_push (token& t, token_type& tt, bool standalone) { attributes_.push ( - attributes {tt == type::lsbrace, get_location (t, &path_), {}}); + attributes {tt == type::lsbrace, get_location (t), {}}); attributes& a (attributes_.top ()); const location& l (a.loc); @@ -1913,7 +1910,7 @@ namespace build2 if (peek () == type::lcbrace && !peeked ().separated) { next (t, tt); // Get '{'. - const location loc (get_location (t, &path_)); + const location loc (get_location (t)); names_type x; // Parse into a separate list of names. names_trailer (t, tt, x, 0, nullptr, nullptr, nullptr); @@ -2103,7 +2100,6 @@ namespace build2 p = last ? string::npos : p - (p1 + 1); // Now process the project name. - // @@ Validate it. // proj.resize (p1); @@ -2243,7 +2239,7 @@ namespace build2 // mode (lexer_mode::variable); next (t, tt); - loc = get_location (t, &path_); + loc = get_location (t); name qual; string name; @@ -2369,7 +2365,7 @@ namespace build2 // Lookup. // - const auto& var (var_pool.find (move (name))); + const auto& var (var_pool.insert (move (name))); auto l (target_ != nullptr ? (*target_)[var] : (*scope_)[var]); if (!l) @@ -2398,7 +2394,7 @@ namespace build2 } else { - loc = get_location (t, &path_); + loc = get_location (t); result = eval (t, tt); tt = peek (); @@ -2788,11 +2784,11 @@ namespace build2 if (tt != type::name && tt != type::lcbrace && // Untyped name group: '{foo ...' tt != type::dollar && // Variable expansion: '$foo ...' - !(tt == type::lparen && mode () == lexer_mode::quoted) && + !(tt == type::lparen && mode () == lexer_mode::double_quoted) && tt != type::pair_separator) // Empty pair LHS: '@foo ...' fail (t) << "operation or target expected instead of " << t; - const location l (get_location (t, &path_)); // Start of names. + const location l (get_location (t)); // Start of names. // This call will parse the next chunk of output and produce // zero or more names. @@ -2815,7 +2811,7 @@ namespace build2 // Inside '(' and ')' we have another, nested, buildspec. // next (t, tt); - const location l (get_location (t, &path_)); // Start of nested names. + const location l (get_location (t)); // Start of nested names. buildspec nbs (buildspec_clause (t, tt, type::rparen)); // Merge the nested buildspec into ours. But first determine @@ -3080,12 +3076,4 @@ namespace build2 return peek_.type; } - - static location - get_location (const token& t, const void* data) - { - assert (data != nullptr); // &parser::path_ - const path* p (*static_cast (data)); - return location (p, t.line, t.column); - } } diff --git a/build2/scope b/build2/scope index 2efa899..154ee9d 100644 --- a/build2/scope +++ b/build2/scope @@ -96,7 +96,7 @@ namespace build2 lookup operator[] (const string& name) const { - return operator[] (var_pool.find (name)); + return operator[] (var_pool[name]); } // As above, but include target type/pattern-specific variables. @@ -110,7 +110,7 @@ namespace build2 lookup find (const string& var, const target_key& tk) const { - return find (var_pool.find (var), tk); + return find (var_pool[var], tk); } lookup @@ -122,7 +122,7 @@ namespace build2 lookup find (const string& var, const target_type& tt, const string& tn) const { - return find (var_pool.find (var), tt, tn); + return find (var_pool[var], tt, tn); } pair @@ -178,7 +178,7 @@ namespace build2 append (const variable&); value& - append (const string& name) {return append (var_pool.find (name));} + append (const string& name) {return append (var_pool[name]);} // Target type/pattern-specific variables. // diff --git a/build2/target b/build2/target index 681fcee..8fc0e8c 100644 --- a/build2/target +++ b/build2/target @@ -359,7 +359,7 @@ namespace build2 lookup operator[] (const string& name) const { - return operator[] (var_pool.find (name)); + return operator[] (var_pool[name]); } // As above but also return the depth at which the value is found. The @@ -370,6 +370,7 @@ namespace build2 // that given two lookups from the same target, we can say which one came // earlier. If no value is found, then the depth is set to ~0. // + // pair find (const variable& var) const { @@ -380,10 +381,13 @@ namespace build2 } pair - find (const string& name) const {return find (var_pool.find (name));} + find (const string& name) const {return find (var_pool[name]);} + // If target_only is true, then only look in target and its target group + // without continuing in scopes. + // pair - find_original (const variable&) const; + find_original (const variable&, bool target_only = false) const; // Return a value suitable for assignment. See scope for details. // @@ -409,7 +413,7 @@ namespace build2 value& append (const string& name) { - return append (var_pool.find (name)); + return append (var_pool[name]); } public: diff --git a/build2/target.cxx b/build2/target.cxx index 8541deb..cdecfd3 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -117,7 +117,7 @@ namespace build2 } pair target:: - find_original (const variable& var) const + find_original (const variable& var, bool target_only) const { pair r (lookup (), 0); @@ -147,15 +147,20 @@ namespace build2 // if (!r.first) { - auto p (base_scope ().find_original ( - var, - &type (), - &name, - g != nullptr ? &g->type () : nullptr, - g != nullptr ? &g->name : nullptr)); - - r.first = move (p.first); - r.second = r.first ? r.second + p.second : p.second; + if (!target_only) + { + auto p (base_scope ().find_original ( + var, + &type (), + &name, + g != nullptr ? &g->type () : nullptr, + g != nullptr ? &g->name : nullptr)); + + r.first = move (p.first); + r.second = r.first ? r.second + p.second : p.second; + } + else + r.second = size_t (~0); } return r; diff --git a/build2/token b/build2/token index c896ba1..04a7ebd 100644 --- a/build2/token +++ b/build2/token @@ -8,31 +8,46 @@ #include #include +#include + namespace build2 { - enum class token_type + // Extendable/inheritable enum-like class. + // + struct token_type { - eos, - name, - newline, - pair_separator, - colon, - lcbrace, // { - rcbrace, // } - lsbrace, // [ - rsbrace, // ] - assign, // = - prepend, // =+ - append, // += - equal, // == - not_equal, // != - less, // < - greater, // > - less_equal, // <= - greater_equal, // >= - dollar, - lparen, - rparen + enum + { + eos, + name, + newline, + pair_separator, + colon, + lcbrace, // { + rcbrace, // } + lsbrace, // [ + rsbrace, // ] + assign, // = + prepend, // =+ + append, // += + equal, // == + not_equal, // != + less, // < + greater, // > + less_equal, // <= + greater_equal, // >= + dollar, // $ + lparen, // ( + rparen, // ) + + value_next + }; + + using value_type = uint16_t; + + token_type (value_type v = eos): v_ (v) {} + operator value_type () const {return v_;} + value_type v_; }; class token @@ -64,6 +79,23 @@ namespace build2 // ostream& operator<< (ostream&, const token&); + + // Diagnostics plumbing. We assume that any diag stream for which we can use + // token as location has its aux data pointing to pointer to path. + // + inline location + get_location (const token& t, const path& p) + { + return location (&p, t.line, t.column); + } + + inline location + get_location (const token& t, const void* data) + { + assert (data != nullptr); // E.g., must be &parser::path_. + const path* p (*static_cast (data)); + return get_location (t, *p); + } } #endif // BUILD2_TOKEN diff --git a/build2/utility b/build2/utility index 6c73e8e..396c79e 100644 --- a/build2/utility +++ b/build2/utility @@ -34,8 +34,11 @@ namespace build2 // // - using butl::combine_hash; using butl::reverse_iterate; + using butl::compare_c_string; + using butl::compare_pointer_target; + //using butl::hash_pointer_target; + using butl::combine_hash; using butl::casecmp; using butl::case_compare_string; using butl::case_compare_c_string; diff --git a/build2/variable b/build2/variable index 000ac9c..326c290 100644 --- a/build2/variable +++ b/build2/variable @@ -8,9 +8,10 @@ #include #include // hash #include // aligned_storage -#include +#include #include +#include // map_key #include #include @@ -654,107 +655,144 @@ namespace build2 static const string type_name; static const build2::value_type value_type; }; -} -// Variable map. -// -namespace std -{ - template <> - struct hash: hash + // variable_pool + // + class variable_pool { - size_t - operator() (const build2::variable& v) const noexcept - { - return hash::operator() (v.name); - } - }; -} + public: + // Find existing or insert new. Find bias. + // + const variable& + operator[] (const string& name); -namespace butl -{ - template <> - struct compare_prefix>: - compare_prefix - { - typedef compare_prefix base; + // Find existing or insert new. Insert bias. + // + const variable& + insert (string name); - explicit - compare_prefix (char d): base (d) {} + // Return NULL if there is no variable with this name. + // + const variable* + find (const string& name); - bool - operator() (const build2::variable& x, const build2::variable& y) const + // Insert or override. + // + template + const variable& + insert (string name) { - return base::operator() (x.name, y.name); + return insert ( + move (name), &value_traits::value_type, nullptr, nullptr); } - bool - prefix (const build2::variable& p, const build2::variable& k) const + const variable& + insert (string name, variable_visibility v) { - return base::prefix (p.name, k.name); + return insert (move (name), nullptr, &v, nullptr); } - }; -} -namespace build2 -{ - // variable_pool - // - using variable_pool_base = std::unordered_set; - struct variable_pool: private variable_pool_base - { const variable& - insert (string name, variable_visibility v = variable_visibility::normal) + insert (string name, bool overridable) { - return insert (move (name), nullptr, v, false); + return insert (move (name), nullptr, nullptr, &overridable); } const variable& - insert (string name, - bool overridable, - variable_visibility v = variable_visibility::normal) + insert (string name, bool overridable, variable_visibility v) { - return insert (move (name), nullptr, v, overridable); + return insert (move (name), nullptr, &v, &overridable); } template const variable& - insert (string name, variable_visibility v = variable_visibility::normal) + insert (string name, variable_visibility v) { - return insert (move (name), &value_traits::value_type, v, false); + return insert (move (name), &value_traits::value_type, &v, nullptr); } template const variable& - insert (string name, - bool overridable, - variable_visibility v = variable_visibility::normal) + insert (string name, bool overridable) { return insert ( - move (name), &value_traits::value_type, v, overridable); + move (name), &value_traits::value_type, nullptr, &overridable); } + template const variable& - find (const string& name); //@@ TODO: Move to operator[], remove. - //@@ ranmae var_pool to varpool or vpool? - - const variable& - operator[] (const string& name) {return find (name);} + insert (string name, bool overridable, variable_visibility v) + { + return insert ( + move (name), &value_traits::value_type, &v, &overridable); + } - using variable_pool_base::clear; + void + clear () {map_.clear ();} private: const variable& insert (string name, const build2::value_type*, - variable_visibility, - bool overridable); + const variable_visibility*, + const bool* overridable); + + private: + using key = butl::map_key; + using map = std::unordered_map; + + pair + insert (variable&& var) + { + // Keeping a pointer to the key while moving things during insertion is + // tricky. We could use a C-string instead of C++ for a key but that + // gets hairy very quickly (there is no std::hash for C-strings). So + // let's rely on small object-optimized std::string for now. + // + string n (var.name); + auto r (map_.insert (map::value_type (&n, move (var)))); + + if (r.second) + r.first->first.p = &r.first->second.name; + + return r; + } + + map map_; }; extern variable_pool var_pool; +} - // variable_map - // +// variable_map +// +namespace butl +{ + template <> + struct compare_prefix>: + compare_prefix + { + typedef compare_prefix base; + + explicit + compare_prefix (char d): base (d) {} + + bool + operator() (const build2::variable& x, const build2::variable& y) const + { + return base::operator() (x.name, y.name); + } + + bool + prefix (const build2::variable& p, const build2::variable& k) const + { + return base::prefix (p.name, k.name); + } + }; +} + +namespace build2 +{ class variable_map { public: @@ -794,7 +832,7 @@ namespace build2 lookup operator[] (const string& name) const { - return operator[] (var_pool.find (name)); + return operator[] (var_pool[name]); } // If typed is false, leave the value untyped even if the variable is. @@ -833,13 +871,13 @@ namespace build2 pair, bool> insert (const string& name, bool typed = true) { - return insert (var_pool.find (name), typed); + return insert (var_pool[name], typed); } pair find_namespace (const string& ns) const { - auto r (m_.find_prefix (var_pool.find (ns))); + auto r (m_.find_prefix (var_pool[ns])); return make_pair (const_iterator (r.first), const_iterator (r.second)); } diff --git a/build2/variable.cxx b/build2/variable.cxx index 0134683..cf274be 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -437,7 +437,7 @@ namespace build2 if (n.directory (true)) // Use either the precise or traditional representation depending on - // whethe this is the original name (if it is, then this might not be + // whether this is the original name (if it is, then this might not be // a path after all; think s/foo/bar/). // s = n.original @@ -773,11 +773,18 @@ namespace build2 const variable& variable_pool:: insert (string n, const build2::value_type* t, - variable_visibility v, - bool o) + const variable_visibility* v, + const bool* o) { - auto p (variable_pool_base::insert (variable {move (n), t, nullptr, v})); - const variable& r (*p.first); + auto p ( + insert ( + variable { + move (n), + t, + nullptr, + v != nullptr ? *v : variable_visibility::normal})); + + const variable& r (p.first->second); if (!p.second) { @@ -794,16 +801,16 @@ namespace build2 // were set, in which case the variable will be entered with the // default visibility. // - if (r.visibility != v) + if (v != nullptr && r.visibility != *v) { assert (r.visibility == variable_visibility::normal); // Default. - const_cast (r).visibility = v; // Not changing the key. + const_cast (r).visibility = *v; // Not changing the key. } // Check overridability (all overrides, if any, should already have // been enetered (see context.cxx:reset()). // - if (r.override != nullptr && !o) + if (o != nullptr && r.override != nullptr && !*o) fail << "variable " << r.name << " cannot be overridden"; } diff --git a/build2/variable.ixx b/build2/variable.ixx index 393a796..5c9118b 100644 --- a/build2/variable.ixx +++ b/build2/variable.ixx @@ -591,12 +591,33 @@ namespace build2 // variable_pool // - inline const variable& variable_pool:: + inline const variable* variable_pool:: find (const string& n) { - auto p (variable_pool_base::insert ( - variable {n, nullptr, nullptr, variable_visibility::normal})); - return *p.first; + auto i (map_.find (&n)); + return i != map_.end () ? &i->second : nullptr; + } + + inline const variable& variable_pool:: + insert (string n) + { + // We are not overriding anything so skip the custom insert() checks. + // + auto p ( + insert ( + variable {move (n), nullptr, nullptr, variable_visibility::normal})); + + return p.first->second; + } + + + inline const variable& variable_pool:: + operator[] (const string& n) + { + if (const variable* v = find (n)) + return *v; + else + return insert (n); } // variable_map::iterator_adapter -- cgit v1.1