diff --git a/exprtk.hpp b/exprtk.hpp index c70a5bd..09dbf84 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -228,8 +228,8 @@ namespace exprtk static const std::string reserved_words[] = { "and", "default", "case", "false", "for", "if", "ilike", "in", "like", - "nand", "nor", "not", "null", "or", "shl", "shr", "switch", "true", - "while", "xnor", "xor", "&", "|" + "nand", "nor", "not", "null", "or", "repeat", "shl", "shr", "switch", + "true", "until", "while", "xnor", "xor", "&", "|" }; static const std::size_t reserved_words_size = sizeof(reserved_words) / sizeof(std::string); @@ -241,9 +241,9 @@ namespace exprtk "equal", "erf", "erfc", "exp", "false", "floor", "for", "frac", "grad2deg", "hypot", "if", "ilike", "in", "inrange", "like", "log", "log10", "log2", "logn", "log1p", "mand", "max", "min", "mod", "mor", "mul", "nand", "nor", - "not", "not_equal", "null", "or", "pow", "rad2deg", "root", "round", "roundn", - "sec", "sgn", "shl", "shr", "sin", "sinh", "sqrt", "sum", "switch", "tan", - "tanh", "true", "trunc", "while", "xnor", "xor", "&", "|" + "not", "not_equal", "null", "or", "pow", "rad2deg", "repeat", "root", "round", + "roundn", "sec", "sgn", "shl", "shr", "sin", "sinh", "sqrt", "sum", "switch", + "tan", "tanh", "true", "trunc", "until", "while", "xnor", "xor", "&", "|" }; static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string); @@ -1296,8 +1296,8 @@ namespace exprtk based on the compiler and target architecture. The benchmark should provide enough information to make the right choice. */ - //typedef T Type - //typedef const T Type + //typedef T Type; + //typedef const T Type; typedef const T& Type; typedef T (*qfunc_t)(Type t0, Type t1, Type t2, Type t3); typedef T (*tfunc_t)(Type t0, Type t1, Type t2); @@ -1314,39 +1314,17 @@ namespace exprtk enum token_type { - e_none = 0, - e_error = 1, - e_err_symbol = 2, - e_err_number = 3, - e_err_string = 4, - e_err_sfunc = 5, - e_eof = 6, - e_number = 7, - e_symbol = 8, - e_string = 9, - e_assign = 10, - e_shr = 11, - e_shl = 12, - e_lte = 13, - e_ne = 14, - e_gte = 15, - e_lt = '<', - e_gt = '>', - e_eq = '=', - e_rbracket = ')', - e_lbracket = '(', - e_rsqrbracket = ']', - e_lsqrbracket = '[', - e_rcrlbracket = '}', - e_lcrlbracket = '{', - e_comma = ',', - e_add = '+', - e_sub = '-', - e_div = '/', - e_mul = '*', - e_mod = '%', - e_pow = '^', - e_colon = ':' + e_none = 0, e_error = 1, e_err_symbol = 2, + e_err_number = 3, e_err_string = 4, e_err_sfunc = 5, + e_eof = 6, e_number = 7, e_symbol = 8, + e_string = 9, e_assign = 10, e_shr = 11, + e_shl = 12, e_lte = 13, e_ne = 14, + e_gte = 15, e_lt = '<', e_gt = '>', + e_eq = '=', e_rbracket = ')', e_lbracket = '(', + e_rsqrbracket = ']', e_lsqrbracket = '[', e_rcrlbracket = '}', + e_lcrlbracket = '{', e_comma = ',', e_add = '+', + e_sub = '-', e_div = '/', e_mul = '*', + e_mod = '%', e_pow = '^', e_colon = ':' }; token() @@ -1423,7 +1401,7 @@ namespace exprtk return *this; } - inline std::string to_str(token_type t) const + static inline std::string to_str(token_type t) { switch (t) { @@ -1561,6 +1539,16 @@ namespace exprtk return eof_token_; } + inline token_t& peek_next_token() + { + if (token_list_.end() != token_itr_) + { + return *token_itr_; + } + else + return eof_token_; + } + inline token_t& operator[](const std::size_t& index) { if (index < token_list_.size()) @@ -3002,29 +2990,29 @@ namespace exprtk e_none , e_null , e_constant , e_unary , e_binary , e_binary_ext , e_trinary , e_quaternary , e_quinary , e_senary , e_vararg , e_conditional , - e_while , e_switch , e_variable , e_stringvar , - e_stringconst , e_function , e_vafunction , e_add , - e_sub , e_mul , e_div , e_mod , - e_pow , e_lt , e_lte , e_gt , - e_gte , e_eq , e_ne , e_and , - e_nand , e_or , e_nor , e_xor , - e_xnor , e_in , e_like , e_ilike , - e_inranges , e_ipow , e_ipowinv , e_abs , - e_acos , e_asin , e_atan , e_ceil , - e_cos , e_cosh , e_exp , e_floor , - e_log , e_log10 , e_log2 , e_log1p , - e_neg , e_pos , e_round , e_sin , - e_sinh , e_sqrt , e_tan , e_tanh , - e_cot , e_sec , e_csc , e_r2d , - e_d2r , e_d2g , e_g2d , e_notl , - e_sgn , e_erf , e_erfc , e_frac , - e_trunc , e_uvouv , e_vov , e_cov , - e_voc , e_vob , e_bov , e_cob , - e_boc , e_vovov , e_vovoc , e_vocov , - e_covov , e_covoc , e_vovovov , e_vovovoc , - e_vovocov , e_vocovov , e_covovov , e_covocov , - e_vocovoc , e_covovoc , e_vococov , e_sf3ext , - e_sf4ext + e_while , e_repeat , e_switch , e_variable , + e_stringvar , e_stringconst , e_function , e_vafunction , + e_add , e_sub , e_mul , e_div , + e_mod , e_pow , e_lt , e_lte , + e_gt , e_gte , e_eq , e_ne , + e_and , e_nand , e_or , e_nor , + e_xor , e_xnor , e_in , e_like , + e_ilike , e_inranges , e_ipow , e_ipowinv , + e_abs , e_acos , e_asin , e_atan , + e_ceil , e_cos , e_cosh , e_exp , + e_floor , e_log , e_log10 , e_log2 , + e_log1p , e_neg , e_pos , e_round , + e_sin , e_sinh , e_sqrt , e_tan , + e_tanh , e_cot , e_sec , e_csc , + e_r2d , e_d2r , e_d2g , e_g2d , + e_notl , e_sgn , e_erf , e_erfc , + e_frac , e_trunc , e_uvouv , e_vov , + e_cov , e_voc , e_vob , e_bov , + e_cob , e_boc , e_vovov , e_vovoc , + e_vocov , e_covov , e_covoc , e_vovovov , + e_vovovoc , e_vovocov , e_vocovov , e_covovov , + e_covocov , e_vocovoc , e_covovoc , e_vococov , + e_sf3ext , e_sf4ext }; typedef T value_type; @@ -3586,9 +3574,9 @@ namespace exprtk const T arg3 = branch_[3].first->value(); switch (operation_) { - case e_min : return std::min(std::min(arg0,arg1),std::min(arg2,arg3)); - case e_max : return std::max(std::max(arg0,arg1),std::max(arg2,arg3)); - default : return std::numeric_limits::quiet_NaN(); + case e_min : return std::min(std::min(arg0,arg1),std::min(arg2,arg3)); + case e_max : return std::max(std::max(arg0,arg1),std::max(arg2,arg3)); + default : return std::numeric_limits::quiet_NaN(); } } @@ -3762,8 +3750,7 @@ namespace exprtk typedef expression_node* expression_ptr; - while_loop_node(expression_ptr test, - expression_ptr branch) + while_loop_node(expression_ptr test, expression_ptr branch) : test_(test), branch_(branch), test_deletable_(!is_variable_node(test_)), @@ -3799,6 +3786,50 @@ namespace exprtk bool branch_deletable_; }; + template + class repeat_until_loop_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + repeat_until_loop_node(expression_ptr test, expression_ptr branch) + : test_(test), + branch_(branch), + test_deletable_(!is_variable_node(test_)), + branch_deletable_(!is_variable_node(branch_)) + {} + + ~repeat_until_loop_node() + { + if (test_ && test_deletable_) delete test_; + if (branch_ && branch_deletable_) delete branch_; + } + + inline T value() const + { + T result = T(0); + do + { + result = branch_->value(); + } + while (is_false(test_)); + return result; + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_repeat; + } + + private: + + expression_ptr test_; + expression_ptr branch_; + bool test_deletable_; + bool branch_deletable_; + }; + template class switch_node : public expression_node { @@ -4369,12 +4400,13 @@ namespace exprtk assignment_node(const operator_type& operation, expression_ptr branch0, expression_ptr branch1) - : binary_node(operation,branch0,branch1) + : binary_node(operation,branch0,branch1), + is_lefthand_variable_(is_variable_node(binary_node::branch_[0].first)) {} inline T value() const { - if (is_variable_node(binary_node::branch_[0].first)) + if (is_lefthand_variable_) { variable_node* var_node_ptr = dynamic_cast*>(binary_node::branch_[0].first); if (var_node_ptr) @@ -4386,6 +4418,10 @@ namespace exprtk } return std::numeric_limits::quiet_NaN(); } + + private: + + bool is_lefthand_variable_; }; template @@ -9324,32 +9360,34 @@ namespace exprtk struct state_t; - typedef const T& cref_t; - typedef const T const_t; - typedef ifunction F; - typedef ivararg_function VAF; - typedef ifunction ifunction_t; - typedef ivararg_function ivararg_function_t; - typedef details::expression_node expression_node_t; - typedef details::literal_node literal_node_t; - typedef details::string_literal_node string_literal_node_t; - typedef details::unary_node unary_node_t; - typedef details::binary_node binary_node_t; - typedef details::trinary_node trinary_node_t; - typedef details::quaternary_node quaternary_node_t; - typedef details::quinary_node quinary_node_t; - typedef details::senary_node senary_node_t; - typedef details::conditional_node conditional_node_t; - typedef details::while_loop_node while_loop_node_t; - typedef details::variable_node variable_node_t; + typedef const T& cref_t; + typedef const T const_t; + typedef ifunction F; + typedef ivararg_function VAF; + typedef ifunction ifunction_t; + typedef ivararg_function ivararg_function_t; + typedef details::expression_node expression_node_t; + typedef details::literal_node literal_node_t; + typedef details::string_literal_node string_literal_node_t; + typedef details::unary_node unary_node_t; + typedef details::binary_node binary_node_t; + typedef details::trinary_node trinary_node_t; + typedef details::quaternary_node quaternary_node_t; + typedef details::quinary_node quinary_node_t; + typedef details::senary_node senary_node_t; + typedef details::conditional_node conditional_node_t; + typedef details::while_loop_node while_loop_node_t; + typedef details::repeat_until_loop_node repeat_until_loop_node_t; + typedef details::switch_node switch_node_t; + typedef details::variable_node variable_node_t; #ifndef exprtk_disable_string_capabilities - typedef details::stringvar_node stringvar_node_t; + typedef details::stringvar_node stringvar_node_t; #endif - typedef details::assignment_node assignment_node_t; - typedef details::scand_node scand_node_t; - typedef details::scor_node scor_node_t; - typedef lexer::token token_t; - typedef expression_node_t* expression_node_ptr; + typedef details::assignment_node assignment_node_t; + typedef details::scand_node scand_node_t; + typedef details::scor_node scor_node_t; + typedef lexer::token token_t; + typedef expression_node_t* expression_node_ptr; typedef typename details::functor_t functor_t; typedef typename functor_t::qfunc_t quaternary_functor_t; @@ -9387,6 +9425,8 @@ namespace exprtk typedef details::T0oT1oT2oT3_define covovoc_t; typedef details::T0oT1oT2oT3_define vococov_t; + struct range_pack; + public: enum precompilation_step @@ -9847,12 +9887,7 @@ namespace exprtk } while (!lexer_.finished()); - if (arg_list.empty()) - return error_node(); - else if (arg_list.size() == 1) - result = arg_list[0]; - else - result = expression_generator_.vararg_function(details::e_multi,arg_list); + result = simplify(arg_list); sdd.delete_ptr = (0 == result); return result; @@ -10382,6 +10417,88 @@ namespace exprtk return result; } + inline expression_node_ptr parse_repeat_until_loop() + { + // Parse: [repeat][{][expression][}][until][(][test expr][)] + expression_node_ptr condition = error_node(); + expression_node_ptr branch = error_node(); + next_token(); + + std::deque arg_list; + scoped_deq_delete sdd(*this,arg_list); + + { + token_t::token_type seperator = token_t::e_eof; + + for (;;) + { + expression_node_ptr arg = parse_expression(); + if (0 == arg) + return error_node(); + else + arg_list.push_back(arg); + + if (details::imatch(current_token_.value,"until")) + { + next_token(); + break; + } + else if (!token_is(seperator)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR22 - Expected '" + token_t::to_str(seperator) +"' for body of repeat until loop")); + return error_node(); + } + + if (details::imatch(current_token_.value,"until")) + { + next_token(); + break; + } + } + + branch = simplify(arg_list); + + sdd.delete_ptr = (0 == branch); + + if (0 == branch) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR23 - Failed to parse body of repeat until loop.")); + return error_node(); + } + } + + if (!token_is(token_t::e_lbracket)) + return error_node(); + else if (0 == (condition = parse_expression())) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR24 - Failed to parse condition for repeat until loop.")); + return error_node(); + } + else if (!token_is(token_t::e_rbracket)) + return error_node(); + + expression_node_ptr result; + if (0 == (result = expression_generator_.repeat_until_loop(condition,branch))) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR25 - Failed to synthesize repeat until loop.")); + return error_node(); + } + else + return result; + } + inline expression_node_ptr parse_switch_statement() { std::vector arg_list; @@ -10397,11 +10514,11 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR22 - Expected '{' for call to switch statement.")); + "ERR26 - Expected '{' for call to switch statement.")); return error_node(); } - for (;;) + for ( ; ; ) { if (!details::imatch("case",current_token_.value)) return error_node(); @@ -10417,7 +10534,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR23 - Expected ':' for case of switch statement.")); + "ERR27 - Expected ':' for case of switch statement.")); return error_node(); } @@ -10430,7 +10547,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR24 - Expected ';' at end of case for switch statement.")); + "ERR28 - Expected ';' at end of case for switch statement.")); return error_node(); } @@ -10445,7 +10562,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR25 - Expected ':' for default of switch statement.")); + "ERR29 - Expected ':' for default of switch statement.")); return error_node(); } @@ -10456,7 +10573,7 @@ namespace exprtk { set_error(make_error(parser_error::e_syntax, current_token_, - "ERR26 - Expected ';' at end of default for switch statement.")); + "ERR30 - Expected ';' at end of default for switch statement.")); return error_node(); } @@ -10470,7 +10587,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR27 - Expected '}' at end of switch statement.")); + "ERR31 - Expected '}' at end of switch statement.")); return error_node(); } @@ -10505,7 +10622,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR28 - Unsupported vararg function: " + symbol)); + "ERR32 - Unsupported vararg function: " + symbol)); return error_node(); } @@ -10517,7 +10634,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR29 - Expected '(' for call to vararg function: " + symbol)); + "ERR33 - Expected '(' for call to vararg function: " + symbol)); return error_node(); } @@ -10535,7 +10652,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR30 - Expected ',' for call to vararg function: " + symbol)); + "ERR34 - Expected ',' for call to vararg function: " + symbol)); return error_node(); } } @@ -10546,6 +10663,35 @@ namespace exprtk return result; } + template class Sequence> + inline expression_node_ptr simplify(Sequence& expression_list) + { + if (expression_list.empty()) + return error_node(); + if (expression_list.size() == 1) + return expression_list[0]; + + Sequence tmp_expression_list; + + for (std::size_t i = 0; i < (expression_list.size() - 1); ++i) + { + if (is_variable_node(expression_list[i])) + continue; + else if (is_constant_node(expression_list[i])) + { + free_node(node_allocator_,expression_list[i]); + continue; + } + else + tmp_expression_list.push_back(expression_list[i]); + } + + tmp_expression_list.push_back(expression_list.back()); + expression_list.swap(tmp_expression_list); + return expression_generator_.vararg_function(details::e_multi,expression_list); + } + inline expression_node_ptr parse_multi_sequence(const std::string& source = "") { token_t::token_type close_bracket = token_t::e_rcrlbracket; @@ -10563,7 +10709,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR31 - Expected '"+ details::to_str(close_bracket) + "' for call to multi-sequence" + + "ERR35 - Expected '"+ details::to_str(close_bracket) + "' for call to multi-sequence" + ((!source.empty()) ? std::string(" section of " + source): ""))); return error_node(); } @@ -10583,24 +10729,199 @@ namespace exprtk arg_list.push_back(arg); if (token_is(close_bracket)) break; - else if (!token_is(seperator)) + + bool is_next_close = peek_token_is(close_bracket); + + if (!token_is(seperator) && is_next_close) { set_error( make_error(parser_error::e_syntax, current_token_, - "ERR32 - Expected '"+ details::to_str(seperator) +"' for call to multi-sequence section of " + source)); + "ERR36 - Expected '"+ details::to_str(seperator) +"' for call to multi-sequence section of " + source)); return error_node(); } + + if (token_is(close_bracket)) + break; + } + + result = simplify(arg_list); + + sdd.delete_ptr = (0 == result); + return result; + } + + struct range_pack + { + range_pack() + : n0_e(std::make_pair(false,expression_node_ptr(0))), + n1_e(std::make_pair(false,expression_node_ptr(0))), + n0_c(std::make_pair(false,0)), + n1_c(std::make_pair(false,0)) + {} + + void clear() + { + n0_e = std::make_pair(false,expression_node_ptr(0)); + n1_e = std::make_pair(false,expression_node_ptr(0)); + n0_c = std::make_pair(false,0); + n1_c = std::make_pair(false,0); + } + + void free() + { + if (n0_e.first && n0_e.second) + { + n0_e.first = false; + delete n0_e.second; + } + if (n1_e.first && n1_e.second) + { + n1_e.first = false; + delete n1_e.second; + } } - if (arg_list.empty()) - return error_node(); - else if (arg_list.size() == 1) - result = arg_list[0]; - else - result = expression_generator_.vararg_function(details::e_multi,arg_list); + std::pair n0_e; + std::pair n1_e; + std::pair n0_c; + std::pair n1_c; + }; - sdd.delete_ptr = (0 == result); + inline bool parse_range(range_pack& rp) + { + //Examples of valid ranges: + // 1. [1:5] -> 1..5 + // 2. [ :5] -> 0..5 + // 3. [1: ] -> 1..end + // 4. [x:y] -> x..y where x <= y + // 5. [x+1:y/2] -> x+1..y/2 where x+1 <= y/2 + // 6. [ :y] -> 0..y where 0 <= y + // 7. [x: ] -> x..end where x <= end + + rp.clear(); + + if (!token_is(token_t::e_lsqrbracket)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR37 - Expected '[' for start of range.")); + return false; + } + + if (token_is(token_t::e_colon)) + { + rp.n0_c.first = true; + rp.n0_c.second = 0; + } + else + { + expression_node_ptr r0 = parse_expression(); + + if (0 == r0) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR38 - Failed parse begin section of range.")); + return false; + + } + else if (is_constant_node(r0)) + { + rp.n0_c.first = true; + rp.n0_c.second = static_cast(r0->value()); + free_node(node_allocator_,r0); + } + else + { + rp.n0_e.first = true; + rp.n0_e.second = r0; + } + + if (!token_is(token_t::e_colon)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR39 - Expected ':' for break in range.")); + rp.free(); + return false; + } + } + + if (token_is(token_t::e_rsqrbracket)) + { + rp.n1_c.first = true; + rp.n1_c.second = std::numeric_limits::max(); + } + else + { + expression_node_ptr r1 = parse_expression(); + + if (0 == r1) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR40 - Failed parse end section of range.")); + rp.free(); + return false; + + } + else if (is_constant_node(r1)) + { + rp.n1_c.first = true; + rp.n1_c.second = static_cast(r1->value()); + free_node(node_allocator_,r1); + } + else + { + rp.n1_e.first = true; + rp.n1_e.second = r1; + } + + if (!token_is(token_t::e_rsqrbracket)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR41 - Expected ']' for start of range.")); + rp.free(); + return false; + } + } + + return true; + } + + inline expression_node_ptr parse_string() + { + const std::string symbol = current_token_.value; + expression_node_ptr result = symbol_table_.get_stringvar(symbol); + + if (symbol_name_caching_) + { + symbol_name_cache_.push_back(symbol); + } + if (symbol_table_.is_constant_node(symbol)) + { + result = expression_generator_(dynamic_cast*>(result)->str()); + } + + if (peek_token_is(token_t::e_lsqrbracket)) + { + next_token(); + range_pack rp; + if (!parse_range(rp)) + { + free_node(node_allocator_,result); + return error_node(); + } + } + else + next_token(); return result; } @@ -10619,7 +10940,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR33 - Expected '(' for call to vararg function: " + vararg_function_name)); + "ERR42 - Expected '(' for call to vararg function: " + vararg_function_name)); return error_node(); } @@ -10637,7 +10958,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR34 - Expected ',' for call to vararg function: " + vararg_function_name)); + "ERR43 - Expected ',' for call to vararg function: " + vararg_function_name)); return error_node(); } } @@ -10698,7 +11019,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token_, - "ERR35 - Invalid special function[1]: " + current_token_.value)); + "ERR44 - Invalid special function[1]: " + current_token_.value)); return error_node(); } @@ -10709,7 +11030,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token_, - "ERR36 - Invalid special function[2]: " + current_token_.value)); + "ERR45 - Invalid special function[2]: " + current_token_.value)); return error_node(); } @@ -10752,19 +11073,9 @@ namespace exprtk #ifndef exprtk_disable_string_capabilities // Are we dealing with a string variable? - variable = symbol_table_.get_stringvar(symbol); - if (variable) + if (symbol_table_.is_stringvar(symbol)) { - if (symbol_name_caching_) - { - symbol_name_cache_.push_back(symbol); - } - if (symbol_table_.is_constant_node(symbol)) - { - variable = expression_generator_(dynamic_cast*>(variable)->str()); - } - next_token(); - return variable; + return parse_string(); } #endif @@ -10801,7 +11112,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR37 - Invalid number of parameters for function: " + symbol)); + "ERR46 - Invalid number of parameters for function: " + symbol)); return error_node(); } } @@ -10813,7 +11124,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR38 - Failed to generate node for function: '" + symbol + "'")); + "ERR47 - Failed to generate node for function: '" + symbol + "'")); return error_node(); } } @@ -10833,7 +11144,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR39 - Failed to generate node for vararg function: '" + symbol + "'")); + "ERR48 - Failed to generate node for vararg function: '" + symbol + "'")); return error_node(); } } @@ -10880,7 +11191,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR40 - Failed to create variable: '" + symbol + "'")); + "ERR49 - Failed to create variable: '" + symbol + "'")); return error_node(); } @@ -10889,7 +11200,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR41 - Undefined variable or function: '" + symbol + "'")); + "ERR50 - Undefined variable or function: '" + symbol + "'")); return error_node(); } @@ -10897,6 +11208,7 @@ namespace exprtk { static const std::string symbol_if = "if"; static const std::string symbol_while = "while"; + static const std::string symbol_repeat = "repeat"; static const std::string symbol_switch = "switch"; static const std::string symbol_null = "null"; @@ -10916,6 +11228,10 @@ namespace exprtk { return parse_while_loop(); } + else if (details::imatch(current_token_.value,symbol_repeat)) + { + return parse_repeat_until_loop(); + } else if (details::imatch(current_token_.value,symbol_switch)) { return parse_switch_statement(); @@ -10937,7 +11253,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR41 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); + "ERR51 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); return error_node(); } } @@ -11012,7 +11328,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR42 - Premature end of expression.[1]")); + "ERR52 - Premature end of expression.[1]")); return error_node(); } else @@ -11020,7 +11336,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR43 - Premature end of expression.[2]")); + "ERR53 - Premature end of expression.[2]")); return error_node(); } } @@ -11035,6 +11351,11 @@ namespace exprtk return true; } + inline bool peek_token_is(const typename token_t::token_type& ttype) + { + return (lexer_.peek_next_token().type == ttype); + } + template class expression_generator { @@ -11641,6 +11962,30 @@ namespace exprtk return node_allocator_->allocate(condition,branch); } + inline expression_node_ptr repeat_until_loop(expression_node_ptr condition, + expression_node_ptr branch) const + { + if (details::is_constant_node(condition)) + { + if (details::is_true(condition) && details::is_constant_node(branch)) + { + free_node(*node_allocator_,condition); + return branch; + } + expression_node_ptr result = error_node(); + free_node(*node_allocator_,condition); + free_node(*node_allocator_,branch); + return result; + } + else if (details::is_null_node(condition)) + { + free_node(*node_allocator_,condition); + return branch; + } + else + return node_allocator_->allocate(condition,branch); + } + template class Sequence> inline expression_node_ptr const_optimize_switch(Sequence& arglist) diff --git a/exprtk_simple_example_10.cpp b/exprtk_simple_example_10.cpp index ad8d13f..7643eb0 100644 --- a/exprtk_simple_example_10.cpp +++ b/exprtk_simple_example_10.cpp @@ -50,11 +50,10 @@ void newton_sqrt() " ~{ " " z := 100; " " y := x / 2; " - " while ((z := (z - 1)) > 0) " - " { " + " repeat " " if (equal(y * y,x), z := 0, 0);" - " y := (1 / 2) * (y + (x / y)) " - " } " + " y := (1 / 2) * (y + (x / y)); " + " until ((z := (z - 1)) <= 0) " " }; " "} ", "x","y","z"); diff --git a/exprtk_test.cpp b/exprtk_test.cpp index cb7ab87..f9d1c83 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -968,7 +968,15 @@ static const test_t test_list[] = test_t("switch { case {1 <= 2} : switch { case {1 <= 2} : 1; default: 1.12345; }; default: 1.12345; }",1.0), test_t("switch { case [(1 <= 2)] : {1}; default: 1.12345; }",1.0), test_t("switch { case ([1 > 2]) : [0]; case ([1 <= 2]) : 1; default: 1.12345; }",1.0), - test_t("switch { case {(1 <= 2)} : switch { case ({1 <= 2}) : 1; default: 1.12345; }; default: 1.12345; }",1.0) + test_t("switch { case {(1 <= 2)} : switch { case ({1 <= 2}) : 1; default: 1.12345; }; default: 1.12345; }",1.0), + test_t("repeat 1.1 + 2.2 until (1 < 2)",3.3), + test_t("repeat (1.1 + 2.2) until (1 < 2)",3.3), + test_t("repeat 1.1 + 2.2; until (1 < 2)",3.3), + test_t("repeat (1.1 + 2.2); until (1 < 2)",3.3), + test_t("repeat 1.1234; 1 < 2; 1.1 + 2.2 until (1 < 2)",3.3), + test_t("repeat 1.1234; 1 < 2; (1.1 + 2.2) until (1 < 2)",3.3), + test_t("repeat 1.1234; 1 < 2; 1.1 + 2.2; until (1 < 2)",3.3), + test_t("repeat 1.1234; 1 < 2; (1.1 + 2.2); until (1 < 2)",3.3) }; static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_t); @@ -3737,6 +3745,27 @@ inline bool run_test19() "fibonacci_impl3(x,0,1,0)", "x"); + compositor + .add("fibonacci_impl4", + "switch " + "{ " + " case x == 0 : 0; " + " case x == 1 : 1; " + " default : repeat " + " w := z; " + " z := z + y; " + " y := w; " + " x := x - 1; " + " z " + " until (x <= 1);" + "} ", + "x","y","z","w"); + + compositor + .add("fibonacci4", + "fibonacci_impl4(x,0,1,0)", + "x"); + exprtk::symbol_table& symbol_table = compositor.symbol_table(); symbol_table.add_constants(); @@ -3745,13 +3774,16 @@ inline bool run_test19() std::string expression_str1 = "fibonacci1(x)"; std::string expression_str2 = "fibonacci2(x)"; std::string expression_str3 = "fibonacci3(x)"; + std::string expression_str4 = "fibonacci4(x)"; expression_t expression1; expression_t expression2; expression_t expression3; + expression_t expression4; expression1.register_symbol_table(symbol_table); expression2.register_symbol_table(symbol_table); expression3.register_symbol_table(symbol_table); + expression4.register_symbol_table(symbol_table); exprtk::parser parser; @@ -3779,18 +3811,26 @@ inline bool run_test19() return false; } + if (!parser.compile(expression_str4,expression4)) + { + printf("run_test19() - Error: %s Expression4: %s\n", + parser.error().c_str(), + expression_str4.c_str()); + return false; + } + bool failure = false; const std::size_t fibonacci_list[] = { - 0, 1, 1, 2, - 3, 5, 8, 13, - 21, 34, 55, 89, - 144, 233, 377, 610, - 987, 1597, 2584, 4181, - 6765, 10946, 17711, 28657, - 46368, 75025, 121393, 196418, - 317811, 514229, 832040, 1346269 + 0, 1, 1, 2, + 3, 5, 8, 13, + 21, 34, 55, 89, + 144, 233, 377, 610, + 987, 1597, 2584, 4181, + 6765, 10946, 17711, 28657, + 46368, 75025, 121393, 196418, + 317811, 514229, 832040, 1346269 }; const std::size_t fibonacci_list_size = sizeof(fibonacci_list) / sizeof(std::size_t); @@ -3800,28 +3840,43 @@ inline bool run_test19() T result1 = expression1.value(); T result2 = expression2.value(); T result3 = expression3.value(); + T result4 = expression4.value(); - if ((result1 != result2) || (result1 != result3)) + if ( + (result1 != result2) || + (result1 != result3) || + (result1 != result4) + ) { printf("run_test19() - Error in evaluation! (3) Results don't match! fibonacci(%d) = %d " - "Expression1: %s Expression2: %s Expression3: %s\n", + "Expression1: %s = %d Expression2: %s = %d Expression3: %s = %d Expression4: %s = %d\n", static_cast(i), static_cast(fibonacci_list[i]), expression_str1.c_str(), + static_cast(result1), expression_str2.c_str(), - expression_str3.c_str()); + static_cast(result2), + expression_str3.c_str(), + static_cast(result3), + expression_str4.c_str(), + static_cast(result4)); failure = true; } if (fibonacci_list[i] != expression1.value()) { printf("run_test19() - Error in evaluation! (4) fibonacci(%d) = %d " - "Expression1: %s Expression2: %s Expression3: %s\n", + "Expression1: %s = %d Expression2: %s = %d Expression3: %s = %d Expression4: %s = %d\n", static_cast(i), static_cast(fibonacci_list[i]), expression_str1.c_str(), + static_cast(result1), expression_str2.c_str(), - expression_str3.c_str()); + static_cast(result2), + expression_str3.c_str(), + static_cast(result3), + expression_str4.c_str(), + static_cast(result4)); failure = true; } } @@ -3923,7 +3978,7 @@ struct my_usr : public exprtk::parser::unknown_symbol_resolver error_message = ""; return true; } - else if (unknown_symbol[0] == 'w') + else if (unknown_symbol[0] == 'c') { st = usr_t::e_constant_type; default_value = next_value(); @@ -3966,13 +4021,13 @@ inline bool run_test20() musr.next_value(true); parser.enable_unknown_symbol_resolver(&musr); - std::string expr_str = "v01+w02+v03+w04+v05+w06+v07+w08+v09+w10+" - "v11+w12+v13+w14+v15+w16+v17+w18+v19+w20+" - "v21+w22+v23+w24+v25+w26+v27+w28+v29+w30"; + std::string expr_str = "v01+c02+v03+c04+v05+c06+v07+c08+v09+c10+" + "v11+c12+v13+c14+v15+c16+v17+c18+v19+c20+" + "v21+c22+v23+c24+v25+c26+v27+c28+v29+c30 "; if (!parser.compile(expr_str,expression)) { - printf("run_test18() - Error: %s Expression: %s\n", + printf("run_test20() - Error: %s Expression: %s\n", parser.error().c_str(), expr_str.c_str()); return false; diff --git a/readme.txt b/readme.txt index e4c9d32..902aca2 100644 --- a/readme.txt +++ b/readme.txt @@ -10,7 +10,7 @@ very easily extendible. [01 - CAPABILITIES] -The ExprTk evaluator supports the following fundamental mathematical +The ExprTk evaluator supports the following fundamental arithmetic operations, functions and processes: (0) Basic operators: +, -, *, /, %, ^ @@ -114,7 +114,7 @@ include path (e.g: /usr/include/). [08 - BUILT-IN OPERATIONS & FUNCTIONS] -(0) Basic Operators +(0) Arithmetic Operators +-----------+--------------------------------------------------------+ | OPERATOR | DEFINITION | +-----------+--------------------------------------------------------+ @@ -158,14 +158,14 @@ include path (e.g: /usr/include/). | true | True state or any value other than zero (typically 1). | +-----------+--------------------------------------------------------+ | false | False state, value of zero. | -+-----------|--------------------------------------------------------+ ++-----------+--------------------------------------------------------+ | and | Logical AND, True only if x and y are both true. | | | (eg: x and y) | +-----------+--------------------------------------------------------+ | mand | Multi-input logical AND, True only if all inputs are | | | true. Left to right short-circuiting of expressions. | | | (eg: mand(x > y,z < w,u or v,w and x)) | -+-----------|--------------------------------------------------------+ ++-----------+--------------------------------------------------------+ | mor | Multi-input logical OR, True if at least one of the | | | inputs are true. Left to right short-circuiting of | | | expressions. (eg: mand(x > y,z < w,u or v,w and x)) | @@ -187,25 +187,9 @@ include path (e.g: /usr/include/). | xnor | Logical XNOR, True iff the biconditional of x and y is | | | satisfied. (eg: x xnor y) | +-----------+--------------------------------------------------------+ -| if | If x is true then return y else return z. | -| | (eg: if(x, y, z) or if((x + 1) > 2y, z + 1, w / v)) | -+-----------+--------------------------------------------------------+ -| switch | The first true case condition that is encountered will | -| | determine the result of the switch. If none of the case| -| | conditions hold true, the default action is assumed as | -| | the final return value. This is sometimes also known as| -| | a multi-way branch mechanism. | -| | eg: | -| | switch | -| | { | -| | case x > (y+z) : 2*x/abs(y-z); | -| | case x < 3 : sin(x + y) | -| | default : 1 + x; | -| | } | -+-----------|--------------------------------------------------------+ | & | Similar to AND but with left to right expression short | | | circuiting optimisation. (eg: (x & y) == (y and x)) | -+-----------|--------------------------------------------------------+ ++-----------+--------------------------------------------------------+ | | | Similar to OR but with left to right expression short | | | circuiting optimisation. (eg: (x | y) == (y or x)) | +-----------+--------------------------------------------------------+ @@ -217,7 +201,7 @@ include path (e.g: /usr/include/). | abs | Absolute value of x. | +-----------+--------------------------------------------------------+ | avg | Average of all the inputs. | -| | (eg: avg(x,y,z,w,u,v) == (x+y+z+w+u+v)/6) | +| | (eg: avg(x,y,z,w,u,v) == (x + y + z + w + u + v) / 6) | +-----------+--------------------------------------------------------+ | ceil | Smallest integer that is greater than or equal to x. | +-----------+--------------------------------------------------------+ @@ -255,7 +239,7 @@ include path (e.g: /usr/include/). | min | Smallest value of all the inputs. (eg: min(x,y,z,w,u)) | +-----------+--------------------------------------------------------+ | mul | Product of all the inputs. | -| | (eg: mul(x,y,z,w,u,v,t) == (x*y*z*w*u*v*t)) | +| | (eg: mul(x,y,z,w,u,v,t) == (x * y * z * w * u * v * t))| +-----------+--------------------------------------------------------+ | nequal | Not-equal test between x and y using normalized epsilon| +-----------+--------------------------------------------------------+ @@ -272,17 +256,10 @@ include path (e.g: /usr/include/). | sqrt | Square root of x, where x > 0 | +-----------+--------------------------------------------------------+ | sum | Sum of all the inputs. | -| | (eg: sum(x,y,z,w,u,v,t) == (x+y+z+w+u+v+t)) | +| | (eg: sum(x,y,z,w,u,v,t) == (x + y + z + w + u + v + t))| +-----------+--------------------------------------------------------+ | trunc | Integer portion of x | +-----------+--------------------------------------------------------+ -| ~ | Evaluate each sub-expression, then return as the result| -| | the value of the last sub-expression. This is sometimes| -| | known as multiple sequence point evaluation. | -| | eg: | -| | ~(i:=x+1, j:=y/z, k:=sin(w/u)) == (sin(w/u))) | -| | ~{i:=x+1; j:=y/z; k:=sin(w/u)} == (sin(w/u))) | -+-----------+--------------------------------------------------------+ (4) Trigonometry Functions +-----------+--------------------------------------------------------+ @@ -342,6 +319,55 @@ include path (e.g: /usr/include/). | | (eg: x ilike y or 'a1B2c3D4e5F6g7H' like 'a?d*h') | +-----------+--------------------------------------------------------+ +(6) Control Structures ++-----------+--------------------------------------------------------+ +| STRUCTURE | DEFINITION | ++-----------+--------------------------------------------------------+ +| if | If x is true then return y else return z. | +| | (eg: if(x, y, z) or if((x + 1) > 2y, z + 1, w / v)) | ++-----------+--------------------------------------------------------+ +| switch | The first true case condition that is encountered will | +| | determine the result of the switch. If none of the case| +| | conditions hold true, the default action is assumed as | +| | the final return value. This is sometimes also known as| +| | a multi-way branch mechanism. | +| | eg: | +| | switch | +| | { | +| | case x > (y + z) : 2 * x / abs(y - z); | +| | case x < 3 : sin(x + y) | +| | default : 1 + x; | +| | } | ++-----------+--------------------------------------------------------+ +| while | The structure will repeatedly evaluate the internal | +| | statement(s) 'while' the condition is true. The final | +| | statement in the final iteration will be used as the | +| | return value of the loop. | +| | eg: | +| | while ((x := (x - 1)) > 0) | +| | { | +| | y := x + z; | +| | w := z + y; | +| | } | ++-----------+--------------------------------------------------------+ +| repeat/ | The structure will repeatedly evaluate the internal | +| until | statement(s) 'until' the condition is true. The final | +| | statement in the final iteration will be used as the | +| | return value of the loop. | +| | eg: | +| | repeat | +| | y := x + z; | +| | w := z + y; | +| | until ((x := (x - 1)) <= 0) | ++-----------+--------------------------------------------------------+ +| ~ | Evaluate each sub-expression, then return as the result| +| | the value of the last sub-expression. This is sometimes| +| | known as multiple sequence point evaluation. | +| | eg: | +| | ~(i := x + 1, j := y / z, k := sin(w/u)) == (sin(w/u)))| +| | ~{i := x + 1; j := y / z; k := sin(w/u)} == (sin(w/u)))| ++-----------+--------------------------------------------------------+ + [09 - SPECIAL FUNCTIONS]