diff --git a/exprtk.hpp b/exprtk.hpp index b819573..916e74b 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -3577,7 +3577,6 @@ namespace exprtk 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_ternary); add_invalid_set1(lexer::token::e_assign ); add_invalid_set1(lexer::token::e_shr ); add_invalid_set1(lexer::token::e_shl ); @@ -4204,6 +4203,11 @@ namespace exprtk data_(reinterpret_cast(ts_.data)) {} + type_view(const type_store_t& ts) + : ts_(const_cast(ts)), + data_(reinterpret_cast(ts_.data)) + {} + inline std::size_t size() const { return ts_.size; @@ -15975,6 +15979,7 @@ namespace exprtk { public: + typedef T (*ff00_functor)(); typedef T (*ff01_functor)(T); typedef T (*ff02_functor)(T,T); typedef T (*ff03_functor)(T,T,T); @@ -15993,6 +15998,16 @@ namespace exprtk protected: + struct freefunc00 : public exprtk::ifunction + { + using exprtk::ifunction::operator(); + + freefunc00(ff00_functor ff) : exprtk::ifunction(0), f(ff) {} + inline T operator() () + { return f(); } + ff00_functor f; + }; + struct freefunc01 : public exprtk::ifunction { using exprtk::ifunction::operator(); @@ -17034,14 +17049,14 @@ namespace exprtk return add_function(function_name,(*local_data().free_function_list_.back())); \ } \ - exprtk_define_freefunction(01) exprtk_define_freefunction(02) - exprtk_define_freefunction(03) exprtk_define_freefunction(04) - exprtk_define_freefunction(05) exprtk_define_freefunction(06) - exprtk_define_freefunction(07) exprtk_define_freefunction(08) - exprtk_define_freefunction(09) exprtk_define_freefunction(10) - exprtk_define_freefunction(11) exprtk_define_freefunction(12) - exprtk_define_freefunction(13) exprtk_define_freefunction(14) - exprtk_define_freefunction(15) + exprtk_define_freefunction(00) exprtk_define_freefunction(01) + exprtk_define_freefunction(02) exprtk_define_freefunction(03) + exprtk_define_freefunction(04) exprtk_define_freefunction(05) + exprtk_define_freefunction(06) exprtk_define_freefunction(07) + exprtk_define_freefunction(08) exprtk_define_freefunction(09) + exprtk_define_freefunction(10) exprtk_define_freefunction(11) + exprtk_define_freefunction(12) exprtk_define_freefunction(13) + exprtk_define_freefunction(14) exprtk_define_freefunction(15) #undef exprtk_define_freefunction diff --git a/exprtk_simple_example_02.cpp b/exprtk_simple_example_02.cpp index cb18b35..71b2069 100644 --- a/exprtk_simple_example_02.cpp +++ b/exprtk_simple_example_02.cpp @@ -39,14 +39,14 @@ void square_wave() static const T pi = T(3.141592653589793238462643383279502); - T f = pi / T(10); - T t = T(0); - T a = T(10); + const T f = pi / T(10); + const T a = T(10); + T t = T(0); symbol_table_t symbol_table; - symbol_table.add_variable("f",f); symbol_table.add_variable("t",t); - symbol_table.add_variable("a",a); + symbol_table.add_constant("f",f); + symbol_table.add_constant("a",a); symbol_table.add_constants(); expression_t expression; diff --git a/exprtk_simple_example_03.cpp b/exprtk_simple_example_03.cpp index 16b627f..924e404 100644 --- a/exprtk_simple_example_03.cpp +++ b/exprtk_simple_example_03.cpp @@ -30,9 +30,9 @@ void polynomial() std::string expression_string = "25x^5 - 35x^4 - 15x^3 + 40x^2 - 15x + 1"; - T r0 = T(0); - T r1 = T(1); - T x = T(0); + const T r0 = T(0); + const T r1 = T(1); + T x = T(0); symbol_table_t symbol_table; symbol_table.add_variable("x",x); diff --git a/exprtk_test.cpp b/exprtk_test.cpp index 4fcdbe4..9867f33 100644 --- a/exprtk_test.cpp +++ b/exprtk_test.cpp @@ -4848,7 +4848,7 @@ inline std::size_t load_expressions(const std::string& file_name, std::size_t line_count = 0; - while (std::getline(stream,buffer)) + while (std::getline(stream,(buffer))) { if (buffer.empty()) continue; @@ -7685,7 +7685,7 @@ struct my_usr_ext : public exprtk::parser::unknown_symbol_resolver { static T var_default_value = 1.0; - if (!(result = symbol_table.create_variable(unknown_symbol, var_default_value++))) + if ((result = symbol_table.create_variable(unknown_symbol, var_default_value++)) == false) { error_message = "Failed to create variable(" + unknown_symbol + ") in primary symbol table"; } @@ -7694,7 +7694,7 @@ struct my_usr_ext : public exprtk::parser::unknown_symbol_resolver { static T cvar_default_value = 1.0; - if (!(result = symbol_table.add_constant(unknown_symbol, cvar_default_value++))) + if ((result = symbol_table.add_constant(unknown_symbol, cvar_default_value++)) == false) { error_message = "Failed to create const variable(" + unknown_symbol + ") in primary symbol table"; } diff --git a/readme.txt b/readme.txt index 485791b..99dd3b7 100644 --- a/readme.txt +++ b/readme.txt @@ -224,7 +224,7 @@ of C++ compilers: +----------+---------------------------------------------------------+ | true | True state or any value other than zero (typically 1). | +----------+---------------------------------------------------------+ -| false | False state, value of zero. | +| false | False state, value of exactly zero. | +----------+---------------------------------------------------------+ | and | Logical AND, True only if x and y are both true. | | | (eg: x and y) | @@ -275,7 +275,7 @@ of C++ compilers: | clamp | Clamp x in range between r0 and r1, where r0 < r1. | | | (eg: clamp(r0,x,r1)) | +----------+---------------------------------------------------------+ -| equal | Equality test between x and y using normalized epsilon | +| equal | Equality test between x and y using normalised epsilon | +----------+---------------------------------------------------------+ | erf | Error function of x. (eg: erf(x)) | +----------+---------------------------------------------------------+ @@ -321,7 +321,7 @@ of C++ compilers: +----------+---------------------------------------------------------+ | ncdf | Normal cumulative distribution function. (eg: ncdf(x)) | +----------+---------------------------------------------------------+ -| nequal | Not-equal test between x and y using normalized epsilon | +| nequal | Not-equal test between x and y using normalised epsilon | +----------+---------------------------------------------------------+ | pow | x to the power of y. (eg: pow(x,y) == x ^ y) | +----------+---------------------------------------------------------+ @@ -499,7 +499,7 @@ of C++ compilers: | | eg: | | | 1. if (x > y) z; else w; | | | 2. if (x > y) z; else if (w != u) v; | -| | 3. if (x < y) {z; w + 1;} else u; | +| | 3. if (x < y) { z; w + 1; } else u; | | | 4. if ((x != y) and (z > w)) | | | { | | | y := sin(x) / u; | @@ -651,7 +651,7 @@ expressions. The types are as follows: (1) Scalar Type The scalar type is a singular numeric value. The underlying type is -that used to specialize the ExprTk components (float, double, long +that used to specialise the ExprTk components (float, double, long double, MPFR et al). @@ -673,7 +673,7 @@ however can not interact with scalar or vector types. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [SECTION 10 - COMPONENTS] -There are three primary components, that are specialized upon a given +There are three primary components, that are specialised upon a given numeric type, which make up the core of ExprTk. The components are as follows: @@ -791,8 +791,9 @@ methods: 4. bool add_vector (const std::string& name, vector_type&) -The 'vector' type must consist of a contiguous array of scalars which -can be one of the following: +Note: The 'vector' type must be comprised from a contiguous array of +scalars with a size that is larger than zero. The vector type itself +can be any one of the following: 1. std::vector 2. scalar_t(&v)[N] @@ -800,6 +801,18 @@ can be one of the following: 4. exprtk::vector_view +When registering a variable, vector, string or function with an +instance of a symbol_table, the call to 'add_...' may fail and return +a false result due to one or more of the following reasons: + + 1. Variable name contains invalid characters or is ill-formed + 2. Variable name conflicts with a reserved word (eg: 'while') + 3. Variable name conflicts with a previously registered variable + 4. A vector of size (length) zero is being registered + 5. A free function exceeding fifteen parameters is being registered + 6. The symbol_table instance is in an invalid state + + (2) Expression A structure that holds an abstract syntax tree or AST for a specified expression and is used to evaluate said expression. Evaluation of the @@ -920,8 +933,8 @@ including which control block each expression references and their associated reference counts. - exprtk::expression e0; // constructed expression, eg: x + 1 - exprtk::expression e1; // constructed expression, eg: 2z + y + exprtk::expression e0; // constructed expression, eg: x + 1 + exprtk::expression e1; // constructed expression, eg: 2z + y +-----[ e0 cntrl block]----+ +-----[ e1 cntrl block]-----+ | 1. Expression Node 'x+1' | | 1. Expression Node '2z+y' | @@ -934,7 +947,7 @@ associated reference counts. +--------------------+ +--------------------+ - e0 = e1; // e0 and e1 are now 2z+y + e0 = e1; // e0 and e1 are now 2z+y +-----[ e1 cntrl block]-----+ | 1. Expression Node '2z+y' | @@ -1226,7 +1239,7 @@ In the following example, the return value of the expression will be within the loop body on its last iteration: var x := 1; - x + for (var i := i; i < 10; i += 1) + x + for (var i := x; i < 10; i += 1) { i / 2; i + 1; @@ -1303,7 +1316,7 @@ lets review the following expression: var x := 2; // Statement 1 var y := x + 2; // Statement 2 - x + y // Statement 3 + x + y; // Statement 3 y := x + 3y; // Statement 4 x - y; // Statement 5 @@ -1332,7 +1345,7 @@ ExprTk support two forms of conditional branching or otherwise known as if-statements. The first form, is a simple function based conditional statement, that takes exactly three input expressions: condition, consequent and alternative. The following is an example -expression that utilizes the function based if-statement. +expression that utilises the function based if-statement. x := if (y < z, y + 1, 2* z) @@ -1451,7 +1464,7 @@ Special functions dramatically decrease the total evaluation time of expressions which would otherwise have been written using the common form by reducing the total number of nodes in the evaluation tree of an expression and by also leveraging the compiler's ability to -correctly optimize such expressions for a given architecture. +correctly optimise such expressions for a given architecture. 3-Parameter 4-Parameter +-------------+-------------+ +--------------+------------------+ @@ -1581,25 +1594,28 @@ examples of string variable definitions: (a) Initialise to a string var x := 'abc'; - (b) Initialise to a string expression + (b) Initialise to an empty string + var x := ''; + + (c) Initialise to a string expression var x := 'abc' + '123'; - (c) Initialise to a string range + (d) Initialise to a string range var x := 'abc123'[2:4]; - (d) Initialise to another string variable + (e) Initialise to another string variable var x := 'abc'; var y := x; - (e) Initialise to another string variable range + (f) Initialise to another string variable range var x := 'abc123'; var y := x[2:4]; - (f) Initialise to a string expression + (g) Initialise to a string expression var x := 'abc'; var y := x + '123'; - (g) Initialise to a string expression range + (h) Initialise to a string expression range var x := 'abc'; var y := (x + '123')[1:3]; @@ -1775,7 +1791,7 @@ needs to be 'updated' to either another vector or sub-range, the vector_view instance can be efficiently rebased, and the expression evaluated as normal. - exprtk::vector_view view = exprtk::make_vector_view(v, v.size()); + exprtk::vector_view view = exprtk::make_vector_view(v,v.size()); symbol_table_t symbol_table; symbol_table.add_vector("v",view); @@ -1794,7 +1810,7 @@ evaluated as normal. [SECTION 15 - USER DEFINED FUNCTIONS] ExprTk provides a means whereby custom functions can be defined and -utilized within expressions. The concept requires the user to +utilised within expressions. The concept requires the user to provide a reference to the function coupled with an associated name that will be invoked within expressions. Functions may take numerous inputs but will always return a single value of the underlying numeric @@ -1840,7 +1856,7 @@ function called 'foo': (2) ivararg_function This interface supports a variable number of scalar arguments as input into the function. The function operator interface uses a std::vector -specialized upon type T to facilitate parameter passing. The following +specialised upon type T to facilitate parameter passing. The following example defines a vararg function called 'boo': template @@ -1863,7 +1879,7 @@ example defines a vararg function called 'boo': (3) igeneric_function This interface supports a variable number of arguments and types as input into the function. The function operator interface uses a -std::vector specialized upon the type_store type to facilitate +std::vector specialised upon the type_store type to facilitate parameter passing. Scalar <-- function(i_0, i_1, i_2....., i_N) @@ -2335,6 +2351,42 @@ Note: For the igeneric_function type, there also needs to be a 'Z' parameter sequence defined in order for the zero parameter trait to properly take effect otherwise a compilation error will occur. + +(9) Free Functions +The ExprTk symbol table supports the registration of free functions +and lambdas (anonymous functors) for use in expressions. The basic +requirements are similar to those found in ifunction derived user +defined functions. This includes support for free functions using +anywhere from zero up to fifteen input parameters of scalar type, with +a return type that is also scalar. Furthermore such functions will by +default be assumed to have side-effects and hence will not participate +in constant folding optimisations. + +In the following example, a two input parameter free function named +'compute', and a three input parameter lambda will be registered with +the given symbol_table instance: + + + double compute(double v0, double v1) + { + return 2.0 * v0 + v1 / 3.0; + } + + . + . + . + + typedef exprtk::symbol_table symbol_table_t; + + symbol_table_t symbol_table; + + symbol_table.add_function("compute", compute); + + symbol_table.add_function("lambda", + [](double v0, double v1, double v2) -> double + { return v0 / v1 + v2; }); + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [SECTION 16 - EXPRESSION DEPENDENTS] @@ -2443,7 +2495,7 @@ associated assignments: (5) None x + y + z -Note: In expression 4, both variables 'z' and 'w' are denoted as being +Note: In expression 4, both variables 'w' and 'z' are denoted as being assignments even though only one of them can ever be modified at the time of evaluation. Furthermore the determination of which of the two variables the modification will occur upon can only be known with @@ -2489,7 +2541,7 @@ associated with a given expression instance. However as an expression can have more than one symbol table instance associated with itself, when building more complex systems that -utilize many expressions where each can in turn utilize one or more +utilise many expressions where each can in turn utilise one or more variables from a large set of potential variables, functions or constants, it becomes evident that grouping variables into layers of symbol_tables will simplify and streamline the overall process. @@ -2722,7 +2774,7 @@ expressions: In this scenario one can use the 'dependent_entity_collector' component as described in [Section 16] to further determine which of the registered variables were actually used in the given expression. -As an example once the set of utilized variables are known, any +As an example once the set of utilised variables are known, any further 'attention' can be restricted to only those variables when evaluating the expression. This can be quite useful when dealing with expressions that can draw from a set of hundreds or even thousands of @@ -3427,8 +3479,8 @@ values. expression.value(); - printf("Result0: %15.5f\n",result0 ); - printf("Result1: %s\n" ,result1.c_str()); + printf("Result0: %15.5f\n", result0 ); + printf("Result1: %s\n" , result1.c_str()); In the example above, the expression will compute two results. As such @@ -3436,7 +3488,7 @@ two result variables are defined to hold the values named result0 and result1 respectively. The first is of scalar type (double), the second is of string type. Once the expression has been evaluated, the two variables will have been updated with the new result values, and can -then be further utilized from within the calling program. +then be further utilised from within the calling program. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3753,12 +3805,12 @@ overloads, the definitions of which are: (1) No variables (2) One variable called x - (3) Two variable called x and y - (3) Three variable called x, y and z + (3) Two variables called x and y + (3) Three variables called x, y and z -An example use of each of the three overloads for the compute routine -is as follows: +Example uses of each of the three overloads for the compute routine +are as follows: T result = T(0); @@ -3803,11 +3855,11 @@ is as follows: (d) integrate This free function will attempt to perform a numerical integration of -a single variable compiled expression over a defined range and given -step size. The numerical integration is based on the three point form -of the Simpson's rule. The integrate function has two overloads, where -the variable of integration can either be passed as a reference or as -a name in string form. Example usage of the function is as follows: +a single variable compiled expression over a specified range and step +size. The numerical integration is based on the three point form of +Simpson's rule. The integrate function has two overloads, where the +variable of integration can either be passed as a reference or as a +name in string form. Example usage of the function is as follows: typedef exprtk::parser parser_t; typedef exprtk::expression expression_t; @@ -4047,7 +4099,7 @@ expressions. As an example, lets take the following expression: 1 / sqrt(2x) * e^(3y) -Let's say we would like to determine which sub-part of the expression +Lets say we would like to determine which sub-part of the expression takes the most time to evaluate and perhaps attempt to rework the expression based on the results. In order to do this we will create a text file called 'test.txt' and then proceed to make some educated @@ -4133,7 +4185,7 @@ into account when using ExprTk: (09) The life-time of objects registered with or created from a specific symbol-table must span at least the life-time of the - compiled expressions which utilize objects, such as variables, + compiled expressions which utilise objects, such as variables, of that symbol-table, otherwise the result will be undefined behavior. @@ -4289,7 +4341,15 @@ into account when using ExprTk: performance critical code paths, and should instead occur entirely either before or after such code paths. - (32) Before jumping in and using ExprTk, do take the time to peruse + (32) Deep copying an expression instance for the purposes of + persisting to disk or otherwise transmitting elsewhere with the + intent to 'resurrect' the expression instance later on is not + possible due to the reasons described in the final note of + Section 10. The recommendation is to instead simply persist the + string form of the expression and compile the expression at + run-time on the target. + + (33) Before jumping in and using ExprTk, do take the time to peruse the documentation and all of the examples, both in the main and the extras distributions. Having an informed general view of what can and can't be done, and how something should be done