2014-05-06 08:07:18 +00:00
# include "PlaceholderParser.hpp"
2020-09-14 14:27:55 +00:00
# include "Exception.hpp"
2020-02-04 13:43:58 +00:00
# include "Flow.hpp"
2015-12-08 10:01:12 +00:00
# include <cstring>
2014-11-09 19:41:27 +00:00
# include <ctime>
# include <iomanip>
# include <sstream>
2017-12-21 16:07:57 +00:00
# include <map>
2016-08-21 19:46:17 +00:00
# ifdef _MSC_VER
# include <stdlib.h> // provides **_environ
# else
# include <unistd.h> // provides **environ
# endif
2015-07-01 15:56:38 +00:00
2016-04-11 10:04:54 +00:00
# ifdef __APPLE__
# include <crt_externs.h>
# undef environ
# define environ (*_NSGetEnviron())
# else
2016-08-21 19:46:17 +00:00
# ifdef _MSC_VER
# define environ _environ
# else
extern char * * environ ;
# endif
2016-04-11 10:04:54 +00:00
# endif
2014-05-06 08:07:18 +00:00
2017-11-26 08:59:14 +00:00
# include <boost/algorithm/string.hpp>
2017-11-17 10:15:46 +00: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 09:54:54 +00:00
//#define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
2017-11-17 10:15:46 +00:00
2017-11-26 08:59:14 +00:00
# define BOOST_RESULT_OF_USE_DECLTYPE
# define BOOST_SPIRIT_USE_PHOENIX_V3
2017-11-17 10:15:46 +00: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 09:54:54 +00:00
# include <boost/spirit/repository/include/qi_distinct.hpp>
2017-11-26 08:59:14 +00:00
# include <boost/spirit/repository/include/qi_iter_pos.hpp>
2017-11-17 10:15:46 +00:00
# include <boost/variant/recursive_variant.hpp>
# include <boost/phoenix/bind/bind_function.hpp>
# include <iostream>
# include <string>
2017-12-20 20:54:47 +00: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 08:07:18 +00:00
namespace Slic3r {
2019-07-24 10:39:01 +00:00
PlaceholderParser : : PlaceholderParser ( const DynamicConfig * external_config ) : m_external_config ( external_config )
2014-05-06 08:07:18 +00:00
{
2017-12-11 08:31:29 +00:00
this - > set ( " version " , std : : string ( SLIC3R_VERSION ) ) ;
2015-07-01 15:56:38 +00:00
this - > apply_env_variables ( ) ;
2014-11-09 19:41:27 +00:00
this - > update_timestamp ( ) ;
}
2018-09-11 12:04:47 +00:00
void PlaceholderParser : : update_timestamp ( DynamicConfig & config )
2014-11-09 19:41:27 +00: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 12:04:47 +00:00
config . set_key_value ( " timestamp " , new ConfigOptionString ( ss . str ( ) ) ) ;
2014-11-09 19:41:27 +00:00
}
2018-09-11 12:04:47 +00: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 08:07:18 +00:00
}
2018-12-03 12:14:28 +00: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 ) ;
2020-02-04 13:43:58 +00:00
return opt_old ! = nullptr & & * opt_new = = * opt_old ;
2018-12-03 12:14:28 +00:00
}
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 12:39:19 +00:00
if ( ! opts_equal ( m_config , rhs , opt_key ) )
2018-12-03 12:14:28 +00:00
diff_keys . emplace_back ( opt_key ) ;
return diff_keys ;
}
2017-07-31 13:42:55 +00: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 20:53:43 +00:00
bool PlaceholderParser : : apply_config ( const DynamicPrintConfig & rhs )
2014-06-13 13:32:11 +00:00
{
2018-10-23 20:53:43 +00:00
bool modified = false ;
2017-11-17 10:15:46 +00:00
for ( const t_config_option_key & opt_key : rhs . keys ( ) ) {
2018-12-03 12:14:28 +00:00
if ( ! opts_equal ( m_config , rhs , opt_key ) ) {
2020-02-04 13:43:58 +00:00
this - > set ( opt_key , rhs . option ( opt_key ) - > clone ( ) ) ;
2018-12-03 12:14:28 +00:00
modified = true ;
2018-10-23 20:53:43 +00:00
}
2018-12-03 12:14:28 +00:00
}
return modified ;
}
void PlaceholderParser : : apply_only ( const DynamicPrintConfig & rhs , const std : : vector < std : : string > & keys )
{
2020-02-04 13:43:58 +00:00
for ( const t_config_option_key & opt_key : keys )
this - > set ( opt_key , rhs . option ( opt_key ) - > clone ( ) ) ;
2014-06-13 13:32:11 +00:00
}
2019-07-25 12:39:19 +00:00
void PlaceholderParser : : apply_config ( DynamicPrintConfig & & rhs )
{
m_config + = std : : move ( rhs ) ;
}
2017-07-31 13:42:55 +00:00
void PlaceholderParser : : apply_env_variables ( )
2015-07-01 15:56:38 +00:00
{
2017-11-17 10:15:46 +00:00
for ( char * * env = environ ; * env ; + + env ) {
2015-07-01 15:56:38 +00: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 08:59:14 +00:00
namespace spirit = boost : : spirit ;
2019-08-08 12:21:24 +00: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 08:59:14 +00:00
namespace qi = boost : : spirit : : qi ;
namespace px = boost : : phoenix ;
2017-11-17 10:15:46 +00:00
namespace client
2014-11-09 19:41:27 +00:00
{
2017-11-17 17:46:03 +00: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 08:59:14 +00: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 17:46:03 +00:00
template < typename Iterator >
struct expr
{
2017-11-29 19:38:19 +00:00
expr ( ) : type ( TYPE_EMPTY ) { }
2017-11-26 08:59:14 +00: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 18:27:26 +00: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 08:59:14 +00: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 18:27:26 +00:00
{ data . set ( rhs . data ) ; rhs . type = TYPE_EMPTY ; }
2017-11-26 08:59:14 +00:00
~ expr ( ) { this - > reset ( ) ; }
2017-11-17 17:46:03 +00:00
expr & operator = ( const expr & rhs )
{
2017-11-29 18:27:26 +00: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 17:46:03 +00:00
return * this ;
}
expr & operator = ( expr & & rhs )
{
2017-11-29 18:27:26 +00:00
type = rhs . type ;
this - > it_range = rhs . it_range ;
data . set ( rhs . data ) ;
rhs . type = TYPE_EMPTY ;
return * this ;
2017-11-17 17:46:03 +00:00
}
2017-11-26 08:59:14 +00:00
void reset ( )
{
if ( this - > type = = TYPE_STRING )
delete data . s ;
this - > type = TYPE_EMPTY ;
}
2017-11-17 17:46:03 +00:00
bool & b ( ) { return data . b ; }
bool b ( ) const { return data . b ; }
2017-11-26 08:59:14 +00:00
void set_b ( bool v ) { this - > reset ( ) ; this - > data . b = v ; this - > type = TYPE_BOOL ; }
2017-11-17 17:46:03 +00:00
int & i ( ) { return data . i ; }
int i ( ) const { return data . i ; }
2017-11-26 08:59:14 +00: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 17:46:03 +00:00
double & d ( ) { return data . d ; }
double d ( ) const { return data . d ; }
2017-11-26 08:59:14 +00: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 17:46:03 +00:00
std : : string & s ( ) { return * data . s ; }
const std : : string & s ( ) const { return * data . s ; }
2017-11-26 08:59:14 +00: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 17:46:03 +00:00
std : : string to_string ( ) const
{
std : : string out ;
switch ( type ) {
2019-01-18 08:50:56 +00:00
case TYPE_BOOL : out = data . b ? " true " : " false " ; break ;
2019-01-17 14:40:09 +00:00
case TYPE_INT : out = std : : to_string ( data . i ) ; break ;
2019-01-18 08:50:56 +00: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 17:46:03 +00:00
case TYPE_STRING : out = * data . s ; break ;
2017-11-29 18:27:26 +00:00
default : break ;
2017-11-17 17:46:03 +00:00
}
return out ;
}
2017-11-29 18:27:26 +00: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 17:46:03 +00:00
bool b ;
int i ;
double d ;
std : : string * s ;
2017-11-29 18:27:26 +00: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 17:46:03 +00:00
} data ;
enum Type {
TYPE_EMPTY = 0 ,
TYPE_BOOL ,
TYPE_INT ,
TYPE_DOUBLE ,
TYPE_STRING ,
} ;
2017-11-26 08:59:14 +00:00
Type type ;
2017-11-17 17:46:03 +00:00
// Range of input iterators covering this expression.
// Used for throwing parse exceptions.
boost : : iterator_range < Iterator > it_range ;
2017-11-26 09:54:54 +00:00
expr unary_minus ( const Iterator start_pos ) const
2017-11-26 08:59:14 +00: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 ( ) ;
}
2019-12-04 08:31:21 +00:00
expr unary_integer ( const Iterator start_pos ) const
{
switch ( this - > type ) {
case TYPE_INT :
return expr < Iterator > ( this - > i ( ) , start_pos , this - > it_range . end ( ) ) ;
case TYPE_DOUBLE :
2020-01-21 16:11:41 +00:00
return expr < Iterator > ( static_cast < int > ( this - > d ( ) ) , start_pos , this - > it_range . end ( ) ) ;
2019-12-04 08:31:21 +00:00
default :
this - > throw_exception ( " Cannot convert to integer. " ) ;
}
assert ( false ) ;
// Suppress compiler warnings.
return expr ( ) ;
}
2017-11-26 10:16:28 +00: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 08:59:14 +00:00
expr & operator + = ( const expr & rhs )
{
2017-12-05 16:38:29 +00: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 08:59:14 +00: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 16:38:29 +00:00
const char * err_msg = " Cannot subtract non-numeric types. " ;
2017-11-26 08:59:14 +00: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 )
2017-11-17 17:46:03 +00:00
{
2017-11-26 08:59:14 +00:00
this - > throw_if_not_numeric ( " Cannot divide a non-numeric type. " ) ;
rhs . throw_if_not_numeric ( " Cannot divide with a non-numeric type. " ) ;
2020-01-21 16:11:41 +00:00
if ( ( rhs . type = = TYPE_INT ) ? ( rhs . i ( ) = = 0 ) : ( rhs . d ( ) = = 0. ) )
2017-11-26 08:59:14 +00:00
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 17:46:03 +00:00
}
2017-11-26 08:59:14 +00:00
2019-12-04 08:31:21 +00:00
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. " ) ;
2020-01-21 16:11:41 +00:00
if ( ( rhs . type = = TYPE_INT ) ? ( rhs . i ( ) = = 0 ) : ( rhs . d ( ) = = 0. ) )
2019-12-04 08:31:21 +00:00
rhs . throw_exception ( " Division by zero " ) ;
if ( this - > type = = TYPE_DOUBLE | | rhs . type = = TYPE_DOUBLE ) {
double d = std : : fmod ( 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 17:46:03 +00:00
static void to_string2 ( expr & self , std : : string & out )
{
out = self . to_string ( ) ;
}
2017-11-26 08:59:14 +00:00
2017-11-17 17:46:03 +00:00
static void evaluate_boolean ( expr & self , bool & out )
{
if ( self . type ! = TYPE_BOOL )
2017-11-26 08:59:14 +00:00
self . throw_exception ( " Not a boolean expression " ) ;
2017-11-17 17:46:03 +00:00
out = self . b ( ) ;
}
2017-11-26 08:59:14 +00:00
2017-12-18 11:14:09 +00: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 08:59:14 +00:00
// Is lhs==rhs? Store the result into lhs.
2017-12-19 15:48:14 +00:00
static void compare_op ( expr & lhs , expr & rhs , char op , bool invert )
2017-11-26 08:59:14 +00: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 15:48:14 +00: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 08:59:14 +00:00
} else if ( lhs . type = = TYPE_BOOL & & rhs . type = = TYPE_BOOL ) {
// Both type are bool.
2017-12-19 15:48:14 +00: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 08:59:14 +00: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 15:48:14 +00: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 08:59:14 +00:00
} else {
boost : : throw_exception ( qi : : expectation_failure < Iterator > (
2017-12-05 14:54:24 +00:00
lhs . it_range . begin ( ) , rhs . it_range . end ( ) , spirit : : info ( " *Cannot compare the types. " ) ) ) ;
2017-11-26 08:59:14 +00:00
}
lhs . type = TYPE_BOOL ;
2017-12-19 15:48:14 +00:00
lhs . data . b = invert ? ! value : value ;
2017-11-26 08:59:14 +00:00
}
2018-05-15 12:04:29 +00:00
// Compare operators, store the result into lhs.
2017-12-19 15:48:14 +00: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 08:59:14 +00:00
2018-05-15 12:04:29 +00: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 11:02:42 +00:00
int i = 0 ;
2018-05-15 12:04:29 +00: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 16:14:24 +00: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 20:54:47 +00:00
bool result = SLIC3R_REGEX_NAMESPACE : : regex_match ( * subject , SLIC3R_REGEX_NAMESPACE : : regex ( pattern ) ) ;
2017-12-15 16:14:24 +00:00
if ( op = = ' ! ' )
result = ! result ;
lhs . reset ( ) ;
lhs . type = TYPE_BOOL ;
lhs . data . b = result ;
2017-12-20 20:54:47 +00:00
} catch ( SLIC3R_REGEX_NAMESPACE : : regex_error & ex ) {
2017-12-15 16:14:24 +00: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 15:48:14 +00: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 17:46:03 +00: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 08:59:14 +00:00
void throw_exception ( const char * message ) const
{
boost : : throw_exception ( qi : : expectation_failure < Iterator > (
2017-12-05 14:54:24 +00:00
this - > it_range . begin ( ) , this - > it_range . end ( ) , spirit : : info ( std : : string ( " * " ) + message ) ) ) ;
2017-11-26 08:59:14 +00: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 17:46:03 +00:00
} ;
2017-11-26 08:59:14 +00:00
template < typename ITERATOR >
std : : ostream & operator < < ( std : : ostream & os , const expr < ITERATOR > & expression )
{
typedef expr < ITERATOR > Expr ;
2017-11-29 18:27:26 +00:00
os < < std : : string ( expression . it_range . begin ( ) , expression . it_range . end ( ) ) < < " - " ;
2017-11-26 08:59:14 +00: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 ;
}
2020-02-04 13:43:58 +00:00
struct MyContext : public ConfigOptionResolver {
2019-07-24 10:39:01 +00:00
const DynamicConfig * external_config = nullptr ;
2017-12-18 11:14:09 +00: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 14:54:24 +00:00
std : : string error_message ;
2014-11-09 19:41:27 +00:00
2017-12-21 16:07:57 +00: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 11:14:09 +00:00
static void evaluate_full_macro ( const MyContext * ctx , bool & result ) { result = ! ctx - > just_boolean_expression ; }
2020-02-04 13:43:58 +00:00
const ConfigOption * optptr ( const t_config_option_key & opt_key ) const override
2017-11-17 10:15:46 +00:00
{
const ConfigOption * opt = nullptr ;
if ( config_override ! = nullptr )
opt = config_override - > option ( opt_key ) ;
if ( opt = = nullptr )
2017-12-18 11:14:09 +00:00
opt = config - > option ( opt_key ) ;
2019-07-24 10:39:01 +00:00
if ( opt = = nullptr & & external_config ! = nullptr )
opt = external_config - > option ( opt_key ) ;
2017-11-17 10:15:46 +00:00
return opt ;
}
2014-06-13 13:32:11 +00:00
2020-02-04 13:43:58 +00:00
const ConfigOption * resolve_symbol ( const std : : string & opt_key ) const { return this - > optptr ( opt_key ) ; }
2017-11-17 10:15:46 +00: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 14:54:24 +00:00
ctx - > throw_exception ( " Trying to index a scalar variable " , opt_key ) ;
2017-11-17 10:15:46 +00:00
char * endptr = nullptr ;
idx = strtol ( opt_key_str . c_str ( ) + idx + 1 , & endptr , 10 ) ;
if ( endptr = = nullptr | | * endptr ! = 0 )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Invalid vector index " , boost : : iterator_range < Iterator > ( opt_key . begin ( ) + idx + 1 , opt_key . end ( ) ) ) ;
2017-11-17 10:15:46 +00:00
}
}
}
if ( opt = = nullptr )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Variable does not exist " , boost : : iterator_range < Iterator > ( opt_key . begin ( ) , opt_key . end ( ) ) ) ;
2017-11-17 10:15:46 +00:00
if ( opt - > is_scalar ( ) )
output = opt - > serialize ( ) ;
else {
const ConfigOptionVectorBase * vec = static_cast < const ConfigOptionVectorBase * > ( opt ) ;
if ( vec - > empty ( ) )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Indexing an empty vector variable " , opt_key ) ;
2017-11-17 10:15:46 +00:00
output = vec - > vserialize ( ) [ ( idx > = vec - > size ( ) ) ? 0 : idx ] ;
}
}
template < typename Iterator >
static void legacy_variable_expansion2 (
const MyContext * ctx ,
boost : : iterator_range < Iterator > & opt_key ,
boost : : iterator_range < Iterator > & opt_vector_index ,
std : : string & output )
{
std : : string opt_key_str ( opt_key . begin ( ) , opt_key . end ( ) ) ;
const ConfigOption * opt = ctx - > resolve_symbol ( opt_key_str ) ;
if ( opt = = nullptr ) {
// Check whether the opt_key ends with '_'.
if ( opt_key_str . back ( ) = = ' _ ' )
opt_key_str . resize ( opt_key_str . size ( ) - 1 ) ;
opt = ctx - > resolve_symbol ( opt_key_str ) ;
}
if ( ! opt - > is_vector ( ) )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Trying to index a scalar variable " , opt_key ) ;
2017-11-17 10:15:46 +00:00
const ConfigOptionVectorBase * vec = static_cast < const ConfigOptionVectorBase * > ( opt ) ;
if ( vec - > empty ( ) )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Indexing an empty vector variable " , boost : : iterator_range < Iterator > ( opt_key . begin ( ) , opt_key . end ( ) ) ) ;
2017-11-17 10:15:46 +00: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 14:54:24 +00:00
ctx - > throw_exception ( " Variable does not exist " , opt_key ) ;
2017-11-17 10:15:46 +00:00
if ( opt_index - > type ( ) ! = coInt )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Indexing variable has to be integer " , opt_key ) ;
2017-11-17 10:15:46 +00:00
int idx = opt_index - > getInt ( ) ;
if ( idx < 0 )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Negative vector index " , opt_key ) ;
2017-11-17 10:15:46 +00:00
output = vec - > vserialize ( ) [ ( idx > = ( int ) vec - > size ( ) ) ? 0 : idx ] ;
}
2017-11-17 17:46:03 +00:00
template < typename Iterator >
static void resolve_variable (
const MyContext * ctx ,
boost : : iterator_range < Iterator > & opt_key ,
OptWithPos < Iterator > & output )
{
const ConfigOption * opt = ctx - > resolve_symbol ( std : : string ( opt_key . begin ( ) , opt_key . end ( ) ) ) ;
if ( opt = = nullptr )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Not a variable name " , opt_key ) ;
2017-11-17 17:46:03 +00:00
output . opt = opt ;
output . it_range = opt_key ;
2017-11-17 10:15:46 +00:00
}
2017-11-17 17:46:03 +00: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 16:27:50 +00:00
ctx - > throw_exception ( " Referencing a vector variable when scalar is expected " , opt . it_range ) ;
2017-11-17 17:46:03 +00:00
switch ( opt . opt - > type ( ) ) {
2017-11-26 08:59:14 +00: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 17:46:03 +00:00
case coFloatOrPercent :
2020-02-04 13:43:58 +00:00
{
std : : string opt_key ( opt . it_range . begin ( ) , opt . it_range . end ( ) ) ;
if ( boost : : ends_with ( opt_key , " extrusion_width " ) ) {
// Extrusion width supports defaults and a complex graph of dependencies.
output . set_d ( Flow : : extrusion_width ( opt_key , * ctx , static_cast < unsigned int > ( ctx - > current_extruder_id ) ) ) ;
} else if ( ! static_cast < const ConfigOptionFloatOrPercent * > ( opt . opt ) - > percent ) {
// Not a percent, just return the value.
output . set_d ( opt . opt - > getFloat ( ) ) ;
} else {
// Resolve dependencies using the "ratio_over" link to a parent value.
const ConfigOptionDef * opt_def = print_config_def . get ( opt_key ) ;
assert ( opt_def ! = nullptr ) ;
double v = opt . opt - > getFloat ( ) * 0.01 ; // percent to ratio
for ( ; ; ) {
const ConfigOption * opt_parent = opt_def - > ratio_over . empty ( ) ? nullptr : ctx - > resolve_symbol ( opt_def - > ratio_over ) ;
if ( opt_parent = = nullptr )
ctx - > throw_exception ( " FloatOrPercent variable failed to resolve the \" ratio_over \" dependencies " , opt . it_range ) ;
if ( boost : : ends_with ( opt_def - > ratio_over , " extrusion_width " ) ) {
// Extrusion width supports defaults and a complex graph of dependencies.
assert ( opt_parent - > type ( ) = = coFloatOrPercent ) ;
v * = Flow : : extrusion_width ( opt_def - > ratio_over , static_cast < const ConfigOptionFloatOrPercent * > ( opt_parent ) , * ctx , static_cast < unsigned int > ( ctx - > current_extruder_id ) ) ;
break ;
}
if ( opt_parent - > type ( ) = = coFloat | | opt_parent - > type ( ) = = coFloatOrPercent ) {
v * = opt_parent - > getFloat ( ) ;
if ( opt_parent - > type ( ) = = coFloat | | ! static_cast < const ConfigOptionFloatOrPercent * > ( opt_parent ) - > percent )
break ;
v * = 0.01 ; // percent to ratio
}
// Continue one level up in the "ratio_over" hierarchy.
opt_def = print_config_def . get ( opt_def - > ratio_over ) ;
assert ( opt_def ! = nullptr ) ;
}
output . set_d ( v ) ;
}
break ;
}
2017-11-17 17:46:03 +00:00
default :
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Unknown scalar variable type " , opt . it_range ) ;
2017-11-17 17:46:03 +00:00
}
2017-11-26 08:59:14 +00:00
output . it_range = opt . it_range ;
2017-11-17 10:15:46 +00:00
}
2017-11-17 17:46:03 +00:00
template < typename Iterator >
static void vector_variable_reference (
const MyContext * ctx ,
OptWithPos < Iterator > & opt ,
2017-11-26 08:59:14 +00:00
int & index ,
2017-11-17 17:46:03 +00:00
Iterator it_end ,
expr < Iterator > & output )
2017-11-17 10:15:46 +00:00
{
2017-11-17 17:46:03 +00:00
if ( opt . opt - > is_scalar ( ) )
2018-02-16 16:27:50 +00:00
ctx - > throw_exception ( " Referencing a scalar variable when vector is expected " , opt . it_range ) ;
2017-11-17 17:46:03 +00:00
const ConfigOptionVectorBase * vec = static_cast < const ConfigOptionVectorBase * > ( opt . opt ) ;
if ( vec - > empty ( ) )
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Indexing an empty vector variable " , opt . it_range ) ;
2017-11-26 08:59:14 +00:00
size_t idx = ( index < 0 ) ? 0 : ( index > = int ( vec - > size ( ) ) ) ? 0 : size_t ( index ) ;
2017-11-17 17:46:03 +00:00
switch ( opt . opt - > type ( ) ) {
2017-11-26 08:59:14 +00: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 14:54:07 +00:00
case coPoints : output . set_s ( to_string ( static_cast < const ConfigOptionPoints * > ( opt . opt ) - > values [ idx ] ) ) ; break ;
2017-11-26 08:59:14 +00:00
case coBools : output . set_b ( static_cast < const ConfigOptionBools * > ( opt . opt ) - > values [ idx ] ! = 0 ) ; break ;
2017-11-17 17:46:03 +00:00
default :
2017-12-05 14:54:24 +00:00
ctx - > throw_exception ( " Unknown vector variable type " , opt . it_range ) ;
2017-11-17 10:15:46 +00:00
}
2017-11-17 17:46:03 +00:00
output . it_range = boost : : iterator_range < Iterator > ( opt . it_range . begin ( ) , it_end ) ;
2017-11-17 10:15:46 +00:00
}
2017-11-26 08:59:14 +00:00
// Verify that the expression returns an integer, which may be used
// to address a vector.
2017-11-17 17:46:03 +00:00
template < typename Iterator >
2017-11-26 08:59:14 +00:00
static void evaluate_index ( expr < Iterator > & expr_index , int & output )
2017-11-17 17:46:03 +00:00
{
2017-12-05 14:54:24 +00:00
if ( expr_index . type ! = expr < Iterator > : : TYPE_INT )
2017-11-26 08:59:14 +00:00
expr_index . throw_exception ( " Non-integer index is not allowed to address a vector variable. " ) ;
output = expr_index . i ( ) ;
2017-11-17 17:46:03 +00:00
}
2017-12-05 14:54:24 +00: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 16:38:29 +00: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 14:54:24 +00:00
{
std : : string & msg = const_cast < MyContext * > ( context ) - > error_message ;
2017-12-05 16:38:29 +00: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 16:07:57 +00: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 + = " . " ;
2020-01-21 16:11:41 +00:00
msg + = it - > second ;
2017-12-21 16:07:57 +00:00
}
2017-12-05 16:38:29 +00:00
}
msg + = ' \n ' ;
msg + = error_line ;
msg + = ' \n ' ;
for ( size_t i = 0 ; i < error_pos ; + + i )
msg + = ' ' ;
msg + = " ^ \n " ;
2017-12-05 14:54:24 +00:00
}
2017-11-17 10:15:46 +00:00
} ;
2017-12-21 16:07:57 +00: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 08:59:14 +00: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 09:54:54 +00: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 17:22:42 +00: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 16:42:35 +00:00
{
// Define the attribute type exposed by this parser component
template < typename Context , typename Iterator >
struct attribute
{
2017-12-04 17:22:42 +00:00
typedef wchar_t type ;
} ;
2017-12-04 16:42:35 +00: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 17:22:42 +00: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 16:52:12 +00:00
unsigned int cnt = 0 ;
2017-12-04 17:22:42 +00: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 14:54:24 +00:00
MyContext : : throw_exception ( " Invalid utf8 sequence " , boost : : iterator_range < Iterator > ( first , last ) ) ;
return false ;
2017-12-04 17:22:42 +00:00
}
2017-12-04 16:42:35 +00:00
// This function is called during error handling to create a human readable string for the error context.
template < typename Context >
2017-12-04 17:22:42 +00:00
spirit : : info what ( Context & ) const
2017-12-04 16:42:35 +00:00
{
return spirit : : info ( " unicode_char " ) ;
2017-12-04 17:22:42 +00:00
}
2017-12-04 16:42:35 +00:00
} ;
2017-11-17 10:15:46 +00:00
///////////////////////////////////////////////////////////////////////////
2017-12-18 11:14:09 +00:00
// Our macro_processor grammar
2017-11-17 10:15:46 +00:00
///////////////////////////////////////////////////////////////////////////
2017-11-29 18:27:26 +00:00
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
2017-11-17 10:15:46 +00:00
template < typename Iterator >
2019-08-08 12:21:24 +00:00
struct macro_processor : qi : : grammar < Iterator , std : : string ( const MyContext * ) , qi : : locals < bool > , spirit_encoding : : space_type >
2017-11-17 10:15:46 +00:00
{
2017-12-18 11:14:09 +00:00
macro_processor ( ) : macro_processor : : base_type ( start )
2017-11-17 10:15:46 +00:00
{
2017-11-26 09:54:54 +00:00
using namespace qi : : labels ;
2017-11-26 08:59:14 +00: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 09:54:54 +00:00
qi : : real_parser < double , strict_real_policies_without_nan_inf > strict_double ;
2019-08-08 12:21:24 +00:00
spirit_encoding : : char_type char_ ;
2017-12-04 17:22:42 +00:00
utf8_char_skipper_parser utf8char ;
2017-11-26 08:59:14 +00:00
spirit : : bool_type bool_ ;
spirit : : int_type int_ ;
spirit : : double_type double_ ;
2019-08-08 12:21:24 +00:00
spirit_encoding : : string_type string ;
2017-12-21 16:07:57 +00:00
spirit : : eoi_type eoi ;
spirit : : repository : : qi : : iter_pos_type iter_pos ;
2017-11-26 09:54:54 +00:00
auto kw = spirit : : repository : : qi : : distinct ( qi : : copy ( alnum | ' _ ' ) ) ;
2017-11-26 08:59:14 +00:00
qi : : _val_type _val ;
qi : : _1_type _1 ;
qi : : _2_type _2 ;
2017-12-05 14:54:24 +00:00
qi : : _3_type _3 ;
qi : : _4_type _4 ;
2017-11-26 08:59:14 +00:00
qi : : _a_type _a ;
qi : : _b_type _b ;
qi : : _r1_type _r1 ;
2017-11-17 10:15:46 +00: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 11:14:09 +00: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 11:06:04 +00:00
( ( eps ( _a = = true ) > text_block ( _r1 ) [ _val = _1 ] )
2017-12-19 15:48:14 +00:00
| conditional_expression ( _r1 ) [ px : : bind ( & expr < Iterator > : : evaluate_boolean_to_string , _1 , _val ) ]
2017-12-21 16:07:57 +00:00
) > eoi ;
2017-11-17 10:15:46 +00:00
start . name ( " start " ) ;
2017-12-05 16:38:29 +00:00
qi : : on_error < qi : : fail > ( start , px : : bind ( & MyContext : : process_error_message < Iterator > , _r1 , _4 , _1 , _2 , _3 ) ) ;
2017-11-17 10:15:46 +00:00
2017-11-26 08:59:14 +00: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 09:54:54 +00:00
| ( lit ( ' { ' ) > > macro ( _r1 ) [ _val + = _1 ] > ' } ' )
| ( lit ( ' [ ' ) > legacy_variable_expansion ( _r1 ) [ _val + = _1 ] > ' ] ' )
2017-11-26 08:59:14 +00:00
) ;
text_block . name ( " text_block " ) ;
2017-11-17 10:15:46 +00: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 16:42:35 +00:00
text = no_skip [ raw [ + ( utf8char - char_ ( ' [ ' ) - char_ ( ' { ' ) ) ] ] ;
2017-11-17 10:15:46 +00:00
text . name ( " text " ) ;
// New style of macro expansion.
// The macro expansion may contain numeric or string expressions, ifs and cases.
2017-11-17 17:46:03 +00:00
macro =
2017-11-26 09:54:54 +00:00
( kw [ " if " ] > if_else_output ( _r1 ) [ _val = _1 ] )
2017-12-21 16:07:57 +00:00
// | (kw["switch"] > switch_output(_r1) [_val = _1])
2017-11-29 18:27:26 +00:00
| additive_expression ( _r1 ) [ px : : bind ( & expr < Iterator > : : to_string2 , _1 , _val ) ] ;
2017-11-17 10:15:46 +00:00
macro . name ( " macro " ) ;
2017-11-17 17:46:03 +00:00
// An if expression enclosed in {} (the outmost {} are already parsed by the caller).
if_else_output =
eps [ _b = true ] >
2017-11-26 08:59:14 +00:00
bool_expr_eval ( _r1 ) [ _a = _1 ] > ' } ' >
text_block ( _r1 ) [ px : : bind ( & expr < Iterator > : : set_if , _a , _b , _1 , _val ) ] > ' { ' >
2017-11-26 09:54:54 +00:00
* ( kw [ " elsif " ] > bool_expr_eval ( _r1 ) [ _a = _1 ] > ' } ' >
2017-11-26 08:59:14 +00:00
text_block ( _r1 ) [ px : : bind ( & expr < Iterator > : : set_if , _a , _b , _1 , _val ) ] > ' { ' ) >
2017-11-26 09:54:54 +00:00
- ( kw [ " else " ] > lit ( ' } ' ) >
2017-11-26 08:59:14 +00:00
text_block ( _r1 ) [ px : : bind ( & expr < Iterator > : : set_if , _b , _b , _1 , _val ) ] > ' { ' ) >
2017-11-26 09:54:54 +00:00
kw [ " endif " ] ;
2017-11-26 08:59:14 +00:00
if_else_output . name ( " if_else_output " ) ;
2017-11-17 17:46:03 +00:00
// A switch expression enclosed in {} (the outmost {} are already parsed by the caller).
/*
switch_output =
eps [ _b = true ] >
2017-11-26 08:59:14 +00: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 17:46:03 +00:00
" endif " ;
*/
2017-11-17 10:15:46 +00: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 08:59:14 +00:00
[ px : : bind ( & MyContext : : legacy_variable_expansion < Iterator > , _r1 , _1 , _val ) ]
2017-11-17 10:15:46 +00:00
| ( identifier > lit ( ' [ ' ) > identifier > ' ] ' )
2017-11-26 08:59:14 +00:00
[ px : : bind ( & MyContext : : legacy_variable_expansion2 < Iterator > , _r1 , _1 , _2 , _val ) ]
2017-11-17 10:15:46 +00:00
;
legacy_variable_expansion . name ( " legacy_variable_expansion " ) ;
identifier =
2017-11-26 09:54:54 +00:00
! kw [ keywords ] > >
2017-11-26 08:59:14 +00:00
raw [ lexeme [ ( alpha | ' _ ' ) > > * ( alnum | ' _ ' ) ] ] ;
2017-11-17 10:15:46 +00:00
identifier . name ( " identifier " ) ;
2017-12-19 15:48:14 +00: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 16:07:57 +00:00
conditional_expression . name ( " conditional_expression " ) ;
2017-12-19 15:48:14 +00: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 16:07:57 +00:00
logical_or_expression . name ( " logical_or_expression " ) ;
2017-12-19 15:48:14 +00: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 16:07:57 +00:00
logical_and_expression . name ( " logical_and_expression " ) ;
2017-12-19 15:48:14 +00: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 08:59:14 +00:00
) ;
2017-12-19 15:48:14 +00:00
equality_expression . name ( " bool expression " ) ;
2017-11-17 17:46:03 +00:00
// Evaluate a boolean expression stored as expr into a boolean value.
2017-12-19 15:48:14 +00: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 08:59:14 +00:00
bool_expr_eval . name ( " bool_expr_eval " ) ;
2017-11-17 10:15:46 +00:00
2017-12-19 15:48:14 +00:00
relational_expression =
additive_expression ( _r1 ) [ _val = _1 ]
2018-02-02 10:49:09 +00:00
> > * ( ( " <= " > additive_expression ( _r1 ) ) [ px : : bind ( & expr < Iterator > : : leq , _val , _1 ) ]
2017-12-19 15:48:14 +00:00
| ( " >= " > additive_expression ( _r1 ) ) [ px : : bind ( & expr < Iterator > : : geq , _val , _1 ) ]
2018-02-02 10:49:09 +00: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 15:48:14 +00:00
) ;
2017-12-21 16:07:57 +00:00
relational_expression . name ( " relational_expression " ) ;
2017-12-19 15:48:14 +00:00
2017-11-29 18:27:26 +00:00
additive_expression =
2017-12-19 15:48:14 +00:00
multiplicative_expression ( _r1 ) [ _val = _1 ]
> > * ( ( lit ( ' + ' ) > multiplicative_expression ( _r1 ) ) [ _val + = _1 ]
| ( lit ( ' - ' ) > multiplicative_expression ( _r1 ) ) [ _val - = _1 ]
2017-11-26 08:59:14 +00:00
) ;
2017-11-29 18:27:26 +00:00
additive_expression . name ( " additive_expression " ) ;
2017-11-17 10:15:46 +00:00
2017-12-19 15:48:14 +00:00
multiplicative_expression =
unary_expression ( _r1 ) [ _val = _1 ]
> > * ( ( lit ( ' * ' ) > unary_expression ( _r1 ) ) [ _val * = _1 ]
| ( lit ( ' / ' ) > unary_expression ( _r1 ) ) [ _val / = _1 ]
2019-12-04 08:31:21 +00:00
| ( lit ( ' % ' ) > unary_expression ( _r1 ) ) [ _val % = _1 ]
2017-11-26 08:59:14 +00:00
) ;
2017-12-19 15:48:14 +00:00
multiplicative_expression . name ( " multiplicative_expression " ) ;
2017-11-17 10:15:46 +00:00
2017-11-26 08:59:14 +00: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 )
2020-01-21 16:11:41 +00:00
{ auto begin_pos = out . it_range . begin ( ) ; out = expr < Iterator > ( std : : move ( value ) , begin_pos , end_pos ) ; }
2017-11-26 08:59:14 +00:00
static void minus_ ( expr < Iterator > & value , expr < Iterator > & out )
{ out = value . unary_minus ( out . it_range . begin ( ) ) ; }
2017-11-26 10:16:28 +00:00
static void not_ ( expr < Iterator > & value , expr < Iterator > & out )
{ out = value . unary_not ( out . it_range . begin ( ) ) ; }
2019-12-04 08:31:21 +00:00
static void to_int ( expr < Iterator > & value , expr < Iterator > & out )
{ out = value . unary_integer ( out . it_range . begin ( ) ) ; }
2017-11-26 08:59:14 +00:00
} ;
2017-12-19 15:48:14 +00: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 12:04:29 +00: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 ) ]
2020-01-21 16:11:41 +00:00
| ( kw [ " int " ] > ' ( ' > unary_expression ( _r1 ) > ' ) ' ) [ px : : bind ( & FactorActions : : to_int , _1 , _val ) ]
2017-12-19 15:48:14 +00: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 16:42:35 +00:00
| raw [ lexeme [ ' " ' > * ( ( utf8char - char_ ( ' \\ ' ) - char_ ( ' " ' ) ) | ( ' \\ ' > char_ ) ) > ' " ' ] ]
2017-12-19 15:48:14 +00:00
[ px : : bind ( & FactorActions : : string_ , _1 , _val ) ]
2017-11-26 08:59:14 +00:00
) ;
2017-12-19 15:48:14 +00:00
unary_expression . name ( " unary_expression " ) ;
2017-11-17 17:46:03 +00:00
scalar_variable_reference =
2017-11-26 08:59:14 +00:00
variable_reference ( _r1 ) [ _a = _1 ] > >
(
2017-11-29 18:27:26 +00:00
( ' [ ' > additive_expression ( _r1 ) [ px : : bind ( & MyContext : : evaluate_index < Iterator > , _1 , _b ) ] > ' ] ' >
2017-11-26 09:54:54 +00:00
iter_pos [ px : : bind ( & MyContext : : vector_variable_reference < Iterator > , _r1 , _a , _b , _1 , _val ) ] )
2017-11-26 08:59:14 +00:00
| eps [ px : : bind ( & MyContext : : scalar_variable_reference < Iterator > , _r1 , _a , _val ) ]
) ;
2017-11-17 17:46:03 +00:00
scalar_variable_reference . name ( " scalar variable reference " ) ;
variable_reference = identifier
2017-11-26 08:59:14 +00:00
[ px : : bind ( & MyContext : : resolve_variable < Iterator > , _r1 , _1 , _val ) ] ;
2017-11-17 17:46:03 +00:00
variable_reference . name ( " variable reference " ) ;
2017-11-26 08:59:14 +00:00
2017-12-15 16:14:24 +00:00
regular_expression = raw [ lexeme [ ' / ' > * ( ( utf8char - char_ ( ' \\ ' ) - char_ ( ' / ' ) ) | ( ' \\ ' > char_ ) ) > ' / ' ] ] ;
regular_expression . name ( " regular_expression " ) ;
2017-11-26 08:59:14 +00:00
keywords . add
2017-11-26 10:16:28 +00:00
( " and " )
2017-11-26 08:59:14 +00:00
( " if " )
2020-01-21 16:11:41 +00:00
( " int " )
2017-11-26 08:59:14 +00:00
//("inf")
( " else " )
( " elsif " )
2017-11-26 09:54:54 +00:00
( " endif " )
( " false " )
2018-05-15 12:04:29 +00:00
( " min " )
( " max " )
2017-11-26 10:16:28 +00:00
( " not " )
( " or " )
2017-11-26 09:54:54 +00:00
( " true " ) ;
2017-11-26 08:59:14 +00:00
if ( 0 ) {
debug ( start ) ;
debug ( text ) ;
debug ( text_block ) ;
debug ( macro ) ;
debug ( if_else_output ) ;
2017-12-21 16:07:57 +00:00
// debug(switch_output);
2017-11-26 08:59:14 +00:00
debug ( legacy_variable_expansion ) ;
debug ( identifier ) ;
2017-12-19 15:48:14 +00:00
debug ( conditional_expression ) ;
debug ( logical_or_expression ) ;
debug ( logical_and_expression ) ;
debug ( equality_expression ) ;
2017-11-26 08:59:14 +00:00
debug ( bool_expr_eval ) ;
2017-12-19 15:48:14 +00:00
debug ( relational_expression ) ;
2017-11-29 18:27:26 +00:00
debug ( additive_expression ) ;
2017-12-19 15:48:14 +00:00
debug ( multiplicative_expression ) ;
debug ( unary_expression ) ;
2017-11-26 08:59:14 +00:00
debug ( scalar_variable_reference ) ;
debug ( variable_reference ) ;
2017-12-15 16:14:24 +00:00
debug ( regular_expression ) ;
2017-11-26 08:59:14 +00:00
}
2017-11-17 10:15:46 +00:00
}
2017-12-19 15:48:14 +00:00
// Generic expression over expr<Iterator>.
2019-08-08 12:21:24 +00:00
typedef qi : : rule < Iterator , expr < Iterator > ( const MyContext * ) , spirit_encoding : : space_type > RuleExpression ;
2017-12-19 15:48:14 +00:00
2017-11-17 10:15:46 +00:00
// The start of the grammar.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , std : : string ( const MyContext * ) , qi : : locals < bool > , spirit_encoding : : space_type > start ;
2017-11-17 10:15:46 +00:00
// A free-form text.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , std : : string ( ) , spirit_encoding : : space_type > text ;
2017-11-26 08:59:14 +00:00
// A free-form text, possibly empty, possibly containing macro expansions.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , std : : string ( const MyContext * ) , spirit_encoding : : space_type > text_block ;
2017-11-17 10:15:46 +00:00
// Statements enclosed in curely braces {}
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , std : : string ( const MyContext * ) , spirit_encoding : : space_type > macro ;
2017-11-17 10:15:46 +00:00
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , std : : string ( const MyContext * ) , spirit_encoding : : space_type > legacy_variable_expansion ;
2017-11-17 10:15:46 +00:00
// Parsed identifier name.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , boost : : iterator_range < Iterator > ( ) , spirit_encoding : : space_type > identifier ;
2017-12-19 15:48:14 +00: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 16:14:24 +00:00
// Rule to capture a regular expression enclosed in //.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , boost : : iterator_range < Iterator > ( ) , spirit_encoding : : space_type > regular_expression ;
2017-11-17 17:46:03 +00:00
// Evaluate boolean expression into bool.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , bool ( const MyContext * ) , spirit_encoding : : space_type > bool_expr_eval ;
2017-11-17 17:46:03 +00:00
// Reference of a scalar variable, or reference to a field of a vector variable.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , expr < Iterator > ( const MyContext * ) , qi : : locals < OptWithPos < Iterator > , int > , spirit_encoding : : space_type > scalar_variable_reference ;
2017-11-17 17:46:03 +00:00
// Rule to translate an identifier to a ConfigOption, or to fail.
2019-08-08 12:21:24 +00:00
qi : : rule < Iterator , OptWithPos < Iterator > ( const MyContext * ) , spirit_encoding : : space_type > variable_reference ;
2017-11-26 08:59:14 +00:00
2019-08-08 12:21:24 +00: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-17 17:46:03 +00:00
2017-11-26 08:59:14 +00:00
qi : : symbols < char > keywords ;
2017-11-17 10:15:46 +00:00
} ;
2017-05-03 16:28:22 +00:00
}
2017-12-18 11:14:09 +00:00
static std : : string process_macro ( const std : : string & templ , client : : MyContext & context )
2015-07-01 17:35:22 +00:00
{
2017-11-17 10:15:46 +00:00
typedef std : : string : : const_iterator iterator_type ;
2017-12-18 11:14:09 +00:00
typedef client : : macro_processor < iterator_type > macro_processor ;
2017-07-31 13:42:55 +00:00
2017-12-05 14:54:24 +00:00
// Our whitespace skipper.
2019-08-08 12:21:24 +00:00
spirit_encoding : : space_type space ;
2017-12-18 11:14:09 +00: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 14:54:24 +00:00
// Iterators over the source template.
2017-11-17 10:15:46 +00:00
std : : string : : const_iterator iter = templ . begin ( ) ;
2017-12-05 14:54:24 +00:00
std : : string : : const_iterator end = templ . end ( ) ;
// Accumulator for the processed template.
std : : string output ;
2019-06-25 11:06:04 +00:00
phrase_parse ( iter , end , macro_processor_instance ( & context ) , space , output ) ;
2017-12-21 16:07:57 +00:00
if ( ! context . error_message . empty ( ) ) {
2017-12-05 14:54:24 +00:00
if ( context . error_message . back ( ) ! = ' \n ' & & context . error_message . back ( ) ! = ' \r ' )
context . error_message + = ' \n ' ;
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( context . error_message ) ;
2017-11-17 10:15:46 +00:00
}
2017-12-05 14:54:24 +00:00
return output ;
2015-07-01 17:35:22 +00:00
}
2017-12-18 11:14:09 +00: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 10:39:01 +00:00
context . external_config = this - > external_config ( ) ;
2017-12-18 11:14:09 +00: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.
2020-09-14 14:27:55 +00:00
// Throws Slic3r::RuntimeError on syntax or runtime error.
2017-12-19 15:48:14 +00:00
bool PlaceholderParser : : evaluate_boolean_expression ( const std : : string & templ , const DynamicConfig & config , const DynamicConfig * config_override )
2017-12-18 11:14:09 +00:00
{
client : : MyContext context ;
2019-07-24 10:39:01 +00:00
context . config = & config ;
context . config_override = config_override ;
2017-12-18 11:14:09 +00: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 08:07:18 +00:00
}