2014-05-06 11:07:18 +03:00
#include "PlaceholderParser.hpp"
2015-12-08 11:01:12 +01:00
#include <cstring>
2014-11-09 20:41:27 +01:00
#include <ctime>
#include <iomanip>
#include <sstream>
2017-12-21 17:07:57 +01:00
#include <map>
2016-08-21 21:46:17 +02:00
#ifdef _MSC_VER
#include <stdlib.h> // provides **_environ
#else
#include <unistd.h> // provides **environ
#endif
2015-07-01 17:56:38 +02:00
2016-04-11 12:04:54 +02:00
#ifdef __APPLE__
#include <crt_externs.h>
#undef environ
#define environ (*_NSGetEnviron())
#else
2016-08-21 21:46:17 +02:00
#ifdef _MSC_VER
#define environ _environ
#else
extern char ** environ ;
#endif
2016-04-11 12:04:54 +02:00
#endif
2014-05-06 11:07:18 +03:00
2017-11-26 09:59:14 +01:00
#include <boost/algorithm/string.hpp>
2017-11-17 11:15:46 +01:00
// 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).
2017-11-26 10:54:54 +01:00
//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
2017-11-17 11:15:46 +01:00
2017-11-26 09:59:14 +01:00
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
2017-11-17 11:15:46 +01:00
#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>
2017-11-26 10:54:54 +01:00
#include <boost/spirit/repository/include/qi_distinct.hpp>
2017-11-26 09:59:14 +01:00
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
2017-11-17 11:15:46 +01:00
#include <boost/variant/recursive_variant.hpp>
#include <boost/phoenix/bind/bind_function.hpp>
#include <iostream>
#include <string>
2017-12-20 21:54:47 +01:00
// #define USE_CPP11_REGEX
#ifdef USE_CPP11_REGEX
#include <regex>
#define SLIC3R_REGEX_NAMESPACE std
#else /* USE_CPP11_REGEX */
#include <boost/regex.hpp>
#define SLIC3R_REGEX_NAMESPACE boost
#endif /* USE_CPP11_REGEX */
2014-05-06 11:07:18 +03:00
namespace Slic3r {
2019-07-24 12:39:01 +02:00
PlaceholderParser :: PlaceholderParser ( const DynamicConfig * external_config ) : m_external_config ( external_config )
2014-05-06 11:07:18 +03:00
{
2017-12-11 09:31:29 +01:00
this -> set ( "version" , std :: string ( SLIC3R_VERSION ));
2015-07-01 17:56:38 +02:00
this -> apply_env_variables ();
2014-11-09 20:41:27 +01:00
this -> update_timestamp ();
2014-05-06 11:07:18 +03:00
}
2018-09-11 14:04:47 +02:00
void PlaceholderParser :: update_timestamp ( DynamicConfig & config )
2014-05-06 11:07:18 +03:00
{
2014-11-09 20:41:27 +01:00
time_t rawtime ;
time ( & rawtime );
struct tm * timeinfo = localtime ( & rawtime );
{
std :: ostringstream ss ;
ss << ( 1900 + timeinfo -> tm_year );
ss << std :: setw ( 2 ) << std :: setfill ( '0' ) << ( 1 + timeinfo -> tm_mon );
ss << std :: setw ( 2 ) << std :: setfill ( '0' ) << timeinfo -> tm_mday ;
ss << "-" ;
ss << std :: setw ( 2 ) << std :: setfill ( '0' ) << timeinfo -> tm_hour ;
ss << std :: setw ( 2 ) << std :: setfill ( '0' ) << timeinfo -> tm_min ;
ss << std :: setw ( 2 ) << std :: setfill ( '0' ) << timeinfo -> tm_sec ;
2018-09-11 14:04:47 +02:00
config . set_key_value ( "timestamp" , new ConfigOptionString ( ss . str ()));
2014-11-09 20:41:27 +01:00
}
2018-09-11 14:04:47 +02:00
config . set_key_value ( "year" , new ConfigOptionInt ( 1900 + timeinfo -> tm_year ));
config . set_key_value ( "month" , new ConfigOptionInt ( 1 + timeinfo -> tm_mon ));
config . set_key_value ( "day" , new ConfigOptionInt ( timeinfo -> tm_mday ));
config . set_key_value ( "hour" , new ConfigOptionInt ( timeinfo -> tm_hour ));
config . set_key_value ( "minute" , new ConfigOptionInt ( timeinfo -> tm_min ));
config . set_key_value ( "second" , new ConfigOptionInt ( timeinfo -> tm_sec ));
2014-05-06 11:07:18 +03:00
}
2018-12-03 13:14:28 +01:00
static inline bool opts_equal ( const DynamicConfig & config_old , const DynamicConfig & config_new , const std :: string & opt_key )
{
const ConfigOption * opt_old = config_old . option ( opt_key );
const ConfigOption * opt_new = config_new . option ( opt_key );
assert ( opt_new != nullptr );
if ( opt_old == nullptr )
return false ;
return ( opt_new -> type () == coFloatOrPercent ) ?
dynamic_cast < const ConfigOptionFloat *> ( opt_old ) -> value == config_new . get_abs_value ( opt_key ) :
* opt_new == * opt_old ;
}
std :: vector < std :: string > PlaceholderParser :: config_diff ( const DynamicPrintConfig & rhs )
{
std :: vector < std :: string > diff_keys ;
for ( const t_config_option_key & opt_key : rhs . keys ())
2019-07-25 14:39:19 +02:00
if ( ! opts_equal ( m_config , rhs , opt_key ))
2018-12-03 13:14:28 +01:00
diff_keys . emplace_back ( opt_key );
return diff_keys ;
}
2017-07-31 15:42:55 +02:00
// Scalar configuration values are stored into m_single,
// vector configuration values are stored into m_multiple.
// All vector configuration values stored into the PlaceholderParser
// 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.
2018-10-23 22:53:43 +02:00
bool PlaceholderParser :: apply_config ( const DynamicPrintConfig & rhs )
2014-06-13 16:32:11 +03:00
{
2018-10-23 22:53:43 +02:00
bool modified = false ;
2017-11-17 11:15:46 +01:00
for ( const t_config_option_key & opt_key : rhs . keys ()) {
2018-12-03 13:14:28 +01:00
if ( ! opts_equal ( m_config , rhs , 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.
const ConfigOption * opt_rhs = rhs . option ( opt_key );
this -> set ( opt_key , ( opt_rhs -> type () == coFloatOrPercent ) ?
new ConfigOptionFloat ( rhs . get_abs_value ( opt_key )) :
opt_rhs -> clone ());
modified = true ;
2018-10-23 22:53:43 +02:00
}
2018-12-03 13:14:28 +01:00
}
return modified ;
}
void PlaceholderParser :: apply_only ( const DynamicPrintConfig & rhs , const std :: vector < std :: string > & keys )
{
for ( const t_config_option_key & opt_key : keys ) {
2017-11-17 11:15:46 +01:00
// 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.
2018-12-03 13:14:28 +01:00
const ConfigOption * opt_rhs = rhs . option ( opt_key );
2018-10-23 22:53:43 +02:00
this -> set ( opt_key , ( opt_rhs -> type () == coFloatOrPercent ) ?
2017-11-17 11:15:46 +01:00
new ConfigOptionFloat ( rhs . get_abs_value ( opt_key )) :
2018-10-23 22:53:43 +02:00
opt_rhs -> clone ());
2014-06-13 16:32:11 +03:00
}
}
2019-07-25 14:39:19 +02:00
void PlaceholderParser :: apply_config ( DynamicPrintConfig && rhs )
{
m_config += std :: move ( rhs );
}
2017-07-31 15:42:55 +02:00
void PlaceholderParser :: apply_env_variables ()
2015-07-01 17:56:38 +02:00
{
2017-11-17 11:15:46 +01:00
for ( char ** env = environ ; * env ; ++ env ) {
2015-07-01 17:56:38 +02:00
if ( strncmp ( * env , "SLIC3R_" , 7 ) == 0 ) {
std :: stringstream ss ( * env );
std :: string key , value ;
std :: getline ( ss , key , '=' );
ss >> value ;
this -> set ( key , value );
}
}
}
2017-11-26 09:59:14 +01:00
namespace spirit = boost :: spirit ;
2019-08-08 14:21:24 +02:00
// Using an encoding, which accepts unsigned chars.
// Don't use boost::spirit::ascii, as it crashes internally due to indexing with negative char values for UTF8 characters into some 7bit character classification tables.
//namespace spirit_encoding = boost::spirit::ascii;
//FIXME iso8859_1 is just a workaround for the problem above. Replace it with UTF8 support!
namespace spirit_encoding = boost :: spirit :: iso8859_1 ;
2017-11-26 09:59:14 +01:00
namespace qi = boost :: spirit :: qi ;
namespace px = boost :: phoenix ;
2017-11-17 11:15:46 +01:00
namespace client
2014-11-09 20:41:27 +01:00
{
2017-11-17 18:46:03 +01:00
template < typename Iterator >
struct OptWithPos {
OptWithPos () {}
OptWithPos ( ConfigOptionConstPtr opt , boost :: iterator_range < Iterator > it_range ) : opt ( opt ), it_range ( it_range ) {}
ConfigOptionConstPtr opt = nullptr ;
boost :: iterator_range < Iterator > it_range ;
};
2017-11-26 09:59:14 +01:00
template < typename ITERATOR >
std :: ostream & operator << ( std :: ostream & os , OptWithPos < ITERATOR > const & opt )
{
os << std :: string ( opt . it_range . begin (), opt . it_range . end ());
return os ;
}
2017-11-17 18:46:03 +01:00
template < typename Iterator >
struct expr
{
2017-11-29 20:38:19 +01:00
expr () : type ( TYPE_EMPTY ) {}
2017-11-26 09:59:14 +01:00
explicit expr ( bool b ) : type ( TYPE_BOOL ) { data . b = b ; }
explicit expr ( bool b , const Iterator & it_begin , const Iterator & it_end ) : type ( TYPE_BOOL ), it_range ( it_begin , it_end ) { data . b = b ; }
explicit expr ( int i ) : type ( TYPE_INT ) { data . i = i ; }
explicit expr ( int i , const Iterator & it_begin , const Iterator & it_end ) : type ( TYPE_INT ), it_range ( it_begin , it_end ) { data . i = i ; }
explicit expr ( double d ) : type ( TYPE_DOUBLE ) { data . d = d ; }
explicit expr ( double d , const Iterator & it_begin , const Iterator & it_end ) : type ( TYPE_DOUBLE ), it_range ( it_begin , it_end ) { data . d = d ; }
explicit expr ( const char * s ) : type ( TYPE_STRING ) { data . s = new std :: string ( s ); }
explicit expr ( const std :: string & s ) : type ( TYPE_STRING ) { data . s = new std :: string ( s ); }
explicit expr ( const std :: string & s , const Iterator & it_begin , const Iterator & it_end ) :
type ( TYPE_STRING ), it_range ( it_begin , it_end ) { data . s = new std :: string ( s ); }
2017-11-29 19:27:26 +01:00
expr ( const expr & rhs ) : type ( rhs . type ), it_range ( rhs . it_range )
{ if ( rhs . type == TYPE_STRING ) data . s = new std :: string ( * rhs . data . s ); else data . set ( rhs . data ); }
explicit expr ( expr && rhs ) : type ( rhs . type ), it_range ( rhs . it_range )
{ data . set ( rhs . data ); rhs . type = TYPE_EMPTY ; }
2017-11-26 09:59:14 +01:00
explicit expr ( expr && rhs , const Iterator & it_begin , const Iterator & it_end ) : type ( rhs . type ), it_range ( it_begin , it_end )
2017-11-29 19:27:26 +01:00
{ data . set ( rhs . data ); rhs . type = TYPE_EMPTY ; }
2017-11-26 09:59:14 +01:00
~ expr () { this -> reset (); }
2017-11-17 18:46:03 +01:00
expr & operator = ( const expr & rhs )
{
2017-11-29 19:27:26 +01:00
this -> type = rhs . type ;
this -> it_range = rhs . it_range ;
if ( rhs . type == TYPE_STRING )
this -> data . s = new std :: string ( * rhs . data . s );
else
this -> data . set ( rhs . data );
2017-11-17 18:46:03 +01:00
return * this ;
}
expr & operator = ( expr && rhs )
{
2017-11-29 19:27:26 +01:00
type = rhs . type ;
this -> it_range = rhs . it_range ;
data . set ( rhs . data );
rhs . type = TYPE_EMPTY ;
return * this ;
2017-11-17 18:46:03 +01:00
}
2017-11-26 09:59:14 +01:00
void reset ()
{
if ( this -> type == TYPE_STRING )
delete data . s ;
this -> type = TYPE_EMPTY ;
}
2017-11-17 18:46:03 +01:00
bool & b () { return data . b ; }
bool b () const { return data . b ; }
2017-11-26 09:59:14 +01:00
void set_b ( bool v ) { this -> reset (); this -> data . b = v ; this -> type = TYPE_BOOL ; }
2017-11-17 18:46:03 +01:00
int & i () { return data . i ; }
int i () const { return data . i ; }
2017-11-26 09:59:14 +01:00
void set_i ( int v ) { this -> reset (); this -> data . i = v ; this -> type = TYPE_INT ; }
int as_i () const { return ( this -> type == TYPE_INT ) ? this -> i () : int ( this -> d ()); }
2017-11-17 18:46:03 +01:00
double & d () { return data . d ; }
double d () const { return data . d ; }
2017-11-26 09:59:14 +01:00
void set_d ( double v ) { this -> reset (); this -> data . d = v ; this -> type = TYPE_DOUBLE ; }
double as_d () const { return ( this -> type == TYPE_DOUBLE ) ? this -> d () : double ( this -> i ()); }
2017-11-17 18:46:03 +01:00
std :: string & s () { return * data . s ; }
const std :: string & s () const { return * data . s ; }
2017-11-26 09:59:14 +01:00
void set_s ( const std :: string & s ) { this -> reset (); this -> data . s = new std :: string ( s ); this -> type = TYPE_STRING ; }
void set_s ( std :: string && s ) { this -> reset (); this -> data . s = new std :: string ( std :: move ( s )); this -> type = TYPE_STRING ; }
2017-11-17 18:46:03 +01:00
std :: string to_string () const
{
std :: string out ;
switch ( type ) {
2019-01-18 09:50:56 +01:00
case TYPE_BOOL : out = data . b ? "true" : "false" ; break ;
2019-01-17 15:40:09 +01:00
case TYPE_INT : out = std :: to_string ( data . i ); break ;
2019-01-18 09:50:56 +01:00
case TYPE_DOUBLE :
#if 0
// The default converter produces trailing zeros after the decimal point.
out = std::to_string(data.d);
#else
// ostringstream default converter produces no trailing zeros after the decimal point.
// It seems to be doing what the old boost::to_string() did.
{
std :: ostringstream ss ;
ss << data . d ;
out = ss . str ();
}
#endif
break ;
2017-11-17 18:46:03 +01:00
case TYPE_STRING : out = * data . s ; break ;
2017-11-29 19:27:26 +01:00
default : break ;
2017-11-17 18:46:03 +01:00
}
return out ;
}
2017-11-29 19:27:26 +01:00
union Data {
// Raw image of the other data members.
// The C++ compiler will consider a possible aliasing of char* with any other union member,
// therefore copying the raw data is safe.
char raw [ 8 ];
2017-11-17 18:46:03 +01:00
bool b ;
int i ;
double d ;
std :: string * s ;
2017-11-29 19:27:26 +01:00
// Copy the largest member variable through char*, which will alias with all other union members by default.
void set ( const Data & rhs ) { memcpy ( this -> raw , rhs . raw , sizeof ( rhs . raw )); }
2017-11-17 18:46:03 +01:00
} data ;
enum Type {
TYPE_EMPTY = 0 ,
TYPE_BOOL ,
TYPE_INT ,
TYPE_DOUBLE ,
TYPE_STRING ,
};
2017-11-26 09:59:14 +01:00
Type type ;
2017-11-17 18:46:03 +01:00
// Range of input iterators covering this expression.
// Used for throwing parse exceptions.
boost :: iterator_range < Iterator > it_range ;
2017-11-26 10:54:54 +01:00
expr unary_minus ( const Iterator start_pos ) const
2017-11-26 09:59:14 +01:00
{
switch ( this -> type ) {
case TYPE_INT :
return expr < Iterator > ( - this -> i (), start_pos , this -> it_range . end ());
case TYPE_DOUBLE :
return expr < Iterator > ( - this -> d (), start_pos , this -> it_range . end ());
default :
this -> throw_exception ( "Cannot apply unary minus operator." );
}
assert ( false );
// Suppress compiler warnings.
return expr ();
2017-11-17 18:46:03 +01:00
}
2017-11-26 09:59:14 +01:00
2017-11-26 11:16:28 +01:00
expr unary_not ( const Iterator start_pos ) const
{
switch ( this -> type ) {
case TYPE_BOOL :
return expr < Iterator > ( ! this -> b (), start_pos , this -> it_range . end ());
default :
this -> throw_exception ( "Cannot apply a not operator." );
}
assert ( false );
// Suppress compiler warnings.
return expr ();
}
2017-11-26 09:59:14 +01:00
expr & operator += ( const expr & rhs )
{
2017-12-05 17:38:29 +01:00
if ( this -> type == TYPE_STRING ) {
// Convert the right hand side to string and append.
* this -> data . s += rhs . to_string ();
} else if ( rhs . type == TYPE_STRING ) {
// Conver the left hand side to string, append rhs.
this -> data . s = new std :: string ( this -> to_string () + rhs . s ());
this -> type = TYPE_STRING ;
} else {
const char * err_msg = "Cannot add non-numeric types." ;
this -> throw_if_not_numeric ( err_msg );
rhs . throw_if_not_numeric ( err_msg );
if ( this -> type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) {
double d = this -> as_d () + rhs . as_d ();
this -> data . d = d ;
this -> type = TYPE_DOUBLE ;
} else
this -> data . i += rhs . i ();
}
2017-11-26 09:59:14 +01:00
this -> it_range = boost :: iterator_range < Iterator > ( this -> it_range . begin (), rhs . it_range . end ());
return * this ;
}
expr & operator -= ( const expr & rhs )
{
2017-12-05 17:38:29 +01:00
const char * err_msg = "Cannot subtract non-numeric types." ;
2017-11-26 09:59:14 +01:00
this -> throw_if_not_numeric ( err_msg );
rhs . throw_if_not_numeric ( err_msg );
if ( this -> type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) {
double d = this -> as_d () - rhs . as_d ();
this -> data . d = d ;
this -> type = TYPE_DOUBLE ;
} else
this -> data . i -= rhs . i ();
this -> it_range = boost :: iterator_range < Iterator > ( this -> it_range . begin (), rhs . it_range . end ());
return * this ;
}
expr & operator *= ( const expr & rhs )
{
const char * err_msg = "Cannot multiply with non-numeric type." ;
this -> throw_if_not_numeric ( err_msg );
rhs . throw_if_not_numeric ( err_msg );
if ( this -> type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) {
double d = this -> as_d () * rhs . as_d ();
this -> data . d = d ;
this -> type = TYPE_DOUBLE ;
} else
this -> data . i *= rhs . i ();
this -> it_range = boost :: iterator_range < Iterator > ( this -> it_range . begin (), rhs . it_range . end ());
return * this ;
}
expr & operator /= ( const expr & rhs )
{
this -> throw_if_not_numeric ( "Cannot divide a non-numeric type." );
rhs . throw_if_not_numeric ( "Cannot divide with a non-numeric type." );
if (( this -> type == TYPE_INT ) ? ( rhs . i () == 0 ) : ( rhs . d () == 0. ))
rhs . throw_exception ( "Division by zero" );
if ( this -> type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) {
double d = this -> as_d () / rhs . as_d ();
this -> data . d = d ;
this -> type = TYPE_DOUBLE ;
} else
this -> data . i /= rhs . i ();
this -> it_range = boost :: iterator_range < Iterator > ( this -> it_range . begin (), rhs . it_range . end ());
return * this ;
}
2017-11-17 18:46:03 +01:00
static void to_string2 ( expr & self , std :: string & out )
{
out = self . to_string ();
}
2017-11-26 09:59:14 +01:00
2017-11-17 18:46:03 +01:00
static void evaluate_boolean ( expr & self , bool & out )
{
if ( self . type != TYPE_BOOL )
2017-11-26 09:59:14 +01:00
self . throw_exception ( "Not a boolean expression" );
2017-11-17 18:46:03 +01:00
out = self . b ();
}
2017-11-26 09:59:14 +01:00
2017-12-18 12:14:09 +01:00
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" ;
}
2017-11-26 09:59:14 +01:00
// Is lhs==rhs? Store the result into lhs.
2017-12-19 16:48:14 +01:00
static void compare_op ( expr & lhs , expr & rhs , char op , bool invert )
2017-11-26 09:59:14 +01:00
{
bool value = false ;
if (( lhs . type == TYPE_INT || lhs . type == TYPE_DOUBLE ) &&
( rhs . type == TYPE_INT || rhs . type == TYPE_DOUBLE )) {
// Both types are numeric.
2017-12-19 16:48:14 +01:00
switch ( op ) {
case '=' :
value = ( lhs . type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) ?
( std :: abs ( lhs . as_d () - rhs . as_d ()) < 1e-8 ) : ( lhs . i () == rhs . i ());
break ;
case '<' :
value = ( lhs . type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) ?
( lhs . as_d () < rhs . as_d ()) : ( lhs . i () < rhs . i ());
break ;
case '>' :
default :
value = ( lhs . type == TYPE_DOUBLE || rhs . type == TYPE_DOUBLE ) ?
( lhs . as_d () > rhs . as_d ()) : ( lhs . i () > rhs . i ());
break ;
}
2017-11-26 09:59:14 +01:00
} else if ( lhs . type == TYPE_BOOL && rhs . type == TYPE_BOOL ) {
// Both type are bool.
2017-12-19 16:48:14 +01:00
if ( op != '=' )
boost :: throw_exception ( qi :: expectation_failure < Iterator > (
lhs . it_range . begin (), rhs . it_range . end (), spirit :: info ( "*Cannot compare the types." )));
2017-11-26 09:59:14 +01:00
value = lhs . b () == rhs . b ();
} else if ( lhs . type == TYPE_STRING || rhs . type == TYPE_STRING ) {
// One type is string, the other could be converted to string.
2017-12-19 16:48:14 +01:00
value = ( op == '=' ) ? ( lhs . to_string () == rhs . to_string ()) :
( op == '<' ) ? ( lhs . to_string () < rhs . to_string ()) : ( lhs . to_string () > rhs . to_string ());
2017-11-26 09:59:14 +01:00
} else {
boost :: throw_exception ( qi :: expectation_failure < Iterator > (
2017-12-05 15:54:24 +01:00
lhs . it_range . begin (), rhs . it_range . end (), spirit :: info ( "*Cannot compare the types." )));
2017-11-26 09:59:14 +01:00
}
lhs . type = TYPE_BOOL ;
2017-12-19 16:48:14 +01:00
lhs . data . b = invert ? ! value : value ;
2017-11-26 09:59:14 +01:00
}
2018-05-15 14:04:29 +02:00
// Compare operators, store the result into lhs.
2017-12-19 16:48:14 +01:00
static void equal ( expr & lhs , expr & rhs ) { compare_op ( lhs , rhs , '=' , false ); }
static void not_equal ( expr & lhs , expr & rhs ) { compare_op ( lhs , rhs , '=' , true ); }
static void lower ( expr & lhs , expr & rhs ) { compare_op ( lhs , rhs , '<' , false ); }
static void greater ( expr & lhs , expr & rhs ) { compare_op ( lhs , rhs , '>' , false ); }
static void leq ( expr & lhs , expr & rhs ) { compare_op ( lhs , rhs , '>' , true ); }
static void geq ( expr & lhs , expr & rhs ) { compare_op ( lhs , rhs , '<' , true ); }
2017-11-26 09:59:14 +01:00
2018-05-15 14:04:29 +02:00
enum Function2ParamsType {
FUNCTION_MIN ,
FUNCTION_MAX ,
};
// Store the result into param1.
static void function_2params ( expr & param1 , expr & param2 , Function2ParamsType fun )
{
const char * err_msg = "Not a numeric type." ;
param1 . throw_if_not_numeric ( err_msg );
param2 . throw_if_not_numeric ( err_msg );
if ( param1 . type == TYPE_DOUBLE || param2 . type == TYPE_DOUBLE ) {
double d = 0. ;
switch ( fun ) {
case FUNCTION_MIN : d = std :: min ( param1 . as_d (), param2 . as_d ()); break ;
case FUNCTION_MAX : d = std :: max ( param1 . as_d (), param2 . as_d ()); break ;
default : param1 . throw_exception ( "Internal error: invalid function" );
}
param1 . data . d = d ;
param1 . type = TYPE_DOUBLE ;
} else {
2018-11-09 12:02:42 +01:00
int i = 0 ;
2018-05-15 14:04:29 +02:00
switch ( fun ) {
case FUNCTION_MIN : i = std :: min ( param1 . as_i (), param2 . as_i ()); break ;
case FUNCTION_MAX : i = std :: max ( param1 . as_i (), param2 . as_i ()); break ;
default : param1 . throw_exception ( "Internal error: invalid function" );
}
param1 . data . i = i ;
param1 . type = TYPE_INT ;
}
}
// Store the result into param1.
static void min ( expr & param1 , expr & param2 ) { function_2params ( param1 , param2 , FUNCTION_MIN ); }
static void max ( expr & param1 , expr & param2 ) { function_2params ( param1 , param2 , FUNCTION_MAX ); }
2017-12-15 17:14:24 +01:00
static void regex_op ( expr & lhs , boost :: iterator_range < Iterator > & rhs , char op )
{
const std :: string * subject = nullptr ;
if ( lhs . type == TYPE_STRING ) {
// One type is string, the other could be converted to string.
subject = & lhs . s ();
} else {
lhs . throw_exception ( "Left hand side of a regex match must be a string." );
}
try {
std :: string pattern ( ++ rhs . begin (), -- rhs . end ());
2017-12-20 21:54:47 +01:00
bool result = SLIC3R_REGEX_NAMESPACE :: regex_match ( * subject , SLIC3R_REGEX_NAMESPACE :: regex ( pattern ));
2017-12-15 17:14:24 +01:00
if ( op == '!' )
result = ! result ;
lhs . reset ();
lhs . type = TYPE_BOOL ;
lhs . data . b = result ;
2017-12-20 21:54:47 +01:00
} catch ( SLIC3R_REGEX_NAMESPACE :: regex_error & ex ) {
2017-12-15 17:14:24 +01:00
// Syntax error in the regular expression
boost :: throw_exception ( qi :: expectation_failure < Iterator > (
rhs . begin (), rhs . end (), spirit :: info ( std :: string ( "*Regular expression compilation failed: " ) + ex . what ())));
}
}
static void regex_matches ( expr & lhs , boost :: iterator_range < Iterator > & rhs ) { return regex_op ( lhs , rhs , '=' ); }
static void regex_doesnt_match ( expr & lhs , boost :: iterator_range < Iterator > & rhs ) { return regex_op ( lhs , rhs , '!' ); }
2017-12-19 16:48:14 +01:00
static void logical_op ( expr & lhs , expr & rhs , char op )
{
bool value = false ;
if ( lhs . type == TYPE_BOOL && rhs . type == TYPE_BOOL ) {
value = ( op == '|' ) ? ( lhs . b () || rhs . b ()) : ( lhs . b () && rhs . b ());
} else {
boost :: throw_exception ( qi :: expectation_failure < Iterator > (
lhs . it_range . begin (), rhs . it_range . end (), spirit :: info ( "*Cannot apply logical operation to non-boolean operators." )));
}
lhs . type = TYPE_BOOL ;
lhs . data . b = value ;
}
static void logical_or ( expr & lhs , expr & rhs ) { logical_op ( lhs , rhs , '|' ); }
static void logical_and ( expr & lhs , expr & rhs ) { logical_op ( lhs , rhs , '&' ); }
static void ternary_op ( expr & lhs , expr & rhs1 , expr & rhs2 )
{
if ( lhs . type != TYPE_BOOL )
lhs . throw_exception ( "Not a boolean expression" );
if ( lhs . b ())
lhs = std :: move ( rhs1 );
else
lhs = std :: move ( rhs2 );
}
2017-11-17 18:46:03 +01:00
static void set_if ( bool & cond , bool & not_yet_consumed , std :: string & str_in , std :: string & str_out )
{
if ( cond && not_yet_consumed ) {
str_out = str_in ;
not_yet_consumed = false ;
}
}
2017-11-26 09:59:14 +01:00
void throw_exception ( const char * message ) const
{
boost :: throw_exception ( qi :: expectation_failure < Iterator > (
2017-12-05 15:54:24 +01:00
this -> it_range . begin (), this -> it_range . end (), spirit :: info ( std :: string ( "*" ) + message )));
2017-11-26 09:59:14 +01:00
}
void throw_if_not_numeric ( const char * message ) const
{
if ( this -> type != TYPE_INT && this -> type != TYPE_DOUBLE )
this -> throw_exception ( message );
}
2017-11-17 18:46:03 +01:00
};
2017-11-26 09:59:14 +01:00
template < typename ITERATOR >
std :: ostream & operator << ( std :: ostream & os , const expr < ITERATOR > & expression )
{
typedef expr < ITERATOR > Expr ;
2017-11-29 19:27:26 +01:00
os << std :: string ( expression . it_range . begin (), expression . it_range . end ()) << " - " ;
2017-11-26 09:59:14 +01:00
switch ( expression . type ) {
case Expr :: TYPE_EMPTY : os << "empty" ; break ;
case Expr :: TYPE_BOOL : os << "bool (" << expression . b () << ")" ; break ;
case Expr :: TYPE_INT : os << "int (" << expression . i () << ")" ; break ;
case Expr :: TYPE_DOUBLE : os << "double (" << expression . d () << ")" ; break ;
case Expr :: TYPE_STRING : os << "string (" << expression . s () << ")" ; break ;
default : os << "unknown" ;
};
return os ;
}
2017-11-17 11:15:46 +01:00
struct MyContext {
2019-07-24 12:39:01 +02:00
const DynamicConfig * external_config = nullptr ;
2017-12-18 12:14:09 +01:00
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 ;
2017-12-05 15:54:24 +01:00
std :: string error_message ;
2014-11-09 20:41:27 +01:00
2017-12-21 17:07:57 +01:00
// Table to translate symbol tag to a human readable error message.
static std :: map < std :: string , std :: string > tag_to_error_message ;
2017-12-18 12:14:09 +01:00
static void evaluate_full_macro ( const MyContext * ctx , bool & result ) { result = ! ctx -> just_boolean_expression ; }
2017-11-17 11:15:46 +01:00
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 )
2017-12-18 12:14:09 +01:00
opt = config -> option ( opt_key );
2019-07-24 12:39:01 +02:00
if ( opt == nullptr && external_config != nullptr )
opt = external_config -> option ( opt_key );
2017-11-17 11:15:46 +01:00
return opt ;
2017-07-31 15:42:55 +02:00
}
2017-11-17 11:15:46 +01:00
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 ())
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Trying to index a scalar variable" , opt_key );
2017-11-17 11:15:46 +01:00
char * endptr = nullptr ;
idx = strtol ( opt_key_str . c_str () + idx + 1 , & endptr , 10 );
if ( endptr == nullptr || * endptr != 0 )
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Invalid vector index" , boost :: iterator_range < Iterator > ( opt_key . begin () + idx + 1 , opt_key . end ()));
2017-11-17 11:15:46 +01:00
}
2017-07-31 15:42:55 +02:00
}
2015-07-01 19:35:22 +02:00
}
2017-11-17 11:15:46 +01:00
if ( opt == nullptr )
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Variable does not exist" , boost :: iterator_range < Iterator > ( opt_key . begin (), opt_key . end ()));
2017-11-17 11:15:46 +01:00
if ( opt -> is_scalar ())
output = opt -> serialize ();
else {
const ConfigOptionVectorBase * vec = static_cast < const ConfigOptionVectorBase *> ( opt );
if ( vec -> empty ())
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Indexing an empty vector variable" , opt_key );
2017-11-17 11:15:46 +01:00
output = vec -> vserialize ()[( idx >= vec -> size ()) ? 0 : idx ];
}
2015-05-02 21:43:22 +02:00
}
2017-11-17 11:15:46 +01:00
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 ())
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Trying to index a scalar variable" , opt_key );
2017-11-17 11:15:46 +01:00
const ConfigOptionVectorBase * vec = static_cast < const ConfigOptionVectorBase *> ( opt );
if ( vec -> empty ())
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Indexing an empty vector variable" , boost :: iterator_range < Iterator > ( opt_key . begin (), opt_key . end ()));
2017-11-17 11:15:46 +01:00
const ConfigOption * opt_index = ctx -> resolve_symbol ( std :: string ( opt_vector_index . begin (), opt_vector_index . end ()));
if ( opt_index == nullptr )
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Variable does not exist" , opt_key );
2017-11-17 11:15:46 +01:00
if ( opt_index -> type () != coInt )
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Indexing variable has to be integer" , opt_key );
2017-11-17 11:15:46 +01:00
int idx = opt_index -> getInt ();
if ( idx < 0 )
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Negative vector index" , opt_key );
2017-11-17 11:15:46 +01:00
output = vec -> vserialize ()[( idx >= ( int ) vec -> size ()) ? 0 : idx ];
}
2017-11-17 18:46:03 +01:00
template < typename Iterator >
static void resolve_variable (
const MyContext * ctx ,
boost :: iterator_range < Iterator > & opt_key ,
OptWithPos < Iterator > & output )
2017-11-17 11:15:46 +01:00
{
2017-11-17 18:46:03 +01:00
const ConfigOption * opt = ctx -> resolve_symbol ( std :: string ( opt_key . begin (), opt_key . end ()));
if ( opt == nullptr )
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Not a variable name" , opt_key );
2017-11-17 18:46:03 +01:00
output . opt = opt ;
output . it_range = opt_key ;
2017-11-17 11:15:46 +01:00
}
2017-11-17 18:46:03 +01:00
template < typename Iterator >
static void scalar_variable_reference (
const MyContext * ctx ,
OptWithPos < Iterator > & opt ,
expr < Iterator > & output )
{
if ( opt . opt -> is_vector ())
2018-02-16 17:27:50 +01:00
ctx -> throw_exception ( "Referencing a vector variable when scalar is expected" , opt . it_range );
2017-11-17 18:46:03 +01:00
switch ( opt . opt -> type ()) {
2017-11-26 09:59:14 +01:00
case coFloat : output . set_d ( opt . opt -> getFloat ()); break ;
case coInt : output . set_i ( opt . opt -> getInt ()); break ;
case coString : output . set_s ( static_cast < const ConfigOptionString *> ( opt . opt ) -> value ); break ;
case coPercent : output . set_d ( opt . opt -> getFloat ()); break ;
case coPoint : output . set_s ( opt . opt -> serialize ()); break ;
case coBool : output . set_b ( opt . opt -> getBool ()); break ;
2017-11-17 18:46:03 +01:00
case coFloatOrPercent :
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "FloatOrPercent variables are not supported" , opt . it_range );
2017-11-17 18:46:03 +01:00
default :
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Unknown scalar variable type" , opt . it_range );
2017-11-17 18:46:03 +01:00
}
2017-11-26 09:59:14 +01:00
output . it_range = opt . it_range ;
2017-11-17 18:46:03 +01:00
}
2017-11-17 11:15:46 +01:00
2017-11-17 18:46:03 +01:00
template < typename Iterator >
static void vector_variable_reference (
const MyContext * ctx ,
OptWithPos < Iterator > & opt ,
2017-11-26 09:59:14 +01:00
int & index ,
2017-11-17 18:46:03 +01:00
Iterator it_end ,
expr < Iterator > & output )
{
if ( opt . opt -> is_scalar ())
2018-02-16 17:27:50 +01:00
ctx -> throw_exception ( "Referencing a scalar variable when vector is expected" , opt . it_range );
2017-11-17 18:46:03 +01:00
const ConfigOptionVectorBase * vec = static_cast < const ConfigOptionVectorBase *> ( opt . opt );
if ( vec -> empty ())
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Indexing an empty vector variable" , opt . it_range );
2017-11-26 09:59:14 +01:00
size_t idx = ( index < 0 ) ? 0 : ( index >= int ( vec -> size ())) ? 0 : size_t ( index );
2017-11-17 18:46:03 +01:00
switch ( opt . opt -> type ()) {
2017-11-26 09:59:14 +01:00
case coFloats : output . set_d ( static_cast < const ConfigOptionFloats *> ( opt . opt ) -> values [ idx ]); break ;
case coInts : output . set_i ( static_cast < const ConfigOptionInts *> ( opt . opt ) -> values [ idx ]); break ;
case coStrings : output . set_s ( static_cast < const ConfigOptionStrings *> ( opt . opt ) -> values [ idx ]); break ;
case coPercents : output . set_d ( static_cast < const ConfigOptionPercents *> ( opt . opt ) -> values [ idx ]); break ;
2018-08-17 16:54:07 +02:00
case coPoints : output . set_s ( to_string ( static_cast < const ConfigOptionPoints *> ( opt . opt ) -> values [ idx ])); break ;
2017-11-26 09:59:14 +01:00
case coBools : output . set_b ( static_cast < const ConfigOptionBools *> ( opt . opt ) -> values [ idx ] != 0 ); break ;
2017-11-17 18:46:03 +01:00
default :
2017-12-05 15:54:24 +01:00
ctx -> throw_exception ( "Unknown vector variable type" , opt . it_range );
2017-11-17 18:46:03 +01:00
}
output . it_range = boost :: iterator_range < Iterator > ( opt . it_range . begin (), it_end );
}
2017-11-17 11:15:46 +01:00
2017-11-26 09:59:14 +01:00
// Verify that the expression returns an integer, which may be used
// to address a vector.
2017-11-17 18:46:03 +01:00
template < typename Iterator >
2017-11-26 09:59:14 +01:00
static void evaluate_index ( expr < Iterator > & expr_index , int & output )
2017-11-17 18:46:03 +01:00
{
2017-12-05 15:54:24 +01:00
if ( expr_index . type != expr < Iterator >:: TYPE_INT )
2017-11-26 09:59:14 +01:00
expr_index . throw_exception ( "Non-integer index is not allowed to address a vector variable." );
output = expr_index . i ();
2017-11-17 18:46:03 +01:00
}
2017-12-05 15:54:24 +01:00
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
// between the grammer terminal / non-terminal symbol name and a free-form error message.
boost :: throw_exception ( qi :: expectation_failure < Iterator > ( it_range . begin (), it_range . end (), spirit :: info ( std :: string ( "*" ) + msg )));
}
template < typename Iterator >
2017-12-05 17:38:29 +01:00
static void process_error_message ( const MyContext * context , const boost :: spirit :: info & info , const Iterator & it_begin , const Iterator & it_end , const Iterator & it_error )
2017-12-05 15:54:24 +01:00
{
std :: string & msg = const_cast < MyContext *> ( context ) -> error_message ;
2017-12-05 17:38:29 +01:00
std :: string first ( it_begin , it_error );
std :: string last ( it_error , it_end );
auto first_pos = first . rfind ( '\n' );
auto last_pos = last . find ( '\n' );
int line_nr = 1 ;
if ( first_pos == std :: string :: npos )
first_pos = 0 ;
else {
// Calculate the current line number.
for ( size_t i = 0 ; i <= first_pos ; ++ i )
if ( first [ i ] == '\n' )
++ line_nr ;
++ first_pos ;
}
auto error_line = std :: string ( first , first_pos ) + std :: string ( last , 0 , last_pos );
// Position of the it_error from the start of its line.
auto error_pos = ( it_error - it_begin ) - first_pos ;
msg += "Parsing error at line " + std :: to_string ( line_nr );
if ( ! info . tag . empty () && info . tag . front () == '*' ) {
// The gat contains an explanatory string.
msg += ": " ;
msg += info . tag . substr ( 1 );
} else {
2017-12-21 17:07:57 +01:00
auto it = tag_to_error_message . find ( info . tag );
if ( it == tag_to_error_message . end ()) {
// A generic error report based on the nonterminal or terminal symbol name.
msg += ". Expecting tag " ;
msg += info . tag ;
} else {
// Use the human readable error message.
msg += ". " ;
msg + it -> second ;
}
2017-12-05 17:38:29 +01:00
}
msg += '\n' ;
msg += error_line ;
msg += '\n' ;
for ( size_t i = 0 ; i < error_pos ; ++ i )
msg += ' ' ;
msg += "^ \n " ;
2017-12-05 15:54:24 +01:00
}
2017-11-17 11:15:46 +01:00
};
2017-12-21 17:07:57 +01:00
// Table to translate symbol tag to a human readable error message.
std :: map < std :: string , std :: string > MyContext :: tag_to_error_message = {
{ "eoi" , "Unknown syntax error" },
{ "start" , "Unknown syntax error" },
{ "text" , "Invalid text." },
{ "text_block" , "Invalid text block." },
{ "macro" , "Invalid macro." },
{ "if_else_output" , "Not an {if}{else}{endif} macro." },
{ "switch_output" , "Not a {switch} macro." },
{ "legacy_variable_expansion" , "Expecting a legacy variable expansion format" },
{ "identifier" , "Expecting an identifier." },
{ "conditional_expression" , "Expecting a conditional expression." },
{ "logical_or_expression" , "Expecting a boolean expression." },
{ "logical_and_expression" , "Expecting a boolean expression." },
{ "equality_expression" , "Expecting an expression." },
{ "bool_expr_eval" , "Expecting a boolean expression." },
{ "relational_expression" , "Expecting an expression." },
{ "additive_expression" , "Expecting an expression." },
{ "multiplicative_expression" , "Expecting an expression." },
{ "unary_expression" , "Expecting an expression." },
{ "scalar_variable_reference" , "Expecting a scalar variable reference." },
{ "variable_reference" , "Expecting a variable reference." },
{ "regular_expression" , "Expecting a regular expression." }
};
2017-11-26 09:59:14 +01:00
// For debugging the boost::spirit parsers. Print out the string enclosed in it_range.
template < typename Iterator >
std :: ostream & operator << ( std :: ostream & os , const boost :: iterator_range < Iterator > & it_range )
{
os << std :: string ( it_range . begin (), it_range . end ());
return os ;
}
2017-11-26 10:54:54 +01:00
// Disable parsing int numbers (without decimals) and Inf/NaN symbols by the double parser.
struct strict_real_policies_without_nan_inf : public qi :: strict_real_policies < double >
{
template < typename It , typename Attr > static bool parse_nan ( It & , It const & , Attr & ) { return false ; }
template < typename It , typename Attr > static bool parse_inf ( It & , It const & , Attr & ) { return false ; }
};
2017-12-04 18:22:42 +01:00
// This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character.
// If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown.
struct utf8_char_skipper_parser : qi :: primitive_parser < utf8_char_skipper_parser >
2017-12-04 17:42:35 +01:00
{
// Define the attribute type exposed by this parser component
template < typename Context , typename Iterator >
struct attribute
{
2017-12-04 18:22:42 +01:00
typedef wchar_t type ;
};
2017-12-04 17:42:35 +01:00
// This function is called during the actual parsing process
template < typename Iterator , typename Context , typename Skipper , typename Attribute >
bool parse ( Iterator & first , Iterator const & last , Context & context , Skipper const & skipper , Attribute & attr ) const
{
2017-12-04 18:22:42 +01:00
// The skipper shall always be empty, any white space will be accepted.
// skip_over(first, last, skipper);
if ( first == last )
return false ;
// Iterator over the UTF-8 sequence.
auto it = first ;
// Read the first byte of the UTF-8 sequence.
unsigned char c = static_cast < boost :: uint8_t > ( * it ++ );
2017-12-05 17:52:12 +01:00
unsigned int cnt = 0 ;
2017-12-04 18:22:42 +01:00
// UTF-8 sequence must not start with a continuation character:
if (( c & 0xC0 ) == 0x80 )
goto err ;
// Skip high surrogate first if there is one.
// If the most significant bit with a zero in it is in position
// 8-N then there are N bytes in this UTF-8 sequence:
{
unsigned char mask = 0x80u ;
unsigned int result = 0 ;
while ( c & mask ) {
++ result ;
mask >>= 1 ;
}
cnt = ( result == 0 ) ? 1 : (( result > 4 ) ? 4 : result );
}
// Since we haven't read in a value, we need to validate the code points:
for ( -- cnt ; cnt > 0 ; -- cnt ) {
if ( it == last )
goto err ;
c = static_cast < boost :: uint8_t > ( * it ++ );
// We must have a continuation byte:
if ( cnt > 1 && ( c & 0xC0 ) != 0x80 )
goto err ;
}
first = it ;
return true ;
err :
2017-12-05 15:54:24 +01:00
MyContext :: throw_exception ( "Invalid utf8 sequence" , boost :: iterator_range < Iterator > ( first , last ));
return false ;
2017-12-04 18:22:42 +01:00
}
2017-12-04 17:42:35 +01:00
// This function is called during error handling to create a human readable string for the error context.
template < typename Context >
2017-12-04 18:22:42 +01:00
spirit :: info what ( Context & ) const
2017-12-04 17:42:35 +01:00
{
return spirit :: info ( "unicode_char" );
2017-12-04 18:22:42 +01:00
}
2017-12-04 17:42:35 +01:00
};
2017-11-17 11:15:46 +01:00
///////////////////////////////////////////////////////////////////////////
2017-12-18 12:14:09 +01:00
// Our macro_processor grammar
2017-11-17 11:15:46 +01:00
///////////////////////////////////////////////////////////////////////////
2017-11-29 19:27:26 +01:00
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
2017-11-17 11:15:46 +01:00
template < typename Iterator >
2019-08-08 14:21:24 +02:00
struct macro_processor : qi :: grammar < Iterator , std :: string ( const MyContext * ), qi :: locals < bool > , spirit_encoding :: space_type >
2017-11-17 11:15:46 +01:00
{
2017-12-18 12:14:09 +01:00
macro_processor () : macro_processor :: base_type ( start )
2017-11-17 11:15:46 +01:00
{
2017-11-26 10:54:54 +01:00
using namespace qi :: labels ;
2017-11-26 09:59:14 +01:00
qi :: alpha_type alpha ;
qi :: alnum_type alnum ;
qi :: eps_type eps ;
qi :: raw_type raw ;
qi :: lit_type lit ;
qi :: lexeme_type lexeme ;
qi :: no_skip_type no_skip ;
2017-11-26 10:54:54 +01:00
qi :: real_parser < double , strict_real_policies_without_nan_inf > strict_double ;
2019-08-08 14:21:24 +02:00
spirit_encoding :: char_type char_ ;
2017-12-04 18:22:42 +01:00
utf8_char_skipper_parser utf8char ;
2017-11-26 09:59:14 +01:00
spirit :: bool_type bool_ ;
spirit :: int_type int_ ;
spirit :: double_type double_ ;
2019-08-08 14:21:24 +02:00
spirit_encoding :: string_type string ;
2017-12-21 17:07:57 +01:00
spirit :: eoi_type eoi ;
spirit :: repository :: qi :: iter_pos_type iter_pos ;
2017-11-26 10:54:54 +01:00
auto kw = spirit :: repository :: qi :: distinct ( qi :: copy ( alnum | '_' ));
2017-11-17 11:15:46 +01:00
2017-11-26 09:59:14 +01:00
qi :: _val_type _val ;
qi :: _1_type _1 ;
qi :: _2_type _2 ;
2017-12-05 15:54:24 +01:00
qi :: _3_type _3 ;
qi :: _4_type _4 ;
2017-11-26 09:59:14 +01:00
qi :: _a_type _a ;
qi :: _b_type _b ;
qi :: _r1_type _r1 ;
2017-11-17 11:15:46 +01:00
// 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.
2017-12-18 12:14:09 +01:00
// 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 )] >
2019-06-25 13:06:04 +02:00
( ( eps ( _a == true ) > text_block ( _r1 ) [ _val = _1 ])
2017-12-19 16:48:14 +01:00
| conditional_expression ( _r1 ) [ px :: bind ( & expr < Iterator >:: evaluate_boolean_to_string , _1 , _val ) ]
2017-12-21 17:07:57 +01:00
) > eoi ;
2017-11-17 11:15:46 +01:00
start . name ( "start" );
2017-12-05 17:38:29 +01:00
qi :: on_error < qi :: fail > ( start , px :: bind ( & MyContext :: process_error_message < Iterator > , _r1 , _4 , _1 , _2 , _3 ));
2017-11-17 11:15:46 +01:00
2017-11-26 09:59:14 +01:00
text_block = * (
text [ _val += _1 ]
// Allow back tracking after '{' in case of a text_block embedded inside a condition.
// In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired.
// {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block.
2017-11-26 10:54:54 +01:00
| ( lit ( '{' ) >> macro ( _r1 ) [ _val += _1 ] > '}' )
| ( lit ( '[' ) > legacy_variable_expansion ( _r1 ) [ _val += _1 ] > ']' )
2017-11-26 09:59:14 +01:00
);
text_block . name ( "text_block" );
2017-11-17 11:15:46 +01:00
// 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.
2017-12-04 17:42:35 +01:00
text = no_skip [ raw [ + ( utf8char - char_ ( '[' ) - char_ ( '{' ))]];
2017-11-17 11:15:46 +01:00
text . name ( "text" );
// New style of macro expansion.
// The macro expansion may contain numeric or string expressions, ifs and cases.
2017-11-17 18:46:03 +01:00
macro =
2017-11-26 10:54:54 +01:00
( kw [ "if" ] > if_else_output ( _r1 ) [ _val = _1 ])
2017-12-21 17:07:57 +01:00
// | (kw["switch"] > switch_output(_r1) [_val = _1])
2017-11-29 19:27:26 +01:00
| additive_expression ( _r1 ) [ px :: bind ( & expr < Iterator >:: to_string2 , _1 , _val ) ];
2017-11-17 11:15:46 +01:00
macro . name ( "macro" );
2017-11-17 18:46:03 +01:00
// An if expression enclosed in {} (the outmost {} are already parsed by the caller).
if_else_output =
eps [ _b = true ] >
2017-11-26 09:59:14 +01:00
bool_expr_eval ( _r1 )[ _a = _1 ] > '}' >
text_block ( _r1 )[ px :: bind ( & expr < Iterator >:: set_if , _a , _b , _1 , _val )] > '{' >
2017-11-26 10:54:54 +01:00
* ( kw [ "elsif" ] > bool_expr_eval ( _r1 )[ _a = _1 ] > '}' >
2017-11-26 09:59:14 +01:00
text_block ( _r1 )[ px :: bind ( & expr < Iterator >:: set_if , _a , _b , _1 , _val )] > '{' ) >
2017-11-26 10:54:54 +01:00
- ( kw [ "else" ] > lit ( '}' ) >
2017-11-26 09:59:14 +01:00
text_block ( _r1 )[ px :: bind ( & expr < Iterator >:: set_if , _b , _b , _1 , _val )] > '{' ) >
2017-11-26 10:54:54 +01:00
kw [ "endif" ];
2017-11-26 09:59:14 +01:00
if_else_output . name ( "if_else_output" );
2017-11-17 18:46:03 +01:00
// A switch expression enclosed in {} (the outmost {} are already parsed by the caller).
/*
switch_output =
eps[_b=true] >
2017-11-26 09:59:14 +01:00
omit[expr(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr<Iterator>::set_if_equal, _a, _b, _1, _val)] > '{' >
*("elsif" > omit[bool_expr_eval(_r1)[_a=_1]] > '}' > text_block(_r1)[px::bind(&expr<Iterator>::set_if, _a, _b, _1, _val)]) >>
-("else" > '}' >> text_block(_r1)[px::bind(&expr<Iterator>::set_if, _b, _b, _1, _val)]) >
2017-11-17 18:46:03 +01:00
"endif";
*/
2017-11-17 11:15:46 +01:00
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
legacy_variable_expansion =
( identifier >> & lit ( ']' ))
2017-11-26 09:59:14 +01:00
[ px :: bind ( & MyContext :: legacy_variable_expansion < Iterator > , _r1 , _1 , _val ) ]
2017-11-17 11:15:46 +01:00
| ( identifier > lit ( '[' ) > identifier > ']' )
2017-11-26 09:59:14 +01:00
[ px :: bind ( & MyContext :: legacy_variable_expansion2 < Iterator > , _r1 , _1 , _2 , _val ) ]
2017-11-17 11:15:46 +01:00
;
legacy_variable_expansion . name ( "legacy_variable_expansion" );
identifier =
2017-11-26 10:54:54 +01:00
! kw [ keywords ] >>
2017-11-26 09:59:14 +01:00
raw [ lexeme [( alpha | '_' ) >> * ( alnum | '_' )]];
2017-11-17 11:15:46 +01:00
identifier . name ( "identifier" );
2017-12-19 16:48:14 +01:00
conditional_expression =
logical_or_expression ( _r1 ) [ _val = _1 ]
>> - ( '?' > conditional_expression ( _r1 ) > ':' > conditional_expression ( _r1 )) [ px :: bind ( & expr < Iterator >:: ternary_op , _val , _1 , _2 )];
2017-12-21 17:07:57 +01:00
conditional_expression . name ( "conditional_expression" );
2017-12-19 16:48:14 +01:00
logical_or_expression =
logical_and_expression ( _r1 ) [ _val = _1 ]
>> * ( (( kw [ "or" ] | "||" ) > logical_and_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: logical_or , _val , _1 )] );
2017-12-21 17:07:57 +01:00
logical_or_expression . name ( "logical_or_expression" );
2017-12-19 16:48:14 +01:00
logical_and_expression =
equality_expression ( _r1 ) [ _val = _1 ]
>> * ( (( kw [ "and" ] | "&&" ) > equality_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: logical_and , _val , _1 )] );
2017-12-21 17:07:57 +01:00
logical_and_expression . name ( "logical_and_expression" );
2017-12-19 16:48:14 +01:00
equality_expression =
relational_expression ( _r1 ) [ _val = _1 ]
>> * ( ( "==" > relational_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: equal , _val , _1 )]
| ( "!=" > relational_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: not_equal , _val , _1 )]
| ( "<>" > relational_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: not_equal , _val , _1 )]
| ( "=~" > regular_expression ) [ px :: bind ( & expr < Iterator >:: regex_matches , _val , _1 )]
| ( "!~" > regular_expression ) [ px :: bind ( & expr < Iterator >:: regex_doesnt_match , _val , _1 )]
2017-11-26 09:59:14 +01:00
);
2017-12-19 16:48:14 +01:00
equality_expression . name ( "bool expression" );
2017-11-17 18:46:03 +01:00
// Evaluate a boolean expression stored as expr into a boolean value.
2017-12-19 16:48:14 +01:00
// Throw if the equality_expression does not produce a expr of boolean type.
bool_expr_eval = conditional_expression ( _r1 ) [ px :: bind ( & expr < Iterator >:: evaluate_boolean , _1 , _val ) ];
2017-11-26 09:59:14 +01:00
bool_expr_eval . name ( "bool_expr_eval" );
2017-11-17 11:15:46 +01:00
2017-12-19 16:48:14 +01:00
relational_expression =
additive_expression ( _r1 ) [ _val = _1 ]
2018-02-02 11:49:09 +01:00
>> * ( ( "<=" > additive_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: leq , _val , _1 )]
2017-12-19 16:48:14 +01:00
| ( ">=" > additive_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: geq , _val , _1 )]
2018-02-02 11:49:09 +01:00
| ( lit ( '<' ) > additive_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: lower , _val , _1 )]
| ( lit ( '>' ) > additive_expression ( _r1 ) ) [ px :: bind ( & expr < Iterator >:: greater , _val , _1 )]
2017-12-19 16:48:14 +01:00
);
2017-12-21 17:07:57 +01:00
relational_expression . name ( "relational_expression" );
2017-12-19 16:48:14 +01:00
2017-11-29 19:27:26 +01:00
additive_expression =
2017-12-19 16:48:14 +01:00
multiplicative_expression ( _r1 ) [ _val = _1 ]
>> * ( ( lit ( '+' ) > multiplicative_expression ( _r1 ) ) [ _val += _1 ]
| ( lit ( '-' ) > multiplicative_expression ( _r1 ) ) [ _val -= _1 ]
2017-11-26 09:59:14 +01:00
);
2017-11-29 19:27:26 +01:00
additive_expression . name ( "additive_expression" );
2017-11-17 11:15:46 +01:00
2017-12-19 16:48:14 +01:00
multiplicative_expression =
unary_expression ( _r1 ) [ _val = _1 ]
>> * ( ( lit ( '*' ) > unary_expression ( _r1 ) ) [ _val *= _1 ]
| ( lit ( '/' ) > unary_expression ( _r1 ) ) [ _val /= _1 ]
2017-11-26 09:59:14 +01:00
);
2017-12-19 16:48:14 +01:00
multiplicative_expression . name ( "multiplicative_expression" );
2017-11-17 11:15:46 +01:00
2017-11-26 09:59:14 +01:00
struct FactorActions {
static void set_start_pos ( Iterator & start_pos , expr < Iterator > & out )
{ out . it_range = boost :: iterator_range < Iterator > ( start_pos , start_pos ); }
static void int_ ( int & value , Iterator & end_pos , expr < Iterator > & out )
{ out = expr < Iterator > ( value , out . it_range . begin (), end_pos ); }
static void double_ ( double & value , Iterator & end_pos , expr < Iterator > & out )
{ out = expr < Iterator > ( value , out . it_range . begin (), end_pos ); }
static void bool_ ( bool & value , Iterator & end_pos , expr < Iterator > & out )
{ out = expr < Iterator > ( value , out . it_range . begin (), end_pos ); }
static void string_ ( boost :: iterator_range < Iterator > & it_range , expr < Iterator > & out )
{ out = expr < Iterator > ( std :: string ( it_range . begin () + 1 , it_range . end () - 1 ), it_range . begin (), it_range . end ()); }
static void expr_ ( expr < Iterator > & value , Iterator & end_pos , expr < Iterator > & out )
{ out = expr < Iterator > ( std :: move ( value ), out . it_range . begin (), end_pos ); }
static void minus_ ( expr < Iterator > & value , expr < Iterator > & out )
{ out = value . unary_minus ( out . it_range . begin ()); }
2017-11-26 11:16:28 +01:00
static void not_ ( expr < Iterator > & value , expr < Iterator > & out )
{ out = value . unary_not ( out . it_range . begin ()); }
2017-11-26 09:59:14 +01:00
};
2017-12-19 16:48:14 +01:00
unary_expression = iter_pos [ px :: bind ( & FactorActions :: set_start_pos , _1 , _val )] >> (
scalar_variable_reference ( _r1 ) [ _val = _1 ]
| ( lit ( '(' ) > conditional_expression ( _r1 ) > ')' > iter_pos ) [ px :: bind ( & FactorActions :: expr_ , _1 , _2 , _val ) ]
| ( lit ( '-' ) > unary_expression ( _r1 ) ) [ px :: bind ( & FactorActions :: minus_ , _1 , _val ) ]
| ( lit ( '+' ) > unary_expression ( _r1 ) > iter_pos ) [ px :: bind ( & FactorActions :: expr_ , _1 , _2 , _val ) ]
| (( kw [ "not" ] | '!' ) > unary_expression ( _r1 ) > iter_pos ) [ px :: bind ( & FactorActions :: not_ , _1 , _val ) ]
2018-05-15 14:04:29 +02:00
| ( kw [ "min" ] > '(' > conditional_expression ( _r1 ) [ _val = _1 ] > ',' > conditional_expression ( _r1 ) > ')' )
[ px :: bind ( & expr < Iterator >:: min , _val , _2 ) ]
| ( kw [ "max" ] > '(' > conditional_expression ( _r1 ) [ _val = _1 ] > ',' > conditional_expression ( _r1 ) > ')' )
[ px :: bind ( & expr < Iterator >:: max , _val , _2 ) ]
2017-12-19 16:48:14 +01:00
| ( strict_double > iter_pos ) [ px :: bind ( & FactorActions :: double_ , _1 , _2 , _val ) ]
| ( int_ > iter_pos ) [ px :: bind ( & FactorActions :: int_ , _1 , _2 , _val ) ]
| ( kw [ bool_ ] > iter_pos ) [ px :: bind ( & FactorActions :: bool_ , _1 , _2 , _val ) ]
2017-12-04 17:42:35 +01:00
| raw [ lexeme [ '"' > * (( utf8char - char_ ( '\\' ) - char_ ( '"' )) | ( '\\' > char_ )) > '"' ]]
2017-12-19 16:48:14 +01:00
[ px :: bind ( & FactorActions :: string_ , _1 , _val ) ]
2017-11-26 09:59:14 +01:00
);
2017-12-19 16:48:14 +01:00
unary_expression . name ( "unary_expression" );
2017-11-17 11:15:46 +01:00
2017-11-17 18:46:03 +01:00
scalar_variable_reference =
2017-11-26 09:59:14 +01:00
variable_reference ( _r1 )[ _a = _1 ] >>
(
2017-11-29 19:27:26 +01:00
( '[' > additive_expression ( _r1 )[ px :: bind ( & MyContext :: evaluate_index < Iterator > , _1 , _b )] > ']' >
2017-11-26 10:54:54 +01:00
iter_pos [ px :: bind ( & MyContext :: vector_variable_reference < Iterator > , _r1 , _a , _b , _1 , _val )])
2017-11-26 09:59:14 +01:00
| eps [ px :: bind ( & MyContext :: scalar_variable_reference < Iterator > , _r1 , _a , _val )]
);
2017-11-17 18:46:03 +01:00
scalar_variable_reference . name ( "scalar variable reference" );
2017-11-17 11:15:46 +01:00
2017-11-17 18:46:03 +01:00
variable_reference = identifier
2017-11-26 09:59:14 +01:00
[ px :: bind ( & MyContext :: resolve_variable < Iterator > , _r1 , _1 , _val ) ];
2017-11-17 18:46:03 +01:00
variable_reference . name ( "variable reference" );
2017-11-26 09:59:14 +01:00
2017-12-15 17:14:24 +01:00
regular_expression = raw [ lexeme [ '/' > * (( utf8char - char_ ( '\\' ) - char_ ( '/' )) | ( '\\' > char_ )) > '/' ]];
regular_expression . name ( "regular_expression" );
2017-11-26 09:59:14 +01:00
keywords . add
2017-11-26 11:16:28 +01:00
( "and" )
2017-11-26 09:59:14 +01:00
( "if" )
//("inf")
( "else" )
( "elsif" )
2017-11-26 10:54:54 +01:00
( "endif" )
( "false" )
2018-05-15 14:04:29 +02:00
( "min" )
( "max" )
2017-11-26 11:16:28 +01:00
( "not" )
( "or" )
2017-11-26 10:54:54 +01:00
( "true" );
2017-11-26 09:59:14 +01:00
if ( 0 ) {
debug ( start );
debug ( text );
debug ( text_block );
debug ( macro );
debug ( if_else_output );
2017-12-21 17:07:57 +01:00
// debug(switch_output);
2017-11-26 09:59:14 +01:00
debug ( legacy_variable_expansion );
debug ( identifier );
2017-12-19 16:48:14 +01:00
debug ( conditional_expression );
debug ( logical_or_expression );
debug ( logical_and_expression );
debug ( equality_expression );
2017-11-26 09:59:14 +01:00
debug ( bool_expr_eval );
2017-12-19 16:48:14 +01:00
debug ( relational_expression );
2017-11-29 19:27:26 +01:00
debug ( additive_expression );
2017-12-19 16:48:14 +01:00
debug ( multiplicative_expression );
debug ( unary_expression );
2017-11-26 09:59:14 +01:00
debug ( scalar_variable_reference );
debug ( variable_reference );
2017-12-15 17:14:24 +01:00
debug ( regular_expression );
2017-11-26 09:59:14 +01:00
}
2017-11-17 11:15:46 +01:00
}
2017-12-19 16:48:14 +01:00
// Generic expression over expr<Iterator>.
2019-08-08 14:21:24 +02:00
typedef qi :: rule < Iterator , expr < Iterator > ( const MyContext * ), spirit_encoding :: space_type > RuleExpression ;
2017-12-19 16:48:14 +01:00
2017-11-17 11:15:46 +01:00
// The start of the grammar.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , std :: string ( const MyContext * ), qi :: locals < bool > , spirit_encoding :: space_type > start ;
2017-11-17 11:15:46 +01:00
// A free-form text.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , std :: string (), spirit_encoding :: space_type > text ;
2017-11-26 09:59:14 +01:00
// A free-form text, possibly empty, possibly containing macro expansions.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , std :: string ( const MyContext * ), spirit_encoding :: space_type > text_block ;
2017-11-17 11:15:46 +01:00
// Statements enclosed in curely braces {}
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , std :: string ( const MyContext * ), spirit_encoding :: space_type > macro ;
2017-11-17 11:15:46 +01:00
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , std :: string ( const MyContext * ), spirit_encoding :: space_type > legacy_variable_expansion ;
2017-11-17 11:15:46 +01:00
// Parsed identifier name.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , boost :: iterator_range < Iterator > (), spirit_encoding :: space_type > identifier ;
2017-12-19 16:48:14 +01:00
// Ternary operator (?:) over logical_or_expression.
RuleExpression conditional_expression ;
// Logical or over logical_and_expressions.
RuleExpression logical_or_expression ;
// Logical and over relational_expressions.
RuleExpression logical_and_expression ;
// <, >, <=, >=
RuleExpression relational_expression ;
// Math expression consisting of +- operators over multiplicative_expressions.
RuleExpression additive_expression ;
// Boolean expressions over expressions.
RuleExpression equality_expression ;
// Math expression consisting of */ operators over factors.
RuleExpression multiplicative_expression ;
// Number literals, functions, braced expressions, variable references, variable indexing references.
RuleExpression unary_expression ;
2017-12-15 17:14:24 +01:00
// Rule to capture a regular expression enclosed in //.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , boost :: iterator_range < Iterator > (), spirit_encoding :: space_type > regular_expression ;
2017-11-17 18:46:03 +01:00
// Evaluate boolean expression into bool.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , bool ( const MyContext * ), spirit_encoding :: space_type > bool_expr_eval ;
2017-11-17 18:46:03 +01:00
// Reference of a scalar variable, or reference to a field of a vector variable.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , expr < Iterator > ( const MyContext * ), qi :: locals < OptWithPos < Iterator > , int > , spirit_encoding :: space_type > scalar_variable_reference ;
2017-11-17 18:46:03 +01:00
// Rule to translate an identifier to a ConfigOption, or to fail.
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , OptWithPos < Iterator > ( const MyContext * ), spirit_encoding :: space_type > variable_reference ;
2017-11-17 11:15:46 +01:00
2019-08-08 14:21:24 +02:00
qi :: rule < Iterator , std :: string ( const MyContext * ), qi :: locals < bool , bool > , spirit_encoding :: space_type > if_else_output ;
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
2017-11-26 09:59:14 +01:00
qi :: symbols < char > keywords ;
2017-11-17 11:15:46 +01:00
};
}
2017-12-18 12:14:09 +01:00
static std :: string process_macro ( const std :: string & templ , client :: MyContext & context )
2017-11-17 11:15:46 +01:00
{
typedef std :: string :: const_iterator iterator_type ;
2017-12-18 12:14:09 +01:00
typedef client :: macro_processor < iterator_type > macro_processor ;
2017-11-17 11:15:46 +01:00
2017-12-05 15:54:24 +01:00
// Our whitespace skipper.
2019-08-08 14:21:24 +02:00
spirit_encoding :: space_type space ;
2017-12-18 12:14:09 +01:00
// 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 ;
2017-12-05 15:54:24 +01:00
// Iterators over the source template.
2017-11-17 11:15:46 +01:00
std :: string :: const_iterator iter = templ . begin ();
2017-12-05 15:54:24 +01:00
std :: string :: const_iterator end = templ . end ();
// Accumulator for the processed template.
std :: string output ;
2019-06-25 13:06:04 +02:00
phrase_parse ( iter , end , macro_processor_instance ( & context ), space , output );
2017-12-21 17:07:57 +01:00
if ( ! context . error_message . empty ()) {
2017-12-05 15:54:24 +01:00
if ( context . error_message . back () != '\n' && context . error_message . back () != '\r' )
context . error_message += '\n' ;
throw std :: runtime_error ( context . error_message );
2017-11-17 11:15:46 +01:00
}
2017-12-05 15:54:24 +01:00
return output ;
2015-07-01 19:35:22 +02:00
}
2017-12-18 12:14:09 +01:00
std :: string PlaceholderParser :: process ( const std :: string & templ , unsigned int current_extruder_id , const DynamicConfig * config_override ) const
{
client :: MyContext context ;
2019-07-24 12:39:01 +02:00
context . external_config = this -> external_config ();
2017-12-18 12:14:09 +01:00
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.
2017-12-19 16:48:14 +01:00
bool PlaceholderParser :: evaluate_boolean_expression ( const std :: string & templ , const DynamicConfig & config , const DynamicConfig * config_override )
2017-12-18 12:14:09 +01:00
{
client :: MyContext context ;
2019-07-24 12:39:01 +02:00
context . config = & config ;
context . config_override = config_override ;
2017-12-18 12:14:09 +01:00
// 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" ;
}
2014-05-06 11:07:18 +03:00
}