From a53070f5e61cbe5539775cf94fa2de4fb444f87c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 24 Mar 2023 16:35:53 +0100 Subject: [PATCH] PlaceholderParser: Simpler if / elsif / else / endif syntax. {if cond1}{expr1}{elsif cond2}{expr2}{else}{expr3}{endif} could be written as {if cond1;expr1;elsif cond2;expr2;else;expr3;endif} the first semicolon after else is not manadtory, thus the following is valid {if cond1;expr1;elsif cond2;expr2;else expr3;endif} each expression could be also empty or contain multiple expressions: {if cond1;elsif cond2;else endif} --- src/libslic3r/PlaceholderParser.cpp | 24 ++++++++++++------- tests/libslic3r/test_placeholder_parser.cpp | 26 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index fb9fdbea0..db7c95bfd 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -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(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}') + | (lit('{') >> macro(_r1)[_val += _1] % (+lit(';')) > *lit(';') > '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -1884,16 +1884,24 @@ namespace client 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. if_else_output = eps[_a=true] > - (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > '}' > - text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{' > - *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > '}' > - text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{') > - -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > lit('}') > - text_block(_r1)[px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > '{') > + (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > (if_text_block(_r1) | if_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > + *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > (if_text_block(_r1) | if_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)]) > + -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > (if_text_block(_r1) | else_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > kw["endif"]; 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). /* switch_output = @@ -2155,7 +2163,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; + qi::rule macro, 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 0e3521a23..79cdfdea9 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -245,10 +245,36 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } + 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}" + "{size(myfloats)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); + } SECTION("nested if with new variables, two level") { 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}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } + SECTION("if with empty block and ;") { + std::string script = + "{if false;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}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("nested if with new variables, two level, ;, no semicolon after else") { + 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}" + "{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) == ""); } }