diff --git a/Makefile b/Makefile index 7ac66d1..2561cf5 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ BUILD_LIST+=exprtk_simple_example_06 BUILD_LIST+=exprtk_simple_example_07 BUILD_LIST+=exprtk_simple_example_08 BUILD_LIST+=exprtk_simple_example_09 +BUILD_LIST+=exprtk_simple_example_10 all: $(BUILD_LIST) @@ -69,6 +70,9 @@ exprtk_simple_example_08: exprtk_simple_example_08.cpp exprtk.hpp exprtk_simple_example_09: exprtk_simple_example_09.cpp exprtk.hpp $(COMPILER) $(OPTIONS) exprtk_simple_example_09 exprtk_simple_example_09.cpp $(LINKER_OPT) +exprtk_simple_example_10: exprtk_simple_example_10.cpp exprtk.hpp + $(COMPILER) $(OPTIONS) exprtk_simple_example_10 exprtk_simple_example_10.cpp $(LINKER_OPT) + pgo: exprtk_test.cpp exprtk_benchmark.cpp exprtk.hpp $(COMPILER) $(BASE_OPTIONS) -O3 -march=native -fprofile-generate -o exprtk_benchmark exprtk_benchmark.cpp $(LINKER_OPT) ./exprtk_benchmark @@ -86,6 +90,7 @@ strip_bin: strip -s exprtk_simple_example_07 strip -s exprtk_simple_example_08 strip -s exprtk_simple_example_09 + strip -s exprtk_simple_example_10 valgrind_check: valgrind --leak-check=full --show-reachable=yes --track-origins=yes -v ./exprtk_test @@ -99,6 +104,7 @@ valgrind_check: valgrind --leak-check=full --show-reachable=yes --track-origins=yes -v ./exprtk_simple_example_07 valgrind --leak-check=full --show-reachable=yes --track-origins=yes -v ./exprtk_simple_example_08 valgrind --leak-check=full --show-reachable=yes --track-origins=yes -v ./exprtk_simple_example_09 + valgrind --leak-check=full --show-reachable=yes --track-origins=yes -v ./exprtk_simple_example_10 clean: rm -f core.* *~ *.o *.bak *stackdump gmon.out *.gcda *.gcno *.gcnor *.gch diff --git a/exprtk.hpp b/exprtk.hpp index 95e67f5..c70a5bd 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -1,32 +1,32 @@ /* - **************************************************************** - * C++ Mathematical Expression Toolkit Library * - * * - * Author: Arash Partow (1999-2013) * - * URL: http://www.partow.net/programming/exprtk/index.html * - * * - * Copyright notice: * - * Free use of the C++ Mathematical Expression Toolkit Library * - * is permitted under the guidelines and in accordance with the * - * most current version of the Common Public License. * - * http://www.opensource.org/licenses/cpl1.0.php * - * * - * Example expressions: * - * (00) (y+x/y)*(x-y/x) * - * (01) (x^2/sin(2*pi/y))-x/2 * - * (02) sqrt(1-(x^2)) * - * (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 * - * (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) * - * (10) inrange(-2,m,+2)==if(({-2<=m} and [m<=+2]),1,0) * - * (11) (12.34sin(x)cos(2y)7+1)==(12.34*sin(x)*cos(2*y)*7+1) * - * (12) (x ilike 's*ri?g') and [y<(3z^7+w)] * - * * - **************************************************************** + ****************************************************************** + * C++ Mathematical Expression Toolkit Library * + * * + * Author: Arash Partow (1999-2013) * + * URL: http://www.partow.net/programming/exprtk/index.html * + * * + * Copyright notice: * + * Free use of the C++ Mathematical Expression Toolkit Library is * + * permitted under the guidelines and in accordance with the most * + * current version of the Common Public License. * + * http://www.opensource.org/licenses/cpl1.0.php * + * * + * Example expressions: * + * (00) (y+x/y)*(x-y/x) * + * (01) (x^2/sin(2*pi/y))-x/2 * + * (02) sqrt(1-(x^2)) * + * (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 * + * (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) * + * (10) inrange(-2,m,+2)==if(({-2<=m} and [m<=+2]),1,0) * + * (11) (12.34sin(x)cos(2y)7+1)==(12.34*sin(x)*cos(2*y)*7+1) * + * (12) (x ilike 's*ri?g') and [y<(3z^7+w)] * + * * + ****************************************************************** */ @@ -2304,7 +2304,7 @@ namespace exprtk void reset() { - //msvc doesn't support swap properly. + //why? because msvc doesn't support swap properly. stack_ = std::stack(); state_ = true; error_token_.clear(); @@ -9400,6 +9400,27 @@ namespace exprtk e_commutative_check = 32 }; + struct unknown_symbol_resolver + { + + enum symbol_type + { + e_variable_type = 0, + e_constant_type = 1 + }; + + virtual ~unknown_symbol_resolver() + {} + + virtual bool process(const std::string& /*unknown_symbol*/, symbol_type& st, T& default_value, std::string& error_message) + { + st = e_variable_type; + default_value = T(0); + error_message = ""; + return true; + } + }; + static const std::size_t precompile_all_opts = e_replacer + e_joiner + e_numeric_check + @@ -9409,7 +9430,9 @@ namespace exprtk parser(const std::size_t precompile_options = precompile_all_opts) : symbol_name_caching_(false), - precompile_options_(precompile_options) + precompile_options_(precompile_options), + resolve_unknown_symbol_(false), + unknown_symbol_resolver_(reinterpret_cast(0)) { init_precompilation(); load_operations_map(base_ops_map_); @@ -9729,6 +9752,21 @@ namespace exprtk return symbol_replacer_.remove(symbol); } + inline void enable_unknown_symbol_resolver(unknown_symbol_resolver* usr = reinterpret_cast(0)) + { + resolve_unknown_symbol_ = true; + if (usr) + unknown_symbol_resolver_ = usr; + else + unknown_symbol_resolver_ = &default_usr_; + } + + inline void disable_unknown_symbol_resolver() + { + resolve_unknown_symbol_ = false; + unknown_symbol_resolver_ = &default_usr_; + } + private: inline bool valid_base_operation(const std::string& symbol) @@ -10799,14 +10837,60 @@ namespace exprtk return error_node(); } } - else + + // Should we handle unknown symbols? + if (resolve_unknown_symbol_ && unknown_symbol_resolver_) { - set_error( - make_error(parser_error::e_syntax, - current_token_, - "ERR40 - Undefined variable or function: '" + symbol + "'")); - return error_node(); + T default_value = T(0); + std::string error_message; + typename unknown_symbol_resolver::symbol_type symbol_type; + + if (unknown_symbol_resolver_->process(symbol,symbol_type,default_value,error_message)) + { + bool create_result = false; + switch (symbol_type) + { + case unknown_symbol_resolver::e_variable_type : create_result = symbol_table_.create_variable(symbol,default_value); + break; + + case unknown_symbol_resolver::e_constant_type : create_result = symbol_table_.add_constant(symbol,default_value); + break; + + default : create_result = false; + } + + if (create_result) + { + expression_node_ptr variable = symbol_table_.get_variable(symbol); + if (variable) + { + if (symbol_name_caching_) + { + symbol_name_cache_.push_back(symbol); + } + if (symbol_table_.is_constant_node(symbol)) + { + variable = expression_generator_(variable->value()); + } + next_token(); + return variable; + } + } + + set_error( + make_error(parser_error::e_symtab, + current_token_, + "ERR40 - Failed to create variable: '" + symbol + "'")); + + return error_node(); + } } + + set_error( + make_error(parser_error::e_syntax, + current_token_, + "ERR41 - Undefined variable or function: '" + symbol + "'")); + return error_node(); } inline expression_node_ptr parse_symbol() @@ -11747,6 +11831,7 @@ namespace exprtk else if (all_nodes_variables(branch)) return varnode_optimize_sf3(operation,branch); else + { switch (operation) { #define case_stmt(op0,op1) case op0 : return node_allocator_->allocate > >(operation,branch); @@ -11777,6 +11862,7 @@ namespace exprtk #undef case_stmt default : return error_node(); } + } } inline expression_node_ptr const_optimize_sf4(const details::operator_type& operation, expression_node_ptr (&branch)[4]) @@ -15524,6 +15610,9 @@ namespace exprtk std::size_t precompile_options_; std::deque symbol_name_cache_; std::deque error_list_; + bool resolve_unknown_symbol_; + unknown_symbol_resolver* unknown_symbol_resolver_; + unknown_symbol_resolver default_usr_; base_ops_map_t base_ops_map_; unary_op_map_t unary_op_map_; binary_op_map_t binary_op_map_; diff --git a/exprtk_test.cpp b/exprtk_test.cpp index b692920..cb7ab87 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -3830,6 +3830,165 @@ inline bool run_test19() return false; } + { + T x = T(0); + + exprtk::symbol_table symbol_table; + + symbol_table.add_constants(); + symbol_table.add_variable("x",x); + + compositor_t compositor(symbol_table); + + 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 * y,x), z := 0, 0);" + " y := (1 / 2) * (y + (x / y)) " + " } " + " }; " + "} ", + "x","y","z"); + + compositor + .add("newton_sqrt", + "newton_sqrt_impl(x,0,0)","x"); + + std::string expression_str = "newton_sqrt(x)"; + + expression_t expression; + + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + if (!parser.compile(expression_str,expression)) + { + printf("run_test19() - Error: %s Expression: %s\n", + parser.error().c_str(), + expression_str.c_str()); + return false; + } + + bool failure = false; + + for (std::size_t i = 0; i < 100; ++i) + { + x = i; + T result = expression.value(); + + if (not_equal(result,std::sqrt(x),T(0.0000001))) + { + printf("run_test19() - Computation Error " + "Expression: [%s]\tExpected: %12.8f\tResult: %12.8f\n", + expression_str.c_str(), + std::sqrt(x), + result); + failure = true; + } + } + + if (failure) + return false; + } + + return true; +} + +template +struct my_usr : public exprtk::parser::unknown_symbol_resolver +{ + + typedef typename exprtk::parser::unknown_symbol_resolver usr_t; + + bool process(const std::string& unknown_symbol, + typename usr_t::symbol_type& st, + T& default_value, + std::string& error_message) + { + if (unknown_symbol[0] == 'v') + { + st = usr_t::e_variable_type; + default_value = next_value(); + error_message = ""; + return true; + } + else if (unknown_symbol[0] == 'w') + { + st = usr_t::e_constant_type; + default_value = next_value(); + error_message = ""; + return true; + } + else + { + error_message = "Unknown symbol..."; + return false; + } + } + + T next_value(const bool reset = false) + { + static T value = 0; + if (reset) + return (value = 0); + else + return ++value; + } +}; + +template +inline bool run_test20() +{ + typedef exprtk::expression expression_t; + + for (std::size_t i = 0; i < 400; ++i) + { + exprtk::symbol_table symbol_table; + symbol_table.add_constants(); + + expression_t expression; + expression.register_symbol_table(symbol_table); + + exprtk::parser parser; + + my_usr musr; + 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"; + + if (!parser.compile(expr_str,expression)) + { + printf("run_test18() - Error: %s Expression: %s\n", + parser.error().c_str(), + expr_str.c_str()); + return false; + } + + T sum_1_30 = T((1 + 30) * 15); + T result = expression.value(); + + if (sum_1_30 != result) + { + printf("run_test20() - Error in evaluation! (1) Expression: %s\n", + expr_str.c_str()); + return false; + } + } + return true; } @@ -3870,6 +4029,7 @@ int main() perform_test(double,17) perform_test(double,18) perform_test(double,19) + perform_test(double,20) #undef perform_test diff --git a/readme.txt b/readme.txt index 80dbd68..e4c9d32 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ C++ Mathematical Expression Toolkit Library -[INTRODUCTION] +[00 - INTRODUCTION] The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple to use, easy to integrate and extremely efficient mathematical expression parsing and evaluation engine. The parsing engine supports @@ -9,7 +9,7 @@ very easily extendible. -[CAPABILITIES] +[01 - CAPABILITIES] The ExprTk evaluator supports the following fundamental mathematical operations, functions and processes: @@ -42,7 +42,7 @@ operations, functions and processes: -[EXAMPLE EXPRESSIONS] +[02 - EXAMPLE EXPRESSIONS] The following is a short sample of the types of mathematical expressions that can be parsed and evaluated using the ExprTk library. @@ -69,7 +69,7 @@ expressions that can be parsed and evaluated using the ExprTk library. -[COPYRIGHT NOTICE] +[03 - COPYRIGHT NOTICE] Free use of the C++ Mathematical Expression Toolkit Library is permitted under the guidelines and in accordance with the most current version of the Common Public License. @@ -78,7 +78,7 @@ http://www.opensource.org/licenses/cpl1.0.php -[DOWNLOADS & UPDATES] +[04 - DOWNLOADS & UPDATES] The most recent version of the C++ Mathematical Expression Toolkit Library including all updates and tests can be found at the following locations: @@ -88,20 +88,20 @@ locations: -[INSTALLATION] +[05 - INSTALLATION] The header file exprtk.hpp should be placed in a project or system include path (e.g: /usr/include/). -[COMPILATION] +[06 - COMPILATION] (1) For a complete build: make clean all (2) For a PGO build: make clean pgo (3) To strip executables: make strip_bin -[COMPILER COMPATIBILITY] +[07 - COMPILER COMPATIBILITY] (*) GNU Compiler Collection (4.3+) (*) Intel® C++ Compiler (9.x+) (*) Clang/LLVM (1.1+) @@ -112,7 +112,7 @@ include path (e.g: /usr/include/). -[BUILT-IN OPERATIONS & FUNCTIONS] +[08 - BUILT-IN OPERATIONS & FUNCTIONS] (0) Basic Operators +-----------+--------------------------------------------------------+ @@ -344,7 +344,7 @@ include path (e.g: /usr/include/). -[SPECIAL FUNCTIONS] +[09 - SPECIAL FUNCTIONS] The purpose of special functions in ExprTk is to provide compiler generated equivalents of common mathematical expressions which can be invoked by using the 'special function' syntax (eg: $f12(x,y,z) or @@ -415,7 +415,7 @@ correctly optimize such expressions for a given architecture. -[EXPRTK NOTES] +[10 - EXPRTK NOTES] (00) Precision and performance of expression evaluations are the dominant principles of the ExprTk library. @@ -478,7 +478,7 @@ correctly optimize such expressions for a given architecture. -[SIMPLE EXPRTK EXAMPLE] +[11 - SIMPLE EXPRTK EXAMPLE] --- snip --- #include #include @@ -553,9 +553,19 @@ int main() -[FILES] +[12 - FILES] (00) Makefile (01) readme.txt (02) exprtk.hpp (03) exprtk_test.cpp (04) exprtk_benchmark.cpp +(05) exprtk_simple_example_01.cpp +(06) exprtk_simple_example_02.cpp +(07) exprtk_simple_example_03.cpp +(08) exprtk_simple_example_04.cpp +(09) exprtk_simple_example_05.cpp +(10) exprtk_simple_example_06.cpp +(11) exprtk_simple_example_07.cpp +(12) exprtk_simple_example_08.cpp +(13) exprtk_simple_example_09.cpp +(14) exprtk_simple_example_10.cpp