C++ Mathematical Expression Library (ExprTk) http://www.partow.net/programming/exprtk/index.html

This commit is contained in:
Arash Partow 2015-03-28 23:21:55 +11:00
parent 73b1e4c9f3
commit 53b2977902
8 changed files with 3861 additions and 2838 deletions

1408
exprtk.hpp

File diff suppressed because it is too large Load Diff

View File

@ -24,16 +24,21 @@
template <typename T>
void trig_function()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
std::string expression_string = "clamp(-1.0,sin(2 * pi * x) + cos(x / 2 * pi),+1.0)";
T x;
exprtk::symbol_table<T> symbol_table;
symbol_table_t symbol_table;
symbol_table.add_variable("x",x);
symbol_table.add_constants();
exprtk::expression<T> expression;
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser_t parser;
parser.compile(expression_string,expression);
for (x = T(-5); x <= T(+5); x += T(0.001))

View File

@ -24,6 +24,10 @@
template <typename T>
void square_wave()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
std::string expr_string = "a*(4/pi)*"
"((1 /1)*sin( 2*pi*f*t)+(1 /3)*sin( 6*pi*f*t)+"
" (1 /5)*sin(10*pi*f*t)+(1 /7)*sin(14*pi*f*t)+"
@ -39,16 +43,16 @@ void square_wave()
T t = T(0);
T a = T(10);
exprtk::symbol_table<T> symbol_table;
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_constants();
exprtk::expression<T> expression;
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser_t parser;
parser.compile(expr_string,expression);
const T delta = (T(4) * pi) / T(1000);

View File

@ -24,19 +24,23 @@
template <typename T>
void polynomial()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
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);
exprtk::symbol_table<T> symbol_table;
symbol_table_t symbol_table;
symbol_table.add_variable("x",x);
exprtk::expression<T> expression;
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser_t parser;
parser.compile(expression_string,expression);
const T delta = T(1.0 / 100.0);

View File

@ -37,13 +37,17 @@ struct myfunc : public exprtk::ifunction<T>
template <typename T>
void custom_function()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
std::string expression_string = "myfunc(sin(x * pi),y / 2)";
T x = T(1);
T y = T(2);
myfunc<T> mf;
exprtk::symbol_table<T> symbol_table;
symbol_table_t symbol_table;
symbol_table.add_variable("x",x);
symbol_table.add_variable("y",y);
symbol_table.add_function("myfunc",mf);
@ -52,7 +56,7 @@ void custom_function()
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser_t parser;
parser.compile(expression_string,expression);
T result = expression.value();

View File

