From bbfb9a4190adea0a2864ecf3a98cb0ab9942035e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 15 Dec 2017 17:14:24 +0100 Subject: [PATCH] Added regex matching operators to the conditional G-code processor in a form similar to Perl: Following expression returns true, if the variable matches the regex: variable =~ /regex/ Following expression returns true, if the variable does not match the regex: variable !~ /regex/ --- xs/src/libslic3r/PlaceholderParser.cpp | 41 ++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 77ef64e8d..b9f6b803e 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -22,6 +22,7 @@ #endif #include +#include // Unicode iterator to iterate over utf8. #include @@ -382,9 +383,37 @@ namespace client lhs.type = TYPE_BOOL; lhs.data.b = (op == '=') ? value : !value; } - static void equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '='); } + static void equal (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '='); } static void not_equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '!'); } + static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) + { + const std::string *subject = nullptr; + const std::string *mask = nullptr; + if (lhs.type == TYPE_STRING) { + // One type is string, the other could be converted to string. + subject = &lhs.s(); + } else { + lhs.throw_exception("Left hand side of a regex match must be a string."); + } + try { + std::string pattern(++ rhs.begin(), -- rhs.end()); + bool result = boost::regex_match(*subject, boost::regex(pattern)); + if (op == '!') + result = ! result; + lhs.reset(); + lhs.type = TYPE_BOOL; + lhs.data.b = result; + } catch (boost::regex_error &ex) { + // Syntax error in the regular expression + boost::throw_exception(qi::expectation_failure( + rhs.begin(), rhs.end(), spirit::info(std::string("*Regular expression compilation failed: ") + ex.what()))); + } + } + + 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 set_if(bool &cond, bool ¬_yet_consumed, std::string &str_in, std::string &str_out) { if (cond && not_yet_consumed) { @@ -810,6 +839,8 @@ namespace client >> *( ("==" > additive_expression(_r1) ) [px::bind(&expr::equal, _val, _1)] | ("!=" > additive_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] | ("<>" > additive_expression(_r1) ) [px::bind(&expr::not_equal, _val, _1)] + | ("=~" > regular_expression ) [px::bind(&expr::regex_matches, _val, _1)] + | ("!~" > regular_expression ) [px::bind(&expr::regex_doesnt_match, _val, _1)] ); bool_expr.name("bool expression"); @@ -877,6 +908,9 @@ namespace client [ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; variable_reference.name("variable reference"); + regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']]; + regular_expression.name("regular_expression"); + keywords.add ("and") ("if") @@ -905,6 +939,7 @@ namespace client debug(factor); debug(scalar_variable_reference); debug(variable_reference); + debug(regular_expression); } } @@ -919,9 +954,11 @@ namespace client // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. qi::rule legacy_variable_expansion; // Parsed identifier name. - qi::rule(), spirit::ascii::space_type> identifier; + qi::rule(), spirit::ascii::space_type> identifier; // Math expression consisting of +- operators over terms. qi::rule(const MyContext*), spirit::ascii::space_type> additive_expression; + // Rule to capture a regular expression enclosed in //. + qi::rule(), spirit::ascii::space_type> regular_expression; // Boolean expressions over expressions. qi::rule(const MyContext*), spirit::ascii::space_type> bool_expr; // Evaluate boolean expression into bool.