aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/bin/init.cxx4
-rw-r--r--build2/config/utility6
-rw-r--r--build2/context.cxx2
-rw-r--r--build2/dist/operation.cxx2
-rw-r--r--build2/file.cxx4
-rw-r--r--build2/lexer91
-rw-r--r--build2/lexer.cxx342
-rw-r--r--build2/parser10
-rw-r--r--build2/parser.cxx64
-rw-r--r--build2/scope8
-rw-r--r--build2/target12
-rw-r--r--build2/target.cxx25
-rw-r--r--build2/token76
-rw-r--r--build2/utility5
-rw-r--r--build2/variable166
-rw-r--r--build2/variable.cxx23
-rw-r--r--build2/variable.ixx29
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<reference_wrapper<const value>, bool>
@@ -77,7 +77,7 @@ namespace build2
inline pair<const value*, bool>
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<char, bool>
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<failed>
{
fail_mark_base (const path& n): name_ (n) {}
@@ -122,17 +138,44 @@ namespace build2
};
typedef diag_mark<fail_mark_base> 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> 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<char, bool> 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<const path* const*> (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<lookup, size_t>
@@ -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<lookup, size_t>
find (const variable& var) const
{
@@ -380,10 +381,13 @@ namespace build2
}
pair<lookup, size_t>
- 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<lookup, size_t>
- 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<lookup, size_t> target::
- find_original (const variable& var) const
+ find_original (const variable& var, bool target_only) const
{
pair<lookup, size_t> 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 <build2/types>
#include <build2/utility>
+#include <build2/diagnostics>
+
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<const path* const*> (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
// <butl/utility>
//
- 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 <map>
#include <functional> // hash
#include <type_traits> // aligned_storage
-#include <unordered_set>
+#include <unordered_map>
#include <butl/prefix-map>
+#include <butl/multi-index> // map_key
#include <build2/types>
#include <build2/utility>
@@ -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<build2::variable>: hash<string>
+ // variable_pool
+ //
+ class variable_pool
{
- size_t
- operator() (const build2::variable& v) const noexcept
- {
- return hash<string>::operator() (v.name);
- }
- };
-}
+ public:
+ // Find existing or insert new. Find bias.
+ //
+ const variable&
+ operator[] (const string& name);
-namespace butl
-{
- template <>
- struct compare_prefix<std::reference_wrapper<const build2::variable>>:
- compare_prefix<std::string>
- {
- typedef compare_prefix<std::string> 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 <typename T>
+ const variable&
+ insert (string name)
{
- return base::operator() (x.name, y.name);
+ return insert (
+ move (name), &value_traits<T>::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<variable>;
- 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 <typename T>
const variable&
- insert (string name, variable_visibility v = variable_visibility::normal)
+ insert (string name, variable_visibility v)
{
- return insert (move (name), &value_traits<T>::value_type, v, false);
+ return insert (move (name), &value_traits<T>::value_type, &v, nullptr);
}
template <typename T>
const variable&
- insert (string name,
- bool overridable,
- variable_visibility v = variable_visibility::normal)
+ insert (string name, bool overridable)
{
return insert (
- move (name), &value_traits<T>::value_type, v, overridable);
+ move (name), &value_traits<T>::value_type, nullptr, &overridable);
}
+ template <typename T>
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<T>::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<string>;
+ using map = std::unordered_map<key, variable>;
+
+ pair<map::iterator, bool>
+ 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<std::reference_wrapper<const build2::variable>>:
+ compare_prefix<std::string>
+ {
+ typedef compare_prefix<std::string> 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<reference_wrapper<value>, bool>
insert (const string& name, bool typed = true)
{
- return insert (var_pool.find (name), typed);
+ return insert (var_pool[name], typed);
}
pair<const_iterator, const_iterator>
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<variable&> (r).visibility = v; // Not changing the key.
+ const_cast<variable&> (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