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.
This commit is contained in:
parent
200f176951
commit
47f193fe2d
7 changed files with 534 additions and 144 deletions
|
@ -48,7 +48,7 @@ use Slic3r::Test;
|
||||||
{
|
{
|
||||||
my $parser = Slic3r::GCode::PlaceholderParser->new;
|
my $parser = Slic3r::GCode::PlaceholderParser->new;
|
||||||
$parser->apply_config(my $config = Slic3r::Config::new_from_defaults);
|
$parser->apply_config(my $config = Slic3r::Config::new_from_defaults);
|
||||||
$parser->set('foo' => '0');
|
$parser->set('foo' => 0);
|
||||||
is $parser->process('[temperature_[foo]]'),
|
is $parser->process('[temperature_[foo]]'),
|
||||||
$config->temperature->[0],
|
$config->temperature->[0],
|
||||||
"nested config options";
|
"nested config options";
|
||||||
|
|
|
@ -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;
|
t_config_option_key opt_key = opt_key_src;
|
||||||
// Try to deserialize the option by its name.
|
// 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 (optdef == nullptr) {
|
||||||
// If we didn't find an option, look for any other option having this as an alias.
|
// 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) {
|
for (const t_config_option_key &opt_key2 : opt.second.aliases) {
|
||||||
if (opt_key2 == opt_key) {
|
if (opt_key2 == opt_key) {
|
||||||
opt_key = opt_key2;
|
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;
|
return static_cast<const ConfigOptionFloat*>(raw_opt)->value;
|
||||||
if (raw_opt->type() == coFloatOrPercent) {
|
if (raw_opt->type() == coFloatOrPercent) {
|
||||||
// Get option definition.
|
// Get option definition.
|
||||||
const ConfigOptionDef *def = this->def()->get(opt_key);
|
const ConfigDef *def = this->def();
|
||||||
assert(def != nullptr);
|
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.
|
// 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()");
|
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.
|
// Option was not found and a new option shall not be created.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
// Try to create a new ConfigOption.
|
// 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)
|
if (optdef == nullptr)
|
||||||
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
|
// 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().
|
// Let the parent decide what to do if the opt_key is not defined by this->def().
|
||||||
|
|
|
@ -126,6 +126,10 @@ public:
|
||||||
|
|
||||||
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
|
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:
|
protected:
|
||||||
// Used to verify type compatibility when assigning to / from a scalar ConfigOption.
|
// Used to verify type compatibility when assigning to / from a scalar ConfigOption.
|
||||||
|
@ -140,6 +144,8 @@ public:
|
||||||
ConfigOptionVector() {}
|
ConfigOptionVector() {}
|
||||||
explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {}
|
explicit ConfigOptionVector(size_t n, const T &value) : values(n, value) {}
|
||||||
explicit ConfigOptionVector(std::initializer_list<T> il) : values(std::move(il)) {}
|
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;
|
std::vector<T> values;
|
||||||
|
|
||||||
void set(const ConfigOption *rhs) override
|
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
|
bool operator==(const ConfigOption &rhs) const override
|
||||||
{
|
{
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
|
@ -445,6 +454,8 @@ class ConfigOptionStrings : public ConfigOptionVector<std::string>
|
||||||
public:
|
public:
|
||||||
ConfigOptionStrings() : ConfigOptionVector<std::string>() {}
|
ConfigOptionStrings() : ConfigOptionVector<std::string>() {}
|
||||||
explicit ConfigOptionStrings(size_t n, const std::string &value) : ConfigOptionVector<std::string>(n, value) {}
|
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)) {}
|
explicit ConfigOptionStrings(std::initializer_list<std::string> il) : ConfigOptionVector<std::string>(std::move(il)) {}
|
||||||
|
|
||||||
static ConfigOptionType static_type() { return coStrings; }
|
static ConfigOptionType static_type() { return coStrings; }
|
||||||
|
@ -1049,23 +1060,71 @@ private:
|
||||||
class DynamicConfig : public virtual ConfigBase
|
class DynamicConfig : public virtual ConfigBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
DynamicConfig() {}
|
||||||
DynamicConfig(const DynamicConfig& other) { *this = other; }
|
DynamicConfig(const DynamicConfig& other) { *this = other; }
|
||||||
DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); }
|
DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); }
|
||||||
virtual ~DynamicConfig() { 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();
|
this->clear();
|
||||||
for (const auto &kvp : other.options)
|
for (const auto &kvp : rhs.options)
|
||||||
this->options[kvp.first] = kvp.second->clone();
|
this->options[kvp.first] = kvp.second->clone();
|
||||||
return *this;
|
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->clear();
|
||||||
this->options = std::move(other.options);
|
this->options = std::move(rhs.options);
|
||||||
other.options.clear();
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,6 +1153,9 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
|
||||||
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
|
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
|
||||||
// Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
|
// Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
|
||||||
|
@ -1101,6 +1163,22 @@ public:
|
||||||
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
|
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
|
||||||
t_config_option_keys keys() const override;
|
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; }
|
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); }
|
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); }
|
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) 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; }
|
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:
|
private:
|
||||||
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
|
||||||
t_options_map options;
|
t_options_map options;
|
||||||
|
@ -1150,6 +1225,13 @@ public:
|
||||||
const char* what() const noexcept override { return "Unknown config option"; }
|
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
|
#endif
|
||||||
|
|
|
@ -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.
|
// 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()) {
|
if (! print.config.before_layer_gcode.value.empty()) {
|
||||||
PlaceholderParser pp(m_placeholder_parser);
|
DynamicConfig config;
|
||||||
pp.set("layer_num", m_layer_index + 1);
|
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
|
||||||
pp.set("layer_z", print_z);
|
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||||
gcode += pp.process(print.config.before_layer_gcode.value, m_writer.extruder()->id()) + "\n";
|
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
|
gcode += this->change_layer(print_z); // this will increase m_layer_index
|
||||||
m_layer = &layer;
|
m_layer = &layer;
|
||||||
if (! print.config.layer_gcode.value.empty()) {
|
if (! print.config.layer_gcode.value.empty()) {
|
||||||
PlaceholderParser pp(m_placeholder_parser);
|
DynamicConfig config;
|
||||||
pp.set("layer_num", m_layer_index);
|
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||||
pp.set("layer_z", print_z);
|
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
|
||||||
gcode += pp.process(print.config.layer_gcode.value, m_writer.extruder()->id()) + "\n";
|
gcode += m_placeholder_parser.process(
|
||||||
|
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
|
||||||
|
+ "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! first_layer && ! m_second_layer_things_done) {
|
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
|
// append custom toolchange G-code
|
||||||
if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) {
|
if (m_writer.extruder() != nullptr && !m_config.toolchange_gcode.value.empty()) {
|
||||||
PlaceholderParser pp = m_placeholder_parser;
|
DynamicConfig config;
|
||||||
pp.set("previous_extruder", m_writer.extruder()->id());
|
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
|
||||||
pp.set("next_extruder", extruder_id);
|
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
|
||||||
gcode += pp.process(m_config.toolchange_gcode.value, extruder_id) + '\n';
|
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
|
// if ooze prevention is enabled, park current extruder in the nearest
|
||||||
|
|
|
@ -21,6 +21,29 @@
|
||||||
#endif
|
#endif
|
||||||
#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 {
|
namespace Slic3r {
|
||||||
|
|
||||||
PlaceholderParser::PlaceholderParser()
|
PlaceholderParser::PlaceholderParser()
|
||||||
|
@ -61,130 +84,398 @@ void PlaceholderParser::update_timestamp()
|
||||||
// are expected to be addressed by the extruder ID, therefore
|
// are expected to be addressed by the extruder ID, therefore
|
||||||
// if a vector configuration value is addressed without an index,
|
// if a vector configuration value is addressed without an index,
|
||||||
// a current extruder ID is used.
|
// 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 ConfigDef *def = rhs.def();
|
||||||
const ConfigOptionDef* def = config.def()->get(opt_key);
|
for (const t_config_option_key &opt_key : rhs.keys()) {
|
||||||
if (def->multiline || opt_key == "post_process")
|
const ConfigOptionDef *opt_def = def->get(opt_key);
|
||||||
|
if (opt_def->multiline || opt_key == "post_process")
|
||||||
continue;
|
continue;
|
||||||
|
const ConfigOption *opt = rhs.option(opt_key);
|
||||||
const ConfigOption* opt = config.option(opt_key);
|
// Store a copy of the config option.
|
||||||
const ConfigOptionVectorBase* optv = dynamic_cast<const ConfigOptionVectorBase*>(opt);
|
// Convert FloatOrPercent values to floats first.
|
||||||
if (optv != nullptr && opt_key != "bed_shape") {
|
//FIXME there are some ratio_over chains, which end with empty ratio_with.
|
||||||
// set placeholders for options with multiple values
|
// For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly.
|
||||||
this->set(opt_key, optv->vserialize());
|
this->set(opt_key, (opt->type() == coFloatOrPercent) ?
|
||||||
} else if (const ConfigOptionPoint* optp = dynamic_cast<const ConfigOptionPoint*>(opt)) {
|
new ConfigOptionFloat(rhs.get_abs_value(opt_key)) :
|
||||||
this->set(opt_key, optp->serialize());
|
opt->clone());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaceholderParser::apply_env_variables()
|
void PlaceholderParser::apply_env_variables()
|
||||||
{
|
{
|
||||||
for (char** env = environ; *env; env++) {
|
for (char** env = environ; *env; ++ env) {
|
||||||
if (strncmp(*env, "SLIC3R_", 7) == 0) {
|
if (strncmp(*env, "SLIC3R_", 7) == 0) {
|
||||||
std::stringstream ss(*env);
|
std::stringstream ss(*env);
|
||||||
std::string key, value;
|
std::string key, value;
|
||||||
std::getline(ss, key, '=');
|
std::getline(ss, key, '=');
|
||||||
ss >> value;
|
ss >> value;
|
||||||
|
|
||||||
this->set(key, value);
|
this->set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaceholderParser::set(const std::string &key, const std::string &value)
|
namespace client
|
||||||
{
|
{
|
||||||
m_single[key] = value;
|
struct MyContext {
|
||||||
m_multiple.erase(key);
|
const PlaceholderParser *pp = nullptr;
|
||||||
|
const DynamicConfig *config_override = nullptr;
|
||||||
|
const size_t current_extruder_id = 0;
|
||||||
|
|
||||||
|
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, int value)
|
template <typename Iterator>
|
||||||
|
static void legacy_variable_expansion(
|
||||||
|
const MyContext *ctx,
|
||||||
|
boost::iterator_range<Iterator> &opt_key,
|
||||||
|
std::string &output)
|
||||||
{
|
{
|
||||||
std::ostringstream ss;
|
std::string opt_key_str(opt_key.begin(), opt_key.end());
|
||||||
ss << value;
|
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
|
||||||
this->set(key, ss.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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaceholderParser::set(const std::string &key, unsigned int value)
|
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::ostringstream ss;
|
std::string opt_key_str(opt_key.begin(), opt_key.end());
|
||||||
ss << value;
|
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
|
||||||
this->set(key, ss.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaceholderParser::set(const std::string &key, double value)
|
expr &operator=(expr &&rhs)
|
||||||
{
|
{
|
||||||
std::ostringstream ss;
|
type = rhs.type;
|
||||||
ss << value;
|
data.s = rhs.data.s;
|
||||||
this->set(key, ss.str());
|
rhs.data.s = nullptr;
|
||||||
|
rhs.type = TYPE_EMPTY;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
|
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
|
||||||
{
|
{
|
||||||
m_single.erase(key);
|
std::string out;
|
||||||
if (values.empty())
|
switch (type) {
|
||||||
m_multiple.erase(key);
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct printer
|
||||||
|
{
|
||||||
|
typedef boost::spirit::utf8_string string;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) const
|
||||||
|
{
|
||||||
|
typedef std::string::const_iterator iterator_type;
|
||||||
|
typedef client::calculator<iterator_type> calculator;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r && iter == end)
|
||||||
|
{
|
||||||
|
// std::cout << "-------------------------\n";
|
||||||
|
// std::cout << "Parsing succeeded\n";
|
||||||
|
// std::cout << "result = " << result << std::endl;
|
||||||
|
// std::cout << "-------------------------\n";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
m_multiple[key] = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PlaceholderParser::process(std::string str, unsigned int current_extruder_id) const
|
|
||||||
{
|
{
|
||||||
char key[2048];
|
std::string rest(iter, end);
|
||||||
|
std::cout << "-------------------------\n";
|
||||||
// Replace extruder independent single options, like [foo].
|
std::cout << "Parsing failed\n";
|
||||||
for (const auto &key_value : m_single) {
|
std::cout << "stopped at: \" " << rest << "\"\n";
|
||||||
sprintf(key, "[%s]", key_value.first.c_str());
|
std::cout << "source: \n" << templ;
|
||||||
const std::string &replace = key_value.second;
|
std::cout << "-------------------------\n";
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
return result;
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,22 +12,26 @@ namespace Slic3r {
|
||||||
class PlaceholderParser
|
class PlaceholderParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::map<std::string, std::string> m_single;
|
|
||||||
std::map<std::string, std::vector<std::string>> m_multiple;
|
|
||||||
|
|
||||||
PlaceholderParser();
|
PlaceholderParser();
|
||||||
|
|
||||||
void update_timestamp();
|
void update_timestamp();
|
||||||
void apply_config(const DynamicPrintConfig &config);
|
void apply_config(const DynamicPrintConfig &config);
|
||||||
void apply_env_variables();
|
void apply_env_variables();
|
||||||
void set(const std::string &key, const std::string &value);
|
|
||||||
void set(const std::string &key, int value);
|
// Add new ConfigOption values to m_config.
|
||||||
void set(const std::string &key, unsigned int value);
|
void set(const std::string &key, const std::string &value) { this->set(key, new ConfigOptionString(value)); }
|
||||||
void set(const std::string &key, double value);
|
void set(const std::string &key, int value) { this->set(key, new ConfigOptionInt(value)); }
|
||||||
void set(const std::string &key, std::vector<std::string> values);
|
void set(const std::string &key, unsigned int value) { this->set(key, int(value)); }
|
||||||
std::string process(std::string str, unsigned int current_extruder_id) const;
|
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:
|
private:
|
||||||
bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const;
|
DynamicConfig m_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,10 @@
|
||||||
%name{Slic3r::GCode::PlaceholderParser} class PlaceholderParser {
|
%name{Slic3r::GCode::PlaceholderParser} class PlaceholderParser {
|
||||||
PlaceholderParser();
|
PlaceholderParser();
|
||||||
~PlaceholderParser();
|
~PlaceholderParser();
|
||||||
Clone<PlaceholderParser> clone()
|
|
||||||
%code{% RETVAL = THIS; %};
|
|
||||||
|
|
||||||
void update_timestamp();
|
|
||||||
void apply_env_variables();
|
|
||||||
void apply_config(DynamicPrintConfig *config)
|
void apply_config(DynamicPrintConfig *config)
|
||||||
%code%{ THIS->apply_config(*config); %};
|
%code%{ THIS->apply_config(*config); %};
|
||||||
void set(std::string key, std::string value);
|
void set(std::string key, int value);
|
||||||
%name{set_multiple} void set(std::string key, std::vector<std::string> values);
|
|
||||||
std::string process(std::string str) const
|
std::string process(std::string str) const
|
||||||
%code%{ RETVAL = THIS->process(str, 0); %};
|
%code%{ RETVAL = THIS->process(str, 0); %};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue