diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index cb0760d4d..5f8624ad3 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -587,7 +587,7 @@ namespace client param1.set_s(buf); } - static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) + static void regex_op(const expr &lhs, boost::iterator_range &rhs, char op, expr &out) { const std::string *subject = nullptr; if (lhs.type() == TYPE_STRING) { @@ -601,7 +601,7 @@ namespace client bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern)); if (op == '!') result = ! result; - lhs.set_b(result); + out.set_b(result); } catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) { // Syntax error in the regular expression boost::throw_exception(qi::expectation_failure( @@ -609,8 +609,37 @@ namespace client } } - static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '='); } - static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!'); } + static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '=', lhs); } + static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!', lhs); } + + static void one_of_test_init(expr &out) { + out.set_b(false); + } + template + static void one_of_test(const expr &match, const expr &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + if (pattern.type() != TYPE_STRING) + match.throw_exception("one_of(): Pattern has to be a string value"); + if (RegEx) { + try { + out.set_b(SLIC3R_REGEX_NAMESPACE::regex_match(match.s(), SLIC3R_REGEX_NAMESPACE::regex(pattern.s()))); + } catch (SLIC3R_REGEX_NAMESPACE::regex_error &) { + // Syntax error in the regular expression + pattern.throw_exception("Regular expression compilation failed"); + } + } else + out.set_b(match.s() == pattern.s()); + } + } + static void one_of_test_regex(const expr &match, boost::iterator_range &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + regex_op(match, pattern, '=', out); + } + } static void logical_op(expr &lhs, expr &rhs, char op) { @@ -1101,6 +1130,7 @@ namespace client { "multiplicative_expression", "Expecting an expression." }, { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, + { "one_of_list", "Expecting a list of string patterns (simple text or rexep)" }, { "variable_reference", "Expecting a variable reference."}, { "is_nil_test", "Expecting a scalar variable reference."}, { "variable", "Expecting a variable name."}, @@ -1221,6 +1251,7 @@ namespace client qi::_a_type _a; qi::_b_type _b; qi::_r1_type _r1; + qi::_r2_type _r2; // Starting symbol of the grammer. // The leading eps is required by the "expectation point" operator ">". @@ -1395,7 +1426,8 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] - | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1] + | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ] + | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1404,6 +1436,20 @@ namespace client ); unary_expression.name("unary_expression"); + one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2]; + one_of.name("one_of"); + one_of_list = + eps[px::bind(&expr::one_of_test_init, _val)] > + ( ',' > *( + ( + unary_expression(_r1)[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | (lit('~') > unary_expression(_r1))[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] + ) >> -lit(',')) + | eps + ); + one_of_list.name("one_of_list"); + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] @@ -1445,6 +1491,7 @@ namespace client ("random") ("round") ("not") + ("one_of") ("or") ("true"); @@ -1466,6 +1513,8 @@ namespace client debug(additive_expression); debug(multiplicative_expression); debug(unary_expression); + debug(one_of); + debug(one_of_list); debug(optional_parameter); debug(variable_reference); debug(variable); @@ -1517,6 +1566,9 @@ namespace client qi::rule(const MyContext*), spirit_encoding::space_type> variable; // Evaluating whether a nullable variable is nil. qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; + // Evaluating "one of" list of patterns. + qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> one_of; + qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 5248e089a..e40657d16 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -113,6 +113,18 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); } SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); } SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); } + SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); } SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); } SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); }