mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-06 04:48:32 +08:00
Merge branch 'master' into fs_svg
This commit is contained in:
commit
916baa9ef8
@ -1086,6 +1086,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
m_placeholder_parser = print.placeholder_parser();
|
m_placeholder_parser = print.placeholder_parser();
|
||||||
m_placeholder_parser.update_timestamp();
|
m_placeholder_parser.update_timestamp();
|
||||||
m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||||
|
// Enable passing global variables between PlaceholderParser invocations.
|
||||||
|
m_placeholder_parser_context.global_config = std::make_unique<DynamicConfig>();
|
||||||
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
|
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
|
||||||
|
|
||||||
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
||||||
|
@ -191,6 +191,10 @@ namespace client
|
|||||||
struct expr
|
struct expr
|
||||||
{
|
{
|
||||||
expr() {}
|
expr() {}
|
||||||
|
expr(const expr &rhs) : m_type(rhs.type()), it_range(rhs.it_range)
|
||||||
|
{ if (rhs.type() == TYPE_STRING) m_data.s = new std::string(*rhs.m_data.s); else m_data.set(rhs.m_data); }
|
||||||
|
expr(expr &&rhs) : expr(std::move(rhs), rhs.it_range.begin(), rhs.it_range.end()) {}
|
||||||
|
|
||||||
explicit expr(bool b) : m_type(TYPE_BOOL) { m_data.b = b; }
|
explicit expr(bool b) : m_type(TYPE_BOOL) { m_data.b = b; }
|
||||||
explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_BOOL), it_range(it_begin, it_end) { m_data.b = b; }
|
explicit expr(bool b, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_BOOL), it_range(it_begin, it_end) { m_data.b = b; }
|
||||||
explicit expr(int i) : m_type(TYPE_INT) { m_data.i = i; }
|
explicit expr(int i) : m_type(TYPE_INT) { m_data.i = i; }
|
||||||
@ -201,9 +205,6 @@ namespace client
|
|||||||
explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
|
explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
|
||||||
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) :
|
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) :
|
||||||
m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); }
|
m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); }
|
||||||
expr(const expr &rhs) : m_type(rhs.type()), it_range(rhs.it_range)
|
|
||||||
{ if (rhs.type() == TYPE_STRING) m_data.s = new std::string(*rhs.m_data.s); else m_data.set(rhs.m_data); }
|
|
||||||
explicit expr(expr &&rhs) : expr(rhs, rhs.it_range.begin(), rhs.it_range.end()) {}
|
|
||||||
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end }
|
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end }
|
||||||
{
|
{
|
||||||
m_data.set(rhs.m_data);
|
m_data.set(rhs.m_data);
|
||||||
@ -249,6 +250,7 @@ namespace client
|
|||||||
TYPE_STRING,
|
TYPE_STRING,
|
||||||
};
|
};
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
|
bool numeric_type() const { return m_type == TYPE_INT || m_type == TYPE_DOUBLE; }
|
||||||
|
|
||||||
bool& b() { return m_data.b; }
|
bool& b() { return m_data.b; }
|
||||||
bool b() const { return m_data.b; }
|
bool b() const { return m_data.b; }
|
||||||
@ -472,8 +474,7 @@ namespace client
|
|||||||
static void compare_op(expr &lhs, expr &rhs, char op, bool invert)
|
static void compare_op(expr &lhs, expr &rhs, char op, bool invert)
|
||||||
{
|
{
|
||||||
bool value = false;
|
bool value = false;
|
||||||
if ((lhs.type() == TYPE_INT || lhs.type() == TYPE_DOUBLE) &&
|
if (lhs.numeric_type() && rhs.numeric_type()) {
|
||||||
(rhs.type() == TYPE_INT || rhs.type() == TYPE_DOUBLE)) {
|
|
||||||
// Both types are numeric.
|
// Both types are numeric.
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case '=':
|
case '=':
|
||||||
@ -681,7 +682,7 @@ namespace client
|
|||||||
|
|
||||||
void throw_if_not_numeric(const char *message) const
|
void throw_if_not_numeric(const char *message) const
|
||||||
{
|
{
|
||||||
if (this->type() != TYPE_INT && this->type() != TYPE_DOUBLE)
|
if (! this->numeric_type())
|
||||||
this->throw_exception(message);
|
this->throw_exception(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,7 +724,10 @@ namespace client
|
|||||||
const DynamicConfig *config = nullptr;
|
const DynamicConfig *config = nullptr;
|
||||||
const DynamicConfig *config_override = nullptr;
|
const DynamicConfig *config_override = nullptr;
|
||||||
mutable DynamicConfig *config_outputs = nullptr;
|
mutable DynamicConfig *config_outputs = nullptr;
|
||||||
|
// Local variables, read / write
|
||||||
|
mutable DynamicConfig config_local;
|
||||||
size_t current_extruder_id = 0;
|
size_t current_extruder_id = 0;
|
||||||
|
// Random number generator and optionally global variables.
|
||||||
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.
|
||||||
// If true, the macro processor will evaluate just a boolean condition using the full expressive power of the macro processor.
|
// If true, the macro processor will evaluate just a boolean condition using the full expressive power of the macro processor.
|
||||||
@ -748,7 +752,24 @@ 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; }
|
ConfigOption* resolve_output_symbol(const std::string &opt_key) const {
|
||||||
|
ConfigOption *out = nullptr;
|
||||||
|
if (this->config_outputs)
|
||||||
|
out = this->config_outputs->optptr(opt_key, false);
|
||||||
|
if (out == nullptr && this->context_data != nullptr && this->context_data->global_config)
|
||||||
|
out = this->context_data->global_config->optptr(opt_key);
|
||||||
|
if (out == nullptr)
|
||||||
|
out = this->config_local.optptr(opt_key);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
void store_new_variable(const std::string &opt_key, ConfigOption *opt, bool global_variable) {
|
||||||
|
assert(opt != nullptr);
|
||||||
|
if (global_variable) {
|
||||||
|
assert(this->context_data != nullptr && this->context_data->global_config);
|
||||||
|
this->context_data->global_config->set_key_value(opt_key, opt);
|
||||||
|
} else
|
||||||
|
this->config_local.set_key_value(opt_key ,opt);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
static void legacy_variable_expansion(
|
static void legacy_variable_expansion(
|
||||||
@ -953,17 +974,105 @@ namespace client
|
|||||||
output.it_range = opt.it_range;
|
output.it_range = opt.it_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decoding a scalar variable symbol "opt", assigning it a value of "param".
|
template<typename Iterator>
|
||||||
|
struct NewOldVariable {
|
||||||
|
std::string name;
|
||||||
|
boost::iterator_range<Iterator> it_range;
|
||||||
|
ConfigOption *opt{ nullptr };
|
||||||
|
};
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
static void variable_assign(
|
static void new_old_variable(
|
||||||
const MyContext *ctx,
|
const MyContext *ctx,
|
||||||
OptWithPos<Iterator> &opt,
|
bool global_variable,
|
||||||
expr<Iterator> ¶m,
|
const boost::iterator_range<Iterator> &it_range,
|
||||||
// Not used, just clear it.
|
NewOldVariable<Iterator> &out)
|
||||||
std::string &out)
|
|
||||||
{
|
{
|
||||||
|
t_config_option_key key(std::string(it_range.begin(), it_range.end()));
|
||||||
|
if (const ConfigOption* opt = ctx->resolve_symbol(key); opt)
|
||||||
|
ctx->throw_exception("Symbol is already defined in read-only system dictionary", it_range);
|
||||||
|
if (ctx->config_outputs && ctx->config_outputs->optptr(key))
|
||||||
|
ctx->throw_exception("Symbol is already defined as system output variable", it_range);
|
||||||
|
|
||||||
|
bool has_global_dictionary = ctx->context_data != nullptr && ctx->context_data->global_config;
|
||||||
|
if (global_variable) {
|
||||||
|
if (! has_global_dictionary)
|
||||||
|
ctx->throw_exception("Global variables are not available in this context", it_range);
|
||||||
|
if (ctx->config_local.optptr(key))
|
||||||
|
ctx->throw_exception("Variable name already defined in local scope", it_range);
|
||||||
|
out.opt = ctx->context_data->global_config->optptr(key);
|
||||||
|
} else {
|
||||||
|
if (has_global_dictionary && ctx->context_data->global_config->optptr(key))
|
||||||
|
ctx->throw_exception("Variable name already defined in global scope", it_range);
|
||||||
|
out.opt = ctx->config_local.optptr(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.name = std::move(key);
|
||||||
|
out.it_range = it_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void new_scalar_variable(
|
||||||
|
const MyContext *ctx,
|
||||||
|
bool global_variable,
|
||||||
|
NewOldVariable<Iterator> &output_variable,
|
||||||
|
const expr<Iterator> ¶m)
|
||||||
|
{
|
||||||
|
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||||
|
if (! param.numeric_type())
|
||||||
|
param.throw_exception("Right side is not a numeric expression");
|
||||||
|
};
|
||||||
|
if (output_variable.opt) {
|
||||||
|
if (output_variable.opt->is_vector())
|
||||||
|
param.throw_exception("Cannot assign a scalar value to a vector variable.");
|
||||||
|
switch (output_variable.opt->type()) {
|
||||||
|
case coFloat:
|
||||||
|
check_numeric(param);
|
||||||
|
static_cast<ConfigOptionFloat*>(output_variable.opt)->value = param.as_d();
|
||||||
|
break;
|
||||||
|
case coInt:
|
||||||
|
check_numeric(param);
|
||||||
|
static_cast<ConfigOptionInt*>(output_variable.opt)->value = param.as_i();
|
||||||
|
break;
|
||||||
|
case coString:
|
||||||
|
static_cast<ConfigOptionString*>(output_variable.opt)->value = param.to_string();
|
||||||
|
break;
|
||||||
|
case coBool:
|
||||||
|
if (param.type() != expr<Iterator>::TYPE_BOOL)
|
||||||
|
param.throw_exception("Right side is not a boolean expression");
|
||||||
|
static_cast<ConfigOptionBool*>(output_variable.opt)->value = param.b();
|
||||||
|
break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (param.type()) {
|
||||||
|
case expr<Iterator>::TYPE_BOOL: output_variable.opt = new ConfigOptionBool(param.b()); break;
|
||||||
|
case expr<Iterator>::TYPE_INT: output_variable.opt = new ConfigOptionInt(param.i()); break;
|
||||||
|
case expr<Iterator>::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloat(param.d()); break;
|
||||||
|
case expr<Iterator>::TYPE_STRING: output_variable.opt = new ConfigOptionString(param.s()); break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void check_writable(const MyContext *ctx, OptWithPos<Iterator> &opt) {
|
||||||
if (! opt.writable)
|
if (! opt.writable)
|
||||||
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
ctx->throw_exception("Cannot modify a read-only variable", opt.it_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoding a scalar variable symbol "opt", assigning it a value of "param".
|
||||||
|
template <typename Iterator>
|
||||||
|
static void assign_scalar_variable(
|
||||||
|
const MyContext *ctx,
|
||||||
|
OptWithPos<Iterator> &opt,
|
||||||
|
expr<Iterator> ¶m)
|
||||||
|
{
|
||||||
|
check_writable(ctx, opt);
|
||||||
|
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||||
|
if (! param.numeric_type())
|
||||||
|
param.throw_exception("Right side is not a numeric expression");
|
||||||
|
};
|
||||||
if (opt.opt->is_vector()) {
|
if (opt.opt->is_vector()) {
|
||||||
if (! opt.has_index())
|
if (! opt.has_index())
|
||||||
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
|
ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range);
|
||||||
@ -974,21 +1083,18 @@ namespace client
|
|||||||
ctx->throw_exception("Index out of range", opt.it_range);
|
ctx->throw_exception("Index out of range", opt.it_range);
|
||||||
switch (opt.opt->type()) {
|
switch (opt.opt->type()) {
|
||||||
case coFloats:
|
case coFloats:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
|
static_cast<ConfigOptionFloats*>(vec)->values[opt.index] = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coInts:
|
case coInts:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
|
static_cast<ConfigOptionInts*>(vec)->values[opt.index] = param.as_i();
|
||||||
break;
|
break;
|
||||||
case coStrings:
|
case coStrings:
|
||||||
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
|
static_cast<ConfigOptionStrings*>(vec)->values[opt.index] = param.to_string();
|
||||||
break;
|
break;
|
||||||
case coPercents:
|
case coPercents:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
|
static_cast<ConfigOptionPercents*>(vec)->values[opt.index] = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coBools:
|
case coBools:
|
||||||
@ -1004,21 +1110,18 @@ namespace client
|
|||||||
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
|
ConfigOption *wropt = const_cast<ConfigOption*>(opt.opt);
|
||||||
switch (wropt->type()) {
|
switch (wropt->type()) {
|
||||||
case coFloat:
|
case coFloat:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
|
static_cast<ConfigOptionFloat*>(wropt)->value = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coInt:
|
case coInt:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
|
static_cast<ConfigOptionInt*>(wropt)->value = param.as_i();
|
||||||
break;
|
break;
|
||||||
case coString:
|
case coString:
|
||||||
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
|
static_cast<ConfigOptionString*>(wropt)->value = param.to_string();
|
||||||
break;
|
break;
|
||||||
case coPercent:
|
case coPercent:
|
||||||
if (param.type() != expr<Iterator>::TYPE_INT && param.type() != expr<Iterator>::TYPE_DOUBLE)
|
check_numeric(param);
|
||||||
ctx->throw_exception("Right side is not a numeric expression", param.it_range);
|
|
||||||
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
|
static_cast<ConfigOptionPercent*>(wropt)->value = param.as_d();
|
||||||
break;
|
break;
|
||||||
case coBool:
|
case coBool:
|
||||||
@ -1030,7 +1133,363 @@ namespace client
|
|||||||
ctx->throw_exception("Unsupported output scalar variable type", opt.it_range);
|
ctx->throw_exception("Unsupported output scalar variable type", opt.it_range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.clear();
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void new_vector_variable_array(
|
||||||
|
const MyContext *ctx,
|
||||||
|
bool global_variable,
|
||||||
|
NewOldVariable<Iterator> &output_variable,
|
||||||
|
const expr<Iterator> &expr_count,
|
||||||
|
const expr<Iterator> &expr_value)
|
||||||
|
{
|
||||||
|
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||||
|
if (! param.numeric_type())
|
||||||
|
param.throw_exception("Right side is not a numeric expression");
|
||||||
|
};
|
||||||
|
auto evaluate_count = [](const expr<Iterator> &expr_count) -> size_t {
|
||||||
|
if (expr_count.type() != expr<Iterator>::TYPE_INT)
|
||||||
|
expr_count.throw_exception("Expected number of elements to fill a vector with.");
|
||||||
|
int count = expr_count.i();
|
||||||
|
if (count < 0)
|
||||||
|
expr_count.throw_exception("Negative number of elements specified.");
|
||||||
|
return size_t(count);
|
||||||
|
};
|
||||||
|
if (output_variable.opt) {
|
||||||
|
if (output_variable.opt->is_scalar())
|
||||||
|
expr_value.throw_exception("Cannot assign a vector value to a scalar variable.");
|
||||||
|
size_t count = evaluate_count(expr_count);
|
||||||
|
switch (output_variable.opt->type()) {
|
||||||
|
case coFloats:
|
||||||
|
check_numeric(expr_value);
|
||||||
|
static_cast<ConfigOptionFloats*>(output_variable.opt)->values.assign(count, expr_value.as_d());
|
||||||
|
break;
|
||||||
|
case coInts:
|
||||||
|
check_numeric(expr_value);
|
||||||
|
static_cast<ConfigOptionInts*>(output_variable.opt)->values.assign(count, expr_value.as_i());
|
||||||
|
break;
|
||||||
|
case coStrings:
|
||||||
|
static_cast<ConfigOptionStrings*>(output_variable.opt)->values.assign(count, expr_value.to_string());
|
||||||
|
break;
|
||||||
|
case coBools:
|
||||||
|
if (expr_value.type() != expr<Iterator>::TYPE_BOOL)
|
||||||
|
expr_value.throw_exception("Right side is not a boolean expression");
|
||||||
|
static_cast<ConfigOptionBools*>(output_variable.opt)->values.assign(count, expr_value.b());
|
||||||
|
break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size_t count = evaluate_count(expr_count);
|
||||||
|
switch (expr_value.type()) {
|
||||||
|
case expr<Iterator>::TYPE_BOOL: output_variable.opt = new ConfigOptionBools(count, expr_value.b()); break;
|
||||||
|
case expr<Iterator>::TYPE_INT: output_variable.opt = new ConfigOptionInts(count, expr_value.i()); break;
|
||||||
|
case expr<Iterator>::TYPE_DOUBLE: output_variable.opt = new ConfigOptionFloats(count, expr_value.d()); break;
|
||||||
|
case expr<Iterator>::TYPE_STRING: output_variable.opt = new ConfigOptionStrings(count, expr_value.s()); break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void assign_vector_variable_array(
|
||||||
|
const MyContext *ctx,
|
||||||
|
OptWithPos<Iterator> &lhs,
|
||||||
|
const expr<Iterator> &expr_count,
|
||||||
|
const expr<Iterator> &expr_value)
|
||||||
|
{
|
||||||
|
check_writable(ctx, lhs);
|
||||||
|
auto check_numeric = [](const expr<Iterator> ¶m) {
|
||||||
|
if (! param.numeric_type())
|
||||||
|
param.throw_exception("Right side is not a numeric expression");
|
||||||
|
};
|
||||||
|
auto evaluate_count = [](const expr<Iterator> &expr_count) -> size_t {
|
||||||
|
if (expr_count.type() != expr<Iterator>::TYPE_INT)
|
||||||
|
expr_count.throw_exception("Expected number of elements to fill a vector with.");
|
||||||
|
int count = expr_count.i();
|
||||||
|
if (count < 0)
|
||||||
|
expr_count.throw_exception("Negative number of elements specified.");
|
||||||
|
return size_t(count);
|
||||||
|
};
|
||||||
|
if (lhs.opt->is_scalar())
|
||||||
|
expr_value.throw_exception("Cannot assign a vector value to a scalar variable.");
|
||||||
|
auto *opt = const_cast<ConfigOption*>(lhs.opt);
|
||||||
|
size_t count = evaluate_count(expr_count);
|
||||||
|
switch (lhs.opt->type()) {
|
||||||
|
case coFloats:
|
||||||
|
check_numeric(expr_value);
|
||||||
|
static_cast<ConfigOptionFloats*>(opt)->values.assign(count, expr_value.as_d());
|
||||||
|
break;
|
||||||
|
case coInts:
|
||||||
|
check_numeric(expr_value);
|
||||||
|
static_cast<ConfigOptionInts*>(opt)->values.assign(count, expr_value.as_i());
|
||||||
|
break;
|
||||||
|
case coStrings:
|
||||||
|
static_cast<ConfigOptionStrings*>(opt)->values.assign(count, expr_value.to_string());
|
||||||
|
break;
|
||||||
|
case coBools:
|
||||||
|
if (expr_value.type() != expr<Iterator>::TYPE_BOOL)
|
||||||
|
expr_value.throw_exception("Right side is not a boolean expression");
|
||||||
|
static_cast<ConfigOptionBools*>(opt)->values.assign(count, expr_value.b());
|
||||||
|
break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void new_vector_variable_initializer_list(
|
||||||
|
const MyContext *ctx,
|
||||||
|
bool global_variable,
|
||||||
|
NewOldVariable<Iterator> &output_variable,
|
||||||
|
const std::vector<expr<Iterator>> &il)
|
||||||
|
{
|
||||||
|
if (! output_variable.opt) {
|
||||||
|
// First guesstimate type of the output vector.
|
||||||
|
size_t num_bool = 0;
|
||||||
|
size_t num_int = 0;
|
||||||
|
size_t num_double = 0;
|
||||||
|
size_t num_string = 0;
|
||||||
|
for (auto &i : il)
|
||||||
|
switch (i.type()) {
|
||||||
|
case expr<Iterator>::TYPE_BOOL: ++ num_bool; break;
|
||||||
|
case expr<Iterator>::TYPE_INT: ++ num_int; break;
|
||||||
|
case expr<Iterator>::TYPE_DOUBLE: ++ num_double; break;
|
||||||
|
case expr<Iterator>::TYPE_STRING: ++ num_string; break;
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
if (num_string > 0)
|
||||||
|
// Convert everything to strings.
|
||||||
|
output_variable.opt = new ConfigOptionStrings();
|
||||||
|
else if (num_bool > 0) {
|
||||||
|
if (num_double + num_int > 0)
|
||||||
|
ctx->throw_exception("Right side is not valid: Mixing numeric and boolean types.", boost::iterator_range<Iterator>{ il.front().it_range.begin(), il.back().it_range.end() });
|
||||||
|
output_variable.opt = new ConfigOptionBools();
|
||||||
|
} else
|
||||||
|
// Output is numeric.
|
||||||
|
output_variable.opt = num_double == 0 ? static_cast<ConfigOption*>(new ConfigOptionInts()) : static_cast<ConfigOption*>(new ConfigOptionFloats());
|
||||||
|
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto check_numeric = [](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 (output_variable.opt->is_scalar())
|
||||||
|
ctx->throw_exception("Cannot assign a vector value to a scalar variable.", output_variable.it_range);
|
||||||
|
|
||||||
|
switch (output_variable.opt->type()) {
|
||||||
|
case coFloats:
|
||||||
|
{
|
||||||
|
check_numeric(il);
|
||||||
|
auto &out = static_cast<ConfigOptionFloats*>(output_variable.opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto &i : il)
|
||||||
|
out.emplace_back(i.as_d());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coInts:
|
||||||
|
{
|
||||||
|
check_numeric(il);
|
||||||
|
auto &out = static_cast<ConfigOptionInts*>(output_variable.opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto& i : il)
|
||||||
|
out.emplace_back(i.as_i());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coStrings:
|
||||||
|
{
|
||||||
|
auto &out = static_cast<ConfigOptionStrings*>(output_variable.opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto &i : il)
|
||||||
|
out.emplace_back(i.to_string());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coBools:
|
||||||
|
{
|
||||||
|
auto &out = static_cast<ConfigOptionBools*>(output_variable.opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto &i : il)
|
||||||
|
if (i.type() == expr<Iterator>::TYPE_BOOL)
|
||||||
|
out.emplace_back(i.b());
|
||||||
|
else
|
||||||
|
i.throw_exception("Right side is not a boolean expression");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void assign_vector_variable_initializer_list(
|
||||||
|
const MyContext *ctx,
|
||||||
|
OptWithPos<Iterator> &lhs,
|
||||||
|
const std::vector<expr<Iterator>> &il)
|
||||||
|
{
|
||||||
|
check_writable(ctx, lhs);
|
||||||
|
auto check_numeric = [](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:
|
||||||
|
{
|
||||||
|
check_numeric(il);
|
||||||
|
auto &out = static_cast<ConfigOptionFloats*>(opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto &i : il)
|
||||||
|
out.emplace_back(i.as_d());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coInts:
|
||||||
|
{
|
||||||
|
check_numeric(il);
|
||||||
|
auto &out = static_cast<ConfigOptionInts*>(opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto& i : il)
|
||||||
|
out.emplace_back(i.as_i());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coStrings:
|
||||||
|
{
|
||||||
|
auto &out = static_cast<ConfigOptionStrings*>(opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto &i : il)
|
||||||
|
out.emplace_back(i.to_string());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coBools:
|
||||||
|
{
|
||||||
|
auto &out = static_cast<ConfigOptionBools*>(opt)->values;
|
||||||
|
out.clear();
|
||||||
|
out.reserve(il.size());
|
||||||
|
for (auto &i : il)
|
||||||
|
if (i.type() == expr<Iterator>::TYPE_BOOL)
|
||||||
|
out.emplace_back(i.b());
|
||||||
|
else
|
||||||
|
i.throw_exception("Right side is not a boolean expression");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static bool new_vector_variable_copy(
|
||||||
|
const MyContext *ctx,
|
||||||
|
bool global_variable,
|
||||||
|
NewOldVariable<Iterator> &output_variable,
|
||||||
|
const OptWithPos<Iterator> &src_variable)
|
||||||
|
{
|
||||||
|
if (! is_vector_variable_reference(src_variable))
|
||||||
|
// Skip parsing this branch, bactrack.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (! output_variable.opt) {
|
||||||
|
if (one_of(src_variable.opt->type(), { coFloats, coInts, coStrings, coBools }))
|
||||||
|
output_variable.opt = src_variable.opt->clone();
|
||||||
|
else if (src_variable.opt->type() == coPercents)
|
||||||
|
output_variable.opt = new ConfigOptionFloats(static_cast<const ConfigOptionPercents*>(src_variable.opt)->values);
|
||||||
|
else
|
||||||
|
ctx->throw_exception("Duplicating this vector variable is not supported", src_variable.it_range);
|
||||||
|
const_cast<MyContext*>(ctx)->store_new_variable(output_variable.name, output_variable.opt, global_variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (output_variable.opt->type()) {
|
||||||
|
case coFloats:
|
||||||
|
if (output_variable.opt->type() != coFloats)
|
||||||
|
ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||||
|
static_cast<ConfigOptionFloats*>(output_variable.opt)->values = static_cast<const ConfigOptionFloats*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
case coInts:
|
||||||
|
if (output_variable.opt->type() != coInts)
|
||||||
|
ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||||
|
static_cast<ConfigOptionInts*>(output_variable.opt)->values = static_cast<const ConfigOptionInts*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
case coStrings:
|
||||||
|
if (output_variable.opt->type() != coStrings)
|
||||||
|
ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||||
|
static_cast<ConfigOptionStrings*>(output_variable.opt)->values = static_cast<const ConfigOptionStrings*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
case coBools:
|
||||||
|
if (output_variable.opt->type() != coBools)
|
||||||
|
ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", boost::iterator_range<Iterator>{ output_variable.it_range.begin(), src_variable.it_range.end() });
|
||||||
|
static_cast<ConfigOptionBools*>(output_variable.opt)->values = static_cast<const ConfigOptionBools*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
// Continue parsing.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 assign_vector_variable_copy(
|
||||||
|
const MyContext *ctx,
|
||||||
|
OptWithPos<Iterator> &lhs,
|
||||||
|
const OptWithPos<Iterator> &src_variable)
|
||||||
|
{
|
||||||
|
if (! is_vector_variable_reference(src_variable))
|
||||||
|
// Skip parsing this branch, bactrack.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
check_writable(ctx, lhs);
|
||||||
|
|
||||||
|
auto *opt = const_cast<ConfigOption*>(lhs.opt);
|
||||||
|
switch (lhs.opt->type()) {
|
||||||
|
case coFloats:
|
||||||
|
if (lhs.opt->type() != coFloats)
|
||||||
|
ctx->throw_exception("Left hand side is a float vector, while the right hand side is not.", lhs.it_range);
|
||||||
|
static_cast<ConfigOptionFloats*>(opt)->values = static_cast<const ConfigOptionFloats*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
case coInts:
|
||||||
|
if (lhs.opt->type() != coInts)
|
||||||
|
ctx->throw_exception("Left hand side is an int vector, while the right hand side is not.", lhs.it_range);
|
||||||
|
static_cast<ConfigOptionInts*>(opt)->values = static_cast<const ConfigOptionInts*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
case coStrings:
|
||||||
|
if (lhs.opt->type() != coStrings)
|
||||||
|
ctx->throw_exception("Left hand side is a string vector, while the right hand side is not.", lhs.it_range);
|
||||||
|
static_cast<ConfigOptionStrings*>(opt)->values = static_cast<const ConfigOptionStrings*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
case coBools:
|
||||||
|
if (lhs.opt->type() != coBools)
|
||||||
|
ctx->throw_exception("Left hand side is a bool vector, while the right hand side is not.", lhs.it_range);
|
||||||
|
static_cast<ConfigOptionBools*>(opt)->values = static_cast<const ConfigOptionBools*>(src_variable.opt)->values;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue parsing.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
static void new_vector_variable_initializer_list_append(std::vector<expr<Iterator>> &list, expr<Iterator> &expr)
|
||||||
|
{
|
||||||
|
list.emplace_back(std::move(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the expression returns an integer, which may be used
|
// Verify that the expression returns an integer, which may be used
|
||||||
@ -1109,8 +1568,75 @@ namespace client
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
struct InterpolateTableContext {
|
||||||
|
template<typename Iterator>
|
||||||
|
struct Item {
|
||||||
|
double x;
|
||||||
|
boost::iterator_range<Iterator> it_range_x;
|
||||||
|
double y;
|
||||||
|
};
|
||||||
|
std::vector<Item<Iterator>> table;
|
||||||
|
|
||||||
|
static void init(const expr<Iterator> &x) {
|
||||||
|
if (!x.numeric_type())
|
||||||
|
x.throw_exception("Interpolation value must be a number.");
|
||||||
|
}
|
||||||
|
static void add_pair(const expr<Iterator> &x, const expr<Iterator> &y, InterpolateTableContext &table) {
|
||||||
|
if (! x.numeric_type())
|
||||||
|
x.throw_exception("X value of a table point must be a number.");
|
||||||
|
if (! y.numeric_type())
|
||||||
|
y.throw_exception("Y value of a table point must be a number.");
|
||||||
|
table.table.push_back({ x.as_d(), x.it_range, y.as_d() });
|
||||||
|
}
|
||||||
|
static void evaluate(const expr<Iterator> &expr_x, const InterpolateTableContext &table, expr<Iterator> &out) {
|
||||||
|
// Check whether the table X values are sorted.
|
||||||
|
double x = expr_x.as_d();
|
||||||
|
bool evaluated = false;
|
||||||
|
for (size_t i = 1; i < table.table.size(); ++i) {
|
||||||
|
double x0 = table.table[i - 1].x;
|
||||||
|
double x1 = table.table[i].x;
|
||||||
|
if (x0 > x1)
|
||||||
|
boost::throw_exception(qi::expectation_failure<Iterator>(
|
||||||
|
table.table[i - 1].it_range_x.begin(), table.table[i].it_range_x.end(), spirit::info("X coordinates of the table must be increasing")));
|
||||||
|
if (! evaluated && x >= x0 && x <= x1) {
|
||||||
|
double y0 = table.table[i - 1].y;
|
||||||
|
double y1 = table.table[i].y;
|
||||||
|
if (x == x0)
|
||||||
|
out.set_d(y0);
|
||||||
|
else if (x == x1)
|
||||||
|
out.set_d(y1);
|
||||||
|
else if (is_approx(x0, x1))
|
||||||
|
out.set_d(0.5 * (y0 + y1));
|
||||||
|
else
|
||||||
|
out.set_d(Slic3r::lerp(y0, y1, (x - x0) / (x1 - x0)));
|
||||||
|
evaluated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! evaluated) {
|
||||||
|
// Clamp x into the table range with EPSILON.
|
||||||
|
if (x > table.table.front().x - EPSILON)
|
||||||
|
out.set_d(table.table.front().y);
|
||||||
|
else if (x < table.table.back().x + EPSILON)
|
||||||
|
out.set_d(table.table.back().y);
|
||||||
|
else
|
||||||
|
// The value is really outside the table range.
|
||||||
|
expr_x.throw_exception("Interpolation value is outside the table range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
std::ostream& operator<<(std::ostream &os, const InterpolateTableContext<Iterator> &table_context)
|
||||||
|
{
|
||||||
|
for (const auto &item : table_context.table)
|
||||||
|
os << "(" << item.x << "," << item.y << ")";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
// Table to translate symbol tag to a human readable error message.
|
// Table to translate symbol tag to a human readable error message.
|
||||||
std::map<std::string, std::string> MyContext::tag_to_error_message = {
|
std::map<std::string, std::string> MyContext::tag_to_error_message = {
|
||||||
|
{ "array", "Unknown syntax error" },
|
||||||
{ "eoi", "Unknown syntax error" },
|
{ "eoi", "Unknown syntax error" },
|
||||||
{ "start", "Unknown syntax error" },
|
{ "start", "Unknown syntax error" },
|
||||||
{ "text", "Invalid text." },
|
{ "text", "Invalid text." },
|
||||||
@ -1271,7 +1797,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('{') >> macro(_r1)[_val+=_1] > *(+lit(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}')
|
||||||
| (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')
|
| (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')
|
||||||
);
|
);
|
||||||
text_block.name("text_block");
|
text_block.name("text_block");
|
||||||
@ -1287,6 +1813,7 @@ namespace client
|
|||||||
(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])
|
||||||
| (assignment_statement(_r1) [_val = _1])
|
| (assignment_statement(_r1) [_val = _1])
|
||||||
|
| (new_variable_statement(_r1) [_val = _1])
|
||||||
| (additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ])
|
| (additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ])
|
||||||
;
|
;
|
||||||
macro.name("macro");
|
macro.name("macro");
|
||||||
@ -1381,8 +1908,39 @@ namespace client
|
|||||||
multiplicative_expression.name("multiplicative_expression");
|
multiplicative_expression.name("multiplicative_expression");
|
||||||
|
|
||||||
assignment_statement =
|
assignment_statement =
|
||||||
(variable_reference(_r1) >> '=' > additive_expression(_r1))
|
variable_reference(_r1)[_a = _1] >> '=' >
|
||||||
[px::bind(&MyContext::variable_assign<Iterator>, _r1, _1, _2, _val)];
|
( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer.
|
||||||
|
(lit('(') > new_variable_initializer_list(_r1) > ')')
|
||||||
|
[px::bind(&MyContext::assign_vector_variable_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::assign_vector_variable_copy<Iterator>, _r1, _a, _1)]
|
||||||
|
// Would NOT consume '(' conditional_expression ')' because such value was consumed with the expression above.
|
||||||
|
| conditional_expression(_r1)
|
||||||
|
[px::bind(&MyContext::assign_scalar_variable<Iterator>, _r1, _a, _1)]
|
||||||
|
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
|
||||||
|
[px::bind(&MyContext::assign_vector_variable_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) > ')')
|
||||||
|
[px::bind(&MyContext::new_vector_variable_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::new_vector_variable_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::new_scalar_variable<Iterator>, _r1, _a, _b, _1)]
|
||||||
|
| (kw["array"] > "(" > additive_expression(_r1) > "," > conditional_expression(_r1) > ")")
|
||||||
|
[px::bind(&MyContext::new_vector_variable_array<Iterator>, _r1, _a, _b, _1, _2)]
|
||||||
|
);
|
||||||
|
new_variable_initializer_list =
|
||||||
|
conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append<Iterator>, _val, _1)] >>
|
||||||
|
*(lit(',') > conditional_expression(_r1)[px::bind(&MyContext::new_vector_variable_initializer_list_append<Iterator>, _val, _1)]);
|
||||||
|
|
||||||
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)
|
||||||
@ -1428,6 +1986,7 @@ namespace client
|
|||||||
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _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"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ]
|
||||||
| (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ]
|
| (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) ]
|
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
|
||||||
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
|
||||||
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
|
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
|
||||||
@ -1440,16 +1999,26 @@ namespace client
|
|||||||
one_of.name("one_of");
|
one_of.name("one_of");
|
||||||
one_of_list =
|
one_of_list =
|
||||||
eps[px::bind(&expr<Iterator>::one_of_test_init, _val)] >
|
eps[px::bind(&expr<Iterator>::one_of_test_init, _val)] >
|
||||||
( ',' > *(
|
( ( ',' > *(
|
||||||
(
|
(
|
||||||
unary_expression(_r1)[px::bind(&expr<Iterator>::template one_of_test<false>, _r2, _1, _val)]
|
unary_expression(_r1)[px::bind(&expr<Iterator>::template one_of_test<false>, _r2, _1, _val)]
|
||||||
| (lit('~') > unary_expression(_r1))[px::bind(&expr<Iterator>::template one_of_test<true>, _r2, _1, _val)]
|
| (lit('~') > unary_expression(_r1))[px::bind(&expr<Iterator>::template one_of_test<true>, _r2, _1, _val)]
|
||||||
| regular_expression[px::bind(&expr<Iterator>::one_of_test_regex, _r2, _1, _val)]
|
| regular_expression[px::bind(&expr<Iterator>::one_of_test_regex, _r2, _1, _val)]
|
||||||
) >> -lit(','))
|
) >> -lit(','))
|
||||||
| eps
|
)
|
||||||
|
| eps
|
||||||
);
|
);
|
||||||
one_of_list.name("one_of_list");
|
one_of_list.name("one_of_list");
|
||||||
|
|
||||||
|
interpolate_table = (unary_expression(_r1)[_a = _1] > ',' > interpolate_table_list(_r1, _a))
|
||||||
|
[px::bind(&InterpolateTableContext<Iterator>::evaluate, _a, _2, _val)];
|
||||||
|
interpolate_table.name("interpolate_table");
|
||||||
|
interpolate_table_list =
|
||||||
|
eps[px::bind(&InterpolateTableContext<Iterator>::init, _r2)] >
|
||||||
|
( *(( lit('(') > unary_expression(_r1) > ',' > unary_expression(_r1) > ')' )
|
||||||
|
[px::bind(&InterpolateTableContext<Iterator>::add_pair, _1, _2, _val)] >> -lit(',')) );
|
||||||
|
interpolate_table.name("interpolate_table_list");
|
||||||
|
|
||||||
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
|
optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
|
||||||
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
lit(')') [ px::bind(&FactorActions::noexpr, _val) ]
|
||||||
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
| (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ]
|
||||||
@ -1469,23 +2038,27 @@ namespace client
|
|||||||
variable_reference.name("variable reference");
|
variable_reference.name("variable reference");
|
||||||
|
|
||||||
variable = identifier[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
|
variable = identifier[ px::bind(&MyContext::resolve_variable<Iterator>, _r1, _1, _val) ];
|
||||||
variable.name("variable reference");
|
variable.name("variable name");
|
||||||
|
|
||||||
regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']];
|
regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']];
|
||||||
regular_expression.name("regular_expression");
|
regular_expression.name("regular_expression");
|
||||||
|
|
||||||
keywords.add
|
keywords.add
|
||||||
("and")
|
("and")
|
||||||
|
("array")
|
||||||
("digits")
|
("digits")
|
||||||
("zdigits")
|
("zdigits")
|
||||||
("if")
|
("if")
|
||||||
("int")
|
("int")
|
||||||
("is_nil")
|
("is_nil")
|
||||||
|
("local")
|
||||||
//("inf")
|
//("inf")
|
||||||
("else")
|
("else")
|
||||||
("elsif")
|
("elsif")
|
||||||
("endif")
|
("endif")
|
||||||
("false")
|
("false")
|
||||||
|
("global")
|
||||||
|
("interpolate_table")
|
||||||
("min")
|
("min")
|
||||||
("max")
|
("max")
|
||||||
("random")
|
("random")
|
||||||
@ -1501,9 +2074,12 @@ namespace client
|
|||||||
debug(text_block);
|
debug(text_block);
|
||||||
debug(macro);
|
debug(macro);
|
||||||
debug(if_else_output);
|
debug(if_else_output);
|
||||||
|
debug(interpolate_table);
|
||||||
// debug(switch_output);
|
// debug(switch_output);
|
||||||
debug(legacy_variable_expansion);
|
debug(legacy_variable_expansion);
|
||||||
debug(identifier);
|
debug(identifier);
|
||||||
|
debug(interpolate_table);
|
||||||
|
debug(interpolate_table_list);
|
||||||
debug(conditional_expression);
|
debug(conditional_expression);
|
||||||
debug(logical_or_expression);
|
debug(logical_or_expression);
|
||||||
debug(logical_and_expression);
|
debug(logical_and_expression);
|
||||||
@ -1569,11 +2145,17 @@ namespace client
|
|||||||
// Evaluating "one of" list of patterns.
|
// Evaluating "one of" list of patterns.
|
||||||
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> one_of;
|
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> one_of;
|
||||||
qi::rule<Iterator, expr<Iterator>(const MyContext*, const expr<Iterator> ¶m), spirit_encoding::space_type> one_of_list;
|
qi::rule<Iterator, expr<Iterator>(const MyContext*, const expr<Iterator> ¶m), spirit_encoding::space_type> one_of_list;
|
||||||
|
// Evaluating the "interpolate_table" expression.
|
||||||
|
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<expr<Iterator>>, spirit_encoding::space_type> interpolate_table;
|
||||||
|
qi::rule<Iterator, InterpolateTableContext<Iterator>(const MyContext*, const expr<Iterator> ¶m), spirit_encoding::space_type> interpolate_table_list;
|
||||||
|
|
||||||
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<OptWithPos<Iterator>>, 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;
|
// 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::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
|
||||||
qi::symbols<char> keywords;
|
qi::symbols<char> keywords;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,10 @@ public:
|
|||||||
// In the future, the context may hold variables created and modified by the PlaceholderParser
|
// In the future, the context may hold variables created and modified by the PlaceholderParser
|
||||||
// and shared between the PlaceholderParser::process() invocations.
|
// and shared between the PlaceholderParser::process() invocations.
|
||||||
struct ContextData {
|
struct ContextData {
|
||||||
std::mt19937 rng;
|
std::mt19937 rng;
|
||||||
|
// If defined, then this dictionary is used by the scripts to define user variables and persist them
|
||||||
|
// between PlaceholderParser evaluations.
|
||||||
|
std::unique_ptr<DynamicConfig> global_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
PlaceholderParser(const DynamicConfig *external_config = nullptr);
|
PlaceholderParser(const DynamicConfig *external_config = nullptr);
|
||||||
|
@ -39,6 +39,10 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
|||||||
SECTION("nullable is not null") { REQUIRE(parser.process("{is_nil(filament_retract_length[0])}") == "false"); }
|
SECTION("nullable is not null") { REQUIRE(parser.process("{is_nil(filament_retract_length[0])}") == "false"); }
|
||||||
SECTION("nullable is null") { REQUIRE(parser.process("{is_nil(filament_retract_length[1])}") == "true"); }
|
SECTION("nullable is null") { REQUIRE(parser.process("{is_nil(filament_retract_length[1])}") == "true"); }
|
||||||
SECTION("nullable is not null 2") { REQUIRE(parser.process("{is_nil(filament_retract_length[2])}") == "false"); }
|
SECTION("nullable is not null 2") { REQUIRE(parser.process("{is_nil(filament_retract_length[2])}") == "false"); }
|
||||||
|
SECTION("multiple expressions") { REQUIRE(parser.process("{temperature[foo];temperature[foo]}") == "357357"); }
|
||||||
|
SECTION("multiple expressions with semicolons") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];}") == "357357"); }
|
||||||
|
SECTION("multiple expressions with semicolons 2") { REQUIRE(parser.process("{temperature[foo];;temperature[foo];}") == "357357"); }
|
||||||
|
SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); }
|
||||||
|
|
||||||
// Test the math expressions.
|
// Test the math expressions.
|
||||||
SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); }
|
SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); }
|
||||||
@ -71,6 +75,9 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
|||||||
SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); }
|
SECTION("math: zdigits(5., 15, 8)") { REQUIRE(parser.process("{zdigits(5, 15, 8)}") == "000005.00000000"); }
|
||||||
SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); }
|
SECTION("math: digits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{digits(13.84375892476, 15, 8)}") == " 13.84375892"); }
|
||||||
SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); }
|
SECTION("math: zdigits(13.84375892476, 15, 8)") { REQUIRE(parser.process("{zdigits(13.84375892476, 15, 8)}") == "000013.84375892"); }
|
||||||
|
SECTION("math: interpolate_table(13.84375892476, (0, 0), (20, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13.84375892476, (0, 0), (20, 20))}")) == Approx(13.84375892476)); }
|
||||||
|
SECTION("math: interpolate_table(13, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13, (0, 0), (20, 20), (30, 20))}")) == Approx(13.)); }
|
||||||
|
SECTION("math: interpolate_table(25, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(25, (0, 0), (20, 20), (30, 20))}")) == Approx(20.)); }
|
||||||
|
|
||||||
// Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions.
|
// Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions.
|
||||||
// first_layer_extrusion_width ratio_over first_layer_heigth.
|
// first_layer_extrusion_width ratio_over first_layer_heigth.
|
||||||
@ -143,3 +150,76 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
|||||||
REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.));
|
REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCENARIO("Placeholder parser variables", "[PlaceholderParser]") {
|
||||||
|
PlaceholderParser parser;
|
||||||
|
auto config = DynamicPrintConfig::full_print_config();
|
||||||
|
|
||||||
|
config.set_deserialize_strict({
|
||||||
|
{ "filament_notes", "testnotes" },
|
||||||
|
{ "enable_dynamic_fan_speeds", "1" },
|
||||||
|
{ "nozzle_diameter", "0.6;0.6;0.6;0.6" },
|
||||||
|
{ "temperature", "357;359;363;378" }
|
||||||
|
});
|
||||||
|
|
||||||
|
PlaceholderParser::ContextData context_with_global_dict;
|
||||||
|
context_with_global_dict.global_config = std::make_unique<DynamicConfig>();
|
||||||
|
|
||||||
|
SECTION("create an int local variable") { REQUIRE(parser.process("{local myint = 33+2}{myint}", 0, nullptr, nullptr, nullptr) == "35"); }
|
||||||
|
SECTION("create a string local variable") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, nullptr) == "mineonlymine"); }
|
||||||
|
SECTION("create a bool local variable") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, nullptr) == "true"); }
|
||||||
|
SECTION("create an int global variable") { REQUIRE(parser.process("{global myint = 33+2}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "35"); }
|
||||||
|
SECTION("create a string global variable") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
|
||||||
|
SECTION("create a bool global variable") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
|
||||||
|
|
||||||
|
SECTION("create an int local variable and overwrite it") { REQUIRE(parser.process("{local myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); }
|
||||||
|
SECTION("create a string local variable and overwrite it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); }
|
||||||
|
SECTION("create a bool local variable and overwrite it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); }
|
||||||
|
SECTION("create an int global variable and overwrite it") { REQUIRE(parser.process("{global myint = 33+2}{myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); }
|
||||||
|
SECTION("create a string global variable and overwrite it") { REQUIRE(parser.process("{global mystr = \"mine\" + \"only\" + \"mine\"}{mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, &context_with_global_dict) == "yours"); }
|
||||||
|
SECTION("create a bool global variable and overwrite it") { REQUIRE(parser.process("{global mybool = 1 + 1 == 2}{mybool = false}{mybool}", 0, nullptr, nullptr, &context_with_global_dict) == "false"); }
|
||||||
|
|
||||||
|
SECTION("create an int local variable and redefine it") { REQUIRE(parser.process("{local myint = 33+2}{local myint = 12}{myint}", 0, nullptr, nullptr, nullptr) == "12"); }
|
||||||
|
SECTION("create a string local variable and redefine it") { REQUIRE(parser.process("{local mystr = \"mine\" + \"only\" + \"mine\"}{local mystr = \"yours\"}{mystr}", 0, nullptr, nullptr, nullptr) == "yours"); }
|
||||||
|
SECTION("create a bool local variable and redefine it") { REQUIRE(parser.process("{local mybool = 1 + 1 == 2}{local mybool = false}{mybool}", 0, nullptr, nullptr, nullptr) == "false"); }
|
||||||
|
SECTION("create an int global variable and redefine it") { REQUIRE(parser.process("{global myint = 33+2}{global myint = 12}{myint}", 0, nullptr, nullptr, &context_with_global_dict) == "12"); }
|
||||||
|
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 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"); }
|
||||||
|
SECTION("create a bools local variable with initializer list") { REQUIRE(parser.process("{local mybool = (3*3 == 8, 1 + 1 == 2)}{mybool[1]}", 0, nullptr, nullptr, nullptr) == "true"); }
|
||||||
|
SECTION("create an ints global variable with initializer list") { REQUIRE(parser.process("{global myint = (2*3, 4*6, 5*5)}{myint[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "24"); }
|
||||||
|
SECTION("create a strings global variable with initializer list") { REQUIRE(parser.process("{global mystr = (2*3, \"mine\" + \"only\" + \"mine\", 8)}{mystr[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "mineonlymine"); }
|
||||||
|
SECTION("create a bools global variable with initializer list") { REQUIRE(parser.process("{global mybool = (2*3 == 8, 1 + 1 == 2, 5*5 != 33)}{mybool[1]}", 0, nullptr, nullptr, &context_with_global_dict) == "true"); }
|
||||||
|
|
||||||
|
SECTION("create an ints local variable by a copy") { REQUIRE(parser.process("{local myint = temperature}{myint[0]}", 0, &config, nullptr, nullptr) == "357"); }
|
||||||
|
SECTION("create a strings local variable by a copy") { REQUIRE(parser.process("{local mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, nullptr) == "testnotes"); }
|
||||||
|
SECTION("create a bools local variable by a copy") { REQUIRE(parser.process("{local mybool = enable_dynamic_fan_speeds}{mybool[0]}", 0, &config, nullptr, nullptr) == "true"); }
|
||||||
|
SECTION("create an ints global variable by a copy") { REQUIRE(parser.process("{global myint = temperature}{myint[0]}", 0, &config, nullptr, &context_with_global_dict) == "357"); }
|
||||||
|
SECTION("create a strings global variable by a copy") { REQUIRE(parser.process("{global mystr = filament_notes}{mystr[0]}", 0, &config, nullptr, &context_with_global_dict) == "testnotes"); }
|
||||||
|
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 = (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 = (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 = (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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user