Skip to content

Commit 07e8d9a

Browse files
authored
Merge pull request #6 from Ultimaker/CURA-13248
[Cura 13248] Support string membership in chain comparisons
2 parents ae22371 + 4216c1c commit 07e8d9a

3 files changed

Lines changed: 139 additions & 17 deletions

File tree

conandata.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version: "1.2.0"
1+
version: "1.2.1"

src/ast/comp_chain_expr.cpp

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "cura-formulae-engine/ast/comp_chain_expr.h"
22
#include "cura-formulae-engine/eval.h"
33

4+
#include <algorithm>
45
#include <string>
56
#include <unordered_set>
67
#include <variant>
@@ -90,30 +91,31 @@ namespace CuraFormulaeEngine::ast
9091
break;
9192
case Member:
9293
case NotMember:
93-
if (!right_value_result.has_value())
94+
{
95+
bool found = false;
96+
if (std::holds_alternative<std::string>(right_value.value))
9497
{
95-
return zeus::unexpected(right_value_result.error());
98+
if (!std::holds_alternative<std::string>(left_value.value))
99+
{
100+
return zeus::unexpected(eval::Error::TypeMismatch);
101+
}
102+
const auto& haystack = std::get<std::string>(right_value.value);
103+
const auto& needle = std::get<std::string>(left_value.value);
104+
found = haystack.find(needle) != std::string::npos;
96105
}
97-
98-
const auto& rhs_value = right_value_result.value();
99-
if (!std::holds_alternative<std::vector<eval::Value>>(rhs_value.value))
106+
else if (std::holds_alternative<std::vector<eval::Value>>(right_value.value))
100107
{
101-
return zeus::unexpected(eval::Error::TypeMismatch);
108+
const auto& list = std::get<std::vector<eval::Value>>(right_value.value);
109+
found = std::ranges::any_of(list, [&](const eval::Value& item) { return left_value == item; });
102110
}
103-
104-
comparison_result = operators[i] == NotMember;
105-
106-
const auto& list = std::get<std::vector<eval::Value>>(rhs_value.value);
107-
for (const auto& item : list)
111+
else
108112
{
109-
if (left_value == item)
110-
{
111-
comparison_result = operators[i] == Member;
112-
break;
113-
}
113+
return zeus::unexpected(eval::Error::TypeMismatch);
114114
}
115+
comparison_result = (operators[i] == Member) ? found : !found;
115116
break;
116117
}
118+
}
117119

118120
if (!comparison_result.has_value())
119121
{

tests/parser.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,126 @@ TEST_CASE("not member list", "[parser, member]")
508508
REQUIRE(eval.value().deepEq(expected_eval));
509509
}
510510

