aboutsummaryrefslogtreecommitdiff
path: root/build2/parser.cxx
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 /build2/parser.cxx
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.
Diffstat (limited to 'build2/parser.cxx')
-rw-r--r--build2/parser.cxx93
1 files changed, 70 insertions, 23 deletions
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.