From e5f917033f6ac680312a196aa5babd945cf5d962 Mon Sep 17 00:00:00 2001 From: Arash Partow Date: Mon, 7 Apr 2014 07:01:02 +1000 Subject: [PATCH] C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html --- exprtk.hpp | 343 ++++++++++++++++++++++++++++++------------------ exprtk_test.cpp | 55 +++++++- readme.txt | 63 ++++++++- 3 files changed, 323 insertions(+), 138 deletions(-) diff --git a/exprtk.hpp b/exprtk.hpp index 249296c..710e6a4 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -18,7 +18,7 @@ * (03) 1 - sin(2 * x) + cos(pi / y) * * (04) a * exp(2 * t) + c * * (05) if(((x + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z) * - * (06) if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x * + * (06) (avg(x,y) <= x + y ? x - y : x * y) + 2 * pi / x * * (07) z := x + sin(2 * pi / y) * * (08) u := 2 * (pi * z) / (w := x + cos(y / pi)) * * (09) clamp(-1,sin(2 * pi * x) + cos(y / 2 * pi),+1) * @@ -1514,7 +1514,8 @@ namespace exprtk 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_mod = '%', e_pow = '^', e_colon = ':', + e_ternary = '?' }; token() @@ -1635,6 +1636,7 @@ namespace exprtk case e_mod : return "%"; case e_pow : return "^"; case e_colon : return ":"; + case e_ternary : return "?"; default : return "UNKNOWN"; } } @@ -2765,30 +2767,33 @@ namespace exprtk sequence_validator() : lexer::token_scanner(2) { - add_invalid(lexer::token::e_number,lexer::token::e_number); - add_invalid(lexer::token::e_string,lexer::token::e_string); - add_invalid(lexer::token::e_number,lexer::token::e_string); - add_invalid(lexer::token::e_string,lexer::token::e_number); - add_invalid(lexer::token::e_string,lexer::token::e_colon ); - add_invalid(lexer::token::e_colon ,lexer::token::e_string); - add_invalid(lexer::token::e_assign,lexer::token::e_string); - add_invalid_set1(lexer::token::e_assign); - add_invalid_set1(lexer::token::e_shr ); - add_invalid_set1(lexer::token::e_shl ); - add_invalid_set1(lexer::token::e_lte ); - add_invalid_set1(lexer::token::e_ne ); - add_invalid_set1(lexer::token::e_gte ); - add_invalid_set1(lexer::token::e_lt ); - add_invalid_set1(lexer::token::e_gt ); - add_invalid_set1(lexer::token::e_eq ); - add_invalid_set1(lexer::token::e_comma ); - add_invalid_set1(lexer::token::e_add ); - add_invalid_set1(lexer::token::e_sub ); - add_invalid_set1(lexer::token::e_div ); - add_invalid_set1(lexer::token::e_mul ); - add_invalid_set1(lexer::token::e_mod ); - add_invalid_set1(lexer::token::e_pow ); - add_invalid_set1(lexer::token::e_colon ); + add_invalid(lexer::token::e_number ,lexer::token::e_number ); + add_invalid(lexer::token::e_string ,lexer::token::e_string ); + add_invalid(lexer::token::e_number ,lexer::token::e_string ); + add_invalid(lexer::token::e_string ,lexer::token::e_number ); + add_invalid(lexer::token::e_string ,lexer::token::e_colon ); + add_invalid(lexer::token::e_string ,lexer::token::e_ternary); + add_invalid(lexer::token::e_colon ,lexer::token::e_string ); + add_invalid(lexer::token::e_ternary,lexer::token::e_string ); + add_invalid(lexer::token::e_assign ,lexer::token::e_string ); + add_invalid_set1(lexer::token::e_assign ); + add_invalid_set1(lexer::token::e_shr ); + add_invalid_set1(lexer::token::e_shl ); + add_invalid_set1(lexer::token::e_lte ); + add_invalid_set1(lexer::token::e_ne ); + add_invalid_set1(lexer::token::e_gte ); + add_invalid_set1(lexer::token::e_lt ); + add_invalid_set1(lexer::token::e_gt ); + add_invalid_set1(lexer::token::e_eq ); + add_invalid_set1(lexer::token::e_comma ); + add_invalid_set1(lexer::token::e_add ); + add_invalid_set1(lexer::token::e_sub ); + add_invalid_set1(lexer::token::e_div ); + add_invalid_set1(lexer::token::e_mul ); + add_invalid_set1(lexer::token::e_mod ); + add_invalid_set1(lexer::token::e_pow ); + add_invalid_set1(lexer::token::e_colon ); + add_invalid_set1(lexer::token::e_ternary); } bool result() @@ -2878,13 +2883,14 @@ namespace exprtk { switch (t) { - case lexer::token::e_number : return false; - case lexer::token::e_symbol : return false; - case lexer::token::e_string : return false; - case lexer::token::e_add : return false; - case lexer::token::e_sub : return false; - case lexer::token::e_colon : return false; - default : return true; + case lexer::token::e_number : return false; + case lexer::token::e_symbol : return false; + case lexer::token::e_string : return false; + case lexer::token::e_add : return false; + case lexer::token::e_sub : return false; + case lexer::token::e_colon : return false; + case lexer::token::e_ternary : return false; + default : return true; } } } @@ -2892,12 +2898,13 @@ namespace exprtk { switch (base) { - case lexer::token::e_number : return false; - case lexer::token::e_symbol : return false; - case lexer::token::e_string : return false; - case lexer::token::e_eof : return false; - case lexer::token::e_colon : return false; - default : return true; + case lexer::token::e_number : return false; + case lexer::token::e_symbol : return false; + case lexer::token::e_string : return false; + case lexer::token::e_eof : return false; + case lexer::token::e_colon : return false; + case lexer::token::e_ternary : return false; + default : return true; } } else if (details::is_left_bracket(static_cast(t))) @@ -10776,7 +10783,13 @@ namespace exprtk return error_node(); } else + { expression = new_expression; + if (token_is(token_t::e_ternary,false) && (precedence == e_level00)) + { + expression = parse_ternary_conditional_statement(expression); + } + } } return expression; @@ -11071,6 +11084,8 @@ namespace exprtk expression_node_ptr consequent = error_node(); expression_node_ptr alternative = error_node(); + bool result = true; + next_token(); if (!token_is(token_t::e_lbracket)) @@ -11095,7 +11110,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR19 - Expected ',' between if-statement condition and consequent")); - return error_node(); + result = false; } else if (0 == (consequent = parse_expression())) { @@ -11103,7 +11118,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR20 - Failed to parse consequent for if-statement")); - return error_node(); + result = false; } else if (!token_is(token_t::e_comma)) { @@ -11111,8 +11126,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR21 - Expected ',' between if-statement consequent and alternative")); - - return error_node(); + result = false; } else if (0 == (alternative = parse_expression())) { @@ -11120,7 +11134,7 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR22 - Failed to parse alternative for if-statement")); - return error_node(); + result = false; } else if (!token_is(token_t::e_rbracket)) { @@ -11128,6 +11142,74 @@ namespace exprtk make_error(parser_error::e_syntax, current_token_, "ERR23 - Expected ')' at end of if-statement")); + result = false; + } + + if (!result) + { + 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); + } + + inline expression_node_ptr parse_ternary_conditional_statement(expression_node_ptr condition) + { + // Parse: [condition][?][consequent][:][alternative] + expression_node_ptr consequent = error_node(); + expression_node_ptr alternative = error_node(); + + bool result = true; + + if (0 == condition) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR24 - Encountered invalid condition branch for ternary if-statement")); + return error_node(); + } + else if (!token_is(token_t::e_ternary)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR25 - Expected '?' after condition of ternary if-statement")); + result = false; + } + else if (0 == (consequent = parse_expression())) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR26 - Failed to parse consequent for if-statement")); + result = false; + } + else if (!token_is(token_t::e_colon)) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR27 - Expected ':' between ternary if-statement consequent and alternative")); + result = false; + } + else if (0 == (alternative = parse_expression())) + { + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR28 - Failed to parse alternative for if-statement")); + result = false; + } + + if (!result) + { + free_node(node_allocator_,condition ); + free_node(node_allocator_,consequent ); + free_node(node_allocator_,alternative); return error_node(); } else @@ -11139,13 +11221,15 @@ namespace exprtk // Parse: [while][(][test expr][)][{][expression][}] expression_node_ptr condition = error_node(); expression_node_ptr branch = error_node(); + next_token(); + if (!token_is(token_t::e_lbracket)) { set_error( make_error(parser_error::e_syntax, current_token_, - "ERR24 - Expected '(' at start of while-statement condition")); + "ERR29 - Expected '(' at start of while-statement condition")); return error_node(); } else if (0 == (condition = parse_expression())) @@ -11153,7 +11237,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR25 - Failed to parse condition for while-loop")); + "ERR30 - Failed to parse condition for while-loop")); return error_node(); } else if (!token_is(token_t::e_rbracket)) @@ -11161,7 +11245,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR26 - Expected ')' at end of while-statement condition")); + "ERR31 - Expected ')' at end of while-statement condition")); return error_node(); } if (0 == (branch = parse_multi_sequence("while-loop"))) @@ -11169,7 +11253,8 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR27 - Failed to parse body of while-loop")); + "ERR32 - Failed to parse body of while-loop")); + free_node(node_allocator_,condition); return error_node(); } @@ -11179,7 +11264,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR28 - Failed to synthesize while-loop")); + "ERR33 - Failed to synthesize while-loop")); return error_node(); } else @@ -11217,7 +11302,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR29 - Expected '" + token_t::to_str(seperator) +"' for body of repeat until loop")); + "ERR34 - Expected '" + token_t::to_str(seperator) +"' for body of repeat until loop")); return error_node(); } @@ -11237,7 +11322,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR30 - Failed to parse body of repeat until loop")); + "ERR35 - Failed to parse body of repeat until loop")); return error_node(); } } @@ -11247,7 +11332,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR31 - Expected '(' before condition of repeat until loop")); + "ERR36 - Expected '(' before condition of repeat until loop")); return error_node(); } else if (0 == (condition = parse_expression())) @@ -11255,7 +11340,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR32 - Failed to parse condition for repeat until loop")); + "ERR37 - Failed to parse condition for repeat until loop")); return error_node(); } else if (!token_is(token_t::e_rbracket)) @@ -11263,7 +11348,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR33 - Expected ')' after condition of repeat until loop")); + "ERR38 - Expected ')' after condition of repeat until loop")); return error_node(); } @@ -11273,7 +11358,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR34 - Failed to synthesize repeat until loop")); + "ERR39 - Failed to synthesize repeat until loop")); return error_node(); } else @@ -11292,7 +11377,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR35 - Expected keyword 'switch'")); + "ERR40 - Expected keyword 'switch'")); return error_node(); } @@ -11305,7 +11390,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR36 - Expected '{' for call to switch statement")); + "ERR41 - Expected '{' for call to switch statement")); return error_node(); } @@ -11316,7 +11401,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR37 - Expected either a 'case' or 'default' statement")); + "ERR42 - Expected either a 'case' or 'default' statement")); return error_node(); } @@ -11331,7 +11416,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR38 - Expected ':' for case of switch statement")); + "ERR43 - Expected ':' for case of switch statement")); return error_node(); } @@ -11344,7 +11429,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR39 - Expected ';' at end of case for switch statement")); + "ERR44 - Expected ';' at end of case for switch statement")); return error_node(); } @@ -11370,7 +11455,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR40 - Expected ':' for default of switch statement")); + "ERR45 - Expected ':' for default of switch statement")); return error_node(); } @@ -11382,7 +11467,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR41 - Expected ';' at end of default for switch statement")); + "ERR46 - Expected ';' at end of default for switch statement")); return error_node(); } @@ -11396,7 +11481,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR42 - Expected '}' at end of switch statement")); + "ERR47 - Expected '}' at end of switch statement")); return error_node(); } @@ -11418,7 +11503,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR43 - Expected token '[*]'")); + "ERR48 - Expected token '[*]'")); return error_node(); } @@ -11431,7 +11516,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR44 - Expected '{' for call to [*] statement")); + "ERR49 - Expected '{' for call to [*] statement")); return error_node(); } @@ -11442,7 +11527,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR45 - Expected a 'case' statement for multi-switch.")); + "ERR50 - Expected a 'case' statement for multi-switch.")); return error_node(); } @@ -11457,7 +11542,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR46 - Expected ':' for case of [*] statement")); + "ERR51 - Expected ':' for case of [*] statement")); return error_node(); } @@ -11470,7 +11555,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR47 - Expected ';' at end of case for [*] statement")); + "ERR52 - Expected ';' at end of case for [*] statement")); return error_node(); } @@ -11499,7 +11584,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR48 - Expected '}' at end of [*] statement")); + "ERR53 - Expected '}' at end of [*] statement")); return error_node(); } @@ -11538,7 +11623,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR49 - Unsupported vararg function: " + symbol)); + "ERR54 - Unsupported vararg function: " + symbol)); return error_node(); } @@ -11550,7 +11635,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR50 - Expected '(' for call to vararg function: " + symbol)); + "ERR55 - Expected '(' for call to vararg function: " + symbol)); return error_node(); } @@ -11569,7 +11654,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR51 - Expected ',' for call to vararg function: " + symbol)); + "ERR56 - Expected ',' for call to vararg function: " + symbol)); return error_node(); } } @@ -11626,7 +11711,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR52 - Expected '"+ details::to_str(close_bracket) + "' for call to multi-sequence" + + "ERR57 - Expected '"+ details::to_str(close_bracket) + "' for call to multi-sequence" + ((!source.empty()) ? std::string(" section of " + source): ""))); return error_node(); } @@ -11654,7 +11739,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR53 - Expected '"+ details::to_str(seperator) +"' for call to multi-sequence section of " + source)); + "ERR58 - Expected '"+ details::to_str(seperator) +"' for call to multi-sequence section of " + source)); return error_node(); } @@ -11780,7 +11865,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR54 - Expected '[' for start of range")); + "ERR59 - Expected '[' for start of range")); return false; } @@ -11798,7 +11883,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR55 - Failed parse begin section of range")); + "ERR60 - Failed parse begin section of range")); return false; } @@ -11816,7 +11901,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR56 - Range lower bound less than zero! Constraint: r0 >= 0")); + "ERR61 - Range lower bound less than zero! Constraint: r0 >= 0")); return false; } } @@ -11831,7 +11916,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR57 - Expected ':' for break in range")); + "ERR62 - Expected ':' for break in range")); rp.free(); return false; } @@ -11851,7 +11936,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR58 - Failed parse end section of range")); + "ERR63 - Failed parse end section of range")); rp.free(); return false; @@ -11870,7 +11955,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR59 - Range upper bound less than zero! Constraint: r1 >= 0")); + "ERR64 - Range upper bound less than zero! Constraint: r1 >= 0")); return false; } } @@ -11885,7 +11970,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR60 - Expected ']' for start of range")); + "ERR65 - Expected ']' for start of range")); rp.free(); return false; } @@ -11901,7 +11986,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR61 - Invalid range, Constraint: r0 <= r1")); + "ERR66 - Invalid range, Constraint: r0 <= r1")); return false; } } @@ -11926,7 +12011,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR62 - Unknown string symbol")); + "ERR67 - Unknown string symbol")); return error_node(); } @@ -11987,7 +12072,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR63 - Overflow in range for string: '" + const_str + "'[" + + "ERR68 - Overflow in range for string: '" + const_str + "'[" + (rp.n0_c.first ? details::to_str(rp.n0_c.second) : "?") + ":" + (rp.n1_c.first ? details::to_str(rp.n1_c.second) : "?") + "]")); return error_node(); @@ -12016,7 +12101,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR64 - Expected '(' for call to vararg function: " + vararg_function_name)); + "ERR69 - Expected '(' for call to vararg function: " + vararg_function_name)); return error_node(); } @@ -12037,7 +12122,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR65 - Expected ',' for call to vararg function: " + vararg_function_name)); + "ERR70 - Expected ',' for call to vararg function: " + vararg_function_name)); return error_node(); } } @@ -12066,7 +12151,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR66 - Expected '(' for special function")); + "ERR71 - Expected '(' for special function")); return error_node(); } @@ -12084,7 +12169,7 @@ namespace exprtk p.set_error( make_error(parser_error::e_syntax, p.current_token(), - "ERR67 - Expected ',' before next parameter of special function")); + "ERR72 - Expected ',' before next parameter of special function")); return p.error_node(); } } @@ -12110,7 +12195,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token_, - "ERR68 - Invalid special function[1]: " + current_token_.value)); + "ERR73 - Invalid special function[1]: " + current_token_.value)); return error_node(); } @@ -12121,7 +12206,7 @@ namespace exprtk set_error( make_error(parser_error::e_token, current_token_, - "ERR69 - Invalid special function[2]: " + current_token_.value)); + "ERR74 - Invalid special function[2]: " + current_token_.value)); return error_node(); } @@ -12200,7 +12285,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR70 - Invalid number of parameters for function: " + symbol)); + "ERR75 - Invalid number of parameters for function: " + symbol)); return error_node(); } } @@ -12212,7 +12297,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR71 - Failed to generate node for function: '" + symbol + "'")); + "ERR76 - Failed to generate node for function: '" + symbol + "'")); return error_node(); } } @@ -12232,7 +12317,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR72 - Failed to generate node for vararg function: '" + symbol + "'")); + "ERR77 - Failed to generate node for vararg function: '" + symbol + "'")); return error_node(); } } @@ -12276,7 +12361,7 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR73 - Failed to create variable: '" + symbol + "'")); + "ERR78 - Failed to create variable: '" + symbol + "'")); return error_node(); } @@ -12285,7 +12370,7 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR74 - Undefined variable or function: '" + symbol + "'")); + "ERR79 - Undefined variable or function: '" + symbol + "'")); return error_node(); } @@ -12338,13 +12423,15 @@ namespace exprtk set_error( make_error(parser_error::e_symtab, current_token_, - "ERR75 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); + "ERR80 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token_.value)); return error_node(); } } inline expression_node_ptr parse_branch(precedence_level precedence = e_level00) { + expression_node_ptr branch = error_node(); + if (token_t::e_number == current_token_.type) { T numeric_value = T(0); @@ -12353,100 +12440,89 @@ namespace exprtk { expression_node_ptr literal_exp = expression_generator_(numeric_value); next_token(); - return literal_exp; + branch = literal_exp; } else { set_error( make_error(parser_error::e_numeric, current_token_, - "ERR76 - Failed to convert '" + current_token_.value + "' to a number.")); + "ERR81 - Failed to convert '" + current_token_.value + "' to a number.")); return error_node(); } } else if (token_t::e_symbol == current_token_.type) { - return parse_symbol(); + branch = parse_symbol(); } #ifndef exprtk_disable_string_capabilities else if (token_t::e_string == current_token_.type) { - return parse_const_string(); + branch = parse_const_string(); } #endif else if (token_t::e_lbracket == current_token_.type) { next_token(); - expression_node_ptr branch = parse_expression(); - if (0 == branch) + if (0 == (branch = parse_expression())) return error_node(); - else if (token_is(token_t::e_rbracket)) - return branch; - else + else if (!token_is(token_t::e_rbracket)) { set_error( make_error(parser_error::e_syntax, current_token_, - "ERR77 - Expected ')' instead of: '" + current_token_.value + "'")); + "ERR82 - Expected ')' instead of: '" + current_token_.value + "'")); return error_node(); } } else if (token_t::e_lsqrbracket == current_token_.type) { next_token(); - expression_node_ptr branch = parse_expression(); - - if (0 == branch) + if (0 == (branch = parse_expression())) return error_node(); - else if (token_is(token_t::e_rsqrbracket)) - return branch; - else + else if (!token_is(token_t::e_rsqrbracket)) { set_error( make_error(parser_error::e_syntax, current_token_, - "ERR78 - Expected ']' instead of: '" + current_token_.value + "'")); + "ERR83 - Expected ']' instead of: '" + current_token_.value + "'")); return error_node(); } } else if (token_t::e_lcrlbracket == current_token_.type) { next_token(); - expression_node_ptr branch = parse_expression(); - - if (0 == branch) + if (0 == (branch = parse_expression())) return error_node(); - else if (token_is(token_t::e_rcrlbracket)) - return branch; - else + else if (!token_is(token_t::e_rcrlbracket)) { set_error( make_error(parser_error::e_syntax, current_token_, - "ERR79 - Expected '}' instead of: '" + current_token_.value + "'")); + "ERR84 - Expected '}' instead of: '" + current_token_.value + "'")); return error_node(); } } else if (token_t::e_sub == current_token_.type) { next_token(); - return expression_generator_(details::e_neg, - // Was the previous operation exponentiation? - (e_level12 == precedence) ? - parse_branch (e_level09) : - parse_expression(e_level09)); + branch = expression_generator_(details::e_neg, + // Was the previous operation exponentiation? + (e_level12 == precedence) ? + parse_branch (e_level09) : + parse_expression(e_level09)); } else if (token_t::e_add == current_token_.type) { next_token(); - return parse_expression(e_level13); + branch = parse_expression(e_level13); } else if (token_t::e_eof == current_token_.type) { set_error( make_error(parser_error::e_syntax, current_token_, - "ERR80 - Premature end of expression[1]")); + "ERR85 - Premature end of expression[1]")); return error_node(); } else @@ -12454,9 +12530,20 @@ namespace exprtk set_error( make_error(parser_error::e_syntax, current_token_, - "ERR81 - Premature end of expression[2]")); + "ERR86 - Premature end of expression[2]")); return error_node(); } + + if ( + branch && + (e_level00 == precedence) && + token_is(token_t::e_ternary,false) + ) + { + branch = parse_ternary_conditional_statement(branch); + } + + return branch; } inline bool token_is(const typename token_t::token_type& ttype, const bool advance_token = true) diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 7eacd4c..9ebd04e 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -1006,7 +1006,29 @@ static const test_t test_list[] = 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("[*] { case 1 < 2 : 1 / 2; case (1 < 3) : 2 / 2; case 1 < 4 : 3 / 2; case (1 < 5) : 4 / 2; }",2.0) + test_t("[*] { case 1 < 2 : 1 / 2; case (1 < 3) : 2 / 2; case 1 < 4 : 3 / 2; case (1 < 5) : 4 / 2; }",2.0), + test_t(" 0 ? 1 : 2",2.0), + test_t(" 1 ? 3 : 4",3.0), + test_t("(0 ? 1 : 2) == 2",1.0), + test_t("(1 ? 3 : 4) == 3",1.0), + test_t("[(0)] ? [(1)] : [(2)]",2.0), + test_t("([(0)] ? [(1)] : [(2)]) == 2",1.0), + test_t("([(1)] ? [(3)] : [(4)]) == 3",1.0), + test_t("(1 < 2 ? 3 : 4) == 3",1.0), + test_t("(1 > 2 ? 3 : 4) == 4",1.0), + test_t("(1 < 2 ? 3 + 5 : 4) == 8",1.0), + test_t("(1 > 2 ? 3 : 4 + 5) == 9",1.0), + test_t("(2 < 3 + 3 ? 7 : 9) == 7",1.0), + test_t("(1 + 1 < 3 ? 7 : 9) == 7",1.0), + test_t("(1 + 1 < 3 + 3 ? 7 : 9) == 7",1.0), + test_t("(2 > 3 + 3 ? 7 : 9) == 9",1.0), + test_t("(1 + 1 > 3 ? 7 : 9) == 9",1.0), + test_t("(1 + 1 > 3 + 3 ? 7 : 9) == 9",1.0), + test_t("(2 < (3 + 3) ? 7 : 9) == 7",1.0), + test_t("((1 + 1) < 3 ? 7 : 9) == 7",1.0), + test_t("((1 + 1) < (3 + 3) ? 7 : 9) == 7",1.0), + test_t("(min(1,2) ? 1 + 3 : 1 + 4) == 4",1.0), + test_t("(min(0,1) ? 1 + 3 : 1 + 4) == 5",1.0) }; static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_t); @@ -1109,13 +1131,17 @@ inline bool run_test00() const std::size_t rounds = 10; for (std::size_t r = 0; r < rounds; ++r) { + bool result = true; for (std::size_t i = 0; i < test_list_size; ++i) { if (!test_expression(test_list[i].first,T(test_list[i].second))) { - return false; + result = false; } } + + if (!result) + return false; } return true; @@ -1442,7 +1468,23 @@ inline bool run_test01() test_xy("switch { case {(x <= y)} : switch { case ({x <= y}) : x; default: 1.12345; }; default: 1.12345; }",T(1.0),T(2.0),T(1.0)), test_xy("[*]{ case x < y : x + y; case y < x : y - x; }",T(2.0),T(3.0),T(5.0)), test_xy("[*]{ case x > y : x + y; case y > x : y - x; }",T(2.0),T(3.0),T(1.0)), - test_xy("[*]{ case x > y : x - y; case y < x : y + x; }",T(2.0),T(3.0),T(0.0)) + test_xy("[*]{ case x > y : x - y; case y < x : y + x; }",T(2.0),T(3.0),T(0.0)), + test_xy("0 ? x : y" ,T(1.0),T(2.0),T( 2.0)), + test_xy("1 ? x : y" ,T(1.0),T(2.0),T( 1.0)), + test_xy("x ? x : y" ,T(1.0),T(2.0),T( 1.0)), + test_xy("x ? x : y" ,T(0.0),T(2.0),T( 2.0)), + test_xy("(x + y < 4) ? 1 : 2" ,T(1.0),T(2.0),T( 1.0)), + test_xy("(x + y > 4) ? 1 : 2" ,T(1.0),T(2.0),T( 2.0)), + test_xy("x < y ? x + y : x - y" ,T(1.0),T(2.0),T( 3.0)), + test_xy("x > y ? x + y : x - y" ,T(1.0),T(2.0),T(-1.0)), + test_xy("(x + x < y ? 7 : 9) == 7" ,T(1.0),T(3.0),T( 1.0)), + test_xy("(x + x < y + y ? 7 : 9) == 7" ,T(1.0),T(3.0),T( 1.0)), + test_xy("(x > y + y ? 7 : 9) == 9" ,T(1.0),T(3.0),T( 1.0)), + test_xy("(x + x > y ? 7 : 9) == 9" ,T(1.0),T(3.0),T( 1.0)), + test_xy("(x + x > y + 3 ? 7 : 9) == 9" ,T(1.0),T(3.0),T( 1.0)), + test_xy("(x < (y + y) ? 7 : 9) == 7" ,T(1.0),T(3.0),T( 1.0)), + test_xy("((x + x) < y ? 7 : 9) == 7" ,T(1.0),T(3.0),T( 1.0)), + test_xy("((x + x) < (y + y) ? 7 : 9) == 7",T(1.0),T(3.0),T( 1.0)), }; static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_xy); @@ -2391,6 +2433,13 @@ inline bool run_test08() "equal($f96(x,y,z,w),if(x > y,z,w))", "equal($f97(x,y,z,w),if(x >= y,z,w))", "equal($f98(x,y,z,w),if(equal(x,y),z,w))", + "equal($f92(x,y,z,w),x and y ? z : w)", + "equal($f93(x,y,z,w),x or y ? z : w)", + "equal($f94(x,y,z,w),x < y ? z : w)", + "equal($f95(x,y,z,w),x <= y ? z : w)", + "equal($f96(x,y,z,w),x > y ? z : w)", + "equal($f97(x,y,z,w),x >= y ? z : w)", + "equal($f98(x,y,z,w),equal(x,y) ? z : w)", "equal($f99(x,y,z,w),x*sin(y)+z*cos(w))" }; static const std::size_t expr_str_size = sizeof(expr_str) / sizeof(std::string); diff --git a/readme.txt b/readme.txt index b8d50f5..cf0013c 100644 --- a/readme.txt +++ b/readme.txt @@ -32,7 +32,8 @@ arithmetic operations, functions and processes: (5) Conditional, Switch & - Loop statements: if-then-else, switch-case, while, repeat-until + Loop statements: if-then-else, ternary conditional, switch-case, + while, repeat-until (6) Assignment: := @@ -64,7 +65,7 @@ expressions that can be parsed and evaluated using the ExprTk library. (13) (x + y)z + 1.1 / 2.7 == (x + y) * z + 1.1 / 2.7 (14) (sin(x / pi) cos(2y) + 1) == (sin(x / pi) * cos(2 * y) + 1) (15) 75x^17 + 25.1x^5 - 35x^4 - 15.2x^3 + 40x^2 - 15.3x + 1 - (16) if (avg(x,y) <= x + y, x - y, x * y) + 2.345 * pi / x + (16) (avg(x,y) <= x + y ? x - y : x * y) + 2.345 * pi / x (17) fib_i := fib_i + (x := y + 0 * (fib_i := x + (y := fib_i))) (18) while (x <= 100) { x := x + 1 } (19) x <= 'abc123' and (y in 'AString') or ('1x2y3z' != z) @@ -406,6 +407,13 @@ include path (e.g: /usr/include/). | | w := z + y; | | | until ((x := (x - 1)) <= 0) | +----------+---------------------------------------------------------+ +| ?: | Ternary conditional statement, similar to that of the | +| | above denoted if-statement. | +| | eg: | +| | 0. x ? y : z | +| | 1. x + 1 > 2y ? z + 1 : (w / v) | +| | 2. min(x,y) > z ? (x < y + 1) ? x : y : (w * v) | ++----------+---------------------------------------------------------+ | ~ | 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. | @@ -459,6 +467,46 @@ types a symbol table can handle: (e) Functions (f) Vararg functions +During the compilation process if an expression is found to require +any of the elements noted above, the expression's associated +symbol_table will be queried for the element and if present a +reference to the element will be embedded within the expression's AST. +This allows for the original element to be modified independently of +the expression instance and to also allow the expression to be +evaluated using the current value of the element. + +The example below demonstrates the relationship between variables, +symbol_table and expression. Note the variables are modified as they +normally would in a program, and when the expression is evaluated the +current values assigned to the variables will be used. + + typedef exprtk::symbol_table symbol_table_t; + typedef exprtk::expression expression_t; + typedef exprtk::parser parser_t; + + symbol_table_t symbol_table; + expression_t expression; + parser_t parser; + + double x = 0; + double y = 0; + + std::string expression_string = "x * y + 3"; + symbol_table.add_variable("x",x); + symbol_table.add_variable("y",y); + + expression.register_symbol_table(symbol_table); + + parser.compile(expression_string,expression); + + x = 1.0; + y = 2.0; + parser.value(); // 1 * 2 + 3 + x = 3.7; + parser.value(); // 3.7 * 2 + 3 + y = -9.0; + parser.value(); // 3.7 * -9 + 3 + (2) Expression A structure that holds an AST for a specified expression and is used @@ -474,21 +522,21 @@ Expression: z := (x + y^-2.345) * sin(pi / min(w - 7.3,v)) [Assignment] ________/ \_____ / \ - Variable(z) [Multiply] + Variable(z) [Multiplication] ____________/ \___________ / \ / [Unary-Func(sin)] [Addition] | ____/ \____ [Division] / \ ___/ \___ - Variable(x) [Power] / \ + Variable(x) [Exponentiation] / \ ______/ \______ Constant(pi) [Binary-Func(min)] / \ ____/ \____ - Variable(y) [Negate] / \ + Variable(y) [Negation] / \ | / Variable(v) Constant(2.345) / / - [Subtract] + [Subtraction] ____/ \____ / \ Variable(w) Constant(7.3) @@ -809,7 +857,8 @@ int main() typedef exprtk::parser parser_t; typedef exprtk::parser_error::type error_t; - std::string expression_str = "z := 2 myfunc([4+sin(x/pi)^3],y^2)"; + std::string expression_str = + "z := 2 myfunc([4 + sin(x / pi)^3],y ^ 2)"; double x = 1.1; double y = 2.2;