From 371d28c3a861b4f223319a83fbac9b4a60b2a778 Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Mon, 22 Dec 2014 22:00:49 +1100 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 162 ++++++++++++++++++++++++++++++++++++------------ exprtk_test.cpp | 70 +++++++++++++++++++-- readme.txt | 14 +++-- 3 files changed, 197 insertions(+), 49 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 7de78e4..7d861a8 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -449,8 +449,6 @@ namespace exprtk { if (str.empty() || pattern.empty()) return false; - else if (str.size() < pattern.size()) - return false; else if ('*' == pattern[0]) return false; @@ -504,8 +502,11 @@ namespace exprtk } return ( - (p_end == p_itr) && - (s_end == s_itr) + (s_end == s_itr) && + ( + (p_end == p_itr) || + ('*' == *p_itr) + ) ); } @@ -12610,14 +12611,23 @@ namespace exprtk template inline bool is_generally_string_node(const expression_node* node) { - return is_string_node (node) || - is_const_string_node (node) || - is_string_range_node (node) || - is_const_string_range_node(node) || - is_genricstring_range_node(node) || - is_string_assignment_node (node) || - is_string_concat_node (node) || - is_string_function_node (node) ; + if (node) + { + switch (node->type()) + { + case expression_node::e_stringvar : + case expression_node::e_stringconst : + case expression_node::e_stringvarrng : + case expression_node::e_cstringvarrng : + case expression_node::e_strgenrange : + case expression_node::e_strass : + case expression_node::e_strconcat : + case expression_node::e_strfunction : return true; + default : return false; + } + } + + return false; } class node_allocator @@ -15082,7 +15092,7 @@ namespace exprtk T* data = (T*)(element_[i].data); - switch(element_[i].type) + switch (element_[i].type) { case scope_element::e_variable : delete data; break; case scope_element::e_vector : delete [] data; break; @@ -17587,7 +17597,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR83 - Failed to generate string range node.")); + "ERR83 - Failed to generate string range node")); free_node(node_allocator_,expression); } @@ -18168,7 +18178,7 @@ namespace exprtk param_seq, diff_index,diff_value); - if (result) + if (result) { pseq_index = i; return true; @@ -18182,9 +18192,9 @@ namespace exprtk parser_. set_error( make_error(parser_error::e_syntax, - parser_.current_token(), - "ERR100 - Failed parameter type check for function '" + function_name_ + "', " - "Expected '" + param_seq_list_[0] + "' call set: '" + param_seq +"'")); + parser_.current_token(), + "ERR100 - Failed parameter type check for function '" + function_name_ + "', " + "Expected '" + param_seq_list_[0] + "' call set: '" + param_seq +"'")); } else { @@ -18202,9 +18212,9 @@ namespace exprtk parser_. set_error( make_error(parser_error::e_syntax, - parser_.current_token(), - "ERR101 - Failed parameter type check for function '" + function_name_ + "', " - "Best match: '" + param_seq_list_[max_diff_index] + "' call set: '" + param_seq +"'")); + parser_.current_token(), + "ERR101 - Failed parameter type check for function '" + function_name_ + "', " + "Best match: '" + param_seq_list_[max_diff_index] + "' call set: '" + param_seq +"'")); } return false; @@ -19358,7 +19368,7 @@ namespace exprtk return node_allocator_.allocate >(variable0,variable1); } - inline bool post_variable_process() + inline bool post_variable_process(const std::string& symbol) { if ( peek_token_is(token_t::e_lbracket ) || @@ -19367,7 +19377,14 @@ namespace exprtk ) { if (!commutative_check_enabled()) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR154 - Invalid sequence of variable '"+ symbol + "' and bracket")); + return false; + } lexer_.insert_front(token_t::e_mul); } @@ -19375,6 +19392,55 @@ namespace exprtk return true; } + inline bool post_bracket_process(const typename token_t::token_type& token, expression_node_ptr& branch) + { + bool implied_mul = false; + + if (is_generally_string_node(branch)) + return true; + + switch (token) + { + case token_t::e_lcrlbracket : implied_mul = token_is(token_t::e_lbracket ,false) || + token_is(token_t::e_lcrlbracket,false) || + token_is(token_t::e_lsqrbracket,false) ; + break; + + case token_t::e_lbracket : implied_mul = token_is(token_t::e_lbracket ,false) || + token_is(token_t::e_lcrlbracket,false) || + token_is(token_t::e_lsqrbracket,false) ; + break; + + case token_t::e_lsqrbracket : implied_mul = token_is(token_t::e_lbracket ,false) || + token_is(token_t::e_lcrlbracket,false) || + token_is(token_t::e_lsqrbracket,false) ; + break; + + default : return true; + } + + if (implied_mul) + { + if (!commutative_check_enabled()) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR155 - Invalid sequence of brackets")); + + return false; + } + else if (token_t::e_eof != current_token_.type) + { + lexer_.insert_front(current_token_.type); + lexer_.insert_front(token_t::e_mul); + next_token(); + } + } + + return true; + } + inline expression_node_ptr parse_symtab_symbol() { const std::string symbol = current_token_.value; @@ -19389,7 +19455,7 @@ namespace exprtk variable = expression_generator_(variable->value()); } - if (!post_variable_process()) + if (!post_variable_process(symbol)) return error_node(); lodge_symbol(symbol,e_st_variable); @@ -19410,7 +19476,7 @@ namespace exprtk se.active = true; lodge_symbol(symbol,e_st_local_variable); - if (!post_variable_process()) + if (!post_variable_process(symbol)) return error_node(); next_token(); @@ -19450,7 +19516,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR154 - Failed to generate node for function: '" + symbol + "'")); + "ERR156 - Failed to generate node for function: '" + symbol + "'")); return error_node(); } @@ -19475,7 +19541,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR155 - Failed to generate node for vararg function: '" + symbol + "'")); + "ERR157 - Failed to generate node for vararg function: '" + symbol + "'")); return error_node(); } @@ -19500,7 +19566,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR156 - Failed to generate node for generic function: '" + symbol + "'")); + "ERR158 - Failed to generate node for generic function: '" + symbol + "'")); return error_node(); } @@ -19525,7 +19591,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR157 - Failed to generate node for string function: '" + symbol + "'")); + "ERR159 - Failed to generate node for string function: '" + symbol + "'")); return error_node(); } @@ -19544,7 +19610,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR158 - Invalid use of reserved symbol '" + symbol + "'")); + "ERR160 - Invalid use of reserved symbol '" + symbol + "'")); return error_node(); } @@ -19584,7 +19650,7 @@ namespace exprtk lodge_symbol(symbol,e_st_variable); - if (!post_variable_process()) + if (!post_variable_process(symbol)) return error_node(); next_token(); @@ -19596,7 +19662,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR159 - Failed to create variable: '" + symbol + "'")); + "ERR161 - Failed to create variable: '" + symbol + "'")); return error_node(); } @@ -19605,7 +19671,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR160 - Undefined symbol: '" + symbol + "'")); + "ERR162 - Undefined symbol: '" + symbol + "'")); return error_node(); } @@ -19686,7 +19752,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR161 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); + "ERR163 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); return error_node(); } @@ -19711,7 +19777,7 @@ namespace exprtk set_error( make_error(parser_error::e_numeric, current_token_, - "ERR162 - Failed to convert '" + current_token_.value + "' to a number")); + "ERR164 - Failed to convert '" + current_token_.value + "' to a number")); return error_node(); } @@ -19737,10 +19803,16 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR163 - Expected ')' instead of: '" + current_token_.value + "'")); + "ERR165 - Expected ')' instead of: '" + current_token_.value + "'")); free_node(node_allocator_,branch); + return error_node(); + } + else if (!post_bracket_process(token_t::e_lbracket,branch)) + { + free_node(node_allocator_,branch); + return error_node(); } } @@ -19755,10 +19827,16 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR164 - Expected ']' instead of: '" + current_token_.value + "'")); + "ERR166 - Expected ']' instead of: '" + current_token_.value + "'")); free_node(node_allocator_,branch); + return error_node(); + } + else if (!post_bracket_process(token_t::e_lsqrbracket,branch)) + { + free_node(node_allocator_,branch); + return error_node(); } } @@ -19773,10 +19851,16 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR165 - Expected '}' instead of: '" + current_token_.value + "'")); + "ERR167 - Expected '}' instead of: '" + current_token_.value + "'")); free_node(node_allocator_,branch); + return error_node(); + } + else if (!post_bracket_process(token_t::e_lcrlbracket,branch)) + { + free_node(node_allocator_,branch); + return error_node(); } } @@ -19806,7 +19890,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR166 - Premature end of expression[1]")); + "ERR168 - Premature end of expression[1]")); return error_node(); } @@ -19815,7 +19899,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR167 - Premature end of expression[2]")); + "ERR169 - Premature end of expression[2]")); return error_node(); } diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 61fab13..4f90994 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -1276,6 +1276,42 @@ inline bool run_test01() test_xy(" x (y) == (x*y)" ,T(2.0),T(3.0),T(1.0)), test_xy(" ((x) y) == (x*y)" ,T(2.0),T(3.0),T(1.0)), test_xy(" (x (y)) == (x*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" (x)3 == (x*3)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" x(3) == (x*3)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" (x) 3 == (x*3)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" x (3) == (x*3)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" ((x) 3) == (x*3)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" (x (3)) == (x*3)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" (2)y == (2*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" 2(y) == (2*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" (2) y == (2*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" 2 (y) == (2*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" ((2) y) == (2*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy(" (2 (y)) == (2*y)" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; (a)(3) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; (a){3} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; (a)[3] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; {a}(3) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; {a}{3} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; {a}[3] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; (a)(b) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; (a){b} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; (a)[b] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; {a}(b) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; {a}{b} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; {a}[b] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; (a)(a+1) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; (a){a+1} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; (a)[a+1] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; {a}(a+1) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; {a}{a+1} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; {a}[a+1] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; (b-1)(b) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; (b-1){b} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; (b-1)[b] == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; {b-1}(b) == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; {b-1}{b} == 6" ,T(2.0),T(3.0),T(1.0)), + test_xy("var a := 2; var b := 3; {b-1}[b] == 6" ,T(2.0),T(3.0),T(1.0)), test_xy("equal(x^2.2^1.1,17.15193942371376191362)" ,T(3.3),T(0.0),T(1.0)), test_xy("equal(3.3^x^1.1,17.15193942371376191362)" ,T(2.2),T(0.0),T(1.0)), test_xy("equal(3.3^2.2^x,17.15193942371376191362)" ,T(1.1),T(0.0),T(1.0)), @@ -4725,6 +4761,7 @@ struct inc_func : public exprtk::igeneric_function case generic_type::e_vector : { vector_t vector(gt); + for (std::size_t x = 0; x < vector.size(); ++x) { vector[x] += T(1); @@ -4734,6 +4771,7 @@ struct inc_func : public exprtk::igeneric_function case generic_type::e_string : { string_t string(gt); + for (std::size_t x = 0; x < string.size(); ++x) { string[x] += static_cast(1); @@ -5021,12 +5059,23 @@ inline bool run_test18() "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v1 + v2, v0[2], x, 2x + y, z, 2w / 3, 'abc123',s0[2:5]);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v1 + v2, v0[2], x, 2x + y, z, 2w / 3, 'abc123',s0[2:5]);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(x);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(x,x);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(x,x,x);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(x,x,x,x);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0,s0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0,s0,s0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0,s0,s0,s0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v0,v0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v0,v0,v0);", + "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v1 + v2, v0[2], x, 2x + y, z, 2w / 3, 'abc123',s0[2:5]);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v1 + v2, v0[2], x, 2x + y, z, 2w / 3, 'abc123',s0[2:5]);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v1 + v2, v0[2], x, 2x + y, z, 2w / 3, 'abc123',s0[2:5]);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(v0,v1 + v2, v0[2], x, 2x + y, z, 2w / 3, 'abc123',s0[2:5]);" - - }; static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string); @@ -5047,10 +5096,24 @@ inline bool run_test18() "V*TTTTVSS", "VVT*VSS" , "VVTTTTVS*", + + "T*", + "T*", + "T*", + "T*", + "S*", + "S*", + "S*", + "S*", + "V*", + "V*", + "V*", + "V*", + "TTTTTTT|STSTSTS|V*T*VS*" , "TTTTTTT|STSTSTS|V*TTTTVSS", "TTTTTTT|STSTSTS|VVT*VSS" , - "TTTTTTT|STSTSTS|VVTTTTVS*" + "TTTTTTT|STSTSTS|VVTTTTVS*", }; bool failure = false; @@ -5262,7 +5325,6 @@ inline bool run_test18() std::size_t parameter_type_list_size = sizeof(parameter_type_list) / sizeof(std::string); - for (std::size_t i = 0; i < parameter_type_list_size; ++i) { expression_t expression; diff --git a/readme.txt b/readme.txt index b8fafdc..18176f4 100644 --- a/readme.txt +++ b/readme.txt @@ -1026,8 +1026,8 @@ with vectors: (c) Assignment: :=, +=, -=, *=, /=, %=, <=> (d) Inequalities: <, <=, >, >=, ==, = (e) Unary operations: - abs, acos, acosh, asin, asinh, atan, atanh, ceil, cos, cosh, - cot, csc, deg2grad, deg2rad, erf, erfc, exp, expm1, floor, + abs, acos, acosh, asin, asinh, atan, atanh, ceil, cos, cosh, + cot, csc, deg2grad, deg2rad, erf, erfc, exp, expm1, floor, frac, grad2deg, log, log10, log1p, log2, rad2deg, round, sec, sgn, sin, sinc, sinh, sqrt, swap, tan, tanh, trunc (f) Aggregate and Reduce operations: @@ -1403,10 +1403,12 @@ parameters in the following sequence: A final piece of type checking functionality is available for the -scenarios where a single function name is intended to be used for -multiple distinct parameter sequences. Two specific overrides of the -function operator are provided one for standard generic functions and -one for string returning functions. The overrides are as follows: +scenarios where a single function name is intended to be used for +multiple distinct parameter sequences. The parameter sequences are +passed to the constructor as a single string delimited by the pipe '|' +character. Two specific overrides of the function operator are +provided one for standard generic functions and one for string +returning functions. The overrides are as follows: // f(psi,i_0,i_1,....,i_N) --> Scalar inline T operator()(const std::size_t& ps_index,