PlaceholderParser: changed the syntax of if inside a {} block to

{if condition then block elsif}
The "then" keyword is now mandatory.
On the other hand, "then" keyword must NOT be used using old syntax:
{if condition}...{endif}
This commit is contained in:
Vojtech Bubnik 2023-03-27 15:36:05 +02:00
parent c6c6f361da
commit f591c2503a
2 changed files with 38 additions and 22 deletions

View File

@ -177,7 +177,7 @@ namespace client
bool writable { false }; 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. // -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 }; int index { -1 };
IteratorRange it_range; IteratorRange it_range;
bool empty() const { return opt == nullptr; } bool empty() const { return opt == nullptr; }
bool has_index() const { return index != -1; } 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. // 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. // 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. // {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] > ']') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')
); );
text_block.name("text_block"); text_block.name("text_block");
@ -1874,14 +1874,29 @@ namespace client
// New style of macro expansion. // New style of macro expansion.
// The macro expansion may contain numeric or string expressions, ifs and cases. // The macro expansion may contain numeric or string expressions, ifs and cases.
macro = macros =
(kw["if"] > if_else_output(_r1) [_val = _1]) +(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &lit('}')))[_val += _1] | +lit(';'));
// | (kw["switch"] > switch_output(_r1) [_val = _1]) macros.name("macro");
| (assignment_statement(_r1) [_val = _1]) // if_macros and else_macros only differ by the look-ahead ending condition, which is to not have to repeat the last semicolon
| (new_variable_statement(_r1) [_val = _1]) // at the end of the block.
| (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) 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). // An if expression enclosed in {} (the outmost {} are already parsed by the caller).
// Also }{ could be replaced with ; to simplify writing of pure code. // 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_else_output.name("if_else_output");
if_text_block = (lit('}') > text_block(_r1) > '{'); if_text_block = (lit('}') > text_block(_r1) > '{');
if_text_block.name("if_text_block"); 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). // A switch expression enclosed in {} (the outmost {} are already parsed by the caller).
/* /*
@ -2127,7 +2138,7 @@ namespace client
debug(start); debug(start);
debug(text); debug(text);
debug(text_block); debug(text_block);
debug(macro); debug(macros);
debug(if_else_output); debug(if_else_output);
debug(interpolate_table); debug(interpolate_table);
// debug(switch_output); // debug(switch_output);
@ -2163,7 +2174,7 @@ namespace client
// A free-form text, possibly empty, possibly containing macro expansions. // A free-form text, possibly empty, possibly containing macro expansions.
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block; qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block;
// Statements enclosed in curely braces {} // Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> macro, if_text_block, if_macros, else_macros; qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> 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]. // 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; qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> legacy_variable_expansion;
// Parsed identifier name. // Parsed identifier name.

View File

@ -247,7 +247,7 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
} }
SECTION("nested if with new variables 2, mixing }{ with ;") { SECTION("nested if with new variables 2, mixing }{ with ;") {
std::string script = 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)}"; "{size(myfloats)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
} }
@ -259,22 +259,27 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
} }
SECTION("if with empty block and ;") { SECTION("if with empty block and ;") {
std::string script = 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)}"; "{size(myfloats)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7");
} }
SECTION("nested if with new variables, two level, mixing }{ with ;") { SECTION("nested if with new variables, two level, mixing }{ with ;") {
std::string script = 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)}"; "{size(myints)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); 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 = 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)}"; "{size(myints)}";
REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); 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("nested if with new variables, two level, mixing }{ with ; 3") {
SECTION("if else completely empty 2") { REQUIRE(parser.process("{if false; elsif false; else endif}", 0, nullptr, nullptr, nullptr) == ""); } 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) == ""); }
} }