diff options
Diffstat (limited to 'src/libslic3r/PlaceholderParser.cpp')
-rw-r--r-- | src/libslic3r/PlaceholderParser.cpp | 51 |
1 files changed, 44 insertions, 7 deletions
diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 527d82b4c..6f2b7f3ea 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,4 +1,5 @@ #include "PlaceholderParser.hpp" +#include "Exception.hpp" #include "Flow.hpp" #include <cstring> #include <ctime> @@ -24,6 +25,7 @@ #endif #include <boost/algorithm/string.hpp> +#include <boost/nowide/convert.hpp> // Spirit v2.5 allows you to suppress automatic generation // of predefined terminals to speed up complation. With @@ -494,6 +496,12 @@ namespace client static void leq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '>', true ); } static void geq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', true ); } + static void throw_if_not_numeric(const expr ¶m) + { + const char *err_msg = "Not a numeric type."; + param.throw_if_not_numeric(err_msg); + } + enum Function2ParamsType { FUNCTION_MIN, FUNCTION_MAX, @@ -501,9 +509,8 @@ namespace client // Store the result into param1. static void function_2params(expr ¶m1, expr ¶m2, Function2ParamsType fun) { - const char *err_msg = "Not a numeric type."; - param1.throw_if_not_numeric(err_msg); - param2.throw_if_not_numeric(err_msg); + throw_if_not_numeric(param1); + throw_if_not_numeric(param2); if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) { double d = 0.; switch (fun) { @@ -528,6 +535,20 @@ namespace client static void min(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MIN); } static void max(expr ¶m1, expr ¶m2) { function_2params(param1, param2, FUNCTION_MAX); } + // Store the result into param1. + static void random(expr ¶m1, expr ¶m2, std::mt19937 &rng) + { + throw_if_not_numeric(param1); + throw_if_not_numeric(param2); + if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) { + param1.data.d = std::uniform_real_distribution<>(param1.as_d(), param2.as_d())(rng); + param1.type = TYPE_DOUBLE; + } else { + param1.data.i = std::uniform_int_distribution<>(param1.as_i(), param2.as_i())(rng); + param1.type = TYPE_INT; + } + } + static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op) { const std::string *subject = nullptr; @@ -622,6 +643,7 @@ namespace client const DynamicConfig *config = nullptr; const DynamicConfig *config_override = nullptr; size_t current_extruder_id = 0; + PlaceholderParser::ContextData *context_data = nullptr; // 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. bool just_boolean_expression = false; @@ -823,6 +845,15 @@ namespace client } template <typename Iterator> + static void random(const MyContext *ctx, expr<Iterator> ¶m1, expr<Iterator> ¶m2) + { + if (ctx->context_data == nullptr) + ctx->throw_exception("Random number generator not available in this context.", + boost::iterator_range<Iterator>(param1.it_range.begin(), param2.it_range.end())); + expr<Iterator>::random(param1, param2, ctx->context_data->rng); + } + + template <typename Iterator> static void throw_exception(const std::string &msg, const boost::iterator_range<Iterator> &it_range) { // An asterix is added to the start of the string to differentiate the boost::spirit::info::tag content @@ -869,7 +900,9 @@ namespace client } } msg += '\n'; - msg += error_line; + // This hack removes all non-UTF8 characters from the source line, so that the upstream wxWidgets conversions + // from UTF8 to UTF16 don't bail out. + msg += boost::nowide::narrow(boost::nowide::widen(error_line)); msg += '\n'; for (size_t i = 0; i < error_pos; ++ i) msg += ' '; @@ -1172,6 +1205,8 @@ namespace client [ px::bind(&expr<Iterator>::min, _val, _2) ] | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') [ px::bind(&expr<Iterator>::max, _val, _2) ] + | (kw["random"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')') + [ px::bind(&MyContext::random<Iterator>, _r1, _val, _2) ] | (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] @@ -1208,6 +1243,7 @@ namespace client ("false") ("min") ("max") + ("random") ("not") ("or") ("true"); @@ -1303,23 +1339,24 @@ static std::string process_macro(const std::string &templ, client::MyContext &co if (!context.error_message.empty()) { if (context.error_message.back() != '\n' && context.error_message.back() != '\r') context.error_message += '\n'; - throw std::runtime_error(context.error_message); + throw Slic3r::PlaceholderParserError(context.error_message); } return output; } -std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const +std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context_data) const { client::MyContext context; context.external_config = this->external_config(); context.config = &this->config(); context.config_override = config_override; context.current_extruder_id = current_extruder_id; + context.context_data = context_data; return process_macro(templ, context); } // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax. -// Throws std::runtime_error on syntax or runtime error. +// Throws Slic3r::RuntimeError on syntax or runtime error. bool PlaceholderParser::evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override) { client::MyContext context; |