From f1f60cd4680ac6fc55a094a4ab6dc5066e59f215 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 22 Dec 2021 18:01:51 +0100 Subject: Custom macro variables: two new keywords: 'exists' and 'default' * 'default(var_name,value)' check if the variable exist and affect it the value if not. The variable can be boolean 'true' 'false', int '0' '12', double '1.0' '42.24' or string '"a string"'. * 'exists(var_name)' return true if present. Note that it create a dummy boolean variable. Note that if you're using the variable in a formula, it may crash even if guarded by a {if} as everything is interpreted (no lazy check). So be careful to add a 'default' so it get the right type and can the formula be interpreted (unless you just print it). Using 'default' doesn't change the return value of 'exists'. --- src/libslic3r/Config.hpp | 1 + src/libslic3r/PlaceholderParser.cpp | 77 +++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 9c172a493..e2506060e 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -305,6 +305,7 @@ public: enum FlagsConfigOption : uint32_t { FCO_PHONY = 1, FCO_EXTRUDER_ARRAY = 1 << 1, + FCO_PLACEHOLDER_TEMP = 1 << 2, }; ConfigOption() : flags(uint32_t(0)) {} diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 0a4948fab..26f77b1ca 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -652,6 +652,8 @@ namespace client bool just_boolean_expression = false; std::string error_message; + static std::map> checked_vars; + // Table to translate symbol tag to a human readable error message. static std::map tag_to_error_message; @@ -666,6 +668,11 @@ namespace client opt = config->option(opt_key); if (opt == nullptr && external_config != nullptr) opt = external_config->option(opt_key); + if (opt == nullptr) { + auto it = MyContext::checked_vars.find(opt_key); + if (it != MyContext::checked_vars.end()) + opt = it->second.get(); + } return opt; } @@ -825,12 +832,52 @@ namespace client boost::iterator_range &opt_key, OptWithPos &output) { - const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); + std::string str_key = std::string(opt_key.begin(), opt_key.end()); + const ConfigOption *opt = ctx->resolve_symbol(str_key); if (opt == nullptr) ctx->throw_exception("Not a variable name", opt_key); output.opt = opt; output.it_range = opt_key; } + + // function to check if a var exist & add a dummy var if not + template + static void check_variable( + const MyContext* ctx, + boost::iterator_range& opt_key, + Iterator& end_pos, + expr& out, + ConfigOption* default_val = nullptr) + { + t_config_option_key key = std::string(opt_key.begin(), opt_key.end()); + const ConfigOption* opt = nullptr; + if (ctx->config_override != nullptr) + opt = ctx->config_override->option(key); + if (opt == nullptr) + opt = ctx->config->option(key); + if (opt == nullptr && ctx->external_config != nullptr) + opt = ctx->external_config->option(key); + if (opt == nullptr) { + std::unique_ptr ppt; + if(default_val == nullptr) + ppt = std::unique_ptr(new ConfigOptionBool(false)); + else + ppt = std::unique_ptr(default_val); + // set flag to say "it's a var that isn't here, please ignore it" + ppt->flags |= ConfigOption::FCO_PLACEHOLDER_TEMP; + if (MyContext::checked_vars.find(key) != MyContext::checked_vars.end()) { + if (default_val != nullptr) { + // erase previous value + MyContext::checked_vars[key] = std::move(ppt); + } + } else { + // put the var + MyContext::checked_vars.emplace(std::move(key), std::move(ppt)); + } + } + //return + out = expr(opt != nullptr, out.it_range.begin(), end_pos); + } template static void scalar_variable_reference( @@ -944,8 +991,11 @@ namespace client Iterator it_end, expr &output) { - if (opt.opt->is_scalar()) + if (opt.opt->is_scalar()) { + if (0 != (opt.opt->flags & ConfigOption::FCO_PLACEHOLDER_TEMP)) // fake var, from checked_vars + return scalar_variable_reference(ctx, opt, output); ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); + } const ConfigOptionVectorBase *vec = static_cast(opt.opt); if (vec->empty()) ctx->throw_exception("Indexing an empty vector variable", opt.it_range); @@ -1064,6 +1114,7 @@ namespace client { "variable_reference", "Expecting a variable reference."}, { "regular_expression", "Expecting a regular expression."} }; + std::map> MyContext::checked_vars = {}; // For debugging the boost::spirit parsers. Print out the string enclosed in it_range. template @@ -1324,6 +1375,15 @@ namespace client { out = value.unary_not(out.it_range.begin()); } static void to_int(expr &value, expr &out) { out = value.unary_integer(out.it_range.begin()); } + //function for default keyword + static void default_bool_(bool &value, const MyContext* ctx, boost::iterator_range& opt_key, Iterator& end_pos, expr& out) + { MyContext::check_variable(ctx, opt_key, end_pos, out, new ConfigOptionBool(value)); } + static void default_int_(int &value, const MyContext* ctx, boost::iterator_range& opt_key, Iterator& end_pos, expr& out) + { MyContext::check_variable(ctx, opt_key, end_pos, out, new ConfigOptionInt(value)); } + static void default_double_(double &value, const MyContext* ctx, boost::iterator_range& opt_key, Iterator& end_pos, expr& out) + { MyContext::check_variable(ctx, opt_key, end_pos, out, new ConfigOptionFloat(value)); } + static void default_string_(boost::iterator_range& it_range, const MyContext* ctx, boost::iterator_range& opt_key, Iterator& end_pos, expr& out) + { MyContext::check_variable(ctx, opt_key, end_pos, out, new ConfigOptionString(std::string(it_range.begin() + 1, it_range.end() - 1))); } }; unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( scalar_variable_reference(_r1) [ _val = _1 ] @@ -1338,6 +1398,15 @@ namespace client | (kw["random"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&MyContext::random, _r1, _val, _2) ] | (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] + | (kw["exists"] > '(' > identifier > ')' > iter_pos) [ px::bind(&MyContext::check_variable, _r1, _1, _2, _val, nullptr) ] + | (kw["default"] > '(' > identifier > ',' > strict_double > ')' > iter_pos) + [px::bind(&FactorActions::default_double_, _2, _r1, _1, _3, _val)] + | (kw["default"] > '(' > identifier > ',' > int_ > ')' > iter_pos) + [px::bind(&FactorActions::default_int_, _2, _r1, _1, _3, _val)] + | (kw["default"] > '(' > identifier > ',' > kw[bool_] > ')' > iter_pos) + [ px::bind(&FactorActions::default_bool_, _2, _r1, _1, _3, _val) ] + | (kw["default"] > '(' > identifier > ',' > raw[lexeme['"' > *((utf8char - char_('\\') - char_('"')) | ('\\' > char_)) > '"']] > ')' > iter_pos) + [px::bind(&FactorActions::default_string_, _2, _r1, _1, _3, _val)] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1376,7 +1445,9 @@ namespace client ("random") ("not") ("or") - ("true"); + ("true") + ("exists") + ("default"); if (0) { debug(start); -- cgit v1.2.3