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

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2017-11-17 13:15:46 +0300
committerbubnikv <bubnikv@gmail.com>2017-11-17 13:15:46 +0300
commit47f193fe2dd78fd1f5d058ade3ff897ce85cfdb8 (patch)
tree7c33c5b647a0381d8949639658d035e2f9c84459 /xs/src/libslic3r
parent200f176951d06cea9f2e33b7d8a325fb103d14d4 (diff)
The PlaceholderParser has been rewritten to use
a real boost::spirit::qi parser, accessing the DynamicConfig repository directly. This is a first step towards a full fledged expression interpreter.
Diffstat (limited to 'xs/src/libslic3r')
-rw-r--r--xs/src/libslic3r/Config.cpp24
-rw-r--r--xs/src/libslic3r/Config.hpp100
-rw-r--r--xs/src/libslic3r/GCode.cpp30
-rw-r--r--xs/src/libslic3r/PlaceholderParser.cpp475
-rw-r--r--xs/src/libslic3r/PlaceholderParser.hpp26
5 files changed, 525 insertions, 130 deletions
diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp
index cb8dd98f7..d7671c82f 100644
--- a/xs/src/libslic3r/Config.cpp
+++ b/xs/src/libslic3r/Config.cpp
@@ -234,10 +234,13 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
{
t_config_option_key opt_key = opt_key_src;
// Try to deserialize the option by its name.
- const ConfigOptionDef* optdef = this->def()->get(opt_key);
+ const ConfigDef *def = this->def();
+ if (def == nullptr)
+ throw NoDefinitionException();
+ const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr) {
// If we didn't find an option, look for any other option having this as an alias.
- for (const auto &opt : this->def()->options) {
+ for (const auto &opt : def->options) {
for (const t_config_option_key &opt_key2 : opt.second.aliases) {
if (opt_key2 == opt_key) {
opt_key = opt_key2;
@@ -277,10 +280,16 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
return static_cast<const ConfigOptionFloat*>(raw_opt)->value;
if (raw_opt->type() == coFloatOrPercent) {
// Get option definition.
- const ConfigOptionDef *def = this->def()->get(opt_key);
- assert(def != nullptr);
+ const ConfigDef *def = this->def();
+ if (def == nullptr)
+ throw NoDefinitionException();
+ const ConfigOptionDef *opt_def = def->get(opt_key);
+ assert(opt_def != nullptr);
// Compute absolute value over the absolute value of the base option.
- return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(def->ratio_over));
+ //FIXME there are some ratio_over chains, which end with empty ratio_with.
+ // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
+ return opt_def->ratio_over.empty() ? 0. :
+ static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over));
}
throw std::runtime_error("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()");
}
@@ -453,7 +462,10 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// Option was not found and a new option shall not be created.
return nullptr;
// Try to create a new ConfigOption.
- const ConfigOptionDef *optdef = this->def()->get(opt_key);
+ const ConfigDef *def = this->def();
+ if (def == nullptr)
+ throw NoDefinitionException();
+ const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr)
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
// Let the parent decide what to do if the opt_key is not defined by this->def().
diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp
index d37f03c4f..8b384c9b5 100644
--- a/xs/src/libslic3r/Config.hpp
+++ b/xs/src/libslic3r/Config.hpp
@@ -126,6 +126,10 @@ public:
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
+ // Get size of this vector.
+ virtual size_t size() const = 0;
+ // Is this vector empty?
+ virtual bool empty() const = 0;
protected:
// Used to verify type compatibility when assigning to / from a scalar ConfigOption.
@@ -140,6 +144,8 @@ public:
ConfigOptionVector() {}
explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {}
explicit ConfigOptionVector(std::initializer_list<T> il) : values(std::move(il)) {}
+ explicit ConfigOptionVector(const std::vector<T> &values) : values(values) {}
+ explicit ConfigOptionVector(std::vector<T> &&values) : values(std::move(values)) {}
std::vector<T> values;
void set(const ConfigOption *rhs) override
@@ -227,6 +233,9 @@ public:
}
}
+ size_t size() const override { return this->values.size(); }
+ bool empty() const override { return this->values.empty(); }
+
bool operator==(const ConfigOption &rhs) const override
{
if (rhs.type() != this->type())
@@ -445,6 +454,8 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
public:
ConfigOptionStrings() : ConfigOptionVector<std::string>() {}
explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector<std::string>(n, value) {}
+ explicit ConfigOptionStrings(const std::vector<std::string> &values) : ConfigOptionVector<std::string>(values) {}
+ explicit ConfigOptionStrings(std::vector<std::string> &&values) : ConfigOptionVector<std::string>(std::move(values)) {}
explicit ConfigOptionStrings(std::initializer_list<std::string> il) : ConfigOptionVector<std::string>(std::move(il)) {}
static ConfigOptionType static_type() { return coStrings; }
@@ -1049,23 +1060,71 @@ private:
class DynamicConfig : public virtual ConfigBase
{
public:
+ DynamicConfig() {}
DynamicConfig(const DynamicConfig& other) { *this = other; }
DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); }
virtual ~DynamicConfig() { clear(); }
- DynamicConfig& operator=(const DynamicConfig &other)
+ // Copy a content of one DynamicConfig to another DynamicConfig.
+ // If rhs.def() is not null, then it has to be equal to this->def().
+ DynamicConfig& operator=(const DynamicConfig &rhs)
{
+ assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
- for (const auto &kvp : other.options)
+ for (const auto &kvp : rhs.options)
this->options[kvp.first] = kvp.second->clone();
return *this;
}
- DynamicConfig& operator=(DynamicConfig &&other)
+ // Move a content of one DynamicConfig to another DynamicConfig.
+ // If rhs.def() is not null, then it has to be equal to this->def().
+ DynamicConfig& operator=(DynamicConfig &&rhs)
{
+ assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
- this->options = std::move(other.options);
- other.options.clear();
+ this->options = std::move(rhs.options);
+ rhs.options.clear();
+ return *this;
+ }
+
+ // Add a content of one DynamicConfig to another DynamicConfig.
+ // If rhs.def() is not null, then it has to be equal to this->def().
+ DynamicConfig& operator+=(const DynamicConfig &rhs)
+ {
+ assert(this->def() == nullptr || this->def() == rhs.def());
+ for (const auto &kvp : rhs.options) {
+ auto it = this->options.find(kvp.first);
+ if (it == this->options.end())
+ this->options[kvp.first] = kvp.second->clone();
+ else {
+ assert(it->second->type() == kvp.second->type());
+ if (it->second->type() == kvp.second->type())
+ *it->second = *kvp.second;
+ else {
+ delete it->second;
+ it->second = kvp.second->clone();
+ }
+ }
+ }
+ return *this;
+ }
+
+ // Move a content of one DynamicConfig to another DynamicConfig.
+ // If rhs.def() is not null, then it has to be equal to this->def().
+ DynamicConfig& operator+=(DynamicConfig &&rhs)
+ {
+ assert(this->def() == nullptr || this->def() == rhs.def());
+ for (const auto &kvp : rhs.options) {
+ auto it = this->options.find(kvp.first);
+ if (it == this->options.end()) {
+ this->options[kvp.first] = kvp.second;
+ } else {
+ assert(it->second->type() == kvp.second->type());
+ delete it->second;
+ it->second = kvp.second;
+ }
+ }
+ rhs.options.clear();
return *this;
}
@@ -1094,13 +1153,32 @@ public:
return true;
}
- template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
+ // Allow DynamicConfig to be instantiated on ints own without a definition.
+ // If the definition is not defined, the method requiring the definition will throw NoDefinitionException.
+ const ConfigDef* def() const override { return nullptr; };
+ template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
// Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
t_config_option_keys keys() const override;
+ // Set a value for an opt_key. Returns true if the value did not exist yet.
+ // This DynamicConfig will take ownership of opt.
+ // Be careful, as this method does not test the existence of opt_key in this->def().
+ bool set_key_value(const std::string &opt_key, ConfigOption *opt)
+ {
+ auto it = this->options.find(opt_key);
+ if (it == this->options.end()) {
+ this->options[opt_key] = opt;
+ return true;
+ } else {
+ delete it->second;
+ it->second = opt;
+ return false;
+ }
+ }
+
std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option<ConfigOptionString>(opt_key, create)->value; }
const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key); }
std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionStrings>(opt_key)->get_at(idx); }
@@ -1119,9 +1197,6 @@ public:
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
-protected:
- DynamicConfig() {}
-
private:
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map options;
@@ -1150,6 +1225,13 @@ public:
const char* what() const noexcept override { return "Unknown config option"; }
};
+/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
+class NoDefinitionException : public std::exception
+{
+public:
+ const char* what() const noexcept override { return "No config definition"; }
+};
+
}
#endif
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index f46d35657..12444d1a5 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -887,18 +887,22 @@ void GCode::process_layer(
// Set new layer - this will change Z and force a retraction if retract_layer_change is enabled.
if (! print.config.before_layer_gcode.value.empty()) {
- PlaceholderParser pp(m_placeholder_parser);
- pp.set("layer_num", m_layer_index + 1);
- pp.set("layer_z", print_z);
- gcode += pp.process(print.config.before_layer_gcode.value, m_writer.extruder()->id()) + "\n";
+ DynamicConfig config;
+ config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
+ config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
+ gcode += m_placeholder_parser.process(
+ print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
+ + "\n";
}
gcode += this->change_layer(print_z); // this will increase m_layer_index
m_layer = &layer;
if (! print.config.layer_gcode.value.empty()) {
- PlaceholderParser pp(m_placeholder_parser);
- pp.set("layer_num", m_layer_index);
- pp.set("layer_z", print_z);
- gcode += pp.process(print.config.layer_gcode.value, m_writer.extruder()->id()) + "\n";
+ DynamicConfig config;
+ config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
+ config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
+ gcode += m_placeholder_parser.process(
+ print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
+ + "\n";
}
if (! first_layer && ! m_second_layer_things_done) {
@@ -2091,10 +2095,12 @@ std::string GCode::set_extruder(unsigned int extruder_id)
// append custom toolchange G-code
if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) {
- PlaceholderParser pp = m_placeholder_parser;
- pp.set("previous_extruder", m_writer.extruder()->id());
- pp.set("next_extruder", extruder_id);
- gcode += pp.process(m_config.toolchange_gcode.value, extruder_id) + '\n';
+ DynamicConfig config;
+ config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
+ config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
+ gcode += m_placeholder_parser.process(
+ m_config.toolchange_gcode.value, extruder_id, &config)
+ + '\n';
}
// if ooze prevention is enabled, park current extruder in the nearest
diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp
index e25ec9526..5967f6a7e 100644
--- a/xs/src/libslic3r/PlaceholderParser.cpp
+++ b/xs/src/libslic3r/PlaceholderParser.cpp
@@ -21,6 +21,29 @@
#endif
#endif
+// Spirit v2.5 allows you to suppress automatic generation
+// of predefined terminals to speed up complation. With
+// BOOST_SPIRIT_NO_PREDEFINED_TERMINALS defined, you are
+// responsible in creating instances of the terminals that
+// you need (e.g. see qi::uint_type uint_ below).
+//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
+
+#include <boost/config/warning_disable.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/qi_lit.hpp>
+#include <boost/spirit/include/phoenix_core.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix_fusion.hpp>
+#include <boost/spirit/include/phoenix_stl.hpp>
+#include <boost/spirit/include/phoenix_object.hpp>
+#include <boost/fusion/include/adapt_struct.hpp>
+#include <boost/variant/recursive_variant.hpp>
+#include <boost/phoenix/bind/bind_function.hpp>
+
+#include <iostream>
+#include <string>
+
namespace Slic3r {
PlaceholderParser::PlaceholderParser()
@@ -61,130 +84,398 @@ void PlaceholderParser::update_timestamp()
// are expected to be addressed by the extruder ID, therefore
// if a vector configuration value is addressed without an index,
// a current extruder ID is used.
-void PlaceholderParser::apply_config(const DynamicPrintConfig &config)
+void PlaceholderParser::apply_config(const DynamicPrintConfig &rhs)
{
- for (const t_config_option_key &opt_key : config.keys()) {
- const ConfigOptionDef* def = config.def()->get(opt_key);
- if (def->multiline || opt_key == "post_process")
+ const ConfigDef *def = rhs.def();
+ for (const t_config_option_key &opt_key : rhs.keys()) {
+ const ConfigOptionDef *opt_def = def->get(opt_key);
+ if (opt_def->multiline || opt_key == "post_process")
continue;
-
- const ConfigOption* opt = config.option(opt_key);
- const ConfigOptionVectorBase* optv = dynamic_cast<const ConfigOptionVectorBase*>(opt);
- if (optv != nullptr && opt_key != "bed_shape") {
- // set placeholders for options with multiple values
- this->set(opt_key, optv->vserialize());
- } else if (const ConfigOptionPoint* optp = dynamic_cast<const ConfigOptionPoint*>(opt)) {
- this->set(opt_key, optp->serialize());
- Pointf val = *optp;
- this->set(opt_key + "_X", val.x);
- this->set(opt_key + "_Y", val.y);
- } else {
- // set single-value placeholders
- this->set(opt_key, opt->serialize());
- }
+ const ConfigOption *opt = rhs.option(opt_key);
+ // Store a copy of the config option.
+ // Convert FloatOrPercent values to floats first.
+ //FIXME there are some ratio_over chains, which end with empty ratio_with.
+ // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
+ this->set(opt_key, (opt->type() == coFloatOrPercent) ?
+ new ConfigOptionFloat(rhs.get_abs_value(opt_key)) :
+ opt->clone());
}
}
void PlaceholderParser::apply_env_variables()
{
- for (char** env = environ; *env; env++) {
+ for (char** env = environ; *env; ++ env) {
if (strncmp(*env, "SLIC3R_", 7) == 0) {
std::stringstream ss(*env);
std::string key, value;
std::getline(ss, key, '=');
ss >> value;
-
this->set(key, value);
}
}
}
-void PlaceholderParser::set(const std::string &key, const std::string &value)
+namespace client
{
- m_single[key] = value;
- m_multiple.erase(key);
-}
+ struct MyContext {
+ const PlaceholderParser *pp = nullptr;
+ const DynamicConfig *config_override = nullptr;
+ const size_t current_extruder_id = 0;
-void PlaceholderParser::set(const std::string &key, int value)
-{
- std::ostringstream ss;
- ss << value;
- this->set(key, ss.str());
-}
+ 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);
+ return opt;
+ }
-void PlaceholderParser::set(const std::string &key, unsigned int value)
-{
- std::ostringstream ss;
- ss << value;
- this->set(key, ss.str());
+ template <typename Iterator>
+ static void legacy_variable_expansion(
+ const MyContext *ctx,
+ boost::iterator_range<Iterator> &opt_key,
+ std::string &output)
+ {
+ std::string opt_key_str(opt_key.begin(), opt_key.end());
+ const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
+ size_t idx = ctx->current_extruder_id;
+ if (opt == nullptr) {
+ // Check whether this is a legacy vector indexing.
+ idx = opt_key_str.rfind('_');
+ if (idx != std::string::npos) {
+ opt = ctx->resolve_symbol(opt_key_str.substr(0, idx));
+ if (opt != nullptr) {
+ if (! opt->is_vector())
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Trying to index a scalar variable")));
+ char *endptr = nullptr;
+ idx = strtol(opt_key_str.c_str() + idx + 1, &endptr, 10);
+ if (endptr == nullptr || *endptr != 0)
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin() + idx + 1, opt_key.end(), boost::spirit::info("Invalid vector index")));
+ }
+ }
+ }
+ if (opt == nullptr)
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Variable does not exist")));
+ if (opt->is_scalar())
+ output = opt->serialize();
+ else {
+ const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
+ if (vec->empty())
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing an empty vector variable")));
+ output = vec->vserialize()[(idx >= vec->size()) ? 0 : idx];
+ }
+ }
+
+ template <typename Iterator>
+ static void legacy_variable_expansion2(
+ const MyContext *ctx,
+ boost::iterator_range<Iterator> &opt_key,
+ boost::iterator_range<Iterator> &opt_vector_index,
+ std::string &output)
+ {
+ std::string opt_key_str(opt_key.begin(), opt_key.end());
+ const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
+ if (opt == nullptr) {
+ // Check whether the opt_key ends with '_'.
+ if (opt_key_str.back() == '_')
+ opt_key_str.resize(opt_key_str.size() - 1);
+ opt = ctx->resolve_symbol(opt_key_str);
+ }
+ if (! opt->is_vector())
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Trying to index a scalar variable")));
+ const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt);
+ if (vec->empty())
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing an empty vector variable")));
+ const ConfigOption *opt_index = ctx->resolve_symbol(std::string(opt_vector_index.begin(), opt_vector_index.end()));
+ if (opt_index == nullptr)
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Variable does not exist")));
+ if (opt_index->type() != coInt)
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Indexing variable has to be integer")));
+ int idx = opt_index->getInt();
+ if (idx < 0)
+ boost::throw_exception(boost::spirit::qi::expectation_failure<Iterator>(
+ opt_key.begin(), opt_key.end(), boost::spirit::info("Negative vector index")));
+ output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx];
+ }
+ };
+
+ struct expr
+ {
+ expr(int i = 0) : type(TYPE_INT) { data.i = i; }
+ expr(double d) : type(TYPE_DOUBLE) { data.d = d; }
+ expr(const char *s) : type(TYPE_STRING) { data.s = new std::string(s); }
+ expr(std::string &s) : type(TYPE_STRING) { data.s = new std::string(s); }
+ expr(const expr &rhs) : type(rhs.type) { data.s = (rhs.type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s; }
+ expr(expr &&rhs) : type(rhs.type) { data.s = rhs.data.s; rhs.data.s = nullptr; rhs.type = TYPE_EMPTY; }
+ ~expr() { if (type == TYPE_STRING) delete data.s; data.s = nullptr; }
+
+ expr &operator=(const expr &rhs)
+ {
+ type = rhs.type;
+ data.s = (type == TYPE_STRING) ? new std::string(*rhs.data.s) : rhs.data.s;
+ return *this;
+ }
+
+ expr &operator=(expr &&rhs)
+ {
+ type = rhs.type;
+ data.s = rhs.data.s;
+ rhs.data.s = nullptr;
+ rhs.type = TYPE_EMPTY;
+ return *this;
+ }
+
+ int& i() { return data.i; }
+ int i() const { return data.i; }
+ double& d() { return data.d; }
+ double d() const { return data.d; }
+ std::string& s() { return *data.s; }
+ const std::string& s() const { return *data.s; }
+
+ std::string to_string() const
+ {
+ std::string out;
+ switch (type) {
+ case TYPE_INT: out = boost::to_string(data.i); break;
+ case TYPE_DOUBLE: out = boost::to_string(data.d); break;
+ case TYPE_STRING: out = *data.s; break;
+ }
+ return out;
+ }
+
+ union {
+ int i;
+ double d;
+ std::string *s;
+ } data;
+
+ enum Type {
+ TYPE_EMPTY = 0,
+ TYPE_INT,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ };
+
+ int type;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Our calculator grammar
+ ///////////////////////////////////////////////////////////////////////////
+ template <typename Iterator>
+ struct calculator : boost::spirit::qi::grammar<Iterator, std::string(const MyContext*), boost::spirit::ascii::space_type>
+ {
+ calculator() : calculator::base_type(start)
+ {
+ using boost::spirit::qi::alpha;
+ using boost::spirit::qi::alnum;
+ using boost::spirit::qi::eol;
+ using boost::spirit::qi::eoi;
+ using boost::spirit::qi::eps;
+ using boost::spirit::qi::raw;
+ using boost::spirit::qi::lit;
+ using boost::spirit::qi::lexeme;
+ using boost::spirit::qi::on_error;
+ using boost::spirit::qi::fail;
+ using boost::spirit::ascii::char_;
+ using boost::spirit::int_;
+ using boost::spirit::double_;
+ using boost::spirit::ascii::string;
+ using namespace boost::spirit::qi::labels;
+
+ using boost::phoenix::construct;
+ using boost::phoenix::val;
+ using boost::phoenix::begin;
+
+ boost::spirit::qi::_val_type _val;
+ boost::spirit::qi::_1_type _1;
+ boost::spirit::qi::_2_type _2;
+ boost::spirit::qi::_r1_type _r1;
+ boost::spirit::qi::uint_type uint_;
+
+ // 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 [_val+=_1]
+ || ((lit('{') > macro [_val+=_1] > '}')
+ | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']')));
+ start.name("start");
+
+ // Free-form text up to a first brace, including spaces and newlines.
+ // The free-form text will be inserted into the processed text without a modification.
+ text = raw[+(char_ - '[' - '{')];
+ text.name("text");
+
+ // New style of macro expansion.
+ // The macro expansion may contain numeric or string expressions, ifs and cases.
+ macro = identifier;
+ macro.name("macro");
+
+ // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
+ legacy_variable_expansion =
+ (identifier >> &lit(']'))
+ [ boost::phoenix::bind(&MyContext::legacy_variable_expansion<Iterator>, _r1, _1, _val) ]
+ | (identifier > lit('[') > identifier > ']')
+ [ boost::phoenix::bind(&MyContext::legacy_variable_expansion2<Iterator>, _r1, _1, _2, _val) ]
+ ;
+ legacy_variable_expansion.name("legacy_variable_expansion");
+
+ identifier =
+// !expr.keywords >>
+ raw[lexeme[(alpha | '_') >> *(alnum | '_')]]
+ ;
+ identifier.name("identifier");
+
+/*
+ bool_expr =
+ (expression >> '=' >> '=' >> expression) |
+ (expression >> '!' >> '=' >> expression) |
+ (expression >> '<' >> '>' >> expression)
+ ;
+
+ expression =
+ term //[_val = _1]
+ >> *( ('+' >> term ) //[_val += _1])
+ | ('-' >> term )//[_val -= _1])
+ )
+ ;
+
+ term =
+ factor //[_val = _1]
+ >> *( ('*' >> factor )//[_val *= _1])
+ | ('/' >> factor )//[_val /= _1])
+ )
+ ;
+
+ factor =
+ int_ //[_val = expr(_1)]
+ | double_ //[_val = expr(_1)]
+ | '(' >> expression >> ')' // [_val = std::move(_1)] >> ')'
+ | ('-' >> factor ) //[_val = -_1])
+ | ('+' >> factor ) //[_val = std::move(_1)])
+ ;
+*/
+
+// text %= lexeme[+(char_ - '<')];
+
+// text_to_eol %= lexeme[*(char_ - eol) >> (eol | eoi)];
+ /*
+ expression_with_braces = lit('{') >> (
+ string("if") >> if_else_output [_val = _1] |
+ string("switch") >> switch_output [_val = _1] |
+ expression [_val = boost::to_string(_1)] >> '}'
+ );
+ if_else_output =
+ bool_expr[_r1 = _1] >> '}' >> text_to_eol[_val = _r1 ? _1 : std::string()] >>
+ *(lit('{') >> "elsif" >> bool_expr[_r1 = !_r1 && _1] >> '}' >> text_to_eol[_val = _r1 ? _1 : std::string()]) >>
+ -(lit('{') >> "else" >> '}' >> text_to_eol[_val = _r1 ? std::string() : _1]);
+*/
+/*
+ on_error<fail>(start,
+ phx::ref(std::cout)
+ << "Error! Expecting "
+ << boost::spirit::qi::_4
+ << " here: '"
+ << construct<std::string>(boost::spirit::qi::_3, boost::spirit::qi::_2)
+ << "'\n"
+ );
+*/
+ }
+
+ // The start of the grammar.
+ boost::spirit::qi::rule<Iterator, std::string(const MyContext*), boost::spirit::ascii::space_type> start;
+ // A free-form text.
+ boost::spirit::qi::rule<Iterator, std::string(), boost::spirit::ascii::space_type> text;
+ // Statements enclosed in curely braces {}
+ boost::spirit::qi::rule<Iterator, std::string(), boost::spirit::ascii::space_type> macro;
+ // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
+ boost::spirit::qi::rule<Iterator, std::string(const MyContext*), boost::spirit::ascii::space_type> legacy_variable_expansion;
+ // Parsed identifier name.
+ boost::spirit::qi::rule<Iterator, boost::iterator_range<Iterator>, boost::spirit::ascii::space_type> identifier;
+
+ boost::spirit::qi::rule<Iterator, expr(), boost::spirit::ascii::space_type> expression, term, factor;
+ boost::spirit::qi::rule<Iterator, std::string(), boost::spirit::ascii::space_type> text_to_eol;
+ boost::spirit::qi::rule<Iterator, std::string(bool), boost::spirit::ascii::space_type> expression_with_braces;
+ boost::spirit::qi::rule<Iterator, bool, boost::spirit::ascii::space_type> bool_expr;
+
+ boost::spirit::qi::rule<Iterator, std::string(bool), boost::spirit::ascii::space_type> if_else_output;
+ boost::spirit::qi::rule<Iterator, std::string(expr), boost::spirit::ascii::space_type> switch_output;
+ };
}
-void PlaceholderParser::set(const std::string &key, double value)
+struct printer
{
- std::ostringstream ss;
- ss << value;
- this->set(key, ss.str());
-}
+ typedef boost::spirit::utf8_string string;
-void PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
+ void element(string const& tag, string const& value, int depth) const
+ {
+ for (int i = 0; i < (depth*4); ++i) // indent to depth
+ std::cout << ' ';
+ std::cout << "tag: " << tag;
+ if (value != "")
+ std::cout << ", value: " << value;
+ std::cout << std::endl;
+ }
+};
+
+void print_info(boost::spirit::info const& what)
{
- m_single.erase(key);
- if (values.empty())
- m_multiple.erase(key);
- else
- m_multiple[key] = values;
+ using boost::spirit::basic_info_walker;
+ printer pr;
+ basic_info_walker<printer> walker(pr, what.tag, 0);
+ boost::apply_visitor(walker, what.value);
}
-std::string PlaceholderParser::process(std::string str, unsigned int current_extruder_id) const
+std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
{
- char key[2048];
-
- // Replace extruder independent single options, like [foo].
- for (const auto &key_value : m_single) {
- sprintf(key, "[%s]", key_value.first.c_str());
- const std::string &replace = key_value.second;
- for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) {
- str.replace(i, key_value.first.size() + 2, replace);
- i += replace.size();
- }
- }
+ typedef std::string::const_iterator iterator_type;
+ typedef client::calculator<iterator_type> calculator;
- // Replace extruder dependent single options with the value for the active extruder.
- // For example, [temperature] will be replaced with the current extruder temperature.
- for (const auto &key_value : m_multiple) {
- sprintf(key, "[%s]", key_value.first.c_str());
- const std::string &replace = key_value.second[(current_extruder_id < key_value.second.size()) ? current_extruder_id : 0];
- for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) {
- str.replace(i, key_value.first.size() + 2, replace);
- i += replace.size();
- }
+ boost::spirit::ascii::space_type space; // Our skipper
+ calculator calc; // Our grammar
+
+ std::string::const_iterator iter = templ.begin();
+ std::string::const_iterator end = templ.end();
+ //std::string result;
+ std::string result;
+ bool r = false;
+ try {
+ client::MyContext context;
+ context.pp = this;
+ context.config_override = config_override;
+ r = phrase_parse(iter, end, calc(&context), space, result);
+ } catch (boost::spirit::qi::expectation_failure<iterator_type> const& x) {
+ std::cout << "expected: "; print_info(x.what_);
+ std::cout << "got: \"" << std::string(x.first, x.last) << '"' << std::endl;
}
- // Replace multiple options like [foo_0].
- for (const auto &key_value : m_multiple) {
- sprintf(key, "[%s_", key_value.first.c_str());
- const std::vector<std::string> &values = key_value.second;
- for (size_t i = 0; (i = str.find(key, i)) != std::string::npos;) {
- size_t k = str.find(']', i + key_value.first.size() + 2);
- if (k != std::string::npos) {
- // Parse the key index and the closing bracket.
- ++ k;
- int idx = 0;
- if (sscanf(str.c_str() + i + key_value.first.size() + 2, "%d]", &idx) == 1 && idx >= 0) {
- if (idx >= int(values.size()))
- idx = 0;
- str.replace(i, k - i, values[idx]);
- i += values[idx].size();
- continue;
- }
- }
- // The key does not match the pattern [foo_%d]. Skip just [foo_.] with the hope that there was a missing ']',
- // so an opening '[' may be found somewhere before the position k.
- i += key_value.first.size() + 3;
- }
+ if (r && iter == end)
+ {
+// std::cout << "-------------------------\n";
+// std::cout << "Parsing succeeded\n";
+// std::cout << "result = " << result << std::endl;
+// std::cout << "-------------------------\n";
}
-
- return str;
+ else
+ {
+ std::string rest(iter, end);
+ std::cout << "-------------------------\n";
+ std::cout << "Parsing failed\n";
+ std::cout << "stopped at: \" " << rest << "\"\n";
+ std::cout << "source: \n" << templ;
+ std::cout << "-------------------------\n";
+ }
+ return result;
}
}
diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp
index a8abf8871..9ec2eedea 100644
--- a/xs/src/libslic3r/PlaceholderParser.hpp
+++ b/xs/src/libslic3r/PlaceholderParser.hpp
@@ -11,23 +11,27 @@ namespace Slic3r {
class PlaceholderParser
{
-public:
- std::map<std::string, std::string> m_single;
- std::map<std::string, std::vector<std::string>> m_multiple;
-
+public:
PlaceholderParser();
+
void update_timestamp();
void apply_config(const DynamicPrintConfig &config);
void apply_env_variables();
- void set(const std::string &key, const std::string &value);
- void set(const std::string &key, int value);
- void set(const std::string &key, unsigned int value);
- void set(const std::string &key, double value);
- void set(const std::string &key, std::vector<std::string> values);
- std::string process(std::string str, unsigned int current_extruder_id) const;
+
+ // Add new ConfigOption values to m_config.
+ void set(const std::string &key, const std::string &value) { this->set(key, new ConfigOptionString(value)); }
+ void set(const std::string &key, int value) { this->set(key, new ConfigOptionInt(value)); }
+ void set(const std::string &key, unsigned int value) { this->set(key, int(value)); }
+ 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); }
+
+ // Fill in the template.
+ std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr) const;
private:
- bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const;
+ DynamicConfig m_config;
};
}