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

This commit is contained in:
Arash Partow 2014-12-08 06:33:10 +11:00
parent b6cdf0d2f9
commit 1d757d5718
3 changed files with 1255 additions and 351 deletions

1298
exprtk.hpp

File diff suppressed because it is too large Load Diff

View File

@ -1727,7 +1727,6 @@ inline bool run_test01()
const std::size_t expr_list_size = sizeof(expr_list) / sizeof(std::string); const std::size_t expr_list_size = sizeof(expr_list) / sizeof(std::string);
const std::size_t rounds = 60; const std::size_t rounds = 60;
for (std::size_t r = 0; r < rounds; ++r) for (std::size_t r = 0; r < rounds; ++r)
@ -2047,15 +2046,47 @@ inline bool run_test02()
test_ab<T>("{[('!@#$%^&*([{}])-=')]} == [{('!@#$%^&*([{}])-=')}]","","",T(1.0)), test_ab<T>("{[('!@#$%^&*([{}])-=')]} == [{('!@#$%^&*([{}])-=')}]","","",T(1.0)),
test_ab<T>("'1234\\\\abc\nxyz\r890\tqaz\\'567' == a","1234\\abc\nxyz\r890\tqaz'567","",T(1.0)), test_ab<T>("'1234\\\\abc\nxyz\r890\tqaz\\'567' == a","1234\\abc\nxyz\r890\tqaz'567","",T(1.0)),
test_ab<T>("a == '1234\\\\abc\nxyz\r890\tqaz\\'567'","1234\\abc\nxyz\r890\tqaz'567","",T(1.0)), test_ab<T>("a == '1234\\\\abc\nxyz\r890\tqaz\\'567'","1234\\abc\nxyz\r890\tqaz'567","",T(1.0)),
test_ab<T>("'123'[] == 3" ,"","",T(1.0)), test_ab<T>("'123'[] == 3" ,"","" ,T(1.0)),
test_ab<T>("3 == '123'[]" ,"","",T(1.0)), test_ab<T>("3 == '123'[]" ,"","" ,T(1.0)),
test_ab<T>("'123'[] + '1234'[] == 7" ,"","",T(1.0)), test_ab<T>("'123'[] + '1234'[] == 7" ,"","" ,T(1.0)),
test_ab<T>("abs('123'[] - '1234'[]) == 1" ,"","",T(1.0)), test_ab<T>("abs('123'[] - '1234'[]) == 1" ,"","" ,T(1.0)),
test_ab<T>("'1234'[] == a[]" ,"1234","",T(1.0)), test_ab<T>("'1234'[] == a[]" ,"1234","" ,T(1.0)),
test_ab<T>("'123'[] + a[] == 7" ,"1234","",T(1.0)), test_ab<T>("'123'[] + a[] == 7" ,"1234","" ,T(1.0)),
test_ab<T>("abs(a[] - '12345'[]) == 1" ,"1234","",T(1.0)), test_ab<T>("abs(a[] - '12345'[]) == 1" ,"1234","" ,T(1.0)),
test_ab<T>("'1234'[] + '12345'[] == a[] + b[]" ,"1234","12345",T(1.0)), test_ab<T>("'1234'[] + '12345'[] == a[] + b[]" ,"1234","12345" ,T(1.0)),
test_ab<T>("abs('123'[] - '1234'[]) == abs(a[] - b[])" ,"1234","12345",T(1.0)) test_ab<T>("abs('123'[] -'1234'[]) == abs(a[] - b[])" ,"1234","12345",T(1.0)),
test_ab<T>("(a + b) == 'abc123' ","abc","123" ,T(1.0)),
test_ab<T>("(a + '123') == 'abc123' ","abc","123" ,T(1.0)),
test_ab<T>("('abc' + b) == 'abc123' ","abc","123" ,T(1.0)),
test_ab<T>("(a + '1') == 'abc1' ","abc","123" ,T(1.0)),
test_ab<T>("('a' + b) == 'a123' ","abc","123" ,T(1.0)),
test_ab<T>("(a[2:7] + b) == 'cdefgh0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("(a + b[2:7]) == 'abc234567' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[2:7] + '0123') == 'cdefgh0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("('abc' + b[2:7]) == 'abc234567' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[2:2] + b[3:3]) == 'c3' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[3:] + b) == 'defghij0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("('abc' + b[:7]) == 'abc01234567' ","abc","0123456789" ,T(1.0)),
test_ab<T>("a + '123' == 'abc'+ b ","abc" , "123" , T(1.0)),
test_ab<T>("a[0:2] + '123' == 'abc' + b[0:2] ","abcXYZ", "123XYZ", T(1.0)),
test_ab<T>("a[ :2] + '123' == 'abc' + b[ :2] ","abcXYZ", "123XYZ", T(1.0)),
test_ab<T>("a[3: ] + '123' == 'abc' + b[3: ]","XYZabc", "XYZ123", T(1.0)),
test_ab<T>("a[3:a[] - 1] + '123' == 'abc' + b[3:b[] - 1]","XYZabc", "XYZ123", T(1.0)),
test_ab<T>("(a[r0:r2] + b) == 'cdefgh0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("(a + b[r0:r2]) == 'abc234567' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[r0:r2] + '0123') == 'cdefgh0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("('abc' + b[r0:r2]) == 'abc234567' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[r0:r0] + b[r3:r3]) == 'c3' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[r3:] + b) == 'defghij0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("('abc' + b[:r2]) == 'abc01234567' ","abc","0123456789" ,T(1.0)),
test_ab<T>("a[0:r0] + '123' == 'abc' + b[0:r0] ","abcXYZ", "123XYZ", T(1.0)),
test_ab<T>("a[ :r0] + '123' == 'abc' + b[ :r0] ","abcXYZ", "123XYZ", T(1.0)),
test_ab<T>("a[r3: ] + '123' == 'abc' + b[r3: ]","XYZabc", "XYZ123", T(1.0)),
test_ab<T>("a[r3:a[] - 1] + '123' == 'abc' + b[r3:b[] - 1]","XYZabc", "XYZ123", T(1.0)),
test_ab<T>("(a[r0:r0] + b[r3:r0+1]) == 'c3' ","abc","0123456789" ,T(1.0)),
test_ab<T>("(a[r0+1:] + b) == 'defghij0123' ","abcdefghij","0123",T(1.0)),
test_ab<T>("a[r0+1: ] + '123' == 'abc' + b[r0+1: ]","XYZabc", "XYZ123", T(1.0)),
test_ab<T>("a[r0+1:a[] - 1] + '123' == 'abc' + b[r0+1:b[] - 1]","XYZabc", "XYZ123", T(1.0))
}; };
static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_ab<T>); static const std::size_t test_list_size = sizeof(test_list) / sizeof(test_ab<T>);
@ -2074,6 +2105,8 @@ inline bool run_test02()
T r0 = T(2); T r0 = T(2);
T r1 = T(6); T r1 = T(6);
T r2 = T(7);
T r3 = T(3);
exprtk::symbol_table<T> symbol_table; exprtk::symbol_table<T> symbol_table;
symbol_table.add_stringvar("a" ,str_a); symbol_table.add_stringvar("a" ,str_a);
@ -2081,6 +2114,8 @@ inline bool run_test02()
symbol_table.add_stringvar("c" ,str_c); symbol_table.add_stringvar("c" ,str_c);
symbol_table.add_variable ("r0", r0); symbol_table.add_variable ("r0", r0);
symbol_table.add_variable ("r1", r1); symbol_table.add_variable ("r1", r1);
symbol_table.add_variable ("r2", r2);
symbol_table.add_variable ("r3", r3);
exprtk::expression<T> expression; exprtk::expression<T> expression;
expression.register_symbol_table(symbol_table); expression.register_symbol_table(symbol_table);
@ -4490,7 +4525,7 @@ struct gen_func : public exprtk::igeneric_function<T>
string_count(0) string_count(0)
{} {}
inline T operator()(parameter_list_t& params) inline T operator()(parameter_list_t params)
{ {
for (std::size_t i = 0; i < params.size(); ++i) for (std::size_t i = 0; i < params.size(); ++i)
{ {
@ -4556,7 +4591,7 @@ struct inc_func : public exprtk::igeneric_function<T>
inc_func() inc_func()
{} {}
inline T operator()(parameter_list_t& params) inline T operator()(parameter_list_t params)
{ {
for (std::size_t i = 0; i < params.size(); ++i) for (std::size_t i = 0; i < params.size(); ++i)
{ {
@ -4596,6 +4631,35 @@ struct inc_func : public exprtk::igeneric_function<T>
} }
}; };
template <typename T>
struct rem_space_and_uppercase : public exprtk::igeneric_function<T>
{
typedef typename exprtk::igeneric_function<T>::generic_type generic_type;
typedef typename exprtk::igeneric_function<T>::parameter_list_t parameter_list_t;
typedef typename generic_type::string_view string_t;
rem_space_and_uppercase()
: exprtk::igeneric_function<T>("S")
{}
inline T operator()(std::string& result, parameter_list_t params)
{
string_t string(params[0]);
result.reserve(string.size());
result.clear();
char c;
for (std::size_t i = 0; i < string.size(); ++i)
{
if (' ' != (c = string[i]))
result += std::toupper(c);
}
return T(0);
}
};
template <typename T> template <typename T>
inline bool run_test18() inline bool run_test18()
@ -4732,7 +4796,6 @@ inline bool run_test18()
static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string); static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string);
bool failure = false; bool failure = false;
for (std::size_t i = 0; i < expression_list_size; ++i) for (std::size_t i = 0; i < expression_list_size; ++i)
@ -4820,7 +4883,8 @@ inline bool run_test18()
"var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(z, 2w / 3, 'abc123',s0[2:5],v0, v1 + v2, v0[2], x, 2x + y);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(z, 2w / 3, 'abc123',s0[2:5],v0, v1 + v2, v0[2], x, 2x + y);",
"var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(2w / 3, 'abc123',s0[2:5],v0, v1 + v2, v0[2], x, 2x + y, z);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(2w / 3, 'abc123',s0[2:5],v0, v1 + v2, v0[2], x, 2x + y, z);",
"var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo('abc123', s0[2:5],v0, v1 + v2, v0[2], x, 2x + y, z,2w / 3);", "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo('abc123', s0[2:5],v0, v1 + v2, v0[2], x, 2x + y, z,2w / 3);",
"var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0[2:5],v0, v1 + v2, v0[2], x, 2x + y, z,2w / 3, 'abc123');" "var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0[2:5],v0, v1 + v2, v0[2], x, 2x + y, z,2w / 3, 'abc123');",
"var z := 3; var w[3] := { 1/3, 1/5, 1/7 }; foo(s0[2:3]+s0[4:5],v0, v1 + v2, v0[2], x, 2x + y, z,2w / 3, 'abc123');"
}; };
static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string); static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string);
@ -4836,6 +4900,7 @@ inline bool run_test18()
"VSSVVT*" , "VSSVVT*" ,
"SSVVTTTTV", "SSVVTTTTV",
"SVVTTTTVS", "SVVTTTTVS",
"SVVTTTTVS",
}; };
bool failure = false; bool failure = false;
@ -5001,6 +5066,110 @@ inline bool run_test18()
return false; return false;
} }
{
bool failure = false;
rem_space_and_uppercase<T> rsauc;
std::string s0 = "XXXXXXXXXXXXXXX";
std::string s1 = "XXXXXXXXXXXXXXX";
std::string s2 = "XXXXXXXXXXXXXXX";
std::string s3 = "XXXXXXXXXXXXXXX";
std::string s4 = "XXXXXXXXXXXXXXX";
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T> expression_t;
typedef exprtk::parser<T> parser_t;
symbol_table_t symbol_table;
symbol_table.add_constants();
symbol_table.add_stringvar("s0", s0);
symbol_table.add_stringvar("s1", s1);
symbol_table.add_stringvar("s2", s2);
symbol_table.add_stringvar("s3", s3);
symbol_table.add_stringvar("s4", s4);
symbol_table.add_function("remspc_uc",rsauc,symbol_table_t::e_ft_strfunc);
std::string program = " s0 := 'How now '; "
" s1 := 'brown cow?'; "
" s2 := remspc_uc(s0 + s1); "
" s3 := remspc_uc(s0) + s1; "
" s4 := s0 + remspc_uc(s1); "
" remspc_uc(s0 + s1) == remspc_uc(s0) + remspc_uc(s1);";
expression_t expression;
expression.register_symbol_table(symbol_table);
parser_t parser;
if (!parser.compile(program,expression))
{
printf("Error: %s\tExpression: %s\n",
parser.error().c_str(),
program.c_str());
return false;
}
T result = expression.value();
if (result != T(1))
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Result <> 1\n",
program.c_str());
failure = true;
}
if (result != T(1))
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Check: s0\n",
program.c_str());
failure = true;
}
if ("How now " != s0)
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Check: s0\n",
program.c_str());
failure = true;
}
if ("brown cow?" != s1)
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Check: s1\n",
program.c_str());
failure = true;
}
if ("HOWNOWBROWNCOW?" != s2)
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Check: s2\n",
program.c_str());
failure = true;
}
if ("HOWNOWbrown cow?" != s3)
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Check: s3\n",
program.c_str());
failure = true;
}
if ("How now BROWNCOW?" != s4)
{
printf("run_test18() - Error in evaluation! (4) Expression: %s Check: s4\n",
program.c_str());
failure = true;
}
if (failure)
return false;
}
return true; return true;
} }
@ -5139,9 +5308,9 @@ inline bool run_test19()
compositor compositor
.add( .add(
function_t("f6") function_t("f6")
.expression("17 * (f5(x,y,z,w,u) + f5(y,z,w,u,v) + f5(z,w,u,v,x) + f5(w,u,v,x,y))") .var("x").var("y").var("z")
.var("x").var("y").var("z") .var("w").var("u").var("v")
.var("w").var("u").var("v")); .expression("17 * (f5(x,y,z,w,u) + f5(y,z,w,u,v) + f5(z,w,u,v,x) + f5(w,u,v,x,y))"));
symbol_table_t& symbol_table = compositor.symbol_table(); symbol_table_t& symbol_table = compositor.symbol_table();
symbol_table.add_constants(); symbol_table.add_constants();
@ -5494,7 +5663,6 @@ inline bool run_test19()
" fibonacci5(x - 1) + fibonacci5(x - 2); ", " fibonacci5(x - 1) + fibonacci5(x - 2); ",
"x"); "x");
symbol_table_t& symbol_table = compositor.symbol_table(); symbol_table_t& symbol_table = compositor.symbol_table();
symbol_table.add_constants(); symbol_table.add_constants();
symbol_table.add_variable("x",x); symbol_table.add_variable("x",x);

