From 793f078ec31dc61921b382f14412ed3e25cc51d8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 23 Nov 2016 16:29:49 +0200 Subject: Implement assert directive The grammar is as follows: assert [] assert! [] The expression must evaluate to 'true' or 'false', just like in if-else. --- build2/parser | 3 ++ build2/parser.cxx | 93 ++++++++++++++++++++++++++++++++++----------- tests/buildfile | 2 +- tests/directive/assert.test | 28 ++++++++++++++ tests/directive/buildfile | 7 ++++ tests/directive/common.test | 11 ++++++ 6 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 tests/directive/assert.test create mode 100644 tests/directive/buildfile create mode 100644 tests/directive/common.test 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 (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 () << 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 +:1:1: error: assertion failed +EOE + +: false-desc +: +$* <'assert false bad thing happened: (a == b)' 2>>EOE != 0 +:1:1: error: assertion failed: bad thing happened: false +EOE + +: invalid +: +$* <'assert junk' 2>>EOE != 0 +: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 <>>build/bootstrap.build +project = test +amalgamation = +EOI + +test.options += -q --buildfile - noop -- cgit v1.1