diff --git a/conandata.yml b/conandata.yml index dd5ab17..cfb74a8 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1 +1 @@ -version: "1.2.0" +version: "1.2.1" diff --git a/src/ast/comp_chain_expr.cpp b/src/ast/comp_chain_expr.cpp index 7304f09..4afefb3 100644 --- a/src/ast/comp_chain_expr.cpp +++ b/src/ast/comp_chain_expr.cpp @@ -1,6 +1,7 @@ #include "cura-formulae-engine/ast/comp_chain_expr.h" #include "cura-formulae-engine/eval.h" +#include #include #include #include @@ -90,30 +91,31 @@ namespace CuraFormulaeEngine::ast break; case Member: case NotMember: - if (!right_value_result.has_value()) + { + bool found = false; + if (std::holds_alternative(right_value.value)) { - return zeus::unexpected(right_value_result.error()); + if (!std::holds_alternative(left_value.value)) + { + return zeus::unexpected(eval::Error::TypeMismatch); + } + const auto& haystack = std::get(right_value.value); + const auto& needle = std::get(left_value.value); + found = haystack.find(needle) != std::string::npos; } - - const auto& rhs_value = right_value_result.value(); - if (!std::holds_alternative>(rhs_value.value)) + else if (std::holds_alternative>(right_value.value)) { - return zeus::unexpected(eval::Error::TypeMismatch); + const auto& list = std::get>(right_value.value); + found = std::ranges::any_of(list, [&](const eval::Value& item) { return left_value == item; }); } - - comparison_result = operators[i] == NotMember; - - const auto& list = std::get>(rhs_value.value); - for (const auto& item : list) + else { - if (left_value == item) - { - comparison_result = operators[i] == Member; - break; - } + return zeus::unexpected(eval::Error::TypeMismatch); } + comparison_result = (operators[i] == Member) ? found : !found; break; } + } if (!comparison_result.has_value()) { diff --git a/tests/parser.cpp b/tests/parser.cpp index f06fefc..f3cb89f 100644 --- a/tests/parser.cpp +++ b/tests/parser.cpp @@ -508,6 +508,126 @@ TEST_CASE("not member list", "[parser, member]") REQUIRE(eval.value().deepEq(expected_eval)); } +TEST_CASE("member string found", "[parser, member]") +{ + auto input = R"("foo" in "foobar")"sv; + + std::vector expressions; + expressions.push_back(make_expr_ptr(std::string("foo"))); + expressions.push_back(make_expr_ptr(std::string("foobar"))); + + const auto expected_ast = make_expr_ptr(std::move(expressions), std::vector{ComparisonOperators::Member}); + const auto expected_eval = CuraFormulaeEngine::eval::Value(true); + + const auto message = CuraFormulaeEngine::parser::parse(input); + REQUIRE(message.has_value()); + const auto &ast = message.value(); + REQUIRE(ast.deepEq(expected_ast)); + const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env); + REQUIRE(eval.has_value()); + REQUIRE(eval.value().deepEq(expected_eval)); +} + +TEST_CASE("member string not found", "[parser, member]") +{ + auto input = R"("baz" in "foobar")"sv; + + std::vector expressions; + expressions.push_back(make_expr_ptr(std::string("baz"))); + expressions.push_back(make_expr_ptr(std::string("foobar"))); + + const auto expected_ast = make_expr_ptr(std::move(expressions), std::vector{ComparisonOperators::Member}); + const auto expected_eval = CuraFormulaeEngine::eval::Value(false); + + const auto message = CuraFormulaeEngine::parser::parse(input); + REQUIRE(message.has_value()); + const auto &ast = message.value(); + REQUIRE(ast.deepEq(expected_ast)); + const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env); + REQUIRE(eval.has_value()); + REQUIRE(eval.value().deepEq(expected_eval)); +} + +TEST_CASE("not member string found", "[parser, member]") +{ + auto input = R"("foo" not in "foobar")"sv; + + std::vector expressions; + expressions.push_back(make_expr_ptr(std::string("foo"))); + expressions.push_back(make_expr_ptr(std::string("foobar"))); + + const auto expected_ast = make_expr_ptr(std::move(expressions), std::vector{ComparisonOperators::NotMember}); + const auto expected_eval = CuraFormulaeEngine::eval::Value(false); + + const auto message = CuraFormulaeEngine::parser::parse(input); + REQUIRE(message.has_value()); + const auto &ast = message.value(); + REQUIRE(ast.deepEq(expected_ast)); + const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env); + REQUIRE(eval.has_value()); + REQUIRE(eval.value().deepEq(expected_eval)); +} + +TEST_CASE("not member string not found", "[parser, member]") +{ + auto input = R"("baz" not in "foobar")"sv; + + std::vector expressions; + expressions.push_back(make_expr_ptr(std::string("baz"))); + expressions.push_back(make_expr_ptr(std::string("foobar"))); + + const auto expected_ast = make_expr_ptr(std::move(expressions), std::vector{ComparisonOperators::NotMember}); + const auto expected_eval = CuraFormulaeEngine::eval::Value(true); + + const auto message = CuraFormulaeEngine::parser::parse(input); + REQUIRE(message.has_value()); + const auto &ast = message.value(); + REQUIRE(ast.deepEq(expected_ast)); + const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env); + REQUIRE(eval.has_value()); + REQUIRE(eval.value().deepEq(expected_eval)); +} + +TEST_CASE("member string in list", "[parser, member]") +{ + auto input = R"("foo" in ["foo", "bar"])"sv; + + std::vector expressions; + expressions.push_back(make_expr_ptr(std::string("foo"))); + expressions.push_back(make_list_expr(make_expr_ptr(std::string("foo")), make_expr_ptr(std::string("bar")))); + + const auto expected_ast = make_expr_ptr(std::move(expressions), std::vector{ComparisonOperators::Member}); + const auto expected_eval = CuraFormulaeEngine::eval::Value(true); + + const auto message = CuraFormulaeEngine::parser::parse(input); + REQUIRE(message.has_value()); + const auto &ast = message.value(); + REQUIRE(ast.deepEq(expected_ast)); + const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env); + REQUIRE(eval.has_value()); + REQUIRE(eval.value().deepEq(expected_eval)); +} + +TEST_CASE("not member string in list", "[parser, member]") +{ + auto input = R"("baz" not in ["foo", "bar"])"sv; + + std::vector expressions; + expressions.push_back(make_expr_ptr(std::string("baz"))); + expressions.push_back(make_list_expr(make_expr_ptr(std::string("foo")), make_expr_ptr(std::string("bar")))); + + const auto expected_ast = make_expr_ptr(std::move(expressions), std::vector{ComparisonOperators::NotMember}); + const auto expected_eval = CuraFormulaeEngine::eval::Value(true); + + const auto message = CuraFormulaeEngine::parser::parse(input); + REQUIRE(message.has_value()); + const auto &ast = message.value(); + REQUIRE(ast.deepEq(expected_ast)); + const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env); + REQUIRE(eval.has_value()); + REQUIRE(eval.value().deepEq(expected_eval)); +} + TEST_CASE("expression or operator", "[parser, expression]") { auto input = "True or False"sv;