WIP: PlaceholderParser support for writable output variables.
This commit is contained in:
parent
39bca4420c
commit
b9d8fe7118
@ -171,7 +171,8 @@ namespace client
|
|||||||
struct OptWithPos {
|
struct OptWithPos {
|
||||||
OptWithPos() {}
|
OptWithPos() {}
|
||||||
OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range<Iterator> it_range) : opt(opt), it_range(it_range) {}
|
OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range<Iterator> it_range) : opt(opt), it_range(it_range) {}
|
||||||
ConfigOptionConstPtr opt = nullptr;
|
ConfigOptionConstPtr opt { nullptr };
|
||||||
|
bool writable { false };
|
||||||
boost::iterator_range<Iterator> it_range;
|
boost::iterator_range<Iterator> it_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -688,6 +689,7 @@ namespace client
|
|||||||
const DynamicConfig *external_config = nullptr;
|
const DynamicConfig *external_config = nullptr;
|
||||||
const DynamicConfig *config = nullptr;
|
const DynamicConfig *config = nullptr;
|
||||||
const DynamicConfig *config_override = nullptr;
|
const DynamicConfig *config_override = nullptr;
|
||||||
|
mutable DynamicConfig *config_outputs = nullptr;
|
||||||
size_t current_extruder_id = 0;
|
size_t current_extruder_id = 0;
|
||||||
PlaceholderParser::ContextData *context_data = nullptr;
|
PlaceholderParser::ContextData *context_data = nullptr;
|
||||||
// If false, the macro_processor will evaluate a full macro.
|
// If false, the macro_processor will evaluate a full macro.
|
||||||
@ -713,6 +715,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); }
|
const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); }
|
||||||
|
ConfigOption* resolve_output_symbol(const std::string &opt_key) const { return this->config_outputs ? this->config_outputs->optptr(opt_key, false) : nullptr; }
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
static void legacy_variable_expansion(
|
static void legacy_variable_expansion(
|
||||||
@ -788,8 +791,12 @@ namespace client
|
|||||||
OptWithPos<Iterator> &output)
|
OptWithPos<Iterator> &output)
|
||||||
{
|
{
|
||||||
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end()));
|
const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end()));
|
||||||
|
if (opt == nullptr) {
|
||||||
|
opt = ctx->resolve_output_symbol(std::string(opt_key.begin(), opt_key.end()));
|
||||||
if (opt == nullptr)
|
if (opt == nullptr)
|
||||||
ctx->throw_exception("Not a variable name", opt_key);
|
ctx->throw_exception("Not a variable name", opt_key);
|
||||||
|
output.writable = true;
|
||||||
|
}
|
||||||
output.opt = opt;
|
output.opt = opt;
|
||||||
output.it_range = opt_key;
|
output.it_range = opt_key;
|
||||||
}
|
}
|
||||||
@ -914,6 +921,98 @@ namespace client
|
|||||||
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
|
output.it_range = boost::iterator_range<Iterator>(opt.it_range.begin(), it_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decoding a scalar variable symbol "opt", assigning it a value of "param".
|
||||||
|
template <typename Iterator>
|
||||||
|
static void scalar_variable_assign(
|
||||||
|
const MyContext *ctx,
|
||||||
|
OptWithPos<Iterator> &opt,
|
||||||
|
expr<Iterator> ¶m,
|
||||||
|
// Not used, just clear it.
|
||||||
|
std::string &out)
|
||||||
|
{
|
||||||
|
if (! opt.writable)
|
||||||
|
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
||||||
|
if (opt.opt->is_vector())
|
||||||
|
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
|
||||||
|
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
|
||||||
|
switch (wropt->type()) {
|
||||||
|
case coFloat:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||||
|
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
|
||||||
|
break;
|
||||||
|
case coInt:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||||
|
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
|
||||||
|
break;
|
||||||
|
case coString:
|
||||||
|
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
|
||||||
|
break;
|
||||||
|
case coPercent:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||||
|
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
|
||||||
|
break;
|
||||||
|
case coBool:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_BOOL)
|
||||||
|
ctx->throw_exception("Right side is not a boolean expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionBool*>(wropt)->value = param.b();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctx->throw_exception("Unsupported output scalar variable type", opt.it_range);
|
||||||
|
}
|
||||||
|
out.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void vector_variable_assign(
|
||||||
|
const MyContext *ctx,
|
||||||
|
OptWithPos<Iterator> &opt,
|
||||||
|
int &index,
|
||||||
|
expr<Iterator> ¶m,
|
||||||
|
// Not used, just clear it.
|
||||||
|
std::string &out)
|
||||||
|
{
|
||||||
|
if (! opt.writable)
|
||||||
|
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
||||||
|
if (opt.opt->is_scalar())
|
||||||
|
ctx->throw_exception("Referencing an output scalar variable when vector is expected", opt.it_range);
|
||||||
|
ConfigOptionVectorBase *vec = const_cast<ConfigOptionVectorBase*>(static_cast<const ConfigOptionVectorBase*>(opt.opt));
|
||||||
|
if (vec->empty())
|
||||||
|
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
|
||||||
|
if (index < 0 || index >= int(vec->size()))
|
||||||
|
ctx->throw_exception("Index out of range", opt.it_range);
|
||||||
|
switch (opt.opt->type()) {
|
||||||
|
case coFloats:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||||
|
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionFloats*>(vec)->values[index] = param.as_d();
|
||||||
|
break;
|
||||||
|
case coInts:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||||
|
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionInts*>(vec)->values[index] = param.as_i();
|
||||||
|
break;
|
||||||
|
case coStrings:
|
||||||
|
static_cast<ConfigOptionStrings*>(vec)->values[index] = param.to_string();
|
||||||
|
break;
|
||||||
|
case coPercents:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
||||||
|
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionPercents*>(vec)->values[index] = param.as_d();
|
||||||
|
break;
|
||||||
|
case coBools:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_BOOL)
|
||||||
|
ctx->throw_exception("Right side is not a boolean expression", param.it_range);
|
||||||
|
static_cast<ConfigOptionBools*>(vec)->values[index] = param.b();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctx->throw_exception("Unsupported output vector variable type", opt.it_range);
|
||||||
|
}
|
||||||
|
out.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that the expression returns an integer, which may be used
|
// Verify that the expression returns an integer, which may be used
|
||||||
// to address a vector.
|
// to address a vector.
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
@ -1165,7 +1264,9 @@ namespace client
|
|||||||
macro =
|
macro =
|
||||||
(kw["if"] > if_else_output(_r1) [_val = _1])
|
(kw["if"] > if_else_output(_r1) [_val = _1])
|
||||||
// | (kw["switch"] > switch_output(_r1) [_val = _1])
|
// | (kw["switch"] > switch_output(_r1) [_val = _1])
|
||||||
| additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ];
|
| (assignment_statement(_r1) [_val = _1])
|
||||||
|
| (additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ])
|
||||||
|
;
|
||||||
macro.name("macro");
|
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).
|
||||||
@ -1257,6 +1358,15 @@ namespace client
|
|||||||
);
|
);
|
||||||
multiplicative_expression.name("multiplicative_expression");
|
multiplicative_expression.name("multiplicative_expression");
|
||||||
|
|
||||||
|
assignment_statement =
|
||||||
|
variable_reference(_r1)[_a = _1] >>
|
||||||
|
(
|
||||||
|
('[' >> additive_expression(_r1)[px::bind(&MyContext::evaluate_index<Iterator>, _1, _b)] >> ']' >> '=' >> additive_expression(_r1))
|
||||||
|
[px::bind(&MyContext::vector_variable_assign<Iterator>, _r1, _a, _b, _2, _val)]
|
||||||
|
| ('=' >> additive_expression(_r1))
|
||||||
|
[px::bind(&MyContext::scalar_variable_assign<Iterator>, _r1, _a, _1, _val)]
|
||||||
|
);
|
||||||
|
|
||||||
struct FactorActions {
|
struct FactorActions {
|
||||||
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)
|
static void set_start_pos(Iterator &start_pos, expr<Iterator> &out)
|
||||||
{ out.it_range = boost::iterator_range<Iterator>(start_pos, start_pos); }
|
{ out.it_range = boost::iterator_range<Iterator>(start_pos, start_pos); }
|
||||||
@ -1430,6 +1540,7 @@ namespace client
|
|||||||
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> is_nil_test;
|
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> is_nil_test;
|
||||||
|
|
||||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
|
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
|
||||||
|
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> assignment_statement;
|
||||||
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
|
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
|
||||||
|
|
||||||
qi::symbols<char> keywords;
|
qi::symbols<char> keywords;
|
||||||
@ -1461,12 +1572,13 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context_data) const
|
std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context_data) const
|
||||||
{
|
{
|
||||||
client::MyContext context;
|
client::MyContext context;
|
||||||
context.external_config = this->external_config();
|
context.external_config = this->external_config();
|
||||||
context.config = &this->config();
|
context.config = &this->config();
|
||||||
context.config_override = config_override;
|
context.config_override = config_override;
|
||||||
|
context.config_outputs = config_outputs;
|
||||||
context.current_extruder_id = current_extruder_id;
|
context.current_extruder_id = current_extruder_id;
|
||||||
context.context_data = context_data;
|
context.context_data = context_data;
|
||||||
return process_macro(templ, context);
|
return process_macro(templ, context);
|
||||||
|
@ -55,7 +55,9 @@ public:
|
|||||||
|
|
||||||
// Fill in the template using a macro processing language.
|
// Fill in the template using a macro processing language.
|
||||||
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
||||||
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const;
|
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context) const;
|
||||||
|
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const
|
||||||
|
{ return this->process(templ, current_extruder_id, config_override, nullptr /* config_outputs */, context); }
|
||||||
|
|
||||||
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
|
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
|
||||||
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
// Throws Slic3r::PlaceholderParserError on syntax or runtime error.
|
||||||
|
@ -117,4 +117,17 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
|||||||
SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); }
|
SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); }
|
||||||
SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); }
|
SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); }
|
||||||
SECTION("enum expression") { REQUIRE(boolean_expression("gcode_flavor == \"marlin\"")); }
|
SECTION("enum expression") { REQUIRE(boolean_expression("gcode_flavor == \"marlin\"")); }
|
||||||
|
|
||||||
|
SECTION("write to a scalar variable") {
|
||||||
|
DynamicConfig config_outputs;
|
||||||
|
config_outputs.set_key_value("writable_string", new ConfigOptionString());
|
||||||
|
parser.process("{writable_string = \"Written\"}", 0, nullptr, &config_outputs, nullptr);
|
||||||
|
REQUIRE(parser.process("{writable_string}", 0, nullptr, &config_outputs, nullptr) == "Written");
|
||||||
|
}
|
||||||
|
SECTION("write to a vector variable") {
|
||||||
|
DynamicConfig config_outputs;
|
||||||
|
config_outputs.set_key_value("writable_floats", new ConfigOptionFloats({ 0., 0., 0. }));
|
||||||
|
parser.process("{writable_floats[1] = 33}", 0, nullptr, &config_outputs, nullptr);
|
||||||
|
REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user