summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/concepts/expressions.md8
-rw-r--r--src/buildtool/build_engine/expression/evaluator.cpp38
-rw-r--r--test/buildtool/build_engine/expression/expression.test.cpp48
3 files changed, 94 insertions, 0 deletions
diff --git a/doc/concepts/expressions.md b/doc/concepts/expressions.md
index dcd567af..192952f4 100644
--- a/doc/concepts/expressions.md
+++ b/doc/concepts/expressions.md
@@ -259,6 +259,14 @@ those) argument(s) to obtain the final result.
- `"++"` The argument has to be a list of lists. The result is
the concatenation of those lists.
+ - `"+"` The argument has to be a list of numbers. The result is
+ their sum (where the sum of the empty list is, of course, the
+ neutral element 0).
+
+ - `"*"` The argument has to be a list of numbers. The result
+ is their product (where the producut of the empty list is, of
+ course, the neutral element 1).
+
- `"map_union"` The argument has to be a list of maps. The
result is a map containing as keys the union of the keys of
the maps in that list. For each key, the value is the value
diff --git a/src/buildtool/build_engine/expression/evaluator.cpp b/src/buildtool/build_engine/expression/evaluator.cpp
index d2cee809..4499552b 100644
--- a/src/buildtool/build_engine/expression/evaluator.cpp
+++ b/src/buildtool/build_engine/expression/evaluator.cpp
@@ -82,6 +82,42 @@ auto Flatten(ExpressionPtr const& expr) -> ExpressionPtr {
return ExpressionPtr{result};
}
+auto Addition(ExpressionPtr const& expr) -> ExpressionPtr {
+ if (not expr->IsList()) {
+ throw Evaluator::EvaluationError{fmt::format(
+ "Addition expects a list, but found: {}", expr->ToString())};
+ }
+ Expression::number_t result = 0.0;
+ auto const& list = expr->List();
+ std::for_each(list.begin(), list.end(), [&](auto const& f) {
+ if (not f->IsNumber()) {
+ throw Evaluator::EvaluationError{fmt::format(
+ "Non-number entry found for argument to addition: {}",
+ f->ToString())};
+ }
+ result += f->Number();
+ });
+ return ExpressionPtr(result);
+}
+
+auto Multiplication(ExpressionPtr const& expr) -> ExpressionPtr {
+ if (not expr->IsList()) {
+ throw Evaluator::EvaluationError{fmt::format(
+ "Multiplication expects a list, but found: {}", expr->ToString())};
+ }
+ Expression::number_t result = 1.0;
+ auto const& list = expr->List();
+ std::for_each(list.begin(), list.end(), [&](auto const& f) {
+ if (not f->IsNumber()) {
+ throw Evaluator::EvaluationError{fmt::format(
+ "Non-number entry found for argument to multiplication: {}",
+ f->ToString())};
+ }
+ result *= f->Number();
+ });
+ return ExpressionPtr(result);
+}
+
auto All(ExpressionPtr const& list) -> ExpressionPtr {
for (auto const& c : list->List()) {
if (not ValueIsTrue(c)) {
@@ -954,6 +990,8 @@ auto const kBuiltInFunctions =
{"and", AndExpr},
{"or", OrExpr},
{"++", UnaryExpr(Flatten)},
+ {"+", UnaryExpr(Addition)},
+ {"*", UnaryExpr(Multiplication)},
{"nub_right", UnaryExpr(NubRight)},
{"range", UnaryExpr(Range)},
{"change_ending", ChangeEndingExpr},
diff --git a/test/buildtool/build_engine/expression/expression.test.cpp b/test/buildtool/build_engine/expression/expression.test.cpp
index 242c0f4a..83257bf8 100644
--- a/test/buildtool/build_engine/expression/expression.test.cpp
+++ b/test/buildtool/build_engine/expression/expression.test.cpp
@@ -744,6 +744,54 @@ TEST_CASE("Expression Evaluation", "[expression]") { // NOLINT
CHECK(result == Expression::FromJson(R"(["foo", "bar", "baz"])"_json));
}
+ SECTION("+ expression") {
+ auto expr_empty = Expression::FromJson(R"(
+ { "type": "+"
+ , "$1": []
+ })"_json);
+ REQUIRE(expr_empty);
+
+ auto result_empty = expr_empty.Evaluate(env, fcts);
+ REQUIRE(result_empty);
+ REQUIRE(result_empty->IsNumber());
+ CHECK(result_empty == Expression::FromJson(R"(0.0)"_json));
+
+ auto expr = Expression::FromJson(R"(
+ { "type": "+"
+ , "$1": [2, 3, 7, -1]
+ })"_json);
+ REQUIRE(expr);
+
+ auto result = expr.Evaluate(env, fcts);
+ REQUIRE(result);
+ REQUIRE(result->IsNumber());
+ CHECK(result == Expression::FromJson(R"(11.0)"_json));
+ }
+
+ SECTION("* expression") {
+ auto expr_empty = Expression::FromJson(R"(
+ { "type": "*"
+ , "$1": []
+ })"_json);
+ REQUIRE(expr_empty);
+
+ auto result_empty = expr_empty.Evaluate(env, fcts);
+ REQUIRE(result_empty);
+ REQUIRE(result_empty->IsNumber());
+ CHECK(result_empty == Expression::FromJson(R"(1.0)"_json));
+
+ auto expr = Expression::FromJson(R"(
+ { "type": "*"
+ , "$1": [2, 3, 7, -1]
+ })"_json);
+ REQUIRE(expr);
+
+ auto result = expr.Evaluate(env, fcts);
+ REQUIRE(result);
+ REQUIRE(result->IsNumber());
+ CHECK(result == Expression::FromJson(R"(-42.0)"_json));
+ }
+
SECTION("nub_right expression") {
auto expr = Expression::FromJson(R"(
{"type": "nub_right"