| C++ Mathematical Expression Toolkit Library Documentation |
| |
| Section 00 - Introduction |
| Section 01 - Capabilities |
| Section 02 - Example Expressions |
| Section 03 - Copyright Notice |
| Section 04 - Downloads & Updates |
| Section 05 - Installation |
| Section 06 - Compilation |
| Section 07 - Compiler Compatibility |
| Section 08 - Built-In Operations & Functions |
| Section 09 - Fundamental Types |
| Section 10 - Components |
| Section 11 - Compilation Options |
| Section 12 - Expression Structures |
| Section 13 - Variable, Vector & String Definition |
| Section 14 - Vector Processing |
| Section 15 - User Defined Functions |
| Section 16 - Expression Dependents |
| Section 17 - Hierarchies Of Symbol Tables |
| Section 18 - Unknown Unknowns |
| Section 19 - Enabling & Disabling Features |
| Section 20 - Expression Return Values |
| Section 21 - Compilation Errors |
| Section 22 - Runtime Library Packages |
| Section 23 - Helpers & Utils |
| Section 24 - Benchmarking |
| Section 25 - Exprtk Notes |
| Section 26 - Simple Exprtk Example |
| Section 27 - Build Options |
| Section 28 - Files |
| Section 29 - Language Structure |
| |
| |
| [SECTION 00 - INTRODUCTION] |
| The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple |
| to use, easy to integrate and extremely efficient run-time |
| mathematical expression parsing and evaluation engine. The parsing |
| engine supports numerous forms of functional and logic processing |
| semantics and is easily extensible. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 01 - CAPABILITIES] |
| The ExprTk expression evaluator supports the following fundamental |
| arithmetic operations, functions and processes: |
| |
| (00) Types: Scalar, Vector, String |
| |
| (01) Basic operators: +, -, *, /, %, ^ |
| |
| (02) Assignment: :=, +=, -=, *=, /=, %= |
| |
| (03) Equalities & |
| Inequalities: =, ==, <>, !=, <, <=, >, >= |
| |
| (04) Logic operators: and, mand, mor, nand, nor, not, or, shl, shr, |
| xnor, xor, true, false |
| |
| (05) Functions: abs, avg, ceil, clamp, equal, erf, erfc, exp, |
| expm1, floor, frac, log, log10, log1p, log2, |
| logn, max, min, mul, ncdf, not_equal, root, |
| round, roundn, sgn, sqrt, sum, swap, trunc |
| |
| (06) Trigonometry: acos, acosh, asin, asinh, atan, atanh, atan2, |
| cos, cosh, cot, csc, sec, sin, sinc, sinh, |
| tan, tanh, hypot, rad2deg, deg2grad, deg2rad, |
| grad2deg |
| |
| (07) Control |
| structures: if-then-else, ternary conditional, switch-case, |
| return-statement |
| |
| (08) Loop statements: while, for, repeat-until, break, continue |
| |
| (09) String |
| processing: in, like, ilike, concatenation |
| |
| (10) Optimisations: constant-folding, simple strength reduction and |
| dead code elimination |
| |
| (11) Calculus: numerical integration and differentiation |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 02 - EXAMPLE EXPRESSIONS] |
| The following is a short listing of infix format based mathematical |
| expressions that can be parsed and evaluated using the ExprTk library. |
| |
| (01) sqrt(1 - (3 / x^2)) |
| (02) clamp(-1, sin(2 * pi * x) + cos(y / 2 * pi), +1) |
| (03) sin(2.34e-3 * x) |
| (04) if(((x[2] + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z) |
| (05) inrange(-2,m,+2) == if(({-2 <= m} and [m <= +2]),1,0) |
| (06) ({1/1}*[1/2]+(1/3))-{1/4}^[1/5]+(1/6)-({1/7}+[1/8]*(1/9)) |
| (07) a * exp(2.2 / 3.3 * t) + c |
| (08) z := x + sin(2.567 * pi / y) |
| (09) u := 2.123 * {pi * z} / (w := x + cos(y / pi)) |
| (10) 2x + 3y + 4z + 5w == 2 * x + 3 * y + 4 * z + 5 * w |
| (11) 3(x + y) / 2.9 + 1.234e+12 == 3 * (x + y) / 2.9 + 1.234e+12 |
| (12) (x + y)3.3 + 1 / 4.5 == [x + y] * 3.3 + 1 / 4.5 |
| (13) (x + y[i])z + 1.1 / 2.7 == (x + y[i]) * 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) (avg(x,y) <= x + y ? x - y : x * y) + 2.345 * pi / x |
| (17) while (x <= 100) { x -= 1; } |
| (18) x <= 'abc123' and (y in 'AString') or ('1x2y3z' != z) |
| (19) ((x + 'abc') like '*123*') or ('a123b' ilike y) |
| (20) sgn(+1.2^3.4z / -5.6y) <= {-7.8^9 / -10.11x } |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 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 MIT License. |
| |
| http://www.opensource.org/licenses/MIT |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 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: |
| |
| (a) Download: https://www.partow.net/programming/exprtk/index.html |
| (b) Repository: https://github.com/ArashPartow/exprtk |
| https://github.com/ArashPartow/exprtk-extras |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 05 - INSTALLATION] |
| The header file exprtk.hpp should be placed in a project or system |
| include path (e.g: /usr/include/). |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 06 - COMPILATION] |
| (a) For a complete build: make clean all |
| (b) For a PGO build: make clean pgo |
| (c) To strip executables: make strip_bin |
| (d) Execute valgrind check: make valgrind_check |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 07 - COMPILER COMPATIBILITY] |
| ExprTk has been built error and warning free using the following set |
| of C++ compilers: |
| |
| (*) GNU Compiler Collection (3.5+) |
| (*) Intel C++ Compiler (8.x+) |
| (*) Clang/LLVM (1.1+) |
| (*) PGI C++ (10.x+) |
| (*) Microsoft Visual Studio C++ Compiler (8.1+) |
| (*) IBM XL C/C++ (9.x+) |
| (*) C++ Builder (XE4+) |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 08 - BUILT-IN OPERATIONS & FUNCTIONS] |
| |
| (0) Arithmetic & Assignment Operators |
| +----------+---------------------------------------------------------+ |
| | OPERATOR | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | + | Addition between x and y. (eg: x + y) | |
| +----------+---------------------------------------------------------+ |
| | - | Subtraction between x and y. (eg: x - y) | |
| +----------+---------------------------------------------------------+ |
| | * | Multiplication between x and y. (eg: x * y) | |
| +----------+---------------------------------------------------------+ |
| | / | Division between x and y. (eg: x / y) | |
| +----------+---------------------------------------------------------+ |
| | % | Modulus of x with respect to y. (eg: x % y) | |
| +----------+---------------------------------------------------------+ |
| | ^ | x to the power of y. (eg: x ^ y) | |
| +----------+---------------------------------------------------------+ |
| | := | Assign the value of x to y. Where y is either a variable| |
| | | or vector type. (eg: y := x) | |
| +----------+---------------------------------------------------------+ |
| | += | Increment x by the value of the expression on the right | |
| | | hand side. Where x is either a variable or vector type. | |
| | | (eg: x += abs(y - z)) | |
| +----------+---------------------------------------------------------+ |
| | -= | Decrement x by the value of the expression on the right | |
| | | hand side. Where x is either a variable or vector type. | |
| | | (eg: x[i] -= abs(y + z)) | |
| +----------+---------------------------------------------------------+ |
| | *= | Assign the multiplication of x by the value of the | |
| | | expression on the righthand side to x. Where x is either| |
| | | a variable or vector type. | |
| | | (eg: x *= abs(y / z)) | |
| +----------+---------------------------------------------------------+ |
| | /= | Assign the division of x by the value of the expression | |
| | | on the right-hand side to x. Where x is either a | |
| | | variable or vector type. (eg: x[i + j] /= abs(y * z)) | |
| +----------+---------------------------------------------------------+ |
| | %= | Assign x modulo the value of the expression on the right| |
| | | hand side to x. Where x is either a variable or vector | |
| | | type. (eg: x[2] %= y ^ 2) | |
| +----------+---------------------------------------------------------+ |
| |
| (1) Equalities & Inequalities |
| +----------+---------------------------------------------------------+ |
| | OPERATOR | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | == or = | True only if x is strictly equal to y. (eg: x == y) | |
| +----------+---------------------------------------------------------+ |
| | <> or != | True only if x does not equal y. (eg: x <> y or x != y) | |
| +----------+---------------------------------------------------------+ |
| | < | True only if x is less than y. (eg: x < y) | |
| +----------+---------------------------------------------------------+ |
| | <= | True only if x is less than or equal to y. (eg: x <= y) | |
| +----------+---------------------------------------------------------+ |
| | > | True only if x is greater than y. (eg: x > y) | |
| +----------+---------------------------------------------------------+ |
| | >= | True only if x greater than or equal to y. (eg: x >= y) | |
| +----------+---------------------------------------------------------+ |
| |
| (2) Boolean Operations |
| +----------+---------------------------------------------------------+ |
| | OPERATOR | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | true | True state or any value other than zero (typically 1). | |
| +----------+---------------------------------------------------------+ |
| | false | False state, value of exactly zero. | |
| +----------+---------------------------------------------------------+ |
| | and | Logical AND, True only if x and y are both true. | |
| | | (eg: x and y) | |
| +----------+---------------------------------------------------------+ |
| | mand | Multi-input logical AND, True only if all inputs are | |
| | | true. Left to right short-circuiting of expressions. | |
| | | (eg: mand(x > y, z < w, u or v, w and x)) | |
| +----------+---------------------------------------------------------+ |
| | mor | Multi-input logical OR, True if at least one of the | |
| | | inputs are true. Left to right short-circuiting of | |
| | | expressions. (eg: mor(x > y, z < w, u or v, w and x)) | |
| +----------+---------------------------------------------------------+ |
| | nand | Logical NAND, True only if either x or y is false. | |
| | | (eg: x nand y) | |
| +----------+---------------------------------------------------------+ |
| | nor | Logical NOR, True only if the result of x or y is false | |
| | | (eg: x nor y) | |
| +----------+---------------------------------------------------------+ |
| | not | Logical NOT, Negate the logical sense of the input. | |
| | | (eg: not(x and y) == x nand y) | |
| +----------+---------------------------------------------------------+ |
| | or | Logical OR, True if either x or y is true. (eg: x or y) | |
| +----------+---------------------------------------------------------+ |
| | xor | Logical XOR, True only if the logical states of x and y | |
| | | differ. (eg: x xor y) | |
| +----------+---------------------------------------------------------+ |
| | xnor | Logical XNOR, True iff the biconditional of x and y is | |
| | | satisfied. (eg: x xnor y) | |
| +----------+---------------------------------------------------------+ |
| | & | Similar to AND but with left to right expression short | |
| | | circuiting optimisation. (eg: (x & y) == (y and x)) | |
| +----------+---------------------------------------------------------+ |
| | | | Similar to OR but with left to right expression short | |
| | | circuiting optimisation. (eg: (x | y) == (y or x)) | |
| +----------+---------------------------------------------------------+ |
| |
| (3) General Purpose Functions |
| +----------+---------------------------------------------------------+ |
| | FUNCTION | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | abs | Absolute value of x. (eg: abs(x)) | |
| +----------+---------------------------------------------------------+ |
| | avg | Average of all the inputs. | |
| | | (eg: avg(x,y,z,w,u,v) == (x + y + z + w + u + v) / 6) | |
| +----------+---------------------------------------------------------+ |
| | ceil | Smallest integer that is greater than or equal to x. | |
| +----------+---------------------------------------------------------+ |
| | 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 normalised epsilon | |
| +----------+---------------------------------------------------------+ |
| | erf | Error function of x. (eg: erf(x)) | |
| +----------+---------------------------------------------------------+ |
| | erfc | Complimentary error function of x. (eg: erfc(x)) | |
| +----------+---------------------------------------------------------+ |
| | exp | e to the power of x. (eg: exp(x)) | |
| +----------+---------------------------------------------------------+ |
| | expm1 | e to the power of x minus 1, where x is very small. | |
| | | (eg: expm1(x)) | |
| +----------+---------------------------------------------------------+ |
| | floor | Largest integer that is less than or equal to x. | |
| | | (eg: floor(x)) | |
| +----------+---------------------------------------------------------+ |
| | frac | Fractional portion of x. (eg: frac(x)) | |
| +----------+---------------------------------------------------------+ |
| | hypot | Hypotenuse of x and y (eg: hypot(x,y) = sqrt(x*x + y*y))| |
| +----------+---------------------------------------------------------+ |
| | iclamp | Inverse-clamp x outside of the range r0 and r1. Where | |
| | | r0 < r1. If x is within the range it will snap to the | |
| | | closest bound. (eg: iclamp(r0,x,r1) | |
| +----------+---------------------------------------------------------+ |
| | inrange | In-range returns 'true' when x is within the range r0 | |
| | | and r1. Where r0 < r1. (eg: inrange(r0,x,r1) | |
| +----------+---------------------------------------------------------+ |
| | log | Natural logarithm of x. (eg: log(x)) | |
| +----------+---------------------------------------------------------+ |
| | log10 | Base 10 logarithm of x. (eg: log10(x)) | |
| +----------+---------------------------------------------------------+ |
| | log1p | Natural logarithm of 1 + x, where x is very small. | |
| | | (eg: log1p(x)) | |
| +----------+---------------------------------------------------------+ |
| | log2 | Base 2 logarithm of x. (eg: log2(x)) | |
| +----------+---------------------------------------------------------+ |
| | logn | Base N logarithm of x. where n is a positive integer. | |
| | | (eg: logn(x,8)) | |
| +----------+---------------------------------------------------------+ |
| | max | Largest value of all the inputs. (eg: max(x,y,z,w,u,v)) | |
| +----------+---------------------------------------------------------+ |
| | min | Smallest value of all the inputs. (eg: min(x,y,z,w,u)) | |
| +----------+---------------------------------------------------------+ |
| | mul | Product of all the inputs. | |
| | | (eg: mul(x,y,z,w,u,v,t) == (x * y * z * w * u * v * t)) | |
| +----------+---------------------------------------------------------+ |
| | ncdf | Normal cumulative distribution function. (eg: ncdf(x)) | |
| +----------+---------------------------------------------------------+ |
| | not_equal| Not-equal test between x and y using normalised epsilon | |
| +----------+---------------------------------------------------------+ |
| | pow | x to the power of y. (eg: pow(x,y) == x ^ y) | |
| +----------+---------------------------------------------------------+ |
| | root | Nth-Root of x. where n is a positive integer. | |
| | | (eg: root(x,3) == x^(1/3)) | |
| +----------+---------------------------------------------------------+ |
| | round | Round x to the nearest integer. (eg: round(x)) | |
| +----------+---------------------------------------------------------+ |
| | roundn | Round x to n decimal places (eg: roundn(x,3)) | |
| | | where n > 0 and is an integer. | |
| | | (eg: roundn(1.2345678,4) == 1.2346) | |
| +----------+---------------------------------------------------------+ |
| | sgn | Sign of x, -1 where x < 0, +1 where x > 0, else zero. | |
| | | (eg: sgn(x)) | |
| +----------+---------------------------------------------------------+ |
| | sqrt | Square root of x, where x >= 0. (eg: sqrt(x)) | |
| +----------+---------------------------------------------------------+ |
| | sum | Sum of all the inputs. | |
| | | (eg: sum(x,y,z,w,u,v,t) == (x + y + z + w + u + v + t)) | |
| +----------+---------------------------------------------------------+ |
| | swap | Swap the values of the variables x and y and return the | |
| | <=> | current value of y. (eg: swap(x,y) or x <=> y) | |
| +----------+---------------------------------------------------------+ |
| | trunc | Integer portion of x. (eg: trunc(x)) | |
| +----------+---------------------------------------------------------+ |
| |
| (4) Trigonometry Functions |
| +----------+---------------------------------------------------------+ |
| | FUNCTION | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | acos | Arc cosine of x expressed in radians. Interval [-1,+1] | |
| | | (eg: acos(x)) | |
| +----------+---------------------------------------------------------+ |
| | acosh | Inverse hyperbolic cosine of x expressed in radians. | |
| | | (eg: acosh(x)) | |
| +----------+---------------------------------------------------------+ |
| | asin | Arc sine of x expressed in radians. Interval [-1,+1] | |
| | | (eg: asin(x)) | |
| +----------+---------------------------------------------------------+ |
| | asinh | Inverse hyperbolic sine of x expressed in radians. | |
| | | (eg: asinh(x)) | |
| +----------+---------------------------------------------------------+ |
| | atan | Arc tangent of x expressed in radians. Interval [-1,+1] | |
| | | (eg: atan(x)) | |
| +----------+---------------------------------------------------------+ |
| | atan2 | Arc tangent of (x / y) expressed in radians. [-pi,+pi] | |
| | | eg: atan2(x,y) | |
| +----------+---------------------------------------------------------+ |
| | atanh | Inverse hyperbolic tangent of x expressed in radians. | |
| | | (eg: atanh(x)) | |
| +----------+---------------------------------------------------------+ |
| | cos | Cosine of x. (eg: cos(x)) | |
| +----------+---------------------------------------------------------+ |
| | cosh | Hyperbolic cosine of x. (eg: cosh(x)) | |
| +----------+---------------------------------------------------------+ |
| | cot | Cotangent of x. (eg: cot(x)) | |
| +----------+---------------------------------------------------------+ |
| | csc | Cosecant of x. (eg: csc(x)) | |
| +----------+---------------------------------------------------------+ |
| | sec | Secant of x. (eg: sec(x)) | |
| +----------+---------------------------------------------------------+ |
| | sin | Sine of x. (eg: sin(x)) | |
| +----------+---------------------------------------------------------+ |
| | sinc | Sine cardinal of x. (eg: sinc(x)) | |
| +----------+---------------------------------------------------------+ |
| | sinh | Hyperbolic sine of x. (eg: sinh(x)) | |
| +----------+---------------------------------------------------------+ |
| | tan | Tangent of x. (eg: tan(x)) | |
| +----------+---------------------------------------------------------+ |
| | tanh | Hyperbolic tangent of x. (eg: tanh(x)) | |
| +----------+---------------------------------------------------------+ |
| | deg2rad | Convert x from degrees to radians. (eg: deg2rad(x)) | |
| +----------+---------------------------------------------------------+ |
| | deg2grad | Convert x from degrees to gradians. (eg: deg2grad(x)) | |
| +----------+---------------------------------------------------------+ |
| | rad2deg | Convert x from radians to degrees. (eg: rad2deg(x)) | |
| +----------+---------------------------------------------------------+ |
| | grad2deg | Convert x from gradians to degrees. (eg: grad2deg(x)) | |
| +----------+---------------------------------------------------------+ |
| |
| (5) String Processing |
| +----------+---------------------------------------------------------+ |
| | FUNCTION | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | = , == | All common equality/inequality operators are applicable | |
| | !=, <> | to strings and are applied in a case sensitive manner. | |
| | <=, >= | In the following example x, y and z are of type string. | |
| | < , > | (eg: not((x <= 'AbC') and ('1x2y3z' <> y)) or (z == x) | |
| +----------+---------------------------------------------------------+ |
| | in | True only if x is a substring of y. | |
| | | (eg: x in y or 'abc' in 'abcdefgh') | |
| +----------+---------------------------------------------------------+ |
| | like | True only if the string x matches the pattern y. | |
| | | Available wildcard characters are '*' and '?' denoting | |
| | | zero or more and zero or one matches respectively. | |
| | | (eg: x like y or 'abcdefgh' like 'a?d*h') | |
| +----------+---------------------------------------------------------+ |
| | ilike | True only if the string x matches the pattern y in a | |
| | | case insensitive manner. Available wildcard characters | |
| | | are '*' and '?' denoting zero or more and zero or one | |
| | | matches respectively. | |
| | | (eg: x ilike y or 'a1B2c3D4e5F6g7H' ilike 'a?d*h') | |
| +----------+---------------------------------------------------------+ |
| | [r0:r1] | The closed interval [r0,r1] of the specified string. | |
| | | eg: Given a string x with a value of 'abcdefgh' then: | |
| | | 1. x[1:4] == 'bcde' | |
| | | 2. x[ :5] == x[:10 / 2] == 'abcdef' | |
| | | 3. x[2 + 1: ] == x[3:] =='defgh' | |
| | | 4. x[ : ] == x[:] == 'abcdefgh' | |
| | | 5. x[4/2:3+2] == x[2:5] == 'cdef' | |
| | | | |
| | | Note: Both r0 and r1 are assumed to be integers, where | |
| | | r0 <= r1. They may also be the result of an expression, | |
| | | in the event they have fractional components truncation | |
| | | will be performed. (eg: 1.67 --> 1) | |
| +----------+---------------------------------------------------------+ |
| | := | Assign the value of x to y. Where y is a mutable string | |
| | | or string range and x is either a string or a string | |
| | | range. eg: | |
| | | 1. y := x | |
| | | 2. y := 'abc' | |
| | | 3. y := x[:i + j] | |
| | | 4. y := '0123456789'[2:7] | |
| | | 5. y := '0123456789'[2i + 1:7] | |
| | | 6. y := (x := '0123456789'[2:7]) | |
| | | 7. y[i:j] := x | |
| | | 8. y[i:j] := (x + 'abcdefg'[8 / 4:5])[m:n] | |
| | | | |
| | | Note: For options 7 and 8 the shorter of the two ranges | |
| | | will denote the number characters that are to be copied.| |
| +----------+---------------------------------------------------------+ |
| | + | 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' | |
| | | 7. (x + 'a1B2c3D4' + y)[i:2j] | |
| +----------+---------------------------------------------------------+ |
| | += | Append to x the value of y. Where x is a mutable string | |
| | | and y is either a string or a string range. eg: | |
| | | 1. x += y | |
| | | 2. x += 'abc' | |
| | | 3. x += y[:i + j] + 'abc' | |
| | | 4. x += '0123456789'[2:7] | |
| +----------+---------------------------------------------------------+ |
| | <=> | Swap the values of x and y. Where x and y are mutable | |
| | | strings. (eg: x <=> y) | |
| +----------+---------------------------------------------------------+ |
| | [] | The string size operator returns the size of the string | |
| | | being actioned. | |
| | | eg: | |
| | | 1. 'abc'[] == 3 | |
| | | 2. var max_str_length := max(s0[],s1[],s2[],s3[]) | |
| | | 3. ('abc' + 'xyz')[] == 6 | |
| | | 4. (('abc' + 'xyz')[1:4])[] == 4 | |
| +----------+---------------------------------------------------------+ |
| |
| (6) Control Structures |
| +----------+---------------------------------------------------------+ |
| |STRUCTURE | DEFINITION | |
| +----------+---------------------------------------------------------+ |
| | if | If x is true then return y else return z. | |
| | | eg: | |
| | | 1. if (x, y, z) | |
| | | 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 | |
| | | branch the statement will return either the value of the| |
| | | consequent or the alternative branch. | |
| | | 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; | |
| | | 4. if ((x != y) and (z > w)) | |
| | | { | |
| | | y := sin(x) / u; | |
| | | z := w + 1; | |
| | | } | |
| | | else if (x > (z + 1)) | |
| | | { | |
| | | w := abs (x - y) + z; | |
| | | u := (x + 1) > 2y ? 2u : 3u; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | switch | The first true case condition that is encountered will | |
| | | determine the result of the switch. If none of the case | |
| | | conditions hold true, the default action is assumed as | |
| | | the final return value. This is sometimes also known as | |
| | | a multi-way branch mechanism. | |
| | | eg: | |
| | | switch | |
| | | { | |
| | | case x > (y + z) : 2 * x / abs(y - z); | |
| | | case x < 3 : sin(x + y); | |
| | | default : 1 + x; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | while | The structure will repeatedly evaluate the internal | |
| | | statement(s) 'while' the condition is true. The final | |
| | | statement in the final iteration will be used as the | |
| | | return value of the loop. | |
| | | eg: | |
| | | while ((x -= 1) > 0) | |
| | | { | |
| | | y := x + z; | |
| | | w := u + y; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | repeat/ | The structure will repeatedly evaluate the internal | |
| | until | statement(s) 'until' the condition is true. The final | |
| | | statement in the final iteration will be used as the | |
| | | return value of the loop. | |
| | | eg: | |
| | | repeat | |
| | | y := x + z; | |
| | | w := u + y; | |
| | | until ((x += 1) > 100) | |
| +----------+---------------------------------------------------------+ |
| | for | The structure will repeatedly evaluate the internal | |
| | | statement(s) while the condition is true. On each loop | |
| | | iteration, an 'incrementing' expression is evaluated. | |
| | | The conditional is mandatory whereas the initialiser | |
| | | and incrementing expressions are optional. | |
| | | eg: | |
| | | for (var x := 0; (x < n) and (x != y); x += 1) | |
| | | { | |
| | | y := y + x / 2 - z; | |
| | | w := u + y; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | break | Break terminates the execution of the nearest enclosed | |
| | break[] | loop, allowing for the execution to continue on external| |
| | | to the loop. The default break statement will set the | |
| | | return value of the loop to NaN, where as the return | |
| | | based form will set the value to that of the break | |
| | | expression. | |
| | | eg: | |
| | | while ((i += 1) < 10) | |
| | | { | |
| | | if (i < 5) | |
| | | j -= i + 2; | |
| | | else if (i % 2 == 0) | |
| | | break; | |
| | | else | |
| | | break[2i + 3]; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | continue | Continue results in the remaining portion of the nearest| |
| | | enclosing loop body to be skipped. | |
| | | eg: | |
| | | for (var i := 0; i < 10; i += 1) | |
| | | { | |
| | | if (i < 5) | |
| | | continue; | |
| | | j -= i + 2; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | return | Return immediately from within the current expression. | |
| | | With the option of passing back a variable number of | |
| | | values (scalar, vector or string). eg: | |
| | | 1. return [1]; | |
| | | 2. return [x, 'abx']; | |
| | | 3. return [x, x + y,'abx']; | |
| | | 4. return []; | |
| | | 5. if (x < y) | |
| | | return [x, x - y, 'result-set1', 123.456]; | |
| | | else | |
| | | return [y, x + y, 'result-set2']; | |
| +----------+---------------------------------------------------------+ |
| | ?: | Ternary conditional statement, similar to that of the | |
| | | above denoted if-statement. | |
| | | eg: | |
| | | 1. x ? y : z | |
| | | 2. x + 1 > 2y ? z + 1 : (w / v) | |
| | | 3. 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. | |
| | | eg: | |
| | | ~(i := x + 1, j := y / z, k := sin(w/u)) == (sin(w/u))) | |
| | | ~{i := x + 1; j := y / z; k := sin(w/u)} == (sin(w/u))) | |
| +----------+---------------------------------------------------------+ |
| | [*] | Evaluate any consequent for which its case statement is | |
| | | true. The return value will be either zero or the result| |
| | | of the last consequent to have been evaluated. | |
| | | eg: | |
| | | [*] | |
| | | { | |
| | | case (x + 1) > (y - 2) : x := z / 2 + sin(y / pi); | |
| | | case (x + 2) < abs(y + 3) : w / 4 + min(5y,9); | |
| | | case (x + 3) == (y * 4) : y := abs(z / 6) + 7y; | |
| | | } | |
| +----------+---------------------------------------------------------+ |
| | [] | The vector size operator returns the size of the vector | |
| | | being actioned. | |
| | | eg: | |
| | | 1. v[] | |
| | | 2. max_size := max(v0[],v1[],v2[],v3[]) | |
| +----------+---------------------------------------------------------+ |
| |
| Note: In the tables above, the symbols x, y, z, w, u and v where |
| appropriate may represent any of one the following: |
| |
| 1. Literal numeric/string value |
| 2. A variable |
| 3. A vector element |
| 4. A vector |
| 5. A string |
| 6. An expression comprised of [1], [2] or [3] (eg: 2 + x / vec[3]) |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 09 - FUNDAMENTAL TYPES] |
| ExprTk supports three fundamental types which can be used freely in |
| expressions. The types are as follows: |
| |
| (1) Scalar |
| (2) Vector |
| (3) String |
| |
| |
| (1) Scalar Type |
| The scalar type is a singular numeric value. The underlying type is |
| that used to specialise the ExprTk components (float, double, long |
| double, MPFR et al). |
| |
| |
| (2) Vector Type |
| The vector type is a fixed size sequence of contiguous scalar values. |
| A vector can be indexed resulting in a scalar value. Operations |
| between a vector and scalar will result in a vector with a size equal |
| to that of the original vector, whereas operations between vectors |
| will result in a vector of size equal to that of the smaller of the |
| two. In both mentioned cases, the operations will occur element-wise. |
| |
| |
| (3) String Type |
| The string type is a variable length sequence of 8-bit chars. Strings |
| can be assigned and concatenated to one another, they can also be |
| manipulated via sub-ranges using the range definition syntax. Strings |
| however can not interact with scalar or vector types. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 10 - COMPONENTS] |
| 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: |
| |
| (1) Symbol Table exprtk::symbol_table<NumericType> |
| (2) Expression exprtk::expression<NumericType> |
| (3) Parser exprtk::parser<NumericType> |
| |
| |
| (1) Symbol Table |
| A structure that is used to store references to variables, constants |
| and functions that are to be used within expressions. Furthermore in |
| the context of composited recursive functions the symbol table can |
| also be thought of as a simple representation of a stack specific for |
| the expression(s) that reference it. The following is a list of the |
| types a symbol table can handle: |
| |
| (a) Numeric variables |
| (b) Numeric constants |
| (c) Numeric vector elements |
| (d) String variables |
| (e) String constants |
| (f) Functions |
| (g) 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. |
| |
| Note: Any variable reference provided to a given symbol_table |
| instance, must have a life time at least as long as the life-time of |
| the symbol_table instance. In the event the variable reference is |
| invalidated before the symbol_table or any dependent expression |
| instances have been destructed, then any associated expression |
| evaluations or variable referencing via the symbol_table instance will |
| result in undefined behaviour. |
| |
| The following bit of code instantiates a symbol_table and expression |
| instance, then proceeds to demonstrate various ways in which |
| references to variables can be added to the symbol_table, and how |
| those references are subsequently invalidated resulting in various |
| forms of undefined behaviour. |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| |
| symbol_table_t symbol_table; |
| expression_t expression; |
| |
| { |
| double x = 123.4567; |
| symbol_table.add_variable("x", x); |
| } // Reference to variable x has been invalidated |
| |
| std::deque<double> y {1.1, 2.2, 3.3}; |
| |
| symbol_table.add_variable("y", y.back()); |
| |
| y.pop_back(); // Reference to variable y has been invalidated |
| |
| std::vector<double> z {4.4, 5.5, 6.6}; |
| |
| symbol_table.add_variable("z", z.front()); |
| |
| z.erase(z.begin()); |
| // Reference to variable z has been invalidated |
| |
| double* w = new double(123.456); |
| |
| symbol_table.add_variable("w", *w); |
| |
| delete w; // Reference to variable w has been invalidated |
| |
| const std::string expression_str = "x + y / z * w"; |
| |
| // Compilation of expression will succeed |
| parser.compile(expression_str,expression); |
| |
| expression.value(); |
| // Evaluation will result in undefined behaviour |
| |
| symbol_table.get_variable("x")->ref() = 135.791; |
| // Assignment will result in undefined behaviour |
| |
| |
| 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<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| typedef exprtk::parser<double> 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; |
| expression.value(); // 1 * 2 + 3 |
| |
| x = 3.7; |
| expression.value(); // 3.7 * 2 + 3 |
| |
| y = -9.0; |
| expression.value(); // 3.7 * -9 + 3 |
| |
| // 'x * -9 + 3' for x in range of [0,100) in steps of 0.0001 |
| for (x = 0.0; x < 100.0; x += 0.0001) |
| { |
| expression.value(); // x * -9 + 3 |
| } |
| |
| |
| Note: It is possible to register multiple symbol_tables with a single |
| expression object. In the event an expression has multiple symbol |
| tables, and where there exists conflicts between symbols, the |
| compilation stage will resolve the conflicts based on the order of |
| registration of the symbol_tables to the expression. For a more |
| expansive discussion please review section [17 - Hierarchies Of |
| Symbol Tables] |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| typedef exprtk::parser<double> parser_t; |
| |
| symbol_table_t symbol_table0; |
| symbol_table_t symbol_table1; |
| |
| expression_t expression; |
| parser_t parser; |
| |
| double x0 = 123.0; |
| double x1 = 678.0; |
| |
| std::string expression_string = "x + 1"; |
| |
| symbol_table0.add_variable("x",x0); |
| symbol_table1.add_variable("x",x1); |
| |
| expression.register_symbol_table(symbol_table0); |
| expression.register_symbol_table(symbol_table1); |
| |
| parser.compile(expression_string,expression); |
| |
| expression.value(); // 123 + 1 |
| |
| |
| The symbol table supports adding references to external instances of |
| types that can be accessed within expressions via the following |
| methods: |
| |
| 1. bool add_variable (const std::string& name, scalar_t&) |
| 2. bool add_constant (const std::string& name, const scalar_t&) |
| 3. bool add_stringvar(const std::string& name, std::string&) |
| 4. bool add_vector (const std::string& name, vector_type&) |
| |
| |
| 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<scalar_t> |
| 2. scalar_t(&v)[N] |
| 3. scalar_t* and array size |
| 4. exprtk::vector_view<scalar_t> |
| |
| |
| 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 |
| expression is accomplished by performing a post-order traversal of the |
| AST. If a compiled Expression uses variables or user defined |
| functions, it will have an associated Symbol Table, which will contain |
| references to said variables, functions or strings. An example AST |
| structure for the denoted expression is as follows: |
| |
| Expression: z := (x + y^-2.345) * sin(pi / min(w - 7.3,v)) |
| |
| [Root] |
| | |
| [Assignment] |
| ________/ \_____ |
| / \ |
| Variable(z) [Multiplication] |
| ____________/ \___________ |
| / \ |
| / [Unary-Function(sin)] |
| [Addition] | |
| ____/ \____ [Division] |
| / \ ___/ \___ |
| Variable(x) [Exponentiation] / \ |
| ______/ \______ Constant(pi) [Binary-Function(min)] |
| / \ ____/ \____ |
| Variable(y) [Negation] / \ |
| | / Variable(v) |
| Constant(2.345) / |
| / |
| [Subtraction] |
| ____/ \____ |
| / \ |
| Variable(w) Constant(7.3) |
| |
| |
| The above denoted AST will be evaluated in the following order: |
| |
| (01) Load Variable (z) (10) Load Constant (7.3) |
| (02) Load Variable (x) (11) Subtraction (09 & 10) |
| (03) Load Variable (y) (12) Load Variable (v) |
| (04) Load Constant (2.345) (13) Min (11 & 12) |
| (05) Negation (04) (14) Division (08 & 13) |
| (06) Exponentiation (03 & 05) (15) Sin (14) |
| (07) Addition (02 & 06) (16) Multiplication (07 & 15) |
| (08) Load Constant (pi) (17) Assignment (01 & 16) |
| (09) Load Variable (w) |
| |
| |
| Generally an expression in ExprTk can be thought of as a free function |
| similar to those found in imperative languages. This form of pseudo |
| function will have a name, it may have a set of one or more inputs and |
| will return at least one value as its result. Furthermore the function |
| when invoked, may cause a side-effect that changes the state of the |
| host program. |
| |
| As an example the following is a pseudo-code definition of a free |
| function that performs a computation taking four inputs, modifying one |
| of them and returning a value based on some arbitrary calculation: |
| |
| ResultType foo(InputType x, InputType y, InputType z, InputType w) |
| { |
| w = 2 * x^y + z; // Side-Effect |
| return abs(x - y) / z; // Return Result |
| } |
| |
| |
| Given the above definition the following is a functionally equivalent |
| version using ExprTk: |
| |
| const std::string foo_str = " w := 2 * x^y + z; " |
| " abs(x - y) / z; "; |
| |
| T x, y, z, w; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| symbol_table.add_variable("y",y); |
| symbol_table.add_variable("z",z); |
| symbol_table.add_variable("w",w); |
| |
| expression_t foo; |
| foo.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| if (!parser.compile(foo_str,foo)) |
| { |
| // Error in expression... |
| return; |
| } |
| |
| T result = foo.value(); |
| |
| |
| (3) Parser |
| A component 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. |
| |
| |
| Note: The exprtk::expression and exprtk::symbol_table components are |
| reference counted entities. Copy constructing or assigning to or from |
| either component will result in a shallow copy and a reference count |
| increment, rather than a complete replication. Furthermore the |
| expression and symbol_table components being Default-Constructible, |
| Copy-Constructible and Copy-Assignable make them compatible with |
| various C++ standard library containers and adaptors such as |
| std::vector, std::map, std::stack etc. |
| |
| The following is an example of two unique expressions, after having |
| being instantiated and compiled, one expression is assigned to the |
| other. The diagrams depict their initial and post assignment states, |
| 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 |
| |
| +-----[ e0 cntrl block]----+ +-----[ e1 cntrl block]-----+ |
| | 1. Expression Node 'x+1' | | 1. Expression Node '2z+y' | |
| | 2. Ref Count: 1 |<-+ | 2. Ref Count: 1 |<-+ |
| +--------------------------+ | +---------------------------+ | |
| | | |
| +--[ e0 expression]--+ | +--[ e1 expression]--+ | |
| | 1. Reference to ]------+ | 1. Reference to ]-------+ |
| | e0 Control Block | | e1 Control Block | |
| +--------------------+ +--------------------+ |
| |
| |
| e0 = e1; // e0 and e1 are now 2z+y |
| |
| +-----[ e1 cntrl block]-----+ |
| | 1. Expression Node '2z+y' | |
| +----------->| 2. Ref Count: 2 |<----------+ |
| | +---------------------------+ | |
| | | |
| | +--[ e0 expression]--+ +--[ e1 expression]--+ | |
| +---[ 1. Reference to | | 1. Reference to ]---+ |
| | e1 Control Block | | e1 Control Block | |
| +--------------------+ +--------------------+ |
| |
| The reason for the above complexity and restrictions of deep copies |
| for the expression and symbol_table components is because expressions |
| may include user defined variables or functions. These are embedded as |
| references into the expression's AST. When copying an expression, said |
| references need to also be copied. If the references are blindly |
| copied, it will then result in two or more identical expressions |
| utilizing the exact same references for variables. This obviously is |
| not the default assumed scenario and will give rise to non-obvious |
| behaviours when using the expressions in various contexts such as |
| multi-threading et al. |
| |
| The prescribed method for cloning an expression is to compile it from |
| its string form. Doing so will allow the 'user' to properly consider |
| the exact source of user defined variables and functions. |
| |
| Note: The exprtk::parser is a non-copyable and non-thread safe |
| component, and should only be shared via either a reference, a shared |
| pointer or a std::ref mechanism, and considerations relating to |
| synchronisation taken into account where appropriate. The parser |
| represents an object factory, specifically a factory of expressions, |
| and generally should not be instantiated solely on a per expression |
| compilation basis. |
| |
| The following diagram and example depicts the flow of data and |
| operations for compiling multiple expressions via the parser and |
| inserting the newly minted exprtk::expression instances into a |
| std::vector. |
| |
| +----[exprtk::parser]---+ |
| | Expression Factory | |
| | parser_t::compile(...)| |
| +--> ~.~.~.~.~.~.~.~.~.~ ->--+ |
| | +-----------------------+ | |
| Expressions in | | Expressions as |
| string form A V exprtk::expression |
| | | instances |
| [s0:'x+1']--->--+ | | +-[e0: x+1] |
| | | | | |
| [s1:'2z+y']-->--+--+ +->+-[e1: 2z+y] |
| | | |
| [s2:'sin(k+w)']-+ +-[e2: sin(k+w)] |
| |
| |
| const std::string expression_str[3] |
| = { "x + 1", "2x + y", "sin(k + w)" }; |
| |
| std::vector<expression_t> expression_list; |
| |
| parser_t parser; |
| expression_t expression; |
| symbol_table_t symbol_table; |
| |
| expression.register_symbol_table(symbol_table); |
| |
| for (std::size_t i = 0; i < 3; ++i) |
| { |
| if (parser.compile(expression_str[i],expression)) |
| { |
| expression_list.push_back(expression); |
| } |
| else |
| std::cout << "Error in " << expression_str[i] << "\n"; |
| } |
| |
| for (auto& e : expression_list) |
| { |
| e.value(); |
| } |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 11 - COMPILATION OPTIONS] |
| The exprtk::parser when being instantiated takes as input a set of |
| 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>::settings_t settings_t; |
| |
| std::size_t compile_options = settings_t::e_joiner + |
| settings_t::e_commutative_check + |
| settings_t::e_strength_reduction; |
| |
| parser_t parser(compile_options); |
| |
| |
| 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 |
| token of value one. |
| |
| (a) (x < y) == true ---> (x < y) == 1 |
| (b) false == (x > y) ---> 0 == (x > y) |
| |
| |
| (2) Joiner (e_joiner) |
| Enable joining of multi-character operators that may have been |
| incorrectly disjoint in the string representation of the specified |
| expression. For example the consecutive tokens of ">" "=" will become |
| ">=" representing the "greater than or equal to" operator. If not |
| properly resolved the original form will cause a compilation error. |
| The following is a listing of the scenarios that the joiner can |
| handle: |
| |
| (a) '>' '=' ---> '>=' (gte) |
| (b) '<' '=' ---> '<=' (lte) |
| (c) '=' '=' ---> '==' (equal) |
| (d) '!' '=' ---> '!=' (not-equal) |
| (e) '<' '>' ---> '<>' (not-equal) |
| (f) ':' '=' ---> ':=' (assignment) |
| (g) '+' '=' ---> '+=' (addition assignment) |
| (h) '-' '=' ---> '-=' (subtraction assignment) |
| (i) '*' '=' ---> '*=' (multiplication assignment) |
| (j) '/' '=' ---> '/=' (division assignment) |
| (k) '%' '=' ---> '%=' (modulo assignment) |
| (l) '+' '-' ---> '-' (subtraction) |
| (m) '-' '+' ---> '-' (subtraction) |
| (n) '-' '-' ---> '+' (addition) |
| (o) '<=' '>' ---> '<=>' (swap) |
| |
| |
| An example of the transformation that takes place is as follows: |
| |
| (a) (x > = y) and (z ! = w) ---> (x >= y) and (z != w) |
| |
| |
| (3) Numeric Check (e_numeric_check) |
| Enable validation of tokens representing numeric types so as to catch |
| any errors prior to the costly process of the main compilation step |
| commencing. |
| |
| |
| (4) Bracket Check (e_bracket_check) |
| Enable the check for validating the ordering of brackets in the |
| specified expression. |
| |
| |
| (5) Sequence Check (e_sequence_check) |
| Enable the check for validating that sequences of either pairs or |
| triplets of tokens make sense. For example the following sequence of |
| tokens when encountered will raise an error: |
| |
| (a) (x + * 3) ---> sequence error |
| |
| |
| (6) Commutative Check (e_commutative_check) |
| Enable the check that will transform sequences of pairs of tokens that |
| imply a multiplication operation. The following are some examples of |
| such transformations: |
| |
| (a) 2x ---> 2 * x |
| (b) 25x^3 ---> 25 * x^3 |
| (c) 3(x + 1) ---> 3 * (x + 1) |
| (d) (x + 1)4 ---> (x + 1) * 4 |
| (e) 5foo(x,y) ---> 5 * foo(x,y) |
| (f) foo(x,y)6 + 1 ---> foo(x,y) * 6 + 1 |
| (g) (4((2x)3)) ---> 4 * ((2 * x) * 3) |
| (h) w(x) + (y)z ---> w * x + y * z |
| |
| |
| (7) Strength Reduction Check (e_strength_reduction) |
| Enable the use of strength reduction optimisations during the |
| compilation process. In ExprTk strength reduction optimisations |
| predominantly involve transforming sub-expressions into other forms |
| that are algebraically equivalent yet less costly to compute. The |
| following are examples of the various transformations that can occur: |
| |
| (a) (x / y) / z ---> x / (y * z) |
| (b) (x / y) / (z / w) ---> (x * w) / (y * z) |
| (c) (2 * x) - (2 * y) ---> 2 * (x - y) |
| (d) (2 / x) / (3 / y) ---> (2 / 3) / (x * y) |
| (e) (2 * x) * (3 * y) ---> (2 * 3) * (x * y) |
| |
| |
| Note: |
| When using strength reduction in conjunction with expressions whose |
| inputs or sub-expressions may result in values nearing either of the |
| bounds of the underlying numeric type (eg: double), there may be the |
| possibility of a decrease in the precision of results. |
| |
| In the following example the given expression which represents an |
| attempt at computing the average between x and y will be transformed |
| as follows: |
| |
| (0.5 * x) + (y * 0.5) ---> 0.5 * (x + y) |
| |
| There may be situations where the above transformation will cause |
| numerical overflows and that the original form of the expression is |
| desired over the strength reduced form. In these situations it is best |
| to turn off strength reduction optimisations or to use a type with a |
| larger numerical bound. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 12 - EXPRESSION STRUCTURES] |
| Exprtk supports mathematical expressions in numerous forms based on a |
| simple imperative programming model. This section will cover the |
| following topics related to general structure and programming of |
| expression using ExprTk: |
| |
| (1) Multi-Statement Expressions |
| (2) Statements And Side-Effects |
| (3) Conditional Statements |
| (4) Special Functions |
| |
| |
| (1) Multi-Statement Expressions |
| Expressions in ExprTk can be comprised of one more statements, which |
| may sometimes be called sub-expressions. The following are two |
| examples of expressions stored in std::string variables, the first a |
| single statement and the second a multi-statement expression: |
| |
| std::string single_statement = " z := x + y "; |
| |
| std::string multi_statement = " var temp := x; " |
| " x := y + z; " |
| " y := temp; "; |
| |
| |
| In a multi-statement expression, the final statement will determine |
| the overall result of the expression. In the following multi-statement |
| expression, the result of the expression when evaluated will be '2.3', |
| which will also be the value stored in the 'y' variable. |
| |
| z := x + y; |
| y := 2.3; |
| |
| |
| As demonstrated in the expression above, statements within an |
| expression are separated using the semi-colon ';' operator. In the |
| event two statements are not separated by a semi-colon, and the |
| implied multiplication feature is active (enabled by default), the |
| compiler will assume a multiplication operation between the two |
| statements. |
| |
| In the following example we have a multi-statement expression composed |
| of two variable definitions and initialisations for variables x and y |
| and two seemingly separate mathematical operations. |
| |
| var x:= 2; |
| var y:= 3; |
| x + 1 |
| y * 2 |
| |
| |
| However the result of the expression will not be 6 as may have been |
| assumed based on the calculation of 'y * 2', but rather the result |
| will be 8. This is because the compiler will have conjoined the two |
| mathematical statements into one via a multiplication operation. The |
| expression when compiled will actually evaluate as the following: |
| |
| var x:= 2; |
| var y:= 3; |
| x + 1 * y * 2; // 2 + 1 * 3 * 2 == 8 |
| |
| |
| In ExprTk any valid statement will itself return a value. This value |
| can further be used in conjunction with other statements. This |
| includes language structures such as if-statements, loops (for, while) |
| and the switch statement. Typically the last statement executed in the |
| given construct (conditional, loop etc), will be the value that is |
| returned. |
| |
| In the following example, the return value of the expression will be |
| 11, which is the sum of the variable 'x' and the final value computed |
| within the loop body on its last iteration: |
| |
| var x := 1; |
| x + for (var i := x; i < 10; i += 1) |
| { |
| i / 2; |
| i + 1; |
| } |
| |
| |
| (2) Statements And Side-Effects |
| Statements themselves may have side effects, which in-turn effect the |
| proceeding statements in multi-statement expressions. |
| |
| A statement is said to have a side-effect if it causes the state of |
| the expression to change in some way - this includes but is not |
| limited to the modification of the state of external variables used |
| within the expression. Currently the following actions being present |
| in a statement will cause it to have a side-effect: |
| |
| (a) Assignment operation (explicit or potentially) |
| (b) Invoking a user-defined function that has side-effects |
| |
| The following are examples of expressions where the side-effect status |
| of the statements (sub-expressions) within the expressions have been |
| noted: |
| |
| +-+----------------------+------------------------------+ |
| |#| Expression | Side Effect Status | |
| +-+----------------------+------------------------------+ |
| |0| x + y | False | |
| +-+----------------------+------------------------------+ |
| |1| z := x + y | True - Due to assignment | |
| +-+----------------------+------------------------------+ |
| |2| abs(x - y) | False | |
| +-+----------------------+------------------------------+ |
| |3| abs(x - y); | False | |
| | | z := (x += y); | True - Due to assignments | |
| +-+----------------------+------------------------------+ |
| |4| abs(x - y); | False | |
| | | z := (x += y); | True - Due to assignments | |
| +-+----------------------+------------------------------+ |
| |5| var t := abs(x - y); | True - Due to initialisation | |
| | | t + x; | False | |
| | | z := (x += y); | True - Due to assignments | |
| +-+----------------------+------------------------------+ |
| |6| foo(x - y) | True - user defined function | |
| +-+----------------------+------------------------------+ |
| |
| |
| Note: In example 6 from the above set, it is assumed the user defined |
| function foo has been registered as having a side-effect. By default |
| all user defined functions are assumed to have side-effects, unless |
| they are configured in their constructors to not have side-effects |
| using the 'disable_has_side_effects' free function. For more |
| information review Section 15 - User Defined Functions sub-section 7 |
| Function Side-Effects. |
| |
| At this point we can see that there will be expressions composed of |
| certain kinds of statements that when executed will not effect the |
| nature of the expression's result. These statements are typically |
| called 'dead code'. These statements though not effecting the final |
| result will still be executed and as such they will consume processing |
| time that could otherwise be saved. As such ExprTk attempts to detect |
| and remove such statements from expressions. |
| |
| The 'Dead Code Elimination' (DCE) optimisation process, which is |
| enabled by default, will remove any statements that are determined to |
| not have a side effect in a multi-statement expression, excluding the |
| final or last statement. |
| |
| By default the final statement in an expression will always be present |
| regardless of its side-effect status, as it is the statement whose |
| value will be used as the result of the expression. |
| |
| In order to further explain the actions taken during the DCE process, |
| lets review the following expression: |
| |
| var x := 2; // Statement 1 |
| var y := x + 2; // Statement 2 |
| x + y; // Statement 3 |
| y := x + 3y; // Statement 4 |
| x - y; // Statement 5 |
| |
| |
| The above expression has five statements. Three of them (1, 2 and 4) |
| actively have side-effects. The first two are variable declaration and |
| initialisations, where as the third is due to an assignment operation. |
| There are two statements (3 and 5), that do not explicitly have |
| side-effects, however the latter, statement 5, is the final statement |
| in the expression and hence will be assumed to have a side-effect. |
| |
| During compilation when the DCE optimisation is applied to the above |
| expression, statement 3 will be removed from the expression, as it has |
| no bearing on the final result of expression, the rest of the |
| statements will all remain. The optimised form of the expression is as |
| follows: |
| |
| var x := 2; // Statement 1 |
| var y := x + 2; // Statement 2 |
| y := x + 3y; // Statement 3 |
| x - y; // Statement 4 |
| |
| |
| (3) Conditional Statements (If-Then-Else) |
| 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 utilises the function based if-statement. |
| |
| x := if (y < z, y + 1, 2 * z) |
| |
| |
| In the example above, if the condition 'y < z' is true, then the |
| consequent 'y + 1' will be evaluated, its value will be returned and |
| subsequently assigned to the variable 'x'. Otherwise the alternative |
| '2 * z' will be evaluated and its value will be returned. This is |
| essentially the simplest form of an if-then-else statement. A simple |
| variation of the expression where the value of the if-statement is |
| used within another statement is as follows: |
| |
| x := 3 * if (y < z, y + 1, 2 * z) / 2 |
| |
| |
| The second form of if-statement resembles the standard syntax found in |
| most imperative languages. There are two variations of the statement: |
| |
| (a) If-Statement |
| (b) If-Then-Else Statement |
| |
| |
| (a) If-Statement |
| This version of the conditional statement returns the value of the |
| consequent expression when the condition expression is true, else it |
| will return a quiet NaN value as its result. |
| |
| Example 1: |
| x := if (y < z) y + 3; |
| |
| Example 2: |
| x := if (y < z) |
| { |
| y + 3 |
| } |
| |
| The two example expressions above are equivalent. If the condition |
| 'y < z' is true, the 'x' variable will be assigned the value of the |
| consequent 'y + 3', otherwise it will be assigned the value of quiet |
| NaN. As previously discussed, if-statements are value returning |
| constructs, and if not properly terminated using a semi-colon, will |
| end-up combining with the next statement via a multiplication |
| operation. The following example will NOT result in the expected value |
| of 'w + x' being returned: |
| |
| x := if (y < z) y + 3 // missing semi-colon ';' |
| w + x |
| |
| |
| When the above supposed multi-statement expression is compiled, the |
| expression will have a multiplication inserted between the two |
| 'intended' statements resulting in the unanticipated expression: |
| |
| x := (if (y < z) y + 3) * w + x |
| |
| |
| The solution to the above situation is to simply terminate the |
| conditional statement with a semi-colon as follows: |
| |
| x := if (y < z) y + 3; |
| w + x |
| |
| |
| (b) If-Then-Else Statement |
| The second variation of the if-statement is to allow for the use of |
| Else and If-Else cascading statements. Examples of such statements are |
| as follows: |
| |
| Example 1: Example 2: Example 3: |
| if (x < y) if (x < y) if (x > y + 1) |
| z := x + 3; { y := abs(x - z); |
| else y := z + x; else |
| y := x - z; z := x + 3; { |
| } y := z + x; |
| else z := x + 3; |
| y := x - z; }; |
| |
| |
| Example 4: Example 5: Example 6: |
| if (2 * x < max(y,3)) if (x < y) if (x < y or (x + z) > y) |
| { z := x + 3; { |
| y := z + x; else if (2y != z) z := x + 3; |
| z := x + 3; { y := x - z; |
| } z := x + 3; } |
| else if (2y - z) y := x - z; else if (abs(2y - z) >= 3) |
| y := x - z; } y := x - z; |
| else else |
| x * x; { |
| z := abs(x * x); |
| x * y * z; |
| }; |
| |
| |
| In the case where there is no final else statement and the flow |
| through the conditional arrives at this final point, the same rules |
| apply to this form of if-statement as to the previous. That is a quiet |
| NaN will be returned as the result of the if-statement. Furthermore |
| the same requirements of terminating the statement with a semi-colon |
| apply. |
| |
| (4) 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 |
| $f82(x,y,z,w)). |
| |
| 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 optimise such expressions for a given architecture. |
| |
| 3-Parameter 4-Parameter |
| +-------------+-------------+ +--------------+------------------+ |
| | Prototype | Operation | | Prototype | Operation | |
| +-------------+-------------+ +--------------+------------------+ |
| $f00(x,y,z) | (x + y) / z $f48(x,y,z,w) | x + ((y + z) / w) |
| $f01(x,y,z) | (x + y) * z $f49(x,y,z,w) | x + ((y + z) * w) |
| $f02(x,y,z) | (x + y) - z $f50(x,y,z,w) | x + ((y - z) / w) |
| $f03(x,y,z) | (x + y) + z $f51(x,y,z,w) | x + ((y - z) * w) |
| $f04(x,y,z) | (x - y) + z $f52(x,y,z,w) | x + ((y * z) / w) |
| $f05(x,y,z) | (x - y) / z $f53(x,y,z,w) | x + ((y * z) * w) |
| $f06(x,y,z) | (x - y) * z $f54(x,y,z,w) | x + ((y / z) + w) |
| $f07(x,y,z) | (x * y) + z $f55(x,y,z,w) | x + ((y / z) / w) |
| $f08(x,y,z) | (x * y) - z $f56(x,y,z,w) | x + ((y / z) * w) |
| $f09(x,y,z) | (x * y) / z $f57(x,y,z,w) | x - ((y + z) / w) |
| $f10(x,y,z) | (x * y) * z $f58(x,y,z,w) | x - ((y + z) * w) |
| $f11(x,y,z) | (x / y) + z $f59(x,y,z,w) | x - ((y - z) / w) |
| $f12(x,y,z) | (x / y) - z $f60(x,y,z,w) | x - ((y - z) * w) |
| $f13(x,y,z) | (x / y) / z $f61(x,y,z,w) | x - ((y * z) / w) |
| $f14(x,y,z) | (x / y) * z $f62(x,y,z,w) | x - ((y * z) * w) |
| $f15(x,y,z) | x / (y + z) $f63(x,y,z,w) | x - ((y / z) / w) |
| $f16(x,y,z) | x / (y - z) $f64(x,y,z,w) | x - ((y / z) * w) |
| $f17(x,y,z) | x / (y * z) $f65(x,y,z,w) | ((x + y) * z) - w |
| $f18(x,y,z) | x / (y / z) $f66(x,y,z,w) | ((x - y) * z) - w |
| $f19(x,y,z) | x * (y + z) $f67(x,y,z,w) | ((x * y) * z) - w |
| $f20(x,y,z) | x * (y - z) $f68(x,y,z,w) | ((x / y) * z) - w |
| $f21(x,y,z) | x * (y * z) $f69(x,y,z,w) | ((x + y) / z) - w |
| $f22(x,y,z) | x * (y / z) $f70(x,y,z,w) | ((x - y) / z) - w |
| $f23(x,y,z) | x - (y + z) $f71(x,y,z,w) | ((x * y) / z) - w |
| $f24(x,y,z) | x - (y - z) $f72(x,y,z,w) | ((x / y) / z) - w |
| $f25(x,y,z) | x - (y / z) $f73(x,y,z,w) | (x * y) + (z * w) |
| $f26(x,y,z) | x - (y * z) $f74(x,y,z,w) | (x * y) - (z * w) |
| $f27(x,y,z) | x + (y * z) $f75(x,y,z,w) | (x * y) + (z / w) |
| $f28(x,y,z) | x + (y / z) $f76(x,y,z,w) | (x * y) - (z / w) |
| $f29(x,y,z) | x + (y + z) $f77(x,y,z,w) | (x / y) + (z / w) |
| $f30(x,y,z) | x + (y - z) $f78(x,y,z,w) | (x / y) - (z / w) |
| $f31(x,y,z) | x * y^2 + z $f79(x,y,z,w) | (x / y) - (z * w) |
| $f32(x,y,z) | x * y^3 + z $f80(x,y,z,w) | x / (y + (z * w)) |
| $f33(x,y,z) | x * y^4 + z $f81(x,y,z,w) | x / (y - (z * w)) |
| $f34(x,y,z) | x * y^5 + z $f82(x,y,z,w) | x * (y + (z * w)) |
| $f35(x,y,z) | x * y^6 + z $f83(x,y,z,w) | x * (y - (z * w)) |
| $f36(x,y,z) | x * y^7 + z $f84(x,y,z,w) | x*y^2 + z*w^2 |
| $f37(x,y,z) | x * y^8 + z $f85(x,y,z,w) | x*y^3 + z*w^3 |
| $f38(x,y,z) | x * y^9 + z $f86(x,y,z,w) | x*y^4 + z*w^4 |
| $f39(x,y,z) | x * log(y)+z $f87(x,y,z,w) | x*y^5 + z*w^5 |
| $f40(x,y,z) | x * log(y)-z $f88(x,y,z,w) | x*y^6 + z*w^6 |
| $f41(x,y,z) | x * log10(y)+z $f89(x,y,z,w) | x*y^7 + z*w^7 |
| $f42(x,y,z) | x * log10(y)-z $f90(x,y,z,w) | x*y^8 + z*w^8 |
| $f43(x,y,z) | x * sin(y)+z $f91(x,y,z,w) | x*y^9 + z*w^9 |
| $f44(x,y,z) | x * sin(y)-z $f92(x,y,z,w) | (x and y) ? z : w |
| $f45(x,y,z) | x * cos(y)+z $f93(x,y,z,w) | (x or y) ? z : w |
| $f46(x,y,z) | x * cos(y)-z $f94(x,y,z,w) | (x < y) ? z : w |
| $f47(x,y,z) | x ? y : z $f95(x,y,z,w) | (x <= y) ? z : w |
| $f96(x,y,z,w) | (x > y) ? z : w |
| $f97(x,y,z,w) | (x >= y) ? z : w |
| $f98(x,y,z,w) | (x == y) ? z : w |
| $f99(x,y,z,w) | x*sin(y)+z*cos(w) |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 13 - VARIABLE, VECTOR & STRING DEFINITION] |
| ExprTk supports the definition of expression local variables, vectors |
| and strings. The definitions must be unique as shadowing is not |
| allowed and object life-times are based on scope. Definitions use the |
| following general form: |
| |
| var <name> := <initialiser>; |
| |
| (1) Variable Definition |
| Variables are of numeric type denoting a single value. They can be |
| explicitly initialised to a value, otherwise they will be defaulted to |
| zero. The following are examples of variable definitions: |
| |
| (a) Initialise x to zero |
| var x; |
| |
| (b) Initialise y to three |
| var y := 3; |
| |
| (c) Initialise z to the expression |
| var z := if (max(1, x + y) > 2, w, v); |
| |
| |
| (2) Vector Definition |
| Vectors are arrays of a common numeric type. The elements in a vector |
| can be explicitly initialised, otherwise they will all be defaulted to |
| zero. The following are examples of vector definitions: |
| |
| (a) Initialise all values to zero |
| var x[3]; |
| |
| (b) Initialise all values to zero |
| var x[3] := {}; |
| |
| (c) Initialise all values to given expression |
| var x[3] := [123 + 3y + sin(w / z)]; |
| |
| (d) Initialise the first two values, all other elements to zero |
| var x[3] := { 1 + x[2], sin(y[0] / x[]) + 3 }; |
| |
| (e) Initialise the first three (all) values |
| var x[3] := { 1, 2, 3 }; |
| |
| (f) Initialise vector from a vector |
| var x[4] := { 1, 2, 3, 4 }; |
| var y[3] := x; |
| |
| (g) Initialise vector from a smaller vector |
| var x[3] := { 1, 2, 3 }; |
| var y[5] := x; // 1, 2, 3, ??, ?? |
| |
| (h) Non-initialised vector |
| var x[3] := null; // ?? ?? ?? |
| |
| (i) Error as there are too many initialisers |
| var x[3] := { 1, 2, 3, 4 }; |
| |
| (j) Error as a vector of size zero is not allowed. |
| var x[0]; |
| |
| |
| (3) String Definition |
| Strings are sequences comprised of 8-bit characters. They can only be |
| defined with an explicit initialisation value. The following are |
| examples of string variable definitions: |
| |
| (a) Initialise to a string |
| var x := 'abc'; |
| |
| (b) Initialise to an empty string |
| var x := ''; |
| |
| (c) Initialise to a string expression |
| var x := 'abc' + '123'; |
| |
| (d) Initialise to a string range |
| var x := 'abc123'[2:4]; |
| |
| (e) Initialise to another string variable |
| var x := 'abc'; |
| var y := x; |
| |
| (f) Initialise to another string variable range |
| var x := 'abc123'; |
| var y := x[2:4]; |
| |
| (g) Initialise to a string expression |
| var x := 'abc'; |
| var y := x + '123'; |
| |
| (h) Initialise to a string expression range |
| var x := 'abc'; |
| var y := (x + '123')[1:3]; |
| |
| |
| (4) Return Value |
| Variable and vector definitions have a return value. In the case of |
| variable definitions, the value to which the variable is initialised |
| will be returned. Where as for vectors, the value of the first element |
| (eg: v[0]) will be returned. |
| |
| 8 == ((var x := 7;) + 1) |
| 4 == (var y[3] := {4, 5, 6};) |
| |
| |
| (5) Variable/Vector Assignment |
| The value of a variable can be assigned to a vector and a vector or a |
| vector expression can be assigned to a variable. |
| |
| (a) Variable To Vector: |
| Every element of the vector is assigned the value of the variable |
| or expression. |
| var x := 3; |
| var y[3] := { 1, 2, 3 }; |
| y := x + 1; |
| |
| (b) Vector To Variable: |
| The variable is assigned the value of the first element of the |
| vector (aka vec[0]) |
| var x := 3; |
| var y[3] := { 1, 2, 3 }; |
| x := y + 1; |
| |
| |
| Note: During the expression compilation phase, tokens are classified |
| based on the following priorities: |
| |
| (a) Reserved keywords or operators (+, -, and, or, etc) |
| (b) Base functions (abs, sin, cos, min, max etc) |
| (c) Symbol table variables |
| (d) Expression local defined variables |
| (e) Symbol table functions |
| (f) Unknown symbol resolver based variables |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 14 - VECTOR PROCESSING] |
| ExprTk provides support for various forms of vector oriented |
| arithmetic, inequalities and processing. The various supported pairs |
| are as follows: |
| |
| (a) vector and vector (eg: v0 + v1) |
| (b) vector and scalar (eg: v + 33) |
| (c) scalar and vector (eg: 22 * v) |
| |
| The following is a list of operations that can be used in conjunction |
| with vectors: |
| |
| (a) Arithmetic: +, -, *, /, % |
| (b) Exponentiation: vector ^ scalar |
| (c) Assignment: :=, +=, -=, *=, /=, %=, <=> |
| (d) Inequalities: <, <=, >, >=, ==, =, equal |
| (e) Boolean logic: and, nand, nor, or, xnor, xor |
| (f) Unary operations: |
| abs, acos, acosh, asin, asinh, atan, atanh, ceil, cos, cosh, |
| cot, csc, deg2grad, deg2rad, erf, erfc, exp, expm1, floor, |
| frac, grad2deg, log, log10, log1p, log2, rad2deg, round, sec, |
| sgn, sin, sinc, sinh, sqrt, swap, tan, tanh, trunc |
| (g) Aggregate and Reduce operations: |
| avg, max, min, mul, dot, dotk, sum, sumk, count, all_true, |
| all_false, any_true, any_false |
| (h) Transformation operations: |
| copy, rotate-left/right, shift-left/right, sort, nth_element |
| (i) BLAS-L1: |
| axpy, axpby, axpyz, axpbyz, axpbz |
| |
| Note: When one of the above described operations is being performed |
| between two vectors, the operation will only span the size of the |
| smallest vector. The elements of the larger vector outside of the |
| range will not be included. The operation itself will be processed |
| element-wise over values the smaller of the two ranges. |
| |
| The following simple example demonstrates the vector processing |
| capabilities by computing the dot-product of the vectors v0 and v1 and |
| then assigning it to the variable v0dotv1: |
| |
| var v0[3] := { 1, 2, 3 }; |
| var v1[3] := { 4, 5, 6 }; |
| var v0dotv1 := sum(v0 * v1); |
| |
| |
| The following is a for-loop based implementation that is equivalent to |
| the previously mentioned dot-product computation expression: |
| |
| var v0[3] := { 1, 2, 3 }; |
| var v1[3] := { 4, 5, 6 }; |
| var v0dotv1; |
| |
| for (var i := 0; i < min(v0[],v1[]); i += 1) |
| { |
| v0dotv1 += (v0[i] * v1[i]); |
| } |
| |
| |
| Note: When the aggregate or reduction operations denoted above are |
| used in conjunction with a vector or vector expression, the return |
| value is 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) |
| max(x / 2) == (3 / 2) |
| sum(x > 0 and x < 5) == x[] |
| |
| |
| When utilizing external user defined vectors via the symbol table as |
| opposed to expression local defined vectors, the typical 'add_vector' |
| method from the symbol table will register the entirety of the vector |
| that is passed. The following example attempts to evaluate the sum of |
| elements of the external user defined vector within a typical yet |
| trivial expression: |
| |
| std::string reduce_program = " sum(2 * v + 1) "; |
| |
| std::vector<T> v0 { T(1.1), T(2.2), ..... , T(99.99) }; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_vector("v",v); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(reduce_program,expression); |
| |
| T sum = expression.value(); |
| |
| |
| For the most part, this is a very common use-case. However there may |
| be situations where one may want to evaluate the same vector oriented |
| expression many times over, but using different vectors or sub ranges |
| of the same vector of the same size to that of the original upon every |
| evaluation. |
| |
| The usual solution is to either recompile the expression for the new |
| vector instance, or to copy the contents from the new vector to the |
| symbol table registered vector and then perform the evaluation. When |
| the vectors are large or the re-evaluation attempts are numerous, |
| these solutions can become rather time consuming and generally |
| inefficient. |
| |
| std::vector<T> v1 { T(2.2), T(2.2), ..... , T(2.2) }; |
| std::vector<T> v2 { T(3.3), T(3.3), ..... , T(3.3) }; |
| std::vector<T> v3 { T(4.4), T(4.4), ..... , T(4.4) }; |
| |
| std::vector<std::vector<T>> vv { v1, v2, v3 }; |
| ... |
| T sum = T(0); |
| |
| for (auto& new_vec : vv) |
| { |
| v = new_vec; // update vector |
| sum += expression.value(); |
| } |
| |
| |
| A solution to the above 'efficiency' problem, is to use the |
| exprtk::vector_view object. The vector_view is instantiated with a |
| size and backing based upon a vector. Upon evaluations if the backing |
| 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<T> view = exprtk::make_vector_view(v,v.size()); |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_vector("v",view); |
| |
| ... |
| |
| T sum = T(0); |
| |
| for (auto& new_vec : vv) |
| { |
| view.rebase(new_vec.data()); // update vector |
| sum += expression.value(); |
| } |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 15 - USER DEFINED FUNCTIONS] |
| ExprTk provides a means whereby custom functions can be defined and |
| 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 |
| type. |
| |
| 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 five types of function interface: |
| |
| +---+----------------------+--------------+----------------------+ |
| | # | Name | Return Type | Input Types | |
| +---+----------------------+--------------+----------------------+ |
| | 1 | ifunction | Scalar | Scalar | |
| | 2 | ivararg_function | Scalar | Scalar | |
| | 3 | igeneric_function | Scalar | Scalar,Vector,String | |
| | 4 | igeneric_function II | String | Scalar,Vector,String | |
| | 5 | igeneric_function III| String/Scalar| Scalar,Vector,String | |
| | 6 | function_compositor | Scalar | Scalar | |
| +---+----------------------+--------------+----------------------+ |
| |
| (1) ifunction |
| This interface supports zero to 20 input parameters of only the scalar |
| type (numbers). The usage requires a custom function be derived from |
| ifunction and to override one of the 21 function operators. As part of |
| the constructor the custom function will define how many parameters it |
| expects to handle. The following example defines a 3 parameter |
| function called 'foo': |
| |
| template <typename T> |
| struct foo : public exprtk::ifunction<T> |
| { |
| foo() : exprtk::ifunction<T>(3) |
| {} |
| |
| T operator()(const T& v1, const T& v2, const T& v3) |
| { |
| return T(1) + (v1 * v2) / T(v3); |
| } |
| }; |
| |
| |
| (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 |
| specialised upon type T to facilitate parameter passing. The following |
| example defines a vararg function called 'boo': |
| |
| template <typename T> |
| struct boo : public exprtk::ivararg_function<T> |
| { |
| inline T operator()(const std::vector<T>& arglist) |
| { |
| T result = T(0); |
| |
| for (std::size_t i = 0; i < arglist.size(); ++i) |
| { |
| result += arglist[i] / arglist[i > 0 ? (i - 1) : 0]; |
| } |
| |
| return result; |
| } |
| }; |
| |
| |
| (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 specialised upon the type_store type to facilitate |
| parameter passing. |
| |
| Scalar <-- function(i_0, i_1, i_2....., i_N) |
| |
| |
| The fundamental types that can be passed into the function as |
| parameters and their views are as follows: |
| |
| (1) Scalar - scalar_view |
| (2) Vector - vector_view |
| (3) String - string_view |
| |
| |
| The above denoted type views provide non-const reference-like access |
| to each parameter, as such modifications made to the input parameters |
| will persist after the function call has completed. The following |
| example defines a generic function called 'too': |
| |
| template <typename T> |
| struct too : public exprtk::igeneric_function<T> |
| { |
| typedef typename exprtk::igeneric_function<T>::parameter_list_t |
| parameter_list_t; |
| |
| too() |
| {} |
| |
| inline T operator()(parameter_list_t parameters) |
| { |
| for (std::size_t i = 0; i < parameters.size(); ++i) |
| { |
| ... |
| } |
| |
| return T(0); |
| } |
| }; |
| |
| |
| In the example above, the input 'parameters' to the function operator, |
| parameter_list_t, is a type of std::vector of type_store. Each |
| type_store instance has a member called 'type' which holds the |
| enumeration pertaining to the underlying type of the type_store. There |
| are three type enumerations: |
| |
| (1) e_scalar - literals, variables, vector elements, expressions |
| eg: 123.456, x, vec[3x + 1], 2x + 3 |
| |
| (2) e_vector - vectors, vector expressions |
| eg: vec1, 2 * vec1 + vec2 / 3 |
| |
| (3) e_string - strings, string literals and range variants of both |
| eg: 'AString', s0, 'AString'[x:y], s1[1 + x:] + 'AString' |
| |
| |
| Each of the parameters can be accessed using its designated view. A |
| typical loop for processing the parameters is as follows: |
| |
| inline T operator()(parameter_list_t parameters) |
| { |
| typedef typename exprtk::igeneric_function<T>::generic_type |
| generic_type; |
| |
| typedef typename generic_type::scalar_view scalar_t; |
| typedef typename generic_type::vector_view vector_t; |
| typedef typename generic_type::string_view string_t; |
| |
| for (std::size_t i = 0; i < parameters.size(); ++i) |
| { |
| generic_type& gt = parameters[i]; |
| |
| if (generic_type::e_scalar == gt.type) |
| { |
| scalar_t x(gt); |
| ... |
| } |
| else if (generic_type::e_vector == gt.type) |
| { |
| vector_t vector(gt); |
| ... |
| } |
| else if (generic_type::e_string == gt.type) |
| { |
| string_t string(gt); |
| ... |
| } |
| } |
| |
| return T(0); |
| } |
| |
| |
| Most often than not a custom generic function will require a specific |
| sequence of parameters, rather than some arbitrary sequence of types. |
| In those situations, ExprTk can perform compile-time type checking to |
| validate that function invocations are carried out using the correct |
| sequence of parameters. Furthermore performing the checks at compile |
| -time rather than at run-time (aka every time the function is invoked) |
| will result in expression evaluation performance gains. |
| |
| Compile-time type checking of input parameters can be requested by |
| passing a string to the constructor of the igeneric_function that |
| represents the required sequence of parameter types. When no parameter |
| sequence is provided, it is implied the function can accept a variable |
| number of parameters comprised of any of the fundamental types. |
| |
| Each fundamental type has an associated character. The following is a |
| listing of said characters and their meanings: |
| |
| (1) T - Scalar |
| (2) V - Vector |
| (3) S - String |
| (4) Z - Zero or no parameters |
| (5) ? - Any type (Scalar, Vector or String) |
| (6) * - Wildcard operator |
| (7) | - Parameter sequence delimiter |
| |
| |
| No other characters other than the seven denoted above may be included |
| in the parameter sequence definition. If any such invalid characters |
| do exist, registration of the associated generic function to a symbol |
| table ('add_function' method) will fail. If the parameter sequence is |
| modified resulting in it becoming invalid after having been added to |
| the symbol table but before the compilation step, a compilation error |
| will be incurred. |
| |
| The following example demonstrates a simple generic function |
| implementation with a user specified parameter sequence: |
| |
| template <typename T> |
| struct moo : public exprtk::igeneric_function<T> |
| { |
| typedef typename exprtk::igeneric_function<T>::parameter_list_t |
| parameter_list_t; |
| |
| moo() |
| : exprtk::igeneric_function<T>("SVTT") |
| {} |
| |
| inline T operator()(parameter_list_t parameters) |
| { |
| ... |
| } |
| }; |
| |
| |
| In the example above the generic function 'moo' expects exactly four |
| parameters in the following sequence: |
| |
| (1) String |
| (2) Vector |
| (3) Scalar |
| (4) Scalar |
| |
| Note: The 'Z' or no parameter option may not be used in conjunction |
| with any other type option in a parameter sequence. When incorporated |
| in the parameter sequence list, the no parameter option indicates that |
| the function may be invoked without any parameters being passed. For |
| more information refer to the section: 'Zero Parameter Functions' |
| |
| |
| (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. |
| |
| String <-- function(i_0, i_1, i_2....., i_N) |
| |
| |
| The following example defines a generic function named 'toupper' with |
| the string return type function operator being explicitly overridden: |
| |
| 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",igenfunct_t::e_rtrn_string) |
| {} |
| |
| inline T operator()(std::string& result, |
| parameter_list_t parameters) |
| { |
| result.clear(); |
| |
| string_t string(params[0]); |
| |
| 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. Furthermore a second parameter |
| is passed to the constructor indicating that it should be treated as a |
| string returning function - by default it is assumed to be a scalar |
| returning function. |
| |
| When executed, the function will return as a result a copy of the |
| input string converted to uppercase form. An example expression using |
| the toupper function registered as the symbol 'toupper' is as follows: |
| |
| "'ABCDEF' == toupper('aBc') + toupper('DeF')" |
| |
| |
| Note: When adding a string type returning generic function to a symbol |
| table the 'add_function' is invoked. The example below demonstrates |
| how this can be done: |
| |
| toupper<T> tu; |
| |
| exprtk::symbol_table<T> symbol_table; |
| |
| symbol_table.add_function("toupper",tu); |
| |
| |
| 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. |
| |
| template <typename T> |
| struct zoo : public exprtk::igeneric_function<T> |
| { |
| typedef typename exprtk::igeneric_function<T>::parameter_list_t |
| parameter_list_t; |
| |
| zoo() |
| : exprtk::igeneric_function<T>("SVT*V?") |
| {} |
| |
| inline T operator()(parameter_list_t parameters) |
| { |
| ... |
| } |
| }; |
| |
| |
| In the example above the generic function 'zoo' expects at least five |
| parameters in the following sequence: |
| |
| (1) String |
| (2) Vector |
| (3) One or more Scalars |
| (4) Vector |
| (5) Any type (one type of either a scalar, vector or string) |
| |
| |
| A final piece of type checking functionality is available for the |
| scenarios where a single function name is intended to be used for |
| multiple distinct parameter sequences, another name for this feature |
| is function overloading. The parameter sequences are passed to the |
| constructor as a single string delimited by the pipe '|' character. |
| Two specific overrides of the function operator are provided one for |
| standard generic functions and one for string returning functions. The |
| overrides are as follows: |
| |
| // Scalar <-- function(psi,i_0,i_1,....,i_N) |
| inline T operator()(const std::size_t& ps_index, |
| parameter_list_t parameters) |
| { |
| ... |
| } |
| |
| // String <-- function(psi,i_0,i_1,....,i_N) |
| inline T operator()(const std::size_t& ps_index, |
| std::string& result, |
| parameter_list_t parameters) |
| { |
| ... |
| } |
| |
| |
| When the function operator is invoked the 'ps_index' parameter will |
| have as its value the index of the parameter sequence that matches the |
| specific invocation. This way complex and time consuming type checking |
| conditions need not be executed in the function itself but rather a |
| simple and efficient dispatch to a specific implementation for that |
| particular parameter sequence can be performed. |
| |
| template <typename T> |
| struct roo : public exprtk::igeneric_function<T> |
| { |
| typedef typename exprtk::igeneric_function<T>::parameter_list_t |
| parameter_list_t; |
| |
| moo() |
| : exprtk::igeneric_function<T>("SVTT|SS|TTV|S?V*S") |
| {} |
| |
| inline T operator()(const std::size_t& ps_index, |
| parameter_list_t parameters) |
| { |
| ... |
| } |
| }; |
| |
| |
| In the example above there are four distinct parameter sequences that |
| can be processed by the generic function 'roo'. Any other parameter |
| sequences will cause a compilation error. The four valid sequences are |
| as follows: |
| |
| Sequence-0 Sequence-1 Sequence-2 Sequence-3 |
| 'SVTT' 'SS' 'TTV' 'S?V*S' |
| (1) String (1) String (1) Scalar (1) String |
| (2) Vector (2) String (2) Scalar (2) Any Type |
| (3) Scalar (3) Vector (3) One or more Vectors |
| (4) Scalar (4) String |
| |
| |
| (5) igeneric_function III |
| In this section we will discuss an extension of the igeneric_function |
| interface that will allow for the overloading of a user defined custom |
| function, where by it can return either a scalar or string value type |
| depending on the input parameter sequence with which the function is |
| invoked. |
| |
| template <typename T> |
| struct foo : public exprtk::igeneric_function<T> |
| { |
| typedef typename exprtk::igeneric_function<T>::parameter_list_t |
| parameter_list_t; |
| |
| foo() |
| : exprtk::igeneric_function<T> |
| ( |
| "T:T|S:TS", |
| igfun_t::e_rtrn_overload |
| ) |
| {} |
| |
| // Scalar value returning invocations |
| inline T operator()(const std::size_t& ps_index, |
| parameter_list_t parameters) |
| { |
| ... |
| } |
| |
| // String value returning invocations |
| inline T operator()(const std::size_t& ps_index, |
| std::string& result, |
| parameter_list_t& parameters) |
| { |
| ... |
| } |
| }; |
| |
| |
| In the example above the custom user defined function "foo" can be |
| invoked by using either one of two input parameter sequences, which |
| are defined as follows: |
| |
| Sequence-0 Sequence-1 |
| 'T' -> T 'TS' -> S |
| (1) Scalar (1) Scalar |
| (2) String |
| |
| |
| The parameter sequence definitions are identical to the previously |
| define igeneric_function, with the exception of the inclusion of the |
| return type - which can only be either a scalar T or a string S. |
| |
| |
| (6) function_compositor |
| The function compositor is a factory that allows one to define and |
| construct a function using ExprTk syntax. The functions are limited to |
| returning a single scalar value and consuming up to six parameters as |
| input. |
| |
| All composited functions are registered with a symbol table, allowing |
| them to call other functions that have been registered with the symbol |
| table instance. Furthermore the functions can be recursive in nature |
| due to the inherent function prototype forwarding that occurs during |
| construction. The following example defines, by using two different |
| methods, composited functions and implicitly registering the functions |
| with the denoted symbol table. |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::function_compositor<T> compositor_t; |
| typedef typename compositor_t::function function_t; |
| |
| symbol_table_t symbol_table; |
| |
| compositor_t compositor(symbol_table); |
| |
| // define function koo0(v1,v2) { ... } |
| compositor |
| .add( |
| function_t( |
| "koo0", |
| " 1 + cos(v1 * v2) / 3;", |
| "v1","v2")); |
| |
| // define function koo1(x,y,z) { ... } |
| compositor |
| .add(function_t() |
| .name("koo1") |
| .var("x").var("y").var("z") |
| .expression("1 + cos(x * y) / z;")); |
| |
| |
| (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. |
| The following demonstrates how all the pieces are put together: |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| typedef exprtk::parser<double> parser_t; |
| typedef exprtk::function_compositor<double> compositor_t; |
| typedef typename compositor_t::function function_t; |
| |
| foo<double> f; |
| boo<double> b; |
| too<double> t; |
| toupper<double> tu; |
| |
| symbol_table_t symbol_table; |
| compositor_t compositor(symbol_table); |
| |
| symbol_table.add_function("foo",f); |
| symbol_table.add_function("boo",b); |
| symbol_table.add_function("too",t); |
| |
| symbol_table.add_function("toupper", |
| tu, |
| symbol_table_t::e_ft_strfunc); |
| |
| compositor |
| .add(function_t() |
| .name("koo") |
| .var("v1") |
| .var("v2") |
| .expression("1 + cos(v1 * v2) / 3;")); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| std::string expression_str = |
| " if (foo(1,2,3) + boo(1) > boo(1/2,2/3,3/4,4/5)) " |
| " koo(3,4); " |
| " else " |
| " too(2 * v1 + v2 / 3, 'abcdef'[2:4], 3.3); " |
| " "; |
| |
| parser_t parser; |
| parser.compile(expression_str,expression); |
| |
| expression.value(); |
| |
| |
| (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 |
| at compile time. |
| |
| If it is certain that the function being registered does not have any |
| side effects and can be correctly constant folded where appropriate, |
| then during the construction of the function the side-effect trait of |
| the function can be disabled. |
| |
| template <typename T> |
| struct foo : public exprtk::ifunction<T> |
| { |
| foo() : exprtk::ifunction<T>(3) |
| { |
| exprtk::disable_has_side_effects(*this); |
| } |
| |
| T operator()(const T& v1, const T& v2, const T& v3) |
| { ... } |
| }; |
| |
| |
| (8) Zero Parameter Functions |
| When either an ifunction, ivararg_function or igeneric_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 zero input parameters the calling styles are |
| as follows: |
| |
| (1) x + sin(foo()- 2) / y |
| (2) x + sin(foo - 2) / y |
| |
| |
| By default the zero parameter trait is disabled. In order to enable |
| it, a process similar to that of enabling of the side effect trait is |
| carried out: |
| |
| template <typename T> |
| struct foo : public exprtk::ivararg_function<T> |
| { |
| foo() |
| { |
| exprtk::enable_zero_parameters(*this); |
| } |
| |
| inline T operator()(const std::vector<T>& arglist) |
| { ... } |
| }; |
| |
| |
| 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 |
| 'compute1', and a three input parameter lambda named 'compute2' will |
| be registered with the given symbol_table instance: |
| |
| |
| double compute1(double v0, double v1) |
| { |
| return 2.0 * v0 + v1 / 3.0; |
| } |
| |
| . |
| . |
| . |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| |
| symbol_table_t symbol_table; |
| |
| symbol_table.add_function("compute1", compute1); |
| |
| symbol_table.add_function( |
| "compute2", |
| [](double v0, double v1, double v2) -> double |
| { return v0 / v1 + v2; }); |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 16 - EXPRESSION DEPENDENTS] |
| Any expression that is not a literal (aka constant) will have |
| dependencies. The types of 'dependencies' an expression can have are |
| as follows: |
| |
| (a) Variables |
| (b) Vectors |
| (c) Strings |
| (d) Functions |
| (e) Assignments |
| |
| |
| In the following example the denoted expression has its various |
| dependencies listed: |
| |
| z := abs(x + sin(2 * pi / y)) |
| |
| (a) Variables: x, y, z and pi |
| (b) Functions: abs, sin |
| (c) Assignments: z |
| |
| |
| ExprTk allows for the derivation of expression dependencies via the |
| 'dependent_entity_collector' (DEC). When activated either through |
| 'compile_options' at the construction of the parser or through calls |
| to enabler methods just prior to compilation, the DEC will proceed to |
| collect any of the relevant types that are encountered during the |
| parsing phase. Once the compilation process has successfully |
| completed, the caller can then obtain a list of symbols and their |
| associated types from the DEC. |
| |
| The kinds of questions one can ask regarding the dependent entities |
| within an expression are as follows: |
| |
| * What user defined variables, vectors or strings are used? |
| * What functions or custom user functions are used? |
| * Which variables, vectors or strings have values assigned to them? |
| |
| |
| The following example demonstrates usage of the DEC in determining the |
| dependents of the given expression: |
| |
| typedef typename parser_t:: |
| dependent_entity_collector::symbol_t symbol_t; |
| |
| std::string expression_string = |
| "z := abs(x + sin(2 * pi / y))"; |
| |
| T x,y,z; |
| |
| parser_t parser; |
| symbol_table_t symbol_table; |
| |
| symbol_table.add_variable("x",x); |
| symbol_table.add_variable("y",y); |
| symbol_table.add_variable("z",z); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| //Collect only variable and function symbols |
| parser.dec().collect_variables() = true; |
| parser.dec().collect_functions() = true; |
| |
| if (!parser.compile(expression_string,expression)) |
| { |
| // error.... |
| } |
| |
| std::deque<symbol_t> symbol_list; |
| |
| parser.dec().symbols(symbol_list); |
| |
| for (std::size_t i = 0; i < symbol_list.size(); ++i) |
| { |
| symbol_t& symbol = symbol_list[i]; |
| |
| switch (symbol.second) |
| { |
| case parser_t::e_st_variable : ... break; |
| case parser_t::e_st_vector : ... break; |
| case parser_t::e_st_string : ... break; |
| case parser_t::e_st_function : ... break; |
| } |
| } |
| |
| |
| Note: The 'symbol_t' type is a std::pair comprising of the symbol name |
| (std::string) and the associated type of the symbol as denoted by the |
| cases in the switch statement. |
| |
| Having particular symbols (variable or function) present in an |
| expression is one form of dependency. Another and just as interesting |
| and important type of dependency is that of assignments. Assignments |
| are the set of dependent symbols that 'may' have their values modified |
| 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) w, z if (x > y, z := x + 2, w := 'A String') |
| (5) None x + y + z |
| |
| |
| 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 |
| certainty at evaluation time and not beforehand, hence both are listed |
| as being candidates for assignment. |
| |
| The following builds upon the previous example demonstrating the usage |
| of the DEC in determining the 'assignments' of the given expression: |
| |
| //Collect assignments |
| parser.dec().collect_assignments() = true; |
| |
| if (!parser.compile(expression_string,expression)) |
| { |
| // error.... |
| } |
| |
| std::deque<symbol_t> symbol_list; |
| |
| parser.dec().assignment_symbols(symbol_list); |
| |
| for (std::size_t i = 0; i < symbol_list.size(); ++i) |
| { |
| symbol_t& symbol = symbol_list[i]; |
| |
| switch (symbol.second) |
| { |
| case parser_t::e_st_variable : ... break; |
| case parser_t::e_st_vector : ... break; |
| case parser_t::e_st_string : ... break; |
| } |
| } |
| |
| |
| Note: The assignments will only consist of variable types and as such |
| will not contain symbols denoting functions. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 17 - HIERARCHIES OF SYMBOL TABLES] |
| Most situations will only require a single symbol_table instance to be |
| 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 |
| 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. |
| |
| A suggested hierarchy of symbol tables is as follows: |
| |
| (a) Global constant value symbol table |
| (b) Global non side-effect functions symbol table |
| (c) Global variable symbol table |
| (d) Expression specific variable symbol table |
| |
| |
| (a) Global constant value symbol table |
| This symbol table will contain constant variables denoting immutable |
| values. These variables can be made available to all expressions, and |
| in turn expressions will assume the values themselves will never be |
| modified for the duration of the process run-time. Examples of such |
| variables are: |
| |
| (1) pi or e |
| (2) speed_of_light |
| (3) avogadro_number |
| (4) num_cpus |
| |
| |
| (b) Global non side-effect functions symbol table |
| This symbol table will contain only user defined functions that will |
| not incur any side-effects that are visible to any of the expressions |
| that invoke them. These functions will be thread-safe or threading |
| invariant and will not maintain any form of state between invocations. |
| Examples of such functions are: |
| |
| (1) calc_volume_of_sphere(r) |
| (2) distance(x0,y0,x1,y1) |
| |
| |
| (c) Global variable symbol table |
| This symbol table will contain variables that will be accessible to |
| all associated expressions and will not be specific or exclusive to |
| any one expression. This variant differs from (a) in that the values |
| of the variables can change (or be updated) between evaluations of |
| expressions - but through properly scheduled evaluations are |
| guaranteed to never change during the evaluation of any dependent |
| expressions. Furthermore it is assumed that these variables will be |
| used in a read-only context and that no expressions will attempt to |
| modify these variables via assignments or other means. |
| |
| (1) price_of_stock_xyz |
| (2) outside_temperature or inside_temperature |
| (3) fuel_in_tank |
| (4) num_customers_in_store |
| (5) num_items_on_shelf |
| |
| |
| (d) Expression specific variable symbol table |
| This symbol_table is the most common form, and is used to store |
| variables that are specific and exclusive to a particular expression. |
| That is to say references to variables in this symbol_table will not |
| be part of another expression. Though it may be possible to have |
| expressions that contain the variables with the same name, in that |
| case those variables will be distinctly different. Which would mean if |
| a particular expression were to be compiled twice, each expression |
| would have its own unique symbol_table which in turn would have its |
| own instances of those variables. Examples of such variables could be: |
| |
| (1) x or y |
| (2) customer_name |
| |
| |
| The following is a diagram depicting the possible version of the |
| denoted symbol table hierarchies. In the diagram there are two unique |
| expressions, each of which have a reference to the Global constant, |
| functions and variables symbol tables and an exclusive reference to a |
| local symbol table. |
| |
| +-------------------------+ +-------------------------+ |
| | Global Constants | | Global Functions | |
| | Symbol Table | | Symbol Table | |
| +----o--o-----------------+ +--------------------o----+ |
| | | | |
| | | +-------+ |
| | +------------------->----------------------------+ | |
| | +----------------------------+ | | |
| | | Global Variables | | | |
| | +------o Symbol Table o-----+ | V |
| | | +----------------------------+ | | | |
| | | | | | |
| | | +----------------+ +----------------+ | | | |
| | | | Symbol Table 0 | | Symbol Table 1 | | V | |
| | | +--o-------------+ +--o-------------+ | | | |
| | | | | | | | |
| | | | | | | | |
| +--V--V----V---------+ +-V---------------V--+ | | |
| | Expression 0 | | Expression 1 |<--+--+ |
| | '2 * sin(x) - y' | | 'k + abs(x - y)' | |
| +--------------------+ +--------------------+ |
| |
| |
| Bringing all of the above together, in the following example the |
| hierarchy of symbol tables are instantiated and initialised. An |
| expression that makes use of various elements of each symbol table is |
| then compiled and later on evaluated: |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| |
| // Setup global constants symbol table |
| symbol_table_t glbl_const_symbol_table; |
| glbl_const_symbtab.add_constants(); // pi, epsilon and inf |
| glbl_const_symbtab.add_constant("speed_of_light",299e6); |
| glbl_const_symbtab.add_constant("avogadro_number",6e23); |
| |
| // Setup global function symbol table |
| symbol_table_t glbl_funcs_symbol_table; |
| glbl_func_symbtab.add_function('distance',distance); |
| glbl_func_symbtab.add_function('calc_spherevol',calc_sphrvol); |
| |
| ...... |
| |
| // Setup global variable symbol table |
| symbol_table_t glbl_variable_symbol_table; |
| glbl_variable_symbtab.add_variable('temp_outside',thermo.outside); |
| glbl_variable_symbtab.add_variable('temp_inside' ,thermo.inside ); |
| glbl_variable_symbtab.add_variable('num_cstmrs',store.num_cstmrs); |
| |
| ...... |
| |
| double x,y,z; |
| |
| // Setup expression specific symbol table |
| symbol_table_t symbol_table; |
| symbol_table.add_variable('x',x); |
| symbol_table.add_variable('y',y); |
| symbol_table.add_variable('z',z); |
| |
| expression_t expression; |
| |
| // Register the various symbol tables |
| expression |
| .register_symbol_table(symbol_table); |
| |
| expression |
| .register_symbol_table(glbl_funcs_symbol_table); |
| |
| expression |
| .register_symbol_table(glbl_const_symbol_table); |
| |
| expression |
| .register_symbol_table(glbl_variable_symbol_table); |
| |
| std::string expression_str = |
| "abs(temp_inside - temp_outside) + 2 * speed_of_light / x"; |
| |
| parser_t parser; |
| parser.compile(expression_str,expression); |
| |
| ...... |
| |
| while (keep_evaluating) |
| { |
| .... |
| |
| T result = expression.value(); |
| |
| .... |
| } |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 18 - UNKNOWN UNKNOWNS] |
| In this section we will discuss the process of handling expressions |
| with a mix of known and unknown variables. Initially a discussion into |
| the types of expressions that exist will be provided, then a series of |
| possible solutions will be presented for each scenario. |
| |
| When parsing an expression, there may be situations where one is not |
| fully aware of what if any variables will be used prior to the |
| expression being compiled. |
| |
| This can become problematic, as in the default scenario it is assumed |
| the symbol_table that is registered with the expression instance will |
| already possess the externally available variables, functions and |
| constants needed during the compilation of the expression. |
| |
| In the event there are symbols in the expression that can't be mapped |
| to either a reserved word, or located in the associated |
| symbol_table(s), an "Undefined symbol" error will be raised and the |
| compilation process will fail. |
| |
| The numerous scenarios that can occur when compiling an expression |
| with ExprTk generally fall into one of the following three categories: |
| |
| (a) No external variables |
| (b) Predetermined set of external variables |
| (c) Unknown set of variables |
| |
| |
| (a) No external variables |
| These are expressions that contain no external variables but may |
| contain local variables. As local variables cannot be accessed |
| externally from the expression, it is assumed that such expressions |
| will not have a need for a symbol_table and furthermore expressions |
| which don't make use of functions that have side-effects will be |
| evaluated completely at compile time resulting in a constant return |
| value. The following are examples of such expressions: |
| |
| (1) 1 + 2 |
| (2) var x := 3; 2 * x - 3 |
| (3) var x := 3; var y := abs(x - 8); x - y / 7 |
| |
| |
| (b) Predetermined set of external variables |
| These are expressions that are comprised of externally available |
| variables and functions and will only compile successfully if the |
| symbols that correspond to the variables and functions are already |
| defined in their associated symbol_table(s). This is by far the most |
| common scenario when using ExprTk. |
| |
| As an example, one may have three external variables: x, y and z which |
| have been registered with the associated symbol_table, and will then |
| need to compile and evaluate expressions comprised of any subset of |
| these three variables. The following are a few examples of such |
| expressions: |
| |
| (1) 1 + x |
| (2) x / y |
| (3) 2 * x * y / z |
| |
| |
| 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 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 |
| variables. |
| |
| |
| (c) Unknown set of variables |
| These are expressions that are comprised of symbols other than the |
| standard ExprTk reserved words or what has been registered with their |
| associated symbol_table, and will normally fail compilation due to the |
| associated symbol_table not having a reference to them. As such this |
| scenario can be seen as a combination of scenario B, where one may |
| have a symbol_table with registered variables, but would also like to |
| handle the situation of variables that aren't present in said |
| symbol_table. |
| |
| When dealing with expressions of category (c), one must perform all of |
| the following: |
| |
| (1) Determine the variables used in the expression |
| (2) Populate a symbol_table(s) with the entities from (1) |
| (3) Compile the expression |
| (4) Provide a means by which the entities from (1) can be modified |
| |
| |
| Depending on the nature of processing, steps (1) and (2) can be done |
| either independently of each other or combined into one. The following |
| example will initially look at solving the problem of unknown |
| variables with the latter method using the 'unknown_symbol_resolver' |
| component. |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| symbol_table_t unknown_var_symbol_table; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| symbol_table.add_variable("y",y); |
| |
| expression_t expression; |
| expression.register_symbol_table(unknown_var_symbol_table); |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.enable_unknown_symbol_resolver(); |
| |
| std::string expression_str = "x + abs(y / 3k) * z + 2"; |
| |
| parser.compile(expression_str,expression); |
| |
| |
| In the example above, the symbols 'k' and 'z' will be treated as |
| unknown symbols. The parser in the example is set to handle unknown |
| symbols using the built-in default unknown_symbol_resolver (USR). The |
| default USR will automatically resolve any unknown symbols as a |
| variable (scalar type). The new variables will be added to the primary |
| symbol_table, which in this case is the 'unknown_var_symbol_table' |
| instance. Once the compilation has completed successfully, the |
| variables that were resolved during compilation can be accessed from |
| the primary symbol_table using the 'get_variable_list' and |
| 'variable_ref' methods and then if needed can be modified accordingly |
| after which the expression itself can be evaluated. |
| |
| std::vector<std::string> variable_list; |
| |
| unknown_var_symbol_table.get_variable_list(variable_list); |
| |
| for (auto& var_name : variable_list) |
| { |
| T& v = unknown_var_symbol_table.variable_ref(var_name); |
| |
| v = ...; |
| } |
| |
| ... |
| |
| expression.value(); |
| |
| |
| Note: As previously mentioned the default USR will automatically |
| assume any unknown symbol to be a valid scalar variable, and will then |
| proceed to add said symbol as a variable to the primary symbol_table |
| of the associated expression during the compilation process. However a |
| problem that may arise, is that expressions that are parsed with the |
| USR enabled, but contain 'typos' or otherwise syntactic errors may |
| inadvertently compile successfully due to the simplistic nature of the |
| default USR. The following are some example expressions: |
| |
| (1) 1 + abz(x + 1) |
| (2) sine(y / 2) - coz(3x) |
| |
| |
| The two expressions above contain misspelt symbols (abz, sine, coz) |
| which if implied multiplications and default USR are enabled during |
| compilation will result in them being assumed to be valid 'variables', |
| which obviously is not the intended outcome by the user. A possible |
| solution to this problem is for one to implement their own specific |
| USR that will perform a user defined business logic in determining if |
| an encountered unknown symbol should be treated as a variable or if it |
| should raise a compilation error. The following example demonstrates a |
| simple user defined USR: |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| template <typename T> |
| struct my_usr : public parser_t::unknown_symbol_resolver |
| { |
| typedef typename parser_t::unknown_symbol_resolver usr_t; |
| |
| bool process(const std::string& unknown_symbol, |
| typename usr_t::usr_symbol_type& st, |
| T& default_value, |
| std::string& error_message) |
| { |
| if (0 != unknown_symbol.find("var_")) |
| { |
| error_message = "Invalid symbol: " + unknown_symbol; |
| return false; |
| } |
| |
| st = usr_t::e_usr_variable_type; |
| default_value = T(123.123); |
| |
| return true; |
| } |
| }; |
| |
| ... |
| |
| symbol_table_t unknown_var_symbol_table; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| symbol_table.add_variable("y",y); |
| |
| expression_t expression; |
| expression.register_symbol_table(unknown_var_symbol_table); |
| expression.register_symbol_table(symbol_table); |
| |
| my_usr<T> musr; |
| |
| parser_t parser; |
| parser.enable_unknown_symbol_resolver(&musr); |
| |
| std::string expression_str = "var_x + abs(var_y - 3) * var_z"; |
| |
| parser.compile(expression_str,expression); |
| |
| |
| In the example above, a user specified USR is defined, and is |
| registered with the parser enabling the USR functionality. Then when |
| an unknown symbol is encountered during the compilation process, the |
| USR's process method will be invoked. The USR in the example will only |
| 'accept' unknown symbols that have a prefix of 'var_' as being valid |
| variables, all other unknown symbols will result in a compilation |
| error being raised. |
| |
| In the example above the callback of the USR that is invoked during |
| the unknown symbol resolution process only allows for scalar variables |
| to be defined and resolved - as that is the simplest and most common |
| form. |
| |
| There is also an extended version of the callback that can be |
| overridden that will allow for more control and choice over the type |
| of symbol being resolved. The following is an example definition of |
| said extended callback: |
| |
| template <typename T> |
| struct my_usr : public parser_t::unknown_symbol_resolver |
| { |
| typedef typename parser_t::unknown_symbol_resolver usr_t; |
| |
| my_usr() |
| : usr_t(usr_t::e_usrmode_extended) |
| {} |
| |
| virtual bool process(const std::string& unknown_symbol, |
| symbol_table_t& symbol_table, |
| std::string& error_message) |
| { |
| bool result = false; |
| |
| if (0 == unknown_symbol.find("var_")) |
| { |
| // Default value of zero |
| result = symbol_table.create_variable(unknown_symbol,0); |
| |
| if (!result) |
| { |
| error_message = "Failed to create variable..."; |
| } |
| } |
| else if (0 == unknown_symbol.find("str_")) |
| { |
| // Default value of empty string |
| result = symbol_table.create_stringvar(unknown_symbol,""); |
| |
| if (!result) |
| { |
| error_message = "Failed to create string variable..."; |
| } |
| } |
| else |
| error_message = "Indeterminable symbol type."; |
| |
| return result; |
| } |
| }; |
| |
| |
| In the example above, the USR callback when invoked will pass the |
| primary symbol table associated with the expression being parsed. The |
| symbol resolution business logic can then determine under what |
| conditions a symbol will be resolved including its type (scalar, |
| string, vector etc) and default value. When the callback successfully |
| returns the symbol parsing and resolution process will again be |
| executed by the parser. The idea here is that given the primary symbol |
| table will now have the previously detected unknown symbol registered, |
| it will be correctly resolved and the general parsing processing can |
| then resume as per normal. |
| |
| Note: In order to have the USR's extended mode callback be invoked It |
| is necessary to pass the e_usrmode_extended enum value during the |
| constructor of the user defined USR. |
| |
| Note: The primary symbol table for an expression is the first symbol |
| table to be registered with that instance of the expression. |
| |
| Note: For a successful symbol resolution using the normal USR all of |
| the following are required: |
| |
| (1) Only if successful shall the process method return TRUE |
| (2) The default_value parameter will have been set |
| (3) The error_message parameter will be empty |
| (4) usr_symbol_type input parameter field will be set to either: |
| (*) e_usr_variable_type |
| (*) e_usr_constant_type |
| |
| Note: For a successful symbol resolution using the extended USR all of |
| the following are required: |
| |
| (1) Only if successful shall the process method return TRUE |
| (2) symbol_table parameter will have had the newly resolved |
| variable or string added to it |
| (3) error_message parameter will be empty |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 19 - ENABLING & DISABLING FEATURES] |
| The parser can be configured via its settings instance to either allow |
| or disallow certain features that are available within the ExprTk |
| grammar. The features fall into one of the following six categories: |
| |
| (1) Base Functions |
| (2) Control Flow Structures |
| (3) Logical Operators |
| (4) Arithmetic Operators |
| (5) Inequality Operators |
| (6) Assignment Operators |
| |
| |
| (1) Base Functions |
| The list of available base functions is as follows: |
| |
| abs, acos, acosh, asin, asinh, atan, atanh, atan2, avg, ceil, |
| clamp, cos, cosh, cot, csc, equal, erf, erfc, exp, expm1, |
| floor, frac, hypot, iclamp, like, log, log10, log2, logn, |
| log1p, mand, max, min, mod, mor, mul, ncdf, pow, root, round, |
| roundn, sec, sgn, sin, sinc, sinh, sqrt, sum, swap, tan, tanh, |
| trunc, not_equal, inrange, deg2grad, deg2rad, rad2deg, grad2deg |
| |
| |
| The above mentioned base functions can be either enabled or disabled |
| 'all' at once, as is demonstrated below: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings().disable_all_base_functions(); |
| |
| parser |
| .compile("2 * abs(2 - 3)",expression); // compilation failure |
| |
| parser.settings().enable_all_base_functions(); |
| |
| parser |
| .compile("2 * abs(2 - 3)",expression); // compilation success |
| |
| |
| One can also enable or disable specific base functions. The following |
| example demonstrates the disabling of the trigonometric functions |
| 'sin' and 'cos': |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings() |
| .disable_base_function(settings_t::e_bf_sin) |
| .disable_base_function(settings_t::e_bf_cos); |
| |
| parser |
| .compile("(sin(x) / cos(x)) == tan(x)",expression); // failure |
| |
| parser.settings() |
| .enable_base_function(settings_t::e_bf_sin) |
| .enable_base_function(settings_t::e_bf_cos); |
| |
| parser |
| .compile("(sin(x) / cos(x)) == tan(x)",expression); // success |
| |
| |
| (2) Control Flow Structures |
| The list of available control flow structures is as follows: |
| |
| (a) If or If-Else |
| (b) Switch statement |
| (c) For Loop |
| (d) While Loop |
| (e) Repeat Loop |
| |
| |
| The above mentioned control flow structures can be either enabled |
| or disabled 'all' at once, as is demonstrated below: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| std::string program = |
| " var x := 0; " |
| " for (var i := 0; i < 10; i += 1) " |
| " { " |
| " x += i; " |
| " } "; |
| |
| parser.settings().disable_all_control_structures(); |
| |
| parser |
| .compile(program,expression); // compilation failure |
| |
| parser.settings().enable_all_control_structures(); |
| |
| parser |
| .compile(program,expression); // compilation success |
| |
| |
| One can also enable or disable specific control flow structures. The |
| following example demonstrates the disabling of the for-loop control |
| flow structure: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| std::string program = |
| " var x := 0; " |
| " for (var i := 0; i < 10; i += 1) " |
| " { " |
| " x += i; " |
| " } "; |
| |
| parser.settings() |
| .disable_control_structure(settings_t::e_ctrl_for_loop); |
| |
| parser |
| .compile(program,expression); // failure |
| |
| parser.settings() |
| .enable_control_structure(settings_t::e_ctrl_for_loop); |
| |
| parser |
| .compile(program,expression); // success |
| |
| |
| (3) Logical Operators |
| The list of available logical operators is as follows: |
| |
| and, nand, nor, not, or, xnor, xor, &, | |
| |
| |
| The above mentioned logical operators can be either enabled or |
| disabled 'all' at once, as is demonstrated below: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings().disable_all_logic_ops(); |
| |
| parser |
| .compile("1 or not(0 and 1)",expression); // compilation failure |
| |
| parser.settings().enable_all_logic_ops(); |
| |
| parser |
| .compile("1 or not(0 and 1)",expression); // compilation success |
| |
| |
| One can also enable or disable specific logical operators. The |
| following example demonstrates the disabling of the 'and' logical |
| operator: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings() |
| .disable_logic_operation(settings_t::e_logic_and); |
| |
| parser |
| .compile("1 or not(0 and 1)",expression); // failure |
| |
| parser.settings() |
| .enable_logic_operation(settings_t::e_logic_and); |
| |
| parser |
| .compile("1 or not(0 and 1)",expression); // success |
| |
| |
| (4) Arithmetic Operators |
| The list of available arithmetic operators is as follows: |
| |
| +, -, *, /, %, ^ |
| |
| |
| The above mentioned arithmetic operators can be either enabled or |
| disabled 'all' at once, as is demonstrated below: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings().disable_all_arithmetic_ops(); |
| |
| parser |
| .compile("1 + 2 / 3",expression); // compilation failure |
| |
| parser.settings().enable_all_arithmetic_ops(); |
| |
| parser |
| .compile("1 + 2 / 3",expression); // compilation success |
| |
| |
| One can also enable or disable specific arithmetic operators. The |
| following example demonstrates the disabling of the addition '+' |
| arithmetic operator: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings() |
| .disable_arithmetic_operation(settings_t::e_arith_add); |
| |
| parser |
| .compile("1 + 2 / 3",expression); // failure |
| |
| parser.settings() |
| .enable_arithmetic_operation(settings_t::e_arith_add); |
| |
| parser |
| .compile("1 + 2 / 3",expression); // success |
| |
| |
| (5) Inequality Operators |
| The list of available inequality operators is as follows: |
| |
| <, <=, >, >=, ==, =, != <> |
| |
| |
| The above mentioned inequality operators can be either enabled or |
| disabled 'all' at once, as is demonstrated below: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings().disable_all_inequality_ops(); |
| |
| parser |
| .compile("1 < 3",expression); // compilation failure |
| |
| parser.settings().enable_all_inequality_ops(); |
| |
| parser |
| .compile("1 < 3",expression); // compilation success |
| |
| |
| One can also enable or disable specific inequality operators. The |
| following example demonstrates the disabling of the less-than '<' |
| inequality operator: |
| |
| parser_t parser; |
| expression_t expression; |
| |
| parser.settings() |
| .disable_inequality_operation(settings_t::e_ineq_lt); |
| |
| parser |
| .compile("1 < 3",expression); // failure |
| |
| parser.settings() |
| .enable_inequality_operation(settings_t::e_ineq_lt); |
| |
| parser |
| .compile("1 < 3",expression); // success |
| |
| |
| (6) Assignment Operators |
| The list of available assignment operators is as follows: |
| |
| :=, +=, -=, *=, /=, %= |
| |
| |
| The above mentioned assignment operators can be either enabled or |
| disabled 'all' at once, as is demonstrated below: |
| |
| parser_t parser; |
| expression_t expression; |
| symbol_table_t symbol_table; |
| |
| T x = T(0); |
| |
| symbol_table.add_variable("x",x); |
| |
| expression.register_symbol_table(symbol_table); |
| |
| parser.settings().disable_all_assignment_ops(); |
| |
| parser |
| .compile("x := 3",expression); // compilation failure |
| |
| parser.settings().enable_all_assignment_ops(); |
| |
| parser |
| .compile("x := 3",expression); // compilation success |
| |
| |
| One can also enable or disable specific assignment operators. The |
| following example demonstrates the disabling of the '+=' addition |
| assignment operator: |
| |
| parser_t parser; |
| expression_t expression; |
| symbol_table_t symbol_table; |
| |
| T x = T(0); |
| |
| symbol_table.add_variable("x",x); |
| |
| expression.register_symbol_table(symbol_table); |
| |
| parser.settings() |
| .disable_assignment_operation(settings_t::e_assign_addass); |
| |
| parser |
| .compile("x += 3",expression); // failure |
| |
| parser.settings() |
| .enable_assignment_operation(settings_t::e_assign_addass); |
| |
| parser |
| .compile("x += 3",expression); // success |
| |
| |
| Note: In the event of a base function being disabled, one can redefine |
| the base function using the standard custom function definition |
| process. In the following example the 'sin' function is disabled then |
| redefined as a function taking degree input. |
| |
| template <typename T> |
| struct sine_deg : public exprtk::ifunction<T> |
| { |
| sine_deg() : exprtk::ifunction<T>(1) {} |
| |
| inline T operator()(const T& v) |
| { |
| const T pi = exprtk::details::numeric::constant::pi; |
| return std::sin((v * T(pi)) / T(180)); |
| } |
| }; |
| |
| ... |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| typedef typename parser_t::settings_store settings_t; |
| |
| sine_deg<T> sine; |
| |
| symbol_table.add_reserved_function("sin",sine); |
| |
| expression_t expression; |
| |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| |
| parser.settings() |
| .disable_base_function(settings_t::e_bf_sin); |
| |
| parser.compile("1 + sin(30)",expression); |
| |
| |
| In the example above, the custom 'sin' function is registered with the |
| symbol_table using the method 'add_reserved_function'. This is done so |
| as to bypass the checks for reserved words that are carried out on the |
| provided symbol names when calling the standard 'add_function' method. |
| Normally if a user specified symbol name conflicts with any of the |
| ExprTk reserved words, the add_function call will fail. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 20 - EXPRESSION RETURN VALUES] |
| ExprTk expressions can return immediately from any point by utilizing |
| the return call. Furthermore the return call can be used to transfer |
| out multiple return values from within the expression. |
| |
| If an expression evaluation exits using a return point, the result of |
| the call to the 'value' method will be NaN, and it's expected that the |
| return values will be available from the results_context. |
| |
| In the following example there are three return points in the |
| expression. If neither of the return points are hit, then the |
| expression will return normally. |
| |
| std::string expression_string = |
| " if (x < y) " |
| " return [x + 1,'return-call 1']; " |
| " else if (x > y) " |
| " return [y / 2, y + 1, 'return-call 2']; " |
| " else if (equal(x,y)) " |
| " x + y; " |
| " return [x, y, x + y, x - y, 'return-call 3'] "; |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| typedef exprtk::parser<double> parser_t; |
| |
| symbol_table_t symbol_table; |
| expression_t expression; |
| parser_t parser; |
| |
| double x = 0; |
| double y = 0; |
| |
| symbol_table.add_variable("x",x); |
| symbol_table.add_variable("y",y); |
| |
| expression.register_symbol_table(symbol_table); |
| |
| parser.compile(expression_string,expression); |
| |
| T result = expression.value(); |
| |
| if (expression.results().count()) |
| { |
| typedef exprtk::results_context<T> results_context_t; |
| typedef typename results_context_t::type_store_t type_t; |
| typedef typename type_t::scalar_view scalar_t; |
| typedef typename type_t::vector_view vector_t; |
| typedef typename type_t::string_view string_t; |
| |
| const results_context_t& results = expression.results(); |
| |
| for (std::size_t i = 0; i < results.count(); ++i) |
| { |
| type_t t = results[i]; |
| |
| switch (t.type) |
| { |
| case type_t::e_scalar : ... |
| break; |
| |
| case type_t::e_vector : ... |
| break; |
| |
| case type_t::e_string : ... |
| break; |
| |
| default : continue; |
| } |
| } |
| |
| |
| Note: Processing of the return results is similar to that of the |
| generic function call parameters. |
| |
| It is however recommended that if there is to be only a single flow of |
| execution through the expression, that the simpler approach of |
| registering external variables of appropriate type be used. |
| |
| This method simply requires the variables that are to hold the various |
| results that are to be computed within the expression to be registered |
| with an associated symbol_table instance. Then within the expression |
| itself to have the result variables be assigned the appropriate |
| values. |
| |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| typedef exprtk::parser<double> parser_t; |
| |
| std::string expression_string = |
| " var x := 123.456; " |
| " var s := 'ijk'; " |
| " result0 := x + 78.90; " |
| " result1 := s + '123' "; |
| |
| double result0; |
| std::string result1; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable ("result0",result0); |
| symbol_table.add_stringvar("result1",result1); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(expression_string,expression); |
| |
| expression.value(); |
| |
| 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 |
| 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 utilised from within the calling program. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 21 - COMPILATION ERRORS] |
| When attempting to compile a malformed or otherwise erroneous ExprTk |
| expression, the compilation process will result in an error, as is |
| indicated by the 'compile' method returning a false value. A |
| diagnostic indicating the first error encountered and its cause can be |
| obtained by invoking the 'error' method, as is demonstrated in the |
| following example: |
| |
| if (!parser.compile(expression_string,expression)) |
| { |
| printf("Error: %s\n", parser.error().c_str()); |
| return false; |
| } |
| |
| |
| Any error(s) resulting from a failed compilation will be stored in the |
| parser instance until the next time a compilation is performed. Before |
| then errors can be enumerated in the order they occurred by invoking |
| the 'get_error' method which itself will return a 'parser_error' type. |
| A parser_error object will contain an error diagnostic, an error mode |
| (or class), and the character position of the error in the expression |
| string. The following example demonstrates the enumeration of error(s) |
| in the event of a failed compilation. |
| |
| if (!parser.compile(expression_string,expression)) |
| { |
| for (std::size_t i = 0; i < parser.error_count(); ++i) |
| { |
| typedef exprtk::parser_error::type error_t; |
| |
| error_t error = parser.get_error(i); |
| |
| printf("Error[%02d] Position: %02d Type: [%14s] Msg: %s\n", |
| i, |
| error.token.position, |
| exprtk::parser_error::to_str(error.mode).c_str(), |
| error.diagnostic.c_str()); |
| } |
| |
| return false; |
| } |
| |
| |
| Assuming the following expression '2 + (3 / log(1 + x))' which uses a |
| variable named 'x' that has not been registered with the appropriate |
| symbol_table instance and is not a locally defined variable, once |
| compiled the above denoted post compilation error handling code shall |
| produce the following output: |
| |
| Error: ERR184 - Undefined symbol: 'x' |
| Error[00] Pos:17 Type:[Syntax] Msg: ERR184 - Undefined symbol: 'x' |
| |
| |
| For expressions comprised of multiple lines, the error position |
| provided in the parser_error object can be converted into a pair of |
| line and column numbers by invoking the 'update_error' function as is |
| demonstrated by the following example: |
| |
| if (!parser.compile(program_str,expression)) |
| { |
| for (std::size_t i = 0; i < parser.error_count(); ++i) |
| { |
| typedef exprtk::parser_error::type error_t; |
| |
| error_t error = parser.get_error(i); |
| |
| exprtk::parser_error::update_error(error,program_str); |
| |
| printf("Error[%02d] at line: %d column: %d\n", |
| i, |
| error.line_no, |
| error.column_no); |
| } |
| |
| return false; |
| } |
| |
| |
| Note: There are five distinct error modes in ExprTk which denote the |
| class of an error. These classes are as follows: |
| |
| (a) Syntax |
| (b) Token |
| (c) Numeric |
| (d) Symbol Table |
| (e) Lexer |
| |
| |
| (a) Syntax Errors |
| These are errors related to invalid syntax found within the denoted |
| expression. Examples are invalid sequences of operators and variables, |
| incorrect number of parameters to functions, invalid conditional or |
| loop structures and invalid use of keywords. |
| |
| eg: 'for := sin(x,y,z) + 2 * equal > until[2 - x,3]' |
| |
| |
| (b) Token Errors |
| Errors in this class relate to token level errors detected by one or |
| more of the following checkers: |
| |
| (1) Bracket Checker |
| (2) Numeric Checker |
| (3) Sequence Checker |
| |
| |
| (c) Numeric Errors |
| This class of error is related to conversion of numeric values from |
| their string form to the underlying numerical type (float, double |
| etc). |
| |
| (d) Symbol Table Errors |
| This is the class of errors related to failures when interacting with |
| the registered symbol_table instance. Errors such as not being able to |
| find, within the symbol_table, symbols representing variables or |
| functions, to being unable to create new variables in the symbol_table |
| via the 'unknown symbol resolver' mechanism. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 22 - RUNTIME LIBRARY PACKAGES] |
| ExprTk contains a set of simple extensions, that provide |
| functionalities beyond basic numerical calculations. Currently the |
| available packages are: |
| |
| +---+--------------------+-----------------------------------+ |
| | # | Package Name | Namespace/Type | |
| +---+--------------------+-----------------------------------+ |
| | 1 | Basic I/O | exprtk::rtl::io::package<T> | |
| | 2 | File I/O | exprtk::rtl::io::file::package<T> | |
| | 3 | Vector Operations | exprtk::rtl::vecops::package<T> | |
| +---+--------------------+-----------------------------------+ |
| |
| |
| In order to make the features of a specific package available within |
| an expression, an instance of the package must be added to the |
| expression's associated symbol table. In the following example, the |
| file I/O package is made available for the given expression: |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| exprtk::rtl::io::file::package<T> fileio_package; |
| |
| std::string expression_string = |
| " var file_name := 'file.txt'; " |
| " var stream := null; " |
| " " |
| " stream := open(file_name,'w'); " |
| " " |
| " write(stream,'Hello world....\n'); " |
| " " |
| " close(stream); " |
| " "; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_package(fileio_package); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(expression_string,expression); |
| |
| expression.value(); |
| |
| |
| (1) Basic I/O functions: |
| |
| (a) print |
| (b) println |
| |
| (2) File I/O functions: |
| |
| (a) open (b) close |
| (c) write (d) read |
| (e) getline (f) eof |
| |
| (3) Vector Operations functions: |
| |
| (a) all_true (b) all_false |
| (c) any_true (d) any_false |
| (e) count (f) copy |
| (g) rotate-left (h) rotate-right |
| (i) shift-left (j) shift-right |
| (k) sort (l) nth_element |
| (m) iota (n) sumk |
| (o) axpy (p) axpby |
| (q) axpyz (r) axpbyz |
| (s) axpbz (t) dot |
| (u) dotk |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 23 - HELPERS & UTILS] |
| The ExprTk library provides a series of usage simplifications via |
| helper routines that combine various processes into a single 'function |
| call' making certain actions easier to carry out though not |
| necessarily in the most efficient way possible. A list of the routines |
| are as follows: |
| |
| (a) collect_variables |
| (b) collect_functions |
| (c) compute |
| (d) integrate |
| (e) derivative |
| (f) second_derivative |
| (g) third_derivative |
| |
| |
| (a) collect_variables |
| This function will collect all the variable symbols in a given string |
| representation of an expression and return them in an STL compatible |
| sequence data structure (eg: std::vector, dequeue etc) specialised |
| upon a std::string type. If an error occurs during the parsing of the |
| expression then the return value of the function will be false, |
| otherwise it will be true. An example use of the given routine is as |
| follows: |
| |
| std::string expression = "x + abs(y / z)"; |
| |
| std::vector<std::string> variable_list; |
| |
| if (exprtk::collect_variables(expression, variable_list)) |
| { |
| for (const auto& var : variable_list) |
| { |
| ... |
| } |
| } |
| else |
| printf("An error occurred."); |
| |
| |
| (b) collect_functions |
| This function will collect all the function symbols in a given string |
| representation of an expression and return them in an STL compatible |
| sequence data structure (eg: std::vector, dequeue etc) specialised |
| upon a std::string type. If an error occurs during the parsing of the |
| expression then the return value of the function will be false, |
| otherwise it will be true. An example use of the given routine is as |
| follows: |
| |
| std::string expression = "x + abs(y / cos(1 + z))"; |
| |
| std::deque<std::string> function_list; |
| |
| if (exprtk::collect_functions(expression, function_list)) |
| { |
| for (const auto& func : function_list) |
| { |
| ... |
| } |
| } |
| else |
| printf("An error occurred."); |
| |
| |
| Note: When either the 'collect_variables' or 'collect_functions' free |
| functions return true - that does not necessarily indicate the |
| expression itself is valid. It is still possible that when compiled |
| the expression may have certain 'type' related errors - though it is |
| highly likely that no semantic errors will occur if either return |
| true. |
| |
| Note: The default interface provided for both the collect_variables |
| and collect_functions free_functions, assumes that expressions will |
| only be utilising the ExprTk reserved functions (eg: abs, cos, min |
| etc). When user defined functions are to be used in an expression, a |
| symbol_table instance containing said functions can be passed to |
| either routine, and will be incorporated during the compilation and |
| Dependent Entity Collection processes. In the following example, a |
| user defined free function named 'foo' is registered with a |
| symbol_table. Finally the symbol_table instance and associated |
| expression string are passed to the exprtk::collect_functions routine. |
| |
| template <typename T> |
| T foo(T v) |
| { |
| return std::abs(v + T(2)) / T(3); |
| } |
| |
| ...... |
| |
| exprtk::symbol_table<T> sym_tab; |
| |
| symbol_table.add_function("foo",foo); |
| |
| std::string expression = "x + foo(y / cos(1 + z))"; |
| |
| std::deque<std::string> function_list; |
| |
| if (exprtk::collect_functions(expression, sym_tab, function_list)) |
| { |
| for (const auto& func : function_list) |
| { |
| ... |
| } |
| } |
| else |
| printf("An error occurred."); |
| |
| |
| (c) compute |
| This free function will compute the value of an expression from its |
| string form. If an invalid expression is passed, the result of the |
| function will be false indicating an error, otherwise the return value |
| will be true indicating success. The compute function has three |
| overloads, the definitions of which are: |
| |
| (1) No variables |
| (2) One variable called x |
| (3) Two variables called x and y |
| (3) Three variables called x, y and z |
| |
| |
| Example uses of each of the three overloads for the compute routine |
| are as follows: |
| |
| T result = T(0); |
| |
| // No variables overload |
| std::string no_vars = "abs(1 - (3 / pi)) * 5"; |
| |
| if (!exprtk::compute(no_vars,result)) |
| printf("Failed to compute: %s",no_vars.c_str()); |
| else |
| printf("Result: %15.5f\n",result); |
| |
| // One variable 'x' overload |
| T x = 123.456; |
| |
| std::string one_var = "abs(x - (3 / pi)) * 5"; |
| |
| if (!exprtk::compute(one_var, x, result)) |
| printf("Failed to compute: %s",one_var.c_str()); |
| else |
| printf("Result: %15.5f\n",result); |
| |
| // Two variables 'x' and 'y' overload |
| T y = 789.012; |
| |
| std::string two_var = "abs(x - (y / pi)) * 5"; |
| |
| if (!exprtk::compute(two_var, x, y, result)) |
| printf("Failed to compute: %s",two_var.c_str()); |
| else |
| printf("Result: %15.5f\n",result); |
| |
| // Three variables 'x', 'y' and 'z' overload |
| T z = 345.678; |
| |
| std::string three_var = "abs(x - (y / pi)) * z"; |
| |
| if (!exprtk::compute(three_var, x, y, z, result)) |
| printf("Failed to compute: %s",three_var.c_str()); |
| else |
| printf("Result: %15.5f\n",result); |
| |
| |
| (d) integrate |
| This free function will attempt to perform a numerical integration of |
| 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::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| std::string expression_string = "sqrt(1 - (x^2))"; |
| |
| T x = T(0); |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(expression_string,expression); |
| |
| .... |
| |
| // Integrate in domain [-1,1] using a reference to x variable |
| T area1 = exprtk::integrate(expression, x, T(-1), T(1)); |
| |
| // Integrate in domain [-1,1] using name of x variable |
| T area2 = exprtk::integrate(expression, "x", T(-1), T(1)); |
| |
| |
| (e) derivative |
| This free function will attempt to perform a numerical differentiation |
| of a single variable compiled expression at a given point for a given |
| epsilon, using a variant of Newton's difference quotient called the |
| five-point stencil method. The derivative function has two overloads, |
| where the variable of differentiation can either be passed as a |
| reference or as a name in string form. Example usage of the derivative |
| function is as follows: |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| std::string expression_string = "sqrt(1 - (x^2))"; |
| |
| T x = T(0); |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(expression_string,expression); |
| |
| .... |
| |
| // Differentiate expression at value of x = 12.3 using a reference |
| // to the x variable |
| x = T(12.3); |
| T derivative1 = exprtk::derivative(expression, x); |
| |
| // Differentiate expression where value x = 45.6 using name |
| // of the x variable |
| x = T(45.6); |
| T derivative2 = exprtk::derivative(expression, "x"); |
| |
| |
| (f) second_derivative |
| This free function will attempt to perform a numerical second |
| derivative of a single variable compiled expression at a given point |
| for a given epsilon, using a variant of Newton's difference quotient |
| method. The second_derivative function has two overloads, where the |
| variable of differentiation can either be passed as a reference or as |
| a name in string form. Example usage of the second_derivative function |
| is as follows: |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| std::string expression_string = "sqrt(1 - (x^2))"; |
| |
| T x = T(0); |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(expression_string,expression); |
| |
| .... |
| |
| // Second derivative of expression where value of x = 12.3 using a |
| // reference to x variable |
| x = T(12.3); |
| T derivative1 = exprtk::second_derivative(expression,x); |
| |
| // Second derivative of expression where value of x = 45.6 using |
| // name of x variable |
| x = T(45.6); |
| T derivative2 = exprtk::second_derivative(expression, "x"); |
| |
| |
| (g) third_derivative |
| This free function will attempt to perform a numerical third |
| derivative of a single variable compiled expression at a given point |
| for a given epsilon, using a variant of Newton's difference quotient |
| method. The third_derivative function has two overloads, where the |
| variable of differentiation can either be passed as a reference or as |
| a name in string form. Example usage of the third_derivative function |
| is as follows: |
| |
| typedef exprtk::symbol_table<T> symbol_table_t; |
| typedef exprtk::expression<T> expression_t; |
| typedef exprtk::parser<T> parser_t; |
| |
| std::string expression_string = "sqrt(1 - (x^2))"; |
| |
| T x = T(0); |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_variable("x",x); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| parser.compile(expression_string,expression); |
| |
| .... |
| |
| // Third derivative of expression where value of x = 12.3 using a |
| // reference to the x variable |
| x = T(12.3); |
| T derivative1 = exprtk::third_derivative(expression, x); |
| |
| // Third derivative of expression where value of x = 45.6 using |
| // name of the x variable |
| x = T(45.6); |
| T derivative2 = exprtk::third_derivative(expression, "x"); |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 24 - BENCHMARKING] |
| As part of the ExprTk package there is an expression benchmark utility |
| named 'exprtk_benchmark'. The utility attempts to determine expression |
| evaluation speed (or rate of evaluations - evals per second), by |
| evaluating each expression numerous times and mutating the underlying |
| variables of the expression between each evaluation. The utility |
| assumes any valid ExprTk expression (containing conditionals, loops |
| etc), however it will only make use of a predefined set of scalar |
| variables, namely: a, b, c, x, y, z and w. That being said expressions |
| themselves can contain any number of local variables, vectors or |
| strings. There are two modes of operation: |
| |
| (1) Default |
| (2) User Specified Expressions |
| |
| |
| (1) Default |
| The default mode is enabled simply by executing the exprtk_benchmark |
| binary with no command line parameters. In this mode a predefined set |
| of expressions will be evaluated in three phases: |
| |
| (a) ExprTk evaluation |
| (b) Native evaluation |
| (c) ExprTk parse |
| |
| |
| In the first two phases (a and b) a list of predefined (hard-coded) |
| expressions will be evaluated using both ExprTk and native mode |
| implementations. This is done so as to compare evaluation times |
| between ExprTk and native implementations. The set of expressions used |
| are as follows: |
| |
| (01) (y + x) |
| (02) 2 * (y + x) |
| (03) (2 * y + 2 * x) |
| (04) ((1.23 * x^2) / y) - 123.123 |
| (05) (y + x / y) * (x - y / x) |
| (06) x / ((x + y) + (x - y)) / y |
| (07) 1 - ((x * y) + (y / x)) - 3 |
| (08) (5.5 + x) + (2 * x - 2 / 3 * y) * (x / 3 + y / 4) + (y + 7.7) |
| (09) 1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^15 - 5.5x^23 + 6.6y^55 |
| (10) sin(2 * x) + cos(pi / y) |
| (11) 1 - sin(2 * x) + cos(pi / y) |
| (12) sqrt(111.111 - sin(2 * x) + cos(pi / y) / 333.333) |
| (13) (x^2 / sin(2 * pi / y)) - x / 2 |
| (14) x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y |
| (15) clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0) |
| (16) max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11)) |
| (17) if((y + (x * 2.2)) <= (x + y + 1.1), x - y, x*y) + 2 * pi / x |
| |
| |
| The third and final phase (c), is used to determine average |
| compilation rates (compiles per second) for expressions of varying |
| complexity. Each expression is compiled 100K times and the average for |
| each expression is output. |
| |
| |
| (2) User Specified Expressions |
| In this mode two parameters are passed to the utility via the command |
| line: |
| |
| (a) A name of a text file containing one expression per line |
| (b) An integer representing the number of evaluations per expression |
| |
| |
| An example execution of the benchmark utility in this mode is as |
| follows: |
| |
| ./exprtk_benchmark my_expressions.txt 1000000 |
| |
| |
| The above invocation will load the expressions from the file |
| 'my_expressions.txt' and will then proceed to evaluate each expression |
| one million times, varying the above mentioned variables (x, y, z |
| etc.) between each evaluation, and at the end of each expression round |
| a print out of running times, result of a single evaluation and total |
| sum of results is provided as demonstrated below: |
| |
| Expression 1 of 7 4.770 ns 47700 ns ( 9370368.0) '((((x+y)+z)))' |
| Expression 2 of 7 4.750 ns 47500 ns ( 1123455.9) '((((x+y)-z)))' |
| Expression 3 of 7 4.766 ns 47659 ns (21635410.7) '((((x+y)*z)))' |
| Expression 4 of 7 5.662 ns 56619 ns ( 1272454.9) '((((x+y)/z)))' |
| Expression 5 of 7 4.950 ns 49500 ns ( 4123455.9) '((((x-y)+z)))' |
| Expression 6 of 7 7.581 ns 75810 ns (-4123455.9) '((((x-y)-z)))' |
| Expression 7 of 7 4.801 ns 48010 ns ( 0.0) '((((x-y)*z)))' |
| |
| |
| The benchmark utility can be very useful when investigating evaluation |
| efficiency issues with ExprTk or simply during the prototyping of |
| expressions. As an example, lets take the following expression: |
| |
| 1 / sqrt(2x) * e^(3y) |
| |
| |
| 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 |
| guesses about how to break the expression up into its more |
| 'interesting' sub-parts which we will then add as one expression per |
| line to the file. An example breakdown may be as follows: |
| |
| 1 / sqrt(2x) * e^(3y) |
| 1 / sqrt(2x) |
| e^(3y) |
| |
| |
| The benchmark with the given file, where each expression will be |
| evaluated 100K times can be executed as follows: |
| |
| ./exprtk_benchmark test.txt 100000 |
| Expr 1 of 3 90.340 ns 9034000 ns (296417859.3) '1/sqrt(2x)*e^(3y)' |
| Expr 2 of 3 11.100 ns 1109999 ns ( 44267.3) '1/sqrt(2x)' |
| Expr 3 of 3 77.830 ns 7783000 ns (615985286.6) 'e^(3y)' |
| [*] Number Of Evals: 300000 |
| [*] Total Time: 0.018sec |
| [*] Total Single Eval Time: 0.000ms |
| |
| |
| From the results above we conclude that the third expression (e^(3y)) |
| consumes the largest amount of time. The variable 'e', as used in both |
| the benchmark and in the expression, is an approximation of the |
| transcendental mathematical constant e (2.71828182845904...) hence the |
| sub-expression should perhaps be modified to use the generally more |
| efficient built-in 'exp' function. |
| |
| ./exprtk_benchmark test.txt 1000000 |
| Expr 1 of 5 86.563 ns 8656300ns (296417859.6) '1/sqrt(2x)*e^(3y)' |
| Expr 2 of 5 40.506 ns 4050600ns (296417859.6) '1/sqrt(2x)*exp(3y)' |
| Expr 3 of 5 14.248 ns 1424799ns ( 44267.2) '1/sqrt(2x)' |
| Expr 4 of 5 88.840 ns 8884000ns (615985286.9) 'e^(3y)' |
| Expr 5 of 5 29.267 ns 2926699ns (615985286.9) 'exp(3y)' |
| [*] Number Of Evals: 5000000 |
| [*] Total Time: 0.260sec |
| [*] Total Single Eval Time: 0.000ms |
| |
| |
| The above output demonstrates the results from making the previously |
| mentioned modification to the expression. As can be seen the new form |
| of the expression using the 'exp' function reduces the evaluation time |
| by over 50%, in other words increases the evaluation rate by two fold. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 25 - EXPRTK NOTES] |
| The following is a list of facts and suggestions one may want to take |
| into account when using ExprTk: |
| |
| (00) Precision and performance of expression evaluations are the |
| dominant principles of the ExprTk library. |
| |
| (01) ExprTk uses a rudimentary imperative programming model with |
| syntax based on languages such as Pascal and C. Furthermore |
| ExprTk is an LL(2) type grammar and is processed using a |
| recursive descent parsing algorithm. |
| |
| (02) Supported types are float, double, long double and MPFR/GMP. |
| |
| (03) Standard mathematical operator precedence is applied (BEDMAS). |
| |
| (04) Results of expressions that are deemed as being 'valid' are to |
| exist within the set of Real numbers. All other results will be |
| of the value: Not-A-Number (NaN). |
| |
| (05) Supported user defined types are numeric and string |
| variables, numeric vectors and functions. |
| |
| (06) All reserved words, keywords, variable, vector, string and |
| function names are case-insensitive. |
| |
| (07) Variable, vector, string variable and function names must begin |
| with a letter (A-Z or a-z), then can be comprised of any |
| combination of letters, digits, underscores and dots, ending in |
| either a letter (A-Z or a-z), digit or underscore. (eg: x, y2, |
| var1, power_func99, person.age, item.size.0). The associated |
| regex pattern is: [a-zA-Z]([a-zA-Z0-9_.]*|[a-zA-Z0-9_]) |
| |
| (08) Expression lengths and sub-expression lists are limited only by |
| storage capacity. |
| |
| (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 utilise objects, such as variables, |
| of that symbol-table, otherwise the result will be undefined |
| behavior. |
| |
| (10) Equal and not_equal are normalised-epsilon equality routines, |
| which use epsilons of 0.0000000001 and 0.000001 for double and |
| float types respectively. |
| |
| (11) All trigonometric functions assume radian input unless stated |
| otherwise. |
| |
| (12) Expressions may contain white-space characters such as space, |
| tabs, new-lines, control-feed et al. |
| ('\n', '\r', '\t', '\b', '\v', '\f') |
| |
| (13) Strings may be comprised of any combination of letters, digits |
| special characters including (~!@#$%^&*()[]|=+ ,./?<>;:"`~_) or |
| hexadecimal escaped sequences (eg: \0x30) and must be enclosed |
| with single-quotes. |
| eg: 'Frankly my dear, \0x49 do n0t give a damn!' |
| |
| (14) User defined normal functions can have up to 20 parameters, |
| where as user defined generic-functions and vararg-functions |
| can have an unlimited number of parameters. |
| |
| (15) The inbuilt polynomial functions can be at most of degree 12. |
| |
| (16) Where appropriate constant folding optimisations may be applied. |
| (eg: The expression '2 + (3 - (x / y))' becomes '5 - (x / y)') |
| |
| (17) If the strength reduction compilation option has been enabled, |
| then where applicable strength reduction optimisations may be |
| applied. |
| |
| (18) String processing capabilities are available by default. To |
| turn them off, the following needs to be defined at compile |
| time: exprtk_disable_string_capabilities |
| |
| (19) Composited functions can call themselves or any other functions |
| that have been defined prior to their own definition. |
| |
| (20) Recursive calls made from within composited functions will have |
| a stack size bound by the stack of the executing architecture. |
| |
| (21) User defined functions by default are assumed to have side |
| effects. As such an "all constant parameter" invocation of such |
| functions wont result in constant folding. If the function has |
| no side effects then that can be noted during the constructor |
| of the ifunction allowing it to be constant folded where |
| appropriate. |
| |
| (22) The entity relationship between symbol_table and an expression |
| is many-to-many. However the intended 'typical' use-case where |
| possible, is to have a single symbol table manage the variable |
| and function requirements of multiple expressions. |
| |
| (23) The common use-case for an expression is to have it compiled |
| only ONCE and then subsequently have it evaluated multiple |
| times. An extremely inefficient and suboptimal approach would |
| be to recompile an expression from its string form every time |
| it requires evaluating. |
| |
| (24) It is strongly recommended that the return value of method |
| invocations from the parser and symbol_table types be taken |
| 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. |
| 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: |
| |
| (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) /* .... */ |
| |
| (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 |
| |
| (28) The following is a list of reserved words and symbols used by |
| ExprTk. Attempting to add a variable or custom function to a |
| symbol table using any of the reserved words will result in a |
| failure. |
| |
| abs, acos, acosh, and, asin, asinh, atan, atan2, atanh, avg, |
| break, case, ceil, clamp, continue, cosh, cos, cot, csc, |
| default, deg2grad, deg2rad, else, equal, erfc, erf, exp, |
| 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, 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. |
| Unlike some languages that limit the types of expressions that |
| can be performed in certain situations, in ExprTk any valid |
| expression can be used in any "value consuming" context. eg: |
| |
| var y := 3; |
| for (var x := switch |
| { |
| case 1 : 7; |
| case 2 : -1 + ~{var x{};}; |
| default : y > 2 ? 3 : 4; |
| }; |
| x != while (y > 0) { y -= 1; }; |
| x -= { |
| if (min(x,y) < 2 * max(x,y)) |
| x + 2; |
| else |
| x + y - 3; |
| } |
| ) |
| { |
| (x + y) / (x - y); |
| } |
| |
| (30) It is recommended when prototyping expressions that the ExprTk |
| REPL be utilised, as it supports all the features available in |
| the library, including complete error analysis, benchmarking |
| and dependency dumps etc which allows for rapid |
| coding/prototyping and debug cycles without the hassle of |
| having to recompile test programs with expressions that have |
| been hard-coded. It's also a good source of truth for how the |
| library's various features can be applied. |
| |
| (31) For performance considerations, one should assume the actions |
| of expression, symbol table and parser instance instantiation |
| and destruction, and the expression compilation process itself |
| to be of high latency. Hence none of them should be part of any |
| performance critical code paths, and should instead occur |
| entirely either before or after such code paths. |
| |
| (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 |
| with ExprTk, will likely result in a far more productive and |
| enjoyable programming experience. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 26 - SIMPLE EXPRTK EXAMPLE] |
| The following is a simple yet complete example demonstrating typical |
| usage of the ExprTk Library. The example instantiates a symbol table |
| object, adding to it three variables named x, y and z, and a custom |
| user defined function, that accepts only two parameters, named myfunc. |
| The example then proceeds to instantiate an expression object and |
| register to it the symbol table instance. |
| |
| A parser is then instantiated, and the string representation of the |
| expression and the expression object are passed to the parser's |
| compile method for compilation. If an error occurred during |
| compilation, the compile method will return false, leading to a series |
| of error diagnostics being printed to stdout. Otherwise the newly |
| compiled expression is evaluated by invoking the expression object's |
| value method, and subsequently printing the result of the computation |
| to stdout. |
| |
| |
| --- snip --- |
| #include <cstdio> |
| #include <string> |
| |
| #include "exprtk.hpp" |
| |
| template <typename T> |
| struct myfunc : public exprtk::ifunction<T> |
| { |
| myfunc() : exprtk::ifunction<T>(2) {} |
| |
| T operator()(const T& v1, const T& v2) |
| { |
| return T(1) + (v1 * v2) / T(3); |
| } |
| }; |
| |
| int main() |
| { |
| typedef exprtk::symbol_table<double> symbol_table_t; |
| typedef exprtk::expression<double> expression_t; |
| typedef exprtk::parser<double> parser_t; |
| typedef exprtk::parser_error::type error_t; |
| |
| std::string expression_str = |
| "z := 2 myfunc([4 + sin(x / pi)^3],y ^ 2)"; |
| |
| double x = 1.1; |
| double y = 2.2; |
| double z = 3.3; |
| |
| myfunc<double> mf; |
| |
| symbol_table_t symbol_table; |
| symbol_table.add_constants(); |
| symbol_table.add_variable("x",x); |
| symbol_table.add_variable("y",y); |
| symbol_table.add_variable("z",z); |
| symbol_table.add_function("myfunc",mf); |
| |
| expression_t expression; |
| expression.register_symbol_table(symbol_table); |
| |
| parser_t parser; |
| |
| if (!parser.compile(expression_str,expression)) |
| { |
| // A compilation error has occurred. Attempt to |
| // print all errors to stdout. |
| |
| printf("Error: %s\tExpression: %s\n", |
| parser.error().c_str(), |
| expression_str.c_str()); |
| |
| for (std::size_t i = 0; i < parser.error_count(); ++i) |
| { |
| // Include the specific nature of each error |
| // and its position in the expression string. |
| |
| error_t error = parser.get_error(i); |
| |
| printf("Error: %02d Position: %02d " |
| "Type: [%s] " |
| "Message: %s " |
| "Expression: %s\n", |
| static_cast<int>(i), |
| static_cast<int>(error.token.position), |
| exprtk::parser_error::to_str(error.mode).c_str(), |
| error.diagnostic.c_str(), |
| expression_str.c_str()); |
| } |
| |
| return 1; |
| } |
| |
| // Evaluate the expression and obtain its result. |
| |
| double result = expression.value(); |
| |
| printf("Result: %10.5f\n",result); |
| |
| return 0; |
| } |
| --- snip --- |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 27 - BUILD OPTIONS] |
| When building ExprTk there are a number of defines that will enable or |
| disable certain features and capabilities. The defines can either be |
| part of a compiler command line switch or scoped around the include to |
| the ExprTk header. The defines are as follows: |
| |
| (01) exprtk_enable_debugging |
| (02) exprtk_disable_comments |
| (03) exprtk_disable_break_continue |
| (04) exprtk_disable_sc_andor |
| (05) exprtk_disable_return_statement |
| (06) exprtk_disable_enhanced_features |
| (07) exprtk_disable_string_capabilities |
| (08) exprtk_disable_superscalar_unroll |
| (09) exprtk_disable_rtl_io_file |
| (10) exprtk_disable_rtl_vecops |
| (11) exprtk_disable_caseinsensitivity |
| |
| |
| (01) exprtk_enable_debugging |
| This define will enable printing of debug information to stdout during |
| the compilation process. |
| |
| (02) exprtk_disable_comments |
| This define will disable the ability for expressions to have comments. |
| Expressions that have comments when parsed with a build that has this |
| option, will result in a compilation failure. |
| |
| (03) exprtk_disable_break_continue |
| This define will disable the loop-wise 'break' and 'continue' |
| capabilities. Any expression that contains those keywords will result |
| in a compilation failure. |
| |
| (04) exprtk_disable_sc_andor |
| This define will disable the short-circuit '&' (and) and '|' (or) |
| operators |
| |
| (05) exprtk_disable_return_statement |
| This define will disable use of return statements within expressions. |
| |
| (06) exprtk_disable_enhanced_features |
| This define will disable all enhanced features such as strength |
| reduction and special function optimisations and expression specific |
| type instantiations. This feature will reduce compilation times and |
| binary sizes but will also result in massive performance degradation |
| of expression evaluations. |
| |
| (07) exprtk_disable_string_capabilities |
| This define will disable all string processing capabilities. Any |
| expression that contains a string or string related syntax will result |
| in a compilation failure. |
| |
| (08) exprtk_disable_superscalar_unroll |
| This define will set the loop unroll batch size to 4 operations per |
| loop instead of the default 8 operations. This define is used in |
| operations that involve vectors and aggregations over vectors. When |
| targeting non-superscalar architectures, it may be recommended to |
| build using this particular option if efficiency of evaluations is of |
| concern. |
| |
| (09) exprtk_disable_rtl_io_file |
| This define will disable the file I/O RTL package features. When |
| present, any attempts to register the file I/O package with a given |
| symbol table will fail causing a compilation error. |
| |
| (10) exprtk_disable_rtl_vecops |
| This define will disable the extended vector operations RTL package |
| features. When present, any attempts to register the vector operations |
| package with a given symbol table will fail causing a compilation |
| error. |
| |
| (11) exprtk_disable_caseinsensitivity |
| This define will disable case-insensitivity when matching variables |
| and functions. Furthermore all reserved and keywords will only be |
| acknowledged when in all lower-case. |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 28 - FILES] |
| The source distribution of ExprTk is comprised of the following set of |
| 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 |
| (15) exprtk_simple_example_11.cpp |
| (16) exprtk_simple_example_12.cpp |
| (17) exprtk_simple_example_13.cpp |
| (18) exprtk_simple_example_14.cpp |
| (19) exprtk_simple_example_15.cpp |
| (20) exprtk_simple_example_16.cpp |
| (21) exprtk_simple_example_17.cpp |
| (22) exprtk_simple_example_18.cpp |
| (23) exprtk_simple_example_19.cpp |
| |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| [SECTION 29 - LANGUAGE STRUCTURE] |
| The following are the various language structures available within |
| ExprTk and their structural representations. |
| |
| (00) If Statement |
| (01) Else Statement |
| (02) Ternary Statement |
| (03) While Loop |
| (04) Repeat Until Loop |
| (05) For Loop |
| (06) Switch Statement |
| (07) Multi Subexpression Statement |
| (08) Multi Case-Consequent Statement |
| (09) Variable Definition Statement |
| (10) Vector Definition Statement |
| (11) String Definition Statement |
| (12) Range Statement |
| (13) Return Statement |
| |
| |
| (00) - If Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [if] ---> [(] ---> [condition] -+-> [,] -+ | |
| | | | | |
| | +---------------<---------------+ | | |
| | | | | |
| | | +------------------<------------------+ | |
| | | | | |
| | | +--> [consequent] ---> [,] ---> [alternative] ---> [)] | |
| | | | |
| | +--> [)] --+-> [{] ---> [expression*] ---> [}] --+ | |
| | | | | |
| | | +---------<----------+ | |
| | +----<-----+ | | |
| | | v | |
| | +--> [consequent] --> [;] -{*}-> [else-statement] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (01) - Else Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [else] -+-> [alternative] ---> [;] | |
| | | | |
| | +--> [{] ---> [expression*] ---> [}] | |
| | | | |
| | +--> [if-statement] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (02) - Ternary Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [condition] ---> [?] ---> [consequent] ---> [:] --+ | |
| | | | |
| | +------------------------<------------------------+ | |
| | | | |
| | +--> [alternative] --> [;] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (03) - While Loop |
| +-------------------------------------------------------------+ |
| | | |
| | [while] ---> [(] ---> [condition] ---> [)] ---+ | |
| | | | |
| | +----------------------<----------------------+ | |
| | | | |
| | +--> [{] ---> [expression*] ---> [}] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (04) - Repeat Until Loop |
| +-------------------------------------------------------------+ |
| | | |
| | [repeat] ---> [expression*] ---+ | |
| | | | |
| | +--------------<---------------+ | |
| | | | |
| | +--> [until] ---> [(] ---> [condition] --->[)] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (05) - For Loop |
| +-------------------------------------------------------------+ |
| | | |
| | [for] ---> [(] -+-> [initialise expression] --+--+ | |
| | | | | | |
| | +------------->---------------+ v | |
| | | | |
| | +-----------------------<------------------------+ | |
| | | | |
| | +--> [;] -+-> [condition] -+-> [;] ---+ | |
| | | | | | |
| | +------->--------+ v | |
| | | | |
| | +------------------<---------+--------+ | |
| | | | | |
| | +--> [increment expression] -+-> [)] --+ | |
| | | | |
| | +------------------<-------------------+ | |
| | | | |
| | +--> [{] ---> [expression*] ---> [}] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (06) - Switch Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [switch] ---> [{] ---+ | |
| | | | |
| | +---------<----------+-----------<-----------+ | |
| | | | | |
| | +--> [case] ---> [condition] ---> [:] ---+ | | |
| | | | | |
| | +-------------------<--------------------+ | | |
| | | | | |
| | +--> [consequent] ---> [;] --------->--------+ | |
| | | | | |
| | | | | |
| | +--> [default] ---> [consequent] ---> [;] ---+ | |
| | | | | |
| | +---------------------<----------------------+ | |
| | | | |
| | +--> [}] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (07) - Multi Subexpression Statement |
| +-------------------------------------------------------------+ |
| | | |
| | +--------------<---------------+ | |
| | | | | |
| | [~] ---> [{\(] -+-> [expression] -+-> [;\,] ---+ | |
| | | | |
| | +----------------<----------------+ | |
| | | | |
| | +--> [}\)] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (08) - Multi Case-Consequent Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [[*]] ---> [{] ---+ | |
| | | | |
| | +--------<--------+--------------<----------+ | |
| | | | | |
| | +--> [case] ---> [condition] ---> [:] ---+ | | |
| | | | | |
| | +-------------------<--------------------+ | | |
| | | | | |
| | +--> [consequent] ---> [;] ---+------>------+ | |
| | | | |
| | +--> [}] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (09) - Variable Definition Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [var] ---> [symbol] -+-> [:=] -+-> [expression] -+-> [;] | |
| | | | | | |
| | | +-----> [{}] -->--+ | |
| | | | | |
| | +------------->-------------+ | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (10) - Vector Definition Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [var] ---> [symbol] ---> [[] ---> [constant] ---> []] --+ | |
| | | | |
| | +---------------------------<---------------------------+ | |
| | | | |
| | | +--------->---------+ | |
| | | | | | |
| | +--> [:=] ---> [{] -+-+-> [expression] -+-> [}] ---> [;] | |
| | | | | |
| | +--<--- [,] <-----+ | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (11) - String Definition Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [var] --> [symbol] --> [:=] --> [str-expression] ---> [;] | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (12) - Range Statement |
| +-------------------------------------------------------------+ |
| | | |
| | +-------->--------+ | |
| | | | | |
| | [[] -+-> [expression] -+-> [:] -+-> [expression] -+--> []] | |
| | | | | |
| | +-------->--------+ | |
| | | |
| +-------------------------------------------------------------+ |
| |
| |
| (13) - Return Statement |
| +-------------------------------------------------------------+ |
| | | |
| | [return] ---> [[] -+-> [expression] -+-> []] ---> [;] | |
| | | | | |
| | +--<--- [,] <-----+ | |
| | | |
| +-------------------------------------------------------------+ |