From ab7c1677e5faad252bb99d0d65023264f02c5520 Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Sun, 27 Apr 2014 14:15:43 +1000 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 625 +++++++++++++++++++++++++++++++---- exprtk_simple_example_10.cpp | 31 +- exprtk_test.cpp | 74 +++-- readme.txt | 29 +- 4 files changed, 654 insertions(+), 105 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 799e564..34e43fb 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -272,24 +273,23 @@ namespace exprtk static const std::string reserved_words[] = { - "default", "case", "false", "for", "if", "else", "ilike", "in", "like", - "and", "nand", "nor", "not", "null", "or", "repeat", "shl", "shr", "switch", - "true", "until", "while", "xnor", "xor", "&", "|" + "break", "case", "continue", "default", "false", "for", "if", "else", + "ilike", "in", "like", "and", "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); static const std::string reserved_symbols[] = { - "abs", "acos", "acosh", "and", "asin", "asinh", "atan", "atanh", "atan2", - "avg", "case", "ceil", "clamp", "cos", "cosh", "cot", "csc", "default", - "deg2grad", "deg2rad", "equal", "erf", "erfc", "exp", "expm1", "false", - "floor", "for", "frac", "grad2deg", "hypot", "iclamp", "if", "else", "ilike", - "in", "inrange", "like", "log", "log10", "log2", "logn", "log1p", "mand", "max", - "min", "mod", "mor", "mul", "nand", "nor", "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", "&", "|" + "abs", "acos", "acosh", "and", "asin", "asinh", "atan", "atanh", "atan2", "avg", + "break", "case", "ceil", "clamp", "continue", "cos", "cosh", "cot", "csc", "default", + "deg2grad", "deg2rad", "equal", "erf", "erfc", "exp", "expm1", "false", "floor", + "for", "frac", "grad2deg", "hypot", "iclamp", "if", "else", "ilike", "in", "inrange", + "like", "log", "log10", "log2", "logn", "log1p", "mand", "max", "min", "mod", "mor", + "mul", "nand", "nor", "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); @@ -3386,7 +3386,8 @@ namespace exprtk 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_nulleq , e_vecelem + e_sf3ext , e_sf4ext , e_nulleq , e_vecelem , + e_break , e_continue }; typedef T value_type; @@ -3479,6 +3480,18 @@ namespace exprtk return node && (details::expression_node::e_null == node->type()); } + template + inline bool is_break_node(const expression_node* node) + { + return node && (details::expression_node::e_break == node->type()); + } + + template + inline bool is_continue_node(const expression_node* node) + { + return node && (details::expression_node::e_continue == node->type()); + } + template inline bool is_function(const expression_node* node) { @@ -4232,6 +4245,77 @@ namespace exprtk bool consequent_deletable_; }; + #ifndef exprtk_disable_break_continue + template + class break_exception : public std::exception + { + public: + + break_exception(const T& v) + : value(v) + {} + + T value; + }; + + class continue_exception : public std::exception + {}; + + template + class break_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + break_node(expression_ptr ret = expression_ptr(0)) + : return_(ret), + return_deletable_(branch_deletable(return_)) + {} + + ~break_node() + { + if (return_deletable_) + { + delete return_; + } + } + + inline T value() const + { + throw break_exception(return_ ? return_->value() : std::numeric_limits::quiet_NaN()); + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_break; + } + + private: + + expression_ptr return_; + bool return_deletable_; + }; + + template + class continue_node : public expression_node + { + public: + + inline T value() const + { + throw continue_exception(); + return std::numeric_limits::quiet_NaN(); + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_break; + } + }; + #endif + template class while_loop_node : public expression_node { @@ -4436,6 +4520,248 @@ namespace exprtk T* loop_counter_var_; }; + #ifndef exprtk_disable_break_continue + template + class while_loop_bc_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + 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() + { + if (condition_ && condition_deletable_) + { + delete condition_; + } + + if (loop_body_ && loop_body_deletable_) + { + delete loop_body_; + } + } + + inline T value() const + { + T result = T(0); + while (is_true(condition_)) + { + try + { + result = loop_body_->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + return result; + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_while; + } + + private: + + expression_ptr condition_; + expression_ptr loop_body_; + bool condition_deletable_; + bool loop_body_deletable_; + }; + + template + class repeat_until_loop_bc_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + 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() + { + if (condition_ && condition_deletable_) + { + delete condition_; + } + + if (loop_body_ && loop_body_deletable_) + { + delete loop_body_; + } + } + + inline T value() const + { + T result = T(0); + do + { + try + { + result = loop_body_->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + while (is_false(condition_)); + return result; + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_repeat; + } + + private: + + expression_ptr condition_; + expression_ptr loop_body_; + bool condition_deletable_; + bool loop_body_deletable_; + }; + + template + class for_loop_bc_node : public expression_node + { + public: + + typedef expression_node* expression_ptr; + + for_loop_bc_node(expression_ptr initializer, + expression_ptr condition, + expression_ptr incrementor, + expression_ptr loop_body, + expression_ptr loop_var, + T* loop_counter_var = 0) + : initializer_(initializer), + condition_ (condition), + incrementor_(incrementor), + loop_body_ (loop_body), + loop_var_ (loop_var), + initializer_deletable_(branch_deletable(initializer_)), + condition_deletable_ (branch_deletable(condition_ )), + incrementor_deletable_(branch_deletable(incrementor_)), + loop_body_deletable_ (branch_deletable(loop_body_ )), + loop_counter_var_(loop_counter_var) + {} + + ~for_loop_bc_node() + { + if (initializer_ && initializer_deletable_) + { + delete initializer_; + } + + if (condition_ && condition_deletable_) + { + delete condition_; + } + + if (incrementor_ && incrementor_deletable_) + { + delete incrementor_; + } + + if (loop_body_ && loop_body_deletable_) + { + delete loop_body_; + } + + if (loop_var_) + { + delete loop_var_; + } + + if (loop_counter_var_) + { + delete loop_counter_var_; + } + } + + inline T value() const + { + T result = T(0); + + if (initializer_) + initializer_->value(); + + if (incrementor_) + { + while (is_true(condition_)) + { + try + { + result = loop_body_->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + incrementor_->value(); + } + } + else + { + while (is_true(condition_)) + { + try + { + result = loop_body_->value(); + } + catch(const break_exception& e) + { + return e.value; + } + catch(const continue_exception&) + {} + } + } + + return result; + } + + inline typename expression_node::node_type type() const + { + return expression_node::e_for; + } + + private: + + expression_ptr initializer_; + expression_ptr condition_ ; + expression_ptr incrementor_; + expression_ptr loop_body_ ; + expression_ptr loop_var_ ; + bool initializer_deletable_; + bool condition_deletable_ ; + bool incrementor_deletable_; + bool loop_body_deletable_ ; + T* loop_counter_var_; + }; + #endif + template class switch_node : public expression_node { @@ -10575,7 +10901,6 @@ namespace exprtk exprtk::parser_error::to_str(error.mode).c_str(), error.diagnostic.c_str()); } - } template @@ -10624,6 +10949,11 @@ namespace exprtk typedef details::while_loop_node while_loop_node_t; typedef details::repeat_until_loop_node repeat_until_loop_node_t; typedef details::for_loop_node for_loop_node_t; + #ifndef exprtk_disable_break_continue + typedef details::while_loop_bc_node while_loop_bc_node_t; + typedef details::repeat_until_loop_bc_node repeat_until_loop_bc_node_t; + typedef details::for_loop_bc_node for_loop_bc_node_t; + #endif typedef details::switch_node switch_node_t; typedef details::variable_node variable_node_t; typedef details::vector_node vector_node_t; @@ -10808,6 +11138,7 @@ namespace exprtk { error_list_.clear(); synthesis_error_.clear(); + brkcnt_list_.clear(); local_symbol_table_.clear_variables(false); expression_generator_.set_allocator(node_allocator_); @@ -11743,7 +12074,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR22 - Failed to parse body of consequent for if-statement.")); + "ERR22 - Failed to parse body of consequent for if-statement")); result = false; } } @@ -11764,7 +12095,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR23 - Expected ';' at the end of the consequent for if-statement.")); + "ERR23 - Expected ';' at the end of the consequent for if-statement")); result = false; } } @@ -11773,7 +12104,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR24 - Failed to parse body of consequent for if-statement.")); + "ERR24 - Failed to parse body of consequent for if-statement")); result = false; } } @@ -11792,7 +12123,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR25 - Failed to parse body of the 'else' for if-statement.")); + "ERR25 - Failed to parse body of the 'else' for if-statement")); result = false; } } @@ -11803,7 +12134,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR26 - Failed to parse body of if-else statement.")); + "ERR26 - Failed to parse body of if-else statement")); result = false; } } @@ -11814,7 +12145,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR27 - Expected ';' at the end of the 'else-if' for the if-statement.")); + "ERR27 - Expected ';' at the end of the 'else-if' for the if-statement")); result = false; } } @@ -11823,7 +12154,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR28 - Failed to parse body of the 'else' for if-statement.")); + "ERR28 - Failed to parse body of the 'else' for if-statement")); result = false; } } @@ -11959,8 +12290,11 @@ namespace exprtk inline expression_node_ptr parse_while_loop() { // Parse: [while][(][test expr][)][{][expression][}] - expression_node_ptr condition = error_node(); - expression_node_ptr branch = error_node(); + expression_node_ptr condition = error_node(); + expression_node_ptr branch = error_node(); + expression_node_ptr result_node = error_node(); + + bool result = true; next_token(); @@ -11986,30 +12320,43 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR39 - Expected ')' at end of while-statement condition")); - return error_node(); - } - if (0 == (branch = parse_multi_sequence("while-loop"))) - { - set_error( - make_error(parser_error::e_syntax, - current_token_, - "ERR40 - Failed to parse body of while-loop")); - free_node(node_allocator_,condition); - return error_node(); + result = false; } - expression_node_ptr result; + brkcnt_list_.push_front(false); - if (0 == (result = expression_generator_.while_loop(condition,branch))) + if (result) { - set_error( - make_error(parser_error::e_syntax, - current_token_, - "ERR41 - Failed to synthesize while-loop")); + if (0 == (branch = parse_multi_sequence("while-loop"))) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR40 - Failed to parse body of while-loop")); + result = false; + } + else if (0 == (result_node = expression_generator_.while_loop(condition, + branch, + brkcnt_list_.front()))) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR41 - Failed to synthesize while-loop")); + result = false; + } + } + + if (!result) + { + free_node(node_allocator_,condition ); + free_node(node_allocator_,branch ); + free_node(node_allocator_,result_node); + brkcnt_list_.pop_front(); return error_node(); } else - return result; + return result_node; } inline expression_node_ptr parse_repeat_until_loop() @@ -12025,6 +12372,8 @@ namespace exprtk { token_t::token_type seperator = token_t::e_eof; + brkcnt_list_.push_front(false); + for (;;) { expression_node_ptr arg = parse_expression(); @@ -12065,6 +12414,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR43 - Failed to parse body of repeat until loop")); + brkcnt_list_.pop_front(); return error_node(); } } @@ -12075,6 +12425,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR44 - Expected '(' before condition of repeat until loop")); + brkcnt_list_.pop_front(); return error_node(); } else if (0 == (condition = parse_expression())) @@ -12083,6 +12434,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR45 - Failed to parse condition for repeat until loop")); + brkcnt_list_.pop_front(); return error_node(); } else if (!token_is(token_t::e_rbracket)) @@ -12091,21 +12443,28 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR46 - Expected ')' after condition of repeat until loop")); + free_node(node_allocator_,condition); + brkcnt_list_.pop_front(); return error_node(); } expression_node_ptr result; - if (0 == (result = expression_generator_.repeat_until_loop(condition,branch))) + if (0 == (result = expression_generator_.repeat_until_loop(condition,branch,brkcnt_list_.front()))) { set_error( make_error(parser_error::e_syntax, current_token_, "ERR47 - Failed to synthesize repeat until loop")); + free_node(node_allocator_,condition); + brkcnt_list_.pop_front(); return error_node(); } else + { + brkcnt_list_.pop_front(); return result; + } } inline expression_node_ptr parse_for_loop() @@ -12156,7 +12515,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR51 - For-loop variable '" +loop_counter_symbol+ "' is being shadowed by a previous declaration.")); + "ERR51 - For-loop variable '" +loop_counter_symbol+ "' is being shadowed by a previous declaration")); return error_node(); } else if (!symbol_table_.is_variable(loop_counter_symbol)) @@ -12227,6 +12586,7 @@ namespace exprtk if (result) { + brkcnt_list_.push_front(false); if (0 == (loop_body = parse_multi_sequence("for-loop"))) { set_error( @@ -12245,6 +12605,7 @@ namespace exprtk free_node(node_allocator_,incrementor); free_node(node_allocator_,loop_body ); delete loop_counter; + brkcnt_list_.pop_front(); return error_node(); } else @@ -12254,12 +12615,16 @@ namespace exprtk local_symbol_table_.remove_variable(loop_counter_symbol,false); } - return expression_generator_.for_loop(initializer, + expression_node_ptr result_node = + expression_generator_.for_loop(initializer, condition, incrementor, loop_body, loop_var, - loop_counter); + loop_counter, + brkcnt_list_.front()); + brkcnt_list_.pop_front(); + return result_node; } } @@ -12427,7 +12792,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR69 - Expected a 'case' statement for multi-switch.")); + "ERR69 - Expected a 'case' statement for multi-switch")); return error_node(); } @@ -13172,6 +13537,64 @@ namespace exprtk return node_allocator_.allocate >(); } + #ifndef exprtk_disable_break_continue + inline expression_node_ptr parse_break_statement() + { + if (!brkcnt_list_.empty()) + { + next_token(); + brkcnt_list_.front() = true; + expression_node_ptr ret = error_node(); + if (token_is(token_t::e_lsqrbracket)) + { + if (0 == (ret = parse_expression())) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR97 - Failed to parse return expression for 'break' statement")); + return error_node(); + } + else if (!token_is(token_t::e_rsqrbracket)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR98 - Expected ']' at the completed of break's return expression")); + return error_node(); + } + } + return node_allocator_.allocate >(ret); + } + else + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR99 - Invalid use of 'break', allowed only in the scope of a loop")); + } + return error_node(); + } + + inline expression_node_ptr parse_continue_statement() + { + if (!brkcnt_list_.empty()) + { + next_token(); + brkcnt_list_.front() = true; + return node_allocator_.allocate >(); + } + else + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR100 - Invalid use of 'continue', allowed only in the scope of a loop")); + return error_node(); + } + } + #endif + inline expression_node_ptr parse_symtab_symbol() { const std::string symbol = current_token_.value; @@ -13242,7 +13665,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR97 - Invalid number of parameters for function: " + symbol)); + "ERR101 - Invalid number of parameters for function: " + symbol)); return error_node(); } } @@ -13254,7 +13677,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR98 - Failed to generate node for function: '" + symbol + "'")); + "ERR102 - Failed to generate node for function: '" + symbol + "'")); return error_node(); } } @@ -13274,7 +13697,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR99 - Failed to generate node for vararg function: '" + symbol + "'")); + "ERR103 - Failed to generate node for vararg function: '" + symbol + "'")); return error_node(); } } @@ -13285,6 +13708,15 @@ namespace exprtk return parse_vector(); } + if (details::is_reserved_symbol(symbol)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR104 - Invalid use of reserved symbol '" + symbol + "'")); + return error_node(); + } + // Should we handle unknown symbols? if (resolve_unknown_symbol_ && unknown_symbol_resolver_) { @@ -13324,7 +13756,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR100 - Failed to create variable: '" + symbol + "'")); + "ERR105 - Failed to create variable: '" + symbol + "'")); return error_node(); } @@ -13333,18 +13765,20 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR101 - Undefined variable or function: '" + symbol + "'")); + "ERR106 - Undefined variable or function: '" + symbol + "'")); return error_node(); } inline expression_node_ptr parse_symbol() { - 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_for = "for" ; - static const std::string symbol_switch = "switch"; - static const std::string symbol_null = "null" ; + 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_for = "for" ; + static const std::string symbol_switch = "switch" ; + static const std::string symbol_null = "null" ; + static const std::string symbol_break = "break" ; + static const std::string symbol_continue = "continue"; if (valid_vararg_operation(current_token_.value)) { @@ -13382,6 +13816,16 @@ namespace exprtk { return parse_null_statement(); } + #ifndef exprtk_disable_break_continue + else if (details::imatch(current_token_.value,symbol_break)) + { + return parse_break_statement(); + } + else if (details::imatch(current_token_.value,symbol_continue)) + { + return parse_continue_statement(); + } + #endif else if (symbol_table_.valid()) { return parse_symtab_symbol(); @@ -13391,7 +13835,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR102 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); + "ERR107 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); return error_node(); } } @@ -13415,7 +13859,7 @@ namespace exprtk set_error( make_error(parser_error::e_numeric, current_token_, - "ERR103 - Failed to convert '" + current_token_.value + "' to a number.")); + "ERR108 - Failed to convert '" + current_token_.value + "' to a number")); return error_node(); } } @@ -13439,7 +13883,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR104 - Expected ')' instead of: '" + current_token_.value + "'")); + "ERR109 - Expected ')' instead of: '" + current_token_.value + "'")); return error_node(); } } @@ -13453,7 +13897,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR105 - Expected ']' instead of: '" + current_token_.value + "'")); + "ERR110 - Expected ']' instead of: '" + current_token_.value + "'")); return error_node(); } } @@ -13467,7 +13911,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR106 - Expected '}' instead of: '" + current_token_.value + "'")); + "ERR111 - Expected '}' instead of: '" + current_token_.value + "'")); return error_node(); } } @@ -13490,7 +13934,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR107 - Premature end of expression[1]")); + "ERR112 - Premature end of expression[1]")); return error_node(); } else @@ -13498,7 +13942,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR108 - Premature end of expression[2]")); + "ERR113 - Premature end of expression[2]")); return error_node(); } @@ -13774,6 +14218,10 @@ namespace exprtk return error_node(); else if (details::is_null_node(branch[0])) return branch[0]; + else if (details::is_break_node(branch[0])) + return error_node(); + else if (details::is_continue_node(branch[0])) + return error_node(); else if (details::is_constant_node(branch[0])) return synthesize_expression(operation,branch); else if (unary_optimizable(operation) && details::is_variable_node(branch[0])) @@ -13990,6 +14438,16 @@ namespace exprtk ); } + inline bool is_invalid_break_continue_op(expression_node_ptr (&branch)[2]) + { + return ( + details::is_break_node (branch[0]) || + details::is_break_node (branch[1]) || + details::is_continue_node(branch[0]) || + details::is_continue_node(branch[1]) + ); + } + inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) { const bool b0_string = is_generally_string_node(branch[0]); @@ -14064,6 +14522,8 @@ namespace exprtk return error_node(); else if (is_invalid_assignment_op(operation,branch)) return error_node(); + else if (is_invalid_break_continue_op(branch)) + return error_node(); else if (details::e_assign == operation) return synthesize_assignment_expression(operation,branch); else if (is_assignment_operation(operation)) @@ -14185,7 +14645,8 @@ namespace exprtk } inline expression_node_ptr while_loop(expression_node_ptr condition, - expression_node_ptr branch) const + expression_node_ptr branch, + const bool brkcont = false) const { if (details::is_constant_node(condition)) { @@ -14204,12 +14665,19 @@ namespace exprtk free_node(*node_allocator_,condition); return branch; } - else + else if (!brkcont) return node_allocator_->allocate(condition,branch); + #ifndef exprtk_disable_break_continue + else + return node_allocator_->allocate(condition,branch); + #else + return error_node(); + #endif } inline expression_node_ptr repeat_until_loop(expression_node_ptr condition, - expression_node_ptr branch) const + expression_node_ptr branch, + const bool brkcont = false) const { if (details::is_constant_node(condition)) { @@ -14228,8 +14696,14 @@ namespace exprtk free_node(*node_allocator_,condition); return branch; } - else + else if (!brkcont) return node_allocator_->allocate(condition,branch); + #ifndef exprtk_disable_break_continue + else + return node_allocator_->allocate(condition,branch); + #else + return error_node(); + #endif } inline expression_node_ptr for_loop(expression_node_ptr initializer, @@ -14237,7 +14711,8 @@ namespace exprtk expression_node_ptr incrementor, expression_node_ptr loop_body, expression_node_ptr loop_var, - T* loop_counter) const + T* loop_counter, + bool brkcont = false) const { if (details::is_constant_node(condition)) { @@ -14262,13 +14737,24 @@ namespace exprtk free_node(*node_allocator_,incrementor); return loop_body; } - else + else if (!brkcont) return node_allocator_->allocate(initializer, condition, incrementor, loop_body, loop_var, loop_counter); + #ifndef exprtk_disable_break_continue + else + return node_allocator_->allocate(initializer, + condition, + incrementor, + loop_body, + loop_var, + loop_counter); + #else + return error_node(); + #endif } template local_var_set_; std::deque symbol_name_cache_; std::deque error_list_; + std::deque brkcnt_list_; bool resolve_unknown_symbol_; unknown_symbol_resolver* unknown_symbol_resolver_; unknown_symbol_resolver default_usr_; diff --git a/exprtk_simple_example_10.cpp b/exprtk_simple_example_10.cpp index 7cb9409..76dcf8e 100644 --- a/exprtk_simple_example_10.cpp +++ b/exprtk_simple_example_10.cpp @@ -41,21 +41,22 @@ void newton_sqrt() compositor .add("newton_sqrt_impl", - "switch " - "{ " - " case x < 0 : -inf; " - " case x == 0 : 0; " - " case x == 1 : 1; " - " default: " - " ~{ " - " z := 100; " - " y := x / 2; " - " repeat " - " if (equal(y * y,x), z := 0, 0);" - " y := (1 / 2) * (y + (x / y)); " - " until ((z -= 1) <= 0) " - " }; " - "} ", + "switch " + "{ " + " case x < 0 : -inf; " + " case x == 0 : 0; " + " case x == 1 : 1; " + " default: " + " ~{ " + " z := 100; " + " y := x / 2; " + " repeat " + " y := (1 / 2) * (y + (x / y)); " + " if (equal(y * y,x)) " + " break[y]; " + " until ((z -= 1) <= 0); " + " }; " + "} ", "x","y","z"); compositor diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 342028e..1ebe66a 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -1521,7 +1521,9 @@ inline bool run_test01() test_xy("(x /= 2y) == (1/6)" ,T(1),T(3),T(1)), test_xy("for(i := 0; (i < 10);) { i += 1; }; x;" ,T(1),T(20),T( 1)), test_xy("for(i := 0; (i < 10) and (i != y); i+=2) { x += i; }; x;" ,T(1),T(20),T(21)), - test_xy("for(i := 0; (i < 10) and (i != y);) { x += i; i+=2; }; x;",T(1),T(20),T(21)) + test_xy("for(i := 0; (i < 10) and (i != y);) { x += i; i+=2; }; x;",T(1),T(20),T(21)), + test_xy("for(i := 0; (i < y); i += 1) { if (i <= (y / 2)) x += i; else break; }; x;" ,T(0),T(10),T(15)), + test_xy("for(i := 0; (i < y); i += 1) { if (i <= (y / 2)) continue; else x += i; }; x;" ,T(0),T(10),T(30)) }; static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_xy); @@ -4317,6 +4319,39 @@ inline bool run_test19() "} ", "x"); + compositor + .add("is_prime_impl4", + "switch " + "{ " + " case 1 == x : false; " + " case 2 == x : true; " + " case 3 == x : true; " + " case 5 == x : true; " + " case 7 == x : true; " + " case 0 == x % 2 : false; " + " default : " + " { " + " for (i := 3; i < y; i += 2) " + " { " + " if ((x % i) == 0) " + " break[false]; " + " else " + " true; " + " } " + " }; " + "} ", + "x","y"); + + compositor + .add("is_prime4", + "switch " + "{ " + " case x <= 0 : false; " + " case frac(x) != 0 : false; " + " default : is_prime_impl4(x,min(x - 1,trunc(sqrt(x)) + 1));" + "} ", + "x"); + symbol_table_t& symbol_table = compositor.symbol_table(); symbol_table.add_constants(); symbol_table.add_variable("x",x); @@ -4324,7 +4359,8 @@ inline bool run_test19() const std::string expression_str[] = { "is_prime1(x)", "is_prime2(x)", - "is_prime3(x)" + "is_prime3(x)", + "is_prime4(x)" }; const std::size_t expression_count = sizeof(expression_str) / sizeof(std::string); @@ -4615,24 +4651,22 @@ inline bool run_test19() compositor .add("newton_sqrt_impl", - "switch " - "{ " - " case x < 0 : -inf; " - " case x == 0 : 0; " - " case x == 1 : 1; " - " default: " - " ~{ " - " z := 100; " - " y := x / 2; " - " while ((z := (z - 1)) > 0) " - " { " - " if (equal(y^2,x)) " - " y := y + (z := 0); " - " else " - " y := (1 / 2) * (y + (x / y)); " - " } " - " }; " - "} ", + "switch " + "{ " + " case x < 0 : -inf; " + " case x == 0 : 0; " + " case x == 1 : 1; " + " default: " + " ~{ " + " z := 100; " + " y := x / 2; " + " repeat " + " y := (1 / 2) * (y + (x / y)); " + " if (equal(y * y,x)) " + " break[y]; " + " until ((z -= 1) <= 0); " + " }; " + "} ", "x","y","z"); compositor diff --git a/readme.txt b/readme.txt index ac0aeb8..670107b 100644 --- a/readme.txt +++ b/readme.txt @@ -33,7 +33,7 @@ arithmetic operations, functions and processes: (5) Conditional, Switch & Loop statements: if-then-else, ternary conditional, switch-case, - while, for, repeat-until + while, for, repeat-until, break, continue (6) Assignment: :=, +=, -=, *=, /= @@ -456,6 +456,33 @@ include path (e.g: /usr/include/). | | w := u + y; | | | } | +----------+---------------------------------------------------------+ +| break | Break terminates the execution of the nearest enclosed | +| break[] | loop, allowing for the execution to continue on external| +| | to the loop. The default break statement will set the | +| | return value of the loop to NaN, where as the return | +| | based form will set the value to that of the break | +| | expression. | +| | eg: | +| | while ((i += 1) < 10) | +| | { | +| | if (i < 5) | +| | j -= i + 2; | +| | else if (i % 2 == 0) | +| | break; | +| | else | +| | break[2i + 3]; | +| | } | ++----------+---------------------------------------------------------+ +| continue | Continue results in the remaining portion of the nearest| +| | enclosing loop body to be skipped. | +| | eg: | +| | for (i := 0; i < 10; i+= 1) | +| | { | +| | if (i < 5) | +| | continue; | +| | j -= i + 2; | +| | } | ++----------+---------------------------------------------------------+ | ?: | Ternary conditional statement, similar to that of the | | | above denoted if-statement. | | | eg: |