diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index db7c95bfd..11ffd4a54 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -177,7 +177,7 @@ namespace client bool writable { false }; // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. int index { -1 }; - IteratorRange it_range; + IteratorRange it_range; bool empty() const { return opt == nullptr; } bool has_index() const { return index != -1; } @@ -1862,7 +1862,7 @@ namespace client // Allow back tracking after '{' in case of a text_block embedded inside a condition. // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. - | (lit('{') >> macro(_r1)[_val += _1] % (+lit(';')) > *lit(';') > '}') + | (lit('{') >> (macros(_r1)[_val += _1] > '}') | '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -1874,14 +1874,29 @@ namespace client // New style of macro expansion. // The macro expansion may contain numeric or string expressions, ifs and cases. - macro = - (kw["if"] > if_else_output(_r1) [_val = _1]) -// | (kw["switch"] > switch_output(_r1) [_val = _1]) - | (assignment_statement(_r1) [_val = _1]) - | (new_variable_statement(_r1) [_val = _1]) - | (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + macros = + +(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &lit('}')))[_val += _1] | +lit(';')); + macros.name("macro"); + // if_macros and else_macros only differ by the look-ahead ending condition, which is to not have to repeat the last semicolon + // at the end of the block. + if_macros = kw["then"] > *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])))[_val += _1] | +lit(';')); + if_macros.name("if_macros"); + else_macros = *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &kw["endif"]))[_val += _1] | +lit(';')); + else_macros.name("else_macros"); + + // Blocks do not require a separating semicolon. + block = + (kw["if"] > if_else_output(_r1)[_val = _1]) + // (kw["switch"] ... + ; + block.name("block"); + + // Statements require a separating semicolon. + statement = + (assignment_statement(_r1) [_val = _1]) + | (new_variable_statement(_r1)[_val = _1]) + | (conditional_expression(_r1)[px::bind(&expr::to_string2, _1, _val)]) ; - macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). // Also }{ could be replaced with ; to simplify writing of pure code. @@ -1897,10 +1912,6 @@ namespace client if_else_output.name("if_else_output"); if_text_block = (lit('}') > text_block(_r1) > '{'); if_text_block.name("if_text_block"); - if_macros = +lit(';') > (macro(_r1)[_val += _1] % (+lit(';')) | eps) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])); - if_macros.name("if_macros"); - else_macros = *lit(';') > (macro(_r1)[_val += _1] % (+lit(';')) | eps) > (+lit(';') | &kw["endif"]); - else_macros.name("else_macros"); // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). /* @@ -2127,7 +2138,7 @@ namespace client debug(start); debug(text); debug(text_block); - debug(macro); + debug(macros); debug(if_else_output); debug(interpolate_table); // debug(switch_output); @@ -2163,7 +2174,7 @@ namespace client // A free-form text, possibly empty, possibly containing macro expansions. qi::rule text_block; // Statements enclosed in curely braces {} - qi::rule macro, if_text_block, if_macros, else_macros; + qi::rule block, statement, macros, if_text_block, if_macros, else_macros; // 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. diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 79cdfdea9..a6da48bf3 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -247,7 +247,7 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { } SECTION("nested if with new variables 2, mixing }{ with ;") { std::string script = - "{if 1 == 0;local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{if 1 == 0 then local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } @@ -259,22 +259,27 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { } SECTION("if with empty block and ;") { std::string script = - "{if false;else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{if false then else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } SECTION("nested if with new variables, two level, mixing }{ with ;") { std::string script = - "{if 1 == 1;if 2 == 3;nejaka / haluz;else;local myints = (6, 5, 4, 3, 2, 1);endif;else;if zase * haluz;else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif;endif}" + "{if 1 == 1 then if 2 == 3}nejaka / haluz{else local myints = (6, 5, 4, 3, 2, 1) endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } - SECTION("nested if with new variables, two level, ;, no semicolon after else") { + SECTION("nested if with new variables, two level, mixing }{ with ; 2") { std::string script = - "{if 1 == 1;if 2 == 3;nejaka / haluz;else local myints = (6, 5, 4, 3, 2, 1);endif;else if zase * haluz;else local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif;endif}" + "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } - SECTION("if else completely empty") { REQUIRE(parser.process("{if false; elsif false; else; endif}", 0, nullptr, nullptr, nullptr) == ""); } - SECTION("if else completely empty 2") { REQUIRE(parser.process("{if false; elsif false; else endif}", 0, nullptr, nullptr, nullptr) == ""); } + SECTION("nested if with new variables, two level, mixing }{ with ; 3") { + std::string script = + "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else}{if zase * haluz}{else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif}{endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("if else completely empty") { REQUIRE(parser.process("{if false then elsif false then else endif}", 0, nullptr, nullptr, nullptr) == ""); } }