aboutsummaryrefslogtreecommitdiff
path: root/build2/test/script
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-10-21 11:25:15 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-11-04 09:26:34 +0200
commit096b10b96162eca90958af42e24520e2bc728494 (patch)
tree81162704a4273d4550c0d2b9f90646b73a8674fc /build2/test/script
parentd7c4a08efd25ac11d5931a3bd2c50fbe717faa8b (diff)
Add notion of testscript test and group scopes
Diffstat (limited to 'build2/test/script')
-rw-r--r--build2/test/script/parser16
-rw-r--r--build2/test/script/parser.cxx139
-rw-r--r--build2/test/script/script42
3 files changed, 151 insertions, 46 deletions
diff --git a/build2/test/script/parser b/build2/test/script/parser
index 6531aba..1292738 100644
--- a/build2/test/script/parser
+++ b/build2/test/script/parser
@@ -32,7 +32,10 @@ namespace build2
pre_parse (istream&, const path& name, script&);
void
- parse (const path& name, script&, runner&);
+ parse (const path& name, script& s, runner& r)
+ {
+ parse (s, name, s, r);
+ }
// Recursive descent parser.
//
@@ -43,6 +46,9 @@ namespace build2
//
protected:
void
+ parse (scope&, const path& name, script&, runner&);
+
+ void
pre_parse_script ();
void
@@ -90,6 +96,14 @@ namespace build2
lexer* lexer_;
script* script_;
runner* runner_;
+
+ // Pre-parse state.
+ //
+ group* group_;
+ test* test_;
+
+ // Parse state.
+ //
scope* scope_;
};
}
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index 243b931..7377e88 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -28,7 +28,10 @@ namespace build2
script_ = &s;
runner_ = nullptr;
- scope_ = script_;
+
+ group_ = script_;
+ test_ = nullptr;
+ scope_ = nullptr;
pre_parse_ = true;
@@ -36,7 +39,7 @@ namespace build2
}
void parser::
- parse (const path& p, script& s, runner& r)
+ parse (scope& sc, const path& p, script& s, runner& r)
{
path_ = &p;
@@ -45,7 +48,10 @@ namespace build2
script_ = &s;
runner_ = &r;
- scope_ = script_;
+
+ group_ = nullptr;
+ test_ = nullptr;
+ scope_ = &sc;
pre_parse_ = false;
@@ -78,7 +84,33 @@ namespace build2
// Stop saving and get the tokens.
//
- scope_->lines.push_back (line {lt, replay_data ()});
+ line l {lt, replay_data ()};
+
+ // Decide where it goes.
+ //
+ lines* ls (nullptr);
+ switch (lt)
+ {
+ case line_type::variable:
+ {
+ ls = &group_->setup;
+ break;
+ }
+ case line_type::test:
+ {
+ // Create implicit test scope.
+ //
+ group_->scopes.push_back (
+ unique_ptr<test> (
+ (test_ = new test (*group_))));
+
+ ls = &test_->tests;
+
+ test_ = nullptr;
+ }
+ }
+
+ ls->push_back (move (l));
}
replay_stop (); // Discard replay of eos.
@@ -87,23 +119,53 @@ namespace build2
void parser::
parse_script ()
{
- token t;
- type tt;
-
- for (line& l: scope_->lines)
+ auto play = [this] (lines& ls) // Note: destructive to lines.
{
- replay_data (move (l.tokens)); // Set the tokens and start playing.
+ token t;
+ type tt;
- // We don't really need the assign mode since we already know the
- // line type.
- //
- next (t, tt);
+ for (line& l: ls)
+ {
+ replay_data (move (l.tokens)); // Set the tokens and start playing.
- parse_script_line (t, tt, l.type);
- assert (tt == type::newline);
+ // We don't really need the assign mode since we already know the
+ // line type.
+ //
+ next (t, tt);
+
+ parse_script_line (t, tt, l.type);
+ assert (tt == type::newline);
- replay_stop (); // Stop playing.
+ replay_stop (); // Stop playing.
+ }
+ };
+
+ play (scope_->setup);
+
+ if (test* t = dynamic_cast<test*> (scope_))
+ {
+ play (t->tests);
}
+ else if (group* g = dynamic_cast<group*> (scope_))
+ {
+ for (const unique_ptr<scope>& s: g->scopes)
+ {
+ // Hand it off to a sub-parser potentially in another thread. But
+ // we could also have handled it serially in this parser:
+ //
+ // scope* os (scope_);
+ // scope_ = s.get ();
+ // parse_script ();
+ // scope_ = os;
+ //
+ parser p;
+ p.parse (*s, *path_, *script_, *runner_);
+ }
+ }
+ else
+ assert (false);
+
+ play (scope_->tdown);
}
line_type parser::
@@ -226,7 +288,7 @@ namespace build2
void parser::
parse_test_line (token& t, type& tt)
{
- test ts;
+ command c;
// Pending positions where the next word should go.
//
@@ -256,7 +318,7 @@ namespace build2
// Add the next word to either one of the pending positions or
// to program arguments by default.
//
- auto add_word = [&ts, &p, &hd, this] (string&& w, const location& l)
+ auto add_word = [&c, &p, &hd, this] (string&& w, const location& l)
{
auto add_here_end = [&hd] (redirect& r, string&& w)
{
@@ -265,14 +327,14 @@ namespace build2
switch (p)
{
- case pending::none: ts.arguments.push_back (move (w)); break;
+ case pending::none: c.arguments.push_back (move (w)); break;
case pending::program:
{
try
{
- ts.program = path (move (w));
+ c.program = path (move (w));
- if (ts.program.empty ())
+ if (c.program.empty ())
fail (l) << "empty program path";
}
catch (const invalid_path& e)
@@ -282,13 +344,13 @@ namespace build2
break;
}
- case pending::in_document: add_here_end (ts.in, move (w)); break;
- case pending::out_document: add_here_end (ts.out, move (w)); break;
- case pending::err_document: add_here_end (ts.err, move (w)); break;
+ case pending::in_document: add_here_end (c.in, move (w)); break;
+ case pending::out_document: add_here_end (c.out, move (w)); break;
+ case pending::err_document: add_here_end (c.err, move (w)); break;
- case pending::in_string: ts.in.value = move (w); break;
- case pending::out_string: ts.out.value = move (w); break;
- case pending::err_string: ts.err.value = move (w); break;
+ case pending::in_string: c.in.value = move (w); break;
+ case pending::out_string: c.out.value = move (w); break;
+ case pending::err_string: c.err.value = move (w); break;
}
p = pending::none;
@@ -319,7 +381,7 @@ namespace build2
// Parse the redirect operator.
//
auto parse_redirect =
- [&ts, &p, this] (const token& t, const location& l)
+ [&c, &p, this] (const token& t, const location& l)
{
// Our semantics is the last redirect seen takes effect.
//
@@ -330,10 +392,10 @@ namespace build2
unsigned long fd (3);
if (!t.separated)
{
- if (ts.arguments.empty ())
+ if (c.arguments.empty ())
fail (l) << "missing redirect file descriptor";
- const string& s (ts.arguments.back ());
+ const string& s (c.arguments.back ());
try
{
@@ -348,7 +410,7 @@ namespace build2
fail (l) << "invalid redirect file descriptor '" << s << "'";
}
- ts.arguments.pop_back ();
+ c.arguments.pop_back ();
}
type tt (t.type);
@@ -388,7 +450,7 @@ namespace build2
case type::out_document: rt = redirect_type::here_document; break;
}
- redirect& r (fd == 0 ? ts.in : fd == 1 ? ts.out : ts.err);
+ redirect& r (fd == 0 ? c.in : fd == 1 ? c.out : c.err);
r.type = rt;
switch (rt)
@@ -643,7 +705,7 @@ namespace build2
// going to continue lexing in the script_line mode.
//
if (tt == type::equal || tt == type::not_equal)
- ts.exit = parse_command_exit (t, tt);
+ c.exit = parse_command_exit (t, tt);
if (tt != type::newline)
fail (t) << "unexpected " << t;
@@ -671,10 +733,10 @@ namespace build2
expire_mode ();
}
- // Now that we have all the pieces, run the test.
+ // Now that we have all the pieces, run the command.
//
if (!pre_parse_)
- runner_->run (ts);
+ runner_->run (c);
}
command_exit parser::
@@ -781,6 +843,8 @@ namespace build2
if (!qual.empty ())
fail (loc) << "qualified variable name";
+ // @@ MT: will need RW mutex on var_pool.
+ //
if (name != "*" && !digits (name))
return scope_->find (script_->var_pool.insert (move (name)));
@@ -788,6 +852,11 @@ namespace build2
//
// See the parse_variable_line() for the overall plan.
//
+ // @@ MT: we are potentially changing outer scopes. Could force
+ // lookup before executing tests in each group scope. Poblem is
+ // we don't know which $NN vars will be looked up from inside.
+ // Could we collect all the variable names during the pre-parse
+ // stage? They could be computed.
// In both cases first thing we do is lookup $*. It should always be
// defined since we set it on the script's root scope.
diff --git a/build2/test/script/script b/build2/test/script/script
index a847e2b..860e5d4 100644
--- a/build2/test/script/script
+++ b/build2/test/script/script
@@ -32,6 +32,8 @@ namespace build2
replay_tokens tokens;
};
+ using lines = vector<line>;
+
// Parse object model.
//
enum class redirect_type
@@ -101,17 +103,13 @@ namespace build2
ostream&
operator<< (ostream&, const command&);
- struct test: command {};
-
class scope
{
public:
scope* parent; // NULL for the root (script) scope.
- scope (scope& p): parent (&p) {}
-
- protected:
- scope (): parent (nullptr) {} // For the root (script) scope.
+ lines setup;
+ lines tdown;
// Variables.
//
@@ -145,13 +143,37 @@ namespace build2
value&
append (const variable&);
- // Pre-parse.
- //
public:
- vector<line> lines;
+ virtual
+ ~scope () = default;
+
+ protected:
+ scope (scope* p): parent (p) {}
+ scope (): parent (nullptr) {} // For the root (script) scope.
+ };
+
+ class group: public scope
+ {
+ public:
+ vector<unique_ptr<scope>> scopes;
+
+ public:
+ group (group& p): scope (&p) {}
+
+ protected:
+ group (): scope (nullptr) {} // For the root (script) scope.
+ };
+
+ class test: public scope
+ {
+ public:
+ lines tests;
+
+ public:
+ test (group& p): scope (&p) {}
};
- class script: public scope
+ class script: public group
{
public:
script (target& test_target, testscript& script_target);