@ -24,10 +24,13 @@
template <typename T>
void logic()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
std::string expression_string = "not(A and B) or C";
exprtk::symbol_table<T> symbol_table;
symbol_table_t symbol_table;
symbol_table.create_variable("A");
symbol_table.create_variable("B");
symbol_table.create_variable("C");
@ -35,7 +38,7 @@ void logic()
expression_t expression;
expression.register_symbol_table(symbol_table);
exprtk::parser<T> parser;
parser_t parser;
parser.compile(expression_string,expression);
printf(" # | A | B | C | %s\n"

View File

@ -2287,7 +2287,8 @@ inline bool run_test02()
parser.error().c_str(),
test.expr.c_str());
return false;
result = false;
continue;
}
}
@ -2303,7 +2304,9 @@ inline bool run_test02()
test.expr.c_str(),
(double)test.result,
(double)expr_result);
result = false;
continue;
}
}
@ -2347,18 +2350,21 @@ inline bool run_test02()
{
printf("run_test02() - Evaluation Error [2]: Expression: [%s]\tExpected: True\n",
expression_str.c_str());
return false;
}
else if ("234567" != s0)
{
printf("run_test02() - Evaluation Error [2]: Expression: [%s]\tInvalid value for s0\n",
expression_str.c_str());
return false;
}
else if ("xyz" != s1)
{
printf("run_test02() - Evaluation Error [2]: Expression: [%s]\tInvalid value for s1\n",
expression_str.c_str());
return false;
}
}
@ -2406,7 +2412,8 @@ inline bool run_test03()
for (std::size_t r = 0; r < rounds; ++r)
{
exprtk::symbol_table<T> symbol_table;
exprtk::symbol_table<T> symbol_table_0;
exprtk::symbol_table<T> symbol_table_1;
exprtk::expression<T> expression;
std::vector<T> v;
@ -2415,21 +2422,28 @@ inline bool run_test03()
for (std::size_t i = 0; i < variable_list_size; ++i)
{
v[i] = T(i);
symbol_table.add_variable(variable_list[i],v[i]);
if (i & 1)
symbol_table_0.add_variable(variable_list[i],v[i]);
else
symbol_table_1.add_variable(variable_list[i],v[i]);
}
if (variable_list_size != symbol_table.variable_count())
std::size_t total_symbol_count = symbol_table_0.variable_count() +
symbol_table_1.variable_count();
if (variable_list_size != total_symbol_count)
{
printf("run_test03() - Error - Invalid number of variables in symbol_table! Expected: %d got: %d\n",
static_cast<unsigned int>(variable_list_size),
static_cast<unsigned int>(symbol_table.variable_count()));
static_cast<unsigned int>(total_symbol_count));
return false;
}
symbol_table.add_constants();
symbol_table_0.add_constants();
expression.register_symbol_table(symbol_table);
expression.register_symbol_table(symbol_table_0);
expression.register_symbol_table(symbol_table_1);
{
exprtk::parser<T> parser;
@ -3750,6 +3764,21 @@ inline bool run_test10()
"~{ var x := 1 } + ~{ var x := 2 } == 3",
"(~{ var x := 1 } + ~{ var x := 2 }) == (~{ var x := 2 } + ~{ var x := 1 })",
"(~{ var x := 1 } + ~{ var x := 2 } + ~{~{ var x := 1 } + ~{ var x := 2 }}) == 6",
"(~{ var x[1] := [1] } + ~{ var x[1] := [2] } + ~{~{ var x[1] := [1] } + ~{ var x[1] := [2] }}) == 6",
"(~{ var x := [1] } + ~{ var x[1] := [2] } + ~{~{ var x[1] := [1] } + ~{ var x[1] := [2] }}) == 6",
"(~{ var x[1] := [1] } + ~{ var x := [2] } + ~{~{ var x[1] := [1] } + ~{ var x[1] := [2] }}) == 6",
"(~{ var x[1] := [1] } + ~{ var x[1] := [2] } + ~{~{ var x := [1] } + ~{ var x[1] := [2] }}) == 6",
"(~{ var x[1] := [1] } + ~{ var x[1] := [2] } + ~{~{ var x[1] := [1] } + ~{ var x := [2] }}) == 6",
"(~{~{ var x[1] := [1] } + ~{ var x[1] := [2] }} + ~{ var x[1] := [1] } + ~{ var x[1] := [2] }) == 6",
"(~{~{ var x := [1] } + ~{ var x[1] := [2] }} + ~{ var x[1] := [1] } + ~{ var x[1] := [2] }) == 6",
"(~{~{ var x[1] := [1] } + ~{ var x := [2] }} + ~{ var x[1] := [1] } + ~{ var x[1] := [2] }) == 6",
"(~{~{ var x[1] := [1] } + ~{ var x[1] := [2] }} + ~{ var x := [1] } + ~{ var x[1] := [2] }) == 6",
"(~{~{ var x[1] := [1] } + ~{ var x[1] := [2] }} + ~{ var x[1] := [1] } + ~{ var x := [2] }) == 6",
"(~{~{ var x[1] := [1] }} + ~{ var x[1] := [1] } + ~{ var x[1] := [2] } + ~{{ var x[1] := [2] }}) == 6",
"(~{~{ var x := [1] }} + ~{ var x[1] := [1] } + ~{ var x[1] := [2] } + ~{{ var x[1] := [2] }}) == 6",
"(~{~{ var x[1] := [1] }} + ~{ var x := [1] } + ~{ var x[1] := [2] } + ~{{ var x[1] := [2] }}) == 6",
"(~{~{ var x[1] := [1] }} + ~{ var x[1] := [1] } + ~{ var x := [2] } + ~{{ var x[1] := [2] }}) == 6",
"(~{~{ var x[1] := [1] }} + ~{ var x[1] := [1] } + ~{ var x[1] := [2] } + ~{{ var x := [2] }}) == 6",
"(~{ var x[3] := [1] } + ~{ var x[6] := {6,5,4,3,2,1}}) == 7",
"(~{ var x[6] := {6,5,4,3,2,1} } + ~{ var x := 1 }) == 7",
"(~{ var x := 1 } + ~{ var x[6] := {6,5,4,3,2,1} }) == 7",
@ -3885,7 +3914,12 @@ inline bool run_test10()
"+ ~{ for (var i := 0; i < 10; i += 1) { for (var j := 0; j <= i; j += 1) "
" { var y := 3; var v2[3] := {1,2,3}; if ((i + j + y + x + abs(v0[i % v0[]] - "
"v2[j % v2[]])) < 6) { var v3[3] := {1,2,3}; y += x / v3[j % v3[]]; "
"continue; } else break[i + j]; } } } ) == 18 "
"continue; } else break[i + j]; } } } ) == 18 ",
"12 == (if (1 > 2) { var x:= 2; } else { var x[3] := {7,2,3}; sum(x); })",
"12 == (if (1 < 2) { var x[3] := {7,2,3}; sum(x); } else { var x:= 2; })",
"12 == (if (1 > 2) { var x:= 2; } else { var x[3] := {7,2,3}; sum(x); })",
"12 == (if (1 < 2) { var x[3] := {7,2,3}; sum(x); } else { var x:= 2; })"
};
const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string);
@ -3916,7 +3950,8 @@ inline bool run_test10()
parser.error().c_str(),
expression_list[i].c_str());
return false;
failed = true;
continue;
}
}
@ -3926,6 +3961,7 @@ inline bool run_test10()
{
printf("run_test10() - swaps evaluation error Expression: %s\n",
expression_list[i].c_str());
failed = true;
}
}
@ -4142,36 +4178,52 @@ struct cosine_deg : public exprtk::ifunction<T>
template <typename T>
inline bool run_test13()
{
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
static const std::string expression_string[] =
{
"equal(sin(30),0.5)",
"equal(cos(60),0.5)",
"equal(sin(60),sqrt(3)/2)",
"equal(cos(30),sqrt(3)/2)",
"equal(sin(x_deg),0.5)",
"equal(cos(y_deg),0.5)",
"equal(sin(y_deg),sqrt(3)/2)",
"equal(cos(x_deg),sqrt(3)/2)",
"equal(sin(30),0.5) ",
"equal(cos(60),0.5) ",
"equal(sin(60),sqrt(3)/2) ",
"equal(cos(30),sqrt(3)/2) ",
"equal(sin(x_deg),0.5) ",
"equal(cos(y_deg),0.5) ",
"equal(sin(y_deg),sqrt(3)/2) ",
"equal(cos(x_deg),sqrt(3)/2) ",
"equal(sin(30) + sin(30),1.0) ",
"equal(cos(60) + cos(60),1.0) ",
"equal(sin(60) + sin(60),sqrt(3)) ",
"equal(cos(30) + cos(30),sqrt(3)) ",
"equal(sin(x_deg) + sin(x_deg),1.0) ",
"equal(cos(y_deg) + cos(y_deg),1.0) ",
"equal(sin(y_deg) + sin(y_deg),sqrt(3))",
"equal(cos(x_deg) + cos(x_deg),sqrt(3))"
};
static const std::size_t expression_string_size = sizeof(expression_string) / sizeof(std::string);
{
T x_deg = T(30);
T y_deg = T(60);
sine_deg<T> sine;
sine_deg <T> sine;
cosine_deg<T> cosine;
exprtk::symbol_table<T> symbol_table;
symbol_table_t symbol_table_0;
symbol_table_t symbol_table_1;
symbol_table.add_variable("x_deg",x_deg);
symbol_table.add_variable("y_deg",y_deg);
symbol_table_0.add_variable("x_deg",x_deg);
symbol_table_1.add_variable("y_deg",y_deg);
symbol_table.add_function("sine_deg",sine);
symbol_table.add_function("cosine_deg",cosine);
symbol_table_0.add_function( "sine_deg", sine);
symbol_table_1.add_function("cosine_deg", cosine);
expression_t expression;
expression.register_symbol_table(symbol_table);
expression.register_symbol_table(symbol_table_0);
expression.register_symbol_table(symbol_table_1);
static const std::size_t rounds = 100;
@ -4182,14 +4234,14 @@ inline bool run_test13()
const std::string& expr_str = expression_string[j];
{
exprtk::parser<T> parser;
parser_t parser;
parser.replace_symbol("sin","sine_deg");
parser.replace_symbol("cos","cosine_deg");
parser.replace_symbol("sin", "sine_deg");
parser.replace_symbol("cos", "cosine_deg");
if (!parser.compile(expr_str,expression))
{
printf("run_test13() - Error: %s Expression: %s\n",
printf("run_test13() - Error: %s Expression: %s [1]\n",
parser.error().c_str(),
expr_str.c_str());
@ -4199,11 +4251,69 @@ inline bool run_test13()
if (T(1) != expression.value())
{
printf("run_test13() - Error in evaluation! Expression: %s\n",expr_str.c_str());
printf("run_test13() - Error in evaluation! Expression: %s [1]\n",expr_str.c_str());
return false;
}
}
}
}
{
T x_deg = T(30);
T y_deg = T(60);
sine_deg <T> sine;
cosine_deg<T> cosine;
symbol_table_t symbol_table_0;
symbol_table_t symbol_table_1;
symbol_table_0.add_variable("x_deg",x_deg);
symbol_table_1.add_variable("y_deg",y_deg);
symbol_table_0.add_reserved_function("sin", sine);
symbol_table_1.add_reserved_function("cos",cosine);
expression_t expression;
expression.register_symbol_table(symbol_table_0);
expression.register_symbol_table(symbol_table_1);
static const std::size_t rounds = 100;
for (std::size_t i = 0; i < rounds; ++i)
{
for (std::size_t j = 0; j < expression_string_size; ++j)
{
const std::string& expr_str = expression_string[j];
{
typedef typename parser_t::settings_store settings_t;
parser_t parser;
parser.settings()
.disable_base_function(settings_t::e_bf_sin)
.disable_base_function(settings_t::e_bf_cos);
if (!parser.compile(expr_str,expression))
{
printf("run_test13() - Error: %s Expression: %s [2]\n",
parser.error().c_str(),
expr_str.c_str());
return false;
}
}
if (T(1) != expression.value())
{
printf("run_test13() - Error in evaluation! Expression: %s [2]\n",expr_str.c_str());
return false;
}
}
}
}
return true;
}
@ -6397,11 +6507,18 @@ inline bool run_test20()
for (std::size_t i = 0; i < 100; ++i)
{
exprtk::symbol_table<T> symbol_table;
symbol_table.add_constants();
exprtk::symbol_table<T> symbol_table0;
exprtk::symbol_table<T> symbol_table1;
exprtk::symbol_table<T> symbol_table2;
exprtk::symbol_table<T> symbol_table3;
symbol_table0.add_constants();
expression_t expression;
expression.register_symbol_table(symbol_table);
expression.register_symbol_table(symbol_table0);
expression.register_symbol_table(symbol_table1);
expression.register_symbol_table(symbol_table2);
expression.register_symbol_table(symbol_table3);
exprtk::parser<T> parser;

View File

@ -442,7 +442,7 @@ of C++ compilers:
| | eg: |
| | 1. 'abc'[] == 3 |
| | 2. var max_str_length := max(s0[],s1[],s2[],s3[]) |
| | 3. ('abc' + 'xyz')[] == 3 |
| | 3. ('abc' + 'xyz')[] == 6 |
| | 4. (('abc' + 'xyz')[1:4])[] == 4 |
+----------+---------------------------------------------------------+
@ -604,7 +604,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
double MPFR et al).
double, MPFR et al).
(2) Vector Type
@ -628,9 +628,9 @@ There are three primary components, that are specialized upon a given
numeric type, which make up the core of ExprTk. The components are as
follows:
1. Symbol Table exprtk::symbol_table<NumericType>
2. Expression exprtk::expression<NumericType>
3. Parser exprtk::parser<NumericType>
(1) Symbol Table exprtk::symbol_table<NumericType>
(2) Expression exprtk::expression<NumericType>
(3) Parser exprtk::parser<NumericType>
(1) Symbol Table
@ -736,12 +736,12 @@ Expression: z := (x + y^-2.345) * sin(pi / min(w - 7.3,v))
(3) Parser
A structure which takes as input a string representation of an
expression and attempts to compile said input with the result
being an instance of Expression. If an error is encountered
during the compilation process, the parser will stop compiling
and return an error status code, with a more detailed
description of the error(s) and its location within the input
provided by the 'get_error' interface.
expression and attempts to compile said input with the result being an
instance of Expression. If an error is encountered during the
compilation process, the parser will stop compiling and return an
error status code, with a more detailed description of the error(s)
and its location within the input provided by the 'get_error'
interface.
@ -751,11 +751,11 @@ options to be used during the compilation process of expressions.
An example instantiation of exprtk::parser where only the joiner,
commutative and strength reduction options are enabled is as follows:
typedef exprtk::parser<NumericType> parser_t;
typedef exprtk::parser<NumericType>::settings_t settings_t;
std::size_t compile_options = parser_t::e_joiner +
parser_t::e_commutative_check +
parser_t::e_strength_reduction;
std::size_t compile_options = settings_t::e_joiner +
settings_t::e_commutative_check +
settings_t::e_strength_reduction;
parser_t parser(compile_options);
@ -763,6 +763,15 @@ commutative and strength reduction options are enabled is as follows:
Currently seven types of compile time options are supported, and
enabled by default. The options and their explanations are as follows:
(1) Replacer
(2) Joiner
(3) Numeric Check
(4) Bracket Check
(5) Sequence Check
(6) Commutative Check
(7) Strength Reduction Check
(1) Replacer (e_replacer)
Enable replacement of specific tokens with other tokens. For example
the token "true" of type symbol will be replaced with the numeric
@ -1084,6 +1093,7 @@ not a vector but rather a single value.
var x[3] := { 1, 2, 3 };
sum(x) == 6
sum(1 + 2x) == 15
avg(3x + 1) == 7
min(1 / x) == (1 / 3)
@ -1095,7 +1105,7 @@ not a vector but rather a single value.
ExprTk provides a means whereby custom functions can be defined and
utilized 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. Function can take in numerous
that will be invoked within expressions. Functions may take numerous
inputs but will always return a single value of the underlying numeric
type.
@ -1103,7 +1113,7 @@ During expression compilation when required the reference to the
function will be obtained from the associated symbol_table and be
embedded into the expression.
There are two types of function interface:
There are five types of function interface:
(1) ifunction
(2) ivararg_function
@ -1323,7 +1333,7 @@ situations such as concatenation and equality operations.
String <-- function(i_0, i_1, i_2....., i_N)
The following example defines an generic function named 'toupper' with
The following example defines a generic function named 'toupper' with
the string return type function operator being explicitly overridden:
template <typename T>
@ -1376,10 +1386,10 @@ is done:
symbol_table_t::e_ft_strfunc);
Note: There are two further refinements to the type checking facility
are the possibilities of a variable number of common types which can
be accomplished by using a wildcard '*' and a special 'any type' which
is done using the '?' character. It should be noted that the wildcard
Note: Two further refinements to the type checking facility are the
possibilities of a variable number of common types which can be
accomplished by using a wildcard '*' and a special 'any type' which is
done using the '?' character. It should be noted that the wildcard
operator is associated with the previous type in the sequence and
implies one or more of that type.
@ -1507,7 +1517,7 @@ symbol table.
.expression("1 + cos(x * y) / z;"));
(4) Using Functions In Expressions
(6) Using Functions In Expressions
For the above denoted custom and composited functions to be used in an
expression, an instance of each function needs to be registered with a
symbol_table that has been associated with the expression instance.
@ -1558,7 +1568,7 @@ The following demonstrates how all the pieces are put together:
expression.value();
(5) Function Side-Effects
(7) Function Side-Effects
All function calls are assumed to have side-effects by default. This
assumption implicitly disables constant folding optimisations when all
parameters being passed to the function are deduced as being constants
@ -1580,7 +1590,7 @@ to the constructor to denote the lack of side-effects.
};
(6) Zero Parameter Functions
(8) Zero Parameter Functions
When either an ifunction or ivararg_function derived type is defined
with zero number of parameters, there are two calling conventions
within expressions that are allowed. For a function named 'foo' with
@ -1676,11 +1686,11 @@ within an expression. The following are example expressions and their
associated assignments:
Assignments Expression
1. x x := y + z
2. x, y x += y += z
3. x, y, z x := y += sin(z := w + 2)
4. z, w if (x > y, z := x + 2, w := 'A String')
5. None x + y + z
(1) x x := y + z
(2) x, y x += y += z
(3) x, y, z x := y += sin(z := w + 2)
(4) z, w if (x > y, z := x + 2, w := 'A String')
(5) None x + y + z
Note: In expression 4, both variables 'z' and 'w' are denoted as being
@ -1835,7 +1845,7 @@ via the 'unknown symbol resolver' mechanism.
[18 - EXPRTK NOTES]
The following is a list of facts and suggestions one may want to take
into account when using Exprtk:
into account when using ExprTk:
(00) Precision and performance of expression evaluations are the
dominant principles of the ExprTk library.
@ -1935,38 +1945,38 @@ into account when using Exprtk:
into account. Specifically the 'compile' method of the parser
and the 'add_xxx' set of methods of the symbol_table as they
denote either the success or failure state of the invoked call.
Cointinued processing from a failed state without having first
Continued processing from a failed state without having first
rectified the underlying issue will in turn result in further
failures and undefined behaviours.
(25) The following are examples of compliant floating point value
representations:
(a) 12345 (e) -123.456
(b) +123.456e+12 (f) 123.456E-12
(c) +012.045e+07 (g) .1234
(d) 123.456f (h) -321.654E+3L
(1) 12345 (5) -123.456
(2) +123.456e+12 (6) 123.456E-12
(3) +012.045e+07 (7) .1234
(4) 123.456f (8) -321.654E+3L
(26) Expressions may contain any of the following comment styles:
1. // .... \n
2. # .... \n
3. /* .... */
(1) // .... \n
(2) # .... \n
(3) /* .... */
(27) The 'null' value type is a special non-zero type that
incorporates specific semantics when undergoing operations with
the standard numeric type. The following is a list of type and
boolean results associated with the use of 'null':
1. null +,-,*,/,% x --> x
2. x +,-,*,/,% null --> x
3. null +,-,*,/,% null --> null
4. null == null --> true
5. null == x --> true
6. x == null --> true
7. x != null --> false
8. null != null --> false
9. null != x --> false
(1) null +,-,*,/,% x --> x
(2) x +,-,*,/,% null --> x
(3) null +,-,*,/,% null --> null
(4) null == null --> true
(5) null == x --> true
(6) x == null --> true
(7) x != null --> false
(8) null != null --> false
(9) null != x --> false
(28) The following is a list of reserved words and symbols used by
ExprTk. Attempting to add a variable or custom function to a
@ -1979,9 +1989,9 @@ into account when using Exprtk:
expm1, false, floor, for, frac, grad2deg, hypot, iclamp, if,
ilike, in, inrange, in, like, log, log10, log1p, log2, logn,
mand, max, min, mod, mor, mul, nand, ncdf, nor, not,
not_equal, not, null, or, pow, rad2deg, repeat, root,
roundn, round, sec, sgn, shl, shr, sinc, sinh, sin, sqrt,
sum, swap, switch, tanh, tan, true, trunc, until, var,
not_equal, not, null, or, pow, rad2deg, repeat, return,
root, roundn, round, sec, sgn, shl, shr, sinc, sinh, sin,
sqrt, sum, swap, switch, tanh, tan, true, trunc, until, var,
while, xnor, xor, xor
(29) Every valid ExprTk statement is a "value returning" expression.