Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Slic3r.pm4
-rw-r--r--t/custom_gcode.t10
-rw-r--r--xs/src/libslic3r/PlaceholderParser.cpp71
-rw-r--r--xs/src/libslic3r/PlaceholderParser.hpp10
-rw-r--r--xs/xsp/PlaceholderParser.xsp9
5 files changed, 84 insertions, 20 deletions
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 22a6ee389..66039ddf0 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -80,6 +80,10 @@ my $paused = 0;
$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0;
set_logging_level($Slic3r::loglevel);
+# Let the palceholder parser evaluate one expression to initialize its local static macro_processor
+# class instance in a thread safe manner.
+Slic3r::GCode::PlaceholderParser->new->evaluate_boolean_expression('1==1');
+
sub spawn_thread {
my ($cb) = @_;
@_ = ();
diff --git a/t/custom_gcode.t b/t/custom_gcode.t
index 1ccf738aa..75ce0b868 100644
--- a/t/custom_gcode.t
+++ b/t/custom_gcode.t
@@ -1,4 +1,4 @@
-use Test::More tests => 49;
+use Test::More tests => 55;
use strict;
use warnings;
@@ -67,6 +67,14 @@ use Slic3r::Test;
is $parser->process('{2*foo*(3-12)}'), '0', 'math: 2*foo*(3-12)';
is $parser->process('{2*bar*(3-12)}'), '-36', 'math: 2*bar*(3-12)';
ok abs($parser->process('{2.5*bar*(3-12)}') - -45) < 1e-7, 'math: 2.5*bar*(3-12)';
+
+ # Test the boolean expression parser.
+ is $parser->evaluate_boolean_expression('12 == 12'), 1, 'boolean expression parser: 12 == 12';
+ is $parser->evaluate_boolean_expression('12 != 12'), 0, 'boolean expression parser: 12 != 12';
+ is $parser->evaluate_boolean_expression('"has some PATTERN embedded" =~ /.*PATTERN.*/'), 1, 'boolean expression parser: regex matches';
+ is $parser->evaluate_boolean_expression('"has some PATTERN embedded" =~ /.*PTRN.*/'), 0, 'boolean expression parser: regex does not match';
+ is $parser->evaluate_boolean_expression('foo + 2 == bar'), 1, 'boolean expression parser: accessing variables, equal';
+ is $parser->evaluate_boolean_expression('foo + 3 == bar'), 0, 'boolean expression parser: accessing variables, not equal';
}
{
diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp
index b9f6b803e..0b33304a8 100644
--- a/xs/src/libslic3r/PlaceholderParser.cpp
+++ b/xs/src/libslic3r/PlaceholderParser.cpp
@@ -361,6 +361,13 @@ namespace client
out = self.b();
}
+ static void evaluate_boolean_to_string(expr &self, std::string &out)
+ {
+ if (self.type != TYPE_BOOL)
+ self.throw_exception("Not a boolean expression");
+ out = self.b() ? "true" : "false";
+ }
+
// Is lhs==rhs? Store the result into lhs.
static void compare_op(expr &lhs, expr &rhs, char op)
{
@@ -452,18 +459,23 @@ namespace client
}
struct MyContext {
- const PlaceholderParser *pp = nullptr;
- const DynamicConfig *config_override = nullptr;
- const size_t current_extruder_id = 0;
+ const DynamicConfig *config = nullptr;
+ const DynamicConfig *config_override = nullptr;
+ size_t current_extruder_id = 0;
+ // 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;
std::string error_message;
+ static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; }
+
const ConfigOption* resolve_symbol(const std::string &opt_key) const
{
const ConfigOption *opt = nullptr;
if (config_override != nullptr)
opt = config_override->option(opt_key);
if (opt == nullptr)
- opt = pp->option(opt_key);
+ opt = config->option(opt_key);
return opt;
}
@@ -734,13 +746,13 @@ namespace client
};
///////////////////////////////////////////////////////////////////////////
- // Our calculator grammar
+ // Our macro_processor grammar
///////////////////////////////////////////////////////////////////////////
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
template <typename Iterator>
- struct calculator : qi::grammar<Iterator, std::string(const MyContext*), spirit::ascii::space_type>
+ struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit::ascii::space_type>
{
- calculator() : calculator::base_type(start)
+ macro_processor() : macro_processor::base_type(start)
{
using namespace qi::labels;
qi::alpha_type alpha;
@@ -772,7 +784,13 @@ namespace client
// Starting symbol of the grammer.
// The leading eps is required by the "expectation point" operator ">".
// Without it, some of the errors would not trigger the error handler.
- start = eps > text_block(_r1);
+ // Also the start symbol switches between the "full macro syntax" and a "boolean expression only",
+ // depending on the context->just_boolean_expression flag. This way a single static expression parser
+ // could serve both purposes.
+ start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] >
+ ( eps(_a==true) > text_block(_r1) [_val=_1]
+ | bool_expr(_r1) [ px::bind(&expr<Iterator>::evaluate_boolean_to_string, _1, _val) ]
+ );
start.name("start");
qi::on_error<qi::fail>(start, px::bind(&MyContext::process_error_message<Iterator>, _r1, _4, _1, _2, _3));
@@ -944,7 +962,7 @@ namespace client
}
// The start of the grammar.
- qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> start;
+ qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit::ascii::space_type> start;
// A free-form text.
qi::rule<Iterator, std::string(), spirit::ascii::space_type> text;
// A free-form text, possibly empty, possibly containing macro expansions.
@@ -979,24 +997,23 @@ namespace client
};
}
-std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
+static std::string process_macro(const std::string &templ, client::MyContext &context)
{
typedef std::string::const_iterator iterator_type;
- typedef client::calculator<iterator_type> calculator;
+ typedef client::macro_processor<iterator_type> macro_processor;
// Our whitespace skipper.
spirit::ascii::space_type space;
- // Our grammar.
- calculator calc;
+ // Our grammar, statically allocated inside the method, meaning it will be allocated the first time
+ // PlaceholderParser::process() runs.
+ //FIXME this kind of initialization is not thread safe!
+ static macro_processor macro_processor_instance;
// Iterators over the source template.
std::string::const_iterator iter = templ.begin();
std::string::const_iterator end = templ.end();
// Accumulator for the processed template.
std::string output;
- client::MyContext context;
- context.pp = this;
- context.config_override = config_override;
- bool res = phrase_parse(iter, end, calc(&context), space, output);
+ bool res = phrase_parse(iter, end, macro_processor_instance(&context), space, output);
if (! context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n';
@@ -1005,4 +1022,24 @@ std::string PlaceholderParser::process(const std::string &templ, unsigned int cu
return output;
}
+std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
+{
+ client::MyContext context;
+ context.config = &this->config();
+ context.config_override = config_override;
+ context.current_extruder_id = current_extruder_id;
+ 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.
+bool PlaceholderParser::evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config)
+{
+ client::MyContext context;
+ context.config = &config;
+ // Let the macro processor parse just a boolean expression, not the full macro language.
+ context.just_boolean_expression = true;
+ return process_macro(templ, context) == "true";
+}
+
}
diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp
index 25264d461..ec2b837ad 100644
--- a/xs/src/libslic3r/PlaceholderParser.hpp
+++ b/xs/src/libslic3r/PlaceholderParser.hpp
@@ -26,11 +26,17 @@ public:
void set(const std::string &key, double value) { this->set(key, new ConfigOptionFloat(value)); }
void set(const std::string &key, const std::vector<std::string> &values) { this->set(key, new ConfigOptionStrings(values)); }
void set(const std::string &key, ConfigOption *opt) { m_config.set_key_value(key, opt); }
- const ConfigOption* option(const std::string &key) const { return m_config.option(key); }
+ const DynamicConfig& config() const { return m_config; }
+ const ConfigOption* option(const std::string &key) const { return m_config.option(key); }
- // Fill in the template.
+ // Fill in the template using a macro processing language.
+ // Throws std::runtime_error on syntax or runtime error.
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr) const;
+ // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
+ // Throws std::runtime_error on syntax or runtime error.
+ static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config);
+
private:
DynamicConfig m_config;
};
diff --git a/xs/xsp/PlaceholderParser.xsp b/xs/xsp/PlaceholderParser.xsp
index ab06450f9..244f89cf8 100644
--- a/xs/xsp/PlaceholderParser.xsp
+++ b/xs/xsp/PlaceholderParser.xsp
@@ -21,4 +21,13 @@
croak(e.what());
}
%};
+
+ bool evaluate_boolean_expression(const char *str) const
+ %code%{
+ try {
+ RETVAL = THIS->evaluate_boolean_expression(str, THIS->config());
+ } catch (std::exception& e) {
+ croak(e.what());
+ }
+ %};
};