511+
TEST_CASE("member string found", "[parser, member]")
512+
{
513+
auto input = R"("foo" in "foobar")"sv;
514+
515+
std::vector<ExprPtr> expressions;
516+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foo")));
517+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foobar")));
518+
519+
const auto expected_ast = make_expr_ptr<ComparisonChainExpr>(std::move(expressions), std::vector{ComparisonOperators::Member});
520+
const auto expected_eval = CuraFormulaeEngine::eval::Value(true);
521+
522+
const auto message = CuraFormulaeEngine::parser::parse(input);
523+
REQUIRE(message.has_value());
524+
const auto &ast = message.value();
525+
REQUIRE(ast.deepEq(expected_ast));
526+
const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env);
527+
REQUIRE(eval.has_value());
528+
REQUIRE(eval.value().deepEq(expected_eval));
529+
}
530+
531+
TEST_CASE("member string not found", "[parser, member]")
532+
{
533+
auto input = R"("baz" in "foobar")"sv;
534+
535+
std::vector<ExprPtr> expressions;
536+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("baz")));
537+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foobar")));
538+
539+
const auto expected_ast = make_expr_ptr<ComparisonChainExpr>(std::move(expressions), std::vector{ComparisonOperators::Member});
540+
const auto expected_eval = CuraFormulaeEngine::eval::Value(false);
541+
542+
const auto message = CuraFormulaeEngine::parser::parse(input);
543+
REQUIRE(message.has_value());
544+
const auto &ast = message.value();
545+
REQUIRE(ast.deepEq(expected_ast));
546+
const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env);
547+
REQUIRE(eval.has_value());
548+
REQUIRE(eval.value().deepEq(expected_eval));
549+
}
550+
551+
TEST_CASE("not member string found", "[parser, member]")
552+
{
553+
auto input = R"("foo" not in "foobar")"sv;
554+
555+
std::vector<ExprPtr> expressions;
556+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foo")));
557+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foobar")));
558+
559+
const auto expected_ast = make_expr_ptr<ComparisonChainExpr>(std::move(expressions), std::vector{ComparisonOperators::NotMember});
560+
const auto expected_eval = CuraFormulaeEngine::eval::Value(false);
561+
562+
const auto message = CuraFormulaeEngine::parser::parse(input);
563+
REQUIRE(message.has_value());
564+
const auto &ast = message.value();
565+
REQUIRE(ast.deepEq(expected_ast));
566+
const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env);
567+
REQUIRE(eval.has_value());
568+
REQUIRE(eval.value().deepEq(expected_eval));
569+
}
570+
571+
TEST_CASE("not member string not found", "[parser, member]")
572+
{
573+
auto input = R"("baz" not in "foobar")"sv;
574+
575+
std::vector<ExprPtr> expressions;
576+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("baz")));
577+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foobar")));
578+
579+
const auto expected_ast = make_expr_ptr<ComparisonChainExpr>(std::move(expressions), std::vector{ComparisonOperators::NotMember});
580+
const auto expected_eval = CuraFormulaeEngine::eval::Value(true);
581+
582+
const auto message = CuraFormulaeEngine::parser::parse(input);
583+
REQUIRE(message.has_value());
584+
const auto &ast = message.value();
585+
REQUIRE(ast.deepEq(expected_ast));
586+
const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env);
587+
REQUIRE(eval.has_value());
588+
REQUIRE(eval.value().deepEq(expected_eval));
589+
}
590+
591+
TEST_CASE("member string in list", "[parser, member]")
592+
{
593+
auto input = R"("foo" in ["foo", "bar"])"sv;
594+
595+
std::vector<ExprPtr> expressions;
596+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("foo")));
597+
expressions.push_back(make_list_expr(make_expr_ptr<StringExpr>(std::string("foo")), make_expr_ptr<StringExpr>(std::string("bar"))));
598+
599+
const auto expected_ast = make_expr_ptr<ComparisonChainExpr>(std::move(expressions), std::vector{ComparisonOperators::Member});
600+
const auto expected_eval = CuraFormulaeEngine::eval::Value(true);
601+
602+
const auto message = CuraFormulaeEngine::parser::parse(input);
603+
REQUIRE(message.has_value());
604+
const auto &ast = message.value();
605+
REQUIRE(ast.deepEq(expected_ast));
606+
const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env);
607+
REQUIRE(eval.has_value());
608+
REQUIRE(eval.value().deepEq(expected_eval));
609+
}
610+
611+
TEST_CASE("not member string in list", "[parser, member]")
612+
{
613+
auto input = R"("baz" not in ["foo", "bar"])"sv;
614+
615+
std::vector<ExprPtr> expressions;
616+
expressions.push_back(make_expr_ptr<StringExpr>(std::string("baz")));
617+
expressions.push_back(make_list_expr(make_expr_ptr<StringExpr>(std::string("foo")), make_expr_ptr<StringExpr>(std::string("bar"))));
618+
619+
const auto expected_ast = make_expr_ptr<ComparisonChainExpr>(std::move(expressions), std::vector{ComparisonOperators::NotMember});
620+
const auto expected_eval = CuraFormulaeEngine::eval::Value(true);
621+
622+
const auto message = CuraFormulaeEngine::parser::parse(input);
623+
REQUIRE(message.has_value());
624+
const auto &ast = message.value();
625+
REQUIRE(ast.deepEq(expected_ast));
626+
const auto eval = ast.evaluate(&CuraFormulaeEngine::env::std_env);
627+
REQUIRE(eval.has_value());
628+
REQUIRE(eval.value().deepEq(expected_eval));
629+
}
630+
511631
TEST_CASE("expression or operator", "[parser, expression]")
512632
{
513633
auto input = "True or False"sv;

0 commit comments

Comments
 (0)