From 98761d3a94bc228ca7683c23887caa30c60278b5 Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Thu, 18 Aug 2016 18:51:21 +1000 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 259 +++++++++++++++++++++++++++-------- exprtk_simple_example_09.cpp | 8 +- exprtk_test.cpp | 64 ++++++++- readme.txt | 4 +- 4 files changed, 273 insertions(+), 62 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 8773dc1..fca963e 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -4218,6 +4218,28 @@ namespace exprtk unsigned int num_params; }; + namespace loop_unroll + { + #ifndef exprtk_disable_superscalar_unroll + const std::size_t loop_batch_size = 8; + #else + const std::size_t loop_batch_size = 4; + #endif + + struct details + { + details(const std::size_t& vsize) + : batch_size(loop_batch_size), + remainder (vsize % batch_size), + upper_bound(static_cast(vsize - (remainder ? loop_batch_size : 0))) + {} + + int batch_size; + int remainder; + int upper_bound; + }; + } + namespace numeric { namespace details @@ -4994,7 +5016,7 @@ namespace exprtk const char* base() const { - return value_.data(); + return value_.data(); } std::size_t size() const @@ -6747,7 +6769,7 @@ namespace exprtk const char* base() const { - return (*value_).data(); + return &(*value_)[0]; } std::size_t size() const @@ -6827,7 +6849,7 @@ namespace exprtk const char* base() const { - return (*value_).data(); + return &(*value_)[0]; } std::size_t size() const @@ -6905,7 +6927,7 @@ namespace exprtk const char* base() const { - return value_.data(); + return value_.data(); } std::size_t size() const @@ -7036,7 +7058,7 @@ namespace exprtk const char* base() const { - return value_.data(); + return &value_[0]; } std::size_t size() const @@ -7175,7 +7197,7 @@ namespace exprtk const char* base() const { - return value_.data(); + return &value_[0]; } std::size_t size() const @@ -7293,6 +7315,143 @@ namespace exprtk strvar_node_ptr str1_node_ptr_; }; + template + class swap_genstrings_node : public binary_node + { + public: + + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + typedef range_pack range_t; + typedef range_t* range_ptr; + typedef range_interface irange_t; + typedef irange_t* irange_ptr; + + swap_genstrings_node(expression_ptr branch0, + expression_ptr branch1) + : binary_node(details::e_default,branch0,branch1), + str0_base_ptr_ (0), + str1_base_ptr_ (0), + str0_range_ptr_(0), + str1_range_ptr_(0), + initialised_(false) + { + if (is_generally_string_node(binary_node::branch_[0].first)) + { + str0_base_ptr_ = dynamic_cast(binary_node::branch_[0].first); + + if (0 == str0_base_ptr_) + return; + + irange_ptr range_ptr = dynamic_cast(binary_node::branch_[0].first); + + if (0 == range_ptr) + return; + + str0_range_ptr_ = &(range_ptr->range_ref()); + } + + if (is_generally_string_node(binary_node::branch_[1].first)) + { + str1_base_ptr_ = dynamic_cast(binary_node::branch_[1].first); + + if (0 == str1_base_ptr_) + return; + + irange_ptr range_ptr = dynamic_cast(binary_node::branch_[1].first); + + if (0 == range_ptr) + return; + + str1_range_ptr_ = &(range_ptr->range_ref()); + } + + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_range_ptr_ && + str1_range_ptr_ ; + } + + inline T value() const + { + if (initialised_) + { + binary_node::branch_[0].first->value(); + binary_node::branch_[1].first->value(); + + std::size_t str0_r0 = 0; + std::size_t str0_r1 = 0; + + std::size_t str1_r0 = 0; + std::size_t str1_r1 = 0; + + range_t& range0 = (*str0_range_ptr_); + range_t& range1 = (*str1_range_ptr_); + + if ( + range0(str0_r0,str0_r1,str0_base_ptr_->size()) && + range1(str1_r0,str1_r1,str1_base_ptr_->size()) + ) + { + const std::size_t size0 = range0.cache_size(); + const std::size_t size1 = range1.cache_size(); + const std::size_t max_size = std::min(size0,size1); + + char* s0 = const_cast(str0_base_ptr_->base() + str0_r0); + char* s1 = const_cast(str1_base_ptr_->base() + str1_r0); + + loop_unroll::details lud(max_size); + int i = 0; + + for (; i < lud.upper_bound; i += lud.batch_size) + { + std::swap(s0[i ], s1[i ]); + std::swap(s0[i + 1], s1[i + 1]); + std::swap(s0[i + 2], s1[i + 2]); + std::swap(s0[i + 3], s1[i + 3]); + #ifndef exprtk_disable_superscalar_unroll + std::swap(s0[i + 4], s1[i + 4]); + std::swap(s0[i + 5], s1[i + 5]); + std::swap(s0[i + 6], s1[i + 6]); + std::swap(s0[i + 7], s1[i + 7]); + #endif + } + + switch (lud.remainder) + { + #ifndef exprtk_disable_superscalar_unroll + case 7 : { std::swap(s0[i],s1[i]); ++i; } + case 6 : { std::swap(s0[i],s1[i]); ++i; } + case 5 : { std::swap(s0[i],s1[i]); ++i; } + case 4 : { std::swap(s0[i],s1[i]); ++i; } + #endif + case 3 : { std::swap(s0[i],s1[i]); ++i; } + case 2 : { std::swap(s0[i],s1[i]); ++i; } + case 1 : { std::swap(s0[i],s1[i]); ++i; } + } + } + } + + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_strswap; + } + + private: + + swap_genstrings_node(swap_genstrings_node&); + swap_genstrings_node& operator=(swap_genstrings_node&); + + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + range_ptr str0_range_ptr_; + range_ptr str1_range_ptr_; + bool initialised_; + }; + template class stringvar_size_node : public expression_node { @@ -7765,7 +7924,7 @@ namespace exprtk const char* base() const { - return value_.data(); + return &value_[0]; } std::size_t size() const @@ -7885,7 +8044,7 @@ namespace exprtk const char* base() const { - return value_.data(); + return &value_[0]; } std::size_t size() const @@ -8491,28 +8650,6 @@ namespace exprtk vector_elem_node* vec_node_ptr_; }; - namespace loop_unroll - { - #ifndef exprtk_disable_superscalar_unroll - const std::size_t loop_batch_size = 8; - #else - const std::size_t loop_batch_size = 4; - #endif - - struct details - { - details(const std::size_t& vsize) - : batch_size(loop_batch_size), - remainder (vsize % batch_size), - upper_bound(static_cast(vsize - (remainder ? loop_batch_size : 0))) - {} - - int batch_size; - int remainder; - int upper_bound; - }; - } - template class assignment_vec_node : public binary_node , public vector_interface @@ -10519,7 +10656,7 @@ namespace exprtk const char* base() const { - return ret_string_.data(); + return &ret_string_[0]; } std::size_t size() const @@ -21185,30 +21322,44 @@ namespace exprtk { const std::string symbol = current_token().value; - if (!symtab_store_.is_conststr_stringvar(symbol)) - { - set_error( - make_error(parser_error::e_syntax, - current_token(), - "ERR101 - Unknown string symbol")); - - return error_node(); - } - - expression_node_ptr result = symtab_store_.get_stringvar(symbol); - typedef details::stringvar_node* strvar_node_t; + + expression_node_ptr result = error_node(); strvar_node_t const_str_node = static_cast(0); + bool is_const_string = false; - const bool is_const_string = symtab_store_.is_constant_string(symbol); + scope_element& se = sem_.get_active_element(symbol); - if (is_const_string) + if (scope_element::e_string == se.type) { - const_str_node = static_cast(result); - result = expression_generator_(const_str_node->str()); + se.active = true; + result = se.str_node; + lodge_symbol(symbol,e_st_local_string); } + else + { + if (!symtab_store_.is_conststr_stringvar(symbol)) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR101 - Unknown string symbol")); - lodge_symbol(symbol,e_st_string); + return error_node(); + } + + result = symtab_store_.get_stringvar(symbol); + + is_const_string = symtab_store_.is_constant_string(symbol); + + if (is_const_string) + { + const_str_node = static_cast(result); + result = expression_generator_(const_str_node->str()); + } + + lodge_symbol(symbol,e_st_string); + } if (peek_token_is(token_t::e_lsqrbracket)) { @@ -23122,12 +23273,7 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities else if (scope_element::e_string == se.type) { - se.active = true; - lodge_symbol(symbol,e_st_local_string); - - next_token(); - - return se.str_node; + return parse_string(); } #endif } @@ -25895,7 +26041,10 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities else if (v0_is_str && v1_is_str) { - result = node_allocator_->allocate >(branch[0],branch[1]); + if (is_string_node(branch[0]) && is_string_node(branch[1])) + result = node_allocator_->allocate >(branch[0],branch[1]); + else + result = node_allocator_->allocate >(branch[0],branch[1]); } #endif else diff --git a/exprtk_simple_example_09.cpp b/exprtk_simple_example_09.cpp index 93a866e..2ee1a23 100644 --- a/exprtk_simple_example_09.cpp +++ b/exprtk_simple_example_09.cpp @@ -44,8 +44,8 @@ void primes() .add( function_t( "is_prime_impl1", - "if(y == 1,true, " - " if(0 == (x % y),false, " + "if (y == 1,true, " + " if (0 == (x % y),false, " " is_prime_impl1(x,y - 1)))", "x","y")); @@ -53,8 +53,8 @@ void primes() .add( function_t( "is_prime1", - "if(frac(x) != 0, false, " - " if(x <= 0, false, " + "if (frac(x) != 0, false, " + " if (x <= 0, false, " " is_prime_impl1(x,min(x - 1,trunc(sqrt(x)) + 1))))", "x")); diff --git a/exprtk_test.cpp b/exprtk_test.cpp index d7125ac..7003572 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -2313,7 +2313,69 @@ inline bool run_test02() test_ab("a[] > 2 and a like b ","abc","*bc",T(1.0)), test_ab("a[] > 2 and a ilike b","abc","*Bc",T(1.0)), - test_ab("a[] > 2 and a in b ","abc","123abc123",T(1.0)) + test_ab("a[] > 2 and a in b ","abc","123abc123",T(1.0)), + + test_ab("a[0:0] := b[ :]; a == '0XXXX'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:1] := b[ :]; a == '01XXX'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:2] := b[ :]; a == '012XX'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:3] := b[ :]; a == '0123X'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:4] := b[ :]; a == '01234'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:0] := b[6:]; a == '6XXXX'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:1] := b[6:]; a == '67XXX'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:2] := b[6:]; a == '678XX'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:3] := b[6:]; a == '6789X'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:4] := b[6:]; a == '67890'", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:0] <=> b[ :]; (a == '0XXXX') and (b == 'X1234567890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:1] <=> b[ :]; (a == '01XXX') and (b == 'XX234567890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:2] <=> b[ :]; (a == '012XX') and (b == 'XXX34567890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:3] <=> b[ :]; (a == '0123X') and (b == 'XXXX4567890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:4] <=> b[ :]; (a == '01234') and (b == 'XXXXX567890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:0] <=> b[6:]; (a == '6XXXX') and (b == '012345X7890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:1] <=> b[6:]; (a == '67XXX') and (b == '012345XX890')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:2] <=> b[6:]; (a == '678XX') and (b == '012345XXX90')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:3] <=> b[6:]; (a == '6789X') and (b == '012345XXXX0')", "XXXXX","01234567890",T(1.0)), + test_ab("a[0:4] <=> b[6:]; (a == '67890') and (b == '012345XXXXX')", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+0] := b[:]; a == '0XXXX'", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+1] := b[:]; a == '01XXX'", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+2] := b[:]; a == '012XX'", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+3] := b[:]; a == '0123X'", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+4] := b[:]; a == '01234'", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+0] <=> b[:]; (a == '0XXXX') and (b == 'X1234567890')", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+1] <=> b[:]; (a == '01XXX') and (b == 'XX234567890')", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+2] <=> b[:]; (a == '012XX') and (b == 'XXX34567890')", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+3] <=> b[:]; (a == '0123X') and (b == 'XXXX4567890')", "XXXXX","01234567890",T(1.0)), + test_ab("var i := 0; a[0:i+4] <=> b[:]; (a == '01234') and (b == 'XXXXX567890')", "XXXXX","01234567890",T(1.0)), + + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:0] := y[:]; x == '0XXXX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:1] := y[:]; x == '01XXX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:2] := y[:]; x == '012XX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:3] := y[:]; x == '0123X'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:4] := y[:]; x == '01234'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:0] := y[6:]; x == '6XXXX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:1] := y[6:]; x == '67XXX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:2] := y[6:]; x == '678XX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:3] := y[6:]; x == '6789X'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:4] := y[6:]; x == '67890'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:0] <=> y[:]; (x == '0XXXX') and (y == 'X1234567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:1] <=> y[:]; (x == '01XXX') and (y == 'XX234567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:2] <=> y[:]; (x == '012XX') and (y == 'XXX34567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:3] <=> y[:]; (x == '0123X') and (y == 'XXXX4567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:4] <=> y[:]; (x == '01234') and (y == 'XXXXX567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:0] <=> y[6:]; (x == '6XXXX') and (y == '012345X7890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:1] <=> y[6:]; (x == '67XXX') and (y == '012345XX890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:2] <=> y[6:]; (x == '678XX') and (y == '012345XXX90')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:3] <=> y[6:]; (x == '6789X') and (y == '012345XXXX0')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; x[0:4] <=> y[6:]; (x == '67890') and (y == '012345XXXXX')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+0] := y[:]; x == '0XXXX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+1] := y[:]; x == '01XXX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+2] := y[:]; x == '012XX'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+3] := y[:]; x == '0123X'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+4] := y[:]; x == '01234'", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+0] <=> y[:]; (x == '0XXXX') and (y == 'X1234567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+1] <=> y[:]; (x == '01XXX') and (y == 'XX234567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+2] <=> y[:]; (x == '012XX') and (y == 'XXX34567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+3] <=> y[:]; (x == '0123X') and (y == 'XXXX4567890')", "","",T(1.0)), + test_ab("var x := 'XXXXX'; var y := '01234567890'; var i := 0; x[0:i+4] <=> y[:]; (x == '01234') and (y == 'XXXXX567890')", "","",T(1.0)) }; static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_ab); diff --git a/readme.txt b/readme.txt index 8e6ebbc..b1c84d3 100644 --- a/readme.txt +++ b/readme.txt @@ -849,8 +849,8 @@ The reason for the above complexity and restrictions of deep copies for the expression and symbol_table components is because expressions may include user defined variables or functions. These are embedded as references into the expression's AST. When copying an expression, said -references need to also be copied. if the references are blindly -copied, then it will result in two or more identical expressions +references need to also be copied. If the references are blindly +copied, it will then result in two or more identical expressions utilizing the exact same references for variables. This obviously is not the default assumed scenario and will give rise to non-obvious behaviours when using the expressions in various contexts such as