aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-11-23 16:29:49 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-11-23 16:29:49 +0200
commit793f078ec31dc61921b382f14412ed3e25cc51d8 (patch)
tree62fb0b58dde440387ca585b151a10dc5af192a25
parent79cb3221c2babe6f560f2e3e463e899631a32b33 (diff)
Implement assert directive
The grammar is as follows: assert <expression> [<description>] assert! <expression> [<description>] The expression must evaluate to 'true' or 'false', just like in if-else.
-rw-r--r--build2/parser3
-rw-r--r--build2/parser.cxx93
-rw-r--r--tests/buildfile2
-rw-r--r--tests/directive/assert.test28
-rw-r--r--tests/directive/buildfile7
-rw-r--r--tests/directive/common.test11
6 files changed, 120 insertions, 24 deletions
diff --git a/build2/parser b/build2/parser
index a2a046b..9b326b1 100644
--- a/build2/parser
+++ b/build2/parser
@@ -64,6 +64,9 @@ namespace build2
parse_clause (token&, token_type&, bool one = false);
void
+ parse_assert (token&, token_type&);
+
+ void
parse_print (token&, token_type&);
void
diff --git a/build2/parser.cxx b/build2/parser.cxx
index da39696..df2d168 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -273,11 +273,16 @@ namespace build2
const string& n (t.value);
void (parser::*f) (token&, type&) = nullptr;
- if (n == "print")
+ // @@ Is this the only place where some of these are valid? Probably
+ // also in the var namespace?
+ //
+ if (n == "assert" ||
+ n == "assert!")
+ {
+ f = &parser::parse_assert;
+ }
+ else if (n == "print")
{
- // @@ Is this the only place where it is valid? Probably also
- // in var namespace.
- //
f = &parser::parse_print;
}
else if (n == "source")
@@ -1402,36 +1407,78 @@ namespace build2
}
void parser::
- parse_print (token& t, type& tt)
+ parse_assert (token& t, type& tt)
{
- // Parse the rest as a variable value to get expansion, attributes, etc.
+ bool neg (t.value.back () == '!');
+ const location al (get_location (t));
+
+ // Parse the next chunk as names to get variable expansion, evaluation,
+ // etc. Do it in the value mode so that we don't treat ':', etc., as
+ // special.
//
- value v (parse_variable_value (t, tt));
+ mode (lexer_mode::value);
+ next (t, tt);
+
+ const location el (get_location (t));
+ names ns (parse_names (t, tt, true, "expression", nullptr));
- if (attributes_top ())
+ // Should evaluate to 'true' or 'false'.
+ //
+ try
{
- // Round-trip it through (a potentially typed) value.
- //
- value tv;
- apply_value_attributes (nullptr, tv, move (v), type::assign);
+ if (ns.size () != 1)
+ throw invalid_argument (string ());
+
+ bool e (convert<bool> (move (ns[0])));
+ e = (neg ? !e : e);
- if (tv)
+ if (e)
{
- names storage;
- cout << reverse (tv, storage) << endl;
+ skip_line (t, tt);
+
+ if (tt != type::eos)
+ next (t, tt); // Swallow newline.
+
+ return;
}
- else
- cout << "[null]" << endl;
}
- else
+ catch (const invalid_argument&)
{
- attributes_pop ();
+ fail (el) << "expected assert-expression to evaluate to "
+ << "'true' or 'false' instead of '" << ns << "'";
+ }
- if (v)
- cout << v.as<names> () << endl;
- else
- cout << "[null]" << endl;
+ // Being here means things didn't end up well. Parse the description, if
+ // any, with expansion. Then fail.
+ //
+ ns = tt != type::newline && tt != type::eos
+ ? parse_names (t, tt, false, "description", nullptr)
+ : names ();
+
+ diag_record dr (fail (al));
+ dr << "assertion failed";
+
+ if (!ns.empty ())
+ dr << ": " << ns;
+ }
+
+ void parser::
+ parse_print (token& t, type& tt)
+ {
+ // Parse the rest as a variable value to get expansion, attributes, etc.
+ //
+ value rhs (parse_variable_value (t, tt));
+
+ value lhs;
+ apply_value_attributes (nullptr, lhs, move (rhs), type::assign);
+
+ if (lhs)
+ {
+ names storage;
+ cout << reverse (lhs, storage) << endl;
}
+ else
+ cout << "[null]" << endl;
if (tt != type::eos)
next (t, tt); // Swallow newline.
diff --git a/tests/buildfile b/tests/buildfile
index cd5177e..f38b3ca 100644
--- a/tests/buildfile
+++ b/tests/buildfile
@@ -2,6 +2,6 @@
# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-d = function/ test/
+d = directive/ function/ test/
./: $d
include $d
diff --git a/tests/directive/assert.test b/tests/directive/assert.test
new file mode 100644
index 0000000..ca1b035
--- /dev/null
+++ b/tests/directive/assert.test
@@ -0,0 +1,28 @@
+# file : tests/directive/assert.test
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+.include common.test
+
+$* <'assert true' : true
+$* <'assert! false foo' : not-false
+$* <'assert true $no_such_function()' : skip-line
+$* <'assert! $empty($build.version)' : expr
+
+: false
+:
+$* <'assert false' 2>>EOE != 0
+<stdin>:1:1: error: assertion failed
+EOE
+
+: false-desc
+:
+$* <'assert false bad thing happened: (a == b)' 2>>EOE != 0
+<stdin>:1:1: error: assertion failed: bad thing happened: false
+EOE
+
+: invalid
+:
+$* <'assert junk' 2>>EOE != 0
+<stdin>:1:8: error: expected assert-expression to evaluate to 'true' or 'false' instead of 'junk'
+EOE
diff --git a/tests/directive/buildfile b/tests/directive/buildfile
new file mode 100644
index 0000000..e9e946e
--- /dev/null
+++ b/tests/directive/buildfile
@@ -0,0 +1,7 @@
+# file : tests/directive/buildfile
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: test{assert} file{common.test}
+
+test{*}: test = $effect($build.path)
diff --git a/tests/directive/common.test b/tests/directive/common.test
new file mode 100644
index 0000000..351c133
--- /dev/null
+++ b/tests/directive/common.test
@@ -0,0 +1,11 @@
+# file : tests/directive/assert.test
+# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
++mkdir build
++cat <<EOI >>>build/bootstrap.build
+project = test
+amalgamation =
+EOI
+
+test.options += -q --buildfile - noop