PlaceholderParser: replaced the new "array" operator with "repeat",

simplified the parser a bit more.
This commit is contained in:
Vojtech Bubnik 2023-03-23 17:08:56 +01:00
parent 3045884640
commit 929a0eba2c
2 changed files with 68 additions and 77 deletions

View File

@ -1263,15 +1263,24 @@ namespace client
const std::vector<expr<Iterator>> &il)
{
check_writable(ctx, lhs);
if (lhs.opt->is_scalar()) {
if (il.size() == 1)
// scalar_var = ( scalar )
scalar_variable_assign_scalar_expression(ctx, lhs, il.front());
else
// scalar_var = ()
// or
// scalar_var = ( scalar, scalar, ... )
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
}
auto check_numeric_vector = [](const std::vector<expr<Iterator>> &il) {
for (auto &i : il)
if (! i.numeric_type())
i.throw_exception("Right side is not a numeric expression");
};
if (lhs.opt->is_scalar())
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
ConfigOption *opt = const_cast<ConfigOption*>(lhs.opt);
switch (lhs.opt->type()) {
case coFloats:
@ -1304,11 +1313,11 @@ namespace client
{
if (lhs.opt) {
// Assign to an existing vector variable.
if (lhs.opt->is_scalar())
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", lhs.it_range);
OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
vector_variable_assign_initializer_list(ctx, lhs_opt, il);
} else {
if (il.empty())
ctx->throw_exception("Cannot create vector variable from an empty initializer list, because its type cannot be deduced.", lhs.it_range);
// Allocate a new vector variable.
// First guesstimate type of the output vector.
size_t num_bool = 0;
@ -1344,6 +1353,17 @@ namespace client
}
}
template <typename Iterator>
static bool is_vector_variable_reference(const OptWithPos<Iterator> &var) {
return ! var.has_index() && var.opt->is_vector();
}
// Called when checking whether the NewOldVariable could be assigned a vectir right hand side.
template <typename Iterator>
static bool could_be_vector_variable_reference(const NewOldVariable<Iterator> &var) {
return var.opt == nullptr || var.opt->is_vector();
}
template <typename Iterator>
static void copy_vector_variable_to_vector_variable(
const MyContext *ctx,
@ -1351,9 +1371,9 @@ namespace client
const OptWithPos<Iterator> &rhs)
{
check_writable(ctx, lhs);
assert(rhs.opt->is_vector());
if (! lhs.opt->is_vector())
ctx->throw_exception("Cannot assign vector to a scalar", lhs.it_range);
assert(lhs.opt->is_vector());
if (rhs.has_index() || ! rhs.opt->is_vector())
ctx->throw_exception("Cannot assign scalar to a vector", lhs.it_range);
if (lhs.opt->type() != rhs.opt->type()) {
// Vector types are not compatible.
switch (lhs.opt->type()) {
@ -1372,11 +1392,6 @@ namespace client
const_cast<ConfigOption*>(lhs.opt)->set(rhs.opt);
}
template <typename Iterator>
static bool is_vector_variable_reference(const OptWithPos<Iterator> &var) {
return ! var.has_index() && var.opt->is_vector();
}
template <typename Iterator>
static bool vector_variable_new_from_copy(
const MyContext *ctx,
@ -1384,43 +1399,26 @@ namespace client
NewOldVariable<Iterator> &lhs,
const OptWithPos<Iterator> &rhs)
{
if (is_vector_variable_reference(rhs)) {
if (lhs.opt) {
OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
copy_vector_variable_to_vector_variable(ctx, lhs_opt, rhs);
} else {
// Clone the vector variable.
std::unique_ptr<ConfigOption> opt_new;
if (one_of(rhs.opt->type(), { coFloats, coInts, coStrings, coBools }))
opt_new = std::unique_ptr<ConfigOption>(rhs.opt->clone());
else if (rhs.opt->type() == coPercents)
opt_new = std::make_unique<ConfigOptionFloats>(static_cast<const ConfigOptionPercents*>(rhs.opt)->values);
else
ctx->throw_exception("Duplicating this type of vector variable is not supported", rhs.it_range);
const_cast<MyContext*>(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable);
}
// Continue parsing.
return true;
if (lhs.opt) {
assert(lhs.opt->is_vector());
OptWithPos lhs_opt{ lhs.opt, lhs.it_range, true };
copy_vector_variable_to_vector_variable(ctx, lhs_opt, rhs);
} else {
// Skip parsing this branch, bactrack.
return false;
}
}
template <typename Iterator>
static bool vector_variable_assign_copy(
const MyContext *ctx,
OptWithPos<Iterator> &lhs,
const OptWithPos<Iterator> &rhs)
{
if (is_vector_variable_reference(rhs)) {
copy_vector_variable_to_vector_variable(ctx, lhs, rhs);
// Continue parsing.
return true;
} else {
// Skip parsing this branch, bactrack.
return false;
if (rhs.has_index() || ! rhs.opt->is_vector())
// Stop parsing, let the other rules resolve this case.
return false;
// Clone the vector variable.
std::unique_ptr<ConfigOption> opt_new;
if (one_of(rhs.opt->type(), { coFloats, coInts, coStrings, coBools }))
opt_new = std::unique_ptr<ConfigOption>(rhs.opt->clone());
else if (rhs.opt->type() == coPercents)
opt_new = std::make_unique<ConfigOptionFloats>(static_cast<const ConfigOptionPercents*>(rhs.opt)->values);
else
ctx->throw_exception("Duplicating this type of vector variable is not supported", rhs.it_range);
const_cast<MyContext*>(ctx)->store_new_variable(lhs.name, std::move(opt_new), global_variable);
}
// Continue parsing.
return true;
}
template <typename Iterator>
@ -1572,12 +1570,12 @@ namespace client
// Table to translate symbol tag to a human readable error message.
std::map<std::string, std::string> MyContext::tag_to_error_message = {
{ "array", "Unknown syntax error" },
{ "eoi", "Unknown syntax error" },
{ "start", "Unknown syntax error" },
{ "text", "Invalid text." },
{ "text_block", "Invalid text block." },
{ "macro", "Invalid macro." },
{ "repeat", "Unknown syntax error" },
{ "if_else_output", "Not an {if}{else}{endif} macro." },
{ "switch_output", "Not a {switch} macro." },
{ "legacy_variable_expansion", "Expecting a legacy variable expansion format" },
@ -1594,7 +1592,6 @@ namespace client
{ "optional_parameter", "Expecting a closing brace or an optional parameter." },
{ "one_of_list", "Expecting a list of string patterns (simple text or rexep)" },
{ "variable_reference", "Expecting a variable reference."},
{ "is_nil_test", "Expecting a scalar variable reference."},
{ "variable", "Expecting a variable name."},
{ "regular_expression", "Expecting a regular expression."}
};
@ -1846,37 +1843,35 @@ namespace client
assignment_statement =
variable_reference(_r1)[_a = _1] >> '=' >
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
(lit('(') > new_variable_initializer_list(_r1) > ')')
(lit('(') > initializer_list(_r1) > ')')
[px::bind(&MyContext::vector_variable_assign_initializer_list<Iterator>, _r1, _a, _1)]
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
// Only process such variable references, which return a naked vector variable.
| variable_reference(_r1)
[px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_assign_copy<Iterator>, _r1, _a, _1)]
| eps(px::bind(&MyContext::is_vector_variable_reference<Iterator>, _a)) >>
variable_reference(_r1)[px::bind(&MyContext::copy_vector_variable_to_vector_variable<Iterator>, _r1, _a, _1)]
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
| conditional_expression(_r1)
[px::bind(&MyContext::scalar_variable_assign_scalar_expression<Iterator>, _r1, _a, _1)]
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
| (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
[px::bind(&MyContext::vector_variable_assign_array<Iterator>, _r1, _a, _1, _2)]
);
new_variable_statement =
(kw["local"][_a = false] | kw["global"][_a = true]) > identifier[px::bind(&MyContext::new_old_variable<Iterator>, _r1, _a, _1, _b)] > lit('=') >
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
(lit('(') > new_variable_initializer_list(_r1) > ')')
(lit('(') > initializer_list(_r1) > ')')
[px::bind(&MyContext::vector_variable_new_from_initializer_list<Iterator>, _r1, _a, _b, _1)]
// Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index.
// Only process such variable references, which return a naked vector variable.
| variable_reference(_r1)
[px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy<Iterator>, _r1, _a, _b, _1)]
| eps(px::bind(&MyContext::could_be_vector_variable_reference<Iterator>, _b)) >>
variable_reference(_r1)[px::ref(qi::_pass) = px::bind(&MyContext::vector_variable_new_from_copy<Iterator>, _r1, _a, _b, _1)]
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
| conditional_expression(_r1)
[px::bind(&MyContext::scalar_variable_new_from_scalar_expression<Iterator>, _r1, _a, _b, _1)]
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
| (kw["repeat"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
[px::bind(&MyContext::vector_variable_new_from_array<Iterator>, _r1, _a, _b, _1, _2)]
);
new_variable_initializer_list =
conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append<Iterator>, _val, _1)] >>
*(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append<Iterator>, _val, _1)]);
initializer_list = *(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::initializer_list_append<Iterator>, _val, _1)]);
struct FactorActions {
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)
@ -1920,7 +1915,7 @@ namespace client
[ px::bind(&expr<Iterator>::template digits<true>, _val, _2, _3) ]
| (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
| (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ]
| (kw["is_nil"] > '(' > variable_reference(_r1) > ')') [px::bind(&MyContext::is_nil_test<Iterator>, _r1, _1, _val)]
| (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ]
| (kw["interpolate_table"] > '(' > interpolate_table(_r1) > ')') [ _val = _1 ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
@ -1961,9 +1956,6 @@ namespace client
);
optional_parameter.name("optional_parameter");
is_nil_test = variable_reference(_r1)[px::bind(&MyContext::is_nil_test<Iterator>, _r1, _1, _val)];
is_nil_test.name("is_nil test");
variable_reference =
variable(_r1)[_a=_1] >>
(
@ -1981,7 +1973,6 @@ namespace client
keywords.add
("and")
("array")
("digits")
("zdigits")
("if")
@ -1998,6 +1989,7 @@ namespace client
("min")
("max")
("random")
("repeat")
("round")
("not")
("one_of")
@ -2030,7 +2022,6 @@ namespace client
debug(optional_parameter);
debug(variable_reference);
debug(variable);
debug(is_nil_test);
debug(regular_expression);
}
}
@ -2089,7 +2080,7 @@ namespace client
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>>, spirit_encoding::space_type> assignment_statement;
// Allocating new local or global variables.
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable<Iterator>>, spirit_encoding::space_type> new_variable_statement;
qi::rule<Iterator, std::vector<expr<Iterator>>(const MyContext*), spirit_encoding::space_type> new_variable_initializer_list;
qi::rule<Iterator, std::vector<expr<Iterator>>(const MyContext*), spirit_encoding::space_type> initializer_list;
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
qi::symbols<char> keywords;