View File

@ -39,7 +39,7 @@ arithmetic operations, functions and processes:
(07) Assignment: :=, +=, -=, *=, /=, %= (07) Assignment: :=, +=, -=, *=, /=, %=
(08) String (08) String
processing: in, like, ilike processing: in, like, ilike, concatenation
(09) Optimisations: constant-folding and simple strength reduction (09) Optimisations: constant-folding and simple strength reduction
@ -409,6 +409,15 @@ of C++ compilers:
| | 5. x := '0123456789'[2i + 1:7] | | | 5. x := '0123456789'[2i + 1:7] |
| | 6. x := (y := '0123456789'[2:7]) | | | 6. x := (y := '0123456789'[2:7]) |
+----------+---------------------------------------------------------+ +----------+---------------------------------------------------------+
| + | Concatenation of x and y. Where x and y are strings or |
| | string ranges. eg |
| | 1. x + y |
| | 2. x + 'abc' |
| | 3. x + y[:i + j] |
| | 4. x[i:j] + y[2:3] + '0123456789'[2:7] |
| | 5. 'abc' + x + y |
| | 6. 'abc' + '1234567' |
+----------+---------------------------------------------------------+
| [] | The string size operator returns the size of the string | | [] | The string size operator returns the size of the string |
| | being actioned. | | | being actioned. |
| | eg: | | | eg: |
@ -424,6 +433,8 @@ of C++ compilers:
| | eg: | | | eg: |
| | 1. if(x, y, z) | | | 1. if(x, y, z) |
| | 2. if((x + 1) > 2y, z + 1, w / v) | | | 2. if((x + 1) > 2y, z + 1, w / v) |
| | 3. if(x > y) z; |
| | 4. if(x <= 2*y) { z + w }; |
+----------+---------------------------------------------------------+ +----------+---------------------------------------------------------+
| if-else | The if-else/else-if statement. Subject to the condition | | if-else | The if-else/else-if statement. Subject to the condition |
| | branch the statement will return either the value of the| | | branch the statement will return either the value of the|
@ -635,11 +646,13 @@ current values assigned to the variables will be used.
(2) Expression (2) Expression
A structure that holds an AST for a specified expression and is used A structure that holds an abstract syntax tree or AST for a specified
to evaluate said expression. If a compiled Expression uses variables expression and is used to evaluate said expression. Evaluation of the
or user defined functions, it will then also have an associated Symbol expression is accomplished by performing a post-order traversal of the
Table, which will contain references to said variables, functions et AST. If a compiled Expression uses variables or user defined
al. An example AST structure for the denoted expression is as follows: functions, it will have an associated Symbol Table, which will contain
references to said variables, functions or string. An example AST
structure for the denoted expression is as follows:
Expression: z := (x + y^-2.345) * sin(pi / min(w - 7.3,v)) Expression: z := (x + y^-2.345) * sin(pi / min(w - 7.3,v))
@ -1040,7 +1053,8 @@ There are two types of function interface:
(1) ifunction (1) ifunction
(2) ivararg_function (2) ivararg_function
(3) igeneric_function (3) igeneric_function
(4) function_compositor (4) igeneric_function II
(5) function_compositor
(1) ifunction (1) ifunction
@ -1112,7 +1126,7 @@ called 'too':
too() too()
{} {}
inline T operator()(parameter_list_t& parameters) inline T operator()(parameter_list_t parameters)
{ {
for (std::size_t i = 0; i < parameters.size(); ++i) for (std::size_t i = 0; i < parameters.size(); ++i)
{ {
@ -1142,7 +1156,7 @@ are three type enumerations:
Each of the parameters can be accessed using its designated view. A Each of the parameters can be accessed using its designated view. A
typical loop for processing the parameters is as follows: typical loop for processing the parameters is as follows:
inline T operator()(parameter_list_t& parameters) inline T operator()(parameter_list_t parameters)
{ {
typedef typename exprtk::igeneric_function<T>::generic_type typedef typename exprtk::igeneric_function<T>::generic_type
generic_type; generic_type;
@ -1199,7 +1213,7 @@ achieved:
: exprtk::igeneric_function<T>("SVTT") : exprtk::igeneric_function<T>("SVTT")
{} {}
inline T operator()(parameter_list_t& parameters) inline T operator()(parameter_list_t parameters)
{ {
... ...
} }
@ -1230,7 +1244,7 @@ definition.
: exprtk::igeneric_function<T>("SVTTV*") : exprtk::igeneric_function<T>("SVTTV*")
{} {}
inline T operator()(parameter_list_t& parameters) inline T operator()(parameter_list_t parameters)
{ {
... ...
} }
@ -1247,7 +1261,64 @@ parameters in the following sequence:
(e) One or more vectors (e) One or more vectors
(4) function_compositor (4) igeneric_function II
This interface is identical to the igeneric_function, in that in can
consume an arbitrary number of parameters of varying type, but the
difference being that the function returns a string and as such is
treated as a string when invoked within expressions. As a result the
function call can alias a string and interact with other strings in
situations such as concatenation and equality operations.
The following example defines an generic function named 'toupper' with
the string return type function operator being explicitly overriden:
template <typename T>
struct toupper : public exprtk::igeneric_function<T>
{
typedef exprtk::igeneric_function<T> igenfunct_t
typedef typename igenfunct_t::generic_type generic_t;
typedef typename igenfunct_t::parameter_list_t parameter_list_t;
typedef typename generic_t::string_view string_t;
toupper()
: exprtk::igeneric_function<T>("S")
{}
inline T operator()(std::string& result,
parameter_list_t parameters)
{
result.clear();
for (std::size_t i = 0; i < string.size(); ++i)
{
result += std::toupper(string[i]);
}
return T(0);
}
};
In the example above the generic function 'toupper' expects only one
input parameter of type string, as noted by the parameter sequence
string passed during the constructor. When executed, the function will
return as a result a copy of the input string converted to uppercase
form.
When adding a string type returning generic function to a symbol
table, the 'add_function' is invoked with an extra parameter
(e_ft_strfunc) that denotes the function should be treated as a string
returning function type. The following example demonstrates how this
is done:
toupper<T> tu;
exprtk::symbol_table<T> symbol_table;
symbol_table.add_function("toupper",
tu,
symbol_table_t::e_ft_strfunc);
(5) function_compositor
The function compositor interface allows a user to define a function The function compositor interface allows a user to define a function
using ExprTk syntax. The functions are limited to returning a single using ExprTk syntax. The functions are limited to returning a single
scalar value and consuming up to six parameters as input. scalar value and consuming up to six parameters as input.
@ -1296,6 +1367,7 @@ The following demonstrates how all the pieces are put together:
foo<double> f; foo<double> f;
boo<double> b; boo<double> b;
too<double> t; too<double> t;
toupper<double> tu;
symbol_table_t symbol_table; symbol_table_t symbol_table;
compositor_t compositor(symbol_table); compositor_t compositor(symbol_table);
@ -1304,6 +1376,10 @@ The following demonstrates how all the pieces are put together:
symbol_table.add_function("boo",b); symbol_table.add_function("boo",b);
symbol_table.add_function("too",t); symbol_table.add_function("too",t);
symbol_table.add_function("toupper",
tu,
symbol_table_t::e_ft_strfunc);
compositor compositor
.add(function_t() .add(function_t()
.name("koo") .name("koo")
@ -1523,8 +1599,8 @@ into account when using Exprtk:
eg: 'Frankly my dear, 1 do n0t give a damn!' eg: 'Frankly my dear, 1 do n0t give a damn!'
(14) User defined normal functions can have up to 20 parameters, (14) User defined normal functions can have up to 20 parameters,
where as user defined vararg-functions can have an unlimited where as user defined generic-functions and vararg-functions
number of parameters. can have an unlimited number of parameters.
(15) The inbuilt polynomial functions can be at most of degree 12. (15) The inbuilt polynomial functions can be at most of degree 12.