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.cxx | 93 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 23 deletions(-) (limited to 'build2/parser.cxx') 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. -- cgit v1.1