diff --git a/bench_expr_weird.txt b/bench_expr_weird.txt index 9ae28d6..6e59da3 100644 --- a/bench_expr_weird.txt +++ b/bench_expr_weird.txt @@ -8,6 +8,9 @@ +2+1 +2-1 -2-1 +2-+1 +2+-1 +2--1 +2-+1 +2+-1 +2--1 diff --git a/exprtk/Makefile b/exprtk/Makefile index 76d43a0..bdae9b1 100644 --- a/exprtk/Makefile +++ b/exprtk/Makefile @@ -2,7 +2,7 @@ # ************************************************************** # * C++ Mathematical Expression Toolkit Library * # * * -# * Author: Arash Partow (1999-2020) * +# * Author: Arash Partow (1999-2021) * # * URL: http://www.partow.net/programming/exprtk/index.html * # * * # * Copyright notice: * diff --git a/exprtk/exprtk.hpp b/exprtk/exprtk.hpp index 75ee0de..0375189 100644 --- a/exprtk/exprtk.hpp +++ b/exprtk/exprtk.hpp @@ -2,7 +2,7 @@ ****************************************************************** * C++ Mathematical Expression Toolkit Library * * * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * @@ -35,6 +35,7 @@ #include +#include #include #include #include @@ -83,12 +84,14 @@ namespace exprtk namespace details { - typedef unsigned char uchar_t; - typedef char char_t; - typedef uchar_t* uchar_ptr; - typedef char_t* char_ptr; - typedef uchar_t const* uchar_cptr; - typedef char_t const* char_cptr; + typedef unsigned char uchar_t; + typedef char char_t; + typedef uchar_t* uchar_ptr; + typedef char_t* char_ptr; + typedef uchar_t const* uchar_cptr; + typedef char_t const* char_cptr; + typedef unsigned long long int _uint64_t; + typedef long long int _int64_t; inline bool is_whitespace(const char_t c) { @@ -162,6 +165,12 @@ namespace exprtk ('\'' != c); } + inline bool is_valid_string_char(const char_t c) + { + return std::isprint(static_cast(c)) || + is_whitespace(c); + } + #ifndef exprtk_disable_caseinsensitivity inline void case_normalise(std::string& s) { @@ -308,31 +317,30 @@ namespace exprtk } template - inline void parse_hex(Iterator& itr, Iterator end, std::string::value_type& result) + inline bool parse_hex(Iterator& itr, Iterator end, + std::string::value_type& result) { if ( - (end != (itr )) && - (end != (itr + 1)) && - (end != (itr + 2)) && - (end != (itr + 3)) && - ('0' == *(itr )) && - ( - ('x' == *(itr + 1)) || - ('X' == *(itr + 1)) - ) && - (is_hex_digit(*(itr + 2))) && - (is_hex_digit(*(itr + 3))) + (end == (itr )) || + (end == (itr + 1)) || + (end == (itr + 2)) || + (end == (itr + 3)) || + ('0' != *(itr )) || + ('X' != std::toupper(*(itr + 1))) || + (!is_hex_digit(*(itr + 2))) || + (!is_hex_digit(*(itr + 3))) ) { - result = hex_to_bin(static_cast(*(itr + 2))) << 4 | - hex_to_bin(static_cast(*(itr + 3))) ; - itr += 3; + return false; } - else - result = '\0'; + + result = hex_to_bin(static_cast(*(itr + 2))) << 4 | + hex_to_bin(static_cast(*(itr + 3))) ; + + return true; } - inline void cleanup_escapes(std::string& s) + inline bool cleanup_escapes(std::string& s) { typedef std::string::iterator str_itr_t; @@ -346,36 +354,41 @@ namespace exprtk { if ('\\' == (*itr1)) { - ++removal_count; - if (end == ++itr1) - break; - else if ('\\' != (*itr1)) { - switch (*itr1) - { - case 'n' : (*itr1) = '\n'; break; - case 'r' : (*itr1) = '\r'; break; - case 't' : (*itr1) = '\t'; break; - case '0' : parse_hex(itr1, end, (*itr1)); - removal_count += 3; - break; - } - - continue; + return false; } + else if (parse_hex(itr1, end, *itr2)) + { + itr1+= 4; + itr2+= 1; + removal_count +=4; + } + else if ('a' == (*itr1)) { (*itr2++) = '\a'; ++itr1; ++removal_count; } + else if ('b' == (*itr1)) { (*itr2++) = '\b'; ++itr1; ++removal_count; } + else if ('f' == (*itr1)) { (*itr2++) = '\f'; ++itr1; ++removal_count; } + else if ('n' == (*itr1)) { (*itr2++) = '\n'; ++itr1; ++removal_count; } + else if ('r' == (*itr1)) { (*itr2++) = '\r'; ++itr1; ++removal_count; } + else if ('t' == (*itr1)) { (*itr2++) = '\t'; ++itr1; ++removal_count; } + else if ('v' == (*itr1)) { (*itr2++) = '\v'; ++itr1; ++removal_count; } + else if ('0' == (*itr1)) { (*itr2++) = '\0'; ++itr1; ++removal_count; } + else + { + (*itr2++) = (*itr1++); + ++removal_count; + } + continue; } - - if (itr1 != itr2) - { - (*itr2) = (*itr1); - } - - ++itr1; - ++itr2; + else + (*itr2++) = (*itr1++); } + if ((removal_count > s.size()) || (0 == removal_count)) + return false; + s.resize(s.size() - removal_count); + + return true; } class build_string @@ -643,23 +656,19 @@ namespace exprtk inline bool wc_match(const std::string& wild_card, const std::string& str) { - return match_impl(wild_card.data(), - wild_card.data() + wild_card.size(), - str.data(), - str.data() + str.size(), - '*', - '?'); + return match_impl( + wild_card.data(), wild_card.data() + wild_card.size(), + str.data(), str.data() + str.size(), + '*', '?'); } inline bool wc_imatch(const std::string& wild_card, const std::string& str) { - return match_impl(wild_card.data(), - wild_card.data() + wild_card.size(), - str.data(), - str.data() + str.size(), - '*', - '?'); + return match_impl( + wild_card.data(), wild_card.data() + wild_card.size(), + str.data(), str.data() + str.size(), + '*', '?'); } inline bool sequence_match(const std::string& pattern, @@ -773,15 +782,15 @@ namespace exprtk }; #define exprtk_register_real_type_tag(T) \ - template<> struct number_type \ + template <> struct number_type \ { typedef real_type_tag type; number_type() {} }; \ #define exprtk_register_complex_type_tag(T) \ - template<> struct number_type > \ + template <> struct number_type > \ { typedef complex_type_tag type; number_type() {} }; \ #define exprtk_register_int_type_tag(T) \ - template<> struct number_type \ + template <> struct number_type \ { typedef int_type_tag type; number_type() {} }; \ exprtk_register_real_type_tag(double ) @@ -792,45 +801,34 @@ namespace exprtk exprtk_register_complex_type_tag(long double) exprtk_register_complex_type_tag(float ) - exprtk_register_int_type_tag(short ) - exprtk_register_int_type_tag(int ) - exprtk_register_int_type_tag(long long int ) - exprtk_register_int_type_tag(unsigned short ) - exprtk_register_int_type_tag(unsigned int ) - exprtk_register_int_type_tag(unsigned long long int) + exprtk_register_int_type_tag(short ) + exprtk_register_int_type_tag(int ) + exprtk_register_int_type_tag(_int64_t ) + exprtk_register_int_type_tag(unsigned short) + exprtk_register_int_type_tag(unsigned int ) + exprtk_register_int_type_tag(_uint64_t ) #undef exprtk_register_real_type_tag #undef exprtk_register_int_type_tag template - struct epsilon_type - { - static inline T value() - { - const T epsilon = T(0.0000000001); - return epsilon; - } - }; + struct epsilon_type {}; - template <> - struct epsilon_type - { - static inline float value() - { - const float epsilon = float(0.000001f); - return epsilon; - } - }; + #define exprtk_define_epsilon_type(Type, Epsilon) \ + template <> struct epsilon_type \ + { \ + static inline Type value() \ + { \ + const Type epsilon = static_cast(Epsilon); \ + return epsilon; \ + } \ + }; \ - template <> - struct epsilon_type - { - static inline long double value() - { - const long double epsilon = (long double)(0.000000000001); - return epsilon; - } - }; + exprtk_define_epsilon_type(float , 0.000001f) + exprtk_define_epsilon_type(double , 0.0000000001) + exprtk_define_epsilon_type(long double, 0.000000000001) + + #undef exprtk_define_epsilon_type template inline bool is_nan_impl(const T v, real_type_tag) @@ -845,9 +843,9 @@ namespace exprtk } template - inline long long int to_int64_impl(const T v, real_type_tag) + inline _int64_t to_int64_impl(const T v, real_type_tag) { - return static_cast(v); + return static_cast<_int64_t>(v); } template @@ -1028,7 +1026,7 @@ namespace exprtk template inline T roundn_impl(const T v0, const T v1, real_type_tag) { - const int index = std::max(0, std::min(pow10_size - 1, (int)std::floor(v1))); + const int index = std::max(0, std::min(pow10_size - 1, static_cast(std::floor(v1)))); const T p10 = T(pow10[index]); if (v0 < T(0)) @@ -1372,10 +1370,10 @@ namespace exprtk template struct numeric_info { enum { length = 0, size = 32, bound_length = 0, min_exp = 0, max_exp = 0 }; }; - template<> struct numeric_info { enum { length = 10, size = 16, bound_length = 9}; }; - template<> struct numeric_info { enum { min_exp = -38, max_exp = +38}; }; - template<> struct numeric_info { enum { min_exp = -308, max_exp = +308}; }; - template<> struct numeric_info { enum { min_exp = -308, max_exp = +308}; }; + template <> struct numeric_info { enum { length = 10, size = 16, bound_length = 9}; }; + template <> struct numeric_info { enum { min_exp = -38, max_exp = +38}; }; + template <> struct numeric_info { enum { min_exp = -308, max_exp = +308}; }; + template <> struct numeric_info { enum { min_exp = -308, max_exp = +308}; }; template inline int to_int32(const T v) @@ -1385,7 +1383,7 @@ namespace exprtk } template - inline long long int to_int64(const T v) + inline _int64_t to_int64(const T v) { const typename details::number_type::type num_type; return to_int64_impl(v, num_type); @@ -1562,17 +1560,17 @@ namespace exprtk } }; - template struct fast_exp { static inline T result(T v) { T v_5 = fast_exp::result(v); return v_5 * v_5; } }; - template struct fast_exp { static inline T result(T v) { return fast_exp::result(v) * v; } }; - template struct fast_exp { static inline T result(T v) { T v_4 = fast_exp::result(v); return v_4 * v_4; } }; - template struct fast_exp { static inline T result(T v) { return fast_exp::result(v) * v; } }; - template struct fast_exp { static inline T result(T v) { T v_3 = fast_exp::result(v); return v_3 * v_3; } }; - template struct fast_exp { static inline T result(T v) { return fast_exp::result(v) * v; } }; - template struct fast_exp { static inline T result(T v) { T v_2 = v * v; return v_2 * v_2; } }; - template struct fast_exp { static inline T result(T v) { return v * v * v; } }; - template struct fast_exp { static inline T result(T v) { return v * v; } }; - template struct fast_exp { static inline T result(T v) { return v; } }; - template struct fast_exp { static inline T result(T ) { return T(1); } }; + template struct fast_exp { static inline T result(const T v) { T v_5 = fast_exp::result(v); return v_5 * v_5; } }; + template struct fast_exp { static inline T result(const T v) { return fast_exp::result(v) * v; } }; + template struct fast_exp { static inline T result(const T v) { T v_4 = fast_exp::result(v); return v_4 * v_4; } }; + template struct fast_exp { static inline T result(const T v) { return fast_exp::result(v) * v; } }; + template struct fast_exp { static inline T result(const T v) { T v_3 = fast_exp::result(v); return v_3 * v_3; } }; + template struct fast_exp { static inline T result(const T v) { return fast_exp::result(v) * v; } }; + template struct fast_exp { static inline T result(const T v) { T v_2 = v * v; return v_2 * v_2; } }; + template struct fast_exp { static inline T result(const T v) { return v * v * v; } }; + template struct fast_exp { static inline T result(const T v) { return v * v; } }; + template struct fast_exp { static inline T result(const T v) { return v; } }; + template struct fast_exp { static inline T result(const T ) { return T(1); } }; #define exprtk_define_unary_function(FunctionName) \ template \ @@ -1827,6 +1825,13 @@ namespace exprtk return true; } + template + inline bool valid_exponent(const int exponent, numeric::details::real_type_tag) + { + using namespace details::numeric; + return (numeric_info::min_exp <= exponent) && (exponent <= numeric_info::max_exp); + } + template inline bool string_to_real(Iterator& itr_external, const Iterator end, T& t, numeric::details::real_type_tag) { @@ -1867,16 +1872,9 @@ namespace exprtk while ((end != itr) && (zero == (*itr))) ++itr; - unsigned int digit; - while (end != itr) { - // Note: For 'physical' superscalar architectures it - // is advised that the following loop be: 4xPD1 and 1xPD2 - #ifdef exprtk_enable_superscalar - parse_digit_1(d) - parse_digit_1(d) - #endif + unsigned int digit; parse_digit_1(d) parse_digit_1(d) parse_digit_2(d) @@ -1892,16 +1890,11 @@ namespace exprtk if ('.' == (*itr)) { const Iterator curr = ++itr; - unsigned int digit; T tmp_d = T(0); while (end != itr) { - #ifdef exprtk_enable_superscalar - parse_digit_1(tmp_d) - parse_digit_1(tmp_d) - parse_digit_1(tmp_d) - #endif + unsigned int digit; parse_digit_1(tmp_d) parse_digit_1(tmp_d) parse_digit_2(tmp_d) @@ -1910,7 +1903,13 @@ namespace exprtk if (curr != itr) { instate = true; - d += compute_pow10(tmp_d,static_cast(-std::distance(curr,itr))); + + const int frac_exponent = static_cast(-std::distance(curr, itr)); + + if (!valid_exponent(frac_exponent, numeric::details::real_type_tag())) + return false; + + d += compute_pow10(tmp_d, frac_exponent); } #undef parse_digit_1 @@ -1981,6 +1980,8 @@ namespace exprtk if ((end != itr) || (!instate)) return false; + else if (!valid_exponent(exponent, numeric::details::real_type_tag())) + return false; else if (exponent) d = compute_pow10(d,exponent); @@ -2019,6 +2020,50 @@ namespace exprtk } // namespace details + struct loop_runtime_check + { + enum loop_types + { + e_invalid = 0, + e_for_loop = 1, + e_while_loop = 2, + e_repeat_until_loop = 4, + e_all_loops = 7 + }; + + enum violation_type + { + e_unknown = 0, + e_iteration_count = 1, + e_timeout = 2 + }; + + loop_types loop_set; + + loop_runtime_check() + : loop_set(e_invalid), + max_loop_iterations(0) + {} + + details::_uint64_t max_loop_iterations; + + struct violation_context + { + loop_types loop; + violation_type violation; + details::_uint64_t iteration_count; + }; + + virtual void handle_runtime_violation(const violation_context&) + { + throw std::runtime_error("ExprTk Loop run-time violation."); + } + + virtual ~loop_runtime_check() {} + }; + + typedef loop_runtime_check* loop_runtime_check_ptr; + namespace lexer { struct token @@ -2199,7 +2244,7 @@ namespace exprtk typedef token token_t; typedef std::vector token_list_t; - typedef std::vector::iterator token_list_itr_t; + typedef token_list_t::iterator token_list_itr_t; typedef details::char_t char_t; generator() @@ -2334,9 +2379,9 @@ namespace exprtk if (finished()) return ""; else if (token_list_.begin() != token_itr_) - return std::string(base_itr_ + (token_itr_ - 1)->position,s_end_); + return std::string(base_itr_ + (token_itr_ - 1)->position, s_end_); else - return std::string(base_itr_ + token_itr_->position,s_end_); + return std::string(base_itr_ + token_itr_->position, s_end_); } private: @@ -2346,9 +2391,9 @@ namespace exprtk return (s_end_ == itr); } + #ifndef exprtk_disable_comments inline bool is_comment_start(details::char_cptr itr) { - #ifndef exprtk_disable_comments const char_t c0 = *(itr + 0); const char_t c1 = *(itr + 1); @@ -2359,9 +2404,14 @@ namespace exprtk if (('/' == c0) && ('/' == c1)) return true; if (('/' == c0) && ('*' == c1)) return true; } - #endif return false; } + #else + inline bool is_comment_start(details::char_cptr) + { + return false; + } + #endif inline void skip_whitespace() { @@ -2637,6 +2687,7 @@ namespace exprtk { t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_); token_list_.push_back(t); + return; } @@ -2715,7 +2766,10 @@ namespace exprtk // $fdd(x,x,x) = at least 11 chars if (std::distance(s_itr_,s_end_) < 11) { - t.set_error(token::e_err_sfunc, initial_itr, s_itr_, base_itr_); + t.set_error( + token::e_err_sfunc, + initial_itr, std::min(initial_itr + 11, s_end_), + base_itr_); token_list_.push_back(t); return; @@ -2728,7 +2782,10 @@ namespace exprtk (details::is_digit(*(s_itr_ + 3)))) ) { - t.set_error(token::e_err_sfunc, initial_itr, s_itr_, base_itr_); + t.set_error( + token::e_err_sfunc, + initial_itr, std::min(initial_itr + 4, s_end_), + base_itr_); token_list_.push_back(t); return; @@ -2752,6 +2809,7 @@ namespace exprtk { t.set_error(token::e_err_string, s_itr_, s_end_, base_itr_); token_list_.push_back(t); + return; } @@ -2762,7 +2820,14 @@ namespace exprtk while (!is_end(s_itr_)) { - if (!escaped && ('\\' == *s_itr_)) + if (!details::is_valid_string_char(*s_itr_)) + { + t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } + else if (!escaped && ('\\' == *s_itr_)) { escaped_found = true; escaped = true; @@ -2777,28 +2842,17 @@ namespace exprtk } else if (escaped) { - if (!is_end(s_itr_) && ('0' == *(s_itr_))) + if ( + !is_end(s_itr_) && ('0' == *(s_itr_)) && + ((s_itr_ + 4) <= s_end_) + ) { - /* - Note: The following 'awkward' conditional is - due to various broken msvc compilers. - */ - #if defined(_MSC_VER) && (_MSC_VER == 1600) - const bool within_range = !is_end(s_itr_ + 2) && - !is_end(s_itr_ + 3) ; - #else - const bool within_range = !is_end(s_itr_ + 1) && - !is_end(s_itr_ + 2) && - !is_end(s_itr_ + 3) ; - #endif - - const bool x_seperator = ('x' == *(s_itr_ + 1)) || - ('X' == *(s_itr_ + 1)) ; + const bool x_seperator = ('X' == std::toupper(*(s_itr_ + 1))); - const bool both_digits = details::is_hex_digit(*(s_itr_ + 2)) && - details::is_hex_digit(*(s_itr_ + 3)) ; + const bool both_digits = details::is_hex_digit(*(s_itr_ + 2)) && + details::is_hex_digit(*(s_itr_ + 3)) ; - if (!within_range || !x_seperator || !both_digits) + if (!(x_seperator && both_digits)) { t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); token_list_.push_back(t); @@ -2829,7 +2883,13 @@ namespace exprtk { std::string parsed_string(initial_itr,s_itr_); - details::cleanup_escapes(parsed_string); + if (!details::cleanup_escapes(parsed_string)) + { + t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_); + token_list_.push_back(t); + + return; + } t.set_string( parsed_string, @@ -2845,10 +2905,10 @@ namespace exprtk private: - token_list_t token_list_; - token_list_itr_t token_itr_; - token_list_itr_t store_token_itr_; - token_t eof_token_; + token_list_t token_list_; + token_list_itr_t token_itr_; + token_list_itr_t store_token_itr_; + token_t eof_token_; details::char_cptr base_itr_; details::char_cptr s_itr_; details::char_cptr s_end_; @@ -3018,6 +3078,10 @@ namespace exprtk std::size_t changes = 0; + typedef std::pair insert_t; + std::vector insert_list; + insert_list.reserve(10000); + for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i) { int insert_index = -1; @@ -3041,17 +3105,36 @@ namespace exprtk break; } - typedef std::iterator_traits::difference_type diff_t; - if ((insert_index >= 0) && (insert_index <= (static_cast(stride_) + 1))) { - g.token_list_.insert( - g.token_list_.begin() + static_cast(i + static_cast(insert_index)), t); - + insert_list.push_back(insert_t(i, t)); changes++; } } + if (!insert_list.empty()) + { + generator::token_list_t token_list; + + std::size_t insert_index = 0; + + for (std::size_t i = 0; i < g.token_list_.size(); ++i) + { + token_list.push_back(g.token_list_[i]); + + if ( + (insert_index < insert_list.size()) && + (insert_list[insert_index].first == i) + ) + { + token_list.push_back(insert_list[insert_index].second); + insert_index++; + } + } + + std::swap(g.token_list_,token_list); + } + return changes; } @@ -3110,59 +3193,86 @@ namespace exprtk inline std::size_t process_stride_2(generator& g) { - typedef std::iterator_traits::difference_type diff_t; - if (g.token_list_.size() < 2) return 0; std::size_t changes = 0; + generator::token_list_t token_list; + token_list.reserve(10000); + for (int i = 0; i < static_cast(g.token_list_.size() - 1); ++i) { token t; - while (join(g[i], g[i + 1], t)) + for ( ; ; ) { - g.token_list_[i] = t; + if (!join(g[i], g[i + 1], t)) + { + token_list.push_back(g[i]); + break; + } - g.token_list_.erase(g.token_list_.begin() + static_cast(i + 1)); + token_list.push_back(t); ++changes; - if (static_cast(i + 1) >= g.token_list_.size()) + i+=2; + + if (static_cast(i) >= g.token_list_.size() - 1) break; } } + token_list.push_back(g.token_list_.back()); + + assert(token_list.size() <= g.token_list_.size()); + + std::swap(token_list, g.token_list_); + return changes; } inline std::size_t process_stride_3(generator& g) { - typedef std::iterator_traits::difference_type diff_t; - if (g.token_list_.size() < 3) return 0; std::size_t changes = 0; + generator::token_list_t token_list; + token_list.reserve(10000); + for (int i = 0; i < static_cast(g.token_list_.size() - 2); ++i) { token t; - while (join(g[i], g[i + 1], g[i + 2], t)) + for ( ; ; ) { - g.token_list_[i] = t; + if (!join(g[i], g[i + 1], g[i + 2], t)) + { + token_list.push_back(g[i]); + break; + } + + token_list.push_back(t); - g.token_list_.erase(g.token_list_.begin() + static_cast(i + 1), - g.token_list_.begin() + static_cast(i + 3)); ++changes; - if (static_cast(i + 2) >= g.token_list_.size()) + i+=3; + + if (static_cast(i) >= g.token_list_.size() - 2) break; } } + token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 2)); + token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 1)); + + assert(token_list.size() <= g.token_list_.size()); + + std::swap(token_list, g.token_list_); + return changes; } @@ -3172,11 +3282,11 @@ namespace exprtk namespace helper { - inline void dump(lexer::generator& generator) + inline void dump(const lexer::generator& generator) { for (std::size_t i = 0; i < generator.size(); ++i) { - lexer::token t = generator[i]; + const lexer::token& t = generator[i]; printf("Token[%02d] @ %03d %6s --> '%s'\n", static_cast(i), static_cast(t.position), @@ -4232,14 +4342,14 @@ namespace exprtk inline vector_view make_vector_view(T* data, const std::size_t size, const std::size_t offset = 0) { - return vector_view(data + offset,size); + return vector_view(data + offset, size); } template inline vector_view make_vector_view(std::vector& v, const std::size_t size, const std::size_t offset = 0) { - return vector_view(v.data() + offset,size); + return vector_view(v.data() + offset, size); } template class results_context; @@ -4274,7 +4384,7 @@ namespace exprtk { public: - parameter_list(std::vector& pl) + explicit parameter_list(std::vector& pl) : parameter_list_(pl) {} @@ -4331,12 +4441,12 @@ namespace exprtk typedef type_store type_store_t; typedef ViewType value_t; - type_view(type_store_t& ts) + explicit type_view(type_store_t& ts) : ts_(ts), data_(reinterpret_cast(ts_.data)) {} - type_view(const type_store_t& ts) + explicit type_view(const type_store_t& ts) : ts_(const_cast(ts)), data_(reinterpret_cast(ts_.data)) {} @@ -4381,11 +4491,11 @@ namespace exprtk typedef type_store type_store_t; typedef T value_t; - scalar_view(type_store_t& ts) + explicit scalar_view(type_store_t& ts) : v_(*reinterpret_cast(ts.data)) {} - scalar_view(const type_store_t& ts) + explicit scalar_view(const type_store_t& ts) : v_(*reinterpret_cast(const_cast(ts).data)) {} @@ -4573,27 +4683,33 @@ namespace exprtk { switch (opr) { - case e_add : return "+"; - case e_sub : return "-"; - case e_mul : return "*"; - case e_div : return "/"; - case e_mod : return "%"; - case e_pow : return "^"; - case e_assign : return ":="; - case e_addass : return "+="; - case e_subass : return "-="; - case e_mulass : return "*="; - case e_divass : return "/="; - case e_modass : return "%="; - case e_lt : return "<"; - case e_lte : return "<="; - case e_eq : return "=="; - case e_equal : return "="; - case e_ne : return "!="; - case e_nequal : return "<>"; - case e_gte : return ">="; - case e_gt : return ">"; - default : return"N/A"; + case e_add : return "+" ; + case e_sub : return "-" ; + case e_mul : return "*" ; + case e_div : return "/" ; + case e_mod : return "%" ; + case e_pow : return "^" ; + case e_assign : return ":=" ; + case e_addass : return "+=" ; + case e_subass : return "-=" ; + case e_mulass : return "*=" ; + case e_divass : return "/=" ; + case e_modass : return "%=" ; + case e_lt : return "<" ; + case e_lte : return "<=" ; + case e_eq : return "==" ; + case e_equal : return "=" ; + case e_ne : return "!=" ; + case e_nequal : return "<>" ; + case e_gte : return ">=" ; + case e_gt : return ">" ; + case e_and : return "and" ; + case e_or : return "or" ; + case e_xor : return "xor" ; + case e_nand : return "nand"; + case e_nor : return "nor" ; + case e_xnor : return "xnor"; + default : return "N/A" ; } } @@ -4618,8 +4734,8 @@ namespace exprtk struct details { - details(const std::size_t& vsize, - const unsigned int loop_batch_size = global_loop_batch_size) + explicit details(const std::size_t& vsize, + const unsigned int loop_batch_size = global_loop_batch_size) : batch_size(loop_batch_size ), remainder (vsize % batch_size), upper_bound(static_cast(vsize - (remainder ? loop_batch_size : 0))) @@ -4666,7 +4782,7 @@ namespace exprtk destruct (true) {} - control_block(const std::size_t& dsize) + explicit control_block(const std::size_t& dsize) : ref_count(1 ), size (dsize), data (0 ), @@ -4733,7 +4849,7 @@ namespace exprtk { destruct = true; data = new T[size]; - std::fill_n(data,size,T(0)); + std::fill_n(data, size, T(0)); dump_ptr("control_block::create_data() - data",data,size); } }; @@ -4744,8 +4860,8 @@ namespace exprtk : control_block_(control_block::create(0)) {} - vec_data_store(const std::size_t& size) - : control_block_(control_block::create(size,(data_t)(0),true)) + explicit vec_data_store(const std::size_t& size) + : control_block_(control_block::create(size,reinterpret_cast(0),true)) {} vec_data_store(const std::size_t& size, data_t data, bool dstrct = false) @@ -4830,7 +4946,7 @@ namespace exprtk static inline void match_sizes(type& vds0, type& vds1) { - std::size_t size = min_size(vds0.control_block_,vds1.control_block_); + const std::size_t size = min_size(vds0.control_block_,vds1.control_block_); vds0.control_block_->size = size; vds1.control_block_->size = size; } @@ -4998,8 +5114,24 @@ namespace exprtk } } + template + struct node_collector_interface + { + typedef Node* node_ptr_t; + typedef Node** node_pp_t; + typedef std::vector noderef_list_t; + + virtual ~node_collector_interface() {} + + virtual void collect_nodes(noderef_list_t&) {} + }; + + template + struct node_depth_base; + template - class expression_node + class expression_node : public node_collector_interface >, + public node_depth_base > { public: @@ -5046,6 +5178,9 @@ namespace exprtk typedef T value_type; typedef expression_node* expression_ptr; + typedef node_collector_interface > nci_t; + typedef typename nci_t::noderef_list_t noderef_list_t; + typedef node_depth_base > ndb_t; virtual ~expression_node() {} @@ -5096,12 +5231,24 @@ namespace exprtk return std::not_equal_to()(T(0),node->value()); } + template + inline bool is_true(const std::pair*,bool>& node) + { + return std::not_equal_to()(T(0),node.first->value()); + } + template inline bool is_false(const expression_node* node) { return std::equal_to()(T(0),node->value()); } + template + inline bool is_false(const std::pair*,bool>& node) + { + return std::equal_to()(T(0),node.first->value()); + } + template inline bool is_unary_node(const expression_node* node) { @@ -5244,7 +5391,8 @@ namespace exprtk template inline bool branch_deletable(expression_node* node) { - return !is_variable_node(node) && + return (0 != node) && + !is_variable_node(node) && !is_string_node (node) ; } @@ -5302,6 +5450,76 @@ namespace exprtk return true; } + template + class node_collection_destructor + { + public: + + typedef node_collector_interface nci_t; + + typedef typename nci_t::node_ptr_t node_ptr_t; + typedef typename nci_t::node_pp_t node_pp_t; + typedef typename nci_t::noderef_list_t noderef_list_t; + + static void delete_nodes(node_ptr_t& root) + { + std::vector node_delete_list; + node_delete_list.reserve(1000); + + collect_nodes(root, node_delete_list); + + for (std::size_t i = 0; i < node_delete_list.size(); ++i) + { + node_ptr_t& node = *node_delete_list[i]; + exprtk_debug(("ncd::delete_nodes() - deleting: %p\n", static_cast(node))); + delete node; + node = reinterpret_cast(0); + } + } + + private: + + static void collect_nodes(node_ptr_t& root, noderef_list_t& node_delete_list) + { + std::deque node_list; + node_list.push_back(root); + node_delete_list.push_back(&root); + + noderef_list_t child_node_delete_list; + child_node_delete_list.reserve(1000); + + while (!node_list.empty()) + { + node_list.front()->collect_nodes(child_node_delete_list); + + if (!child_node_delete_list.empty()) + { + for (std::size_t i = 0; i < child_node_delete_list.size(); ++i) + { + node_pp_t& node = child_node_delete_list[i]; + + if (0 == (*node)) + { + exprtk_debug(("ncd::collect_nodes() - null node encountered.\n")); + } + + node_list.push_back(*node); + } + + node_delete_list.insert( + node_delete_list.end(), + child_node_delete_list.begin(), child_node_delete_list.end()); + + child_node_delete_list.clear(); + } + + node_list.pop_front(); + } + + std::reverse(node_delete_list.begin(), node_delete_list.end()); + } + }; + template inline void free_all_nodes(NodeAllocator& node_allocator, expression_node* (&b)[N]) { @@ -5326,28 +5544,239 @@ namespace exprtk } template - inline void free_node(NodeAllocator& node_allocator, expression_node*& node, const bool force_delete = false) + inline void free_node(NodeAllocator&, expression_node*& node) { - if (0 != node) + if ((0 == node) || is_variable_node(node) || is_string_node(node)) { - if ( - (is_variable_node(node) || is_string_node(node)) || - force_delete - ) - return; - - node_allocator.free(node); - node = reinterpret_cast*>(0); + return; } + + node_collection_destructor > + ::delete_nodes(node); } template inline void destroy_node(expression_node*& node) { - delete node; - node = reinterpret_cast*>(0); + if (0 != node) + { + node_collection_destructor > + ::delete_nodes(node); + } } + template + struct node_depth_base + { + node_depth_base() + : depth_set(false), + depth(0) + {} + + virtual ~node_depth_base() {} + + virtual std::size_t node_depth() const { return 1; } + + std::size_t compute_node_depth(const Node* const& node) const + { + if (!depth_set) + { + depth = 1 + (node ? node->node_depth() : 0); + depth_set = true; + } + + return depth; + } + + std::size_t compute_node_depth(const std::pair& branch) const + { + if (!depth_set) + { + depth = 1 + (branch.first ? branch.first->node_depth() : 0); + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const std::pair (&branch)[N]) const + { + if (!depth_set) + { + depth = 0; + for (std::size_t i = 0; i < N; ++i) + { + if (branch[i].first) + { + depth = std::max(depth,branch[i].first->node_depth()); + } + } + depth += 1; + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1) const + { + if (!depth_set) + { + depth = 1 + std::max(compute_node_depth(n0), compute_node_depth(n1)); + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1, + const BranchType& n2) const + { + if (!depth_set) + { + depth = 1 + std::max( + std::max(compute_node_depth(n0), compute_node_depth(n1)), + compute_node_depth(n2)); + depth_set = true; + } + + return depth; + } + + template + std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1, + const BranchType& n2, const BranchType& n3) const + { + if (!depth_set) + { + depth = 1 + std::max( + std::max(compute_node_depth(n0), compute_node_depth(n1)), + std::max(compute_node_depth(n2), compute_node_depth(n3))); + depth_set = true; + } + + return depth; + } + + template class Sequence> + std::size_t compute_node_depth(const Sequence& branch_list) const + { + if (!depth_set) + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + if (branch_list[i]) + { + depth = std::max(depth, compute_node_depth(branch_list[i])); + } + } + depth_set = true; + } + + return depth; + } + + template class Sequence> + std::size_t compute_node_depth(const Sequence,Allocator>& branch_list) const + { + if (!depth_set) + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + if (branch_list[i].first) + { + depth = std::max(depth, compute_node_depth(branch_list[i].first)); + } + } + depth_set = true; + } + + return depth; + } + + mutable bool depth_set; + mutable std::size_t depth; + + template + void collect(Node*const& node, + const bool deletable, + NodeSequence& delete_node_list) const + { + if ((0 != node) && deletable) + { + delete_node_list.push_back(const_cast(&node)); + } + } + + template + void collect(const std::pair& branch, + NodeSequence& delete_node_list) const + { + collect(branch.first, branch.second, delete_node_list); + } + + template + void collect(Node*& node, + NodeSequence& delete_node_list) const + { + collect(node, branch_deletable(node), delete_node_list); + } + + template + void collect(const std::pair(&branch)[N], + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < N; ++i) + { + collect(branch[i].first, branch[i].second, delete_node_list); + } + } + + template class Sequence, + typename NodeSequence> + void collect(const Sequence, Allocator>& branch, + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < branch.size(); ++i) + { + collect(branch[i].first, branch[i].second, delete_node_list); + } + } + + template class Sequence, + typename NodeSequence> + void collect(const Sequence& branch_list, + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + collect(branch_list[i], branch_deletable(branch_list[i]), delete_node_list); + } + } + + template class Sequence, + typename NodeSequence> + void collect(const Sequence& branch_list, + const Sequence& branch_deletable_list, + NodeSequence& delete_node_list) const + { + for (std::size_t i = 0; i < branch_list.size(); ++i) + { + collect(branch_list[i], branch_deletable_list[i], delete_node_list); + } + } + }; + template class vector_holder { @@ -5561,30 +5990,70 @@ namespace exprtk } }; + template + inline void construct_branch_pair(std::pair*,bool> (&branch)[N], + expression_node* b, + const std::size_t& index) + { + if (b && (index < N)) + { + branch[index] = std::make_pair(b,branch_deletable(b)); + } + } + + template + inline void construct_branch_pair(std::pair*,bool>& branch, expression_node* b) + { + if (b) + { + branch = std::make_pair(b,branch_deletable(b)); + } + } + + template + inline void init_branches(std::pair*,bool> (&branch)[N], + expression_node* b0, + expression_node* b1 = reinterpret_cast*>(0), + expression_node* b2 = reinterpret_cast*>(0), + expression_node* b3 = reinterpret_cast*>(0), + expression_node* b4 = reinterpret_cast*>(0), + expression_node* b5 = reinterpret_cast*>(0), + expression_node* b6 = reinterpret_cast*>(0), + expression_node* b7 = reinterpret_cast*>(0), + expression_node* b8 = reinterpret_cast*>(0), + expression_node* b9 = reinterpret_cast*>(0)) + { + construct_branch_pair(branch, b0, 0); + construct_branch_pair(branch, b1, 1); + construct_branch_pair(branch, b2, 2); + construct_branch_pair(branch, b3, 3); + construct_branch_pair(branch, b4, 4); + construct_branch_pair(branch, b5, 5); + construct_branch_pair(branch, b6, 6); + construct_branch_pair(branch, b7, 7); + construct_branch_pair(branch, b8, 8); + construct_branch_pair(branch, b9, 9); + } + template class null_eq_node : public expression_node { public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; - null_eq_node(expression_ptr brnch, const bool equality = true) - : branch_(brnch), - branch_deletable_(branch_deletable(branch_)), - equality_(equality) - {} - - ~null_eq_node() + explicit null_eq_node(expression_ptr branch, const bool equality = true) + : equality_(equality) { - if (branch_ && branch_deletable_) - { - destroy_node(branch_); - } + construct_branch_pair(branch_, branch); } inline T value() const { - const T v = branch_->value(); + assert(branch_.first); + + const T v = branch_.first->value(); const bool result = details::numeric::is_nan(v); if (result) @@ -5605,14 +6074,23 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(branch_,node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } private: - expression_ptr branch_; - const bool branch_deletable_; bool equality_; + branch_t branch_; }; template @@ -5760,25 +6238,19 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; - unary_node(const operator_type& opr, - expression_ptr brnch) - : operation_(opr), - branch_(brnch), - branch_deletable_(branch_deletable(branch_)) - {} - - ~unary_node() + unary_node(const operator_type& opr, expression_ptr branch) + : operation_(opr) { - if (branch_ && branch_deletable_) - { - destroy_node(branch_); - } + construct_branch_pair(branch_,branch); } inline T value() const { - const T arg = branch_->value(); + assert(branch_.first); + + const T arg = branch_.first->value(); return numeric::process(operation_,arg); } @@ -5795,94 +6267,28 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_; + return branch_.first; } inline void release() { - branch_deletable_ = false; + branch_.second = false; } - protected: - - operator_type operation_; - expression_ptr branch_; - bool branch_deletable_; - }; - - template - struct construct_branch_pair - { - template - static inline void process(std::pair*,bool> (&)[N], expression_node*) - {} - }; - - template - struct construct_branch_pair - { - template - static inline void process(std::pair*,bool> (&branch)[N], expression_node* b) + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) { - if (b) - { - branch[D] = std::make_pair(b,branch_deletable(b)); - } + expression_node::ndb_t::collect(branch_, node_delete_list); } - }; - template - inline void init_branches(std::pair*,bool> (&branch)[N], - expression_node* b0, - expression_node* b1 = reinterpret_cast*>(0), - expression_node* b2 = reinterpret_cast*>(0), - expression_node* b3 = reinterpret_cast*>(0), - expression_node* b4 = reinterpret_cast*>(0), - expression_node* b5 = reinterpret_cast*>(0), - expression_node* b6 = reinterpret_cast*>(0), - expression_node* b7 = reinterpret_cast*>(0), - expression_node* b8 = reinterpret_cast*>(0), - expression_node* b9 = reinterpret_cast*>(0)) - { - construct_branch_pair 0)>::process(branch,b0); - construct_branch_pair 1)>::process(branch,b1); - construct_branch_pair 2)>::process(branch,b2); - construct_branch_pair 3)>::process(branch,b3); - construct_branch_pair 4)>::process(branch,b4); - construct_branch_pair 5)>::process(branch,b5); - construct_branch_pair 6)>::process(branch,b6); - construct_branch_pair 7)>::process(branch,b7); - construct_branch_pair 8)>::process(branch,b8); - construct_branch_pair 9)>::process(branch,b9); - } - - struct cleanup_branches - { - template - static inline void execute(std::pair*,bool> (&branch)[N]) + std::size_t node_depth() const { - for (std::size_t i = 0; i < N; ++i) - { - if (branch[i].first && branch[i].second) - { - destroy_node(branch[i].first); - } - } + return expression_node::ndb_t::compute_node_depth(branch_); } - template class Sequence> - static inline void execute(Sequence*,bool>,Allocator>& branch) - { - for (std::size_t i = 0; i < branch.size(); ++i) - { - if (branch[i].first && branch[i].second) - { - destroy_node(branch[i].first); - } - } - } + protected: + + operator_type operation_; + branch_t branch_; }; template @@ -5901,13 +6307,11 @@ namespace exprtk init_branches<2>(branch_, branch0, branch1); } - ~binary_node() - { - cleanup_branches::execute(branch_); - } - inline T value() const { + assert(branch_[0].first); + assert(branch_[1].first); + const T arg0 = branch_[0].first->value(); const T arg1 = branch_[1].first->value(); @@ -5934,6 +6338,16 @@ namespace exprtk return reinterpret_cast(0); } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::template compute_node_depth<2>(branch_); + } + protected: operator_type operation_; @@ -5953,13 +6367,11 @@ namespace exprtk init_branches<2>(branch_, branch0, branch1); } - ~binary_ext_node() - { - cleanup_branches::execute(branch_); - } - inline T value() const { + assert(branch_[0].first); + assert(branch_[1].first); + const T arg0 = branch_[0].first->value(); const T arg1 = branch_[1].first->value(); @@ -5986,6 +6398,16 @@ namespace exprtk return reinterpret_cast(0); } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::template compute_node_depth<2>(branch_); + } + protected: branch_t branch_[2]; @@ -6008,13 +6430,12 @@ namespace exprtk init_branches<3>(branch_, branch0, branch1, branch2); } - ~trinary_node() - { - cleanup_branches::execute(branch_); - } - inline T value() const { + assert(branch_[0].first); + assert(branch_[1].first); + assert(branch_[2].first); + const T arg0 = branch_[0].first->value(); const T arg1 = branch_[1].first->value(); const T arg2 = branch_[2].first->value(); @@ -6040,6 +6461,16 @@ namespace exprtk return expression_node::e_trinary; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::template compute_node_depth<3>(branch_); + } + protected: operator_type operation_; @@ -6064,11 +6495,6 @@ namespace exprtk init_branches<4>(branch_, branch0, branch1, branch2, branch3); } - ~quaternary_node() - { - cleanup_branches::execute(branch_); - } - inline T value() const { return std::numeric_limits::quiet_NaN(); @@ -6079,6 +6505,16 @@ namespace exprtk return expression_node::e_quaternary; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::template compute_node_depth<4>(branch_); + } + protected: operator_type operation_; @@ -6091,42 +6527,27 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; - conditional_node(expression_ptr test, + conditional_node(expression_ptr condition, expression_ptr consequent, expression_ptr alternative) - : test_(test), - consequent_(consequent), - alternative_(alternative), - test_deletable_(branch_deletable(test_)), - consequent_deletable_(branch_deletable(consequent_)), - alternative_deletable_(branch_deletable(alternative_)) - {} - - ~conditional_node() { - if (test_ && test_deletable_) - { - destroy_node(test_); - } - - if (consequent_ && consequent_deletable_ ) - { - destroy_node(consequent_); - } - - if (alternative_ && alternative_deletable_) - { - destroy_node(alternative_); - } + construct_branch_pair(condition_ , condition ); + construct_branch_pair(consequent_ , consequent ); + construct_branch_pair(alternative_, alternative); } inline T value() const { - if (is_true(test_)) - return consequent_->value(); + assert(condition_ .first); + assert(consequent_ .first); + assert(alternative_.first); + + if (is_true(condition_)) + return consequent_.first->value(); else - return alternative_->value(); + return alternative_.first->value(); } inline typename expression_node::node_type type() const @@ -6134,14 +6555,24 @@ namespace exprtk return expression_node::e_conditional; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(consequent_ , node_delete_list); + expression_node::ndb_t::collect(alternative_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth + (condition_, consequent_, alternative_); + } + private: - expression_ptr test_; - expression_ptr consequent_; - expression_ptr alternative_; - const bool test_deletable_; - const bool consequent_deletable_; - const bool alternative_deletable_; + branch_t condition_; + branch_t consequent_; + branch_t alternative_; }; template @@ -6151,32 +6582,22 @@ namespace exprtk // Consequent only conditional statement node typedef expression_node* expression_ptr; + typedef std::pair branch_t; - cons_conditional_node(expression_ptr test, + cons_conditional_node(expression_ptr condition, expression_ptr consequent) - : test_(test), - consequent_(consequent), - test_deletable_(branch_deletable(test_)), - consequent_deletable_(branch_deletable(consequent_)) - {} - - ~cons_conditional_node() { - if (test_ && test_deletable_) - { - destroy_node(test_); - } - - if (consequent_ && consequent_deletable_) - { - destroy_node(consequent_); - } + construct_branch_pair(condition_ , condition ); + construct_branch_pair(consequent_, consequent); } inline T value() const { - if (is_true(test_)) - return consequent_->value(); + assert(condition_ .first); + assert(consequent_.first); + + if (is_true(condition_)) + return consequent_.first->value(); else return std::numeric_limits::quiet_NaN(); } @@ -6186,12 +6607,22 @@ namespace exprtk return expression_node::e_conditional; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(consequent_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t:: + compute_node_depth(condition_, consequent_); + } + private: - expression_ptr test_; - expression_ptr consequent_; - const bool test_deletable_; - const bool consequent_deletable_; + branch_t condition_; + branch_t consequent_; }; #ifndef exprtk_disable_break_continue @@ -6200,7 +6631,7 @@ namespace exprtk { public: - break_exception(const T& v) + explicit break_exception(const T& v) : value(v) {} @@ -6216,23 +6647,16 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; break_node(expression_ptr ret = expression_ptr(0)) - : return_(ret), - return_deletable_(branch_deletable(return_)) - {} - - ~break_node() { - if (return_deletable_) - { - destroy_node(return_); - } + construct_branch_pair(return_, ret); } inline T value() const { - throw break_exception(return_ ? return_->value() : std::numeric_limits::quiet_NaN()); + throw break_exception(return_.first ? return_.first->value() : std::numeric_limits::quiet_NaN()); #ifndef _MSC_VER return std::numeric_limits::quiet_NaN(); #endif @@ -6243,10 +6667,19 @@ namespace exprtk return expression_node::e_break; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(return_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(return_); + } + private: - expression_ptr return_; - const bool return_deletable_; + branch_t return_; }; template @@ -6269,40 +6702,90 @@ namespace exprtk }; #endif - template - class while_loop_node : public expression_node + #ifdef exprtk_enable_runtime_checks + struct loop_runtime_checker { - public: - - typedef expression_node* expression_ptr; - - while_loop_node(expression_ptr condition, expression_ptr loop_body) - : condition_(condition), - loop_body_(loop_body), - condition_deletable_(branch_deletable(condition_)), - loop_body_deletable_(branch_deletable(loop_body_)) + loop_runtime_checker(loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0), + loop_runtime_check::loop_types lp_typ = loop_runtime_check::e_invalid) + : iteration_count_(0), + loop_runtime_check_(loop_rt_chk), + loop_type(lp_typ) {} - ~while_loop_node() + inline void reset(const _uint64_t initial_value = 0) const { - if (condition_ && condition_deletable_) - { - destroy_node(condition_); - } + iteration_count_ = initial_value; + } - if (loop_body_ && loop_body_deletable_) + inline bool check() const + { + if ( + (0 == loop_runtime_check_) || + (++iteration_count_ <= loop_runtime_check_->max_loop_iterations) + ) { - destroy_node(loop_body_); + return true; } + + loop_runtime_check::violation_context ctxt; + ctxt.loop = loop_type; + ctxt.violation = loop_runtime_check::e_iteration_count; + + loop_runtime_check_->handle_runtime_violation(ctxt); + + return false; + } + + mutable _uint64_t iteration_count_; + mutable loop_runtime_check_ptr loop_runtime_check_; + loop_runtime_check::loop_types loop_type; + }; + #else + struct loop_runtime_checker + { + loop_runtime_checker(loop_runtime_check_ptr, loop_runtime_check::loop_types) + {} + + inline void reset(const _uint64_t = 0) const + {} + + inline bool check() const + { + return true; + } + }; + #endif + + template + class while_loop_node : public expression_node, + public loop_runtime_checker + { + public: + + typedef expression_node* expression_ptr; + typedef std::pair branch_t; + + while_loop_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0)) + : loop_runtime_checker(loop_rt_chk,loop_runtime_check::e_while_loop) + { + construct_branch_pair(condition_, condition); + construct_branch_pair(loop_body_, loop_body); } inline T value() const { + assert(condition_.first); + assert(loop_body_.first); + T result = T(0); - while (is_true(condition_)) + loop_runtime_checker::reset(); + + while (is_true(condition_) && loop_runtime_checker::check()) { - result = loop_body_->value(); + result = loop_body_.first->value(); } return result; @@ -6313,50 +6796,55 @@ namespace exprtk return expression_node::e_while; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(condition_, node_delete_list); + expression_node::ndb_t::collect(loop_body_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(condition_, loop_body_); + } + private: - expression_ptr condition_; - expression_ptr loop_body_; - const bool condition_deletable_; - const bool loop_body_deletable_; + branch_t condition_; + branch_t loop_body_; }; template - class repeat_until_loop_node : public expression_node + class repeat_until_loop_node : public expression_node, + public loop_runtime_checker { public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; - repeat_until_loop_node(expression_ptr condition, expression_ptr loop_body) - : condition_(condition), - loop_body_(loop_body), - condition_deletable_(branch_deletable(condition_)), - loop_body_deletable_(branch_deletable(loop_body_)) - {} - - ~repeat_until_loop_node() + repeat_until_loop_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0)) + : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop) { - if (condition_ && condition_deletable_) - { - destroy_node(condition_); - } - - if (loop_body_ && loop_body_deletable_) - { - destroy_node(loop_body_); - } + construct_branch_pair(condition_, condition); + construct_branch_pair(loop_body_, loop_body); } inline T value() const { + assert(condition_.first); + assert(loop_body_.first); + T result = T(0); + loop_runtime_checker::reset(1); + do { - result = loop_body_->value(); + result = loop_body_.first->value(); } - while (is_false(condition_)); + while (is_false(condition_.first) && loop_runtime_checker::check()); return result; } @@ -6366,78 +6854,70 @@ namespace exprtk return expression_node::e_repeat; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(condition_, node_delete_list); + expression_node::ndb_t::collect(loop_body_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(condition_, loop_body_); + } + private: - expression_ptr condition_; - expression_ptr loop_body_; - const bool condition_deletable_; - const bool loop_body_deletable_; + branch_t condition_; + branch_t loop_body_; }; template - class for_loop_node : public expression_node + class for_loop_node : public expression_node, + public loop_runtime_checker { public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; for_loop_node(expression_ptr initialiser, expression_ptr condition, expression_ptr incrementor, - expression_ptr loop_body) - : initialiser_(initialiser), - condition_ (condition ), - incrementor_(incrementor), - loop_body_ (loop_body ), - initialiser_deletable_(branch_deletable(initialiser_)), - condition_deletable_ (branch_deletable(condition_ )), - incrementor_deletable_(branch_deletable(incrementor_)), - loop_body_deletable_ (branch_deletable(loop_body_ )) - {} - - ~for_loop_node() + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0)) + : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop) { - if (initialiser_ && initialiser_deletable_) - { - destroy_node(initialiser_); - } - - if (condition_ && condition_deletable_) - { - destroy_node(condition_); - } - - if (incrementor_ && incrementor_deletable_) - { - destroy_node(incrementor_); - } - - if (loop_body_ && loop_body_deletable_) - { - destroy_node(loop_body_); - } + construct_branch_pair(initialiser_, initialiser); + construct_branch_pair(condition_ , condition ); + construct_branch_pair(incrementor_, incrementor); + construct_branch_pair(loop_body_ , loop_body ); } inline T value() const { + assert(condition_.first); + assert(loop_body_.first); + T result = T(0); - if (initialiser_) - initialiser_->value(); + loop_runtime_checker::reset(); - if (incrementor_) + if (initialiser_.first) + initialiser_.first->value(); + + if (incrementor_.first) { - while (is_true(condition_)) + while (is_true(condition_) && loop_runtime_checker::check()) { - result = loop_body_->value(); - incrementor_->value(); + result = loop_body_.first->value(); + incrementor_.first->value(); } } else { - while (is_true(condition_)) + while (is_true(condition_) && loop_runtime_checker::check()) { - result = loop_body_->value(); + result = loop_body_.first->value(); } } @@ -6449,55 +6929,61 @@ namespace exprtk return expression_node::e_for; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(initialiser_, node_delete_list); + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(incrementor_, node_delete_list); + expression_node::ndb_t::collect(loop_body_ , node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth + (initialiser_, condition_, incrementor_, loop_body_); + } + private: - expression_ptr initialiser_ ; - expression_ptr condition_ ; - expression_ptr incrementor_ ; - expression_ptr loop_body_ ; - const bool initialiser_deletable_; - const bool condition_deletable_ ; - const bool incrementor_deletable_; - const bool loop_body_deletable_ ; + branch_t initialiser_; + branch_t condition_ ; + branch_t incrementor_; + branch_t loop_body_ ; }; #ifndef exprtk_disable_break_continue template - class while_loop_bc_node : public expression_node + class while_loop_bc_node : public expression_node, + public loop_runtime_checker { public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; - while_loop_bc_node(expression_ptr condition, expression_ptr loop_body) - : condition_(condition), - loop_body_(loop_body), - condition_deletable_(branch_deletable(condition_)), - loop_body_deletable_(branch_deletable(loop_body_)) - {} - - ~while_loop_bc_node() + while_loop_bc_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0)) + : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop) { - if (condition_ && condition_deletable_) - { - destroy_node(condition_); - } - - if (loop_body_ && loop_body_deletable_) - { - destroy_node(loop_body_); - } + construct_branch_pair(condition_, condition); + construct_branch_pair(loop_body_, loop_body); } inline T value() const { + assert(condition_.first); + assert(loop_body_.first); + T result = T(0); - while (is_true(condition_)) + loop_runtime_checker::reset(); + + while (is_true(condition_) && loop_runtime_checker::check()) { try { - result = loop_body_->value(); + result = loop_body_.first->value(); } catch(const break_exception& e) { @@ -6515,50 +7001,55 @@ namespace exprtk return expression_node::e_while; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(condition_, node_delete_list); + expression_node::ndb_t::collect(loop_body_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(condition_, loop_body_); + } + private: - expression_ptr condition_; - expression_ptr loop_body_; - const bool condition_deletable_; - const bool loop_body_deletable_; + branch_t condition_; + branch_t loop_body_; }; template - class repeat_until_loop_bc_node : public expression_node + class repeat_until_loop_bc_node : public expression_node, + public loop_runtime_checker { public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; - repeat_until_loop_bc_node(expression_ptr condition, expression_ptr loop_body) - : condition_(condition), - loop_body_(loop_body), - condition_deletable_(branch_deletable(condition_)), - loop_body_deletable_(branch_deletable(loop_body_)) - {} - - ~repeat_until_loop_bc_node() + repeat_until_loop_bc_node(expression_ptr condition, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0)) + : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop) { - if (condition_ && condition_deletable_) - { - destroy_node(condition_); - } - - if (loop_body_ && loop_body_deletable_) - { - destroy_node(loop_body_); - } + construct_branch_pair(condition_, condition); + construct_branch_pair(loop_body_, loop_body); } inline T value() const { + assert(condition_.first); + assert(loop_body_.first); + T result = T(0); + loop_runtime_checker::reset(); + do { try { - result = loop_body_->value(); + result = loop_body_.first->value(); } catch(const break_exception& e) { @@ -6567,7 +7058,7 @@ namespace exprtk catch(const continue_exception&) {} } - while (is_false(condition_)); + while (is_false(condition_.first) && loop_runtime_checker::check()); return result; } @@ -6577,72 +7068,64 @@ namespace exprtk return expression_node::e_repeat; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(condition_, node_delete_list); + expression_node::ndb_t::collect(loop_body_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(condition_, loop_body_); + } + private: - expression_ptr condition_; - expression_ptr loop_body_; - const bool condition_deletable_; - const bool loop_body_deletable_; + branch_t condition_; + branch_t loop_body_; }; template - class for_loop_bc_node : public expression_node + class for_loop_bc_node : public expression_node, + public loop_runtime_checker { public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; for_loop_bc_node(expression_ptr initialiser, - expression_ptr condition, - expression_ptr incrementor, - expression_ptr loop_body) - : initialiser_(initialiser), - condition_ (condition ), - incrementor_(incrementor), - loop_body_ (loop_body ), - initialiser_deletable_(branch_deletable(initialiser_)), - condition_deletable_ (branch_deletable(condition_ )), - incrementor_deletable_(branch_deletable(incrementor_)), - loop_body_deletable_ (branch_deletable(loop_body_ )) - {} - - ~for_loop_bc_node() + expression_ptr condition, + expression_ptr incrementor, + expression_ptr loop_body, + loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0)) + : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop) { - if (initialiser_ && initialiser_deletable_) - { - destroy_node(initialiser_); - } - - if (condition_ && condition_deletable_) - { - destroy_node(condition_); - } - - if (incrementor_ && incrementor_deletable_) - { - destroy_node(incrementor_); - } - - if (loop_body_ && loop_body_deletable_) - { - destroy_node(loop_body_); - } + construct_branch_pair(initialiser_, initialiser); + construct_branch_pair(condition_ , condition ); + construct_branch_pair(incrementor_, incrementor); + construct_branch_pair(loop_body_ , loop_body ); } inline T value() const { + assert(condition_.first); + assert(loop_body_.first); + T result = T(0); - if (initialiser_) - initialiser_->value(); + loop_runtime_checker::reset(); + + if (initialiser_.first) + initialiser_.first->value(); - if (incrementor_) + if (incrementor_.first) { - while (is_true(condition_)) + while (is_true(condition_) && loop_runtime_checker::check()) { try { - result = loop_body_->value(); + result = loop_body_.first->value(); } catch(const break_exception& e) { @@ -6651,16 +7134,16 @@ namespace exprtk catch(const continue_exception&) {} - incrementor_->value(); + incrementor_.first->value(); } } else { - while (is_true(condition_)) + while (is_true(condition_) && loop_runtime_checker::check()) { try { - result = loop_body_->value(); + result = loop_body_.first->value(); } catch(const break_exception& e) { @@ -6679,16 +7162,26 @@ namespace exprtk return expression_node::e_for; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(initialiser_, node_delete_list); + expression_node::ndb_t::collect(condition_ , node_delete_list); + expression_node::ndb_t::collect(incrementor_, node_delete_list); + expression_node::ndb_t::collect(loop_body_ , node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth + (initialiser_, condition_, incrementor_, loop_body_); + } + private: - expression_ptr initialiser_; - expression_ptr condition_ ; - expression_ptr incrementor_; - expression_ptr loop_body_ ; - const bool initialiser_deletable_; - const bool condition_deletable_ ; - const bool incrementor_deletable_; - const bool loop_body_deletable_ ; + branch_t initialiser_; + branch_t condition_ ; + branch_t incrementor_; + branch_t loop_body_ ; }; #endif @@ -6698,6 +7191,7 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; template class Sequence> @@ -6707,35 +7201,21 @@ namespace exprtk return; arg_list_.resize(arg_list.size()); - delete_branch_.resize(arg_list.size()); for (std::size_t i = 0; i < arg_list.size(); ++i) { if (arg_list[i]) { - arg_list_[i] = arg_list[i]; - delete_branch_[i] = static_cast(branch_deletable(arg_list_[i]) ? 1 : 0); + construct_branch_pair(arg_list_[i], arg_list[i]); } else { arg_list_.clear(); - delete_branch_.clear(); return; } } } - ~switch_node() - { - for (std::size_t i = 0; i < arg_list_.size(); ++i) - { - if (arg_list_[i] && delete_branch_[i]) - { - destroy_node(arg_list_[i]); - } - } - } - inline T value() const { if (!arg_list_.empty()) @@ -6744,8 +7224,8 @@ namespace exprtk for (std::size_t i = 0; i < upper_bound; i += 2) { - expression_ptr condition = arg_list_[i ]; - expression_ptr consequent = arg_list_[i + 1]; + expression_ptr condition = arg_list_[i ].first; + expression_ptr consequent = arg_list_[i + 1].first; if (is_true(condition)) { @@ -6753,7 +7233,7 @@ namespace exprtk } } - return arg_list_[upper_bound]->value(); + return arg_list_[upper_bound].first->value(); } else return std::numeric_limits::quiet_NaN(); @@ -6764,10 +7244,19 @@ namespace exprtk return expression_node::e_switch; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(arg_list_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + protected: - std::vector arg_list_; - std::vector delete_branch_; + std::vector arg_list_; }; template @@ -6795,6 +7284,7 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; template class Sequence> @@ -6804,35 +7294,21 @@ namespace exprtk return; arg_list_.resize(arg_list.size()); - delete_branch_.resize(arg_list.size()); for (std::size_t i = 0; i < arg_list.size(); ++i) { if (arg_list[i]) { - arg_list_[i] = arg_list[i]; - delete_branch_[i] = static_cast(branch_deletable(arg_list_[i]) ? 1 : 0); + construct_branch_pair(arg_list_[i], arg_list[i]); } else { arg_list_.clear(); - delete_branch_.clear(); return; } } } - ~multi_switch_node() - { - for (std::size_t i = 0; i < arg_list_.size(); ++i) - { - if (arg_list_[i] && delete_branch_[i]) - { - destroy_node(arg_list_[i]); - } - } - } - inline T value() const { T result = T(0); @@ -6846,8 +7322,8 @@ namespace exprtk for (std::size_t i = 0; i < upper_bound; i += 2) { - expression_ptr condition = arg_list_[i ]; - expression_ptr consequent = arg_list_[i + 1]; + expression_ptr condition = arg_list_[i ].first; + expression_ptr consequent = arg_list_[i + 1].first; if (is_true(condition)) { @@ -6863,10 +7339,19 @@ namespace exprtk return expression_node::e_mswitch; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(arg_list_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + private: - std::vector arg_list_; - std::vector delete_branch_; + std::vector arg_list_; }; template @@ -6982,30 +7467,26 @@ namespace exprtk } } - bool const_range() + bool const_range() const { return ( n0_c.first && n1_c.first) && (!n0_e.first && !n1_e.first); } - bool var_range() + bool var_range() const { return ( n0_e.first && n1_e.first) && (!n0_c.first && !n1_c.first); } - bool operator() (std::size_t& r0, std::size_t& r1, const std::size_t& size = std::numeric_limits::max()) const + bool operator() (std::size_t& r0, std::size_t& r1, + const std::size_t& size = std::numeric_limits::max()) const { if (n0_c.first) r0 = n0_c.second; else if (n0_e.first) { - const T r0_value = n0_e.second->value(); - - if (r0_value < 0) - return false; - else - r0 = static_cast(details::numeric::to_int64(r0_value)); + r0 = static_cast(details::numeric::to_int64(n0_e.second->value())); } else return false; @@ -7014,12 +7495,7 @@ namespace exprtk r1 = n1_c.second; else if (n1_e.first) { - const T r1_value = n1_e.second->value(); - - if (r1_value < 0) - return false; - else - r1 = static_cast(details::numeric::to_int64(r1_value)); + r1 = static_cast(details::numeric::to_int64(n1_e.second->value())); } else return false; @@ -7035,7 +7511,11 @@ namespace exprtk cache.first = r0; cache.second = r1; + #ifndef exprtk_enable_runtime_checks return (r0 <= r1); + #else + return range_runtime_check(r0, r1, size); + #endif } inline std::size_t const_size() const @@ -7053,6 +7533,27 @@ namespace exprtk std::pair n0_c; std::pair n1_c; mutable cached_range_t cache; + + #ifdef exprtk_enable_runtime_checks + bool range_runtime_check(const std::size_t r0, + const std::size_t r1, + const std::size_t size) const + { + if (r0 >= size) + { + throw std::runtime_error("range error: (r0 < 0) || (r0 >= size)"); + return false; + } + + if (r1 >= size) + { + throw std::runtime_error("range error: (r1 < 0) || (r1 >= size)"); + return false; + } + + return (r0 <= r1); + } + #endif }; template @@ -7180,38 +7681,31 @@ namespace exprtk { public: - typedef expression_node* expression_ptr; - typedef vector_holder vector_holder_t; - typedef vector_holder_t* vector_holder_ptr; + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef std::pair branch_t; vector_elem_node(expression_ptr index, vector_holder_ptr vec_holder) - : index_(index), - vec_holder_(vec_holder), - vector_base_((*vec_holder)[0]), - index_deletable_(branch_deletable(index_)) - {} - - ~vector_elem_node() + : vec_holder_(vec_holder), + vector_base_((*vec_holder)[0]) { - if (index_ && index_deletable_) - { - destroy_node(index_); - } + construct_branch_pair(index_, index); } inline T value() const { - return *(vector_base_ + static_cast(details::numeric::to_int64(index_->value()))); + return *(vector_base_ + static_cast(details::numeric::to_int64(index_.first->value()))); } inline T& ref() { - return *(vector_base_ + static_cast(details::numeric::to_int64(index_->value()))); + return *(vector_base_ + static_cast(details::numeric::to_int64(index_.first->value()))); } inline const T& ref() const { - return *(vector_base_ + static_cast(details::numeric::to_int64(index_->value()))); + return *(vector_base_ + static_cast(details::numeric::to_int64(index_.first->value()))); } inline typename expression_node::node_type type() const @@ -7224,12 +7718,21 @@ namespace exprtk return (*vec_holder_); } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(index_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(index_); + } + private: - expression_ptr index_; vector_holder_ptr vec_holder_; T* vector_base_; - const bool index_deletable_; + branch_t index_; }; template @@ -7238,41 +7741,33 @@ namespace exprtk { public: - typedef expression_node* expression_ptr; - typedef vector_holder vector_holder_t; - typedef vector_holder_t* vector_holder_ptr; - typedef vec_data_store vds_t; + typedef expression_node* expression_ptr; + typedef vector_holder vector_holder_t; + typedef vector_holder_t* vector_holder_ptr; + typedef vec_data_store vds_t; + typedef std::pair branch_t; rebasevector_elem_node(expression_ptr index, vector_holder_ptr vec_holder) - : index_(index), - index_deletable_(branch_deletable(index_)), - vector_holder_(vec_holder), + : vector_holder_(vec_holder), vds_((*vector_holder_).size(),(*vector_holder_)[0]) { vector_holder_->set_ref(&vds_.ref()); - } - - ~rebasevector_elem_node() - { - if (index_ && index_deletable_) - { - destroy_node(index_); - } + construct_branch_pair(index_, index); } inline T value() const { - return *(vds_.data() + static_cast(details::numeric::to_int64(index_->value()))); + return *(vds_.data() + static_cast(details::numeric::to_int64(index_.first->value()))); } inline T& ref() { - return *(vds_.data() + static_cast(details::numeric::to_int64(index_->value()))); + return *(vds_.data() + static_cast(details::numeric::to_int64(index_.first->value()))); } inline const T& ref() const { - return *(vds_.data() + static_cast(details::numeric::to_int64(index_->value()))); + return *(vds_.data() + static_cast(details::numeric::to_int64(index_.first->value()))); } inline typename expression_node::node_type type() const @@ -7285,12 +7780,21 @@ namespace exprtk return (*vector_holder_); } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(index_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(index_); + } + private: - expression_ptr index_; - const bool index_deletable_; vector_holder_ptr vector_holder_; vds_t vds_; + branch_t index_; }; template @@ -7361,17 +7865,6 @@ namespace exprtk single_value_initialse_(single_value_initialse) {} - ~vector_assignment_node() - { - for (std::size_t i = 0; i < initialiser_list_.size(); ++i) - { - if (branch_deletable(initialiser_list_[i])) - { - destroy_node(initialiser_list_[i]); - } - } - } - inline T value() const { if (single_value_initialse_) @@ -7407,6 +7900,16 @@ namespace exprtk return expression_node::e_vecdefass; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(initialiser_list_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(initialiser_list_); + } + private: vector_assignment_node& operator=(const vector_assignment_node&); @@ -7528,6 +8031,9 @@ namespace exprtk inline T value() const { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + if (initialised_) { binary_node::branch_[0].first->value(); @@ -7832,18 +8338,18 @@ namespace exprtk { public: - typedef expression_node * expression_ptr; - typedef stringvar_node * strvar_node_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; + typedef expression_node * expression_ptr; + typedef stringvar_node * strvar_node_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; + typedef std::pair branch_t; + generic_string_range_node(expression_ptr str_branch, const range_t& brange) : initialised_(false), - branch_(str_branch), - branch_deletable_(branch_deletable(branch_)), str_base_ptr_ (0), str_range_ptr_(0), base_range_(brange) @@ -7853,14 +8359,16 @@ namespace exprtk range_.cache.first = range_.n0_c.second; range_.cache.second = range_.n1_c.second; - if (is_generally_string_node(branch_)) + construct_branch_pair(branch_, str_branch); + + if (is_generally_string_node(branch_.first)) { - str_base_ptr_ = dynamic_cast(branch_); + str_base_ptr_ = dynamic_cast(branch_.first); if (0 == str_base_ptr_) return; - str_range_ptr_ = dynamic_cast(branch_); + str_range_ptr_ = dynamic_cast(branch_.first); if (0 == str_range_ptr_) return; @@ -7872,18 +8380,15 @@ namespace exprtk ~generic_string_range_node() { base_range_.free(); - - if (branch_ && branch_deletable_) - { - destroy_node(branch_); - } } inline T value() const { if (initialised_) { - branch_->value(); + assert(branch_.first); + + branch_.first->value(); std::size_t str_r0 = 0; std::size_t str_r1 = 0; @@ -7891,13 +8396,13 @@ namespace exprtk std::size_t r0 = 0; std::size_t r1 = 0; - range_t& range = str_range_ptr_->range_ref(); + const range_t& range = str_range_ptr_->range_ref(); const std::size_t base_str_size = str_base_ptr_->size(); if ( - range (str_r0,str_r1,base_str_size) && - base_range_( r0, r1,base_str_size) + range (str_r0, str_r1, base_str_size) && + base_range_( r0, r1, base_str_size - str_r0) ) { const std::size_t size = (r1 - r0) + 1; @@ -7942,11 +8447,20 @@ namespace exprtk return expression_node::e_strgenrange; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + private: bool initialised_; - expression_ptr branch_; - const bool branch_deletable_; + branch_t branch_; str_base_ptr str_base_ptr_; irange_ptr str_range_ptr_; mutable range_t base_range_; @@ -8020,6 +8534,9 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); @@ -8029,8 +8546,8 @@ namespace exprtk std::size_t str1_r0 = 0; std::size_t str1_r1 = 0; - range_t& range0 = str0_range_ptr_->range_ref(); - range_t& range1 = str1_range_ptr_->range_ref(); + const range_t& range0 = str0_range_ptr_->range_ref(); + const range_t& range1 = str1_range_ptr_->range_ref(); if ( range0(str0_r0, str0_r1, str0_base_ptr_->size()) && @@ -8130,10 +8647,13 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); - std::swap(str0_node_ptr_->ref(),str1_node_ptr_->ref()); + std::swap(str0_node_ptr_->ref(), str1_node_ptr_->ref()); } return std::numeric_limits::quiet_NaN(); @@ -8237,6 +8757,9 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); @@ -8246,8 +8769,8 @@ namespace exprtk std::size_t str1_r0 = 0; std::size_t str1_r1 = 0; - range_t& range0 = (*str0_range_ptr_); - range_t& range1 = (*str1_range_ptr_); + const range_t& range0 = (*str0_range_ptr_); + const range_t& range1 = (*str1_range_ptr_); if ( range0(str0_r0, str0_r1, str0_base_ptr_->size()) && @@ -8289,8 +8812,8 @@ namespace exprtk exprtk_disable_fallthrough_begin switch (lud.remainder) { - #define case_stmt(N) \ - case N : { std::swap(s0[i],s1[i]); ++i; } \ + #define case_stmt(N) \ + case N : { std::swap(s0[i], s1[i]); ++i; } \ #ifndef exprtk_disable_superscalar_unroll case_stmt(15) case_stmt(14) @@ -8368,38 +8891,32 @@ namespace exprtk { public: - typedef expression_node * expression_ptr; - typedef string_base_node* str_base_ptr; + typedef expression_node * expression_ptr; + typedef string_base_node* str_base_ptr; + typedef std::pair branch_t; - explicit string_size_node(expression_ptr brnch) - : branch_(brnch), - branch_deletable_(branch_deletable(branch_)), - str_base_ptr_(0) + + explicit string_size_node(expression_ptr branch) + : str_base_ptr_(0) { - if (is_generally_string_node(branch_)) + construct_branch_pair(branch_, branch); + + if (is_generally_string_node(branch_.first)) { - str_base_ptr_ = dynamic_cast(branch_); + str_base_ptr_ = dynamic_cast(branch_.first); if (0 == str_base_ptr_) return; } } - ~string_size_node() - { - if (branch_ && branch_deletable_) - { - destroy_node(branch_); - } - } - inline T value() const { T result = std::numeric_limits::quiet_NaN(); if (str_base_ptr_) { - branch_->value(); + branch_.first->value(); result = T(str_base_ptr_->size()); } @@ -8411,11 +8928,20 @@ namespace exprtk return expression_node::e_stringsize; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + private: - expression_ptr branch_; - const bool branch_deletable_; - str_base_ptr str_base_ptr_; + branch_t branch_; + str_base_ptr str_base_ptr_; }; struct asn_assignment @@ -8487,12 +9013,15 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[1].first->value(); std::size_t r0 = 0; std::size_t r1 = 0; - range_t& range = (*str1_range_ptr_); + const range_t& range = (*str1_range_ptr_); if (range(r0, r1, str1_base_ptr_->size())) { @@ -8553,28 +9082,29 @@ namespace exprtk { public: - typedef expression_node * expression_ptr; - typedef stringvar_node * strvar_node_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; + typedef expression_node * expression_ptr; + typedef stringvar_node * strvar_node_ptr; + typedef string_range_node* str_rng_node_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; assignment_string_range_node(const operator_type& opr, expression_ptr branch0, expression_ptr branch1) : binary_node(opr, branch0, branch1), initialised_(false), - str0_base_ptr_ (0), - str1_base_ptr_ (0), - str0_node_ptr_ (0), - str0_range_ptr_(0), - str1_range_ptr_(0) + str0_base_ptr_ (0), + str1_base_ptr_ (0), + str0_rng_node_ptr_ (0), + str0_range_ptr_ (0), + str1_range_ptr_ (0) { if (is_string_range_node(binary_node::branch_[0].first)) { - str0_node_ptr_ = static_cast(binary_node::branch_[0].first); + str0_rng_node_ptr_ = static_cast(binary_node::branch_[0].first); str0_base_ptr_ = dynamic_cast(binary_node::branch_[0].first); @@ -8601,17 +9131,20 @@ namespace exprtk str1_range_ptr_ = &(range->range_ref()); } - initialised_ = str0_base_ptr_ && - str1_base_ptr_ && - str0_node_ptr_ && - str0_range_ptr_ && - str1_range_ptr_ ; + initialised_ = str0_base_ptr_ && + str1_base_ptr_ && + str0_rng_node_ptr_ && + str0_range_ptr_ && + str1_range_ptr_ ; } inline T value() const { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); @@ -8621,15 +9154,15 @@ namespace exprtk std::size_t s1_r0 = 0; std::size_t s1_r1 = 0; - range_t& range0 = (*str0_range_ptr_); - range_t& range1 = (*str1_range_ptr_); + const range_t& range0 = (*str0_range_ptr_); + const range_t& range1 = (*str1_range_ptr_); if ( range0(s0_r0, s0_r1, str0_base_ptr_->size()) && range1(s1_r0, s1_r1, str1_base_ptr_->size()) ) { - std::size_t size = std::min((s0_r1 - s0_r0),(s1_r1 - s1_r0)) + 1; + const std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0)) + 1; std::copy(str1_base_ptr_->base() + s1_r0, str1_base_ptr_->base() + s1_r0 + size, @@ -8642,27 +9175,27 @@ namespace exprtk std::string str() const { - return str0_node_ptr_->str(); + return str0_base_ptr_->str(); } char_cptr base() const { - return str0_node_ptr_->base(); + return str0_base_ptr_->base(); } std::size_t size() const { - return str0_node_ptr_->size(); + return str0_base_ptr_->size(); } range_t& range_ref() { - return str0_node_ptr_->range_ref(); + return str0_rng_node_ptr_->range_ref(); } const range_t& range_ref() const { - return str0_node_ptr_->range_ref(); + return str0_rng_node_ptr_->range_ref(); } inline typename expression_node::node_type type() const @@ -8672,12 +9205,12 @@ namespace exprtk private: - bool initialised_; - str_base_ptr str0_base_ptr_; - str_base_ptr str1_base_ptr_; - strvar_node_ptr str0_node_ptr_; - range_ptr str0_range_ptr_; - range_ptr str1_range_ptr_; + bool initialised_; + str_base_ptr str0_base_ptr_; + str_base_ptr str1_base_ptr_; + str_rng_node_ptr str0_rng_node_ptr_; + range_ptr str0_range_ptr_; + range_ptr str1_range_ptr_; }; template @@ -8694,16 +9227,16 @@ namespace exprtk typedef range_interface irange_t; typedef irange_t* irange_ptr; - conditional_string_node(expression_ptr test, + conditional_string_node(expression_ptr condition, expression_ptr consequent, expression_ptr alternative) - : trinary_node(details::e_default,consequent,alternative,test), + : trinary_node(details::e_default,consequent,alternative,condition), initialised_(false), str0_base_ptr_ (0), str1_base_ptr_ (0), str0_range_ptr_(0), str1_range_ptr_(0), - test_ (test), + condition_ (condition), consequent_ (consequent), alternative_(alternative) { @@ -8750,14 +9283,18 @@ namespace exprtk { if (initialised_) { + assert(condition_ ); + assert(consequent_ ); + assert(alternative_); + std::size_t r0 = 0; std::size_t r1 = 0; - if (is_true(test_)) + if (is_true(condition_)) { consequent_->value(); - range_t& range = str0_range_ptr_->range_ref(); + const range_t& range = str0_range_ptr_->range_ref(); if (range(r0, r1, str0_base_ptr_->size())) { @@ -8775,7 +9312,7 @@ namespace exprtk { alternative_->value(); - range_t& range = str1_range_ptr_->range_ref(); + const range_t& range = str1_range_ptr_->range_ref(); if (range(r0, r1, str1_base_ptr_->size())) { @@ -8834,7 +9371,7 @@ namespace exprtk mutable range_t range_; mutable std::string value_; - expression_ptr test_; + expression_ptr condition_; expression_ptr consequent_; expression_ptr alternative_; }; @@ -8853,13 +9390,13 @@ namespace exprtk typedef range_interface irange_t; typedef irange_t* irange_ptr; - cons_conditional_str_node(expression_ptr test, + cons_conditional_str_node(expression_ptr condition, expression_ptr consequent) - : binary_node(details::e_default, consequent, test), + : binary_node(details::e_default, consequent, condition), initialised_(false), str0_base_ptr_ (0), str0_range_ptr_(0), - test_ (test), + condition_ (condition), consequent_(consequent) { range_.n0_c = std::make_pair(true,0); @@ -8888,11 +9425,14 @@ namespace exprtk { if (initialised_) { - if (is_true(test_)) + assert(condition_ ); + assert(consequent_); + + if (is_true(condition_)) { consequent_->value(); - range_t& range = str0_range_ptr_->range_ref(); + const range_t& range = str0_range_ptr_->range_ref(); std::size_t r0 = 0; std::size_t r1 = 0; @@ -8952,7 +9492,7 @@ namespace exprtk mutable range_t range_; mutable std::string value_; - expression_ptr test_; + expression_ptr condition_; expression_ptr consequent_; }; @@ -8963,33 +9503,34 @@ namespace exprtk { 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; + 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; + typedef std::pair branch_t; template class Sequence> explicit str_vararg_node(const Sequence& arg_list) - : final_node_(arg_list.back()), - final_deletable_(branch_deletable(final_node_)), - initialised_(false), + : initialised_(false), str_base_ptr_ (0), str_range_ptr_(0) { - if (0 == final_node_) + construct_branch_pair(final_node_, const_cast(arg_list.back())); + + if (0 == final_node_.first) return; - else if (!is_generally_string_node(final_node_)) + else if (!is_generally_string_node(final_node_.first)) return; - str_base_ptr_ = dynamic_cast(final_node_); + str_base_ptr_ = dynamic_cast(final_node_.first); if (0 == str_base_ptr_) return; - str_range_ptr_ = dynamic_cast(final_node_); + str_range_ptr_ = dynamic_cast(final_node_.first); if (0 == str_range_ptr_) return; @@ -9001,41 +9542,22 @@ namespace exprtk const std::size_t arg_list_size = arg_list.size() - 1; arg_list_.resize(arg_list_size); - delete_branch_.resize(arg_list_size); for (std::size_t i = 0; i < arg_list_size; ++i) { if (arg_list[i]) { - arg_list_[i] = arg_list[i]; - delete_branch_[i] = static_cast(branch_deletable(arg_list_[i]) ? 1 : 0); + construct_branch_pair(arg_list_[i], arg_list[i]); } else { - arg_list_ .clear(); - delete_branch_.clear(); + arg_list_.clear(); return; } } } } - ~str_vararg_node() - { - if (final_node_ && final_deletable_) - { - destroy_node(final_node_); - } - - for (std::size_t i = 0; i < arg_list_.size(); ++i) - { - if (arg_list_[i] && delete_branch_[i]) - { - destroy_node(arg_list_[i]); - } - } - } - inline T value() const { if (!arg_list_.empty()) @@ -9043,7 +9565,7 @@ namespace exprtk VarArgFunction::process(arg_list_); } - final_node_->value(); + final_node_.first->value(); return std::numeric_limits::quiet_NaN(); } @@ -9078,27 +9600,38 @@ namespace exprtk return expression_node::e_stringvararg; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(final_node_, node_delete_list); + expression_node::ndb_t::collect(arg_list_ , node_delete_list); + } + + std::size_t node_depth() const + { + return std::max( + expression_node::ndb_t::compute_node_depth(final_node_), + expression_node::ndb_t::compute_node_depth(arg_list_ )); + } + private: - expression_ptr final_node_; - bool final_deletable_; - bool initialised_; - str_base_ptr str_base_ptr_; - irange_ptr str_range_ptr_; - std::vector arg_list_; - std::vector delete_branch_; + bool initialised_; + branch_t final_node_; + str_base_ptr str_base_ptr_; + irange_ptr str_range_ptr_; + std::vector arg_list_; }; #endif template - inline T axn(T a, T x) + inline T axn(const T a, const T x) { // a*x^n return a * exprtk::details::numeric::fast_exp::result(x); } template - inline T axnb(T a, T x, T b) + inline T axnb(const T a, const T x, const T b) { // a*x^n+b return a * exprtk::details::numeric::fast_exp::result(x) + b; @@ -9119,14 +9652,14 @@ namespace exprtk template \ struct sf##NN##_op : public sf_base \ { \ - typedef typename sf_base::Type Type; \ + typedef typename sf_base::Type const Type; \ static inline T process(Type x, Type y, Type z) \ { \ return (OP0); \ } \ static inline std::string id() \ { \ - return OP1; \ + return (OP1); \ } \ }; \ @@ -9183,12 +9716,15 @@ namespace exprtk template \ struct sf##NN##_op : public sf_base \ { \ - typedef typename sf_base::Type Type; \ + typedef typename sf_base::Type const Type; \ static inline T process(Type x, Type y, Type z, Type w) \ { \ return (OP0); \ } \ - static inline std::string id() { return OP1; } \ + static inline std::string id() \ + { \ + return (OP1); \ + } \ }; \ define_sfop4(48,(x + ((y + z) / w)),"t+((t+t)/t)") @@ -9327,6 +9863,10 @@ namespace exprtk inline T value() const { + assert(trinary_node::branch_[0].first); + assert(trinary_node::branch_[1].first); + assert(trinary_node::branch_[2].first); + const T x = trinary_node::branch_[0].first->value(); const T y = trinary_node::branch_[1].first->value(); const T z = trinary_node::branch_[2].first->value(); @@ -9352,6 +9892,11 @@ namespace exprtk inline T value() const { + assert(quaternary_node::branch_[0].first); + assert(quaternary_node::branch_[1].first); + assert(quaternary_node::branch_[2].first); + assert(quaternary_node::branch_[3].first); + const T x = quaternary_node::branch_[0].first->value(); const T y = quaternary_node::branch_[1].first->value(); const T z = quaternary_node::branch_[2].first->value(); @@ -9435,41 +9980,28 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; template class Sequence> explicit vararg_node(const Sequence& arg_list) { - arg_list_ .resize(arg_list.size()); - delete_branch_.resize(arg_list.size()); + arg_list_.resize(arg_list.size()); for (std::size_t i = 0; i < arg_list.size(); ++i) { if (arg_list[i]) { - arg_list_[i] = arg_list[i]; - delete_branch_[i] = static_cast(branch_deletable(arg_list_[i]) ? 1 : 0); + construct_branch_pair(arg_list_[i],arg_list[i]); } else { arg_list_.clear(); - delete_branch_.clear(); return; } } } - ~vararg_node() - { - for (std::size_t i = 0; i < arg_list_.size(); ++i) - { - if (arg_list_[i] && delete_branch_[i]) - { - destroy_node(arg_list_[i]); - } - } - } - inline T value() const { return VarArgFunction::process(arg_list_); @@ -9480,10 +10012,19 @@ namespace exprtk return expression_node::e_vararg; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(arg_list_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + private: - std::vector arg_list_; - std::vector delete_branch_; + std::vector arg_list_; }; template @@ -9538,33 +10079,29 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; explicit vectorize_node(const expression_ptr v) - : ivec_ptr_(0), - v_(v), - v_deletable_(branch_deletable(v_)) + : ivec_ptr_(0) { - if (is_ivector_node(v)) + construct_branch_pair(v_, v); + + if (is_ivector_node(v_.first)) { - ivec_ptr_ = dynamic_cast*>(v); + ivec_ptr_ = dynamic_cast*>(v_.first); } else ivec_ptr_ = 0; } - ~vectorize_node() - { - if (v_ && v_deletable_) - { - destroy_node(v_); - } - } - inline T value() const { if (ivec_ptr_) { - v_->value(); + assert(v_.first); + + v_.first->value(); + return VecFunction::process(ivec_ptr_); } else @@ -9576,11 +10113,20 @@ namespace exprtk return expression_node::e_vecfunc; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(v_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(v_); + } + private: vector_interface* ivec_ptr_; - expression_ptr v_; - const bool v_deletable_; + branch_t v_; }; template @@ -9606,6 +10152,8 @@ namespace exprtk { if (var_node_ptr_) { + assert(binary_node::branch_[1].first); + T& result = var_node_ptr_->ref(); result = binary_node::branch_[1].first->value(); @@ -9644,6 +10192,8 @@ namespace exprtk { if (vec_node_ptr_) { + assert(binary_node::branch_[1].first); + T& result = vec_node_ptr_->ref(); result = binary_node::branch_[1].first->value(); @@ -9682,6 +10232,8 @@ namespace exprtk { if (rbvec_node_ptr_) { + assert(binary_node::branch_[1].first); + T& result = rbvec_node_ptr_->ref(); result = binary_node::branch_[1].first->value(); @@ -9720,6 +10272,8 @@ namespace exprtk { if (rbvec_node_ptr_) { + assert(binary_node::branch_[1].first); + T& result = rbvec_node_ptr_->ref(); result = binary_node::branch_[1].first->value(); @@ -9762,6 +10316,8 @@ namespace exprtk { if (vec_node_ptr_) { + assert(binary_node::branch_[1].first); + const T v = binary_node::branch_[1].first->value(); T* vec = vds().data(); @@ -9907,6 +10463,8 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[1].first); + binary_node::branch_[1].first->value(); if (src_is_ivec_) @@ -10028,6 +10586,8 @@ namespace exprtk { if (var_node_ptr_) { + assert(binary_node::branch_[1].first); + T& v = var_node_ptr_->ref(); v = Operation::process(v,binary_node::branch_[1].first->value()); @@ -10065,6 +10625,8 @@ namespace exprtk { if (vec_node_ptr_) { + assert(binary_node::branch_[1].first); + T& v = vec_node_ptr_->ref(); v = Operation::process(v,binary_node::branch_[1].first->value()); @@ -10102,6 +10664,8 @@ namespace exprtk { if (rbvec_node_ptr_) { + assert(binary_node::branch_[1].first); + T& v = rbvec_node_ptr_->ref(); v = Operation::process(v,binary_node::branch_[1].first->value()); @@ -10139,6 +10703,8 @@ namespace exprtk { if (rbvec_node_ptr_) { + assert(binary_node::branch_[1].first); + T& v = rbvec_node_ptr_->ref(); v = Operation::process(v,binary_node::branch_[1].first->value()); @@ -10180,6 +10746,8 @@ namespace exprtk { if (vec_node_ptr_) { + assert(binary_node::branch_[1].first); + const T v = binary_node::branch_[1].first->value(); T* vec = vds().data(); @@ -10325,6 +10893,9 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); @@ -10510,6 +11081,9 @@ namespace exprtk { if (initialised_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); @@ -10669,6 +11243,9 @@ namespace exprtk { if (vec0_node_ptr_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + binary_node::branch_[0].first->value(); const T v = binary_node::branch_[1].first->value(); @@ -10824,6 +11401,9 @@ namespace exprtk { if (vec1_node_ptr_) { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + const T v = binary_node::branch_[0].first->value(); binary_node::branch_[1].first->value(); @@ -10940,15 +11520,15 @@ namespace exprtk { bool vec0_is_ivec = false; - if (is_vector_node(unary_node::branch_)) + if (is_vector_node(unary_node::branch_.first)) { - vec0_node_ptr_ = static_cast(unary_node::branch_); + vec0_node_ptr_ = static_cast(unary_node::branch_.first); } - else if (is_ivector_node(unary_node::branch_)) + else if (is_ivector_node(unary_node::branch_.first)) { vector_interface* vi = reinterpret_cast*>(0); - if (0 != (vi = dynamic_cast*>(unary_node::branch_))) + if (0 != (vi = dynamic_cast*>(unary_node::branch_.first))) { vec0_node_ptr_ = vi->vec(); vec0_is_ivec = true; @@ -10975,7 +11555,9 @@ namespace exprtk inline T value() const { - unary_node::branch_->value(); + assert(unary_node::branch_.first); + + unary_node::branch_.first->value(); if (vec0_node_ptr_) { @@ -11088,6 +11670,9 @@ namespace exprtk inline T value() const { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + return ( std::not_equal_to() (T(0),binary_node::branch_[0].first->value()) && @@ -11112,6 +11697,9 @@ namespace exprtk inline T value() const { + assert(binary_node::branch_[0].first); + assert(binary_node::branch_[1].first); + return ( std::not_equal_to() (T(0),binary_node::branch_[0].first->value()) || @@ -11136,11 +11724,6 @@ namespace exprtk parameter_count_(func->param_count) {} - ~function_N_node() - { - cleanup_branches::execute(branch_); - } - template bool init_branches(expression_ptr (&b)[NumBranches]) { @@ -11407,6 +11990,16 @@ namespace exprtk return expression_node::e_function; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::template compute_node_depth(branch_); + } + private: ifunction* function_; @@ -11464,17 +12057,6 @@ namespace exprtk value_list_.resize(arg_list.size(),std::numeric_limits::quiet_NaN()); } - ~vararg_function_node() - { - for (std::size_t i = 0; i < arg_list_.size(); ++i) - { - if (arg_list_[i] && !details::is_variable_node(arg_list_[i])) - { - destroy_node(arg_list_[i]); - } - } - } - inline bool operator <(const vararg_function_node& fn) const { return this < (&fn); @@ -11496,6 +12078,22 @@ namespace exprtk return expression_node::e_vafunction; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + for (std::size_t i = 0; i < arg_list_.size(); ++i) + { + if (arg_list_[i] && !details::is_variable_node(arg_list_[i])) + { + node_delete_list.push_back(&arg_list_[i]); + } + } + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(arg_list_); + } + private: inline void populate_value_list() const @@ -11531,15 +12129,23 @@ namespace exprtk typedef std::vector typestore_list_t; typedef std::vector range_list_t; - generic_function_node(const std::vector& arg_list, - GenericFunction* func = (GenericFunction*)(0)) + explicit generic_function_node(const std::vector& arg_list, + GenericFunction* func = reinterpret_cast(0)) : function_(func), arg_list_(arg_list) {} virtual ~generic_function_node() + {} + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) { - cleanup_branches::execute(branch_); + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } virtual bool init_branches() @@ -11547,7 +12153,7 @@ namespace exprtk expr_as_vec1_store_.resize(arg_list_.size(),T(0) ); typestore_list_ .resize(arg_list_.size(),type_store_t() ); range_list_ .resize(arg_list_.size(),range_data_type_t()); - branch_ .resize(arg_list_.size(),branch_t((expression_ptr)0,false)); + branch_ .resize(arg_list_.size(),branch_t(reinterpret_cast(0),false)); for (std::size_t i = 0; i < arg_list_.size(); ++i) { @@ -11589,7 +12195,7 @@ namespace exprtk if (0 == (ri = dynamic_cast(arg_list_[i]))) return false; - range_t& rp = ri->range_ref(); + const range_t& rp = ri->range_ref(); if ( rp.const_range() && @@ -11668,11 +12274,11 @@ namespace exprtk if (rdt.range) { - range_t& rp = (*rdt.range); - std::size_t r0 = 0; - std::size_t r1 = 0; + const range_t& rp = (*rdt.range); + std::size_t r0 = 0; + std::size_t r1 = 0; - if (rp(r0,r1,rdt.size)) + if (rp(r0, r1, rdt.size)) { type_store_t& ts = typestore_list_[i]; @@ -11738,7 +12344,10 @@ namespace exprtk typedef typename StringFunction::parameter_list_t parameter_list_t; const T result = (*gen_function_t::function_) - (ret_string_, parameter_list_t(gen_function_t::typestore_list_)); + ( + ret_string_, + parameter_list_t(gen_function_t::typestore_list_) + ); range_.n1_c.second = ret_string_.size() - 1; range_.cache.second = range_.n1_c.second; @@ -11810,8 +12419,11 @@ namespace exprtk { typedef typename GenericFunction::parameter_list_t parameter_list_t; - return (*gen_function_t::function_)(param_seq_index_, - parameter_list_t(gen_function_t::typestore_list_)); + return (*gen_function_t::function_) + ( + param_seq_index_, + parameter_list_t(gen_function_t::typestore_list_) + ); } } @@ -11852,9 +12464,12 @@ namespace exprtk { typedef typename StringFunction::parameter_list_t parameter_list_t; - const T result = (*str_function_t::function_)(param_seq_index_, - str_function_t::ret_string_, - parameter_list_t(str_function_t::typestore_list_)); + const T result = (*str_function_t::function_) + ( + param_seq_index_, + str_function_t::ret_string_, + parameter_list_t(str_function_t::typestore_list_) + ); str_function_t::range_.n1_c.second = str_function_t::ret_string_.size() - 1; str_function_t::range_.cache.second = str_function_t::range_.n1_c.second; @@ -11949,30 +12564,25 @@ namespace exprtk typedef expression_node* expression_ptr; typedef results_context results_context_t; + typedef std::pair branch_t; return_envelope_node(expression_ptr body, results_context_t& rc) : results_context_(&rc ), - return_invoked_ (false), - body_ (body ), - body_deletable_ (branch_deletable(body_)) - {} - - ~return_envelope_node() + return_invoked_ (false) { - if (body_ && body_deletable_) - { - destroy_node(body_); - } + construct_branch_pair(body_, body); } inline T value() const { + assert(body_.first); + try { return_invoked_ = false; results_context_->clear(); - return body_->value(); + return body_.first->value(); } catch(const return_exception&) { @@ -11991,12 +12601,21 @@ namespace exprtk return &return_invoked_; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(body_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(body_); + } + private: results_context_t* results_context_; - mutable bool return_invoked_; - expression_ptr body_; - const bool body_deletable_; + mutable bool return_invoked_; + branch_t body_; }; #endif @@ -12343,11 +12962,23 @@ namespace exprtk } template - inline T value(T* t) + inline T value(std::pair*,bool> n) + { + return n.first->value(); + } + + template + inline T value(const T* t) { return (*t); } + template + inline T value(const T& t) + { + return t; + } + template struct vararg_add_op : public opr_base { @@ -12595,16 +13226,16 @@ namespace exprtk static inline T process_4(const Sequence& arg_list) { return std::min( - std::min(value(arg_list[0]),value(arg_list[1])), - std::min(value(arg_list[2]),value(arg_list[3]))); + std::min(value(arg_list[0]), value(arg_list[1])), + std::min(value(arg_list[2]), value(arg_list[3]))); } template static inline T process_5(const Sequence& arg_list) { return std::min( - std::min(std::min(value(arg_list[0]),value(arg_list[1])), - std::min(value(arg_list[2]),value(arg_list[3]))), + std::min(std::min(value(arg_list[0]), value(arg_list[1])), + std::min(value(arg_list[2]), value(arg_list[3]))), value(arg_list[4])); } }; @@ -12666,16 +13297,16 @@ namespace exprtk static inline T process_4(const Sequence& arg_list) { return std::max( - std::max(value(arg_list[0]),value(arg_list[1])), - std::max(value(arg_list[2]),value(arg_list[3]))); + std::max(value(arg_list[0]), value(arg_list[1])), + std::max(value(arg_list[2]), value(arg_list[3]))); } template static inline T process_5(const Sequence& arg_list) { return std::max( - std::max(std::max(value(arg_list[0]),value(arg_list[1])), - std::max(value(arg_list[2]),value(arg_list[3]))), + std::max(std::max(value(arg_list[0]), value(arg_list[1])), + std::max(value(arg_list[2]), value(arg_list[3]))), value(arg_list[4])); } }; @@ -13179,7 +13810,7 @@ namespace exprtk for (std::size_t i = 1; i < vec_size; ++i) { - T v_i = vec[i]; + const T v_i = vec[i]; if (v_i < result) result = v_i; @@ -13203,7 +13834,7 @@ namespace exprtk for (std::size_t i = 1; i < vec_size; ++i) { - T v_i = vec[i]; + const T v_i = vec[i]; if (v_i > result) result = v_i; @@ -13294,8 +13925,8 @@ namespace exprtk { public: - virtual ~cob_base_node() - {} + virtual ~cob_base_node() + {} inline virtual operator_type operation() const { @@ -13514,24 +14145,17 @@ namespace exprtk public: typedef expression_node* expression_ptr; + typedef std::pair branch_t; typedef Operation operation_t; - explicit unary_branch_node(expression_ptr brnch) - : branch_(brnch), - branch_deletable_(branch_deletable(branch_)) - {} - - ~unary_branch_node() + explicit unary_branch_node(expression_ptr branch) { - if (branch_ && branch_deletable_) - { - destroy_node(branch_); - } + construct_branch_pair(branch_, branch); } inline T value() const { - return Operation::process(branch_->value()); + return Operation::process(branch_.first->value()); } inline typename expression_node::node_type type() const @@ -13546,12 +14170,22 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_; + return branch_.first; } inline void release() { - branch_deletable_ = false; + branch_.second = false; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } private: @@ -13559,8 +14193,7 @@ namespace exprtk unary_branch_node(unary_branch_node&); unary_branch_node& operator=(unary_branch_node&); - expression_ptr branch_; - bool branch_deletable_; + branch_t branch_; }; template struct is_const { enum {result = 0}; }; @@ -14621,20 +15254,16 @@ namespace exprtk typedef Operation operation_t; // variable op constant node - explicit vob_node(const T& var, const expression_ptr brnch) + explicit vob_node(const T& var, const expression_ptr branch) : v_(var) { - init_branches<1>(branch_,brnch); - } - - ~vob_node() - { - cleanup_branches::execute(branch_); + construct_branch_pair(branch_, branch); } inline T value() const { - return Operation::process(v_,branch_[0].first->value()); + assert(branch_.first); + return Operation::process(v_,branch_.first->value()); } inline operator_type operation() const @@ -14649,7 +15278,17 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_[0].first; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } private: @@ -14658,7 +15297,7 @@ namespace exprtk vob_node& operator=(const vob_node&); const T& v_; - branch_t branch_[1]; + branch_t branch_; }; template @@ -14671,20 +15310,16 @@ namespace exprtk typedef Operation operation_t; // variable op constant node - explicit bov_node(const expression_ptr brnch, const T& var) + explicit bov_node(const expression_ptr branch, const T& var) : v_(var) { - init_branches<1>(branch_,brnch); - } - - ~bov_node() - { - cleanup_branches::execute(branch_); + construct_branch_pair(branch_, branch); } inline T value() const { - return Operation::process(branch_[0].first->value(),v_); + assert(branch_.first); + return Operation::process(branch_.first->value(),v_); } inline operator_type operation() const @@ -14699,7 +15334,17 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_[0].first; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } private: @@ -14708,7 +15353,7 @@ namespace exprtk bov_node& operator=(const bov_node&); const T& v_; - branch_t branch_[1]; + branch_t branch_; }; template @@ -14721,20 +15366,16 @@ namespace exprtk typedef Operation operation_t; // variable op constant node - explicit cob_node(const T const_var, const expression_ptr brnch) + explicit cob_node(const T const_var, const expression_ptr branch) : c_(const_var) { - init_branches<1>(branch_,brnch); - } - - ~cob_node() - { - cleanup_branches::execute(branch_); + construct_branch_pair(branch_, branch); } inline T value() const { - return Operation::process(c_,branch_[0].first->value()); + assert(branch_.first); + return Operation::process(c_,branch_.first->value()); } inline operator_type operation() const @@ -14754,13 +15395,23 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_[0].first; + return branch_.first; } inline expression_node* move_branch(const std::size_t&) { - branch_[0].second = false; - return branch_[0].first; + branch_.second = false; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } private: @@ -14769,7 +15420,7 @@ namespace exprtk cob_node& operator=(const cob_node&); const T c_; - branch_t branch_[1]; + branch_t branch_; }; template @@ -14782,20 +15433,16 @@ namespace exprtk typedef Operation operation_t; // variable op constant node - explicit boc_node(const expression_ptr brnch, const T const_var) + explicit boc_node(const expression_ptr branch, const T const_var) : c_(const_var) { - init_branches<1>(branch_,brnch); - } - - ~boc_node() - { - cleanup_branches::execute(branch_); + construct_branch_pair(branch_, branch); } inline T value() const { - return Operation::process(branch_[0].first->value(),c_); + assert(branch_.first); + return Operation::process(branch_.first->value(),c_); } inline operator_type operation() const @@ -14815,13 +15462,23 @@ namespace exprtk inline expression_node* branch(const std::size_t&) const { - return branch_[0].first; + return branch_.first; } inline expression_node* move_branch(const std::size_t&) { - branch_[0].second = false; - return branch_[0].first; + branch_.second = false; + return branch_.first; + } + + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); } private: @@ -14830,7 +15487,7 @@ namespace exprtk boc_node& operator=(const boc_node&); const T c_; - branch_t branch_[1]; + branch_t branch_; }; #ifndef exprtk_disable_string_capabilities @@ -15156,8 +15813,8 @@ namespace exprtk std::size_t str1_r0 = 0; std::size_t str1_r1 = 0; - range_t& range0 = (*str0_range_ptr_); - range_t& range1 = (*str1_range_ptr_); + const range_t& range0 = (*str0_range_ptr_); + const range_t& range1 = (*str1_range_ptr_); if ( range0(str0_r0, str0_r1, str0_base_ptr_->size()) && @@ -15292,19 +15949,15 @@ namespace exprtk typedef std::pair branch_t; typedef PowOp operation_t; - explicit bipow_node(expression_ptr brnch) - { - init_branches<1>(branch_, brnch); - } - - ~bipow_node() + explicit bipow_node(expression_ptr branch) { - cleanup_branches::execute(branch_); + construct_branch_pair(branch_, branch); } inline T value() const { - return PowOp::result(branch_[0].first->value()); + assert(branch_.first); + return PowOp::result(branch_.first->value()); } inline typename expression_node::node_type type() const @@ -15312,12 +15965,22 @@ namespace exprtk return expression_node::e_ipow; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + private: bipow_node(const bipow_node&); bipow_node& operator=(const bipow_node&); - branch_t branch_[1]; + branch_t branch_; }; template @@ -15359,19 +16022,15 @@ namespace exprtk typedef std::pair branch_t; typedef PowOp operation_t; - explicit bipowninv_node(expression_ptr brnch) - { - init_branches<1>(branch_, brnch); - } - - ~bipowninv_node() + explicit bipowninv_node(expression_ptr branch) { - cleanup_branches::execute(branch_); + construct_branch_pair(branch_, branch); } inline T value() const { - return (T(1) / PowOp::result(branch_[0].first->value())); + assert(branch_.first); + return (T(1) / PowOp::result(branch_.first->value())); } inline typename expression_node::node_type type() const @@ -15379,12 +16038,22 @@ namespace exprtk return expression_node::e_ipowinv; } + void collect_nodes(typename expression_node::noderef_list_t& node_delete_list) + { + expression_node::ndb_t::template collect(branch_, node_delete_list); + } + + std::size_t node_depth() const + { + return expression_node::ndb_t::compute_node_depth(branch_); + } + private: bipowninv_node(const bipowninv_node&); bipowninv_node& operator=(const bipowninv_node&); - branch_t branch_[1]; + branch_t branch_; }; template @@ -15533,37 +16202,55 @@ namespace exprtk template inline expression_node* allocate(OpType& operation, ExprNode (&branch)[1]) { - return allocate(operation, branch[0]); + expression_node* result = + allocate(operation, branch[0]); + result->node_depth(); + return result; } template inline expression_node* allocate(OpType& operation, ExprNode (&branch)[2]) { - return allocate(operation, branch[0], branch[1]); + expression_node* result = + allocate(operation, branch[0], branch[1]); + result->node_depth(); + return result; } template inline expression_node* allocate(OpType& operation, ExprNode (&branch)[3]) { - return allocate(operation, branch[0], branch[1], branch[2]); + expression_node* result = + allocate(operation, branch[0], branch[1], branch[2]); + result->node_depth(); + return result; } template inline expression_node* allocate(OpType& operation, ExprNode (&branch)[4]) { - return allocate(operation, branch[0], branch[1], branch[2], branch[3]); + expression_node* result = + allocate(operation, branch[0], branch[1], branch[2], branch[3]); + result->node_depth(); + return result; } template inline expression_node* allocate(OpType& operation, ExprNode (&branch)[5]) { - return allocate(operation, branch[0],branch[1], branch[2], branch[3], branch[4]); + expression_node* result = + allocate(operation, branch[0],branch[1], branch[2], branch[3], branch[4]); + result->node_depth(); + return result; } template inline expression_node* allocate(OpType& operation, ExprNode (&branch)[6]) { - return allocate(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]); + expression_node* result = + allocate(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]); + result->node_depth(); + return result; } template @@ -15578,89 +16265,128 @@ namespace exprtk template class Sequence> inline expression_node* allocate(const Sequence& seq) const { - return (new node_type(seq)); + expression_node* + result = (new node_type(seq)); + result->node_depth(); + return result; } template inline expression_node* allocate(T1& t1) const { - return (new node_type(t1)); + expression_node* + result = (new node_type(t1)); + result->node_depth(); + return result; } template inline expression_node* allocate_c(const T1& t1) const { - return (new node_type(t1)); + expression_node* + result = (new node_type(t1)); + result->node_depth(); + return result; } template inline expression_node* allocate(const T1& t1, const T2& t2) const { - return (new node_type(t1, t2)); + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; } template inline expression_node* allocate_cr(const T1& t1, T2& t2) const { - return (new node_type(t1, t2)); + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; } template inline expression_node* allocate_rc(T1& t1, const T2& t2) const { - return (new node_type(t1, t2)); + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; } template inline expression_node* allocate_rr(T1& t1, T2& t2) const { - return (new node_type(t1, t2)); + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; } template inline expression_node* allocate_tt(T1 t1, T2 t2) const { - return (new node_type(t1, t2)); + expression_node* + result = (new node_type(t1, t2)); + result->node_depth(); + return result; } template inline expression_node* allocate_ttt(T1 t1, T2 t2, T3 t3) const { - return (new node_type(t1, t2, t3)); + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; } template inline expression_node* allocate_tttt(T1 t1, T2 t2, T3 t3, T4 t4) const { - return (new node_type(t1, t2, t3, t4)); + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; } template inline expression_node* allocate_rrr(T1& t1, T2& t2, T3& t3) const { - return (new node_type(t1, t2, t3)); + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; } template inline expression_node* allocate_rrrr(T1& t1, T2& t2, T3& t3, T4& t4) const { - return (new node_type(t1, t2, t3, t4)); + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; } template inline expression_node* allocate_rrrrr(T1& t1, T2& t2, T3& t3, T4& t4, T5& t5) const { - return (new node_type(t1, t2, t3, t4, t5)); + expression_node* + result = (new node_type(t1, t2, t3, t4, t5)); + result->node_depth(); + return result; } template * allocate(const T1& t1, const T2& t2, const T3& t3) const { - return (new node_type(t1, t2, t3)); + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; } template * allocate(const T1& t1, const T2& t2, const T3& t3, const T4& t4) const { - return (new node_type(t1, t2, t3, t4)); + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6, t7)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)); + result->node_depth(); + return result; } template inline expression_node* allocate_type(T1 t1, T2 t2, T3 t3) const { - return (new node_type(t1, t2, t3)); + expression_node* + result = (new node_type(t1, t2, t3)); + result->node_depth(); + return result; } template * allocate_type(T1 t1, T2 t2, T3 t3, T4 t4) const { - return (new node_type(t1, t2, t3, t4)); + expression_node* + result = (new node_type(t1, t2, t3, t4)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6)); + result->node_depth(); + return result; } template * + result = (new node_type(t1, t2, t3, t4, t5, t6, t7)); + result->node_depth(); + return result; } template void inline free(expression_node*& e) const { + exprtk_debug(("node_allocator::free() - deleting expression_node " + "type: %03d addr: %p\n", + static_cast(e->type()), + reinterpret_cast(e))); delete e; e = 0; } @@ -15969,83 +16738,84 @@ namespace exprtk virtual ~ifunction() {} - #define empty_method_body \ + #define empty_method_body(N) \ { \ + exprtk_debug(("ifunction::operator() - Operator(" #N ") has not been overridden\n")); \ return std::numeric_limits::quiet_NaN(); \ } \ inline virtual T operator() () - empty_method_body + empty_method_body(0) - inline virtual T operator() (const T&) - empty_method_body + inline virtual T operator() (const T&) + empty_method_body(1) - inline virtual T operator() (const T&,const T&) - empty_method_body + inline virtual T operator() (const T&,const T&) + empty_method_body(2) - inline virtual T operator() (const T&, const T&, const T&) - empty_method_body + inline virtual T operator() (const T&, const T&, const T&) + empty_method_body(3) inline virtual T operator() (const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(4) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(5) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(6) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(7) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(8) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(9) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(10) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, - const T&) - empty_method_body + const T&) + empty_method_body(11) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(12) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(13) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(14) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(15) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(16) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(17) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(18) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(19) inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&) - empty_method_body + empty_method_body(20) #undef empty_method_body @@ -16062,7 +16832,7 @@ namespace exprtk inline virtual T operator() (const std::vector&) { - exprtk_debug(("ivararg_function::operator() - Operator has not been overridden.\n")); + exprtk_debug(("ivararg_function::operator() - Operator has not been overridden\n")); return std::numeric_limits::quiet_NaN(); } }; @@ -16093,7 +16863,7 @@ namespace exprtk #define igeneric_function_empty_body(N) \ { \ - exprtk_debug(("igeneric_function::operator() - Operator has not been overridden. ["#N"]\n")); \ + exprtk_debug(("igeneric_function::operator() - Operator(" #N ") has not been overridden\n")); \ return std::numeric_limits::quiet_NaN(); \ } \ @@ -16346,6 +17116,27 @@ namespace exprtk : size(0) {} + struct deleter + { + #define exprtk_define_process(Type) \ + static inline void process(std::pair& n) \ + { \ + delete n.second; \ + } \ + + exprtk_define_process(variable_node_t ) + exprtk_define_process(vector_t ) + #ifndef exprtk_disable_string_capabilities + exprtk_define_process(stringvar_node_t) + #endif + + #undef exprtk_define_process + + template + static inline void process(std::pair&) + {} + }; + inline bool symbol_exists(const std::string& symbol_name) const { if (symbol_name.empty()) @@ -16581,16 +17372,6 @@ namespace exprtk if (map.end() != itr) { - struct deleter - { - static inline void process(std::pair& n) { delete n.second; } - static inline void process(std::pair& n) { delete n.second; } - #ifndef exprtk_disable_string_capabilities - static inline void process(std::pair& n) { delete n.second; } - #endif - static inline void process(std::pair&) { } - }; - if (delete_node) { deleter::process((*itr).second); @@ -16627,16 +17408,6 @@ namespace exprtk inline void clear(const bool delete_node = true) { - struct deleter - { - static inline void process(std::pair& n) { delete n.second; } - static inline void process(std::pair& n) { delete n.second; } - static inline void process(std::pair&) { } - #ifndef exprtk_disable_string_capabilities - static inline void process(std::pair& n) { delete n.second; } - #endif - }; - if (!map.empty()) { if (delete_node) @@ -16702,20 +17473,20 @@ namespace exprtk } }; - typedef details::expression_node* expression_ptr; - typedef typename details::variable_node variable_t; - typedef typename details::vector_holder vector_holder_t; - typedef variable_t* variable_ptr; + typedef details::expression_node* expression_ptr; + typedef typename details::variable_node variable_t; + typedef typename details::vector_holder vector_holder_t; + typedef variable_t* variable_ptr; #ifndef exprtk_disable_string_capabilities typedef typename details::stringvar_node stringvar_t; - typedef stringvar_t* stringvar_ptr; + typedef stringvar_t* stringvar_ptr; #endif - typedef ifunction function_t; - typedef ivararg_function vararg_function_t; - typedef igeneric_function generic_function_t; - typedef function_t* function_ptr; - typedef vararg_function_t* vararg_function_ptr; - typedef generic_function_t* generic_function_ptr; + typedef ifunction function_t; + typedef ivararg_function vararg_function_t; + typedef igeneric_function generic_function_t; + typedef function_t* function_ptr; + typedef vararg_function_t* vararg_function_ptr; + typedef generic_function_t* generic_function_ptr; static const std::size_t lut_size = 256; @@ -16724,16 +17495,16 @@ namespace exprtk { struct st_data { - type_store,T> variable_store; + type_store variable_store; + type_store function_store; + type_store vararg_function_store; + type_store generic_function_store; + type_store string_function_store; + type_store overload_function_store; + type_store vector_store; #ifndef exprtk_disable_string_capabilities - type_store,std::string> stringvar_store; + type_store stringvar_store; #endif - type_store,ifunction > function_store; - type_store,ivararg_function > vararg_function_store; - type_store,igeneric_function > generic_function_store; - type_store,igeneric_function > string_function_store; - type_store,igeneric_function > overload_function_store; - type_store vector_store; st_data() { @@ -17115,7 +17886,7 @@ namespace exprtk else if (symbol_exists(variable_name)) return false; else - return local_data().variable_store.add(variable_name,t,is_constant); + return local_data().variable_store.add(variable_name, t, is_constant); } inline bool add_constant(const std::string& constant_name, const T& value) @@ -17130,7 +17901,7 @@ namespace exprtk local_data().local_symbol_list_.push_back(value); T& t = local_data().local_symbol_list_.back(); - return add_variable(constant_name,t,true); + return add_variable(constant_name, t, true); } #ifndef exprtk_disable_string_capabilities @@ -17143,7 +17914,7 @@ namespace exprtk else if (symbol_exists(stringvar_name)) return false; else - return local_data().stringvar_store.add(stringvar_name,s,is_constant); + return local_data().stringvar_store.add(stringvar_name, s, is_constant); } #endif @@ -17179,30 +17950,22 @@ namespace exprtk return false; else if (symbol_exists(function_name)) return false; - else if ( - ( - (generic_function_t::e_rtrn_scalar == function.rtrn_type) || - (generic_function_t::e_rtrn_string == function.rtrn_type) - ) && - std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|") - ) - return false; - else if ( - (generic_function_t::e_rtrn_overload == function.rtrn_type) && - std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|:") - ) - return false; - - switch (function.rtrn_type) + else { - case generic_function_t::e_rtrn_scalar : - return local_data().generic_function_store.add(function_name,function); + switch (function.rtrn_type) + { + case generic_function_t::e_rtrn_scalar : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().generic_function_store.add(function_name,function) : false; - case generic_function_t::e_rtrn_string : - return local_data().string_function_store.add(function_name,function); + case generic_function_t::e_rtrn_string : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().string_function_store.add(function_name,function) : false; - case generic_function_t::e_rtrn_overload : - return local_data().overload_function_store.add(function_name,function); + case generic_function_t::e_rtrn_overload : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ? + local_data().overload_function_store.add(function_name,function) : false; + } } return false; @@ -17268,30 +18031,22 @@ namespace exprtk return false; else if (symbol_exists(function_name,false)) return false; - else if ( - ( - (generic_function_t::e_rtrn_scalar == function.rtrn_type) || - (generic_function_t::e_rtrn_string == function.rtrn_type) - ) && - std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|") - ) - return false; - else if ( - generic_function_t::e_rtrn_overload && - std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|:") - ) - return false; - - switch (function.rtrn_type) + else { - case generic_function_t::e_rtrn_scalar : - return local_data().generic_function_store.add(function_name,function); + switch (function.rtrn_type) + { + case generic_function_t::e_rtrn_scalar : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().generic_function_store.add(function_name,function) : false; - case generic_function_t::e_rtrn_string : - return local_data().string_function_store.add(function_name,function); + case generic_function_t::e_rtrn_string : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ? + local_data().string_function_store.add(function_name,function) : false; - case generic_function_t::e_rtrn_overload : - return local_data().overload_function_store.add(function_name,function); + case generic_function_t::e_rtrn_overload : + return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ? + local_data().overload_function_store.add(function_name,function) : false; + } } return false; @@ -17321,7 +18076,7 @@ namespace exprtk else if (0 == v_size) return false; else - return local_data().vector_store.add(vector_name,v,v_size); + return local_data().vector_store.add(vector_name, v, v_size); } template @@ -17684,7 +18439,7 @@ namespace exprtk ('_' != symbol[i]) ) { - if (('.' == symbol[i]) && (i < (symbol.size() - 1))) + if ((i < (symbol.size() - 1)) && ('.' == symbol[i])) continue; else return false; @@ -17710,7 +18465,7 @@ namespace exprtk ('_' != symbol[i]) ) { - if (('.' == symbol[i]) && (i < (symbol.size() - 1))) + if ((i < (symbol.size() - 1)) && ('.' == symbol[i])) continue; else return false; @@ -17819,13 +18574,13 @@ namespace exprtk case e_vecholder : delete reinterpret_cast(local_data_list[i].pointer); break; - case e_data : delete (T*)(local_data_list[i].pointer); + case e_data : delete reinterpret_cast(local_data_list[i].pointer); break; - case e_vecdata : delete [] (T*)(local_data_list[i].pointer); + case e_vecdata : delete [] reinterpret_cast(local_data_list[i].pointer); break; - case e_string : delete (std::string*)(local_data_list[i].pointer); + case e_string : delete reinterpret_cast(local_data_list[i].pointer); break; default : break; @@ -17944,6 +18699,9 @@ namespace exprtk inline T value() const { + assert(control_block_ ); + assert(control_block_->expr); + return control_block_->expr->value(); } @@ -18101,7 +18859,7 @@ namespace exprtk } control_block* control_block_; - symtab_list_t symbol_table_list_; + symtab_list_t symbol_table_list_; friend class parser; friend class expression_helper; @@ -18160,7 +18918,8 @@ namespace exprtk e_numeric = 4, e_symtab = 5, e_lexer = 6, - e_helper = 7 + e_helper = 7, + e_parser = 8 }; struct type @@ -18218,6 +18977,7 @@ namespace exprtk case e_symtab : return std::string("Symbol Error" ); case e_lexer : return std::string("Lexer Error" ); case e_helper : return std::string("Helper Error" ); + case e_parser : return std::string("Parser Error" ); default : return std::string("Unknown Error"); } } @@ -18617,26 +19377,24 @@ namespace exprtk inline void free_element(scope_element& se) { - #ifdef exprtk_enable_debugging exprtk_debug(("free_element() - se[%s]\n", se.name.c_str())); - #endif switch (se.type) { - case scope_element::e_variable : if (se.data ) delete (T*) se.data; - if (se.var_node) delete se.var_node; + case scope_element::e_variable : delete reinterpret_cast(se.data); + delete se.var_node; break; - case scope_element::e_vector : if (se.data ) delete[] (T*) se.data; - if (se.vec_node) delete se.vec_node; + case scope_element::e_vector : delete[] reinterpret_cast(se.data); + delete se.vec_node; break; - case scope_element::e_vecelem : if (se.var_node) delete se.var_node; + case scope_element::e_vecelem : delete se.var_node; break; #ifndef exprtk_disable_string_capabilities - case scope_element::e_string : if (se.data ) delete (std::string*) se.data; - if (se.str_node) delete se.str_node; + case scope_element::e_string : delete reinterpret_cast(se.data); + delete se.str_node; break; #endif @@ -18734,6 +19492,45 @@ namespace exprtk parser_t& parser_; }; + class stack_limit_handler + { + public: + + typedef parser parser_t; + + explicit stack_limit_handler(parser& p) + : parser_(p), + limit_exceeded_(false) + { + if (++parser_.state_.stack_depth > parser_.settings_.max_stack_depth_) + { + limit_exceeded_ = true; + parser_.set_error( + make_error(parser_error::e_parser, + "ERR000 - Current stack depth " + details::to_str(parser_.state_.stack_depth) + + " exceeds maximum allowed stack depth of " + details::to_str(parser_.settings_.max_stack_depth_), + exprtk_error_location)); + } + } + + ~stack_limit_handler() + { + parser_.state_.stack_depth--; + } + + bool operator!() + { + return limit_exceeded_; + } + + private: + + stack_limit_handler& operator=(const stack_limit_handler&); + + parser_t& parser_; + bool limit_exceeded_; + }; + struct symtab_store { symbol_table_list_t symtab_list_; @@ -19004,7 +19801,7 @@ namespace exprtk continue; else if (!local_data(i).stringvar_store.symbol_exists(symbol_name)) continue; - else if ( local_data(i).stringvar_store.is_constant(symbol_name)) + else if (local_data(i).stringvar_store.is_constant(symbol_name)) return true; } @@ -19177,11 +19974,13 @@ namespace exprtk void reset() { - parsing_return_stmt = false; - parsing_break_stmt = false; - return_stmt_present = false; - side_effect_present = false; - scope_depth = 0; + parsing_return_stmt = false; + parsing_break_stmt = false; + return_stmt_present = false; + side_effect_present = false; + scope_depth = 0; + stack_depth = 0; + parsing_loop_stmt_count = 0; } #ifndef exprtk_enable_debugging @@ -19204,6 +20003,8 @@ namespace exprtk bool side_effect_present; bool type_check_enabled; std::size_t scope_depth; + std::size_t stack_depth; + std::size_t parsing_loop_stmt_count; }; public: @@ -19530,6 +20331,8 @@ namespace exprtk e_strength_reduction; settings_store(const std::size_t compile_options = compile_all_opts) + : max_stack_depth_(400), + max_node_depth_(10000) { load_compile_options(compile_options); } @@ -19734,6 +20537,15 @@ namespace exprtk .find(assign_opr_to_string(assignment_operation)); } + bool logic_disabled(const details::operator_type logic_operation) const + { + if (disabled_logic_set_.empty()) + return false; + else + return disabled_logic_set_.end() != disabled_logic_set_ + .find(logic_opr_to_string(logic_operation)); + } + bool arithmetic_disabled(const details::operator_type arithmetic_operation) const { if (disabled_arithmetic_set_.empty()) @@ -19938,6 +20750,16 @@ namespace exprtk return (*this); } + void set_max_stack_depth(const std::size_t mx_stack_depth) + { + max_stack_depth_ = mx_stack_depth; + } + + void set_max_node_depth(const std::size_t max_node_depth) + { + max_node_depth_ = max_node_depth; + } + private: void load_compile_options(const std::size_t compile_options) @@ -20000,6 +20822,21 @@ namespace exprtk } } + std::string logic_opr_to_string(details::operator_type opr) const + { + switch (opr) + { + case details::e_and : return "and" ; + case details::e_or : return "or" ; + case details::e_xor : return "xor" ; + case details::e_nand : return "nand"; + case details::e_nor : return "nor" ; + case details::e_xnor : return "xnor"; + case details::e_notl : return "not" ; + default : return "" ; + } + } + bool enable_replacer_; bool enable_joiner_; bool enable_numeric_check_; @@ -20021,6 +20858,9 @@ namespace exprtk disabled_entity_set_t disabled_assignment_set_; disabled_entity_set_t disabled_inequality_set_; + std::size_t max_stack_depth_; + std::size_t max_node_depth_; + friend class parser; }; @@ -20040,7 +20880,8 @@ namespace exprtk #pragma warning(pop) #endif operator_joiner_2_(2), - operator_joiner_3_(3) + operator_joiner_3_(3), + loop_runtime_check_(0) { init_precompilation(); @@ -20078,8 +20919,8 @@ namespace exprtk if (settings_.replacer_enabled()) { symbol_replacer_.clear(); - symbol_replacer_.add_replace("true" ,"1",lexer::token::e_number); - symbol_replacer_.add_replace("false","0",lexer::token::e_number); + symbol_replacer_.add_replace("true" , "1", lexer::token::e_number); + symbol_replacer_.add_replace("false", "0", lexer::token::e_number); helper_assembly_.token_modifier_list.clear(); helper_assembly_.register_modifier(&symbol_replacer_); } @@ -20144,7 +20985,7 @@ namespace exprtk { set_error( make_error(parser_error::e_syntax, - "ERR000 - Empty expression!", + "ERR001 - Empty expression!", exprtk_error_location)); return false; @@ -20160,7 +21001,7 @@ namespace exprtk { set_error( make_error(parser_error::e_syntax, - "ERR001 - Empty expression!", + "ERR002 - Empty expression!", exprtk_error_location)); return false; @@ -20207,7 +21048,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR002 - Invalid expression encountered", + "ERR003 - Invalid expression encountered", exprtk_error_location)); } @@ -20226,13 +21067,13 @@ namespace exprtk inline expression_t compile(const std::string& expression_string, symbol_table_t& symtab) { - expression_t expr; + expression_t expression; - expr.register_symbol_table(symtab); + expression.register_symbol_table(symtab); - compile(expression_string,expr); + compile(expression_string,expression); - return expr; + return expression; } void process_lexer_errors() @@ -20241,7 +21082,7 @@ namespace exprtk { if (lexer()[i].is_error()) { - std::string diagnostic = "ERR003 - "; + std::string diagnostic = "ERR004 - "; switch (lexer()[i].type) { @@ -20309,7 +21150,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, bracket_checker_ptr->error_token(), - "ERR004 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'", + "ERR005 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'", exprtk_error_location)); } else if (0 != (numeric_checker_ptr = dynamic_cast(helper_assembly_.error_token_scanner))) @@ -20321,7 +21162,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, error_token, - "ERR005 - Invalid numeric token: '" + error_token.value + "'", + "ERR006 - Invalid numeric token: '" + error_token.value + "'", exprtk_error_location)); } @@ -20339,7 +21180,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, error_token.first, - "ERR006 - Invalid token sequence: '" + + "ERR007 - Invalid token sequence: '" + error_token.first.value + "' and '" + error_token.second.value + "'", exprtk_error_location)); @@ -20359,7 +21200,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, error_token.first, - "ERR007 - Invalid token sequence: '" + + "ERR008 - Invalid token sequence: '" + error_token.first.value + "' and '" + error_token.second.value + "'", exprtk_error_location)); @@ -20453,6 +21294,16 @@ namespace exprtk unknown_symbol_resolver_ = &default_usr_; } + inline void register_loop_runtime_check(loop_runtime_check& lrtchk) + { + loop_runtime_check_ = &lrtchk; + } + + inline void clear_loop_runtime_check() + { + loop_runtime_check_ = loop_runtime_check_ptr(0); + } + private: inline bool valid_base_operation(const std::string& symbol) const @@ -20496,6 +21347,11 @@ namespace exprtk settings_.function_enabled(symbol); } + bool is_invalid_logic_operation(const details::operator_type operation) const + { + return settings_.logic_disabled(operation); + } + bool is_invalid_arithmetic_operation(const details::operator_type operation) const { return settings_.arithmetic_disabled(operation); @@ -20515,13 +21371,17 @@ namespace exprtk inline void next_token() { const std::string ct_str = current_token().value; + const std::size_t ct_pos = current_token().position; parser_helper::next_token(); const std::string depth(2 * state_.scope_depth,' '); exprtk_debug(("%s" - "prev[%s] --> curr[%s]\n", + "prev[%s | %04d] --> curr[%s | %04d] stack_level: %3d\n", depth.c_str(), ct_str.c_str(), - current_token().value.c_str())); + static_cast(ct_pos), + current_token().value.c_str(), + static_cast(current_token().position), + static_cast(state_.stack_depth))); } #endif @@ -20550,7 +21410,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR008 - Invalid expression encountered", + "ERR009 - Invalid expression encountered", exprtk_error_location)); } @@ -20642,6 +21502,13 @@ namespace exprtk inline expression_node_ptr parse_expression(precedence_level precedence = e_level00) { + stack_limit_handler slh(*this); + + if (!slh) + { + return error_node(); + } + expression_node_ptr expression = parse_branch(precedence); if (0 == expression) @@ -20780,14 +21647,26 @@ namespace exprtk expression_node_ptr right_branch = error_node(); expression_node_ptr new_expression = error_node(); - if (is_invalid_arithmetic_operation(current_state.operation)) + if (is_invalid_logic_operation(current_state.operation)) { free_node(node_allocator_,expression); set_error( make_error(parser_error::e_syntax, prev_token, - "ERR009 - Invalid arithmetic operation '" + details::to_str(current_state.operation) + "'", + "ERR010 - Invalid or disabled logic operation '" + details::to_str(current_state.operation) + "'", + exprtk_error_location)); + + return error_node(); + } + else if (is_invalid_arithmetic_operation(current_state.operation)) + { + free_node(node_allocator_,expression); + + set_error( + make_error(parser_error::e_syntax, + prev_token, + "ERR011 - Invalid or disabled arithmetic operation '" + details::to_str(current_state.operation) + "'", exprtk_error_location)); return error_node(); @@ -20799,7 +21678,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR010 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'", + "ERR012 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'", exprtk_error_location)); return error_node(); @@ -20811,7 +21690,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR011 - Invalid assignment operation '" + details::to_str(current_state.operation) + "'", + "ERR013 - Invalid or disabled assignment operation '" + details::to_str(current_state.operation) + "'", exprtk_error_location)); return error_node(); @@ -20820,17 +21699,17 @@ namespace exprtk if (0 != (right_branch = parse_expression(current_state.right))) { if ( - details::is_return_node( expression) || + details::is_return_node(expression ) || details::is_return_node(right_branch) ) { - free_node(node_allocator_, expression); + free_node(node_allocator_, expression ); free_node(node_allocator_, right_branch); set_error( make_error(parser_error::e_syntax, prev_token, - "ERR012 - Return statements cannot be part of sub-expressions", + "ERR014 - Return statements cannot be part of sub-expressions", exprtk_error_location)); return error_node(); @@ -20853,11 +21732,11 @@ namespace exprtk prev_token, !synthesis_error_.empty() ? synthesis_error_ : - "ERR013 - General parsing error at token: '" + prev_token.value + "'", + "ERR015 - General parsing error at token: '" + prev_token.value + "'", exprtk_error_location)); } - free_node(node_allocator_, expression); + free_node(node_allocator_, expression ); free_node(node_allocator_, right_branch); return error_node(); @@ -20878,6 +21757,20 @@ namespace exprtk } } + if ((0 != expression) && (expression->node_depth() > settings_.max_node_depth_)) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR016 - Expression depth of " + details::to_str(static_cast(expression->node_depth())) + + " exceeds maximum allowed expression depth of " + details::to_str(static_cast(settings_.max_node_depth_)), + exprtk_error_location)); + + free_node(node_allocator_,expression); + + return error_node(); + } + return expression; } @@ -20923,7 +21816,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR014 - Failed to find variable node in symbol table", + "ERR017 - Failed to find variable node in symbol table", exprtk_error_location)); free_node(node_allocator_,node); @@ -20941,6 +21834,31 @@ namespace exprtk return reinterpret_cast(0); } + struct scoped_expression_delete + { + scoped_expression_delete(parser& pr, expression_node_ptr& expression) + : delete_ptr(true), + parser_(pr), + expression_(expression) + {} + + ~scoped_expression_delete() + { + if (delete_ptr) + { + free_node(parser_.node_allocator_, expression_); + } + } + + bool delete_ptr; + parser& parser_; + expression_node_ptr& expression_; + + private: + + scoped_expression_delete& operator=(const scoped_expression_delete&); + }; + template struct scoped_delete { @@ -20964,7 +21882,7 @@ namespace exprtk { for (std::size_t i = 0; i < N; ++i) { - free_node(parser_.node_allocator_,p_[i]); + free_node(parser_.node_allocator_, p_[i]); } } } @@ -21072,6 +21990,21 @@ namespace exprtk bool original_value_; }; + struct scoped_inc_dec + { + explicit scoped_inc_dec(std::size_t& v) + : v_(v) + { ++v_; } + + ~scoped_inc_dec() + { + assert(v_ > 0); + --v_; + } + + std::size_t& v_; + }; + inline expression_node_ptr parse_function_invocation(ifunction* function, const std::string& function_name) { expression_node_ptr func_node = reinterpret_cast(0); @@ -21103,7 +22036,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR015 - Invalid number of parameters for function: '" + function_name + "'", + "ERR018 - Invalid number of parameters for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21117,7 +22050,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR016 - Failed to generate call to function: '" + function_name + "'", + "ERR019 - Failed to generate call to function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21136,7 +22069,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR017 - Expecting ifunction '" + function_name + "' to have non-zero parameter count", + "ERR020 - Expecting ifunction '" + function_name + "' to have non-zero parameter count", exprtk_error_location)); return error_node(); @@ -21159,7 +22092,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR018 - Expecting argument list for function: '" + function_name + "'", + "ERR021 - Expecting argument list for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21174,7 +22107,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR019 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'", + "ERR022 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21186,7 +22119,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR020 - Invalid number of arguments for function: '" + function_name + "'", + "ERR023 - Invalid number of arguments for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21199,7 +22132,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR021 - Invalid number of arguments for function: '" + function_name + "'", + "ERR024 - Invalid number of arguments for function: '" + function_name + "'", exprtk_error_location)); return error_node(); @@ -21207,7 +22140,7 @@ namespace exprtk else result = expression_generator_.function(function,branch); - sd.delete_ptr = false; + sd.delete_ptr = (0 == result); return result; } @@ -21228,7 +22161,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR022 - Expecting '()' to proceed call to function: '" + function_name + "'", + "ERR025 - Expecting '()' to proceed call to function: '" + function_name + "'", exprtk_error_location)); free_node(node_allocator_,result); @@ -21253,7 +22186,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR023 - Expected a '(' at start of function call to '" + function_name + + "ERR026 - Expected a '(' at start of function call to '" + function_name + "', instead got: '" + current_token().value + "'", exprtk_error_location)); @@ -21265,7 +22198,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR024 - Expected at least one input parameter for function call '" + function_name + "'", + "ERR027 - Expected at least one input parameter for function call '" + function_name + "'", exprtk_error_location)); return 0; @@ -21291,7 +22224,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR025 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'", + "ERR028 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'", exprtk_error_location)); return 0; @@ -21303,7 +22236,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR026 - Invalid number of input parameters passed to function '" + function_name + "'", + "ERR029 - Invalid number of input parameters passed to function '" + function_name + "'", exprtk_error_location)); return 0; @@ -21326,7 +22259,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, diagnostic_token, - "ERR027 - No entry found for base operation: " + operation_name, + "ERR030 - No entry found for base operation: " + operation_name, exprtk_error_location)); return error_node(); @@ -21373,7 +22306,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, diagnostic_token, - "ERR028 - Invalid number of input parameters for call to function: '" + operation_name + "'", + "ERR031 - Invalid number of input parameters for call to function: '" + operation_name + "'", exprtk_error_location)); return error_node(); @@ -21393,7 +22326,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR029 - Expected ',' between if-statement condition and consequent", + "ERR032 - Expected ',' between if-statement condition and consequent", exprtk_error_location)); result = false; } @@ -21402,7 +22335,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR030 - Failed to parse consequent for if-statement", + "ERR033 - Failed to parse consequent for if-statement", exprtk_error_location)); result = false; } @@ -21411,7 +22344,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR031 - Expected ',' between if-statement consequent and alternative", + "ERR034 - Expected ',' between if-statement consequent and alternative", exprtk_error_location)); result = false; } @@ -21420,7 +22353,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR032 - Failed to parse alternative for if-statement", + "ERR035 - Failed to parse alternative for if-statement", exprtk_error_location)); result = false; } @@ -21429,7 +22362,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR033 - Expected ')' at the end of if-statement", + "ERR036 - Expected ')' at the end of if-statement", exprtk_error_location)); result = false; } @@ -21437,7 +22370,7 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities if (result) { - const bool consq_is_str = is_generally_string_node( consequent); + const bool consq_is_str = is_generally_string_node(consequent ); const bool alter_is_str = is_generally_string_node(alternative); if (consq_is_str || alter_is_str) @@ -21445,13 +22378,13 @@ namespace exprtk if (consq_is_str && alter_is_str) { return expression_generator_ - .conditional_string(condition,consequent,alternative); + .conditional_string(condition, consequent, alternative); } set_error( make_error(parser_error::e_syntax, current_token(), - "ERR034 - Return types of ternary if-statement differ", + "ERR037 - Return types of ternary if-statement differ", exprtk_error_location)); result = false; @@ -21461,15 +22394,15 @@ namespace exprtk if (!result) { - free_node(node_allocator_, condition); - free_node(node_allocator_, consequent); - free_node(node_allocator_,alternative); + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent ); + free_node(node_allocator_, alternative); return error_node(); } else return expression_generator_ - .conditional(condition,consequent,alternative); + .conditional(condition, consequent, alternative); } inline expression_node_ptr parse_conditional_statement_02(expression_node_ptr condition) @@ -21486,7 +22419,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR035 - Failed to parse body of consequent for if-statement", + "ERR038 - Failed to parse body of consequent for if-statement", exprtk_error_location)); result = false; @@ -21509,7 +22442,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR036 - Expected ';' at the end of the consequent for if-statement", + "ERR039 - Expected ';' at the end of the consequent for if-statement", exprtk_error_location)); result = false; @@ -21520,7 +22453,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR037 - Failed to parse body of consequent for if-statement", + "ERR040 - Failed to parse body of consequent for if-statement", exprtk_error_location)); result = false; @@ -21540,7 +22473,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR038 - Failed to parse body of the 'else' for if-statement", + "ERR041 - Failed to parse body of the 'else' for if-statement", exprtk_error_location)); result = false; @@ -21553,7 +22486,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR039 - Failed to parse body of if-else statement", + "ERR042 - Failed to parse body of if-else statement", exprtk_error_location)); result = false; @@ -21566,7 +22499,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR040 - Expected ';' at the end of the 'else-if' for the if-statement", + "ERR043 - Expected ';' at the end of the 'else-if' for the if-statement", exprtk_error_location)); result = false; @@ -21577,7 +22510,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR041 - Failed to parse body of the 'else' for if-statement", + "ERR044 - Failed to parse body of the 'else' for if-statement", exprtk_error_location)); result = false; @@ -21588,7 +22521,7 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities if (result) { - const bool consq_is_str = is_generally_string_node( consequent); + const bool consq_is_str = is_generally_string_node(consequent ); const bool alter_is_str = is_generally_string_node(alternative); if (consq_is_str || alter_is_str) @@ -21602,7 +22535,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR042 - Return types of ternary if-statement differ", + "ERR045 - Return types of ternary if-statement differ", exprtk_error_location)); result = false; @@ -21612,8 +22545,8 @@ namespace exprtk if (!result) { - free_node(node_allocator_, condition); - free_node(node_allocator_, consequent); + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent ); free_node(node_allocator_, alternative); return error_node(); @@ -21634,7 +22567,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR043 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'", + "ERR046 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'", exprtk_error_location)); return error_node(); @@ -21644,7 +22577,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR044 - Failed to parse condition for if-statement", + "ERR047 - Failed to parse condition for if-statement", exprtk_error_location)); return error_node(); @@ -21676,7 +22609,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR045 - Invalid if-statement", + "ERR048 - Invalid if-statement", exprtk_error_location)); free_node(node_allocator_,condition); @@ -21697,7 +22630,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR046 - Encountered invalid condition branch for ternary if-statement", + "ERR049 - Encountered invalid condition branch for ternary if-statement", exprtk_error_location)); return error_node(); @@ -21707,7 +22640,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR047 - Expected '?' after condition of ternary if-statement", + "ERR050 - Expected '?' after condition of ternary if-statement", exprtk_error_location)); result = false; @@ -21717,7 +22650,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR048 - Failed to parse consequent for ternary if-statement", + "ERR051 - Failed to parse consequent for ternary if-statement", exprtk_error_location)); result = false; @@ -21727,7 +22660,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR049 - Expected ':' between ternary if-statement consequent and alternative", + "ERR052 - Expected ':' between ternary if-statement consequent and alternative", exprtk_error_location)); result = false; @@ -21737,7 +22670,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR050 - Failed to parse alternative for ternary if-statement", + "ERR053 - Failed to parse alternative for ternary if-statement", exprtk_error_location)); result = false; @@ -21746,7 +22679,7 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities if (result) { - const bool consq_is_str = is_generally_string_node( consequent); + const bool consq_is_str = is_generally_string_node(consequent ); const bool alter_is_str = is_generally_string_node(alternative); if (consq_is_str || alter_is_str) @@ -21760,7 +22693,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR051 - Return types of ternary if-statement differ", + "ERR054 - Return types of ternary if-statement differ", exprtk_error_location)); result = false; @@ -21770,8 +22703,8 @@ namespace exprtk if (!result) { - free_node(node_allocator_, condition); - free_node(node_allocator_, consequent); + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent ); free_node(node_allocator_, alternative); return error_node(); @@ -21781,6 +22714,22 @@ namespace exprtk .conditional(condition, consequent, alternative); } + inline expression_node_ptr parse_not_statement() + { + if (settings_.logic_disabled("not")) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR055 - Invalid or disabled logic operation 'not'", + exprtk_error_location)); + + return error_node(); + } + + return parse_base_operation(); + } + inline expression_node_ptr parse_while_loop() { // Parse: [while][(][test expr][)][{][expression][}] @@ -21797,7 +22746,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR052 - Expected '(' at start of while-loop condition statement", + "ERR056 - Expected '(' at start of while-loop condition statement", exprtk_error_location)); return error_node(); @@ -21807,7 +22756,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR053 - Failed to parse condition for while-loop", + "ERR057 - Failed to parse condition for while-loop", exprtk_error_location)); return error_node(); @@ -21817,7 +22766,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR054 - Expected ')' at end of while-loop condition statement", + "ERR058 - Expected ')' at end of while-loop condition statement", exprtk_error_location)); result = false; @@ -21827,12 +22776,14 @@ namespace exprtk if (result) { + scoped_inc_dec sid(state_.parsing_loop_stmt_count); + if (0 == (branch = parse_multi_sequence("while-loop"))) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR055 - Failed to parse body of while-loop")); + "ERR059 - Failed to parse body of while-loop")); result = false; } else if (0 == (result_node = expression_generator_.while_loop(condition, @@ -21842,7 +22793,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR056 - Failed to synthesize while-loop", + "ERR060 - Failed to synthesize while-loop", exprtk_error_location)); result = false; @@ -21851,8 +22802,8 @@ namespace exprtk if (!result) { - free_node(node_allocator_, branch); - free_node(node_allocator_, condition); + free_node(node_allocator_, branch ); + free_node(node_allocator_, condition ); free_node(node_allocator_, result_node); brkcnt_list_.pop_front(); @@ -21890,6 +22841,8 @@ namespace exprtk scoped_bool_or_restorer sbr(state_.side_effect_present); + scoped_inc_dec sid(state_.parsing_loop_stmt_count); + for ( ; ; ) { state_.side_effect_present = false; @@ -21918,7 +22871,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR057 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop", + "ERR061 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop", exprtk_error_location)); return error_node(); @@ -21942,7 +22895,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR058 - Failed to parse body of repeat until loop", + "ERR062 - Failed to parse body of repeat until loop", exprtk_error_location)); return error_node(); @@ -21956,7 +22909,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR059 - Expected '(' before condition statement of repeat until loop", + "ERR063 - Expected '(' before condition statement of repeat until loop", exprtk_error_location)); free_node(node_allocator_,branch); @@ -21970,7 +22923,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR060 - Failed to parse condition for repeat until loop", + "ERR064 - Failed to parse condition for repeat until loop", exprtk_error_location)); free_node(node_allocator_,branch); @@ -21982,10 +22935,10 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR061 - Expected ')' after condition of repeat until loop", + "ERR065 - Expected ')' after condition of repeat until loop", exprtk_error_location)); - free_node(node_allocator_, branch); + free_node(node_allocator_, branch ); free_node(node_allocator_, condition); brkcnt_list_.pop_front(); @@ -22003,7 +22956,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR062 - Failed to synthesize repeat until loop", + "ERR066 - Failed to synthesize repeat until loop", exprtk_error_location)); free_node(node_allocator_,condition); @@ -22038,7 +22991,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR063 - Expected '(' at start of for-loop", + "ERR067 - Expected '(' at start of for-loop", exprtk_error_location)); return error_node(); @@ -22058,7 +23011,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR064 - Expected a variable at the start of initialiser section of for-loop", + "ERR068 - Expected a variable at the start of initialiser section of for-loop", exprtk_error_location)); return error_node(); @@ -22068,7 +23021,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR065 - Expected variable assignment of initialiser section of for-loop", + "ERR069 - Expected variable assignment of initialiser section of for-loop", exprtk_error_location)); return error_node(); @@ -22083,7 +23036,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR066 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration", + "ERR070 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration", exprtk_error_location)); return error_node(); @@ -22108,14 +23061,14 @@ namespace exprtk nse.type = scope_element::e_variable; nse.depth = state_.scope_depth; nse.data = new T(T(0)); - nse.var_node = node_allocator_.allocate(*(T*)(nse.data)); + nse.var_node = node_allocator_.allocate(*reinterpret_cast(nse.data)); if (!sem_.add_element(nse)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR067 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM", + "ERR071 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM", exprtk_error_location)); sem_.free_element(nse); @@ -22137,7 +23090,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR068 - Failed to parse initialiser of for-loop", + "ERR072 - Failed to parse initialiser of for-loop", exprtk_error_location)); result = false; @@ -22147,7 +23100,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR069 - Expected ';' after initialiser of for-loop", + "ERR073 - Expected ';' after initialiser of for-loop", exprtk_error_location)); result = false; @@ -22161,7 +23114,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR070 - Failed to parse condition of for-loop", + "ERR074 - Failed to parse condition of for-loop", exprtk_error_location)); result = false; @@ -22171,7 +23124,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR071 - Expected ';' after condition section of for-loop", + "ERR075 - Expected ';' after condition section of for-loop", exprtk_error_location)); result = false; @@ -22185,7 +23138,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR072 - Failed to parse incrementor of for-loop", + "ERR076 - Failed to parse incrementor of for-loop", exprtk_error_location)); result = false; @@ -22195,7 +23148,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR073 - Expected ')' after incrementor section of for-loop", + "ERR077 - Expected ')' after incrementor section of for-loop", exprtk_error_location)); result = false; @@ -22206,12 +23159,14 @@ namespace exprtk { brkcnt_list_.push_front(false); + scoped_inc_dec sid(state_.parsing_loop_stmt_count); + if (0 == (loop_body = parse_multi_sequence("for-loop"))) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR074 - Failed to parse body of for-loop", + "ERR078 - Failed to parse body of for-loop", exprtk_error_location)); result = false; @@ -22226,9 +23181,9 @@ namespace exprtk } free_node(node_allocator_, initialiser); - free_node(node_allocator_, condition); + free_node(node_allocator_, condition ); free_node(node_allocator_, incrementor); - free_node(node_allocator_, loop_body); + free_node(node_allocator_, loop_body ); if (!brkcnt_list_.empty()) { @@ -22261,7 +23216,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR075 - Expected keyword 'switch'", + "ERR079 - Expected keyword 'switch'", exprtk_error_location)); return error_node(); @@ -22276,85 +23231,100 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR076 - Expected '{' for call to switch statement", + "ERR080 - Expected '{' for call to switch statement", exprtk_error_location)); return error_node(); } + expression_node_ptr default_statement = error_node(); + + scoped_expression_delete defstmt_delete((*this), default_statement); + for ( ; ; ) { - if (!details::imatch("case",current_token().value)) + if (details::imatch("case",current_token().value)) { - set_error( - make_error(parser_error::e_syntax, - current_token(), - "ERR077 - Expected either a 'case' or 'default' statement", - exprtk_error_location)); + next_token(); - return error_node(); - } + expression_node_ptr condition = parse_expression(); - next_token(); + if (0 == condition) + return error_node(); + else if (!token_is(token_t::e_colon)) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR081 - Expected ':' for case of switch statement", + exprtk_error_location)); - expression_node_ptr condition = parse_expression(); + free_node(node_allocator_, condition); - if (0 == condition) - return error_node(); - else if (!token_is(token_t::e_colon)) - { - set_error( - make_error(parser_error::e_syntax, - current_token(), - "ERR078 - Expected ':' for case of switch statement", - exprtk_error_location)); + return error_node(); + } - return error_node(); - } + expression_node_ptr consequent = parse_expression(); - expression_node_ptr consequent = parse_expression(); + if (0 == consequent) + { + free_node(node_allocator_, condition); - if (0 == consequent) - return error_node(); - else if (!token_is(token_t::e_eof)) - { - set_error( - make_error(parser_error::e_syntax, - current_token(), - "ERR079 - Expected ';' at end of case for switch statement", - exprtk_error_location)); + return error_node(); + } + else if (!token_is(token_t::e_eof)) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR082 - Expected ';' at end of case for switch statement", + exprtk_error_location)); - return error_node(); - } + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent); + + return error_node(); + } + + // Can we optimise away the case statement? + if (is_constant_node(condition) && is_false(condition)) + { + free_node(node_allocator_, condition ); + free_node(node_allocator_, consequent); + } + else + { + arg_list.push_back(condition ); + arg_list.push_back(consequent); + } - // Can we optimise away the case statement? - if (is_constant_node(condition) && is_false(condition)) - { - free_node(node_allocator_, condition); - free_node(node_allocator_, consequent); } - else + else if (details::imatch("default",current_token().value)) { - arg_list.push_back( condition); - arg_list.push_back(consequent); - } + if (0 != default_statement) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR083 - Multiple default cases for switch statement", + exprtk_error_location)); + + return error_node(); + } - if (details::imatch("default",current_token().value)) - { next_token(); + if (!token_is(token_t::e_colon)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR080 - Expected ':' for default of switch statement", + "ERR084 - Expected ':' for default of switch statement", exprtk_error_location)); return error_node(); } - expression_node_ptr default_statement = error_node(); - if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) default_statement = parse_multi_sequence("switch-default"); else @@ -22364,36 +23334,40 @@ namespace exprtk return error_node(); else if (!token_is(token_t::e_eof)) { - free_node(node_allocator_,default_statement); - set_error( make_error(parser_error::e_syntax, current_token(), - "ERR081 - Expected ';' at end of default for switch statement", + "ERR085 - Expected ';' at end of default for switch statement", exprtk_error_location)); return error_node(); } - - arg_list.push_back(default_statement); + } + else if (token_is(token_t::e_rcrlbracket)) break; + else + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR086 - Expected '}' at end of switch statement", + exprtk_error_location)); + + return error_node(); } } - if (!token_is(token_t::e_rcrlbracket)) - { - set_error( - make_error(parser_error::e_syntax, - current_token(), - "ERR082 - Expected '}' at end of switch statement", - exprtk_error_location)); + const bool default_statement_present = (0 != default_statement); - return error_node(); + if (default_statement_present) + { + arg_list.push_back(default_statement); } - result = expression_generator_.switch_statement(arg_list); + result = expression_generator_.switch_statement(arg_list, (0 != default_statement)); svd.delete_ptr = (0 == result); + defstmt_delete.delete_ptr = (0 == result); return result; } @@ -22407,7 +23381,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR083 - Expected token '[*]'", + "ERR087 - Expected token '[*]'", exprtk_error_location)); return error_node(); @@ -22422,7 +23396,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR084 - Expected '{' for call to [*] statement", + "ERR088 - Expected '{' for call to [*] statement", exprtk_error_location)); return error_node(); @@ -22435,7 +23409,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR085 - Expected a 'case' statement for multi-switch", + "ERR089 - Expected a 'case' statement for multi-switch", exprtk_error_location)); return error_node(); @@ -22453,7 +23427,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR086 - Expected ':' for case of [*] statement", + "ERR090 - Expected ':' for case of [*] statement", exprtk_error_location)); return error_node(); @@ -22469,7 +23443,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR087 - Expected ';' at end of case for [*] statement", + "ERR091 - Expected ';' at end of case for [*] statement", exprtk_error_location)); return error_node(); @@ -22478,12 +23452,12 @@ namespace exprtk // Can we optimise away the case statement? if (is_constant_node(condition) && is_false(condition)) { - free_node(node_allocator_, condition); + free_node(node_allocator_, condition ); free_node(node_allocator_, consequent); } else { - arg_list.push_back( condition); + arg_list.push_back(condition ); arg_list.push_back(consequent); } @@ -22498,7 +23472,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR088 - Expected '}' at end of [*] statement", + "ERR092 - Expected '}' at end of [*] statement", exprtk_error_location)); return error_node(); @@ -22539,7 +23513,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR089 - Unsupported vararg function: " + symbol, + "ERR093 - Unsupported vararg function: " + symbol, exprtk_error_location)); return error_node(); @@ -22556,7 +23530,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR090 - Expected '(' for call to vararg function: " + symbol, + "ERR094 - Expected '(' for call to vararg function: " + symbol, exprtk_error_location)); return error_node(); @@ -22578,7 +23552,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR091 - Expected ',' for call to vararg function: " + symbol, + "ERR095 - Expected ',' for call to vararg function: " + symbol, exprtk_error_location)); return error_node(); @@ -22599,7 +23573,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR092 - Expected '[' as start of string range definition", + "ERR096 - Expected '[' as start of string range definition", exprtk_error_location)); free_node(node_allocator_,expression); @@ -22627,10 +23601,11 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR093 - Failed to generate string range node", + "ERR097 - Failed to generate string range node", exprtk_error_location)); free_node(node_allocator_,expression); + rp.free(); } rp.clear(); @@ -22763,7 +23738,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR094 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" + + "ERR098 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" + ((!source.empty()) ? std::string(" section of " + source): ""), exprtk_error_location)); @@ -22810,7 +23785,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR095 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source, + "ERR099 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source, exprtk_error_location)); return error_node(); @@ -22844,7 +23819,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR096 - Expected '[' for start of range", + "ERR100 - Expected '[' for start of range", exprtk_error_location)); return false; @@ -22865,7 +23840,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR097 - Failed parse begin section of range", + "ERR101 - Failed parse begin section of range", exprtk_error_location)); return false; @@ -22888,7 +23863,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR098 - Range lower bound less than zero! Constraint: r0 >= 0", + "ERR102 - Range lower bound less than zero! Constraint: r0 >= 0", exprtk_error_location)); return false; @@ -22905,7 +23880,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR099 - Expected ':' for break in range", + "ERR103 - Expected ':' for break in range", exprtk_error_location)); rp.free(); @@ -22928,7 +23903,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR100 - Failed parse end section of range", + "ERR104 - Failed parse end section of range", exprtk_error_location)); rp.free(); @@ -22953,9 +23928,11 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR101 - Range upper bound less than zero! Constraint: r1 >= 0", + "ERR105 - Range upper bound less than zero! Constraint: r1 >= 0", exprtk_error_location)); + rp.free(); + return false; } } @@ -22970,7 +23947,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR102 - Expected ']' for start of range", + "ERR106 - Expected ']' for start of range", exprtk_error_location)); rp.free(); @@ -22984,14 +23961,21 @@ namespace exprtk std::size_t r0 = 0; std::size_t r1 = 0; - const bool rp_result = rp(r0,r1); + bool rp_result = false; + + try + { + rp_result = rp(r0, r1); + } + catch (std::runtime_error&) + {} if (!rp_result || (r0 > r1)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR103 - Invalid range, Constraint: r0 <= r1", + "ERR107 - Invalid range, Constraint: r0 <= r1", exprtk_error_location)); return false; @@ -23032,7 +24016,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR104 - Unknown string symbol", + "ERR108 - Unknown string symbol", exprtk_error_location)); return error_node(); @@ -23126,6 +24110,7 @@ namespace exprtk if (!parse_range(rp)) { free_node(node_allocator_,result); + rp.free(); return error_node(); } @@ -23146,11 +24131,13 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR105 - Overflow in range for string: '" + const_str + "'[" + + "ERR109 - Overflow in range for string: '" + const_str + "'[" + (rp.n0_c.first ? details::to_str(static_cast(rp.n0_c.second)) : "?") + ":" + (rp.n1_c.first ? details::to_str(static_cast(rp.n1_c.second)) : "?") + "]", exprtk_error_location)); + rp.free(); + return error_node(); } @@ -23190,7 +24177,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR106 - Symbol '" + symbol+ " not a vector", + "ERR110 - Symbol '" + symbol+ " not a vector", exprtk_error_location)); return error_node(); @@ -23216,7 +24203,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR107 - Failed to parse index for vector: '" + symbol + "'", + "ERR111 - Failed to parse index for vector: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -23226,7 +24213,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR108 - Expected ']' for index of vector: '" + symbol + "'", + "ERR112 - Expected ']' for index of vector: '" + symbol + "'", exprtk_error_location)); free_node(node_allocator_,index_expr); @@ -23245,7 +24232,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR109 - Index of " + details::to_str(index) + " out of range for " + "ERR113 - Index of " + details::to_str(index) + " out of range for " "vector '" + symbol + "' of size " + details::to_str(vec_size), exprtk_error_location)); @@ -23255,7 +24242,7 @@ namespace exprtk } } - return expression_generator_.vector_element(symbol,vec,index_expr); + return expression_generator_.vector_element(symbol, vec, index_expr); } inline expression_node_ptr parse_vararg_function_call(ivararg_function* vararg_function, const std::string& vararg_function_name) @@ -23277,7 +24264,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR110 - Zero parameter call to vararg function: " + "ERR114 - Zero parameter call to vararg function: " + vararg_function_name + " not allowed", exprtk_error_location)); @@ -23302,7 +24289,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR111 - Expected ',' for call to vararg function: " + "ERR115 - Expected ',' for call to vararg function: " + vararg_function_name, exprtk_error_location)); @@ -23316,7 +24303,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR112 - Zero parameter call to vararg function: " + "ERR116 - Zero parameter call to vararg function: " + vararg_function_name + " not allowed", exprtk_error_location)); @@ -23328,7 +24315,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR113 - Invalid number of parameters to call to vararg function: " + "ERR117 - Invalid number of parameters to call to vararg function: " + vararg_function_name + ", require at least " + details::to_str(static_cast(vararg_function->min_num_args())) + " parameters", exprtk_error_location)); @@ -23340,7 +24327,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR114 - Invalid number of parameters to call to vararg function: " + "ERR118 - Invalid number of parameters to call to vararg function: " + vararg_function_name + ", require no more than " + details::to_str(static_cast(vararg_function->max_num_args())) + " parameters", exprtk_error_location)); @@ -23387,11 +24374,6 @@ namespace exprtk parse_function_prototypes(func_prototypes); } - void set_default_return_type(const std::string& return_type) - { - default_return_type_ = return_type; - } - bool verify(const std::string& param_seq, std::size_t& pseq_index) { if (function_definition_list_.empty()) @@ -23423,7 +24405,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR115 - Failed parameter type check for function '" + function_name_ + "', " + "ERR119 - Failed parameter type check for function '" + function_name_ + "', " "Expected '" + function_definition_list_[0].param_seq + "' call set: '" + param_seq + "'", exprtk_error_location)); @@ -23445,7 +24427,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR116 - Failed parameter type check for function '" + function_name_ + "', " + "ERR120 - Failed parameter type check for function '" + function_name_ + "', " "Best match: '" + function_definition_list_[max_diff_index].param_seq + "' call set: '" + param_seq + "'", exprtk_error_location)); @@ -23587,7 +24569,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR117 - Invalid parameter sequence of '" + param_seq_list[i] + + "ERR121 - Invalid parameter sequence of '" + param_seq_list[i] + "' for function: " + function_name_, exprtk_error_location)); return; @@ -23603,7 +24585,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, parser_.current_token(), - "ERR118 - Function '" + function_name_ + "' has a parameter sequence conflict between " + + "ERR122 - Function '" + function_name_ + "' has a parameter sequence conflict between " + "pseq_idx[" + details::to_str(seq_itr->second) + "] and" + "pseq_idx[" + details::to_str(i) + "] " + "param seq: " + param_seq_list[i], @@ -23642,7 +24624,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR119 - Type checker instantiation failure for generic function: " + function_name, + "ERR123 - Type checker instantiation failure for generic function: " + function_name, exprtk_error_location)); return error_node(); @@ -23660,7 +24642,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR120 - Zero parameter call to generic function: " + "ERR124 - Zero parameter call to generic function: " + function_name + " not allowed", exprtk_error_location)); @@ -23692,7 +24674,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR121 - Expected ',' for call to generic function: " + function_name, + "ERR125 - Expected ',' for call to generic function: " + function_name, exprtk_error_location)); return error_node(); @@ -23709,7 +24691,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR122 - Zero parameter call to generic function: " + "ERR126 - Zero parameter call to generic function: " + function_name + " not allowed", exprtk_error_location)); @@ -23726,7 +24708,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR123 - Invalid input parameter sequence for call to generic function: " + function_name, + "ERR127 - Invalid input parameter sequence for call to generic function: " + function_name, exprtk_error_location)); return error_node(); @@ -23748,7 +24730,7 @@ namespace exprtk inline bool parse_igeneric_function_params(std::string& param_type_list, std::vector& arg_list, - const std::string function_name, + const std::string& function_name, igeneric_function* function, const type_checker& tc) { @@ -23764,7 +24746,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR124 - Zero parameter call to generic function: " + "ERR128 - Zero parameter call to generic function: " + function_name + " not allowed", exprtk_error_location)); @@ -23796,7 +24778,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR125 - Expected ',' for call to string function: " + function_name, + "ERR129 - Expected ',' for call to string function: " + function_name, exprtk_error_location)); return false; @@ -23843,7 +24825,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR126 - Invalid input parameter sequence for call to string function: " + function_name, + "ERR130 - Invalid input parameter sequence for call to string function: " + function_name, exprtk_error_location)); return error_node(); @@ -23895,7 +24877,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR127 - Invalid input parameter sequence for call to overloaded function: " + function_name, + "ERR131 - Invalid input parameter sequence for call to overloaded function: " + function_name, exprtk_error_location)); return error_node(); @@ -23926,7 +24908,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR128 - Invalid return type for call to overloaded function: " + function_name, + "ERR132 - Invalid return type for call to overloaded function: " + function_name, exprtk_error_location)); } @@ -23938,12 +24920,12 @@ namespace exprtk template struct parse_special_function_impl { - static inline expression_node_ptr process(parser& p,const details::operator_type opt_type, const std::string& sf_name) + static inline expression_node_ptr process(parser& p, const details::operator_type opt_type, const std::string& sf_name) { expression_node_ptr branch[NumberOfParameters]; - expression_node_ptr result = error_node(); + expression_node_ptr result = error_node(); - std::fill_n(branch,NumberOfParameters,reinterpret_cast(0)); + std::fill_n(branch, NumberOfParameters, reinterpret_cast(0)); scoped_delete sd(p,branch); @@ -23954,7 +24936,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR129 - Expected '(' for special function '" + sf_name + "'", + "ERR133 - Expected '(' for special function '" + sf_name + "'", exprtk_error_location)); return error_node(); @@ -23975,7 +24957,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR130 - Expected ',' before next parameter of special function '" + sf_name + "'", + "ERR134 - Expected ',' before next parameter of special function '" + sf_name + "'", exprtk_error_location)); return p.error_node(); @@ -23988,7 +24970,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR131 - Invalid number of parameters for special function '" + sf_name + "'", + "ERR135 - Invalid number of parameters for special function '" + sf_name + "'", exprtk_error_location)); return p.error_node(); @@ -24015,7 +24997,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token(), - "ERR132 - Invalid special function[1]: " + sf_name, + "ERR136 - Invalid special function[1]: " + sf_name, exprtk_error_location)); return error_node(); @@ -24029,7 +25011,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token(), - "ERR133 - Invalid special function[2]: " + sf_name, + "ERR137 - Invalid special function[2]: " + sf_name, exprtk_error_location)); return error_node(); @@ -24061,7 +25043,17 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR134 - Break call within a break call is not allowed", + "ERR138 - Invoking 'break' within a break call is not allowed", + exprtk_error_location)); + + return error_node(); + } + else if (0 == state_.parsing_loop_stmt_count) + { + set_error( + make_error(parser_error::e_syntax, + current_token(), + "ERR139 - Invalid use of 'break', allowed only in the scope of a loop", exprtk_error_location)); return error_node(); @@ -24084,7 +25076,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR135 - Failed to parse return expression for 'break' statement", + "ERR140 - Failed to parse return expression for 'break' statement", exprtk_error_location)); return error_node(); @@ -24094,7 +25086,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR136 - Expected ']' at the completion of break's return expression", + "ERR141 - Expected ']' at the completion of break's return expression", exprtk_error_location)); free_node(node_allocator_,return_expr); @@ -24112,7 +25104,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR137 - Invalid use of 'break', allowed only in the scope of a loop", + "ERR142 - Invalid use of 'break', allowed only in the scope of a loop", exprtk_error_location)); } @@ -24121,25 +25113,25 @@ namespace exprtk inline expression_node_ptr parse_continue_statement() { - if (!brkcnt_list_.empty()) - { - next_token(); - - brkcnt_list_.front() = true; - state_.activate_side_effect("parse_continue_statement()"); - - return node_allocator_.allocate >(); - } - else + if (0 == state_.parsing_loop_stmt_count) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR138 - Invalid use of 'continue', allowed only in the scope of a loop", + "ERR143 - Invalid use of 'continue', allowed only in the scope of a loop", exprtk_error_location)); return error_node(); } + else + { + next_token(); + + brkcnt_list_.front() = true; + state_.activate_side_effect("parse_continue_statement()"); + + return node_allocator_.allocate >(); + } } #endif @@ -24152,7 +25144,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR139 - Expected '[' as part of vector size definition", + "ERR144 - Expected '[' as part of vector size definition", exprtk_error_location)); return error_node(); @@ -24162,7 +25154,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR140 - Failed to determine size of vector '" + vec_name + "'", + "ERR145 - Failed to determine size of vector '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -24174,13 +25166,13 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR141 - Expected a literal number as size of vector '" + vec_name + "'", + "ERR146 - Expected a literal number as size of vector '" + vec_name + "'", exprtk_error_location)); return error_node(); } - T vector_size = size_expr->value(); + const T vector_size = size_expr->value(); free_node(node_allocator_,size_expr); @@ -24196,7 +25188,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR142 - Invalid vector size. Must be an integer in the range [0,2e9], size: " + + "ERR147 - Invalid vector size. Must be an integer in the range [0,2e9], size: " + details::to_str(details::numeric::to_int32(vector_size)), exprtk_error_location)); @@ -24216,7 +25208,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR143 - Expected ']' as part of vector size definition", + "ERR148 - Expected ']' as part of vector size definition", exprtk_error_location)); return error_node(); @@ -24228,7 +25220,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR144 - Expected ':=' as part of vector definition", + "ERR149 - Expected ':=' as part of vector definition", exprtk_error_location)); return error_node(); @@ -24242,7 +25234,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR145 - Failed to parse single vector initialiser", + "ERR150 - Failed to parse single vector initialiser", exprtk_error_location)); return error_node(); @@ -24255,7 +25247,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR146 - Expected ']' to close single value vector initialiser", + "ERR151 - Expected ']' to close single value vector initialiser", exprtk_error_location)); return error_node(); @@ -24271,7 +25263,7 @@ namespace exprtk if (token_t::e_symbol == current_token().type) { // Is it a locally defined vector? - scope_element& se = sem_.get_active_element(current_token().value); + const scope_element& se = sem_.get_active_element(current_token().value); if (scope_element::e_vector == se.type) { @@ -24302,7 +25294,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR147 - Expected '{' as part of vector initialiser list", + "ERR152 - Expected '{' as part of vector initialiser list", exprtk_error_location)); return error_node(); @@ -24322,7 +25314,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR148 - Expected '{' as part of vector initialiser list", + "ERR153 - Expected '{' as part of vector initialiser list", exprtk_error_location)); return error_node(); @@ -24340,7 +25332,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR149 - Expected ',' between vector initialisers", + "ERR154 - Expected ',' between vector initialisers", exprtk_error_location)); return error_node(); @@ -24362,7 +25354,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR150 - Expected ';' at end of vector definition", + "ERR155 - Expected ';' at end of vector definition", exprtk_error_location)); return error_node(); @@ -24374,7 +25366,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR151 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'", + "ERR156 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -24394,7 +25386,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR152 - Illegal redefinition of local vector: '" + vec_name + "'", + "ERR157 - Illegal redefinition of local vector: '" + vec_name + "'", exprtk_error_location)); return error_node(); @@ -24421,14 +25413,14 @@ namespace exprtk nse.depth = state_.scope_depth; nse.size = vec_size; nse.data = new T[vec_size]; - nse.vec_node = new typename scope_element::vector_holder_t((T*)(nse.data),nse.size); + nse.vec_node = new typename scope_element::vector_holder_t(reinterpret_cast(nse.data),nse.size); if (!sem_.add_element(nse)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR153 - Failed to add new local vector '" + vec_name + "' to SEM", + "ERR158 - Failed to add new local vector '" + vec_name + "' to SEM", exprtk_error_location)); sem_.free_element(nse); @@ -24487,7 +25479,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR154 - Illegal redefinition of local variable: '" + str_name + "'", + "ERR159 - Illegal redefinition of local variable: '" + str_name + "'", exprtk_error_location)); free_node(node_allocator_,initialisation_expression); @@ -24512,14 +25504,14 @@ namespace exprtk nse.type = scope_element::e_string; nse.depth = state_.scope_depth; nse.data = new std::string; - nse.str_node = new stringvar_node_t(*(std::string*)(nse.data)); + nse.str_node = new stringvar_node_t(*reinterpret_cast(nse.data)); if (!sem_.add_element(nse)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR155 - Failed to add new local string variable '" + str_name + "' to SEM", + "ERR160 - Failed to add new local string variable '" + str_name + "' to SEM", exprtk_error_location)); free_node(node_allocator_,initialisation_expression); @@ -24565,7 +25557,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR156 - Illegal variable definition", + "ERR161 - Illegal variable definition", exprtk_error_location)); return error_node(); @@ -24586,7 +25578,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR157 - Expected a symbol for variable definition", + "ERR162 - Expected a symbol for variable definition", exprtk_error_location)); return error_node(); @@ -24596,7 +25588,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR158 - Illegal redefinition of reserved keyword: '" + var_name + "'", + "ERR163 - Illegal redefinition of reserved keyword: '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24606,7 +25598,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR159 - Illegal redefinition of variable '" + var_name + "'", + "ERR164 - Illegal redefinition of variable '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24616,7 +25608,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR160 - Illegal redefinition of local variable: '" + var_name + "'", + "ERR165 - Illegal redefinition of local variable: '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24636,7 +25628,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR161 - Failed to parse initialisation expression", + "ERR166 - Failed to parse initialisation expression", exprtk_error_location)); return error_node(); @@ -24654,7 +25646,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR162 - Expected ';' after variable definition", + "ERR167 - Expected ';' after variable definition", exprtk_error_location)); free_node(node_allocator_,initialisation_expression); @@ -24682,7 +25674,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR163 - Illegal redefinition of local variable: '" + var_name + "'", + "ERR168 - Illegal redefinition of local variable: '" + var_name + "'", exprtk_error_location)); free_node(node_allocator_, initialisation_expression); @@ -24707,14 +25699,14 @@ namespace exprtk nse.type = scope_element::e_variable; nse.depth = state_.scope_depth; nse.data = new T(T(0)); - nse.var_node = node_allocator_.allocate(*(T*)(nse.data)); + nse.var_node = node_allocator_.allocate(*reinterpret_cast(nse.data)); if (!sem_.add_element(nse)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR164 - Failed to add new local variable '" + var_name + "' to SEM", + "ERR169 - Failed to add new local variable '" + var_name + "' to SEM", exprtk_error_location)); free_node(node_allocator_, initialisation_expression); @@ -24751,7 +25743,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR165 - Expected a '{}' for uninitialised var definition", + "ERR170 - Expected a '{}' for uninitialised var definition", exprtk_error_location)); return error_node(); @@ -24761,7 +25753,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR166 - Expected ';' after uninitialised variable definition", + "ERR171 - Expected ';' after uninitialised variable definition", exprtk_error_location)); return error_node(); @@ -24778,7 +25770,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR167 - Illegal redefinition of local variable: '" + var_name + "'", + "ERR172 - Illegal redefinition of local variable: '" + var_name + "'", exprtk_error_location)); return error_node(); @@ -24801,14 +25793,14 @@ namespace exprtk nse.depth = state_.scope_depth; nse.ip_index = sem_.next_ip_index(); nse.data = new T(T(0)); - nse.var_node = node_allocator_.allocate(*(T*)(nse.data)); + nse.var_node = node_allocator_.allocate(*reinterpret_cast(nse.data)); if (!sem_.add_element(nse)) { set_error( make_error(parser_error::e_syntax, current_token(), - "ERR168 - Failed to add new local variable '" + var_name + "' to SEM", + "ERR173 - Failed to add new local variable '" + var_name + "' to SEM", exprtk_error_location)); sem_.free_element(nse); @@ -24841,7 +25833,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR169 - Expected '(' at start of swap statement", + "ERR174 - Expected '(' at start of swap statement", exprtk_error_location)); return error_node(); @@ -24860,7 +25852,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR170 - Expected a symbol for variable or vector element definition", + "ERR175 - Expected a symbol for variable or vector element definition", exprtk_error_location)); return error_node(); @@ -24872,7 +25864,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR171 - First parameter to swap is an invalid vector element: '" + var0_name + "'", + "ERR176 - First parameter to swap is an invalid vector element: '" + var0_name + "'", exprtk_error_location)); return error_node(); @@ -24887,7 +25879,7 @@ namespace exprtk variable0 = symtab_store_.get_variable(var0_name); } - scope_element& se = sem_.get_element(var0_name); + const scope_element& se = sem_.get_element(var0_name); if ( (se.active) && @@ -24905,7 +25897,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR172 - First parameter to swap is an invalid variable: '" + var0_name + "'", + "ERR177 - First parameter to swap is an invalid variable: '" + var0_name + "'", exprtk_error_location)); return error_node(); @@ -24919,7 +25911,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR173 - Expected ',' between parameters to swap", + "ERR178 - Expected ',' between parameters to swap", exprtk_error_location)); if (variable0_generated) @@ -24937,7 +25929,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR174 - Expected a symbol for variable or vector element definition", + "ERR179 - Expected a symbol for variable or vector element definition", exprtk_error_location)); if (variable0_generated) @@ -24954,7 +25946,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR175 - Second parameter to swap is an invalid vector element: '" + var1_name + "'", + "ERR180 - Second parameter to swap is an invalid vector element: '" + var1_name + "'", exprtk_error_location)); if (variable0_generated) @@ -24974,7 +25966,7 @@ namespace exprtk variable1 = symtab_store_.get_variable(var1_name); } - scope_element& se = sem_.get_element(var1_name); + const scope_element& se = sem_.get_element(var1_name); if ( (se.active) && @@ -24992,7 +25984,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR176 - Second parameter to swap is an invalid variable: '" + var1_name + "'", + "ERR181 - Second parameter to swap is an invalid variable: '" + var1_name + "'", exprtk_error_location)); if (variable0_generated) @@ -25011,7 +26003,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR177 - Expected ')' at end of swap statement", + "ERR182 - Expected ')' at end of swap statement", exprtk_error_location)); if (variable0_generated) @@ -25068,7 +26060,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR178 - Return call within a return call is not allowed", + "ERR183 - Return call within a return call is not allowed", exprtk_error_location)); return error_node(); @@ -25092,7 +26084,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR179 - Expected '[' at start of return statement", + "ERR184 - Expected '[' at start of return statement", exprtk_error_location)); return error_node(); @@ -25115,7 +26107,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR180 - Expected ',' between values during call to return", + "ERR185 - Expected ',' between values during call to return", exprtk_error_location)); return error_node(); @@ -25127,7 +26119,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR181 - Zero parameter return statement not allowed", + "ERR186 - Zero parameter return statement not allowed", exprtk_error_location)); return error_node(); @@ -25142,7 +26134,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, prev_token, - "ERR182 - Invalid ']' found during return call", + "ERR187 - Invalid ']' found during return call", exprtk_error_location)); return error_node(); @@ -25195,7 +26187,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR183 - Invalid sequence of variable '"+ symbol + "' and bracket", + "ERR188 - Invalid sequence of variable '"+ symbol + "' and bracket", exprtk_error_location)); return false; @@ -25243,7 +26235,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR184 - Invalid sequence of brackets", + "ERR189 - Invalid sequence of brackets", exprtk_error_location)); return false; @@ -25340,7 +26332,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR185 - Failed to generate node for function: '" + symbol + "'", + "ERR190 - Failed to generate node for function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25366,7 +26358,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR186 - Failed to generate node for vararg function: '" + symbol + "'", + "ERR191 - Failed to generate node for vararg function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25392,7 +26384,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR187 - Failed to generate node for generic function: '" + symbol + "'", + "ERR192 - Failed to generate node for generic function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25419,7 +26411,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR188 - Failed to generate node for string function: '" + symbol + "'", + "ERR193 - Failed to generate node for string function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25445,7 +26437,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR189 - Failed to generate node for overload function: '" + symbol + "'", + "ERR194 - Failed to generate node for overload function: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25471,7 +26463,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR190 - Invalid use of reserved symbol '" + symbol + "'", + "ERR195 - Invalid use of reserved symbol '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25534,7 +26526,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token(), - "ERR191 - Failed to create variable: '" + symbol + "'" + + "ERR196 - Failed to create variable: '" + symbol + "'" + (error_message.empty() ? "" : " - " + error_message), exprtk_error_location)); @@ -25554,7 +26546,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token(), - "ERR192 - Failed to resolve symbol: '" + symbol + "'" + + "ERR197 - Failed to resolve symbol: '" + symbol + "'" + (error_message.empty() ? "" : " - " + error_message), exprtk_error_location)); } @@ -25566,7 +26558,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR193 - Undefined symbol: '" + symbol + "'", + "ERR198 - Undefined symbol: '" + symbol + "'", exprtk_error_location)); return error_node(); @@ -25585,11 +26577,16 @@ namespace exprtk static const std::string symbol_var = "var" ; static const std::string symbol_swap = "swap" ; static const std::string symbol_return = "return" ; + static const std::string symbol_not = "not" ; if (valid_vararg_operation(current_token().value)) { return parse_vararg_function(); } + else if (details::imatch(current_token().value, symbol_not)) + { + return parse_not_statement(); + } else if (valid_base_operation(current_token().value)) { return parse_base_operation(); @@ -25673,7 +26670,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token(), - "ERR194 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value, + "ERR199 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value, exprtk_error_location)); return error_node(); @@ -25682,6 +26679,13 @@ namespace exprtk inline expression_node_ptr parse_branch(precedence_level precedence = e_level00) { + stack_limit_handler slh(*this); + + if (!slh) + { + return error_node(); + } + expression_node_ptr branch = error_node(); if (token_t::e_number == current_token().type) @@ -25697,7 +26701,7 @@ namespace exprtk set_error( make_error(parser_error::e_numeric, current_token(), - "ERR195 - Failed generate node for scalar: '" + current_token().value + "'", + "ERR200 - Failed generate node for scalar: '" + current_token().value + "'", exprtk_error_location)); return error_node(); @@ -25711,7 +26715,7 @@ namespace exprtk set_error( make_error(parser_error::e_numeric, current_token(), - "ERR196 - Failed to convert '" + current_token().value + "' to a number", + "ERR201 - Failed to convert '" + current_token().value + "' to a number", exprtk_error_location)); return error_node(); @@ -25738,7 +26742,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR197 - Expected ')' instead of: '" + current_token().value + "'", + "ERR202 - Expected ')' instead of: '" + current_token().value + "'", exprtk_error_location)); free_node(node_allocator_,branch); @@ -25763,7 +26767,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR198 - Expected ']' instead of: '" + current_token().value + "'", + "ERR203 - Expected ']' instead of: '" + current_token().value + "'", exprtk_error_location)); free_node(node_allocator_,branch); @@ -25788,7 +26792,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR199 - Expected '}' instead of: '" + current_token().value + "'", + "ERR204 - Expected '}' instead of: '" + current_token().value + "'", exprtk_error_location)); free_node(node_allocator_,branch); @@ -25815,7 +26819,16 @@ namespace exprtk ) ) { - branch = expression_generator_(details::e_neg,branch); + expression_node_ptr result = expression_generator_(details::e_neg,branch); + + if (0 == result) + { + free_node(node_allocator_,branch); + + return error_node(); + } + else + branch = result; } } else if (token_t::e_add == current_token().type) @@ -25828,7 +26841,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR200 - Premature end of expression[1]", + "ERR205 - Premature end of expression[1]", exprtk_error_location)); return error_node(); @@ -25838,7 +26851,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token(), - "ERR201 - Premature end of expression[2]", + "ERR206 - Premature end of expression[2]", exprtk_error_location)); return error_node(); @@ -26741,8 +27754,8 @@ namespace exprtk { if ((0 == condition) || (0 == consequent)) { - free_node(*node_allocator_, condition); - free_node(*node_allocator_, consequent); + free_node(*node_allocator_, condition ); + free_node(*node_allocator_, consequent ); free_node(*node_allocator_, alternative); return error_node(); @@ -26753,7 +27766,7 @@ namespace exprtk // True branch if (details::is_true(condition)) { - free_node(*node_allocator_, condition); + free_node(*node_allocator_, condition ); free_node(*node_allocator_, alternative); return consequent; @@ -26761,7 +27774,7 @@ namespace exprtk // False branch else { - free_node(*node_allocator_, condition); + free_node(*node_allocator_, condition ); free_node(*node_allocator_, consequent); if (alternative) @@ -26787,8 +27800,8 @@ namespace exprtk { if ((0 == condition) || (0 == consequent)) { - free_node(*node_allocator_, condition); - free_node(*node_allocator_, consequent); + free_node(*node_allocator_, condition ); + free_node(*node_allocator_, consequent ); free_node(*node_allocator_, alternative); return error_node(); @@ -26799,7 +27812,7 @@ namespace exprtk // True branch if (details::is_true(condition)) { - free_node(*node_allocator_, condition); + free_node(*node_allocator_, condition ); free_node(*node_allocator_, alternative); return consequent; @@ -26807,7 +27820,7 @@ namespace exprtk // False branch else { - free_node(*node_allocator_, condition); + free_node(*node_allocator_, condition ); free_node(*node_allocator_, consequent); if (alternative) @@ -26832,6 +27845,19 @@ namespace exprtk } #endif + inline loop_runtime_check_ptr get_loop_runtime_check(const loop_runtime_check::loop_types loop_type) const + { + if ( + parser_->loop_runtime_check_ && + (loop_type == (parser_->loop_runtime_check_->loop_set & loop_type)) + ) + { + return parser_->loop_runtime_check_; + } + + return loop_runtime_check_ptr(0); + } + inline expression_node_ptr while_loop(expression_node_ptr& condition, expression_node_ptr& branch, const bool brkcont = false) const @@ -26846,7 +27872,7 @@ namespace exprtk result = node_allocator_->allocate >(); free_node(*node_allocator_, condition); - free_node(*node_allocator_, branch); + free_node(*node_allocator_, branch ); return result; } @@ -26857,10 +27883,20 @@ namespace exprtk return branch; } else if (!brkcont) - return node_allocator_->allocate(condition,branch); + return node_allocator_->allocate + ( + condition, + branch, + get_loop_runtime_check(loop_runtime_check::e_while_loop) + ); #ifndef exprtk_disable_break_continue else - return node_allocator_->allocate(condition,branch); + return node_allocator_->allocate + ( + condition, + branch, + get_loop_runtime_check(loop_runtime_check::e_while_loop) + ); #else return error_node(); #endif @@ -26883,7 +27919,7 @@ namespace exprtk } free_node(*node_allocator_, condition); - free_node(*node_allocator_, branch); + free_node(*node_allocator_, branch ); return error_node(); } @@ -26894,10 +27930,20 @@ namespace exprtk return branch; } else if (!brkcont) - return node_allocator_->allocate(condition,branch); + return node_allocator_->allocate + ( + condition, + branch, + get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop) + ); #ifndef exprtk_disable_break_continue else - return node_allocator_->allocate(condition,branch); + return node_allocator_->allocate + ( + condition, + branch, + get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop) + ); #else return error_node(); #endif @@ -26920,16 +27966,16 @@ namespace exprtk result = node_allocator_->allocate >(); free_node(*node_allocator_, initialiser); - free_node(*node_allocator_, condition); + free_node(*node_allocator_, condition ); free_node(*node_allocator_, incrementor); - free_node(*node_allocator_, loop_body); + free_node(*node_allocator_, loop_body ); return result; } else if (details::is_null_node(condition) || (0 == condition)) { free_node(*node_allocator_, initialiser); - free_node(*node_allocator_, condition); + free_node(*node_allocator_, condition ); free_node(*node_allocator_, incrementor); return loop_body; @@ -26940,7 +27986,8 @@ namespace exprtk initialiser, condition, incrementor, - loop_body + loop_body, + get_loop_runtime_check(loop_runtime_check::e_for_loop) ); #ifndef exprtk_disable_break_continue @@ -26950,7 +27997,8 @@ namespace exprtk initialiser, condition, incrementor, - loop_body + loop_body, + get_loop_runtime_check(loop_runtime_check::e_for_loop) ); #else return error_node(); @@ -27031,10 +28079,10 @@ namespace exprtk struct switch_nodes { - typedef std::vector arg_list_t; + typedef std::vector > arg_list_t; - #define case_stmt(N) \ - if (is_true(arg[(2 * N)])) { return arg[(2 * N) + 1]->value(); } \ + #define case_stmt(N) \ + if (is_true(arg[(2 * N)].first)) { return arg[(2 * N) + 1].first->value(); } \ struct switch_1 { @@ -27042,7 +28090,7 @@ namespace exprtk { case_stmt(0) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27052,7 +28100,7 @@ namespace exprtk { case_stmt(0) case_stmt(1) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27063,7 +28111,7 @@ namespace exprtk case_stmt(0) case_stmt(1) case_stmt(2) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27074,7 +28122,7 @@ namespace exprtk case_stmt(0) case_stmt(1) case_stmt(2) case_stmt(3) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27086,7 +28134,7 @@ namespace exprtk case_stmt(2) case_stmt(3) case_stmt(4) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27098,7 +28146,7 @@ namespace exprtk case_stmt(2) case_stmt(3) case_stmt(4) case_stmt(5) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27111,7 +28159,7 @@ namespace exprtk case_stmt(4) case_stmt(5) case_stmt(6) - return arg.back()->value(); + return arg.back().first->value(); } }; @@ -27120,14 +28168,13 @@ namespace exprtk template class Sequence> - inline expression_node_ptr switch_statement(Sequence& arg_list) + inline expression_node_ptr switch_statement(Sequence& arg_list, const bool default_statement_present) { if (arg_list.empty()) return error_node(); else if ( - !all_nodes_valid(arg_list) || - (arg_list.size() < 3) || - ((arg_list.size() % 2) != 1) + !all_nodes_valid(arg_list) || + (!default_statement_present && (arg_list.size() < 2)) ) { details::free_all_nodes(*node_allocator_,arg_list); @@ -27624,24 +28671,31 @@ namespace exprtk if (details::is_constant_node(result)) return result; else if (!all_nodes_valid(b)) + { + details::free_node(*node_allocator_,result); + std::fill_n(b, N, reinterpret_cast(0)); + return error_node(); + } else if (N != f->param_count) { - details::free_all_nodes(*node_allocator_,b); + details::free_node(*node_allocator_,result); + std::fill_n(b, N, reinterpret_cast(0)); return error_node(); } - function_N_node_t* func_node_ptr = static_cast(result); + function_N_node_t* func_node_ptr = reinterpret_cast(result); - if (func_node_ptr->init_branches(b)) - return result; - else + if (!func_node_ptr->init_branches(b)) { - details::free_all_nodes(*node_allocator_,b); + details::free_node(*node_allocator_,result); + std::fill_n(b, N, reinterpret_cast(0)); return error_node(); } + + return result; } } @@ -27865,7 +28919,7 @@ namespace exprtk return node_allocator_->allocate(i,vector_base); } - scope_element& se = parser_->sem_.get_element(symbol,i); + const scope_element& se = parser_->sem_.get_element(symbol,i); if (se.index == i) { @@ -28717,8 +29771,9 @@ namespace exprtk { expression_node_ptr result = error_node(); - const bool synthesis_result = synthesize_sf4ext_expression::template compile_right - (expr_gen, v, operation, branch[1], result); + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_right + (expr_gen, v, operation, branch[1], result); if (synthesis_result) { @@ -28791,8 +29846,9 @@ namespace exprtk { expression_node_ptr result = error_node(); - const bool synthesis_result = synthesize_sf4ext_expression::template compile_left - (expr_gen, v, operation, branch[0], result); + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_left + (expr_gen, v, operation, branch[0], result); if (synthesis_result) { @@ -28973,7 +30029,11 @@ namespace exprtk { expression_node_ptr result = error_node(); - if (synthesize_sf4ext_expression::template compile_right(expr_gen,c,operation,branch[1],result)) + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_right + (expr_gen, c, operation, branch[1], result); + + if (synthesis_result) { free_node(*expr_gen.node_allocator_,branch[1]); @@ -29088,8 +30148,9 @@ namespace exprtk { expression_node_ptr result = error_node(); - const bool synthesis_result = synthesize_sf4ext_expression::template compile_left - (expr_gen, c, operation, branch[0], result); + const bool synthesis_result = + synthesize_sf4ext_expression::template compile_left + (expr_gen, c, operation, branch[0], result); if (synthesis_result) { @@ -34565,8 +35626,8 @@ namespace exprtk { const std::string s0 = static_cast*>(branch[0])->str (); std::string& s1 = static_cast*> (branch[1])->ref (); - range_t rp0 = static_cast*>(branch[0])->range(); - range_t rp1 = static_cast*> (branch[1])->range(); + const range_t rp0 = static_cast*>(branch[0])->range(); + const range_t rp1 = static_cast*> (branch[1])->range(); static_cast*>(branch[0])->range_ref().clear(); static_cast*> (branch[1])->range_ref().clear(); @@ -34579,9 +35640,9 @@ namespace exprtk inline expression_node_ptr synthesize_csrocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) { - std::string s0 = static_cast*>(branch[0])->str (); + const std::string s0 = static_cast*>(branch[0])->str (); const std::string s1 = static_cast*> (branch[1])->str (); - range_t rp0 = static_cast*>(branch[0])->range(); + const range_t rp0 = static_cast*>(branch[0])->range(); static_cast*>(branch[0])->range_ref().clear(); @@ -34592,10 +35653,10 @@ namespace exprtk inline expression_node_ptr synthesize_csrocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2]) { - std::string s0 = static_cast*>(branch[0])->str (); - std::string s1 = static_cast*>(branch[1])->str (); - range_t rp0 = static_cast*>(branch[0])->range(); - range_t rp1 = static_cast*>(branch[1])->range(); + const std::string s0 = static_cast*>(branch[0])->str (); + const std::string s1 = static_cast*>(branch[1])->str (); + const range_t rp0 = static_cast*>(branch[0])->range(); + const range_t rp1 = static_cast*>(branch[1])->range(); static_cast*>(branch[0])->range_ref().clear(); static_cast*>(branch[1])->range_ref().clear(); @@ -34902,21 +35963,22 @@ namespace exprtk { return branch[0]; } - else if ( - (details::e_lt == operation) || (details::e_lte == operation) || - (details::e_gt == operation) || (details::e_gte == operation) || - (details::e_and == operation) || (details::e_nand == operation) || - (details::e_or == operation) || (details::e_nor == operation) || - (details::e_xor == operation) || (details::e_xnor == operation) || - (details::e_in == operation) || (details::e_like == operation) || - (details::e_ilike == operation) - ) + + details::free_node(*node_allocator_, branch[0]); + + if ( + (details::e_lt == operation) || (details::e_lte == operation) || + (details::e_gt == operation) || (details::e_gte == operation) || + (details::e_and == operation) || (details::e_nand == operation) || + (details::e_or == operation) || (details::e_nor == operation) || + (details::e_xor == operation) || (details::e_xnor == operation) || + (details::e_in == operation) || (details::e_like == operation) || + (details::e_ilike == operation) + ) { return node_allocator_->allocate_c(T(0)); } - details::free_node(*node_allocator_,branch[0]); - return node_allocator_->allocate >(); } @@ -34946,7 +36008,7 @@ namespace exprtk if (is_constant_foldable(branch)) { - Type v = expression_point->value(); + const Type v = expression_point->value(); details::free_node(*node_allocator_,expression_point); return node_allocator_->allocate(v); @@ -35321,6 +36383,8 @@ namespace exprtk lexer::helper::sequence_validator sequence_validator_; lexer::helper::sequence_validator_3tokens sequence_validator_3tkns_; + loop_runtime_check_ptr loop_runtime_check_; + template friend void details::disable_type_checking(ParserType& p); }; @@ -35565,8 +36629,8 @@ namespace exprtk if (var) { T& x = var->ref(); - T x_original = x; - T result = integrate(e, x, r0, r1, number_of_intervals); + const T x_original = x; + const T result = integrate(e, x, r0, r1, number_of_intervals); x = x_original; return result; @@ -35656,8 +36720,8 @@ namespace exprtk if (var) { T& x = var->ref(); - T x_original = x; - T result = derivative(e, x, h); + const T x_original = x; + const T result = derivative(e, x, h); x = x_original; return result; @@ -36004,62 +37068,62 @@ namespace exprtk inline virtual T operator() (const T& x, const T& c1, const T& c0) { - poly_rtrn(1) poly_impl::evaluate(x,c1,c0); + poly_rtrn(1) (poly_impl::evaluate(x, c1, c0)); } inline virtual T operator() (const T& x, const T& c2, const T& c1, const T& c0) { - poly_rtrn(2) poly_impl::evaluate(x,c2,c1,c0); + poly_rtrn(2) (poly_impl::evaluate(x, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(3) poly_impl::evaluate(x,c3,c2,c1,c0); + poly_rtrn(3) (poly_impl::evaluate(x, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(4) poly_impl::evaluate(x,c4,c3,c2,c1,c0); + poly_rtrn(4) (poly_impl::evaluate(x, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(5) poly_impl::evaluate(x,c5,c4,c3,c2,c1,c0); + poly_rtrn(5) (poly_impl::evaluate(x, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(6) poly_impl::evaluate(x,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(6) (poly_impl::evaluate(x, c6, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(7) poly_impl::evaluate(x,c7,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(7) (poly_impl::evaluate(x, c7, c6, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(8) poly_impl::evaluate(x,c8,c7,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(8) (poly_impl::evaluate(x, c8, c7, c6, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(9) poly_impl::evaluate(x,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(9) (poly_impl::evaluate(x, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(10) poly_impl::evaluate(x,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(10) (poly_impl::evaluate(x, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(11) poly_impl::evaluate(x,c11,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(11) (poly_impl::evaluate(x, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); } inline virtual T operator() (const T& x, const T& c12, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) { - poly_rtrn(12) poly_impl::evaluate(x,c12,c11,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0); + poly_rtrn(12) (poly_impl::evaluate(x, c12, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0)); } #undef poly_rtrn @@ -36247,7 +37311,7 @@ namespace exprtk typedef typename expression_t::control_block::local_data_list_t ldl_t; - ldl_t ldl = expr.local_data_list(); + const ldl_t ldl = expr.local_data_list(); std::vector index_list; @@ -36416,8 +37480,16 @@ namespace exprtk template struct scoped_bft { - scoped_bft(BaseFuncType& bft) : bft_(bft) { bft_.pre (); } - ~scoped_bft() { bft_.post(); } + explicit scoped_bft(BaseFuncType& bft) + : bft_(bft) + { + bft_.pre (); + } + + ~scoped_bft() + { + bft_.post(); + } BaseFuncType& bft_; @@ -36517,7 +37589,7 @@ namespace exprtk typedef typename results_context_t::type_store_t type_t; typedef typename type_t::scalar_view scalar_t; - T result = e.value(); + const T result = e.value(); if (e.return_invoked()) { @@ -36611,6 +37683,11 @@ namespace exprtk return symbol_table_; } + inline const symbol_table_t& symbol_table() const + { + return symbol_table_; + } + inline void add_auxiliary_symtab(symbol_table_t& symtab) { auxiliary_symtab_list_.push_back(&symtab); @@ -36826,89 +37903,90 @@ namespace exprtk template inline bool pgo_primer() { - static const std::string expression_list[] - = { - "(y + x)", - "2 * (y + x)", - "(2 * y + 2 * x)", - "(y + x / y) * (x - y / x)", - "x / ((x + y) * (x - y)) / y", - "1 - ((x * y) + (y / x)) - 3", - "sin(2 * x) + cos(pi / y)", - "1 - sin(2 * x) + cos(pi / y)", - "sqrt(1 - sin(2 * x) + cos(pi / y) / 3)", - "(x^2 / sin(2 * pi / y)) -x / 2", - "x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y", - "clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)", - "iclamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)", - "max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))", - "if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x", - "1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^4 - 5.5x^5 + 6.6y^6 - 7.7x^27 + 8.8y^55", - "(yy + xx)", - "2 * (yy + xx)", - "(2 * yy + 2 * xx)", - "(yy + xx / yy) * (xx - yy / xx)", - "xx / ((xx + yy) * (xx - yy)) / yy", - "1 - ((xx * yy) + (yy / xx)) - 3", - "sin(2 * xx) + cos(pi / yy)", - "1 - sin(2 * xx) + cos(pi / yy)", - "sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3)", - "(xx^2 / sin(2 * pi / yy)) -xx / 2", - "xx + (cos(yy - sin(2 / xx * pi)) - sin(xx - cos(2 * yy / pi))) - yy", - "clamp(-1.0, sin(2 * pi * xx) + cos(yy / 2 * pi), +1.0)", - "max(3.33, min(sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3), 1.11))", - "if(avg(xx,yy) <= xx + yy, xx - yy, xx * yy) + 2 * pi / xx", - "1.1xx^1 + 2.2yy^2 - 3.3xx^3 + 4.4yy^4 - 5.5xx^5 + 6.6yy^6 - 7.7xx^27 + 8.8yy^55", - "(1.1*(2.2*(3.3*(4.4*(5.5*(6.6*(7.7*(8.8*(9.9+x)))))))))", - "(((((((((x+9.9)*8.8)*7.7)*6.6)*5.5)*4.4)*3.3)*2.2)*1.1)", - "(x + y) * z", "x + (y * z)", "(x + y) * 7", "x + (y * 7)", - "(x + 7) * y", "x + (7 * y)", "(7 + x) * y", "7 + (x * y)", - "(2 + x) * 3", "2 + (x * 3)", "(2 + 3) * x", "2 + (3 * x)", - "(x + 2) * 3", "x + (2 * 3)", - "(x + y) * (z / w)", "(x + y) * (z / 7)", "(x + y) * (7 / z)", "(x + 7) * (y / z)", - "(7 + x) * (y / z)", "(2 + x) * (y / z)", "(x + 2) * (y / 3)", "(2 + x) * (y / 3)", - "(x + 2) * (3 / y)", "x + (y * (z / w))", "x + (y * (z / 7))", "x + (y * (7 / z))", - "x + (7 * (y / z))", "7 + (x * (y / z))", "2 + (x * (3 / y))", "x + (2 * (y / 4))", - "2 + (x * (y / 3))", "x + (2 * (3 / y))", - "x + ((y * z) / w)", "x + ((y * z) / 7)", "x + ((y * 7) / z)", "x + ((7 * y) / z)", - "7 + ((y * z) / w)", "2 + ((x * 3) / y)", "x + ((2 * y) / 3)", "2 + ((x * y) / 3)", - "x + ((2 * 3) / y)", "(((x + y) * z) / w)", - "(((x + y) * z) / 7)", "(((x + y) * 7) / z)", "(((x + 7) * y) / z)", "(((7 + x) * y) / z)", - "(((2 + x) * 3) / y)", "(((x + 2) * y) / 3)", "(((2 + x) * y) / 3)", "(((x + 2) * 3) / y)", - "((x + (y * z)) / w)", "((x + (y * z)) / 7)", "((x + (y * 7)) / y)", "((x + (7 * y)) / z)", - "((7 + (x * y)) / z)", "((2 + (x * 3)) / y)", "((x + (2 * y)) / 3)", "((2 + (x * y)) / 3)", - "((x + (2 * 3)) / y)", - "(xx + yy) * zz", "xx + (yy * zz)", - "(xx + yy) * 7", "xx + (yy * 7)", - "(xx + 7) * yy", "xx + (7 * yy)", - "(7 + xx) * yy", "7 + (xx * yy)", - "(2 + x) * 3", "2 + (x * 3)", - "(2 + 3) * x", "2 + (3 * x)", - "(x + 2) * 3", "x + (2 * 3)", - "(xx + yy) * (zz / ww)", "(xx + yy) * (zz / 7)", - "(xx + yy) * (7 / zz)", "(xx + 7) * (yy / zz)", - "(7 + xx) * (yy / zz)", "(2 + xx) * (yy / zz)", - "(xx + 2) * (yy / 3)", "(2 + xx) * (yy / 3)", - "(xx + 2) * (3 / yy)", "xx + (yy * (zz / ww))", - "xx + (yy * (zz / 7))", "xx + (yy * (7 / zz))", - "xx + (7 * (yy / zz))", "7 + (xx * (yy / zz))", - "2 + (xx * (3 / yy))", "xx + (2 * (yy / 4))", - "2 + (xx * (yy / 3))", "xx + (2 * (3 / yy))", - "xx + ((yy * zz) / ww)", "xx + ((yy * zz) / 7)", - "xx + ((yy * 7) / zz)", "xx + ((7 * yy) / zz)", - "7 + ((yy * zz) / ww)", "2 + ((xx * 3) / yy)", - "xx + ((2 * yy) / 3)", "2 + ((xx * yy) / 3)", - "xx + ((2 * 3) / yy)", "(((xx + yy) * zz) / ww)", - "(((xx + yy) * zz) / 7)", "(((xx + yy) * 7) / zz)", - "(((xx + 7) * yy) / zz)", "(((7 + xx) * yy) / zz)", - "(((2 + xx) * 3) / yy)", "(((xx + 2) * yy) / 3)", - "(((2 + xx) * yy) / 3)", "(((xx + 2) * 3) / yy)", - "((xx + (yy * zz)) / ww)", "((xx + (yy * zz)) / 7)", - "((xx + (yy * 7)) / yy)", "((xx + (7 * yy)) / zz)", - "((7 + (xx * yy)) / zz)", "((2 + (xx * 3)) / yy)", - "((xx + (2 * yy)) / 3)", "((2 + (xx * yy)) / 3)", - "((xx + (2 * 3)) / yy)" - }; + static const std::string expression_list[] = + { + "(y + x)", + "2 * (y + x)", + "(2 * y + 2 * x)", + "(y + x / y) * (x - y / x)", + "x / ((x + y) * (x - y)) / y", + "1 - ((x * y) + (y / x)) - 3", + "sin(2 * x) + cos(pi / y)", + "1 - sin(2 * x) + cos(pi / y)", + "sqrt(1 - sin(2 * x) + cos(pi / y) / 3)", + "(x^2 / sin(2 * pi / y)) -x / 2", + "x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y", + "clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)", + "iclamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)", + "max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))", + "if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x", + "1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^4 - 5.5x^5 + 6.6y^6 - 7.7x^27 + 8.8y^55", + "(yy + xx)", + "2 * (yy + xx)", + "(2 * yy + 2 * xx)", + "(yy + xx / yy) * (xx - yy / xx)", + "xx / ((xx + yy) * (xx - yy)) / yy", + "1 - ((xx * yy) + (yy / xx)) - 3", + "sin(2 * xx) + cos(pi / yy)", + "1 - sin(2 * xx) + cos(pi / yy)", + "sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3)", + "(xx^2 / sin(2 * pi / yy)) -xx / 2", + "xx + (cos(yy - sin(2 / xx * pi)) - sin(xx - cos(2 * yy / pi))) - yy", + "clamp(-1.0, sin(2 * pi * xx) + cos(yy / 2 * pi), +1.0)", + "max(3.33, min(sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3), 1.11))", + "if(avg(xx,yy) <= xx + yy, xx - yy, xx * yy) + 2 * pi / xx", + "1.1xx^1 + 2.2yy^2 - 3.3xx^3 + 4.4yy^4 - 5.5xx^5 + 6.6yy^6 - 7.7xx^27 + 8.8yy^55", + "(1.1*(2.2*(3.3*(4.4*(5.5*(6.6*(7.7*(8.8*(9.9+x)))))))))", + "(((((((((x+9.9)*8.8)*7.7)*6.6)*5.5)*4.4)*3.3)*2.2)*1.1)", + "(x + y) * z", "x + (y * z)", "(x + y) * 7", "x + (y * 7)", + "(x + 7) * y", "x + (7 * y)", "(7 + x) * y", "7 + (x * y)", + "(2 + x) * 3", "2 + (x * 3)", "(2 + 3) * x", "2 + (3 * x)", + "(x + 2) * 3", "x + (2 * 3)", + "(x + y) * (z / w)", "(x + y) * (z / 7)", "(x + y) * (7 / z)", "(x + 7) * (y / z)", + "(7 + x) * (y / z)", "(2 + x) * (y / z)", "(x + 2) * (y / 3)", "(2 + x) * (y / 3)", + "(x + 2) * (3 / y)", "x + (y * (z / w))", "x + (y * (z / 7))", "x + (y * (7 / z))", + "x + (7 * (y / z))", "7 + (x * (y / z))", "2 + (x * (3 / y))", "x + (2 * (y / 4))", + "2 + (x * (y / 3))", "x + (2 * (3 / y))", + "x + ((y * z) / w)", "x + ((y * z) / 7)", "x + ((y * 7) / z)", "x + ((7 * y) / z)", + "7 + ((y * z) / w)", "2 + ((x * 3) / y)", "x + ((2 * y) / 3)", "2 + ((x * y) / 3)", + "x + ((2 * 3) / y)", "(((x + y) * z) / w)", + "(((x + y) * z) / 7)", "(((x + y) * 7) / z)", "(((x + 7) * y) / z)", "(((7 + x) * y) / z)", + "(((2 + x) * 3) / y)", "(((x + 2) * y) / 3)", "(((2 + x) * y) / 3)", "(((x + 2) * 3) / y)", + "((x + (y * z)) / w)", "((x + (y * z)) / 7)", "((x + (y * 7)) / y)", "((x + (7 * y)) / z)", + "((7 + (x * y)) / z)", "((2 + (x * 3)) / y)", "((x + (2 * y)) / 3)", "((2 + (x * y)) / 3)", + "((x + (2 * 3)) / y)", + "(xx + yy) * zz", "xx + (yy * zz)", + "(xx + yy) * 7", "xx + (yy * 7)", + "(xx + 7) * yy", "xx + (7 * yy)", + "(7 + xx) * yy", "7 + (xx * yy)", + "(2 + x) * 3", "2 + (x * 3)", + "(2 + 3) * x", "2 + (3 * x)", + "(x + 2) * 3", "x + (2 * 3)", + "(xx + yy) * (zz / ww)", "(xx + yy) * (zz / 7)", + "(xx + yy) * (7 / zz)", "(xx + 7) * (yy / zz)", + "(7 + xx) * (yy / zz)", "(2 + xx) * (yy / zz)", + "(xx + 2) * (yy / 3)", "(2 + xx) * (yy / 3)", + "(xx + 2) * (3 / yy)", "xx + (yy * (zz / ww))", + "xx + (yy * (zz / 7))", "xx + (yy * (7 / zz))", + "xx + (7 * (yy / zz))", "7 + (xx * (yy / zz))", + "2 + (xx * (3 / yy))", "xx + (2 * (yy / 4))", + "2 + (xx * (yy / 3))", "xx + (2 * (3 / yy))", + "xx + ((yy * zz) / ww)", "xx + ((yy * zz) / 7)", + "xx + ((yy * 7) / zz)", "xx + ((7 * yy) / zz)", + "7 + ((yy * zz) / ww)", "2 + ((xx * 3) / yy)", + "xx + ((2 * yy) / 3)", "2 + ((xx * yy) / 3)", + "xx + ((2 * 3) / yy)", "(((xx + yy) * zz) / ww)", + "(((xx + yy) * zz) / 7)", "(((xx + yy) * 7) / zz)", + "(((xx + 7) * yy) / zz)", "(((7 + xx) * yy) / zz)", + "(((2 + xx) * 3) / yy)", "(((xx + 2) * yy) / 3)", + "(((2 + xx) * yy) / 3)", "(((xx + 2) * 3) / yy)", + "((xx + (yy * zz)) / ww)", "((xx + (yy * zz)) / 7)", + "((xx + (yy * 7)) / yy)", "((xx + (7 * yy)) / zz)", + "((7 + (xx * yy)) / zz)", "((2 + (xx * 3)) / yy)", + "((xx + (2 * yy)) / 3)", "((2 + (xx * yy)) / 3)", + "((xx + (2 * 3)) / yy)" + }; + static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string); T x = T(0); @@ -36990,7 +38068,7 @@ namespace exprtk { const T v = T(123.456 + i); - if (details::is_true(details::numeric::nequal(details::numeric::fast_exp::result(v),details::numeric::pow(v,T( 1))))) + if (details::is_true(details::numeric::nequal(details::numeric::fast_exp::result(v),details::numeric::pow(v,T(1))))) return false; #define else_stmt(N) \ @@ -37093,14 +38171,14 @@ namespace exprtk { if (stop_time_.tv_sec >= start_time_.tv_sec) { - return 1000000LLU * static_cast(stop_time_.tv_sec - start_time_.tv_sec ) + - static_cast(stop_time_.tv_usec - start_time_.tv_usec) ; + return 1000000LLU * static_cast(stop_time_.tv_sec - start_time_.tv_sec ) + + static_cast(stop_time_.tv_usec - start_time_.tv_usec) ; } else - return std::numeric_limits::max(); + return std::numeric_limits::max(); } else - return std::numeric_limits::max(); + return std::numeric_limits::max(); } inline double time() const @@ -38152,10 +39230,13 @@ namespace exprtk ) return T(0); - std::size_t dist = r1 - r0 + 1; - std::size_t shift = n % dist; + const std::size_t dist = r1 - r0 + 1; + const std::size_t shift = n % dist; - std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1); + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + shift, + vec.begin() + r1 + 1); return T(1); } @@ -38203,7 +39284,10 @@ namespace exprtk std::size_t dist = r1 - r0 + 1; std::size_t shift = (dist - (n % dist)) % dist; - std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1); + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + shift, + vec.begin() + r1 + 1); return T(1); } @@ -38248,12 +39332,15 @@ namespace exprtk ) return T(0); - std::size_t dist = r1 - r0 + 1; + const std::size_t dist = r1 - r0 + 1; if (n > dist) return T(0); - std::rotate(vec.begin() + r0, vec.begin() + r0 + n, vec.begin() + r1 + 1); + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + n, + vec.begin() + r1 + 1); for (std::size_t i = r1 - n + 1; i <= r1; ++i) { @@ -38303,14 +39390,17 @@ namespace exprtk ) return T(0); - std::size_t dist = r1 - r0 + 1; + const std::size_t dist = r1 - r0 + 1; if (n > dist) return T(0); - std::size_t shift = (dist - (n % dist)) % dist; + const std::size_t shift = (dist - (n % dist)) % dist; - std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1); + std::rotate( + vec.begin() + r0, + vec.begin() + r0 + shift, + vec.begin() + r1 + 1); for (std::size_t i = r0; i < r0 + n; ++i) { @@ -38927,9 +40017,11 @@ namespace exprtk namespace information { static const char* library = "Mathematical Expression Toolkit"; - static const char* version = "2.7182818284590452353602874713526624977572470936999595749" - "669676277240766303535475945713821785251664274274663919320"; - static const char* date = "20200101"; + static const char* version = "2.718281828459045235360287471352" + "66249775724709369995957496696762" + "77240766303535475945713821785251" + "66427427466391932003059921817413"; + static const char* date = "20210101"; static inline std::string data() { diff --git a/exprtk/exprtk_benchmark.cpp b/exprtk/exprtk_benchmark.cpp index 8e9c5fa..79bcc57 100644 --- a/exprtk/exprtk_benchmark.cpp +++ b/exprtk/exprtk_benchmark.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * ExprTk vs Native Benchmarks * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_01.cpp b/exprtk/exprtk_simple_example_01.cpp index 80eafe1..0f08d04 100644 --- a/exprtk/exprtk_simple_example_01.cpp +++ b/exprtk/exprtk_simple_example_01.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 1 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_02.cpp b/exprtk/exprtk_simple_example_02.cpp index 56b9c9b..04c9731 100644 --- a/exprtk/exprtk_simple_example_02.cpp +++ b/exprtk/exprtk_simple_example_02.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 2 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_03.cpp b/exprtk/exprtk_simple_example_03.cpp index 8e5a164..618343c 100644 --- a/exprtk/exprtk_simple_example_03.cpp +++ b/exprtk/exprtk_simple_example_03.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 3 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_04.cpp b/exprtk/exprtk_simple_example_04.cpp index aca7014..db35945 100644 --- a/exprtk/exprtk_simple_example_04.cpp +++ b/exprtk/exprtk_simple_example_04.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 4 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_05.cpp b/exprtk/exprtk_simple_example_05.cpp index 50d7987..9790feb 100644 --- a/exprtk/exprtk_simple_example_05.cpp +++ b/exprtk/exprtk_simple_example_05.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 5 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_06.cpp b/exprtk/exprtk_simple_example_06.cpp index 0af6f8b..8d9c6d3 100644 --- a/exprtk/exprtk_simple_example_06.cpp +++ b/exprtk/exprtk_simple_example_06.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 6 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_07.cpp b/exprtk/exprtk_simple_example_07.cpp index bdf3c3b..169337c 100644 --- a/exprtk/exprtk_simple_example_07.cpp +++ b/exprtk/exprtk_simple_example_07.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 7 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_08.cpp b/exprtk/exprtk_simple_example_08.cpp index 4565098..855c340 100644 --- a/exprtk/exprtk_simple_example_08.cpp +++ b/exprtk/exprtk_simple_example_08.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 8 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_09.cpp b/exprtk/exprtk_simple_example_09.cpp index b11f193..3445f4c 100644 --- a/exprtk/exprtk_simple_example_09.cpp +++ b/exprtk/exprtk_simple_example_09.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 9 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_10.cpp b/exprtk/exprtk_simple_example_10.cpp index 13728da..75d5618 100644 --- a/exprtk/exprtk_simple_example_10.cpp +++ b/exprtk/exprtk_simple_example_10.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 10 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_11.cpp b/exprtk/exprtk_simple_example_11.cpp index 454c8f0..8156b16 100644 --- a/exprtk/exprtk_simple_example_11.cpp +++ b/exprtk/exprtk_simple_example_11.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 11 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_12.cpp b/exprtk/exprtk_simple_example_12.cpp index ce398bd..8679ab3 100644 --- a/exprtk/exprtk_simple_example_12.cpp +++ b/exprtk/exprtk_simple_example_12.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 12 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_13.cpp b/exprtk/exprtk_simple_example_13.cpp index 839e749..2e8cb32 100644 --- a/exprtk/exprtk_simple_example_13.cpp +++ b/exprtk/exprtk_simple_example_13.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 13 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * @@ -89,7 +89,7 @@ void savitzky_golay_filter() for (std::size_t i = 0; i < v_out.size(); ++i) { - printf("%10.6f\t%10.6f\n",v_in[i],v_out[i]); + printf("%10.6f\t%10.6f\n", v_in[i], v_out[i]); } } diff --git a/exprtk/exprtk_simple_example_14.cpp b/exprtk/exprtk_simple_example_14.cpp index bc508a2..ea56d86 100644 --- a/exprtk/exprtk_simple_example_14.cpp +++ b/exprtk/exprtk_simple_example_14.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 14 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_15.cpp b/exprtk/exprtk_simple_example_15.cpp index f7364fd..294cf03 100644 --- a/exprtk/exprtk_simple_example_15.cpp +++ b/exprtk/exprtk_simple_example_15.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 15 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_16.cpp b/exprtk/exprtk_simple_example_16.cpp index cd76f84..6777055 100644 --- a/exprtk/exprtk_simple_example_16.cpp +++ b/exprtk/exprtk_simple_example_16.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 16 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_17.cpp b/exprtk/exprtk_simple_example_17.cpp index 50263aa..343f4f1 100644 --- a/exprtk/exprtk_simple_example_17.cpp +++ b/exprtk/exprtk_simple_example_17.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 17 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_18.cpp b/exprtk/exprtk_simple_example_18.cpp index 4fabc72..b1bbad3 100644 --- a/exprtk/exprtk_simple_example_18.cpp +++ b/exprtk/exprtk_simple_example_18.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 18 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * diff --git a/exprtk/exprtk_simple_example_19.cpp b/exprtk/exprtk_simple_example_19.cpp index 7720155..c200646 100644 --- a/exprtk/exprtk_simple_example_19.cpp +++ b/exprtk/exprtk_simple_example_19.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Simple Example 19 * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * @@ -55,7 +55,7 @@ class randu : public exprtk::igeneric_function if ( (1 == ps_index) && !exprtk::rtl::vecops::helper:: - load_vector_range::process(parameters,r0,r1,1,2,0) + load_vector_range::process(parameters, r0, r1, 1, 2, 0) ) return T(0); diff --git a/exprtk/exprtk_test.cpp b/exprtk/exprtk_test.cpp index 91e7593..63e7376 100644 --- a/exprtk/exprtk_test.cpp +++ b/exprtk/exprtk_test.cpp @@ -3,7 +3,7 @@ * C++ Mathematical Expression Toolkit Library * * * * Examples and Unit-Tests * - * Author: Arash Partow (1999-2020) * + * Author: Arash Partow (1999-2021) * * URL: http://www.partow.net/programming/exprtk/index.html * * * * Copyright notice: * @@ -358,6 +358,42 @@ static const test_t global_test_list[] = test_t("( 7 - 2 )",+5.0), test_t("( 8 - 1 )",+7.0), test_t("( 9 - 0 )",+9.0), + test_t("1 - -1" , 2.0), + test_t("1 --1" , 2.0), + test_t("1-- 1" , 2.0), + test_t("1--1" , 2.0), + test_t("1 -- -1", 0.0), + test_t("1 + -1" , 0.0), + test_t("1 +-1" , 0.0), + test_t("1+- 1" , 0.0), + test_t("1+-1" , 0.0), + test_t("1 +- -1", 2.0), + test_t("1 + +1" , 2.0), + test_t("1 ++1" , 2.0), + test_t("1 - -1 + 1" , 3.0), + test_t("1 --1 + 1" , 3.0), + test_t("1-- 1 + 1" , 3.0), + test_t("1--1 + 1" , 3.0), + test_t("1 -- -1 + 1", 1.0), + test_t("1 + -1 + 1" , 1.0), + test_t("1 +-1 + 1" , 1.0), + test_t("1+- 1 + 1" , 1.0), + test_t("1+-1 + 1" , 1.0), + test_t("1 +- -1 + 1", 3.0), + test_t("1 + +1 + 1" , 3.0), + test_t("1 ++1 + 1" , 3.0), + test_t("1 - -1 - 1" , 1.0), + test_t("1 --1 - 1" , 1.0), + test_t("1-- 1 - 1" , 1.0), + test_t("1--1 - 1" , 1.0), + test_t("1 -- -1 - 1", -1.0), + test_t("1 + -1 - 1" , -1.0), + test_t("1 +-1 - 1" , -1.0), + test_t("1+- 1 - 1" , -1.0), + test_t("1+-1 - 1" , -1.0), + test_t("1 +- -1 - 1", 1.0), + test_t("1 + +1 - 1" , 1.0), + test_t("1 ++1 - 1" , 1.0), test_t("-(1+2)",-3.0), test_t("+(1+2)",+3.0), test_t("+(1-2)",-1.0), @@ -1162,14 +1198,14 @@ inline bool test_expression(const std::string& expression_string, const T& expec return false; } - T result = expression.value(); + const T result = expression.value(); if (not_equal(result,expected_result)) { printf("test_expression() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\n", expression_string.c_str(), - (double)expected_result, - (double)result); + static_cast(expected_result), + static_cast(result)); return false; } @@ -1177,6 +1213,43 @@ inline bool test_expression(const std::string& expression_string, const T& expec return true; } +template +struct edge_cases {}; + +template <> +struct edge_cases +{ + static inline std::vector test_cases() + { + std::vector cases; + cases.push_back(test_t(" 1.175494350822287508e-38", 1.175494350822287508e-38)); + cases.push_back(test_t(" 3.402823466385288598e+38", 3.402823466385288598e+38)); + cases.push_back(test_t("+1.175494350822287508e-38", +1.175494350822287508e-38)); + cases.push_back(test_t("+3.402823466385288598e+38", +3.402823466385288598e+38)); + cases.push_back(test_t("-1.175494350822287508e-38", -1.175494350822287508e-38)); + cases.push_back(test_t("-3.402823466385288598e+38", -3.402823466385288598e+38)); + + return cases; + } +}; + +template <> +struct edge_cases +{ + static inline std::vector test_cases() + { + std::vector cases; + cases.push_back(test_t(" 2.2250738585072013831e-308", 2.2250738585072013831e-308)); + cases.push_back(test_t(" 1.7976931348623157081e+308", 1.7976931348623157081e+308)); + cases.push_back(test_t("+2.2250738585072013831e-308", +2.2250738585072013831e-308)); + cases.push_back(test_t("+1.7976931348623157081e+308", +1.7976931348623157081e+308)); + cases.push_back(test_t("-2.2250738585072013831e-308", -2.2250738585072013831e-308)); + cases.push_back(test_t("-1.7976931348623157081e+308", -1.7976931348623157081e+308)); + + return cases; + } +}; + template inline bool run_test00() { @@ -1198,6 +1271,25 @@ inline bool run_test00() } } + { + const std::vector tests = edge_cases::test_cases(); + + bool result = true; + + for (std::size_t i = 0; i < tests.size(); ++i) + { + if (!test_expression(tests[i].first,T(tests[i].second))) + { + result = false; + } + } + + if (!result) + { + return false; + } + } + return true; } @@ -1256,34 +1348,106 @@ inline bool run_test01() test_xy("2 * (x + y) - 1" ,T(2.2),T(3.3),T(10.0 )), test_xy("y + (x + 1)" ,T(2.2),T(3.3),T(6.5 )), test_xy("(x + 1) + y" ,T(2.2),T(3.3),T(6.5 )), - test_xy("2 * x" ,T(2.2),T(0.0),T(4.4)), - test_xy("x * 2" ,T(2.2),T(0.0),T(4.4)), - test_xy("1.1 + x",T(2.2),T(0.0),T(3.3)), - test_xy("x + 1.1",T(2.2),T(0.0),T(3.3)), - test_xy("x * 1 == x" ,T(2.0),T(3.0),T(1.0)), - test_xy("1 * x == x" ,T(2.0),T(3.0),T(1.0)), - test_xy("y * 1 == y" ,T(2.0),T(3.0),T(1.0)), - test_xy("1 * y == y" ,T(2.0),T(3.0),T(1.0)), - test_xy("x * 0 == 0" ,T(2.0),T(3.0),T(1.0)), - test_xy("0 * x == 0" ,T(2.0),T(3.0),T(1.0)), - test_xy("y * 0 == 0" ,T(2.0),T(3.0),T(1.0)), - test_xy("0 * y == 0" ,T(2.0),T(3.0),T(1.0)), - test_xy("x + 1 == 1 + x",T(2.0),T(3.0),T(1.0)), - test_xy("y + 1 == 1 + y",T(2.0),T(3.0),T(1.0)), - test_xy("x + y == y + x",T(2.0),T(3.0),T(1.0)), - test_xy("x * y == y * x",T(2.0),T(3.0),T(1.0)), - test_xy("x < y" ,T(2.0),T(3.0),T(1.0)), - test_xy("y > x" ,T(2.0),T(3.0),T(1.0)), - test_xy("x <= y" ,T(2.0),T(3.0),T(1.0)), - test_xy("y >= x" ,T(2.0),T(3.0),T(1.0)), - test_xy("x + y > y" ,T(2.0),T(3.0),T(1.0)), - test_xy("x + y > x" ,T(2.0),T(3.0),T(1.0)), - test_xy("x * y > y" ,T(2.0),T(3.0),T(1.0)), - test_xy("x * y > x" ,T(2.0),T(3.0),T(1.0)), - test_xy("(x + y) > y" ,T(2.0),T(3.0),T(1.0)), - test_xy("(x + y) > x" ,T(2.0),T(3.0),T(1.0)), - test_xy("(x * y) > y" ,T(2.0),T(3.0),T(1.0)), - test_xy("(x * y) > x" ,T(2.0),T(3.0),T(1.0)), + test_xy("2 * x" ,T(2.2),T(0.0),T(4.4)), + test_xy("x * 2" ,T(2.2),T(0.0),T(4.4)), + test_xy("1.1 + x" ,T(2.2),T(0.0),T(3.3)), + test_xy("x + 1.1" ,T(2.2),T(0.0),T(3.3)), + test_xy("x - -1 " ,T(1.0),T(0.0),T(2)), + test_xy("x --1 " ,T(1.0),T(0.0),T(2)), + test_xy("x-- 1 " ,T(1.0),T(0.0),T(2)), + test_xy("x--1 " ,T(1.0),T(0.0),T(2)), + test_xy("x -- -1" ,T(1.0),T(0.0),T(0)), + test_xy("x + -1 " ,T(1.0),T(0.0),T(0)), + test_xy("x +-1 " ,T(1.0),T(0.0),T(0)), + test_xy("x+- 1 " ,T(1.0),T(0.0),T(0)), + test_xy("x+-1 " ,T(1.0),T(0.0),T(0)), + test_xy("x +- -1" ,T(1.0),T(0.0),T(2)), + test_xy("x + +1 " ,T(1.0),T(0.0),T(2)), + test_xy("x ++1 " ,T(1.0),T(0.0),T(2)), + test_xy("1 - -x " ,T(1.0),T(0.0),T(2)), + test_xy("1 --x " ,T(1.0),T(0.0),T(2)), + test_xy("1-- x " ,T(1.0),T(0.0),T(2)), + test_xy("1--x " ,T(1.0),T(0.0),T(2)), + test_xy("1 -- -x" ,T(1.0),T(0.0),T(0)), + test_xy("1 + -x " ,T(1.0),T(0.0),T(0)), + test_xy("1 +-x " ,T(1.0),T(0.0),T(0)), + test_xy("1+- x " ,T(1.0),T(0.0),T(0)), + test_xy("1+-x " ,T(1.0),T(0.0),T(0)), + test_xy("1 +- -x" ,T(1.0),T(0.0),T(2)), + test_xy("1 + +x " ,T(1.0),T(0.0),T(2)), + test_xy("1 ++x " ,T(1.0),T(0.0),T(2)), + test_xy("(x - -1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x --1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x-- 1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x--1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x -- -1 + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x + -1 + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x +-1 + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x+- 1 + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x+-1 + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x +- -1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x + +1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x ++1 + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1 - -x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1 --x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1-- x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1--x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1 -- -x + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 + -x + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 +-x + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1+- x + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1+-x + 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 +- -x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1 + +x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(1 ++x + 1)" ,T(1.0),T(0.0),T(3)), + test_xy("(x - -1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x --1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x-- 1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x--1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x -- -1 - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(x + -1 - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(x +-1 - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(x+- 1 - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(x+-1 - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(x +- -1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x + +1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(x ++1 - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 - -x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 --x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1-- x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1--x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 -- -x - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(1 + -x - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(1 +-x - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(1+- x - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(1+-x - 1)" ,T(1.0),T(0.0),T(-1)), + test_xy("(1 +- -x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 + +x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("(1 ++x - 1)" ,T(1.0),T(0.0),T(1)), + test_xy("x * 1 == x" ,T(2.0),T(3.0),T(1.0)), + test_xy("1 * x == x" ,T(2.0),T(3.0),T(1.0)), + test_xy("y * 1 == y" ,T(2.0),T(3.0),T(1.0)), + test_xy("1 * y == y" ,T(2.0),T(3.0),T(1.0)), + test_xy("x * 0 == 0" ,T(2.0),T(3.0),T(1.0)), + test_xy("0 * x == 0" ,T(2.0),T(3.0),T(1.0)), + test_xy("y * 0 == 0" ,T(2.0),T(3.0),T(1.0)), + test_xy("0 * y == 0" ,T(2.0),T(3.0),T(1.0)), + test_xy("x + 1 == 1 + x" ,T(2.0),T(3.0),T(1.0)), + test_xy("y + 1 == 1 + y" ,T(2.0),T(3.0),T(1.0)), + test_xy("x + y == y + x" ,T(2.0),T(3.0),T(1.0)), + test_xy("x * y == y * x" ,T(2.0),T(3.0),T(1.0)), + test_xy("x < y" ,T(2.0),T(3.0),T(1.0)), + test_xy("y > x" ,T(2.0),T(3.0),T(1.0)), + test_xy("x <= y" ,T(2.0),T(3.0),T(1.0)), + test_xy("y >= x" ,T(2.0),T(3.0),T(1.0)), + test_xy("x + y > y" ,T(2.0),T(3.0),T(1.0)), + test_xy("x + y > x" ,T(2.0),T(3.0),T(1.0)), + test_xy("x * y > y" ,T(2.0),T(3.0),T(1.0)), + test_xy("x * y > x" ,T(2.0),T(3.0),T(1.0)), + test_xy("(x + y) > y" ,T(2.0),T(3.0),T(1.0)), + test_xy("(x + y) > x" ,T(2.0),T(3.0),T(1.0)), + test_xy("(x * y) > y" ,T(2.0),T(3.0),T(1.0)), + test_xy("(x * y) > x" ,T(2.0),T(3.0),T(1.0)), test_xy("(2x + 3y) == (2*x + 3*y)" ,T(2.0),T(3.0),T(1.0)), test_xy("2(x + y) == (2*x + 2*y)" ,T(2.0),T(3.0),T(1.0)), test_xy(" (x + y)3 == (3*x + 3*y)" ,T(2.0),T(3.0),T(1.0)), @@ -1646,14 +1810,14 @@ inline bool run_test01() } } - T result = expression.value(); + const T result = expression.value(); if (not_equal(result,test.result)) { printf("run_test01() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\n", test.expr.c_str(), - (double)test.result, - (double)result); + static_cast(test.result), + static_cast(result)); loop_result = false; } @@ -1758,14 +1922,14 @@ inline bool run_test01() } } - T result = expression.value(); + const T result = expression.value(); if (not_equal(result,test.result)) { printf("run_test01() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\n", test.expr.c_str(), - (double)test.result, - (double)result); + static_cast(test.result), + static_cast(result)); loop_result = false; } @@ -1841,14 +2005,14 @@ inline bool run_test01() } } - T result = expression.value(); + const T result = expression.value(); if (not_equal(result,T(1))) { printf("run_test01() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\n", expr_list[i].c_str(), - (double)1.0, - (double)result); + static_cast(1.0), + static_cast(result)); loop_result = false; } @@ -1901,14 +2065,14 @@ inline bool run_test01() } } - T result = expression.value(); + const T result = expression.value(); if (not_equal(result,T(1))) { printf("run_test01() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\n", expr_list[i].c_str(), - (double)1.0, - (double)result); + static_cast(1.0), + static_cast(result)); loop_result = false; } } @@ -2422,6 +2586,17 @@ inline bool run_test02() 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 y:= 2; '01234567890'[y:] == a ", "234567890","" ,T(1.0)), + test_ab("var y:= 2; '01234567890'[y:][y:] == a ", "4567890" ,"" ,T(1.0)), + test_ab("var y:= 2; '01234567890'[y:][y:][y:] == a ", "67890" ,"" ,T(1.0)), + test_ab("var y:= 2; '01234567890'[y:][y:][y:][y:] == a ", "890" ,"" ,T(1.0)), + test_ab("var y:= 2; '01234567890'[y:][y:][y:][y:][y:] == a", "0" ,"" ,T(1.0)), + test_ab("var y:= 2; '0123456789'[y:] == a ", "23456789" ,"" ,T(1.0)), + test_ab("var y:= 2; '0123456789'[y:][y:] == a ", "456789" ,"" ,T(1.0)), + test_ab("var y:= 2; '0123456789'[y:][y:][y:] == a ", "6789" ,"" ,T(1.0)), + test_ab("var y:= 2; '0123456789'[y:][y:][y:][y:] == a ", "89" ,"" ,T(1.0)), + test_ab("var y:= 2; '0123456789'[y:][y:][y:][y:][y:] == a ", "" ,"" ,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)), @@ -2598,8 +2773,8 @@ inline bool run_test02() printf("run_test02() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f\t" "a='%s'\tb='%s'\tc='%s'\n", test.expr.c_str(), - (double)test.result, - (double)expr_result, + static_cast(test.result), + static_cast(expr_result), str_a.c_str(), str_b.c_str(), str_c.c_str()); @@ -3013,10 +3188,10 @@ inline bool run_test04() { printf("run_test04() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f x:%19.15f\ty:%19.15f\n", expression_string.c_str(), - (double)result1, - (double)result2, - (double)x, - (double)y); + static_cast(result1), + static_cast(result2), + static_cast(x), + static_cast(y)); return false; } @@ -3083,10 +3258,10 @@ inline bool run_test05() { printf("run_test05() - Computation Error: Expression: [%s]\tExpected: %19.15f\tResult: %19.15f x:%19.15f\ty:%19.15f\tIndex:%d\n", expression_string.c_str(), - (double)real_result, - (double)result, - (double)x, - (double)y, + static_cast(real_result), + static_cast(result), + static_cast(x), + static_cast(y), static_cast(i)); return false; @@ -3140,8 +3315,8 @@ inline bool run_test06() if (not_equal(total_area1,T(pi) / T(2),T(0.000001))) { printf("run_test06() - Integration Error: Expected: %19.15f\tResult: %19.15f\n", - (double)(pi / T(2)), - (double)total_area1); + static_cast(pi / T(2)), + static_cast(total_area1)); return false; } @@ -3192,9 +3367,9 @@ inline bool run_test07() if (not_equal(deriv1_result1,deriv1_real_result,T(0.00001))) { printf("run_test07() - 1st Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", - (double)x, - (double)deriv1_real_result, - (double)deriv1_result1); + static_cast(x), + static_cast(deriv1_real_result), + static_cast(deriv1_result1)); return false; } @@ -3214,9 +3389,9 @@ inline bool run_test07() if (not_equal(deriv2_result1,deriv2_real_result,T(0.01))) { printf("run_test07() - 2nd Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", - (double)x, - (double)deriv2_real_result, - (double)deriv2_result1); + static_cast(x), + static_cast(deriv2_real_result), + static_cast(deriv2_result1)); return false; } @@ -3236,9 +3411,9 @@ inline bool run_test07() if (not_equal(deriv3_result1,deriv3_real_result,T(0.01))) { printf("run_test07() - 3rd Derivative Error: x: %19.15f\tExpected: %19.15f\tResult: %19.15f\n", - (double)x, - (double)deriv3_real_result, - (double)deriv3_result1); + static_cast(x), + static_cast(deriv3_real_result), + static_cast(deriv3_result1)); return false; } @@ -3526,9 +3701,9 @@ inline bool run_test09() const T pi = T(3.141592653589793238462643383279502); - T result = expression.value(); + const T result = expression.value(); - T expected = T(4) * + const T expected = T(4) * ( mf(sin(x*pi),y / T(2)) + mf(sin(x*pi),y / T(2)) + @@ -3545,8 +3720,8 @@ inline bool run_test09() if (not_equal(result,expected,T(0.0000001))) { printf("run_test09() - Error Expected: %19.15f\tResult: %19.15f\n", - (double)expected, - (double)result); + static_cast(expected), + static_cast(result)); return false; } @@ -4311,6 +4486,78 @@ inline bool run_test10() { "var x; 1", "var x := 1; x", + "var x:= 1; x - -1 == 2", + "var x:= 1; x --1 == 2", + "var x:= 1; x-- 1 == 2", + "var x:= 1; x--1 == 2", + "var x:= 1; x -- -1== 0", + "var x:= 1; x + -1 == 0", + "var x:= 1; x +-1 == 0", + "var x:= 1; x+- 1 == 0", + "var x:= 1; x+-1 == 0", + "var x:= 1; x +- -1== 2", + "var x:= 1; x + +1 == 2", + "var x:= 1; x ++1 == 2", + "var x:= 1; 1 - -x == 2", + "var x:= 1; 1 --x == 2", + "var x:= 1; 1-- x == 2", + "var x:= 1; 1--x == 2", + "var x:= 1; 1 -- -x== 0", + "var x:= 1; 1 + -x == 0", + "var x:= 1; 1 +-x == 0", + "var x:= 1; 1+- x == 0", + "var x:= 1; 1+-x == 0", + "var x:= 1; 1 +- -x== 2", + "var x:= 1; 1 + +x == 2", + "var x:= 1; 1 ++x == 2", + "var x:= 1; (x - -1 + 1) == 3", + "var x:= 1; (x --1 + 1) == 3", + "var x:= 1; (x-- 1 + 1) == 3", + "var x:= 1; (x--1 + 1) == 3", + "var x:= 1; (x -- -1 + 1) == 1", + "var x:= 1; (x + -1 + 1) == 1", + "var x:= 1; (x +-1 + 1) == 1", + "var x:= 1; (x+- 1 + 1) == 1", + "var x:= 1; (x+-1 + 1) == 1", + "var x:= 1; (x +- -1 + 1) == 3", + "var x:= 1; (x + +1 + 1) == 3", + "var x:= 1; (x ++1 + 1) == 3", + "var x:= 1; (1 - -x + 1) == 3", + "var x:= 1; (1 --x + 1) == 3", + "var x:= 1; (1-- x + 1) == 3", + "var x:= 1; (1--x + 1) == 3", + "var x:= 1; (1 -- -x + 1) == 1", + "var x:= 1; (1 + -x + 1) == 1", + "var x:= 1; (1 +-x + 1) == 1", + "var x:= 1; (1+- x + 1) == 1", + "var x:= 1; (1+-x + 1) == 1", + "var x:= 1; (1 +- -x + 1) == 3", + "var x:= 1; (1 + +x + 1) == 3", + "var x:= 1; (1 ++x + 1) == 3", + "var x:= 1; (x - -1 - 1) == 1", + "var x:= 1; (x --1 - 1) == 1", + "var x:= 1; (x-- 1 - 1) == 1", + "var x:= 1; (x--1 - 1) == 1", + "var x:= 1; (x -- -1 - 1) == -1", + "var x:= 1; (x + -1 - 1) == -1", + "var x:= 1; (x +-1 - 1) == -1", + "var x:= 1; (x+- 1 - 1) == -1", + "var x:= 1; (x+-1 - 1) == -1", + "var x:= 1; (x +- -1 - 1) == 1", + "var x:= 1; (x + +1 - 1) == 1", + "var x:= 1; (x ++1 - 1) == 1", + "var x:= 1; (1 - -x - 1) == 1", + "var x:= 1; (1 --x - 1) == 1", + "var x:= 1; (1-- x - 1) == 1", + "var x:= 1; (1--x - 1) == 1", + "var x:= 1; (1 -- -x - 1) == -1", + "var x:= 1; (1 + -x - 1) == -1", + "var x:= 1; (1 +-x - 1) == -1", + "var x:= 1; (1+- x - 1) == -1", + "var x:= 1; (1+-x - 1) == -1", + "var x:= 1; (1 +- -x - 1) == 1", + "var x:= 1; (1 + +x - 1) == 1", + "var x:= 1; (1 ++x - 1) == 1", "var x := 1; var y := 2; 1", "var x := 1; var y := 2; x", "var x:=6; var y:=4; x + -3 == 3", @@ -4662,7 +4909,7 @@ inline bool run_test10() } } - T result = expression.value(); + const T result = expression.value(); if (T(1) != result) { @@ -4699,7 +4946,7 @@ inline bool run_test10() continue; } - T result = expression.value(); + const T result = expression.value(); if (T(1) != result) { @@ -5287,8 +5534,8 @@ inline bool run_test15() if (not_equal(base_result,result)) { printf("run_test15() - Error in evaluation! (1) Base: %20.10f\tResult: %20.10f\tExpression: %s\n", - (double)base_result, - (double)result, + static_cast(base_result), + static_cast(result), expr_str_list[i].c_str()); failure = true; @@ -5917,6 +6164,96 @@ struct overload_func : exprtk::igeneric_function template inline bool run_test18() { + { + exprtk::symbol_table symbol_table; + symbol_table.remove_variable("x",true); + symbol_table.remove_variable("x",false); + symbol_table.remove_stringvar("x"); + symbol_table.remove_function("x"); + symbol_table.remove_vararg_function("x"); + symbol_table.remove_vector("x"); + } + + { + exprtk::symbol_table symbol_table; + + { + T x; + const bool result1 = symbol_table.add_variable("x", x); + const bool result2 = symbol_table.remove_variable("x"); + const bool result3 = symbol_table.remove_variable("x"); + + if (!result1 || !result2 || result3) + { + printf("run_test18() - Failed sym_tab add/remove [1]\n"); + } + } + + { + std::string x; + const bool result1 = symbol_table.add_stringvar("x", x); + const bool result2 = symbol_table.remove_stringvar("x"); + const bool result3 = symbol_table.remove_stringvar("x"); + + if (!result1 || !result2 || result3) + { + printf("run_test18() - Failed sym_tab add/remove [2]\n"); + } + } + + { + std::vector x(10,T(0)); + const bool result1 = symbol_table.add_vector("x", x); + const bool result2 = symbol_table.remove_vector("x"); + const bool result3 = symbol_table.remove_vector("x"); + + if (!result1 || !result2 || result3) + { + printf("run_test18() - Failed sym_tab add/remove [3]\n"); + } + } + + { + myfunc x; + const bool result1 = symbol_table.add_function("x", x); + const bool result2 = symbol_table.remove_function("x"); + const bool result3 = symbol_table.remove_function("x"); + + if (!result1 || !result2 || result3) + { + printf("run_test18() - Failed sym_tab add/remove [4]\n"); + } + } + + { + va_func x; + const bool result1 = symbol_table.add_function("x", x); + const bool result2 = symbol_table.remove_vararg_function("x"); + const bool result3 = symbol_table.remove_vararg_function("x"); + + if (!result1 || !result2 || result3) + { + printf("run_test18() - Failed sym_tab add/remove [5]\n"); + } + } + + { + symbol_table.add_function("foo1",foo1); + symbol_table.add_function("foo2",foo2); + symbol_table.add_function("foo3",foo3); + symbol_table.add_function("foo4",foo4); + symbol_table.add_function("foo5",foo5); + symbol_table.add_function("foo6",foo6); + + symbol_table.remove_function("foo1"); + symbol_table.remove_function("foo2"); + symbol_table.remove_function("foo3"); + symbol_table.remove_function("foo4"); + symbol_table.remove_function("foo5"); + symbol_table.remove_function("foo6"); + } + } + { typedef exprtk::expression expression_t; @@ -6448,7 +6785,7 @@ inline bool run_test18() return false; } - T result = expression.value(); + const T result = expression.value(); if (result != T(1)) { @@ -7041,7 +7378,7 @@ inline bool run_test18() continue; } - T result = expression.value(); + const T result = expression.value(); if (result != T(1)) { @@ -7564,7 +7901,7 @@ inline bool run_test19() continue; } - T result = expression.value(); + const T result = expression.value(); if (result_list[i] != result) { @@ -8061,15 +8398,15 @@ inline bool run_test19() { x = static_cast(i); - T result = expression.value(); + const T result = expression.value(); if (not_equal(result,std::sqrt(x),T(0.0000001))) { printf("run_test19() - Computation Error " "Expression: [%s]\tExpected: %12.8f\tResult: %12.8f\n", expression_str.c_str(), - (double)std::sqrt(x), - (double)result); + static_cast(std::sqrt(x)), + static_cast(result)); failure = true; } @@ -8191,7 +8528,7 @@ inline bool run_test19() sum += x; - T result = expression.value(); + const T result = expression.value(); if (result != sum) { diff --git a/exprtk/readme.txt b/exprtk/readme.txt index 4f4911f..8e4dc46 100644 --- a/exprtk/readme.txt +++ b/exprtk/readme.txt @@ -428,8 +428,8 @@ of C++ compilers: | [r0:r1] | The closed interval [r0,r1] of the specified string. | | | eg: Given a string x with a value of 'abcdefgh' then: | | | 1. x[1:4] == 'bcde' | -| | 2. x[ :5] == x[:5] == 'abcdef' | -| | 3. x[3: ] == x[3:] =='cdefgh' | +| | 2. x[ :5] == x[:10 / 2] == 'abcdef' | +| | 3. x[2 + 1: ] == x[3:] =='defgh' | | | 4. x[ : ] == x[:] == 'abcdefgh' | | | 5. x[4/2:3+2] == x[2:5] == 'cdef' | | | | @@ -920,7 +920,7 @@ The above denoted AST will be evaluated in the following order: Generally an expression in ExprTk can be thought of as a free function similar to those found in imperative languages. This form of pseudo function will have a name, it may have a set of one or more inputs and -will return at least one value as its result. Futhermore the function +will return at least one value as its result. Furthermore the function when invoked, may cause a side-effect that changes the state of the host program. @@ -1023,7 +1023,7 @@ 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 -muli-threading et al. +multi-threading et al. The prescribed method for cloning an expression is to compile it from its string form. Doing so will allow the 'user' to properly consider @@ -1315,7 +1315,7 @@ in a statement will cause it to have a side-effect: (b) Invoking a user-defined function that has side-effects The following are examples of expressions where the side-effect status -of the statements (or sub-exressions) within the expressions have been +of the statements (sub-expressions) within the expressions have been noted: +-+----------------------+------------------------------+ @@ -3843,7 +3843,7 @@ follows: } } else - printf("An error occured."); + printf("An error occurred."); (b) collect_functions @@ -3867,7 +3867,7 @@ follows: } } else - printf("An error occured."); + printf("An error occurred."); Note: When either the 'collect_variables' or 'collect_functions' free @@ -3879,10 +3879,10 @@ true. Note: The default interface provided for both the collect_variables and collect_functions free_functions, assumes that expressions will -only be utilising the ExprTk reserved funnctions (eg: abs, cos, min +only be utilising the ExprTk reserved functions (eg: abs, cos, min etc). When user defined functions are to be used in an expression, a symbol_table instance containing said functions can be passed to -either routine, and will be incorparated during the compilation and +either routine, and will be incorporated during the compilation and Dependent Entity Collection processes. In the following example, a user defined free function named 'foo' is registered with a symbol_table. Finally the symbol_table instance and associated @@ -3912,7 +3912,7 @@ expression string are passed to the exprtk::collect_functions routine. } } else - printf("An error occured."); + printf("An error occurred."); (c) compute @@ -4295,9 +4295,11 @@ into account when using ExprTk: function names are case-insensitive. (07) Variable, vector, string variable and function names must begin - with a letter (A-Z or a-z), then can be comprised of any - combination of letters, digits, underscores and dots. (eg: x, - var1 or power_func99, person.age, item.size.0) + with a letter (A-Z or a-z), then can be comprised of any + combination of letters, digits, underscores and dots, ending in + either a letter (A-Z or a-z), digit or underscore. (eg: x, y2, + var1, power_func99, person.age, item.size.0). The associated + regex pattern is: [a-zA-Z]([a-zA-Z0-9_.]*|[a-zA-Z0-9_]) (08) Expression lengths and sub-expression lists are limited only by storage capacity. diff --git a/include/BenchMexce.h b/include/BenchMexce.h new file mode 100644 index 0000000..7906d40 --- /dev/null +++ b/include/BenchMexce.h @@ -0,0 +1,19 @@ +#ifndef BENCH_MEXCE_H +#define BENCH_MEXCE_H + +#include +#include + +#include "Benchmark.h" + +//------------------------------------------------------------------------------------------------- +class BenchMexce : public Benchmark +{ +public: + + BenchMexce(); + + double DoBenchmark(const std::string& sExpr, long iCount); +}; + +#endif diff --git a/math-parser-benchmark-project.pro b/math-parser-benchmark-project.pro index 699ba29..19d02ab 100644 --- a/math-parser-benchmark-project.pro +++ b/math-parser-benchmark-project.pro @@ -42,6 +42,7 @@ SOURCES += \ BenchLepton.cpp \ Benchmark.cpp \ BenchMathExpr.cpp \ + BenchMexce.cpp \ BenchMuParser2.cpp \ BenchMuParserX.cpp \ cpuid.cpp \ @@ -113,6 +114,7 @@ HEADERS += \ BenchLepton.h \ Benchmark.h \ BenchMathExpr.h \ + BenchMexce.h \ BenchMuParser2.h \ BenchMuParserX.h \ cpuid.h \ diff --git a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj index 7691828..3ce4ead 100644 --- a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj +++ b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj @@ -167,6 +167,7 @@ + @@ -199,6 +200,23 @@ + + + + + + + + + + + + + + + + + @@ -384,6 +402,8 @@ + + diff --git a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj.filters b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj.filters index 1510ba7..a9aa86d 100644 --- a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj.filters +++ b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2015.vcxproj.filters @@ -283,6 +283,8 @@ muparsersse + + @@ -843,6 +845,60 @@ libcpuid + + mexce + + + headers + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + + + metl + @@ -898,5 +954,11 @@ {b1b43109-aa45-419d-bf8d-437e1b5cbab8} + + {4c748351-e9ba-4bc2-a4a4-0b5355bc312c} + + + {26e1e6ff-b194-498d-943c-3f7c231e0ce4} + \ No newline at end of file diff --git a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj index b910fd3..fa7ab8b 100644 --- a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj +++ b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj @@ -169,6 +169,7 @@ + @@ -217,6 +218,7 @@ + @@ -403,6 +405,7 @@ + diff --git a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj.filters b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj.filters index 1ff9aaf..6016b1e 100644 --- a/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj.filters +++ b/math-parser-benchmark-project/math-parser-benchmark-project_msvc2017.vcxproj.filters @@ -284,6 +284,7 @@ muparsersse + @@ -895,6 +896,12 @@ headers + + mexce + + + headers + @@ -953,5 +960,8 @@ {121d274d-9553-487e-9d0e-5d05f26a7453} + + {d6cfefbf-e466-4c35-ba0c-2a2bcbf490cb} + \ No newline at end of file diff --git a/math-parser-benchmark-project_msvc2015.sln b/math-parser-benchmark-project_msvc2015.sln index cb31548..8f8dfa1 100644 --- a/math-parser-benchmark-project_msvc2015.sln +++ b/math-parser-benchmark-project_msvc2015.sln @@ -4,7 +4,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 # Visual Studio 2015 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "math-parser-benchmark-project", "math-parser-benchmark-project\math-parser-benchmark-project.vcxproj", "{48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "math-parser-benchmark-project", "math-parser-benchmark-project\math-parser-benchmark-project_msvc2015.vcxproj", "{48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/math-parser-benchmark-project_msvc2017.sln b/math-parser-benchmark-project_msvc2017.sln index 2937314..cd95f9f 100644 --- a/math-parser-benchmark-project_msvc2017.sln +++ b/math-parser-benchmark-project_msvc2017.sln @@ -1,23 +1,23 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 -MinimumVisualStudioVersion = 10.0.40219.1 -# Visual Studio 2015 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "math-parser-benchmark-project", "math-parser-benchmark-project\math-parser-benchmark-project_msvc2017.vcxproj", "{48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Debug|Win32.ActiveCfg = Debug|Win32 - {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Debug|Win32.Build.0 = Debug|Win32 - {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Release|Win32.ActiveCfg = Release|Win32 - {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio 2015 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "math-parser-benchmark-project", "math-parser-benchmark-project\math-parser-benchmark-project_msvc2017.vcxproj", "{48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Debug|Win32.ActiveCfg = Debug|Win32 + {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Debug|Win32.Build.0 = Debug|Win32 + {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Release|Win32.ActiveCfg = Release|Win32 + {48C67E95-2BEA-48EB-8C6A-C30C4ACACADA}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mexce/LICENSE.txt b/mexce/LICENSE.txt new file mode 100644 index 0000000..079d96a --- /dev/null +++ b/mexce/LICENSE.txt @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2020, Ioannis Makris +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mexce/README.md b/mexce/README.md new file mode 100644 index 0000000..c61541f --- /dev/null +++ b/mexce/README.md @@ -0,0 +1,78 @@ +# mexce + +Mini Expression Compiler/Evaluator + +## Overview + +mexce is a small runtime compiler of mathematical expressions, written in C++. It generates machine code that primarily uses the x87 FPU. +It is a single header with no dependencies. + +I wrote this back in 2003 as part of a larger application and then its existence was almost forgotten. The code was now updated with added support for x64 and Data Execution Prevention. + +It currently supports Windows and Linux. + +## Usage + +Here is an example: + +```cpp +float x = 0.0f; +double y = 0.1; +int z = 200; + +mexce::evaluator eval; + +// associate runtime variables with their aliases in the expression +eval.bind(x, "x", y, "y", z, "z"); + +eval.set_expression("0.3+(-sin(2.33+x-logb((.3*pi+(88/y)/e),3.2+z)))/988.472e-02"); + +cout << endl << "Evaluation results:" << endl; +for (int i = 0; i < 10; i++, x-=0.1f, y+=0.212, z+=2) { + cout << " " << eval.evaluate() << endl; +} +``` + +Output: +``` +Evaluation results: + 0.200122 + 0.210523 + 0.224581 + 0.240747 + 0.258237 + 0.276433 + 0.294792 + 0.312816 + 0.330053 + 0.346095 +``` + +## Performance + +Apparently, mexce is quite fast. +Here is how it compares in the [math-parser-benchmark-project](https://github.com/ArashPartow/math-parser-benchmark-project) on an AMD Zen2: + + +| # |Parser | Type | Points | Score |Failures + --------|---------------------|------------|------------|---------|-------- + ***00***|***mexce*** |***double***| ***2630***|***217***|***0*** + 01 | ExprTk | double | 2577| 100 | 0 + 02 | METL | double | 1857| 51 | 0 + 03 | FParser 4.5 | double | 1832| 53 | 2 + 04 | muparser 2.2.4 | double | 1655| 45 | 0 + 05 | muparserSSE | float | 1640| 89 | 58 + 06 | ExprTkFloat | float | 1635| 61 | 62 + 07 | atmsp 1.0.4 | double | 1616| 45 | 4 + 08 | muparser 2.2.4 (omp)| double | 1442| 43 | 0 + 09 | TinyExpr | double | 1183| 37 | 3 + 10 | MathExpr | double | 930| 27 | 12 + 11 | MTParser | double | 836| 29 | 9 + 12 | Lepton | double | 438| 9 | 4 + 13 | muparserx | double | 239| 5 | 0 + +Full results [here](https://github.com/imakris/mexce/blob/master/bench_expr_all_results.txt) + +## License + +The source code of the library is licensed under the Simplified BSD License. diff --git a/mexce/example.cpp b/mexce/example.cpp new file mode 100644 index 0000000..90563ca --- /dev/null +++ b/mexce/example.cpp @@ -0,0 +1,65 @@ +#include +#include "mexce.h" + +int main() +{ + using std::cout; + using std::endl; + + short x0 = 0; + float x = 0.0f; + double y = 0.1; + int z = 200; + + mexce::evaluator eval; + + // associate runtime variables with their aliases in the expression. + eval.bind(x0, "x", y, "y", z, "z"); + + // binding a variable as "log" will fail, as there is a function called "log" + cout << endl << "Attempting to bind 'x' as 'log'" << endl; + try { + eval.bind(x, "log"); + } + catch (std::exception& e) { + cout << " " << e.what() << endl; + } + + // binding a variable as "pi" will fail, as there is a built-in constant called "pi" + cout << endl << "Attempting to bind 'x' as 'pi'" << endl; + try { + eval.bind(x, "pi"); + } + catch (std::exception& e) { + cout << " " << e.what() << endl; + } + + // replaces previous binding of variable x0 to "x" + eval.bind(x, "x"); + + eval.set_expression("0.3+(-sin(2.33+x-logb((.3*pi+(88/y)/e),3.2+z)))/988.472e-02"); + + cout << endl << "Evaluation results:" << endl; + for (int i = 0; i < 10; i++, x-=0.1f, y+=0.212, z+=2) { + cout << " " << eval.evaluate() << endl; // evaluation will use bound variables x, y and z + } + + // attempting to unbind an unknown variable, will throw an exception + cout << endl << "Attempting to unbind w" << endl; + try { + eval.unbind("w"); + } + catch (std::exception& e) { + cout << " " << e.what() << endl; + } + + // Releasing a bound variable which is contained in the assigned expression will + // invalidate the expression. + cout << endl << "Unbinding x" << endl; + eval.unbind("x"); + + cout << endl << "Evaluation result:" << endl; + cout << " " << eval.evaluate() << endl; // Now it will return 0 + + return 0; +} diff --git a/mexce/mexce.h b/mexce/mexce.h new file mode 100644 index 0000000..041728a --- /dev/null +++ b/mexce/mexce.h @@ -0,0 +1,2484 @@ +// +// Mini Expression Compiler/Evaluator +// ================================== +// +// mexce.h +// Author: Ioannis Makris +// +// mexce can compile and evaluate a mathematical expression at runtime. +// The generated machine code will mostly use the x87 FPU. +// +// An example: +// ----------- +// +// float x = 0.0f; +// double y = 0.1; +// int z = 200; +// +// mexce::evaluator eval; +// +// // associate runtime variables with their aliases in the expression. +// eval.bind(x, "x", y, "y", z, "z"); +// +// eval.set_expression("0.3+(-sin(2.33+x-logb((.3*pi+(88/y)/e),3.2+z)))/988.472e-02"); +// +// cout << endl << "Evaluation results:" << endl; +// for (int i = 0; i < 10; i++, x-=0.1f, y+=0.212, z+=2) { +// cout << " " << eval.evaluate() << endl; // evaluation will use bound variables x, y and z +// } +// + +#ifndef MEXCE_INCLUDED +#define MEXCE_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(_M_X64) || defined(__x86_64__) + #define MEXCE_64 +#elif defined(_M_IX86) || defined(__i386__) + #define MEXCE_32 +#else + #error Unknown CPU architecture +#endif + +#ifdef _WIN32 + #include +#elif defined(__linux__) + //#include + #include +#endif + + + +namespace mexce { + + +class evaluator; + + +namespace impl { + + struct Element; + struct Value; + struct Constant; + struct Variable; + struct Function; + struct mexce_charstream; + + using std::abs; + using std::deque; + using std::exception; + using std::get; + using std::list; + using std::logic_error; + using std::make_pair; + using std::make_shared; + using std::make_tuple; + using std::map; + using std::next; + using std::pair; + using std::shared_ptr; + using std::static_pointer_cast; + using std::string; + using std::stringstream; + using std::vector; + + using constant_map_t = map >; + using variable_map_t = map >; + using elist_t = list >; + using elist_it_t = elist_t::iterator; + using elist_const_it_t = elist_t::const_iterator; + + std::shared_ptr make_intermediate_constant(evaluator* ev, double v); + uint8_t* push_intermediate_code(evaluator* ev, const std::string& s); +} + + + +class evaluator +{ +public: + + evaluator(); + ~evaluator(); + + template + void bind(T& referenced_variable, const std::string& variable_name, Args&... args); + + template + void unbind(const std::string& variable_name, Args&... args); + + void unbind_all(); + + void set_expression(std::string); + + double evaluate(); + + double evaluate(const std::string& expression); + +private: + + bool is_constant_expression = false; + double constant_expression_value = 0.0; + size_t m_buffer_size = 0; + std::string m_expression; + impl::elist_t m_elist; + std::list m_intermediate_code; + impl::constant_map_t m_intermediate_constants; // produced during expression simplification + impl::variable_map_t m_variables; + impl::constant_map_t m_constants; + + double (*evaluate_fptr)(); + +#ifdef MEXCE_64 + volatile double m_x64_return_var; +#endif + + void compile_and_finalize_elist(impl::elist_it_t first, impl::elist_it_t last); + + friend + std::shared_ptr impl::make_intermediate_constant(evaluator* ev, double v); + + friend + uint8_t* impl::push_intermediate_code(evaluator* ev, const std::string& s); + + template void bind() {} + template void unbind() {} +}; + + + +class mexce_parsing_exception: public std::exception +{ +public: + explicit mexce_parsing_exception(const std::string& message, size_t position): + m_message(message), + m_position(position) + {} + + virtual ~mexce_parsing_exception() { } + virtual const char* what() const throw() { return m_message.c_str(); } + +protected: + std::string m_message; + size_t m_position; +}; + + +inline +double evaluator::evaluate(const std::string& expression) +{ + evaluator ev; + ev.m_variables = m_variables; + ev.set_expression(expression); + return ev.evaluate(); +} + + + +namespace impl { + + +#ifdef _WIN32 +inline +size_t get_page_size() +{ + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + return system_info.dwPageSize; +} +#endif + + +inline +uint8_t* get_executable_buffer(size_t sz) +{ +#ifdef _WIN32 + static auto const page_size = get_page_size(); + return (uint8_t*)VirtualAlloc(nullptr, page_size, MEM_COMMIT, PAGE_READWRITE); +#elif defined(__linux__) + return (uint8_t*)mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif +} + + +inline +double (*lock_executable_buffer(uint8_t* buffer, size_t sz))() +{ +#ifdef _WIN32 + DWORD dummy; + VirtualProtect(buffer, sz, PAGE_EXECUTE_READ, &dummy); +#elif defined(__linux__) + if (mprotect((void*) buffer, sz, PROT_EXEC) != 0) { + buffer = 0; + } +#endif + return reinterpret_cast(buffer); +} + + +inline +void free_executable_buffer(double (*buffer)(), size_t sz) +{ + if (!buffer) { + return; + } +#ifdef _WIN32 + VirtualFree( (void*) buffer, 0, MEM_RELEASE); +#elif defined(__linux__) + munmap((void *) buffer, sz); +#endif +} + + +enum Numeric_data_type +{ + M16INT, + M32INT, + M64INT, + M32FP, + M64FP, +}; + + +enum Element_type +{ + CCONST, + CVAR, + CFUNC +}; + + +enum Token_type +{ + UNDEFINED_TOKEN_TYPE, + NUMERIC_LITERAL, + CONSTANT_NAME, + VARIABLE_NAME, + FUNCTION_NAME, + INFIX_1, // infix operator, with priority 1 ( '^', i.e. power ) + INFIX_2, // infix operator, with priority 2 ( '*' and '/' ) + INFIX_3, // infix operator, with priority 3 ( '+' and '-' ) + INFIX_4, // infix operator, with priority 4 ( '<' ) + RIGHT_PARENTHESIS, + LEFT_PARENTHESIS, + COMMA, + FUNCTION_RIGHT_PARENTHESIS, + FUNCTION_LEFT_PARENTHESIS, + UNARY +}; + + + +inline +string double_to_hex( double v ) +{ + uint64_t* u64p = (uint64_t*)&v; + + stringstream stream; + stream << "0x" + << std::setfill ('0') << std::setw(sizeof(*u64p)*2) + << std::hex << *u64p; + return stream.str(); +} + + + +struct Element +{ + Element_type element_type; + Element(Element_type ct): element_type(ct) {} +}; + + +struct Value: public Element +{ + volatile void* address; + Numeric_data_type numeric_data_type; + string name; + + Value(volatile void * address, + Numeric_data_type numeric_data_type, + Element_type element_type, + string name) + : + Element ( element_type ), + address ( address ), + numeric_data_type ( numeric_data_type ), + name ( name ) {} +}; + + +struct Constant: public Value +{ + Constant(string num, string name): + Value( (volatile void *) &internal_constant, M64FP, CCONST, name), + internal_constant(atof(num.data())) + {} + + Constant(double num): + Value( (volatile void *) &internal_constant, M64FP, CCONST, double_to_hex(num)), + internal_constant(num) + {} + + Constant(const Constant& rhs): + Value(rhs), + internal_constant(rhs.internal_constant) + { + address = (void*)&internal_constant; + } + + double get_data_as_double() const { + switch (this->numeric_data_type) { + case M16INT: return (double)*((int16_t*)address); + case M32INT: return (double)*((int32_t*)address); + case M64INT: return (double)*((int64_t*)address); + case M32FP: return (double)*((float* )address); + case M64FP: return *((double* )address); + default: return 0.0; + } + } + +private: + const double internal_constant; +}; + + +struct Variable: public Value +{ + bool referenced; + + Variable(volatile void * addr, string name, Numeric_data_type numeric_data_type): + Value(addr, numeric_data_type, CVAR, name), referenced(false) + {} +}; + + +struct Function: public Element +{ + using optimizer_t = void (*)(elist_it_t, evaluator*, elist_t*); + + size_t stack_req; + string name; + size_t num_args; + + vector args; + elist_it_t parent; + size_t parent_arg_index = size_t(~0); // index in postfix order (inverted), i.e. arg1-arg0 + + list absorbed[2]; + + string code; + optimizer_t optimizer; + + bool force_not_constant = false; + + Function( + const string& name, + size_t num_args, + size_t sreq, + size_t size, + uint8_t *code_buffer, + optimizer_t optimizer = 0) + : + Element ( CFUNC ), + stack_req ( sreq ), + name ( name ), + num_args ( num_args ), + args ( vector(num_args) ), + code ( (char*)code_buffer, size ), + optimizer ( optimizer ) {} +}; + + +struct mexce_charstream { stringstream s; }; + +template +mexce_charstream& operator << (mexce_charstream &s, T data) { + s.s.write((char*)&data, sizeof(T)); + return s; +} + +inline +mexce_charstream& operator < (mexce_charstream &s, int v) { + char ch = (char)v; + s.s.write(&ch, 1); + return s; +} + + + +inline +shared_ptr make_intermediate_constant(evaluator* ev, double v) +{ + auto sc = make_shared(v); + auto it = ev->m_intermediate_constants.find(sc->name); + if (it == ev->m_intermediate_constants.end()) { + ev->m_intermediate_constants[sc->name] = sc; + } + else { + sc = it->second; // releases the constant we just created + } + return sc; +} + + + +inline +uint8_t* push_intermediate_code(evaluator* ev, const string& s) +{ + ev->m_intermediate_code.push_back(string()); + ev->m_intermediate_code.back() = s; + return (uint8_t*)&ev->m_intermediate_code.back()[0]; +} + + + +inline +void link_arguments(elist_t& elist) +{ + vector evec; + for (auto y = elist.begin(); y != elist.end(); y++) { + if ((*y)->element_type == CFUNC) { + auto f = static_pointer_cast(*y); + f->parent = elist.end(); + for (size_t i = 0; i < f->num_args; i++) { + f->args[i] = evec.back(); + + if ((*f->args[i])->element_type == CFUNC) { + auto cf = static_pointer_cast(*f->args[i]); + cf->parent = y; + cf->parent_arg_index = i; // postfix order (inverted) + } + evec.pop_back(); + } + } + evec.push_back(y); + } +} + + +inline +Token_type get_infix_rank(char infix_op) +{ + switch (infix_op) { + case '<': return INFIX_4; + case '+': + case '-': return INFIX_3; + case '*': + case '/': return INFIX_2; + case '^': return INFIX_1; + } + + assert(false); + return UNDEFINED_TOKEN_TYPE; +} + + +inline Function Sin() +{ + static uint8_t code[] = { + 0xd9, 0xfe // fsin + }; + return Function("sin", 1, 0, sizeof(code), code); +} + + +inline Function Cos() +{ +#ifndef MEXCE_ACCURACY + static uint8_t code[] = { + 0xd9, 0xff // fcos + }; +#else + + static uint64_t mfactors[] = { // Maclaurin expansion factors (80-bit) + +#if (MEXCE_ACCURACY > 9) + 0x9c9962823eb07306, 0x000000000000bf93, // - 1 / (30!), +#endif +#if (MEXCE_ACCURACY > 8) + 0x850c5131a842e9ba, 0x0000000000003f9d, // 1 / (28!), +#endif +#if (MEXCE_ACCURACY > 7) + 0xc4742fe35272cd1c, 0x000000000000bfa6, // - 1 / (26!), +#endif +#if (MEXCE_ACCURACY > 6) + 0xf96780cb97abbe65, 0x0000000000003faf, // 1 / (24!), +#endif +#if (MEXCE_ACCURACY > 5) + 0x8671cb6dbfc294a3, 0x000000000000bfb9, // - 1 / (22!), +#endif +#if (MEXCE_ACCURACY > 4) + 0xf2a15d201011283d, 0x0000000000003fc1, // 1 / (20!), +#endif +#if (MEXCE_ACCURACY > 3) + 0xb413c31dcbecbbde, 0x000000000000bfca, // - 1 / (18!), +#endif +#if (MEXCE_ACCURACY > 2) + 0xd73f9f399dc0f88f, 0x0000000000003fd2, // 1 / (16!), +#endif +#if (MEXCE_ACCURACY > 1) + 0xc9cba54603e4e906, 0x000000000000bfda, // - 1 / (14!), +#endif +#if (MEXCE_ACCURACY > 0) + 0x8f76c77fc6c4bdaa, 0x0000000000003fe2, // 1 / (12!), +#endif + 0x93f27dbbc4fae397, 0x000000000000bfe9, // - 1 / (10!), + 0xd00d00d00d00d00d, 0x0000000000003fef, // 1 / ( 8!), + 0xb60b60b60b60b60b, 0x000000000000bff5, // - 1 / ( 6!), + 0xaaaaaaaaaaaaaaab, 0x0000000000003ffa, // 1 / ( 4!), + 0x8000000000000000, 0x000000000000bffe, // - 1 / ( 2!), + 0x8000000000000000, 0x0000000000003fff // 1 + }; + + static uint8_t code[] = { + 0xd9, 0xeb, // fldpi } + 0xdc, 0xc0, // fadd st(0),st } + 0xd9, 0xc9, // fxch st(1) } bring arg to range [0, 2*pi) + 0xd9, 0xf5, // fprem1 } + 0xdd, 0xd9, // fstp st(1) } + + 0xdc, 0xc8, // fmul st(0),st + 0xb8, 0, 0, 0, 0, // mov eax, dword ptr [addr_fct26] + 0xdb, 0x28, // fld tword ptr [eax] + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x10, // fld tword ptr [eax+8] + 0xde, 0xc1, // faddp st(1),st + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x20, // fld tword ptr [eax+10h] + 0xde, 0xc1, // faddp st(1),st + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x30, // fld tword ptr [eax+18h] + 0xde, 0xc1, // faddp st(1),st + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x40, // fld tword ptr [eax+20h] + 0xde, 0xc1, // faddp st(1),st + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x50, // fld tword ptr [eax+28h] + 0xde, 0xc1, // faddp st(1),st + +#if (MEXCE_ACCURACY > 0) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x60, // fld tword ptr [eax+30h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 1) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x70, // fld tword ptr [eax+38h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 2) + 0xd8, 0xc9, // fmul st,st(1) + 0x05, 0x80, 0x00, 0x00, 0x00, // add eax, 80h + 0xdb, 0x28, // fld tword ptr [eax] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 3) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x10, // fld tword ptr [eax+10h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 4) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x20, // fld tword ptr [eax+20h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 5) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x30, // fld tword ptr [eax+30h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 6) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x40, // fld tword ptr [eax+40h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 7) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x50, // fld tword ptr [eax+50h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 8) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x60, // fld tword ptr [eax+60h] + 0xde, 0xc1, // faddp st(1),st +#endif +#if (MEXCE_ACCURACY > 9) + 0xd8, 0xc9, // fmul st,st(1) + 0xdb, 0x68, 0x70, // fld tword ptr [eax+70h] + 0xde, 0xc1, // faddp st(1),st +#endif + 0xdd, 0xd9 // fstp st(1) + }; + + *((void**)(code+13)) = (void*)mfactors; +#endif + + return Function("cos", 1, 0, sizeof(code), code); +} + + +inline Function Tan() +{ + static uint8_t code[] = { + 0xd9, 0xf2, // fptan + 0xdd, 0xd8 // fstp st(0) + }; + return Function("tan", 1, 1, sizeof(code), code); +} + + +inline Function Abs() +{ + static uint8_t code[] = { + 0xd9, 0xe1 // fabs + }; + return Function("abs", 1, 0, sizeof(code), code); +} + + +inline Function Sfc() +{ + static uint8_t code[] = { + 0xd9, 0xf4, // fxtract + 0xdd, 0xd9 // fstp st(1) + }; + return Function("sfc", 1, 1, sizeof(code), code); +} + + +inline Function Expn() +{ + static uint8_t code[] = { + 0xd9, 0xf4, // fxtract + 0xdd, 0xd8 // fstp st(0) + }; + return Function("expn", 1, 1, sizeof(code), code); +} + + +inline Function Sign() +{ + static uint8_t code[] = { + 0xd9, 0xee, // fldz + 0xdf, 0xf1, // fcomip st, st(1) + 0xdd, 0xd8, // fstp st(0) + 0xd9, 0xe8, // fld1 + 0xd9, 0xe8, // fld1 + 0xd9, 0xe0, // fchs + 0xda, 0xc1, // fcmovb st, st(1) + 0xdd, 0xd9 // fstp st(1) + }; + return Function("sign", 1, 1, sizeof(code), code); +} + + +inline Function Signp() +{ + static uint8_t code[] = { + 0xd9, 0xe8, // fld1 + 0xd9, 0xee, // fldz + 0xdb, 0xf2, // fcomi st, st(2) + 0xdd, 0xda, // fstp st(2) + 0xdb, 0xc1, // fcmovnb st, st(1) + 0xdd, 0xd9 // fstp st(1) + }; + return Function("signp", 1, 2, sizeof(code), code); +} + + +inline Function Sqrt() +{ + static uint8_t code[] = { + 0xd9, 0xfa // fsqrt + }; + return Function("sqrt", 1, 0, sizeof(code), code); +} + + +inline +void pow_optimizer(elist_it_t it, evaluator* ev, elist_t* elist) +{ + auto f = static_pointer_cast(*it); + + if ((*f->args[0])->element_type == CCONST) { + auto v = static_pointer_cast(*f->args[0]); + + double v_d = v->get_data_as_double(); + double r_d = round(v_d); + double a_d = abs(v_d); + + bool matched = true; + mexce_charstream s; + + // a special case, that the exponent is 0.5 + if (v_d == 0.5) { + s < 0xd9 < 0xfa; // fsqrt + } + else + if (r_d == v_d && a_d <= 65536.0) { + + // find the closest power of two + uint32_t npo2 = (uint32_t)a_d; + npo2--; + npo2 |= npo2 >> 1; + npo2 |= npo2 >> 2; + npo2 |= npo2 >> 4; + npo2 |= npo2 >> 8; + npo2 |= npo2 >> 16; + npo2++; + npo2>>=1; + + double diff_high = npo2*2 - a_d; + double diff_low = a_d - npo2; + + if (a_d == 0.0) { + s < 0xdd < 0xd8 // fstp st(0) + < 0xd9 < 0xe8; // fld1 + } + else + if (a_d == 1.0) { + // do nothing + } + else + if (diff_high < 28 && diff_low < 28) { + if (diff_high && diff_low) { + // the exponent is not an exact po2, thus we will have to multiply + // or divide to get to the result, thus we keep the base in st(1) + s < 0xd9 < 0xc0; // fld st(0) + } + while (npo2 >>= 1) { // multiply to reach npo2 + s < 0xdc < 0xc8; // fmul st(0) + } + + if (diff_high < diff_low) { + s < 0xdc < 0xc8; // fmul st(0) + + // divide as many times as the difference + // and then get rid of the temporary + if (diff_high > 0.0) { + while (--diff_high) { + s < 0xd8 < 0xf1; // fdivr st(0), st(1) + } + s < 0xde < 0xf1; // fdivrp st(1), st(0) + } + } + else { + // multiply as many times as the difference + // and then get rid of the temporary + if (diff_low > 0) { + while (--diff_low) { + s < 0xd8 < 0xc9; // fmul st(0), st(1) + } + s < 0xde < 0xc9; // fmulp st(1), st(0) + } + } + + if (diff_high && diff_low) { + // now we get rid of the temporary we used earlier + s < 0xde < 0xc9; // fmulp st(1), st(0) + } + } + else { + matched = false; + } + + if (matched && v_d < 0) { + s < 0xd9 < 0xe8 // fld1 + < 0xde < 0xf1; // fdivrp st(1),st // inverse + } + } + else { + matched = false; + } + + if (!matched) { + // this is almost the generic pow, except that it does not try to figure out + // if the exponent is an integer + + s < 0xd9 < 0xc9 // fxch } + < 0xd9 < 0xe4 // ftst } + < 0x9b // wait } if base is 0, leave it in st(0) + < 0xdf < 0xe0 // fnstsw ax } and exit + < 0x9e // sahf } + < 0x74 < 0x14 // je store_and_exit } + < 0xd9 < 0xe1 // fabs + < 0xd9 < 0xf1 // fyl2x } + < 0xd9 < 0xe8 // fld1 } + < 0xd9 < 0xc1 // fld st(1) } + < 0xd9 < 0xf8 // fprem } b^n = 2^(n*log2(b)) + < 0xd9 < 0xf0 // f2xm1 } + < 0xde < 0xc1 // faddp st(1), st } + < 0xd9 < 0xfd // fscale } + < 0x77 < 0x02 // ja store_and_exit + < 0xd9 < 0xe0 // fchs +// store_and_exit: + < 0xdd < 0xd9; // fstp st(1) + } + + + uint8_t* cc = push_intermediate_code(ev, s.s.str()); + auto f_opt = make_shared("pow_opt", 2-matched, 0, s.s.str().size(), cc, nullptr); + + if (matched) { + f_opt->args[0] = f->args[1]; + elist->erase(f->args[0]); + } + else { + f_opt->args[0] = f->args[0]; + f_opt->args[1] = f->args[1]; + } + *it = f_opt; + } +} + + +inline Function Pow() +{ + static uint8_t code[] = { + 0xd9, 0xc0, // fld st(0) } + 0xd9, 0xfc, // frndint } + 0xd8, 0xd1, // fcom st(1) } if (abs(exponent) != round(abs(exponent))) + 0xdf, 0xe0, // fnstsw ax } goto generic_pow; + 0x9e, // sahf } + 0x75, 0x3c, // jne pop_before_generic_pow } + + 0xd9, 0xe1, // fabs } + 0x66, 0xc7, 0x44, 0x24, 0xfe, 0xff, 0xff, // mov word ptr [esp-2],0ffffh } + 0xdf, 0x5c, 0x24, 0xfe, // fistp word ptr [esp-2] } + 0x66, 0x8b, 0x44, 0x24, 0xfe, // mov ax, word ptr [esp-2] } if (abs(exponent) > 32) + 0x66, 0x83, 0xe8, 0x01, // sub ax, 1 } goto generic_pow; + 0x66, 0x83, 0xf8, 0x21, // cmp ax, 1fh } + 0x77, 0x22, // ja generic_pow } + + 0xd9, 0xc1, // fld st(1) +// loop_start: + 0x66, 0x85, 0xc0, // test ax, ax + 0x74, 0x08, // je loop_end + 0xdc, 0xca, // fmul st(2), st + 0x66, 0x83, 0xe8, 0x01, // sub ax, 1 + 0xeb, 0xf3, // jmp loop_start + +// loop_end: + + 0xdd, 0xd8, // fstp st(0) } + 0xd9, 0xe4, // ftst } + 0xdf, 0xe0, // fnstsw ax } if the exponent was NOT negative + 0x9e, // sahf } goto exit_point + 0xdd, 0xd8, // fstp st(0) } + 0x77, 0x36, // ja exit_point } + + 0xd9, 0xe8, // fld1 } + 0xde, 0xf1, // fdivrp st(1),st } inverse + 0xeb, 0x30, // jmp exit_point } + +// pop_before_generic_pow: + 0xdd, 0xd8, // fstp st(0) +// generic_pow: + 0xd9, 0xe4, // ftst } + 0xdf, 0xe0, // fnstsw ax } + 0x9e, // sahf } + 0x75, 0x08, // jne non_zero_exponent } if exponent is 0 + 0xdd, 0xd8, // fstp st(0) } return 1 + 0xdd, 0xd8, // fstp st(0) } + 0xd9, 0xe8, // fld1 } + 0xeb, 0x1f, // jmp exit_point } +// non_zero_exponent: + 0xd9, 0xc9, // fxch } + 0xd9, 0xe4, // ftst } + 0xdf, 0xe0, // fnstsw ax } if base is 0, leave it in st(0) + 0x9e, // sahf } and exit + 0x74, 0x14, // je store_and_exit } + 0xd9, 0xe1, // fabs + 0xd9, 0xf1, // fyl2x } + 0xd9, 0xe8, // fld1 } + 0xd9, 0xc1, // fld st(1) } + 0xd9, 0xf8, // fprem } b^n = 2^(n*log2(b)) + 0xd9, 0xf0, // f2xm1 } + 0xde, 0xc1, // faddp st(1), st } + 0xd9, 0xfd, // fscale } + 0x77, 0x02, // ja store_and_exit + 0xd9, 0xe0, // fchs +// store_and_exit: + 0xdd, 0xd9, // fstp st(1) +// exit_point: + }; + return Function("pow", 2, 1, sizeof(code), code, pow_optimizer); +} + + +inline Function Exp() +{ + static uint8_t code[] = { + 0xd9, 0xea, // fldl2e + 0xde, 0xc9, // fmulp st(1), st + 0xd9, 0xe8, // fld1 + 0xd9, 0xc1, // fld st(1) + 0xd9, 0xf8, // fprem + 0xd9, 0xf0, // f2xm1 + 0xde, 0xc1, // faddp st(1), st + 0xd9, 0xfd, // fscale + 0xdd, 0xd9, // fstp st(1) + }; + return Function("exp", 1, 1, sizeof(code), code); +} + + +inline Function Logb() // implementation with base +{ + static uint8_t code[] = { + 0xd9, 0xe8, // fld1 + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf1, // fyl2x + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xe8, // fld1 + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf1, // fyl2x + 0xde, 0xf9 // fdivp st(1),st + }; + return Function("logb", 2, 1, sizeof(code), code); +} + + +inline Function Ln() +{ + static uint8_t code[] = { + 0xd9, 0xe8, // fld1 + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf1, // fyl2x + 0xd9, 0xea, // fldl2e + 0xde, 0xf9 // fdivp st(1),st + }; + return Function("ln", 1, 1, sizeof(code), code); +} + + +// this is an alias, because of C's math.h +inline Function Log() +{ + Function f = Ln(); + f.name="log"; + return f; +} + + +inline Function Log10() +{ + static uint8_t code[] = { + 0xd9, 0xe8, // fld1 + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf1, // fyl2x + 0xd9, 0xe9, // fldl2t + 0xde, 0xf9 // fdivp st(1),st + }; + return Function("log10", 1, 1, sizeof(code), code); +} + + +inline Function Log2() +{ + static uint8_t code[] = { + 0xd9, 0xe8, // fld1 + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf1 // fyl2x + }; + return Function("log2", 1, 0, sizeof(code), code); +} + + +inline Function Ylog2() +{ + static uint8_t code[] = { + 0xd9, 0xf1 // fyl2x + }; + return Function("ylog2", 2, 0, sizeof(code), code); +} + + +inline Function Max() +{ + static uint8_t code[] = { + 0xdb, 0xf1, // fcomi st,st(1) + 0xda, 0xc1, // fcmovb st,st(1) + 0xdd, 0xd9 // fstp st(1) + }; + return Function("max", 2, 0, sizeof(code), code); +} + + +inline Function Min() +{ + static uint8_t code[] = { + 0xdb, 0xf1, // fcomi st,st(1) + 0xd9, 0xc9, // fxch st(1) + 0xda, 0xc1, // fcmovb st,st(1) + 0xdd, 0xd9 // fstp st(1) + }; + return Function("min", 2, 0, sizeof(code), code); +} + + +inline Function Floor() +{ + static uint8_t code[] = { + 0x66, 0xc7, 0x44, 0x24, 0xfc, 0x7f, 0x06, // mov word ptr [esp-4], 67fh + 0xd9, 0x7c, 0x24, 0xfe, // fnstcw word ptr [esp-2] + 0xd9, 0x6c, 0x24, 0xfc, // fldcw word ptr [esp-4] + 0xd9, 0xfc, // frndint + 0xd9, 0x6c, 0x24, 0xfe // fldcw word ptr [esp-2] + }; + return Function("floor", 1, 0, sizeof(code), code); +} + + +inline Function Ceil() +{ + static uint8_t code[] = { + 0x66, 0xc7, 0x44, 0x24, 0xfc, 0x7f, 0x0a, // mov word ptr [esp-4], a7fh + 0xd9, 0x7c, 0x24, 0xfe, // fnstcw word ptr [esp-2] + 0xd9, 0x6c, 0x24, 0xfc, // fldcw word ptr [esp-4] + 0xd9, 0xfc, // frndint + 0xd9, 0x6c, 0x24, 0xfe // fldcw word ptr [esp-2] + }; + return Function("ceil", 1, 0, sizeof(code), code); +} + + +inline Function Round() +{ + static uint8_t code[] = { + + // NOTE: In this case, saving/restoring the control word is most likely redundant. + + 0x66, 0xc7, 0x44, 0x24, 0xfc, 0x7f, 0x02, // mov word ptr [esp-4], 27fh + 0xd9, 0x7c, 0x24, 0xfe, // fnstcw word ptr [esp-2] + 0xd9, 0x6c, 0x24, 0xfc, // fldcw word ptr [esp-4] + 0xd9, 0xfc, // frndint + 0xd9, 0x6c, 0x24, 0xfe // fldcw word ptr [esp-2] + }; + return Function("round", 1, 0, sizeof(code), code); +} + + +inline Function Int() +{ + static uint8_t code[] = { + 0xd9, 0xfc // frndint + }; + return Function("int", 1, 0, sizeof(code), code); +} + + +inline Function Mod() +{ + static uint8_t code[] = { + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf8, // fprem + 0xdd, 0xd9 // fstp st(1) + }; + return Function("mod", 2, 0, sizeof(code), code); +} + + +inline Function Less_than() +{ + static uint8_t code[] = { + 0xdf, 0xf1, // fcomip st,st(1) + 0xdd, 0xd8, // fstp st(0) + 0xd9, 0xe8, // fld1 + 0xd9, 0xee, // fldz + 0xdb, 0xd1, // fcmovnb st,st(1) + 0xdd, 0xd9, // fstp st(1) + }; + return Function("less_than", 2, 0, sizeof(code), code); +} + + +inline Function Bnd() +{ + static uint8_t code[] = { + 0xd9, 0xc9, // fxch st(1) + 0xd9, 0xf8, // fprem + 0xd9, 0xc0, // fld st(0) + 0xdc, 0xc2, // fadd st(2), st + 0xd9, 0xee, // fldz + 0xdf, 0xf1, // fcomip st,st(1) + 0xdd, 0xd8, // fstp st(0) + 0xdb, 0xc1, // fcmovnb st,st(1) + 0xdd, 0xd9 // fstp st(1) + }; + return Function("bnd", 2, 2, sizeof(code), code); +} + + +inline +pair get_dependent_chunk(elist_it_t it) +{ + auto it_end = next(it); + while ( (*it) ->element_type == CFUNC) { + shared_ptr f = static_pointer_cast( *it ); + if (!f->args.size()) { + break; + } + it = f->args[f->args.size()-1]; + } + return make_pair(it, it_end); +} + + +struct elist_comparison { + bool operator()(const elist_t& a, const elist_t& b) const { + + // empty element lists cannot exist + assert(a.size() && b.size()); + + // if their size differs, we use that for comparison + if (a.size() != b.size()) { + return a.size() > b.size(); + } + + // they are the same size, thus we need to traverse both + auto ita = a.begin(); + auto itb = b.begin(); + for (; ita != a.end(); ita++, itb++) { + // start by comparing element types + if ((*ita)->element_type != (*itb)->element_type) { + return (*ita)->element_type > (*itb)->element_type; + } + + // if they are constants, we compare the values + if ((*ita)->element_type == CCONST) { + double da = static_pointer_cast(*ita)->get_data_as_double(); + double db = static_pointer_cast(*itb)->get_data_as_double(); + if (da != da && db != db) { // i.e. if both of them are NaN + // not all NaNs are the same, we compare binary + auto mcr = memcmp(&da, &db, sizeof(double)); + if (mcr != 0) { + return mcr < 0; + } + } + else + if (da != db) { + return da < db; + } + continue; // it is the same... move on + } + + // if they are variables, we compare addresses + if ((*ita)->element_type == CVAR) { + volatile void* aa = static_pointer_cast(*ita)->address; + volatile void* ab = static_pointer_cast(*itb)->address; + if (aa != ab) { + return aa < ab; + } + continue; // it is the same... move on + } + + // if they are functions, we compare their code + if ((*ita)->element_type == CFUNC) { + string fna = static_pointer_cast(*ita)->code; + string fnb = static_pointer_cast(*itb)->code; + + if (fna != fnb) { + return fna < fnb; + } + continue; // it is the same... move on [this one is not really required] + } + } + + return false; // they are equal + } +}; + + + +template +void emit_apply_op_with_value(impl::mexce_charstream& s, shared_ptr v) +{ + using namespace impl; +#ifdef MEXCE_64 + s < 0x48 < 0xb8; // mov rax, qword ptr +#else + s < 0xb8; // mov eax, dword ptr +#endif + s << v->address; // [the address] + switch(v->numeric_data_type) { + case M16INT: s < 0xde < OP; break; // f[OP] word ptr [eax/rax] + case M32INT: s < 0xda < OP; break; // f[OP] dword ptr [eax/rax] + case M32FP: s < 0xd8 < OP; break; // f[OP] dword ptr [eax/rax] + case M64FP: s < 0xdc < OP; break; // f[OP] qword ptr [eax/rax] + + // the FPU has limited support for 64-bit integers and M64INT cannot be supported here + default: assert(false); + } +} + + +template +void emit_apply_op_with_constant(evaluator* ev, impl::mexce_charstream& s, double v) +{ + emit_apply_op_with_value(s, impl::make_intermediate_constant(ev, v)); +} + + + +inline +void emit_load_constant(evaluator* ev, impl::mexce_charstream& s, double v) +{ + using namespace impl; + + double av = abs(v); + bool hit = false; + + if (av == 0.0) { s < 0xd9 < 0xee; hit = true; } else // fldz + if (av == 1.0) { s < 0xd9 < 0xe8; hit = true; } else // fld1 + if (av == 3.1415926535897932384626433) { s < 0xd9 < 0xeb; hit = true; } else // fldpi + if (av == 3.32192809488736234787) { s < 0xd9 < 0xe9; hit = true; } else // fldl2t + if (av == 1.44269504088896340736) { s < 0xd9 < 0xea; hit = true; } else // fldl2e + if (av == 0.3010299956639811952137) { s < 0xd9 < 0xec; hit = true; } else // fldlg2 + if (av == 0.6931471805599453094172) { s < 0xd9 < 0xed; hit = true; } // fldln2 + + if (hit) { + if (v < 0) { + s < 0xd9 < 0xe0; // fchs + } + return; + } + + auto sc = make_intermediate_constant(ev, v); + +#ifdef MEXCE_64 + s < 0x48 < 0xb8; // mov rax, qword ptr + s << (void*)(sc->address); + s < 0xdd < 0x00; // fld [rax] +#else + s < 0xdd < 0x05; // fld [immediate address] + s << (void*)(sc->address); +#endif +} + + + +inline +void compile_elist(impl::mexce_charstream& code_buffer, const impl::elist_const_it_t first, const impl::elist_const_it_t last) +{ + using namespace impl; + + elist_const_it_t it = first; + + for (; it != last; it++) { + if ((*it)->element_type == CVAR || + (*it)->element_type == CCONST) + { + Value * tn = (Value *) it->get(); + +#ifdef MEXCE_64 + code_buffer << (uint16_t)0xb848; // move input address to rax (opcode) + code_buffer << (void*)tn->address; +#endif + + switch (tn->numeric_data_type) { +#ifdef MEXCE_64 + // On x64, variable addresses are already supplied in rax. + case M32FP: code_buffer < 0xd9 < 0x00; break; + case M64FP: code_buffer < 0xdd < 0x00; break; + case M16INT: code_buffer < 0xdf < 0x00; break; + case M32INT: code_buffer < 0xdb < 0x00; break; + case M64INT: code_buffer < 0xdf < 0x28; break; +#else + // On 32-bit x86, variable addresses are explicitly specified. + case M32FP: code_buffer < 0xd9 < 0x05; break; + case M64FP: code_buffer < 0xdd < 0x05; break; + case M16INT: code_buffer < 0xdf < 0x05; break; + case M32INT: code_buffer < 0xdb < 0x05; break; + case M64INT: code_buffer < 0xdf < 0x2d; break; +#endif + } + +#ifndef MEXCE_64 + code_buffer << (void*)(tn->address); +#endif + } + else { + Function * tf = (Function *) it->get(); + code_buffer.s.write(tf->code.data(), tf->code.size()); + } + } +} + + + +inline shared_ptr make_function(const string& name); + + +inline string infix_operator_to_function_name(const string& op) +{ + static map op_map = { + { "+", "add" }, + { "-", "sub" }, + { "*", "mul" }, + { "/", "div" }, + { "^", "pow" }, + { "<", "less_than" } + }; + auto it = op_map.find(op); + assert(it != op_map.end()); + return it->second; +} + + +inline string function_name_to_infix_operator(const string& fn) +{ + static map op_map = { + { "add", "+" }, + { "sub", "-" }, + { "mul", "*" }, + { "div", "/" }, + { "pow", "^" }, + { "less_than", "<" } + }; + auto it = op_map.find(fn); + if (it == op_map.end()) { + return ""; + } + return it->second; +} + + +inline string function_name_to_unary_operator(const string& fn) +{ + if (fn == "neg") + return "-"; + return ""; +} + + +inline +string double_to_pretty_string(double v) +{ + stringstream ss; + ss << v; + return ss.str(); +} + + +inline +string elist_to_string(const elist_t& elist) +{ + + deque>> st; + + // root element appears as an unnamed function of 1 argument + st.push_back(make_tuple(string(), 2, vector{""})); + + for (auto it = elist.rbegin(); it != elist.rend(); it++) { + auto e = *it; + if (e->element_type == CFUNC) { + auto f = static_pointer_cast(e); + st.push_back(make_tuple(string(), f->args.size() + 1, vector{f->name} )); + } + else + if (e->element_type == CCONST) { + auto c = static_pointer_cast(e); + get<2>(st.back()).push_back( double_to_pretty_string(c->get_data_as_double()) ); + } + else + if (e->element_type == CVAR) { + auto v = static_pointer_cast(e); + get<2>(st.back()).push_back( v->name ); + } + + while ( get<2>(st.back()).size() == get<1>(st.back()) ) { + string& lrs = get<0>(st.back()); + vector& lrv = get<2>(st.back()); + + string symbolic_op; + if ( (get<1>(st.back()) == 2) && !(symbolic_op = function_name_to_unary_operator(lrv[0])).empty() ) { + lrs = string("(") + symbolic_op + lrv.back() + ")"; + } + else + if ( (get<1>(st.back()) == 3) && !(symbolic_op = function_name_to_infix_operator(lrv[0])).empty() ) { + lrs = string("(") + lrv.back() + symbolic_op; + lrv.pop_back(); + lrs += lrv.back() + ")"; + } + else { + lrs += lrv[0] + "("; + if (lrv.size() != 1) { + while (true) { + lrs += lrv.back(); + lrv.pop_back(); + if (lrv.size() == 1) { + break; + } + lrs += ", "; + } + } + lrs += ")"; + } + + if (st.size()!=1) { + string tmp = lrs; + st.pop_back(); + get<2>(st.back()).push_back(tmp); + } + } + } + + assert(st.size() == 1); + string ret = get<0>(st.back()); + return ret.substr(1, ret.size()-2); +} + + +inline +void asmd_optimizer(elist_it_t it, evaluator* ev, elist_t* elist) +{ + auto f = static_pointer_cast(*it); + auto fname = string(f->name); + int fclass = (fname == "add" || fname == "sub") ? 1 : (fname == "mul" || fname == "div") ? 2 : 0; + assert(fclass); + + double neutral = fclass==1 ? 0.0 : 1.0; + + bool arg2_inv = (fname == "sub" || fname == "div"); + + if (f->parent != elist->end() && (*f->parent)->element_type == CFUNC) { + shared_ptr pf = static_pointer_cast(*f->parent); + auto pname = string(pf->name); + int pclass = (pname == "add" || pname == "sub") ? 1 : (pname == "mul" || pname == "div") ? 2 : 0; + bool parg2_inv = (pname == "sub" || pname == "div"); + + if (pclass && pclass == fclass) { + + // the function will be absorbed by its parent + + bool parent_inv_op = f->parent_arg_index == 0 && parg2_inv; // arg 0 in postfix, is the second infix argument + + // this is the first infix argument (postfix order is inverse) + auto arg1_chunk = get_dependent_chunk(f->args[1]); + pf->absorbed[parent_inv_op ].push_back(elist_t(arg1_chunk.first, arg1_chunk.second)); + elist->erase(arg1_chunk.first, arg1_chunk.second); + + // the second infix argument + auto arg0_chunk = get_dependent_chunk(f->args[0]); + pf->absorbed[parent_inv_op ^ arg2_inv].push_back(elist_t(arg0_chunk.first, arg0_chunk.second)); + elist->erase(arg0_chunk.first, arg0_chunk.second); + + pf->absorbed[ parent_inv_op].insert(pf->absorbed[ parent_inv_op].end(), + f->absorbed[0].begin(), f->absorbed[0].end() ); + pf->absorbed[!parent_inv_op].insert(pf->absorbed[!parent_inv_op].end(), + f->absorbed[1].begin(), f->absorbed[1].end() ); + + pf->force_not_constant = true; + + *it = make_shared( neutral ); + + return; + } + } + + // end of chain - simplify and generate code + + // accumulate own args + + // first infix argument + auto arg1_chunk = get_dependent_chunk(f->args[1]); + f->absorbed[0 ].push_back(elist_t(arg1_chunk.first, arg1_chunk.second)); + elist->erase(arg1_chunk.first, arg1_chunk.second); + + // second infix argument + auto arg0_chunk = get_dependent_chunk(f->args[0]); + f->absorbed[arg2_inv].push_back(elist_t(arg0_chunk.first, arg0_chunk.second)); + elist->erase(arg0_chunk.first, arg0_chunk.second); + + // at this point, this is a function of 0 arguments, all of them were absorbed + f->args.clear(); + + // reduce constants + double ac[2] = {neutral, neutral}; + for (int i=0; i<2; i++) { + for (auto e = f->absorbed[i].begin(); e!=f->absorbed[i].end(); ) { + auto next_e = next(e); + if (e->size()==1 && e->front()->element_type == CCONST) { + auto v = static_pointer_cast(e->front()); + if (fclass==1) { + ac[i] += *((double*)v->address); + } + else { + ac[i] *= *((double*)v->address); + } + f->absorbed[i].erase(e); + } + e = next_e; + } + } + double ac_final = (fclass==1) ? (ac[0] -ac[1]) : (ac[0] * 1.0/ac[1]); + + // sort and gather chunks + map sig_map; + for (auto &e : f->absorbed[0]) { sig_map[e]++; } + for (auto &e : f->absorbed[1]) { sig_map[e]--; } + + mexce_charstream s; + + // TODO: assert that none of the values are constant + + if (fclass==1) { // NOTE: children can be mul/div, but they cannot be add/sub + + bool constant_added = false; + + for (auto &e : sig_map) { + + if (e.second == 0) { + // factor 0 in add_sub means 0*a which has no effect. + continue; + } + + // if the stack is not empty and the elist is only one element and the + // factor in e.second is 1 or -1, + // we can multiply directly from memory, to save one place in the FPU + // stack. This is a tradeoff, as it might be slightly slower to do so. + + if (constant_added && next(e.first.begin()) == e.first.end() && abs(e.second)==1.0 && + (e.first.front()->element_type == CCONST || e.first.front()->element_type == CVAR) ) + { + auto v = static_pointer_cast(e.first.front()); + if (v->numeric_data_type != M64INT) { // see emit_apply_op_with_value implementation + if (e.second == 1) { + emit_apply_op_with_value<0x00>(s, v); + } + else { + emit_apply_op_with_value<0x20>(s, v); + } + continue; + } + } + + compile_elist(s, e.first.begin(), e.first.end()); + + if (e.second == 1) { + // 1*a == a + // we loaded the expression, there is nothing further to do + } + else + if (e.second == -1) { + s < 0xd9 < 0xe0; // fchs + } + else + if (e.second == 2) { + s < 0xd8 < 0xc0; // fadd st(0), st(0) + } + else + if (e.second == -2) { + s < 0xd8 < 0xc0; // fadd st(0), st(0) + s < 0xd9 < 0xe0; // fchs + } + else { + emit_apply_op_with_constant<0x08>(ev, s, e.second); + } + + if (!constant_added) { + // add the constant + + // doing this here and not in the beginning requires one place + // less in the fpu stack (and 1 instruction less too) + + if (ac_final != neutral) { + emit_apply_op_with_constant<0x00>(ev, s, ac_final); + } + constant_added = true; + } + else { + s < 0xde < 0xc1; // faddp st(1), st + } + } + + if (!constant_added) { // we did nothing, we should at least load the constant + emit_load_constant(ev, s, ac_final); + } + } + else { + // NOTE: children can be powers, but they cannot be mul/div. + // They can also be add/sub, but this is not interesting for optimization. + + bool constant_multiplied = false; + + for (auto &e : sig_map) { + + if (e.second == 0) { + // factor 0 in mul_div means a^0, which has no effect. + continue; + } + + // if the stack is not empty and the elist is only one element and the + // factor in e.second is 1 or -1, + // we can multiply directly from memory, to save one place in the FPU + // stack. This is a tradeoff, as it might be slightly slower to do so. + + if (constant_multiplied && next(e.first.begin()) == e.first.end() && abs(e.second)==1.0 && + (e.first.front()->element_type == CCONST || e.first.front()->element_type == CVAR) ) + { + auto v = static_pointer_cast(e.first.front()); + if (v->numeric_data_type != M64INT) { // see emit_apply_op_with_value implementation + if (e.second == 1) { + emit_apply_op_with_value<0x08>(s, v); + } + else { + emit_apply_op_with_value<0x30>(s, v); + } + continue; + } + } + + if (e.second >= -2 && e.second <=2) { // cannot be 0, it has been handled above + compile_elist(s, e.first.begin(), e.first.end()); + + if (e.second == 1) { + // a^1 == a + // we loaded the expression, there is nothing further to do + } + else + if (e.second == -1) { // 1/a + s < 0xd9 < 0xe8; // fld1 + s < 0xde < 0xf1; // fdivrp st(1), st + } + else + if (e.second == 2) { // a*a + s < 0xdc < 0xc8; // fmul st(0), st(0) + } + else + if (e.second == -2) { // 1/(a*a) + s < 0xdc < 0xc8; // fmul st(0), st(0) + s < 0xd9 < 0xe8; // fld1 + s < 0xde < 0xf1; // fdivrp st(1), st + } + } + else { + elist_t pow_list = e.first; + pow_list.push_back(make_intermediate_constant(ev, e.second)); + auto pow_f = make_function("pow"); + pow_list.push_back(pow_f); + link_arguments(pow_list); + pow_f->optimizer(prev(pow_list.end()), ev, &pow_list); + + compile_elist(s, pow_list.begin(), pow_list.end()); + } + + if (!constant_multiplied) { + // add the constant + + // doing this here and not in the beginning requires one place + // less in the fpu stack (and 1 instruction less too) + + if (ac_final != neutral) { + emit_apply_op_with_constant<0x08>(ev, s, ac_final); + } + constant_multiplied = true; + } + else { + s < 0xde < 0xc9; // fmulp st(1), st + } + } + + if (!constant_multiplied) { // we did nothing, we should at least load the constant + emit_load_constant(ev, s, ac_final); + } + } + + string new_name = (fclass == 1) ? "add_sub_opt" : "mul_div_opt"; + + uint8_t* cc = push_intermediate_code(ev, s.s.str()); + auto f_opt = make_shared(new_name, 0, 0, s.s.str().size(), cc, nullptr); + + *it = f_opt; + return; +} + + +inline Function Add() +{ + static uint8_t code[] = { + 0xde, 0xc1 // faddp st(1), st + }; + return Function("add", 2, 0, sizeof(code), code, asmd_optimizer); +} + + +inline Function Sub() +{ + static uint8_t code[] = { + 0xde, 0xe9 // fsubp st(1), st + }; + return Function("sub", 2, 0, sizeof(code), code, asmd_optimizer); +} + + +inline Function Mul() +{ + static uint8_t code[] = { + 0xde, 0xc9 // fmulp st(1), st + }; + return Function("mul", 2, 0, sizeof(code), code, asmd_optimizer); +} + + +inline Function Div() +{ + static uint8_t code[] = { + 0xde, 0xf9 // fdivp st(1), st + }; + return Function("div", 2, 0, sizeof(code), code, asmd_optimizer); +} + + +inline Function Neg() +{ + // TODO: this does not need its own internal name, it is a special case of add_sub + static uint8_t code[] = { + 0xd9, 0xe0 // fchs + }; + return Function("neg", 1, 0, sizeof(code), code); +} + + +inline Function Gain() +{ + // x + // ------------------------ if x < 0.5 + // (1 / a - 2) (1 - 2x) + 1 + // gain(x, a) = for x, a in [0, 1] + // (1 / a - 2) (1 - 2x) - x + // ------------------------ if x >= 0.5 + // (1 / a - 2) (1 - 2x) - 1 + + static uint8_t code[] = { // ; FPU stack + 0xd9, 0xc1, // fld st(1) ; x, a, x + 0xd8, 0xc2, // fadd st,st(2) ; 2x, a, x + 0xd9, 0xe8, // fld1 ; 1, 2x, a, x + 0xdf, 0xf1, // fcomip st,st(1) ; 2x, a, x + 0xdd, 0xd8, // fstp st(0) ; a, x + 0xd9, 0xc0, // fld st(0) ; a, a, x + 0xd8, 0xc1, // fadd st,st(1) ; 2a, a, x + 0xd9, 0xe8, // fld1 ; 1, 2a, a, x + 0xde, 0xe9, // fsubp st(1),st ; 2a-1, a, x + 0xde, 0xf1, // fdivrp st(1),st ; (2a-1)/a, x + 0xd9, 0xc1, // fld st(1) ; x, (2a-1)/a, x + 0xdc, 0xc0, // fadd st(0),st ; 2x, (2a-1)/a, x + 0xd9, 0xe8, // fld1 ; 1, 2x, (2a-1)/a, x + 0xde, 0xe9, // fsubp st(1),st ; 2x-1, (2a-1)/a, x + 0xde, 0xc9, // fmulp st(1),st ; (2x-1)*(2a-1)/a, x + 0xd9, 0xe8, // fld1 ; 1, (2x-1)*(2a-1)/a, x + 0x72, 0x06, // jb x_ge_half + 0xde, 0xc1, // faddp st(1),st ; (2x-1)*(2a-1)/a+1, x + 0xde, 0xf9, // fdivp st(1),st ; x/((2x-1)*(2a-1)/a+1) [result] + 0xeb, 0x0a, // jmp gain_exit +// x_ge_half: + 0xd9, 0xc1, // fld st(1) ; (2x-1)*(2a-1)/a, 1, (2x-1)*(2a-1)/a, x + 0xde, 0xe9, // fsubp st(1),st ; 1-(2x-1)*(2a-1)/a, (2x-1)*(2a-1)/a, x + 0xd9, 0xc9, // fxch st(1) ; (2x-1)*(2a-1)/a, 1-(2x-1)*(2a-1)/a, x + 0xde, 0xea, // fsubp st(2),st ; 1-(2x-1)*(2a-1)/a, x-(2x-1)*(2a-1)/a + 0xde, 0xf9, // fdivp st(1),st ; (x-(2x-1)*(2a-1)/a)/(1-(2x-1)*(2a-1)/a) [result] +// gain_exit: + }; + return Function("gain", 2, 1, sizeof(code), code); +} + + +inline Function Bias() +{ + // x + // bias(x, a) = ----------------------- for x, a in [0, 1] + // (1 / a - 2) (1 - x) + 1 + + static uint8_t code[] = { + 0xd9, 0xe8, // fld1 + 0xdc, 0xf1, // fdivr st(1), st + 0xdc, 0xe9, // fsub st(1), st + 0xdc, 0xe9, // fsub st(1), st + 0xd8, 0xe2, // fsub st, st(2) + 0xde, 0xc9, // fmulp st(1), st + 0xd9, 0xe8, // fld1 + 0xde, 0xc1, // faddp st(1), st + 0xde, 0xf9 // fdivp st(1), st + }; + return Function("bias", 2, 1, sizeof(code), code); +} + + + +inline const map& make_function_map() +{ + static Function f[] = { + Sin(), Cos(), Tan(), Abs(), Sign(), Signp(), Expn(), Sfc(), Sqrt(), Pow(), Exp(), Less_than(), + Log(), Log2(), Ln(), Log10(), Logb(), Ylog2(), Max(), Min(), Floor(), Ceil(), Round(), Int(), Mod(), + Bnd(), Add(), Sub(), Neg(), Mul(), Div(), Bias(), Gain() + }; + + static map ret; + for (auto& e : f) { + assert(ret.find(e.name) == ret.end()); //if it fails, some functions share the same name. + ret.insert(make_pair(e.name, e)); + } + return ret; +} + + +inline const map& function_map() +{ + static const map& fname_map = make_function_map(); + return fname_map; +} + + +inline shared_ptr make_function(const string& name) { + auto fn = function_map().find(name); + return make_shared(fn->second); +} + + +inline const map >& built_in_constants_map() +{ + static const map > cname_map = { + { "pi", std::make_shared("3.141592653589793238462643383", "pi") }, + { "e", std::make_shared("2.718281828459045235360287471", "e" ) } + }; + return cname_map; +} + + +struct Token +{ + int type = 0; + int priority = 0; + size_t position = 0; + string content; + Token() = default; + Token(int type, size_t position, char content): + type ( type ), + priority ( type ), + position ( position ), + content ( string() + content ) {} +}; + + +template inline Numeric_data_type get_ndt() { assert(false); } +template <> inline Numeric_data_type get_ndt() { return M64FP; } +template <> inline Numeric_data_type get_ndt() { return M32FP; } +template <> inline Numeric_data_type get_ndt() { return M16INT; } +template <> inline Numeric_data_type get_ndt() { return M32INT; } +template <> inline Numeric_data_type get_ndt() { return M64INT; } + + +inline bool is_operator( char c) { return c=='+' || c=='-' || c=='*' || c=='/' || c=='^' || c=='<'; } +inline bool is_alphabetic(char c) { return (c>='A' && c<='Z') || (c>='a' && c<='z') || c=='_'; } +inline bool is_numeric( char c) { return c>='0' && c<='9'; } + + +} // mexce_impl + + +inline +evaluator::evaluator(): + m_constants(impl::built_in_constants_map()) +{ + set_expression("0"); +} + + +inline +evaluator::~evaluator() +{ + impl::free_executable_buffer(evaluate_fptr, m_buffer_size); +} + + +template +void evaluator::bind(T& v, const std::string& s, Args&... args) +{ + using namespace impl; + if (function_map().find(s) != function_map().end()) { + throw std::logic_error("Attempted to bind a variable, named as an existing function"); + } + if (built_in_constants_map().find(s) != built_in_constants_map().end()) { + throw std::logic_error("Attempted to bind a variable, named as an existing constant"); + } + m_variables[s] = make_shared(&v, s, get_ndt()); + + bind(args...); +} + + +template +void evaluator::unbind(const std::string& s, Args&... args) +{ + if (s.length() == 0) + throw std::logic_error("Variable name was an empty string"); + + auto it = m_variables.find(s); + if (it != m_variables.end()) { + assert(it->second->element_type == impl::CVAR); + if (it->second->referenced) { + set_expression("0"); + } + m_variables.erase(it); + unbind(args...); + return; + } + throw std::logic_error("Attempted to unbind an unknown variable"); +} + + +inline +void evaluator::unbind_all() +{ + m_variables.clear(); +} + + +inline +double evaluator::evaluate() { + if (is_constant_expression) { + return constant_expression_value; + } + return evaluate_fptr(); +} + + + +inline +void evaluator::set_expression(std::string e) +{ + using namespace impl; + using mpe = mexce_parsing_exception; + + deque tokens; + + m_intermediate_constants.clear(); + m_intermediate_code.clear(); + m_elist.clear(); + + if (evaluate_fptr) { + free_executable_buffer(evaluate_fptr, m_buffer_size); + evaluate_fptr = nullptr; + } + + auto x = m_variables.begin(); + for (; x != m_variables.end(); x++) + x->second->referenced = false; + + if (e.length() == 0){ + throw (std::logic_error("Expected an expression")); + } + + e += ' '; + + //stage 1: checking expression syntax + Token temp; + vector< pair > bdarray(1); + map::const_iterator i_fnc; + int state = 0; + size_t i = 0; + int function_parentheses = 0; + for (; i < e.length(); i++) { + switch(state) { + case 0: //start of expression + if (e[i] == '-' || e[i] == '+') { + tokens.push_back(Token(UNARY, i, e[i])); + state = 4; + break; + } + if (e[i] == ')') { + if (bdarray.back().first != 0) + throw (mpe("Expected an expression", i)); + if (bdarray.back().second != 0) + throw (mpe("Expected more arguments", i)); + tokens.push_back(Token(FUNCTION_RIGHT_PARENTHESIS, i, ')')); + function_parentheses--; + bdarray.pop_back(); + state = 5; + break; + } + case 4: //just read an infix operator + if (e[i] == ' ') + break; + if (is_numeric(e[i])) { + temp = Token(NUMERIC_LITERAL, i, e[i]); + state = 1; + break; + } + if (e[i] == '.') { + temp = Token(NUMERIC_LITERAL, i, e[i]); + state = 2; + break; + } + if (is_alphabetic(e[i])) { + temp = Token(0, i, e[i]); + state = 3; + break; + } + if (e[i] == '-' || e[i] == '+') { + tokens.push_back(Token(UNARY, i, e[i])); + state = 4; + break; + } + if (e[i] == '(') { + tokens.push_back(Token(LEFT_PARENTHESIS, i, '(')); + bdarray.back().first++; + state = 0; + break; + } + else { + throw (mpe((string("\"")+e[i])+"\" not expected", i)); + } + case 1: //currently reading a numeric literal + if (e[i] == '.') { + temp.content += e[i]; + state = 2; + break; + } + case 2: // currently reading a numeric literal, found dot + if (is_numeric(e[i])) { + temp.content += e[i]; + break; + } + if (e[i] == ' ') { + tokens.push_back(temp); + state = 5; + break; + } + if (e[i] == ')') { + tokens.push_back(temp); + if (bdarray.back().first > 0) { + tokens.push_back(Token(RIGHT_PARENTHESIS, i, ')')); + bdarray.back().first--; + } + else { + if (function_parentheses <= 0) + throw (mpe("\")\" not expected", i)); + if (bdarray.back().second != 1) + throw (mpe("Expected more arguments", i)); + tokens.push_back(Token(FUNCTION_RIGHT_PARENTHESIS, i, ')')); + function_parentheses--; + bdarray.pop_back(); + } + state = 5; + break; + } + if (is_operator(e[i])) { + tokens.push_back(temp); + tokens.push_back(Token( get_infix_rank(e[i]) , i, e[i])); + state = 4; + break; + } + if (e[i] == ',') { + tokens.push_back(temp); + if (bdarray.back().first != 0) + throw (mpe("Expected a \")\"", i)); + if (bdarray.back().second-- < 2) + throw (mpe("Don\'t expect any arguments here", i)); + tokens.push_back(Token(COMMA, i, ',')); + state = 0; + break; + } + if (e[i] == 'e' && state < 7) { + temp.content += e[i]; + state = 7; + break; + } + throw (mpe((string("\"")+e[i])+"\" not expected", i)); + case 7: // read the 'e' (exponent) while reading a numeric literal + if (e[i] == '+' || e[i] == '-') { + temp.content += e[i]; + state = 8; + break; + } + throw (mpe("expecting '+'/'-' followed by the exponent of the numeric literal", i)); + case 8: // reading the exponent of the numeric literal + if (is_numeric(e[i])) { + temp.content += e[i]; + state = 2; + break; + } + throw (mpe("expecting the exponent of the numeric literal", i)); + case 3: //currently reading alphanumeric + if (is_alphabetic(e[i]) || is_numeric(e[i])) { + temp.content += e[i]; + break; + } + if (e[i] == ' ') { + if (m_variables.find(temp.content) != m_variables.end()) { + temp.type = VARIABLE_NAME; + tokens.push_back(temp); + state = 5; + break; + } + if (m_constants.find(temp.content) != m_constants.end()) { + temp.type = CONSTANT_NAME; + tokens.push_back(temp); + state = 5; + break; + } + if ((i_fnc = function_map().find(temp.content)) != function_map().end()) { + temp.type = FUNCTION_NAME; + tokens.push_back(temp); + tokens.push_back(Token(FUNCTION_LEFT_PARENTHESIS, i, '(')); + bdarray.push_back(make_pair(0, i_fnc->second.num_args)); + function_parentheses++; + state = 6; + break; + } + throw (mpe(string(temp.content) + + " is not a known constant, variable or function name", i)); + } + if (e[i] == ')') { + temp.type = m_variables.find(temp.content) != m_variables.end() ? VARIABLE_NAME : + m_constants.find(temp.content) != m_constants.end() ? CONSTANT_NAME : + throw (mpe(string(temp.content) + + " is not a known constant or variable name", i)); + tokens.push_back(temp); + if (bdarray.back().first > 0) { + tokens.push_back(Token(RIGHT_PARENTHESIS, i, ')')); + bdarray.back().first--; + } + else + if (function_parentheses > 0) { + if (bdarray.back().second != 1) + throw (mpe("Expected more arguments", i)); + tokens.push_back(Token(FUNCTION_RIGHT_PARENTHESIS, i, ')')); + function_parentheses--; + bdarray.pop_back(); + } + else { + throw (mpe("\")\" not expected", i)); + } + state = 5; + break; + } + if (e[i] == '(') { + if ((i_fnc = function_map().find(temp.content)) == function_map().end()) { + throw (mpe(string(temp.content) + " is not a known function name", i)); + } + temp.type = FUNCTION_NAME; + tokens.push_back(temp); + tokens.push_back(Token(FUNCTION_LEFT_PARENTHESIS, i, '(')); + bdarray.push_back(make_pair(0, i_fnc->second.num_args)); + function_parentheses++; + state = 0; + break; + } + if (is_operator(e[i])) { + temp.type = m_variables.find(temp.content) != m_variables.end() ? VARIABLE_NAME : + m_constants.find(temp.content) != m_constants.end() ? CONSTANT_NAME : + throw (mpe(string(temp.content) + + " is not a known constant or variable name", i)); + tokens.push_back(temp); + tokens.push_back(Token(get_infix_rank(e[i]), i, e[i])); + state = 4; + break; + } + if (e[i] == ',') { + temp.type = m_variables.find(temp.content) != m_variables.end() ? VARIABLE_NAME : + m_constants.find(temp.content) != m_constants.end() ? CONSTANT_NAME : + throw (mpe(string(temp.content)+" is not a " + "known constant or variable name", i)); + tokens.push_back(temp); + if (bdarray.back().first != 0) + throw (mpe("Expected a \")\"", i)); + if (bdarray.back().second-- < 2) + throw (mpe("Don\'t expect any arguments here", i)); + tokens.push_back(Token(COMMA, i, ',')); + state = 0; + break; + } + throw (mpe((string("\"")+e[i])+"\" not expected", i)); + case 5: //just read an expression (constant/variable/right parenthesis) + if (e[i] == ' ') + break; + if (is_operator(e[i])) { + tokens.push_back(Token(get_infix_rank(e[i]), i, e[i])); + state = 4; + break; + } + if (e[i] == ')') { + if (bdarray.back().first > 0) { + tokens.push_back(Token(RIGHT_PARENTHESIS, i, ')')); + bdarray.back().first--; + } + else + if (function_parentheses > 0) { + if (bdarray.back().second != 1) + throw (mpe("Expected more arguments", i)); + tokens.push_back(Token(FUNCTION_RIGHT_PARENTHESIS, i, ')')); + function_parentheses--; + bdarray.pop_back(); + } + else { + throw (mpe("\")\" not expected", i)); + } + state = 5; + break; + } + if (e[i] == ',') { + if (bdarray.back().first != 0) + throw (mpe("Expected a \")\"", i)); + if (bdarray.back().second-- < 2) + throw (mpe("Don\'t expect any arguments here", i)); + tokens.push_back(Token(COMMA, i, ',')); + state = 0; + break; + } + throw (mpe((string("\"")+e[i])+"\" not expected", i)); + case 6: //just read a function name + if (e[i] == '(') { + state = 0; + break; + } + throw (mpe("Expected a \"(\"", i)); + } + } + if ((bdarray.back().first > 0) || (function_parentheses > 0)) { + throw (mpe("Expected a \")\"", --i)); + } + if (state != 5) { + throw (mpe("Unexpected end of expression", --i)); + } + + //stage 2: transform expression to postfix + deque postfix; + vector tstack; + while (!tokens.empty()) { + temp = tokens.front(); + tokens.pop_front(); + switch (temp.type) { + case INFIX_4: + case INFIX_3: + case INFIX_2: + while(!tstack.empty()) { + int sp = tstack.back().priority; + if (sp < INFIX_1 || sp > temp.type) { + break; + } + postfix.push_back(tstack.back()); + tstack.pop_back(); + } + case INFIX_1: + case LEFT_PARENTHESIS: + case FUNCTION_NAME: + tstack.push_back(temp); + break; + case UNARY: + temp.priority = (!tstack.empty() && tstack.back().priority == INFIX_1) ? + INFIX_1 : INFIX_3; + tstack.push_back(temp); + break; + case NUMERIC_LITERAL: + case CONSTANT_NAME: + case VARIABLE_NAME: + postfix.push_back(temp); + break; + case RIGHT_PARENTHESIS: + while(tstack.back().type != LEFT_PARENTHESIS) { + postfix.push_back(tstack.back()); + tstack.pop_back(); + } + tstack.pop_back(); + break; + case FUNCTION_RIGHT_PARENTHESIS: + do { + postfix.push_back(tstack.back()); + tstack.pop_back(); + } + while(postfix.back().type != FUNCTION_NAME); + break; + case COMMA: + while(tstack.back().type != FUNCTION_NAME) { + postfix.push_back(tstack.back()); + tstack.pop_back(); + } + break; + case FUNCTION_LEFT_PARENTHESIS: + break; + default: + throw(mpe("internal error", 0)); + } + } + while(!tstack.empty()) { + postfix.push_back(tstack.back()); + tstack.pop_back(); + } + + //stage 3: convert "Token" expression primitives to "Element *" + + while (!postfix.empty()) { + temp = postfix.front(); + postfix.pop_front(); + switch (temp.type) { + case INFIX_4: + case INFIX_3: + case INFIX_2: + case INFIX_1: { + auto name = infix_operator_to_function_name(temp.content); + m_elist.push_back( make_function(name) ); + break; + } + case FUNCTION_NAME: + m_elist.push_back(make_function(temp.content)); + break; + case UNARY: + if (temp.content == "-") { // unary '+' is ignored + + // Rather than having an individual function for the unary minus + // we insert a zero before the last argument (which is already in + // the list), and use a subtraction instead. + // The reason is to allow the optimizer to group + // addition/subtraction chains. Clearly, this is a bit unorthodox + // and could be done in the optimizer too, but the optimizer is + // complex enough already. + + link_arguments(m_elist); + auto chunk = get_dependent_chunk(std::prev(m_elist.end())); + m_elist.insert(chunk.first, make_intermediate_constant(this, 0.0)); + m_elist.push_back( make_function("sub") ); + } + break; + case NUMERIC_LITERAL: { + double c_value = atof(temp.content.c_str()); + m_elist.push_back(make_intermediate_constant(this, c_value)); + break; + } + case CONSTANT_NAME: { + m_elist.push_back(m_constants.find(temp.content)->second); + break; + } + case VARIABLE_NAME: { + auto it = m_variables.find(temp.content); + assert(it != m_variables.end()); + it->second->referenced = true; + m_elist.push_back(it->second); + break; + } + } + } + + // link functions to their arguments (1) + link_arguments(m_elist); + + // choose more suitable functions, where applicable + for (auto y = m_elist.begin(); y != m_elist.end(); ) { + auto y_next = next(y); + if ((*y)->element_type == CFUNC) { + auto f = static_pointer_cast(*y); + + // eliminate constants + if (!f->force_not_constant) { + bool all_args_are_const = true; + for (size_t j = 0; j < f->num_args; j++) { + if ((*f->args[j])->element_type != CCONST) { + all_args_are_const = false; + break; + } + } + if (all_args_are_const) { + elist_it_t first_arg_it = y; + std::advance(first_arg_it, -(int64_t)f->num_args); + compile_and_finalize_elist(first_arg_it, next(y)); + double res = evaluate_fptr(); + m_elist.erase(first_arg_it, y); + *y = make_intermediate_constant(this, res); + y = y_next; + continue; + } + } + + if (f->optimizer != 0) { + f->optimizer(y, this, &m_elist); + } + } + y = y_next; + } + + is_constant_expression = m_elist.size()==1 && m_elist.back()->element_type == CCONST; + if (is_constant_expression) { + auto v = static_pointer_cast(m_elist.back()); + constant_expression_value = v->get_data_as_double(); + } + else { + compile_and_finalize_elist(m_elist.begin(), m_elist.end()); + } +} + + + +inline +void evaluator::compile_and_finalize_elist(impl::elist_it_t first, impl::elist_it_t last) +{ + using namespace impl; + + const static uint8_t return_sequence[] = { +#ifdef MEXCE_64 + // Right before the function returns, in 32-bit x86, the result is in + // st(0), where it is expected to be. There is nothing further to do there + // other than return. + // In x64 however, the result is expected to be in xmm0, thus we should + // move it there and pop the FPU stack. To achieve that, we store the + // result to memory and then load it to xmm0, which requires a temporary. + // This code is used at the very end (see below), but its size must be known here. + + // load return address to rax (last 8 bytes - address is uninitialized) + 0x48, 0xb8, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, // mov rax, cdcdcdcdcdcdcdcdh + + // store the return value + 0xdd, 0x18, // fstp qword ptr [rax] + + // load from the return value to xmm0 + 0xf3, 0x0f, 0x7e, 0x00, // movq xmm0, mmword ptr [rax] + 0x58, // pop rax +#endif + 0xc3 // return + }; + + mexce_charstream code_buffer; + +#ifdef MEXCE_64 + // On x64 we are using rax to fetch/store addresses + code_buffer < 0x50; // push rax +#endif + + compile_elist(code_buffer, first, last); + + // copy the return sequence + code_buffer.s.write((const char*)return_sequence, sizeof(return_sequence)); + + auto code = code_buffer.s.str(); + m_buffer_size = code.size(); + auto buffer = get_executable_buffer(m_buffer_size); + memcpy(buffer, &code[0], m_buffer_size); + +#ifdef MEXCE_64 + // load the intermediate variable's address to rax + *((uint64_t*)(buffer+code.size()-16)) = (uint64_t)&m_x64_return_var; +#endif + + evaluate_fptr = lock_executable_buffer(buffer, code.size()); +} + +} // mexce + +#endif diff --git a/readme.md b/readme.md index 57dc7d8..2ca8d92 100644 --- a/readme.md +++ b/readme.md @@ -12,10 +12,11 @@ expression parsers are part of this benchmark: | 03 | [Lepton](https://simtk.org/home/lepton) | Peter Eastman | [MIT](https://opensource.org/licenses/MIT) | double | | 04 | [MathExpr](http://www.yann-ollivier.org/mathlib/mathexpr) | Yann Ollivier | [Copyright Notice 1997-2000](http://www.yann-ollivier.org/mathlib/mathexpr#C) | double | | 05 | [METL](https://github.com/TillHeinzel/METL) | Till Heinzel | [Apache](https://opensource.org/licenses/Apache-2.0) | double | -| 06 | [MTParser](http://www.codeproject.com/Articles/7335/An-extensible-math-expression-parser-with-plug-ins)| Mathieu Jacques | [CPOL](http://www.codeproject.com/info/cpol10.aspx)| double | -| 07 | [muParser](http://muparser.beltoforion.de) | Ingo Berg | [MIT](http://www.opensource.org/licenses/mit-license.php) | double, float | -| 08 | [muParserX](http://muparserx.beltoforion.de) | Ingo Berg | [MIT](http://www.opensource.org/licenses/mit-license.php) | double, float | -| 09 | [TinyExpr](https://github.com/codeplea/tinyexpr) | Lewis Van Winkle | [Zlib](https://opensource.org/licenses/Zlib) | double | +| 06 | [mexce](https://github.com/imakris/mexce) | Ioannis Makris | [BSD](https://opensource.org/licenses/BSD-2-Clause) | double | +| 07 | [MTParser](http://www.codeproject.com/Articles/7335/An-extensible-math-expression-parser-with-plug-ins)| Mathieu Jacques | [CPOL](http://www.codeproject.com/info/cpol10.aspx)| double | +| 08 | [muParser](http://muparser.beltoforion.de) | Ingo Berg | [MIT](http://www.opensource.org/licenses/mit-license.php) | double, float | +| 09 | [muParserX](http://muparserx.beltoforion.de) | Ingo Berg | [MIT](http://www.opensource.org/licenses/mit-license.php) | double, float | +| 10 | [TinyExpr](https://github.com/codeplea/tinyexpr) | Lewis Van Winkle | [Zlib](https://opensource.org/licenses/Zlib) | double | **Note:** The terms double, float etc found in the table above are defined as follows: diff --git a/src/BenchMexce.cpp b/src/BenchMexce.cpp new file mode 100644 index 0000000..5290ada --- /dev/null +++ b/src/BenchMexce.cpp @@ -0,0 +1,113 @@ +#include "BenchMexce.h" + +#include +#include + +#include "mexce/mexce.h" + + +//------------------------------------------------------------------------------------------------- +BenchMexce::BenchMexce() +: Benchmark() +{ + m_sName = "mexce"; +} + +//------------------------------------------------------------------------------------------------- +double BenchMexce::DoBenchmark(const std::string& sExpr, long iCount) +{ + double a = 1.1; + double b = 2.2; + double c = 3.3; + double x = 2.123456; + double y = 3.123456; + double z = 4.123456; + double w = 5.123456; + + mexce::evaluator ev; + + ev.bind(a, "a", b, "b", c, "c", x, "x", y, "y", z, "z", w, "w"); + + // Perform basic tests for the variables used + // in the expressions + { + bool test_result = true; + + auto tests_list = test_expressions(); + + for (auto test : tests_list) + { + try { + ev.set_expression(test.first.c_str()); + } + catch (mexce::mexce_parsing_exception&) { + test_result = false; + break; + } + + + if (!is_equal(test.second, ev.evaluate() )) + { + test_result = false; + break; + } + } + + if (!test_result) + { + StopTimerAndReport("Failed variable test"); + return m_fTime1; + } + } + + + try { + ev.set_expression(sExpr.c_str()); + } + catch (mexce::mexce_parsing_exception& e) { + StopTimerAndReport(e.what()); + return m_fTime1; + } + + + //Prime the I and D caches for the expression + { + double d0 = 0.0; + double d1 = 0.0; + + for (std::size_t i = 0; i < priming_rounds; ++i) + { + if (i & 1) + d0 += ev.evaluate(); + else + d1 += ev.evaluate(); + } + + if ( + (d0 == std::numeric_limits::infinity()) && + (d1 == std::numeric_limits::infinity()) + ) + { + printf("\n"); + } + } + + // Perform benchmark then return results + double fRes = 0; + double fSum = 0; + + fRes = ev.evaluate(); + + StartTimer(); + + for (int j = 0; j < iCount; ++j) + { + fSum += ev.evaluate(); + std::swap(a,b); + std::swap(x,y); + } + + StopTimer(fRes, fSum, iCount); + + return m_fTime1; +} diff --git a/src/ParserBench.cpp b/src/ParserBench.cpp index ee96a9a..008aac2 100644 --- a/src/ParserBench.cpp +++ b/src/ParserBench.cpp @@ -34,6 +34,7 @@ #include "BenchMathExpr.h" #include "BenchMETL.h" #include "BenchTinyExpr.h" +#include "BenchMexce.h" #include "BenchNative.h" #ifdef ENABLE_MPFR @@ -487,6 +488,7 @@ int main(int argc, const char *argv[]) benchmarks.push_back(std::make_shared() ); benchmarks.push_back(std::make_shared() ); benchmarks.push_back(std::make_shared() ); + benchmarks.push_back(std::make_shared() ); #if defined(_MSC_VER) && defined(NDEBUG) benchmarks.push_back(std::make_shared() ); // <-- Crash in debug mode #endif