diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-12-14 10:12:23 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-12-14 10:12:23 +0200 |
commit | e649f2d988ce4e7a67aee1236850eb0b95a97d56 (patch) | |
tree | a1fe2ea19e24ba1d018fe08633f70be8489d607e | |
parent | de3f340fac96aca1e626068cbbb22ec92c1dc4b6 (diff) |
Add support for variable prepend operator: =+
-rw-r--r-- | build/b.cxx | 4 | ||||
-rw-r--r-- | build/file.cxx | 1 | ||||
-rw-r--r-- | build/lexer.cxx | 54 | ||||
-rw-r--r-- | build/parser.cxx | 30 | ||||
-rw-r--r-- | build/root.build | 2 | ||||
-rw-r--r-- | build/token | 1 | ||||
-rw-r--r-- | build/token.cxx | 1 | ||||
-rw-r--r-- | build/variable | 6 | ||||
-rw-r--r-- | build/variable.cxx | 39 | ||||
-rw-r--r-- | tests/variable/prepend/buildfile | 14 | ||||
-rw-r--r-- | tests/variable/prepend/test.out | 4 | ||||
-rwxr-xr-x | tests/variable/prepend/test.sh | 3 |
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 - |