View File

@ -186,12 +186,12 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
SECTION("create a string global variable and redefine it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{global mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); }
SECTION("create a bool global variable and redefine it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{global mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); }
SECTION("create an ints local variable with array()") { REQUIRE(parser.process("{local myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); }
SECTION("create a strings local variable array()") { REQUIRE(parser.process("{local mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
SECTION("create a bools local variable array()") { REQUIRE(parser.process("{local mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); }
SECTION("create an ints global variable array()") { REQUIRE(parser.process("{global myint = array(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
SECTION("create a strings global variable array()") { REQUIRE(parser.process("{global mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
SECTION("create a bools global variable array()") { REQUIRE(parser.process("{global mybool = array(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
SECTION("create an ints local variable with repeat()") { REQUIRE(parser.process("{local myint = repeat(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, nullptr) == "24"); }
SECTION("create a strings local variable with repeat()") { REQUIRE(parser.process("{local mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
SECTION("create a bools local variable with repeat()") { REQUIRE(parser.process("{local mybool = repeat(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, nullptr) == "true"); }
SECTION("create an ints global variable with repeat()") { REQUIRE(parser.process("{global myint = repeat(2*3, 4*6)}{myint[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
SECTION("create a strings global variable with repeat()") { REQUIRE(parser.process("{global mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
SECTION("create a bools global variable with repeat()") { REQUIRE(parser.process("{global mybool = repeat(5, 1 + 1 == 2)}{mybool[4]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
SECTION("create an ints local variable with initializer list") { REQUIRE(parser.process("{local myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, nullptr) == "24"); }
SECTION("create a strings local variable with initializer list") { REQUIRE(parser.process("{local mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
@ -208,17 +208,17 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
SECTION("create a bools global variable by a copy") { REQUIRE(parser.process("{global mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, &context_with_global_dict) == "true"); }
SECTION("create an ints local variable by a copy and overwrite it") {
REQUIRE(parser.process("{local myint = temperature}{myint = array(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24");
REQUIRE(parser.process("{local myint = temperature}{myint = repeat(2*3, 4*6)}{myint[5]}", 0, &config, nullptr, nullptr) == "24");
REQUIRE(parser.process("{local myint = temperature}{myint = (2*3, 4*6)}{myint[1]}", 0, &config, nullptr, nullptr) == "24");
REQUIRE(parser.process("{local myint = temperature}{myint = (1)}{myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357");
}
SECTION("create a strings local variable by a copy and overwrite it") {
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = array(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine");
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = repeat(2*3, \"mine\" + \"only\" + \"mine\")}{mystr[5]}", 0, &config, nullptr, nullptr) == "mineonlymine");
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr[1]}", 0, &config, nullptr, nullptr) == "mineonlymine");
REQUIRE(parser.process("{local mystr = filament_notes}{mystr = (2*3, \"mine\" + \"only\" + \"mine\")}{mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes");
}
SECTION("create a bools local variable by a copy and overwrite it") {
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = array(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true");
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = repeat(2*3, true)}{mybool[5]}", 0, &config, nullptr, nullptr) == "true");
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, true)}{mybool[1]}", 0, &config, nullptr, nullptr) == "true");
REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool = (false, false)}{mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true");
}