aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-12-14 10:12:23 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-12-14 10:12:23 +0200
commite649f2d988ce4e7a67aee1236850eb0b95a97d56 (patch)
treea1fe2ea19e24ba1d018fe08633f70be8489d607e
parentde3f340fac96aca1e626068cbbb22ec92c1dc4b6 (diff)
Add support for variable prepend operator: =+
-rw-r--r--build/b.cxx4
-rw-r--r--build/file.cxx1
-rw-r--r--build/lexer.cxx54
-rw-r--r--build/parser.cxx30
-rw-r--r--build/root.build2
-rw-r--r--build/token1
-rw-r--r--build/token.cxx1
-rw-r--r--build/variable6
-rw-r--r--build/variable.cxx39
-rw-r--r--tests/variable/prepend/buildfile14
-rw-r--r--tests/variable/prepend/test.out4
-rwxr-xr-xtests/variable/prepend/test.sh3
12 files changed, 129 insertions, 30 deletions
diff --git a/build/b.cxx b/build/b.cxx
index 2193ff5..d72fdfb 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -165,7 +165,9 @@ main (int argc, char* argv[])
token_type tt (l.next ().type);
- if (tt != token_type::equal && tt != token_type::plus_equal)
+ if (tt != token_type::equal &&
+ tt != token_type::equal_plus &&
+ tt != token_type::plus_equal)
break;
parser p;
diff --git a/build/file.cxx b/build/file.cxx
index 73ce7cb..b4b1b77 100644
--- a/build/file.cxx
+++ b/build/file.cxx
@@ -283,6 +283,7 @@ namespace build
if (t.type != token_type::name || t.value != var ||
((tt = lex.next ().type) != token_type::equal &&
+ tt != token_type::equal_plus &&
tt != token_type::plus_equal))
{
error << "variable '" << var << "' expected as first line in " << rbf;
diff --git a/build/lexer.cxx b/build/lexer.cxx
index 77e803c..0835704 100644
--- a/build/lexer.cxx
+++ b/build/lexer.cxx
@@ -8,6 +8,8 @@ using namespace std;
namespace build
{
+ typedef token_type type;
+
token lexer::
next ()
{
@@ -47,7 +49,7 @@ namespace build
uint64_t ln (c.line), cn (c.column);
if (eos (c))
- return token (token_type::eos, sep, ln, cn);
+ return token (type::eos, sep, ln, cn);
switch (c)
{
@@ -61,19 +63,19 @@ namespace build
if (m == lexer_mode::value || m == lexer_mode::pairs)
mode_.pop ();
- return token (token_type::newline, sep, ln, cn);
+ return token (type::newline, sep, ln, cn);
}
- case '{': return token (token_type::lcbrace, sep, ln, cn);
- case '}': return token (token_type::rcbrace, sep, ln, cn);
- case '$': return token (token_type::dollar, sep, ln, cn);
- case '(': return token (token_type::lparen, sep, ln, cn);
- case ')': return token (token_type::rparen, sep, ln, cn);
+ case '{': return token (type::lcbrace, sep, ln, cn);
+ case '}': return token (type::rcbrace, sep, ln, cn);
+ case '$': return token (type::dollar, sep, ln, cn);
+ case '(': return token (type::lparen, sep, ln, cn);
+ case ')': return token (type::rparen, sep, ln, cn);
}
// Handle pair separator.
//
if (m == lexer_mode::pairs && c == pair_separator_)
- return token (token_type::pair_separator, sep, ln, cn);
+ return token (type::pair_separator, sep, ln, cn);
// The following characters are not treated as special in the
// value or pairs mode.
@@ -85,14 +87,24 @@ namespace build
// NOTE: remember to update name(), next_eval() if adding new
// special characters.
//
- case ':': return token (token_type::colon, sep, ln, cn);
- case '=': return token (token_type::equal, sep, ln, cn);
+ case ':': return token (type::colon, sep, ln, cn);
+ case '=':
+ {
+ if (peek () == '+')
+ {
+ get ();
+ return token (type::equal_plus, sep, ln, cn);
+ }
+ else
+ return token (type::equal, sep, ln, cn);
+ }
case '+':
{
- if (get () != '=')
- fail (c) << "expected = after +";
-
- return token (token_type::plus_equal, sep, ln, cn);
+ if (peek () == '=')
+ {
+ get ();
+ return token (type::plus_equal, sep, ln, cn);
+ }
}
}
}
@@ -122,14 +134,14 @@ namespace build
// NOTE: remember to update name() if adding new special characters.
//
case '\n': fail (c) << "newline in evaluation context";
- case '{': return token (token_type::lcbrace, sep, ln, cn);
- case '}': return token (token_type::rcbrace, sep, ln, cn);
- case '$': return token (token_type::dollar, sep, ln, cn);
- case '(': return token (token_type::lparen, sep, ln, cn);
+ case '{': return token (type::lcbrace, sep, ln, cn);
+ case '}': return token (type::rcbrace, sep, ln, cn);
+ case '$': return token (type::dollar, sep, ln, cn);
+ case '(': return token (type::lparen, sep, ln, cn);
case ')':
{
mode_.pop (); // Expire eval mode.
- return token (token_type::rparen, sep, ln, cn);
+ return token (type::rparen, sep, ln, cn);
}
}
@@ -151,8 +163,8 @@ namespace build
switch (c)
{
- case '$': return token (token_type::dollar, false, ln, cn);
- case '(': return token (token_type::lparen, false, ln, cn);
+ case '$': return token (type::dollar, false, ln, cn);
+ case '(': return token (type::lparen, false, ln, cn);
}
// Otherwise it is a name.
diff --git a/build/parser.cxx b/build/parser.cxx
index 7f8b570..1c8f4f6 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -305,7 +305,9 @@ namespace build
// Scope/target-specific variable assignment.
//
- if (tt == type::equal || tt == type::plus_equal)
+ if (tt == type::equal ||
+ tt == type::equal_plus ||
+ tt == type::plus_equal)
{
string v (variable_name (move (pns), ploc));
@@ -376,6 +378,10 @@ namespace build
if (ti == nullptr)
fail (nloc) << "unknown target type " << n.type;
+ if (tt == type::equal_plus)
+ fail (t) << "prepend to target type/pattern-specific "
+ << "variable " << v;
+
if (tt == type::plus_equal)
fail (t) << "append to target type/pattern-specific "
<< "variable " << v;
@@ -460,7 +466,9 @@ namespace build
// Variable assignment.
//
- if (tt == type::equal || tt == type::plus_equal)
+ if (tt == type::equal ||
+ tt == type::equal_plus ||
+ tt == type::plus_equal)
{
variable (t, tt, variable_name (move (ns), nloc), tt);
@@ -729,13 +737,15 @@ namespace build
{
at = peek ();
- if (at == type::equal || at == type::plus_equal)
+ if (at == type::equal ||
+ at == type::equal_plus ||
+ at == type::plus_equal)
{
var = &var_pool.find (t.value);
val = at == type::equal
? &scope_->assign (*var)
: &scope_->append (*var);
- next (t, tt); // Consume =/+=.
+ next (t, tt); // Consume =/=+/+=.
lexer_->mode (lexer_mode::value);
next (t, tt);
}
@@ -759,6 +769,8 @@ namespace build
{
if (at == type::equal)
val->assign (move (r), *var);
+ else if (at == type::equal_plus)
+ val->prepend (move (r), *var);
else
val->append (move (r), *var);
}
@@ -1092,7 +1104,11 @@ namespace build
value& v (target_ != nullptr
? target_->append (var)
: scope_->append (var));
- v.append (move (vns), var);
+
+ if (kind == type::equal_plus)
+ v.prepend (move (vns), var);
+ else
+ v.append (move (vns), var);
}
}
@@ -1791,8 +1807,8 @@ namespace build
//
// - it is not quoted [so a keyword can always be escaped] and
// - next token is eos or '(' [so if(...) will work] or
- // - next token is separated and is not '=' or '+=' [which means a
- // "directive trailer" can never start with one of them].
+ // - next token is separated and is not '=', '=+', or '+=' [which
+ // means a "directive trailer" can never start with one of them].
//
// See tests/keyword.
//
diff --git a/build/root.build b/build/root.build
index 230c58d..5e797f2 100644
--- a/build/root.build
+++ b/build/root.build
@@ -10,7 +10,7 @@ txx{*}: extension = txx
cxx{*}: extension = cxx
cxx.std = 14
-cxx.poptions += -I$src_root
+cxx.poptions =+ -I$src_root -I$out_root
# All exe{} in tests/ are, well, tests. Don't install them.
#
diff --git a/build/token b/build/token
index fedaba0..7eed747 100644
--- a/build/token
+++ b/build/token
@@ -23,6 +23,7 @@ namespace build
lcbrace,
rcbrace,
equal,
+ equal_plus,
plus_equal,
dollar,
lparen,
diff --git a/build/token.cxx b/build/token.cxx
index 35a7f89..cf47fd4 100644
--- a/build/token.cxx
+++ b/build/token.cxx
@@ -22,6 +22,7 @@ namespace build
case token_type::lcbrace: os << "{"; break;
case token_type::rcbrace: os << "}"; break;
case token_type::equal: os << "="; break;
+ case token_type::equal_plus: os << "=+"; break;
case token_type::plus_equal: os << "+="; break;
case token_type::dollar: os << "$"; break;
case token_type::lparen: os << "("; break;
diff --git a/build/variable b/build/variable
index 51d748d..799a35c 100644
--- a/build/variable
+++ b/build/variable
@@ -101,6 +101,9 @@ namespace build
value&
append (value, const variable&); // Aka operator+=().
+ value&
+ prepend (value, const variable&); // Aka operator=+().
+
// Forwarded to the representation type's assign()/append() (see below).
//
template <typename T> value& operator= (T);
@@ -138,6 +141,9 @@ namespace build
void
append (names, const variable&);
+ void
+ prepend (names, const variable&);
+
public:
// Don't use directly except in the implementation of representation
// types, in which case take care to update state.
diff --git a/build/variable.cxx b/build/variable.cxx
index 4386c6a..6e02816 100644
--- a/build/variable.cxx
+++ b/build/variable.cxx
@@ -57,6 +57,14 @@ namespace build
return *this;
}
+ value& value::
+ prepend (value v, const variable& var)
+ {
+ assert (type == v.type);
+ prepend (move (v.data_), var);
+ return *this;
+ }
+
void value::
append (names v, const variable& var)
{
@@ -84,6 +92,37 @@ namespace build
: state_type::empty;
}
+ void value::
+ prepend (names v, const variable& var)
+ {
+ // Reduce to append.
+ //
+ if (!null () && type != nullptr && type->append != nullptr)
+ {
+ state_ = type->append (v, move (data_), var)
+ ? state_type::filled
+ : state_type::empty;
+ swap (data_, v);
+ return;
+ }
+
+ if (data_.empty ())
+ data_ = move (v);
+ else
+ {
+ v.insert (v.end (),
+ make_move_iterator (data_.begin ()),
+ make_move_iterator (data_.end ()));
+ swap (data_, v);
+ }
+
+ state_ = (type != nullptr && type->assign != nullptr
+ ? type->assign (data_, var)
+ : !data_.empty ())
+ ? state_type::filled
+ : state_type::empty;
+ }
+
// bool value
//
bool value_traits<bool>::
diff --git a/tests/variable/prepend/buildfile b/tests/variable/prepend/buildfile
new file mode 100644
index 0000000..896f99d
--- /dev/null
+++ b/tests/variable/prepend/buildfile
@@ -0,0 +1,14 @@
+foo =+ FOO
+print $foo
+
+foo =+ foo
+print $foo
+
+foo = foo
+print $foo
+
+foo =+ FOO
+foo += FOO
+print $foo
+
+./:
diff --git a/tests/variable/prepend/test.out b/tests/variable/prepend/test.out
new file mode 100644
index 0000000..80b77d0
--- /dev/null
+++ b/tests/variable/prepend/test.out
@@ -0,0 +1,4 @@
+FOO
+foo FOO
+foo
+FOO foo FOO
diff --git a/tests/variable/prepend/test.sh b/tests/variable/prepend/test.sh
new file mode 100755
index 0000000..b898b3c
--- /dev/null
+++ b/tests/variable/prepend/test.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+valgrind -q b -q | diff -u test.out -