summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/concepts/expressions.md9
-rw-r--r--src/buildtool/build_engine/expression/evaluator.cpp21
-rw-r--r--test/buildtool/build_engine/expression/expression.test.cpp27
3 files changed, 57 insertions, 0 deletions
diff --git a/doc/concepts/expressions.md b/doc/concepts/expressions.md
index 25da0fc8..80378191 100644
--- a/doc/concepts/expressions.md
+++ b/doc/concepts/expressions.md
@@ -391,3 +391,12 @@ that evaluation included in the error message presented to the user.
two (or more) maps contain the same key, but map it to different
values. It is also an error if the argument is a name-containing
value.
+
+ - `"assert"` Evaluate the argument (given by the parameter `"$1"`);
+ then evaluate the expression `"predicate"` with the variable given
+ at the key `"var"` (which has to be a string literal if given,
+ default value is `"_"`) bound to that value. If the predicate
+ evaluates to a true value, return the result of evaluating the
+ argument, otherwise fail; in evaluating the failure message
+ `"msg"`, also keep the variable specified by `"var"` bound to
+ the result of evaluating the argument.
diff --git a/src/buildtool/build_engine/expression/evaluator.cpp b/src/buildtool/build_engine/expression/evaluator.cpp
index b8102e95..f91e5b13 100644
--- a/src/buildtool/build_engine/expression/evaluator.cpp
+++ b/src/buildtool/build_engine/expression/evaluator.cpp
@@ -1027,6 +1027,26 @@ auto FailExpr(SubExprEvaluator&& eval,
msg->ToString(), false, /* user error*/ true);
}
+auto AssertExpr(SubExprEvaluator&& eval,
+ ExpressionPtr const& expr,
+ Configuration const& env) -> ExpressionPtr {
+ auto val = eval(expr["$1"], env);
+ auto const& var = expr->Get("var", "_"s);
+ auto pred = eval(expr["predicate"], env.Update(var->String(), val));
+ if (ValueIsTrue(pred)) {
+ return val;
+ }
+ auto msg_expr = expr->Get("msg", Expression::kNone);
+ std::string msg;
+ try {
+ auto msg_val = eval(msg_expr, env.Update(var->String(), val));
+ msg = msg_val->ToString();
+ } catch (std::exception const&) {
+ msg = "[non evaluating term] " + msg_expr->ToString();
+ }
+ throw Evaluator::EvaluationError(msg, false, /* user error */ true);
+}
+
auto AssertNonEmptyExpr(SubExprEvaluator&& eval,
ExpressionPtr const& expr,
Configuration const& env) -> ExpressionPtr {
@@ -1057,6 +1077,7 @@ auto const kBuiltInFunctions =
{"case", CaseExpr},
{"case*", SeqCaseExpr},
{"fail", FailExpr},
+ {"assert", AssertExpr},
{"assert_non_empty", AssertNonEmptyExpr},
{"context", ContextExpr},
{"==", EqualExpr},
diff --git a/test/buildtool/build_engine/expression/expression.test.cpp b/test/buildtool/build_engine/expression/expression.test.cpp
index 256eb392..18189baf 100644
--- a/test/buildtool/build_engine/expression/expression.test.cpp
+++ b/test/buildtool/build_engine/expression/expression.test.cpp
@@ -1600,6 +1600,33 @@ TEST_CASE("Expression Assertions", "[expression]") {
[&](auto msg) { log_map << msg; }));
CHECK(log_map.str().find("Found-Empty!!") != std::string::npos);
}
+
+ SECTION("assert") {
+ auto expr = Expression::FromJson(R"(
+ { "type": "assert"
+ , "predicate": {"type": "[]", "index": 0
+ , "list": {"type": "var", "name": "_"}}
+ , "msg": ["First entry UNTRUE", {"type": "var", "name": "_"}]
+ , "$1": {"type": "++", "$1": [{"type": "var", "name": "x"}
+ , ["b", "c"]]}
+ })"_json);
+ REQUIRE(expr);
+
+ CHECK(expr.Evaluate(
+ env.Update("x", Expression::FromJson(R"(["a"])"_json)),
+ fcts) == Expression::FromJson(R"(["a", "b", "c"])"_json));
+
+ std::stringstream log{};
+ CHECK(not expr.Evaluate(
+ env.Update("x", Expression::FromJson(R"([false, "foo"])"_json)),
+ fcts,
+ [&](auto msg) { log << msg; }));
+ // log must contain the canoncial (minimal) repesentation of evaluating
+ // "msg"
+ CHECK(
+ log.str().find(R"(["First entry UNTRUE",[false,"foo","b","c"])"s) !=
+ std::string::npos);
+ }
}
TEST_CASE("Expression hash computation", "[expression]") {