summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-11-25 15:17:01 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-11-25 15:17:01 +0200
commit757f42e7dea94f8b79b3d55074dedeafd853ddc5 (patch)
tree8fa27fd27e36a85a6348d85b746d49a676a27027
parenta3dad2118fb3925ef4f9baa90cea0dfd44ca93c6 (diff)
downloadbuild2-757f42e7dea94f8b79b3d55074dedeafd853ddc5.tar.gz
build2-757f42e7dea94f8b79b3d55074dedeafd853ddc5.tar.xz
build2-757f42e7dea94f8b79b3d55074dedeafd853ddc5.zip
Implement literal here-document support
-rw-r--r--build2/lexer10
-rw-r--r--build2/lexer.cxx10
-rw-r--r--build2/test/script/lexer10
-rw-r--r--build2/test/script/lexer.cxx34
-rw-r--r--build2/test/script/parser6
-rw-r--r--build2/test/script/parser.cxx81
-rw-r--r--doc/testscript.cli20
-rw-r--r--tests/expansion/type.test16
-rw-r--r--tests/function/builtin/testscript2
-rw-r--r--tests/function/path/testscript2
-rw-r--r--tests/test/script/integration/testscript3
-rw-r--r--tests/test/script/runner/cleanup.test4
-rw-r--r--tests/test/script/runner/status.test4
-rw-r--r--unit-tests/function/call.test42
-rw-r--r--unit-tests/function/syntax.test6
-rw-r--r--unit-tests/lexer/comment.test24
-rw-r--r--unit-tests/lexer/quoting.test4
-rw-r--r--unit-tests/test/script/lexer/driver.cxx3
-rw-r--r--unit-tests/test/script/parser/command-if.test2
-rw-r--r--unit-tests/test/script/parser/command-re-parse.test4
-rw-r--r--unit-tests/test/script/parser/description.test4
-rw-r--r--unit-tests/test/script/parser/expansion.test2
-rw-r--r--unit-tests/test/script/parser/here-document.test10
-rw-r--r--unit-tests/test/script/parser/include.test4
-rw-r--r--unit-tests/test/script/parser/scope.test10
25 files changed, 194 insertions, 123 deletions
diff --git a/build2/lexer b/build2/lexer
index 59150a94..e2cf07c9 100644
--- a/build2/lexer
+++ b/build2/lexer
@@ -85,7 +85,9 @@ namespace build2
// specifythe pair separator character (if the mode supports pairs).
//
virtual void
- mode (lexer_mode, char pair_separator = '\0');
+ mode (lexer_mode,
+ char pair_separator = '\0',
+ const char* escapes = nullptr);
// Expire the current mode early.
//
@@ -119,6 +121,8 @@ namespace build2
bool sep_space; // Are whitespaces separators (see skip_spaces())?
bool quotes; // Recognize quoted fragments.
+ const char* escapes; // Effective escape sequences to recognize.
+
// Word 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
@@ -170,16 +174,14 @@ namespace build2
: char_scanner (is),
fail ("error", &name_),
name_ (n),
- escapes_ (e),
processor_ (p),
sep_ (false)
{
if (sm)
- mode (lexer_mode::normal, '@');
+ mode (lexer_mode::normal, '@', e);
}
const path name_;
- const char* escapes_;
void (*processor_) (token&, const lexer&);
std::stack<state> state_;
diff --git a/build2/lexer.cxx b/build2/lexer.cxx
index b73c291a..3c8eb5a8 100644
--- a/build2/lexer.cxx
+++ b/build2/lexer.cxx
@@ -30,7 +30,7 @@ namespace build2
}
void lexer::
- mode (lexer_mode m, char ps)
+ mode (lexer_mode m, char ps, const char* esc)
{
const char* s1 (nullptr);
const char* s2 (nullptr);
@@ -76,7 +76,7 @@ namespace build2
default: assert (false); // Unhandled custom mode.
}
- state_.push (state {m, ps, s, q, s1, s2});
+ state_.push (state {m, ps, s, q, esc, s1, s2});
}
token lexer::
@@ -329,8 +329,10 @@ namespace build2
get ();
xchar p (peek ());
- if (escapes_ == nullptr ||
- (!eos (p) && strchr (escapes_, p) != nullptr))
+ const char* esc (st.escapes);
+
+ if (esc == nullptr ||
+ (*esc != '\0' && !eos (p) && strchr (esc, p) != nullptr))
{
get ();
diff --git a/build2/test/script/lexer b/build2/test/script/lexer
index 5597e9ab..b812f84a 100644
--- a/build2/test/script/lexer
+++ b/build2/test/script/lexer
@@ -29,7 +29,8 @@ namespace build2
second_token, // Expires at the end of the token.
variable_line, // Expires at the end of the line.
command_line,
- here_line,
+ here_line_single,
+ here_line_double,
description_line // Expires at the end of the line.
};
@@ -48,10 +49,13 @@ namespace build2
const path& name,
lexer_mode m,
const char* escapes = nullptr)
- : base_lexer (is, name, escapes, nullptr, false) {mode (m);}
+ : base_lexer (is, name, nullptr, nullptr, false)
+ {
+ mode (m, '\0', escapes);
+ }
virtual void
- mode (base_mode, char = '\0') override;
+ mode (base_mode, char = '\0', const char* = nullptr) override;
// Number of quoted (double or single) tokens since last reset.
//
diff --git a/build2/test/script/lexer.cxx b/build2/test/script/lexer.cxx
index 19e7498d..72fa85b2 100644
--- a/build2/test/script/lexer.cxx
+++ b/build2/test/script/lexer.cxx
@@ -15,7 +15,7 @@ namespace build2
using type = token_type;
void lexer::
- mode (base_mode m, char ps)
+ mode (base_mode m, char ps, const char* esc)
{
const char* s1 (nullptr);
const char* s2 (nullptr);
@@ -76,7 +76,23 @@ namespace build2
s = false;
break;
}
- case lexer_mode::here_line:
+ case lexer_mode::here_line_single:
+ {
+ // This one is like a single-quoted string except it treats
+ // newlines as a separator. We also treat quotes as literals.
+ //
+ // Note that it might be tempting to enable line continuation
+ // escapes. However, we will then have to also enable escaping of
+ // the backslash, which makes it a lot less tempting.
+ //
+ s1 = "\n";
+ s2 = " ";
+ esc = ""; // Disable escape sequences.
+ s = false;
+ q = false;
+ break;
+ }
+ case lexer_mode::here_line_double:
{
// This one is like a double-quoted string except it treats
// newlines as a separator. We also treat quotes as literals.
@@ -105,13 +121,13 @@ namespace build2
m == lexer_mode::eval ||
m == lexer_mode::attribute);
- base_lexer::mode (m, ps);
+ base_lexer::mode (m, ps, esc);
return;
}
}
assert (ps == '\0');
- state_.push (state {m, ps, s, q, s1, s2});
+ state_.push (state {m, ps, s, q, esc, s1, s2});
}
token lexer::
@@ -126,7 +142,8 @@ namespace build2
case lexer_mode::second_token:
case lexer_mode::variable_line:
case lexer_mode::command_line:
- case lexer_mode::here_line:
+ case lexer_mode::here_line_single:
+ case lexer_mode::here_line_double:
r = next_line ();
break;
case lexer_mode::description_line:
@@ -184,7 +201,13 @@ namespace build2
sep = true; // Treat newline as always separated.
return make_token (type::newline);
}
+ }
+ }
+ if (m != lexer_mode::here_line_single)
+ {
+ switch (c)
+ {
// Variable expansion, function call, and evaluation context.
//
case '$': return make_token (type::dollar);
@@ -192,6 +215,7 @@ namespace build2
}
}
+
if (m == lexer_mode::variable_line)
{
switch (c)
diff --git a/build2/test/script/parser b/build2/test/script/parser
index fdfbe116..da82df2b 100644
--- a/build2/test/script/parser
+++ b/build2/test/script/parser
@@ -99,10 +99,10 @@ namespace build2
{
size_t expr; // Index in command_expr.
size_t pipe; // Index in command_pipe.
- size_t redir; // Redirect (0 - in, 1 - out, 2 - err).
-
+ int fd; // Redirect fd (0 - in, 1 - out, 2 - err).
string end;
- bool no_newline;
+ bool literal; // Literal (single-quote).
+ bool no_newline; // No final newline.
};
using here_docs = vector<here_doc>;
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index 7655ba99..9e2018ff 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -1261,11 +1261,11 @@ namespace build2
cleanup_type ct; // Pending cleanup type.
here_docs hd; // Expected here-documents.
- // Add the next word to either one of the pending positions or
- // to program arguments by default.
+ // Add the next word to either one of the pending positions or to
+ // program arguments by default.
//
- auto add_word = [&expr, &c, &p, &nn, &app, &ct, &hd, this]
- (string&& w, const location& l)
+ auto add_word =
+ [&c, &p, &nn, &app, &ct, this] (string&& w, const location& l)
{
auto add_merge = [&l, this] (redirect& r, const string& w, int fd)
{
@@ -1290,13 +1290,6 @@ namespace build2
r.str = move (w);
};
- auto add_here_end = [&expr, &hd, &nn] (size_t r, string&& w)
- {
- hd.push_back (
- here_doc {
- expr.size () - 1, expr.back ().pipe.size (), r, move (w), nn});
- };
-
auto parse_path = [&l, this] (string&& w, const char* what) -> path
{
try
@@ -1335,10 +1328,8 @@ namespace build2
{
case pending::none: c.arguments.push_back (move (w)); break;
case pending::program:
- {
c.program = parse_path (move (w), "program path");
break;
- }
case pending::out_merge: add_merge (c.out, w, 2); break;
case pending::err_merge: add_merge (c.err, w, 1); break;
@@ -1347,21 +1338,19 @@ namespace build2
case pending::out_string: add_here_str (c.out, move (w)); break;
case pending::err_string: add_here_str (c.err, move (w)); break;
- case pending::in_document: add_here_end (0, move (w)); break;
- case pending::out_document: add_here_end (1, move (w)); break;
- case pending::err_document: add_here_end (2, move (w)); break;
+ // These are handled specially below.
+ //
+ case pending::in_document:
+ case pending::out_document:
+ case pending::err_document: assert (false); break;
case pending::in_file: add_file (c.in, 0, move (w)); break;
case pending::out_file: add_file (c.out, 1, move (w)); break;
case pending::err_file: add_file (c.err, 2, move (w)); break;
case pending::clean:
- {
- c.cleanups.push_back (
- {ct, parse_path (move (w), "cleanup path")});
-
- break;
- }
+ c.cleanups.push_back ({ct, parse_path (move (w), "cleanup path")});
+ break;
}
p = pending::none;
@@ -1692,7 +1681,9 @@ namespace build2
fail (t) << "partially-quoted here-document end marker";
}
- hd.push_back (here_doc {0, 0, 0, move (t.value), nn});
+ hd.push_back (
+ here_doc {
+ 0, 0, 0, move (t.value), qt == quote_type::single, nn});
break;
}
@@ -1774,6 +1765,40 @@ namespace build2
}
default:
{
+ // Here-document end markers are literal (we verified that above
+ // during pre-parsing) and we need to know whether they were
+ // quoted. So handle this case specially.
+ //
+ {
+ int fd;
+ switch (p)
+ {
+ case pending::in_document: fd = 0; break;
+ case pending::out_document: fd = 1; break;
+ case pending::err_document: fd = 2; break;
+ default: fd = -1; break;
+ }
+
+ if (fd != -1)
+ {
+ hd.push_back (
+ here_doc {
+ expr.size () - 1,
+ expr.back ().pipe.size (),
+ fd,
+ move (t.value),
+ (t.qtype == quote_type::unquoted ||
+ t.qtype == quote_type::single),
+ nn});
+
+ p = pending::none;
+ nn = false;
+
+ next (t, tt);
+ break;
+ }
+ }
+
// Parse the next chunk as simple names to get expansion, etc.
// Note that we do it in the chunking mode to detect whether
// anything in each chunk is quoted.
@@ -2060,10 +2085,12 @@ namespace build2
//
for (here_doc& h: p.second)
{
- // Switch to the here-line mode which is like double-quoted but
- // recognized the newline as a separator.
+ // Switch to the here-line mode which is like single/double-quoted
+ // string but recognized the newline as a separator.
//
- mode (lexer_mode::here_line);
+ mode (h.literal
+ ? lexer_mode::here_line_single
+ : lexer_mode::here_line_double);
next (t, tt);
string v (parse_here_document (t, tt, h.end, h.no_newline));
@@ -2071,7 +2098,7 @@ namespace build2
if (!pre_parse_)
{
command& c (p.first[h.expr].pipe[h.pipe]);
- redirect& r (h.redir == 0 ? c.in : h.redir == 1 ? c.out : c.err);
+ redirect& r (h.fd == 0 ? c.in : h.fd == 1 ? c.out : c.err);
r.doc.doc = move (v);
r.doc.end = move (h.end);
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 1ba9a4b5..79c68363 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -1421,10 +1421,22 @@ error: no such table 'no_such_table'
EOE
\
-The lines in here-document are expanded as if they were double-quoted except
-that the double quote itself is not treated as special. This means we can use
-variables and evaluation contexts in here-documents but have to escape the
-\c{\\$(} character set.
+Here-strings can be single-quoted literals or double-quoted with expansion.
+This semantics is extended to here-documents as follows. If the end marker
+on the command line is single-quoted, then the here-document lines are
+parsed as if they were single-quoted except that the single quote itself
+is not treated as special. In this mode there are no expansions, escape
+sequences, not even line continuations \- each line is taken literally.
+
+If the end marker on the command line is double-quoted, then the here-document
+lines are parsed as if they were double-quoted except that the double quote
+itself is not treated as special. In this mode we can use variables
+expansions, function calls, and evaluation contexts. However, we have to
+escape the \c{$(\\} character set.
+
+If the end marker is not quoted then it is treated as if it were
+single-quoted. Note also that quoted end markers must be quoted \i{wholly},
+that is, from the beginning and until the end and without any interruptions.
If the preceding command line starts with leading whitespaces, then the
equivalent number is stripped (if present) from each here-document line
diff --git a/tests/expansion/type.test b/tests/expansion/type.test
index 1aae5b67..d524eea6 100644
--- a/tests/expansion/type.test
+++ b/tests/expansion/type.test
@@ -10,30 +10,30 @@
:
$* <<EOI
x = [bool] true
-y = \$x
-assert \(\$type\(\$y) == bool)
+y = $x
+assert ($type($y) == bool)
EOI
: eval
:
$* <<EOI
-y = \([bool] true)
-assert \(\$type\(\$y) == bool)
+y = ([bool] true)
+assert ($type($y) == bool)
EOI
: func
:
$* <<EOI
-y = \$identity\([bool] true)
-assert \(\$type\(\$y) == bool)
+y = $identity([bool] true)
+assert ($type($y) == bool)
EOI
: untypify
:
$* <<EOI
x = [bool] true
-y = "\$x"
-assert \(\$type\(\$y) == "")
+y = "$x"
+assert ($type($y) == "")
EOI
: type-conflict
diff --git a/tests/function/builtin/testscript b/tests/function/builtin/testscript
index 1a4c5d1b..6491a60e 100644
--- a/tests/function/builtin/testscript
+++ b/tests/function/builtin/testscript
@@ -36,7 +36,7 @@ test.options += -q --buildfile - noop
{
$* <<EOI >'true' : empty-untyped
x =
- print \$empty\(\$x)
+ print $empty($x)
EOI
$* <'print $empty([string])' >'true' : empty-typed
diff --git a/tests/function/path/testscript b/tests/function/path/testscript
index e1b08af5..0f83ad69 100644
--- a/tests/function/path/testscript
+++ b/tests/function/path/testscript
@@ -34,6 +34,6 @@ if ($cxx.target.class != windows) # @@ TMP ternarry
else
p = c:/../foo
end;
-$* <"\$path.normalize\('$p')" 2>>EOE != 0
+$* <"\$path.normalize\('$p')" 2>>"EOE" != 0
error: invalid path: '$p'
EOE
diff --git a/tests/test/script/integration/testscript b/tests/test/script/integration/testscript
index b927fe89..93ce08a2 100644
--- a/tests/test/script/integration/testscript
+++ b/tests/test/script/integration/testscript
@@ -69,8 +69,7 @@ touch test/dummy &!test/dummy;
$* <<EOI 2>>EOE
./: test{foo}
EOI
-warning: working directory test/ exists and is not empty at the beginning \
-of the test
+warning: working directory test/ exists and is not empty at the beginning of the test
EOE
: wd-not-empty-after
diff --git a/tests/test/script/runner/cleanup.test b/tests/test/script/runner/cleanup.test
index 474ce483..e6cefd56 100644
--- a/tests/test/script/runner/cleanup.test
+++ b/tests/test/script/runner/cleanup.test
@@ -49,7 +49,7 @@ rm a
: Test that file append redirect doesn't not register cleanup. If it did, that
: cleanup would fail as the file would be already deleted by 'rm'.
:
-$c <<EOI;
+$c <<"EOI";
touch a &!a;
$* -o foo >>>&a;
rm a
@@ -222,7 +222,7 @@ EOE
:
: Test an explicit cleanup not being overwritten with the implicit one.
:
-$c <<EOO;
+$c <<"EOO";
$* &!a;
$* -o foo >>>a
EOO
diff --git a/tests/test/script/runner/status.test b/tests/test/script/runner/status.test
index f1a20ece..716f2d4e 100644
--- a/tests/test/script/runner/status.test
+++ b/tests/test/script/runner/status.test
@@ -36,13 +36,13 @@ $b
: eq-false
:
$c <"$* -s 1 == 0";
-$b 2>>EOE != 0
+$b 2>>"EOE" != 0
testscript:1: error: ../../../driver$ext exit status 1 != 0
EOE
: ne-false
:
$c <"$* -s 1 != 1";
-$b 2>>EOE != 0
+$b 2>>"EOE" != 0
testscript:1: error: ../../../driver$ext exit status 1 == 1
EOE
diff --git a/unit-tests/function/call.test b/unit-tests/function/call.test
index 396090a5..d16b91ce 100644
--- a/unit-tests/function/call.test
+++ b/unit-tests/function/call.test
@@ -13,8 +13,8 @@ $* <'print $dummy.qual()' >'abc'
: qual-fail
:
$* <'print $qual()' 2>>EOE != 0
-buildfile:1:8: error: unmatched call to qual\()
- info: candidate: dummy.qual\()
+buildfile:1:8: error: unmatched call to qual()
+ info: candidate: dummy.qual()
EOE
: derived-base
@@ -32,42 +32,42 @@ $* <'print $variadic([bool] true)' >'1'
:
$* <'$fail()' 2>>EOE != 0
error: failed
-buildfile:1:2: info: while calling fail\()
+buildfile:1:2: info: while calling fail()
EOE
: fail-invalid-arg
:
$* <'$fail_arg(abc)' 2>>EOE != 0
error: invalid argument: invalid uint64 value: 'abc'
-buildfile:1:2: info: while calling fail_arg\(<untyped>)
+buildfile:1:2: info: while calling fail_arg(<untyped>)
EOE
: no-match-name
:
$* <'$bogus()' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to bogus\()
+buildfile:1:2: error: unmatched call to bogus()
EOE
: no-match-count
:
$* <'$dummy0(abc)' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to dummy0\(<untyped>)
- info: candidate: dummy0\(), qualified name dummy.dummy0
+buildfile:1:2: error: unmatched call to dummy0(<untyped>)
+ info: candidate: dummy0(), qualified name dummy.dummy0
EOE
: no-match-type
:
$* <'$dummy1([uint64] 123)' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to dummy1\(uint64)
- info: candidate: dummy1\(string), qualified name dummy.dummy1
+buildfile:1:2: error: unmatched call to dummy1(uint64)
+ info: candidate: dummy1(string), qualified name dummy.dummy1
EOE
: ambig
:
$* <'$ambig(abc)' 2>- != 0 # @@ REGEX
-#buildfile:1:2: error: ambiguous call to ambig\(<untyped>)
-# info: candidate: ambig\(<untyped> [, uint64]), qualified name dummy.ambig
-# info: candidate: ambig\(<untyped> [, string]), qualified name dummy.ambig
+#buildfile:1:2: error: ambiguous call to ambig(<untyped>)
+# info: candidate: ambig(<untyped> [, uint64]), qualified name dummy.ambig
+# info: candidate: ambig(<untyped> [, string]), qualified name dummy.ambig
#EOE
: optional-absent
@@ -90,19 +90,19 @@ $* <'print $nullable(nonull)' >'false'
:
$* <'$dummy1([string null])' 2>>EOE != 0
error: invalid argument: null value
-buildfile:1:2: info: while calling dummy1\(string)
+buildfile:1:2: info: while calling dummy1(string)
EOE
: print-call-1-untyped
:
$* <'$bogus(abc)' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to bogus\(<untyped>)
+buildfile:1:2: error: unmatched call to bogus(<untyped>)
EOE
: print-call-1-typed
:
$* <'$bogus([uint64] 123)' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to bogus\(uint64)
+buildfile:1:2: error: unmatched call to bogus(uint64)
EOE
#\
@@ -110,23 +110,23 @@ EOE
: print-call-2
:
$* <'$bogus(abc, [uint64] 123)' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to bogus\(<untyped>, uint64)
+buildfile:1:2: error: unmatched call to bogus(<untyped>, uint64)
EOE
#\
: print-fovl
:
$* <'$ambig([bool] true)' 2>- != 0 # @@ REGEX
-#buildfile:1:2: error: unmatched call to ambig\(bool)
-# info: candidate: ambig\(<untyped> [, uint64]), qualified name dummy.ambig
-# info: candidate: ambig\(<untyped> [, string]), qualified name dummy.ambig
+#buildfile:1:2: error: unmatched call to ambig(bool)
+# info: candidate: ambig(<untyped> [, uint64]), qualified name dummy.ambig
+# info: candidate: ambig(<untyped> [, string]), qualified name dummy.ambig
#EOE
: print-fovl-variadic
:
$* <'$variadic(abc)' 2>>EOE != 0
-buildfile:1:2: error: unmatched call to variadic\(<untyped>)
- info: candidate: variadic\(bool [, ...])
+buildfile:1:2: error: unmatched call to variadic(<untyped>)
+ info: candidate: variadic(bool [, ...])
EOE
: member-function
diff --git a/unit-tests/function/syntax.test b/unit-tests/function/syntax.test
index 9e653c87..d644fd1c 100644
--- a/unit-tests/function/syntax.test
+++ b/unit-tests/function/syntax.test
@@ -19,9 +19,9 @@ $* <<EOI >>EOO
foo = FOO
bar = BAR
-print \$foo"\(\$bar)"
-print "\$foo"\(\$bar)
-print "\$foo""\(\$bar)"
+print $foo"($bar)"
+print "$foo"($bar)
+print "$foo""($bar)"
EOI
FOOBAR
FOOBAR
diff --git a/unit-tests/lexer/comment.test b/unit-tests/lexer/comment.test
index 07d7ac58..65e768cb 100644
--- a/unit-tests/lexer/comment.test
+++ b/unit-tests/lexer/comment.test
@@ -59,34 +59,34 @@ EOO
#
$* <<EOI >>:EOO # multi-only
-#\\
+#\
comment
comment
-#\\
+#\
EOI
EOO
$* <<EOI >>:EOO # multi-empty
-#\\
-#\\
+#\
+#\
EOI
EOO
$* <<EOI >>EOO # multi-start-same
-foo #\\
+foo #\
comment
comment
-#\\
+#\
EOI
'foo'
<newline>
EOO
$* <<EOI >>EOO # multi-end-same
-#\\
+#\
comment
comment
-foo #\\
+foo #\
bar
EOI
'bar'
@@ -94,10 +94,10 @@ EOI
EOO
$* <<EOI >>EOO # multi-end-not
-#\\
+#\
comment
-#\\ not an end
-foo #\\
+#\ not an end
+foo #\
bar
EOI
'bar'
@@ -105,7 +105,7 @@ EOI
EOO
$* <<EOI 2>>EOE != 0 # multi-unterm
-#\\
+#\
comment
EOI
stdin:3:1: error: unterminated multi-line comment
diff --git a/unit-tests/lexer/quoting.test b/unit-tests/lexer/quoting.test
index 76fd9043..aab02c39 100644
--- a/unit-tests/lexer/quoting.test
+++ b/unit-tests/lexer/quoting.test
@@ -48,7 +48,7 @@ EOO
:
$* <'"$foo"' >>EOO
'' [D/P]
-\$
+$
'foo' [D/P]
<newline>
EOO
@@ -58,7 +58,7 @@ EOO
:
$* <'"foo$"' >>EOO
'foo' [D/P]
-\$
+$
'' [D/P]
<newline>
EOO
diff --git a/unit-tests/test/script/lexer/driver.cxx b/unit-tests/test/script/lexer/driver.cxx
index abd32ba1..37091912 100644
--- a/unit-tests/test/script/lexer/driver.cxx
+++ b/unit-tests/test/script/lexer/driver.cxx
@@ -34,7 +34,8 @@ namespace build2
else if (s == "second-token") m = lexer_mode::second_token;
else if (s == "variable-line") m = lexer_mode::variable_line;
else if (s == "command-line") m = lexer_mode::command_line;
- else if (s == "here-line") m = lexer_mode::here_line;
+ else if (s == "here-line-single") m = lexer_mode::here_line_single;
+ else if (s == "here-line-double") m = lexer_mode::here_line_double;
else if (s == "description-line") m = lexer_mode::description_line;
else if (s == "variable") m = lexer_mode::variable;
else assert (false);
diff --git a/unit-tests/test/script/parser/command-if.test b/unit-tests/test/script/parser/command-if.test
index 88cc7d60..4bbc0162 100644
--- a/unit-tests/test/script/parser/command-if.test
+++ b/unit-tests/test/script/parser/command-if.test
@@ -249,7 +249,7 @@ if true
else
x = bar
end;
-cmd \$x
+cmd $x
EOI
? true
cmd foo
diff --git a/unit-tests/test/script/parser/command-re-parse.test b/unit-tests/test/script/parser/command-re-parse.test
index aee4f787..335ff691 100644
--- a/unit-tests/test/script/parser/command-re-parse.test
+++ b/unit-tests/test/script/parser/command-re-parse.test
@@ -2,8 +2,8 @@
# double-quote
#
$* <<EOI >>EOO
-x = cmd \\">-\\" "'<-'"
-\$x
+x = cmd \">-\" "'<-'"
+$x
EOI
cmd '>-' '<-'
EOO
diff --git a/unit-tests/test/script/parser/description.test b/unit-tests/test/script/parser/description.test
index 1b3f3581..b4ab4352 100644
--- a/unit-tests/test/script/parser/description.test
+++ b/unit-tests/test/script/parser/description.test
@@ -180,7 +180,7 @@ EOE
$* <<EOI >>EOO # legal-var
: foo bar
x = y;
-cmd \$x
+cmd $x
EOI
: sm:foo bar
cmd y
@@ -334,7 +334,7 @@ EOO
:
: No merge since test has description.
:
-$* -s -i <<EOI >>EOO #
+$* -s -i <<EOI >>EOO #
{
: foo-bar
: foo bar
diff --git a/unit-tests/test/script/parser/expansion.test b/unit-tests/test/script/parser/expansion.test
index c23d598a..1d10a63c 100644
--- a/unit-tests/test/script/parser/expansion.test
+++ b/unit-tests/test/script/parser/expansion.test
@@ -6,7 +6,7 @@
$* <<EOI >>EOO
x = dir/ proj% proj%name proj%proj%dir/type{name name {name}}
cmd dir/ proj% proj%name proj%proj%dir/type{name name {name}}
-cmd \$x
+cmd $x
EOI
cmd dir/ proj% proj%name proj%proj%dir/type{name name {name}}
cmd dir/ proj% proj%name proj%proj%dir/type{name name {name}}
diff --git a/unit-tests/test/script/parser/here-document.test b/unit-tests/test/script/parser/here-document.test
index 6f261667..7cb94747 100644
--- a/unit-tests/test/script/parser/here-document.test
+++ b/unit-tests/test/script/parser/here-document.test
@@ -128,8 +128,8 @@
:
$* <<EOI >>EOO
x = foo bar
- cmd <<EOF
- \$x
+ cmd <<"EOF"
+ $x
EOF
EOI
cmd <<EOF
@@ -142,8 +142,8 @@
:
$* <<EOI >>EOO
x = foo
- cmd <<EOF
- \$x bar \$x
+ cmd <<"EOF"
+ $x bar $x
EOF
EOI
cmd <<EOF
@@ -185,7 +185,7 @@ EOO
: Note: they are still recognized in eval contexts.
:
$* <<EOI >>EOO
-cmd <<EOF
+cmd <<"EOF"
'single'
"double"
b'o't"h"
diff --git a/unit-tests/test/script/parser/include.test b/unit-tests/test/script/parser/include.test
index 65ce7ceb..7a3b5170 100644
--- a/unit-tests/test/script/parser/include.test
+++ b/unit-tests/test/script/parser/include.test
@@ -14,7 +14,7 @@ EOO
touch foo.test;
$* <<EOI
x = foo.test
-.include\$x
+.include$x
EOI
: none
@@ -108,7 +108,7 @@ cat <<EOI >>>foo-$(build.version).test;
cmd
EOI
$* <<EOI >>EOO
-.include foo-\$\(build.version\).test
+.include foo-$(build.version).test
EOI
cmd
EOO
diff --git a/unit-tests/test/script/parser/scope.test b/unit-tests/test/script/parser/scope.test
index 3b10d01e..6ddb2650 100644
--- a/unit-tests/test/script/parser/scope.test
+++ b/unit-tests/test/script/parser/scope.test
@@ -74,7 +74,7 @@ EOO
$* -s -i <<EOI >>EOO # test-scope-var
{
x = abc
- cmd \$x
+ cmd $x
}
EOI
{
@@ -88,7 +88,7 @@ $* -s -i <<EOI >>EOO # test-scope-setup
{
x = abc
+setup
- cmd \$x
+ cmd $x
}
EOI
{
@@ -166,7 +166,7 @@ EOO
$* -s <<EOI >>EOO # test-var
cmd1;
x = abc;
-cmd2 \$x
+cmd2 $x
EOI
{
{
@@ -178,7 +178,7 @@ EOO
$* -s <<EOI >>EOO # test-var-first
x = abc;
-cmd \$x
+cmd $x
EOI
{
{
@@ -189,7 +189,7 @@ EOO
$* -s <<EOI >>EOO # var-setup-tdown
x = abc
-cmd \$x
+cmd $x
y = 123
EOI
{