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}
This commit is contained in:
Vojtech Bubnik 2023-03-24 16:35:53 +01:00
parent 59d3683a79
commit a53070f5e6
2 changed files with 42 additions and 8 deletions

View File

@ -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<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block;
// Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> macro;
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> 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<Iterator, std::string(const MyContext*), spirit_encoding::space_type> legacy_variable_expansion;
// Parsed identifier name.

View File

@ -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) == ""); }
}