0000 | C++ Mathematical Expression Toolkit Library Documentation 0001 | 0002 | Section 00 - Introduction 0003 | Section 01 - Capabilities 0004 | Section 02 - Example Expressions 0005 | Section 03 - Copyright Notice 0006 | Section 04 - Downloads & Updates 0007 | Section 05 - Installation 0008 | Section 06 - Compilation 0009 | Section 07 - Compiler Compatibility 0010 | Section 08 - Built-In Operations & Functions 0011 | Section 09 - Fundamental Types 0012 | Section 10 - Components 0013 | Section 11 - Compilation Options 0014 | Section 12 - Expression Structures 0015 | Section 13 - Variable, Vector & String Definition 0016 | Section 14 - Vector Processing 0017 | Section 15 - User Defined Functions 0018 | Section 16 - Expression Dependents 0019 | Section 17 - Hierarchies Of Symbol Tables 0020 | Section 18 - Unknown Unknowns 0021 | Section 19 - Enabling & Disabling Features 0022 | Section 20 - Expression Return Values 0023 | Section 21 - Compilation Errors 0024 | Section 22 - Runtime Library Packages 0025 | Section 23 - Helpers & Utils 0026 | Section 24 - Runtime Checks 0027 | Section 25 - Benchmarking 0028 | Section 26 - Exprtk Notes 0029 | Section 27 - Simple Exprtk Example 0030 | Section 28 - Build Options 0031 | Section 29 - Files 0032 | Section 30 - Language Structure 0033 | 0034 | 0035 | [SECTION 00 - INTRODUCTION] 0036 | The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple 0037 | to use, easy to integrate and extremely efficient run-time 0038 | mathematical expression parsing and evaluation engine. The parsing 0039 | engine supports numerous forms of functional and logic processing 0040 | semantics and is easily extensible. 0041 | 0042 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0043 | 0044 | [SECTION 01 - CAPABILITIES] 0045 | The ExprTk expression evaluator supports the following fundamental 0046 | arithmetic operations, functions and processes: 0047 | 0048 | (00) Types: Scalar, Vector, String 0049 | 0050 | (01) Basic operators: +, -, *, /, %, ^ 0051 | 0052 | (02) Assignment: :=, +=, -=, *=, /=, %= 0053 | 0054 | (03) Equalities & 0055 | Inequalities: =, ==, <>, !=, <, <=, >, >= 0056 | 0057 | (04) Logic operators: and, mand, mor, nand, nor, not, or, shl, shr, 0058 | xnor, xor, true, false 0059 | 0060 | (05) Functions: abs, avg, ceil, clamp, equal, erf, erfc, exp, 0061 | expm1, floor, frac, log, log10, log1p, log2, 0062 | logn, max, min, mul, ncdf, not_equal, root, 0063 | round, roundn, sgn, sqrt, sum, swap, trunc 0064 | 0065 | (06) Trigonometry: acos, acosh, asin, asinh, atan, atanh, atan2, 0066 | cos, cosh, cot, csc, sec, sin, sinc, sinh, 0067 | tan, tanh, hypot, rad2deg, deg2grad, deg2rad, 0068 | grad2deg 0069 | 0070 | (07) Control 0071 | structures: if-then-else, ternary conditional, switch-case, 0072 | return-statement 0073 | 0074 | (08) Loop statements: while, for, repeat-until, break, continue 0075 | 0076 | (09) String 0077 | processing: in, like, ilike, concatenation 0078 | 0079 | (10) Optimisations: constant-folding, simple strength reduction and 0080 | dead code elimination 0081 | 0082 | (11) Runtime checks: vector bounds, string bounds, loop iteration, 0083 | execution-time bounds and compilation process 0084 | checkpointing, assert statements 0085 | 0086 | (12) Calculus: numerical integration and differentiation 0087 | 0088 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0089 | 0090 | [SECTION 02 - EXAMPLE EXPRESSIONS] 0091 | The following is a short listing of infix format based mathematical 0092 | expressions that can be parsed and evaluated using the ExprTk library. 0093 | 0094 | (01) sqrt(1 - (3 / x^2)) 0095 | (02) clamp(-1, sin(2 * pi * x) + cos(y / 2 * pi), +1) 0096 | (03) sin(2.34e-3 * x) 0097 | (04) if(((x[2] + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z) 0098 | (05) inrange(-2,m,+2) == if(({-2 <= m} and [m <= +2]),1,0) 0099 | (06) ({1/1}*[1/2]+(1/3))-{1/4}^[1/5]+(1/6)-({1/7}+[1/8]*(1/9)) 0100 | (07) a * exp(2.2 / 3.3 * t) + c 0101 | (08) z := x + sin(2.567 * pi / y) 0102 | (09) u := 2.123 * {pi * z} / (w := x + cos(y / pi)) 0103 | (10) 2x + 3y + 4z + 5w == 2 * x + 3 * y + 4 * z + 5 * w 0104 | (11) 3(x + y) / 2.9 + 1.234e+12 == 3 * (x + y) / 2.9 + 1.234e+12 0105 | (12) (x + y)3.3 + 1 / 4.5 == [x + y] * 3.3 + 1 / 4.5 0106 | (13) (x + y[i])z + 1.1 / 2.7 == (x + y[i]) * z + 1.1 / 2.7 0107 | (14) (sin(x / pi) cos(2y) + 1) == (sin(x / pi) * cos(2 * y) + 1) 0108 | (15) 75x^17 + 25.1x^5 - 35x^4 - 15.2x^3 + 40x^2 - 15.3x + 1 0109 | (16) (avg(x,y) <= x + y ? x - y : x * y) + 2.345 * pi / x 0110 | (17) while (x <= 100) { x -= 1; } 0111 | (18) x <= 'abc123' and (y in 'AString') or ('1x2y3z' != z) 0112 | (19) ((x + 'abc') like '*123*') or ('a123b' ilike y) 0113 | (20) sgn(+1.2^3.4z / -5.6y) <= {-7.8^9 / -10.11x } 0114 | 0115 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0116 | 0117 | [SECTION 03 - COPYRIGHT NOTICE] 0118 | Free use of the C++ Mathematical Expression Toolkit Library is 0119 | permitted under the guidelines and in accordance with the most current 0120 | version of the MIT License. 0121 | 0122 | (1) https://www.opensource.org/licenses/MIT 0123 | (2) SPDX-License-Identifier: MIT 0124 | (3) SPDX-FileCopyrightText : Copyright (C) 1999-2024 Arash Partow 0125 | 0126 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0127 | 0128 | [SECTION 04 - DOWNLOADS & UPDATES] 0129 | The most recent version of the C++ Mathematical Expression Toolkit 0130 | Library including all updates and tests can be found at the following 0131 | locations: 0132 | 0133 | (1) Download: https://www.partow.net/programming/exprtk/index.html 0134 | (2) Mirror Repository: https://github.com/ArashPartow/exprtk 0135 | https://github.com/ArashPartow/exprtk-extras 0136 | 0137 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0138 | 0139 | [SECTION 05 - INSTALLATION] 0140 | The header file exprtk.hpp should be placed in a project or system 0141 | include path (e.g: /usr/include/). 0142 | 0143 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0144 | 0145 | [SECTION 06 - COMPILATION] 0146 | The ExprTk package contains the ExprTk header, a set of simple 0147 | examples and a benchmark and unit test suite. The following is a list 0148 | of commands to build the various components: 0149 | 0150 | (a) For a complete build: make clean all 0151 | (b) For a PGO build: make clean pgo 0152 | (c) To strip executables: make strip_bin 0153 | (d) Execute valgrind check: make valgrind_check 0154 | 0155 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0156 | 0157 | [SECTION 07 - COMPILER COMPATIBILITY] 0158 | ExprTk has been built error and warning free using the following set 0159 | of C++ compilers: 0160 | 0161 | (*) GNU Compiler Collection (3.5+) 0162 | (*) Clang/LLVM (1.1+) 0163 | (*) Microsoft Visual Studio C++ Compiler (7.1+) 0164 | (*) Intel C++ Compiler (8.x+) 0165 | (*) AMD Optimizing C++ Compiler (1.2+) 0166 | (*) Nvidia C++ Compiler (19.x+) 0167 | (*) PGI C++ (10.x+) 0168 | (*) Circle C++ (circa: b81c37d2bb227c) 0169 | (*) IBM XL C/C++ (9.x+) 0170 | (*) C++ Builder (XE4+) 0171 | 0172 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0173 | 0174 | [SECTION 08 - BUILT-IN OPERATIONS & FUNCTIONS] 0175 | 0176 | (0) Arithmetic & Assignment Operators 0177 | +----------+---------------------------------------------------------+ 0178 | | OPERATOR | DEFINITION | 0179 | +----------+---------------------------------------------------------+ 0180 | | + | Addition between x and y. (eg: x + y) | 0181 | +----------+---------------------------------------------------------+ 0182 | | - | Subtraction between x and y. (eg: x - y) | 0183 | +----------+---------------------------------------------------------+ 0184 | | * | Multiplication between x and y. (eg: x * y) | 0185 | +----------+---------------------------------------------------------+ 0186 | | / | Division between x and y. (eg: x / y) | 0187 | +----------+---------------------------------------------------------+ 0188 | | % | Modulus of x with respect to y. (eg: x % y) | 0189 | +----------+---------------------------------------------------------+ 0190 | | ^ | x to the power of y. (eg: x ^ y) | 0191 | +----------+---------------------------------------------------------+ 0192 | | := | Assign the value of x to y. Where y is either a variable| 0193 | | | or vector type. (eg: y := x) | 0194 | +----------+---------------------------------------------------------+ 0195 | | += | Increment x by the value of the expression on the right | 0196 | | | hand side. Where x is either a variable or vector type. | 0197 | | | (eg: x += abs(y - z)) | 0198 | +----------+---------------------------------------------------------+ 0199 | | -= | Decrement x by the value of the expression on the right | 0200 | | | hand side. Where x is either a variable or vector type. | 0201 | | | (eg: x[i] -= abs(y + z)) | 0202 | +----------+---------------------------------------------------------+ 0203 | | *= | Assign the multiplication of x by the value of the | 0204 | | | expression on the righthand side to x. Where x is either| 0205 | | | a variable or vector type. | 0206 | | | (eg: x *= abs(y / z)) | 0207 | +----------+---------------------------------------------------------+ 0208 | | /= | Assign the division of x by the value of the expression | 0209 | | | on the right-hand side to x. Where x is either a | 0210 | | | variable or vector type. (eg: x[i + j] /= abs(y * z)) | 0211 | +----------+---------------------------------------------------------+ 0212 | | %= | Assign x modulo the value of the expression on the right| 0213 | | | hand side to x. Where x is either a variable or vector | 0214 | | | type. (eg: x[2] %= y ^ 2) | 0215 | +----------+---------------------------------------------------------+ 0216 | 0217 | (1) Equalities & Inequalities 0218 | +----------+---------------------------------------------------------+ 0219 | | OPERATOR | DEFINITION | 0220 | +----------+---------------------------------------------------------+ 0221 | | == or = | True only if x is strictly equal to y. (eg: x == y) | 0222 | +----------+---------------------------------------------------------+ 0223 | | <> or != | True only if x does not equal y. (eg: x <> y or x != y) | 0224 | +----------+---------------------------------------------------------+ 0225 | | < | True only if x is less than y. (eg: x < y) | 0226 | +----------+---------------------------------------------------------+ 0227 | | <= | True only if x is less than or equal to y. (eg: x <= y) | 0228 | +----------+---------------------------------------------------------+ 0229 | | > | True only if x is greater than y. (eg: x > y) | 0230 | +----------+---------------------------------------------------------+ 0231 | | >= | True only if x greater than or equal to y. (eg: x >= y) | 0232 | +----------+---------------------------------------------------------+ 0233 | 0234 | (2) Boolean Operations 0235 | +----------+---------------------------------------------------------+ 0236 | | OPERATOR | DEFINITION | 0237 | +----------+---------------------------------------------------------+ 0238 | | true | True state or any value other than zero (typically 1). | 0239 | +----------+---------------------------------------------------------+ 0240 | | false | False state, value of exactly zero. | 0241 | +----------+---------------------------------------------------------+ 0242 | | and | Logical AND, True only if x and y are both true. | 0243 | | | (eg: x and y) | 0244 | +----------+---------------------------------------------------------+ 0245 | | mand | Multi-input logical AND, True only if all inputs are | 0246 | | | true. Left to right short-circuiting of expressions. | 0247 | | | (eg: mand(x > y, z < w, u or v, w and x)) | 0248 | +----------+---------------------------------------------------------+ 0249 | | mor | Multi-input logical OR, True if at least one of the | 0250 | | | inputs are true. Left to right short-circuiting of | 0251 | | | expressions. (eg: mor(x > y, z < w, u or v, w and x)) | 0252 | +----------+---------------------------------------------------------+ 0253 | | nand | Logical NAND, True only if either x or y is false. | 0254 | | | (eg: x nand y) | 0255 | +----------+---------------------------------------------------------+ 0256 | | nor | Logical NOR, True only if the result of x or y is false | 0257 | | | (eg: x nor y) | 0258 | +----------+---------------------------------------------------------+ 0259 | | not | Logical NOT, Negate the logical sense of the input. | 0260 | | | (eg: not(x and y) == x nand y) | 0261 | +----------+---------------------------------------------------------+ 0262 | | or | Logical OR, True if either x or y is true. (eg: x or y) | 0263 | +----------+---------------------------------------------------------+ 0264 | | xor | Logical XOR, True only if the logical states of x and y | 0265 | | | differ. (eg: x xor y) | 0266 | +----------+---------------------------------------------------------+ 0267 | | xnor | Logical XNOR, True iff the biconditional of x and y is | 0268 | | | satisfied. (eg: x xnor y) | 0269 | +----------+---------------------------------------------------------+ 0270 | | & | Similar to AND but with left to right expression short | 0271 | | | circuiting optimisation. (eg: (x & y) == (y and x)) | 0272 | +----------+---------------------------------------------------------+ 0273 | | | | Similar to OR but with left to right expression short | 0274 | | | circuiting optimisation. (eg: (x | y) == (y or x)) | 0275 | +----------+---------------------------------------------------------+ 0276 | 0277 | (3) General Purpose Functions 0278 | +----------+---------------------------------------------------------+ 0279 | | FUNCTION | DEFINITION | 0280 | +----------+---------------------------------------------------------+ 0281 | | abs | Absolute value of x. (eg: abs(x)) | 0282 | +----------+---------------------------------------------------------+ 0283 | | avg | Average of all the inputs. | 0284 | | | (eg: avg(x,y,z,w,u,v) == (x + y + z + w + u + v) / 6) | 0285 | +----------+---------------------------------------------------------+ 0286 | | ceil | Smallest integer that is greater than or equal to x. | 0287 | +----------+---------------------------------------------------------+ 0288 | | clamp | Clamp x in range between r0 and r1, where r0 < r1. | 0289 | | | (eg: clamp(r0,x,r1)) | 0290 | +----------+---------------------------------------------------------+ 0291 | | equal | Equality test between x and y using normalised epsilon | 0292 | +----------+---------------------------------------------------------+ 0293 | | erf | Error function of x. (eg: erf(x)) | 0294 | +----------+---------------------------------------------------------+ 0295 | | erfc | Complimentary error function of x. (eg: erfc(x)) | 0296 | +----------+---------------------------------------------------------+ 0297 | | exp | e to the power of x. (eg: exp(x)) | 0298 | +----------+---------------------------------------------------------+ 0299 | | expm1 | e to the power of x minus 1, where x is very small. | 0300 | | | (eg: expm1(x)) | 0301 | +----------+---------------------------------------------------------+ 0302 | | floor | Largest integer that is less than or equal to x. | 0303 | | | (eg: floor(x)) | 0304 | +----------+---------------------------------------------------------+ 0305 | | frac | Fractional portion of x. (eg: frac(x)) | 0306 | +----------+---------------------------------------------------------+ 0307 | | hypot | Hypotenuse of x and y (eg: hypot(x,y) = sqrt(x*x + y*y))| 0308 | +----------+---------------------------------------------------------+ 0309 | | iclamp | Inverse-clamp x outside of the range r0 and r1. Where | 0310 | | | r0 < r1. If x is within the range it will snap to the | 0311 | | | closest bound. (eg: iclamp(r0,x,r1) | 0312 | +----------+---------------------------------------------------------+ 0313 | | inrange | In-range returns 'true' when x is within the range r0 | 0314 | | | and r1. Where r0 < r1. (eg: inrange(r0,x,r1) | 0315 | +----------+---------------------------------------------------------+ 0316 | | log | Natural logarithm of x. (eg: log(x)) | 0317 | +----------+---------------------------------------------------------+ 0318 | | log10 | Base 10 logarithm of x. (eg: log10(x)) | 0319 | +----------+---------------------------------------------------------+ 0320 | | log1p | Natural logarithm of 1 + x, where x is very small. | 0321 | | | (eg: log1p(x)) | 0322 | +----------+---------------------------------------------------------+ 0323 | | log2 | Base 2 logarithm of x. (eg: log2(x)) | 0324 | +----------+---------------------------------------------------------+ 0325 | | logn | Base N logarithm of x. where n is a positive integer. | 0326 | | | (eg: logn(x,8)) | 0327 | +----------+---------------------------------------------------------+ 0328 | | max | Largest value of all the inputs. (eg: max(x,y,z,w,u,v)) | 0329 | +----------+---------------------------------------------------------+ 0330 | | min | Smallest value of all the inputs. (eg: min(x,y,z,w,u)) | 0331 | +----------+---------------------------------------------------------+ 0332 | | mul | Product of all the inputs. | 0333 | | | (eg: mul(x,y,z,w,u,v,t) == (x * y * z * w * u * v * t)) | 0334 | +----------+---------------------------------------------------------+ 0335 | | ncdf | Normal cumulative distribution function. (eg: ncdf(x)) | 0336 | +----------+---------------------------------------------------------+ 0337 | | not_equal| Not-equal test between x and y using normalised epsilon | 0338 | +----------+---------------------------------------------------------+ 0339 | | pow | x to the power of y. (eg: pow(x,y) == x ^ y) | 0340 | +----------+---------------------------------------------------------+ 0341 | | root | Nth-Root of x. where n is a positive integer. | 0342 | | | (eg: root(x,3) == x^(1/3)) | 0343 | +----------+---------------------------------------------------------+ 0344 | | round | Round x to the nearest integer. (eg: round(x)) | 0345 | +----------+---------------------------------------------------------+ 0346 | | roundn | Round x to n decimal places (eg: roundn(x,3)) | 0347 | | | where n > 0 and is an integer. | 0348 | | | (eg: roundn(1.2345678,4) == 1.2346) | 0349 | +----------+---------------------------------------------------------+ 0350 | | sgn | Sign of x, -1 where x < 0, +1 where x > 0, else zero. | 0351 | | | (eg: sgn(x)) | 0352 | +----------+---------------------------------------------------------+ 0353 | | sqrt | Square root of x, where x >= 0. (eg: sqrt(x)) | 0354 | +----------+---------------------------------------------------------+ 0355 | | sum | Sum of all the inputs. | 0356 | | | (eg: sum(x,y,z,w,u,v,t) == (x + y + z + w + u + v + t)) | 0357 | +----------+---------------------------------------------------------+ 0358 | | swap | Swap the values of the variables x and y and return the | 0359 | | <=> | current value of y. (eg: swap(x,y) or x <=> y) | 0360 | +----------+---------------------------------------------------------+ 0361 | | trunc | Integer portion of x. (eg: trunc(x)) | 0362 | +----------+---------------------------------------------------------+ 0363 | 0364 | (4) Trigonometry Functions 0365 | +----------+---------------------------------------------------------+ 0366 | | FUNCTION | DEFINITION | 0367 | +----------+---------------------------------------------------------+ 0368 | | acos | Arc cosine of x expressed in radians. Interval [-1,+1] | 0369 | | | (eg: acos(x)) | 0370 | +----------+---------------------------------------------------------+ 0371 | | acosh | Inverse hyperbolic cosine of x expressed in radians. | 0372 | | | (eg: acosh(x)) | 0373 | +----------+---------------------------------------------------------+ 0374 | | asin | Arc sine of x expressed in radians. Interval [-1,+1] | 0375 | | | (eg: asin(x)) | 0376 | +----------+---------------------------------------------------------+ 0377 | | asinh | Inverse hyperbolic sine of x expressed in radians. | 0378 | | | (eg: asinh(x)) | 0379 | +----------+---------------------------------------------------------+ 0380 | | atan | Arc tangent of x expressed in radians. Interval [-1,+1] | 0381 | | | (eg: atan(x)) | 0382 | +----------+---------------------------------------------------------+ 0383 | | atan2 | Arc tangent of (x / y) expressed in radians. [-pi,+pi] | 0384 | | | eg: atan2(x,y) | 0385 | +----------+---------------------------------------------------------+ 0386 | | atanh | Inverse hyperbolic tangent of x expressed in radians. | 0387 | | | (eg: atanh(x)) | 0388 | +----------+---------------------------------------------------------+ 0389 | | cos | Cosine of x. (eg: cos(x)) | 0390 | +----------+---------------------------------------------------------+ 0391 | | cosh | Hyperbolic cosine of x. (eg: cosh(x)) | 0392 | +----------+---------------------------------------------------------+ 0393 | | cot | Cotangent of x. (eg: cot(x)) | 0394 | +----------+---------------------------------------------------------+ 0395 | | csc | Cosecant of x. (eg: csc(x)) | 0396 | +----------+---------------------------------------------------------+ 0397 | | sec | Secant of x. (eg: sec(x)) | 0398 | +----------+---------------------------------------------------------+ 0399 | | sin | Sine of x. (eg: sin(x)) | 0400 | +----------+---------------------------------------------------------+ 0401 | | sinc | Sine cardinal of x. (eg: sinc(x)) | 0402 | +----------+---------------------------------------------------------+ 0403 | | sinh | Hyperbolic sine of x. (eg: sinh(x)) | 0404 | +----------+---------------------------------------------------------+ 0405 | | tan | Tangent of x. (eg: tan(x)) | 0406 | +----------+---------------------------------------------------------+ 0407 | | tanh | Hyperbolic tangent of x. (eg: tanh(x)) | 0408 | +----------+---------------------------------------------------------+ 0409 | | deg2rad | Convert x from degrees to radians. (eg: deg2rad(x)) | 0410 | +----------+---------------------------------------------------------+ 0411 | | deg2grad | Convert x from degrees to gradians. (eg: deg2grad(x)) | 0412 | +----------+---------------------------------------------------------+ 0413 | | rad2deg | Convert x from radians to degrees. (eg: rad2deg(x)) | 0414 | +----------+---------------------------------------------------------+ 0415 | | grad2deg | Convert x from gradians to degrees. (eg: grad2deg(x)) | 0416 | +----------+---------------------------------------------------------+ 0417 | 0418 | (5) String Processing 0419 | +----------+---------------------------------------------------------+ 0420 | | FUNCTION | DEFINITION | 0421 | +----------+---------------------------------------------------------+ 0422 | | = , == | All common equality/inequality operators are applicable | 0423 | | !=, <> | to strings and are applied in a case sensitive manner. | 0424 | | <=, >= | In the following example x, y and z are of type string. | 0425 | | < , > | (eg: not((x <= 'AbC') and ('1x2y3z' <> y)) or (z == x) | 0426 | +----------+---------------------------------------------------------+ 0427 | | in | True only if x is a substring of y. | 0428 | | | (eg: x in y or 'abc' in 'abcdefgh') | 0429 | +----------+---------------------------------------------------------+ 0430 | | like | True only if the string x matches the pattern y. | 0431 | | | Available wildcard characters are '*' and '?' denoting | 0432 | | | zero or more and zero or one matches respectively. | 0433 | | | (eg: x like y or 'abcdefgh' like 'a?d*h') | 0434 | +----------+---------------------------------------------------------+ 0435 | | ilike | True only if the string x matches the pattern y in a | 0436 | | | case insensitive manner. Available wildcard characters | 0437 | | | are '*' and '?' denoting zero or more and zero or one | 0438 | | | matches respectively. | 0439 | | | (eg: x ilike y or 'a1B2c3D4e5F6g7H' ilike 'a?d*h') | 0440 | +----------+---------------------------------------------------------+ 0441 | | [r0:r1] | The closed interval[r0,r1] of the specified string. | 0442 | | | eg: Given a string x with a value of 'abcdefgh' then: | 0443 | | | 1. x[1:4] == 'bcde' | 0444 | | | 2. x[ :4] == x[:8 / 2] == 'abcde' | 0445 | | | 3. x[2 + 1: ] == x[3:] =='defgh' | 0446 | | | 4. x[ : ] == x[:] == 'abcdefgh' | 0447 | | | 5. x[4/2:3+1] == x[2:4] == 'cde' | 0448 | | | | 0449 | | | Note: Both r0 and r1 are assumed to be integers, where | 0450 | | | r0 <= r1. They may also be the result of an expression, | 0451 | | | in the event they have fractional components truncation | 0452 | | | shall be performed. (eg: 1.67 --> 1) | 0453 | +----------+---------------------------------------------------------+ 0454 | | := | Assign the value of x to y. Where y is a mutable string | 0455 | | | or string range and x is either a string or a string | 0456 | | | range. eg: | 0457 | | | 1. y := x | 0458 | | | 2. y := 'abc' | 0459 | | | 3. y := x[:i + j] | 0460 | | | 4. y := '0123456789'[2:7] | 0461 | | | 5. y := '0123456789'[2i + 1:7] | 0462 | | | 6. y := (x := '0123456789'[2:7]) | 0463 | | | 7. y[i:j] := x | 0464 | | | 8. y[i:j] := (x + 'abcdefg'[8 / 4:5])[m:n] | 0465 | | | | 0466 | | | Note: For options 7 and 8 the shorter of the two ranges | 0467 | | | will denote the number characters that are to be copied.| 0468 | +----------+---------------------------------------------------------+ 0469 | | + | Concatenation of x and y. Where x and y are strings or | 0470 | | | string ranges. eg | 0471 | | | 1. x + y | 0472 | | | 2. x + 'abc' | 0473 | | | 3. x + y[:i + j] | 0474 | | | 4. x[i:j] + y[2:3] + '0123456789'[2:7] | 0475 | | | 5. 'abc' + x + y | 0476 | | | 6. 'abc' + '1234567' | 0477 | | | 7. (x + 'a1B2c3D4' + y)[i:2j] | 0478 | +----------+---------------------------------------------------------+ 0479 | | += | Append to x the value of y. Where x is a mutable string | 0480 | | | and y is either a string or a string range. eg: | 0481 | | | 1. x += y | 0482 | | | 2. x += 'abc' | 0483 | | | 3. x += y[:i + j] + 'abc' | 0484 | | | 4. x += '0123456789'[2:7] | 0485 | +----------+---------------------------------------------------------+ 0486 | | <=> | Swap the values of x and y. Where x and y are mutable | 0487 | | | strings. (eg: x <=> y) | 0488 | +----------+---------------------------------------------------------+ 0489 | | [] | The string size operator returns the size of the string | 0490 | | | being actioned. | 0491 | | | eg: | 0492 | | | 1. 'abc'[] == 3 | 0493 | | | 2. var max_str_length := max(s0[], s1[], s2[], s3[]) | 0494 | | | 3. ('abc' + 'd')[] == 6 | 0495 | | | 4. (('abc' + 'xyz')[1:4])[] == 4 | 0496 | +----------+---------------------------------------------------------+ 0497 | 0498 | (6) Control Structures 0499 | +----------+---------------------------------------------------------+ 0500 | |STRUCTURE | DEFINITION | 0501 | +----------+---------------------------------------------------------+ 0502 | | if | If x is true then return y else return z. | 0503 | | | eg: | 0504 | | | 1. if (x, y, z) | 0505 | | | 2. if ((x + 1) > 2y, z + 1, w / v) | 0506 | | | 3. if (x > y) z; | 0507 | | | 4. if (x <= 2*y) { z + w }; | 0508 | +----------+---------------------------------------------------------+ 0509 | | if-else | The if-else/else-if statement. Subject to the condition | 0510 | | | branch the statement will return either the value of the| 0511 | | | consequent or the alternative branch. | 0512 | | | eg: | 0513 | | | 1. if (x > y) z; else w; | 0514 | | | 2. if (x > y) z; else if (w != u) v; | 0515 | | | 3. if (x < y) { z; w + 1; } else u; | 0516 | | | 4. if ((x != y) and (z > w)) | 0517 | | | { | 0518 | | | y := sin(x) / u; | 0519 | | | z := w + 1; | 0520 | | | } | 0521 | | | else if (x > (z + 1)) | 0522 | | | { | 0523 | | | w := abs (x - y) + z; | 0524 | | | u := (x + 1) > 2y ? 2u : 3u; | 0525 | | | } | 0526 | +----------+---------------------------------------------------------+ 0527 | | switch | The first true case condition that is encountered will | 0528 | | | determine the result of the switch. If none of the case | 0529 | | | conditions hold true, the default action is assumed as | 0530 | | | the final return value. This is sometimes also known as | 0531 | | | a multi-way branch mechanism. | 0532 | | | eg: | 0533 | | | switch | 0534 | | | { | 0535 | | | case x > (y + z) : 2 * x / abs(y - z); | 0536 | | | case x < 3 : sin(x + y); | 0537 | | | default : 1 + x; | 0538 | | | } | 0539 | +----------+---------------------------------------------------------+ 0540 | | while | The structure will repeatedly evaluate the internal | 0541 | | | statement(s) 'while' the condition is true. The final | 0542 | | | statement in the final iteration shall be used as the | 0543 | | | return value of the loop. | 0544 | | | eg: | 0545 | | | while ((x -= 1) > 0) | 0546 | | | { | 0547 | | | y := x + z; | 0548 | | | w := u + y; | 0549 | | | } | 0550 | +----------+---------------------------------------------------------+ 0551 | | repeat/ | The structure will repeatedly evaluate the internal | 0552 | | until | statement(s) 'until' the condition is true. The final | 0553 | | | statement in the final iteration shall be used as the | 0554 | | | return value of the loop. | 0555 | | | eg: | 0556 | | | repeat | 0557 | | | y := x + z; | 0558 | | | w := u + y; | 0559 | | | until ((x += 1) > 100) | 0560 | +----------+---------------------------------------------------------+ 0561 | | for | The structure will repeatedly evaluate the internal | 0562 | | | statement(s) while the condition is true. On each loop | 0563 | | | iteration, an 'incrementing' expression is evaluated. | 0564 | | | The conditional is mandatory whereas the initialiser | 0565 | | | and incrementing expressions are optional. | 0566 | | | eg: | 0567 | | | for (var x := 0; (x < n) and (x != y); x += 1) | 0568 | | | { | 0569 | | | y := y + x / 2 - z; | 0570 | | | w := u + y; | 0571 | | | } | 0572 | +----------+---------------------------------------------------------+ 0573 | | break | Break terminates the execution of the nearest enclosed | 0574 | | break[] | loop, allowing for the execution to continue on external| 0575 | | | to the loop. The default break statement will set the | 0576 | | | return value of the loop to NaN, where as the return | 0577 | | | based form will set the value to that of the break | 0578 | | | expression. | 0579 | | | eg: | 0580 | | | while ((i += 1) < 10) | 0581 | | | { | 0582 | | | if (i < 5) | 0583 | | | j -= i + 2; | 0584 | | | else if (i % 2 == 0) | 0585 | | | break; | 0586 | | | else | 0587 | | | break[2i + 3]; | 0588 | | | } | 0589 | +----------+---------------------------------------------------------+ 0590 | | continue | Continue results in the remaining portion of the nearest| 0591 | | | enclosing loop body to be skipped. | 0592 | | | eg: | 0593 | | | for (var i := 0; i < 10; i += 1) | 0594 | | | { | 0595 | | | if (i < 5) | 0596 | | | continue; | 0597 | | | j -= i + 2; | 0598 | | | } | 0599 | +----------+---------------------------------------------------------+ 0600 | | return | Return immediately from within the current expression. | 0601 | | | With the option of passing back a variable number of | 0602 | | | values (scalar, vector or string). eg: | 0603 | | | 1. return [1]; | 0604 | | | 2. return [x, 'abx']; | 0605 | | | 3. return [x, x + y,'abx']; | 0606 | | | 4. return []; | 0607 | | | 5. if (x < y) | 0608 | | | return [x, x - y, 'result-set1', 123.456]; | 0609 | | | else | 0610 | | | return [y, x + y, 'result-set2']; | 0611 | +----------+---------------------------------------------------------+ 0612 | | ?: | Ternary conditional statement, similar to that of the | 0613 | | | above denoted if-statement. | 0614 | | | eg: | 0615 | | | 1. x ? y : z | 0616 | | | 2. x + 1 > 2y ? z + 1 : (w / v) | 0617 | | | 3. min(x,y) > z ? (x < y + 1) ? x : y : (w * v) | 0618 | +----------+---------------------------------------------------------+ 0619 | | ~ | Evaluate each sub-expression, then return as the result | 0620 | | | the value of the last sub-expression. This is sometimes | 0621 | | | known as multiple sequence point evaluation. | 0622 | | | eg: | 0623 | | | ~(i := x + 1, j := y / z, k := sin(w/u)) == (sin(w/u))) | 0624 | | | ~{i := x + 1; j := y / z; k := sin(w/u)} == (sin(w/u))) | 0625 | +----------+---------------------------------------------------------+ 0626 | | [*] | Evaluate any consequent for which its case statement is | 0627 | | | true. The return value will be either zero or the result| 0628 | | | of the last consequent to have been evaluated. | 0629 | | | eg: | 0630 | | | [*] | 0631 | | | { | 0632 | | | case (x + 1) > (y - 2) : x := z / 2 + sin(y / pi); | 0633 | | | case (x + 2) < abs(y + 3) : w / 4 + min(5y,9); | 0634 | | | case (x + 3) == (y * 4) : y := abs(z / 6) + 7y; | 0635 | | | } | 0636 | +----------+---------------------------------------------------------+ 0637 | | [] | The vector size operator returns the size of the vector | 0638 | | | being actioned. | 0639 | | | eg: | 0640 | | | 1. v[] | 0641 | | | 2. max_size := max(v0[],v1[],v2[],v3[]) | 0642 | +----------+---------------------------------------------------------+ 0643 | 0644 | Note01: In the tables above, the symbols x, y, z, w, u and v where 0645 | appropriate may represent any of one the following: 0646 | 0647 | 1. Literal numeric/string value 0648 | 2. A variable 0649 | 3. A vector element 0650 | 4. A vector 0651 | 5. A string 0652 | 6. An expression comprised of [1], [2] or [3] (eg: 2 + x / vec[3]) 0653 | 0654 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0655 | 0656 | [SECTION 09 - FUNDAMENTAL TYPES] 0657 | ExprTk supports three fundamental types which can be used freely in 0658 | expressions. The types are as follows: 0659 | 0660 | (1) Scalar 0661 | (2) Vector 0662 | (3) String 0663 | 0664 | 0665 | (1) Scalar Type 0666 | The scalar type is a singular numeric value. The underlying type is 0667 | that used to specialise the ExprTk components (float, double, long 0668 | double, MPFR et al). 0669 | 0670 | 0671 | (2) Vector Type 0672 | The vector type is a fixed size sequence of contiguous scalar values. 0673 | A vector can be indexed resulting in a scalar value. Operations 0674 | between a vector and scalar will result in a vector with a size equal 0675 | to that of the original vector, whereas operations between vectors 0676 | will result in a vector of size equal to that of the smaller of the 0677 | two. In both mentioned cases, the operations will occur element-wise. 0678 | 0679 | 0680 | (3) String Type 0681 | The string type is a variable length sequence of 8-bit chars. Strings 0682 | can be assigned and concatenated to one another, they can also be 0683 | manipulated via sub-ranges using the range definition syntax. Strings 0684 | however can not interact with scalar or vector types. 0685 | 0686 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0687 | 0688 | [SECTION 10 - COMPONENTS] 0689 | There are three primary components, that are specialised upon a given 0690 | numeric type, which make up the core of ExprTk. The components are as 0691 | follows: 0692 | 0693 | (1) Symbol Table exprtk::symbol_table<NumericType> 0694 | (2) Expression exprtk::expression<NumericType> 0695 | (3) Parser exprtk::parser<NumericType> 0696 | 0697 | 0698 | (1) Symbol Table 0699 | A structure that is used to store references to variables, constants 0700 | and functions that are to be used within expressions. Furthermore in 0701 | the context of composited recursive functions the symbol table can 0702 | also be thought of as a simple representation of a stack specific for 0703 | the expression(s) that reference it. The following is a list of the 0704 | types a symbol table can handle: 0705 | 0706 | (a) Numeric variables 0707 | (b) Numeric constants 0708 | (c) Numeric vector elements 0709 | (d) String variables 0710 | (e) String constants 0711 | (f) Functions 0712 | (g) Vararg functions 0713 | 0714 | 0715 | During the compilation process if an expression is found to require 0716 | any of the elements noted above, the expression's associated 0717 | symbol_table will be queried for the element and if present a 0718 | reference to the element will be embedded within the expression's AST. 0719 | This allows for the original element to be modified independently of 0720 | the expression instance and to also allow the expression to be 0721 | evaluated using the current value of the element. 0722 | 0723 | The example below demonstrates the relationship between variables, 0724 | symbol_table and expression. Note the variables are modified as they 0725 | normally would in a program, and when the expression is evaluated the 0726 | current values assigned to the variables shall be used. 0727 | 0728 | typedef exprtk::symbol_table<double> symbol_table_t; 0729 | typedef exprtk::expression<double> expression_t; 0730 | typedef exprtk::parser<double> parser_t; 0731 | 0732 | double x = 0; 0733 | double y = 0; 0734 | 0735 | symbol_table_t symbol_table; 0736 | expression_t expression; 0737 | parser_t parser; 0738 | 0739 | std::string expression_string = "x * y + 3" 0740 | 0741 | symbol_table.add_variable("x",x); 0742 | symbol_table.add_variable("y",y); 0743 | 0744 | expression.register_symbol_table(symbol_table); 0745 | 0746 | parser.compile(expression_string,expression); 0747 | 0748 | x = 1.0; 0749 | y = 2.0; 0750 | expression.value(); // 1 * 2 + 3 0751 | 0752 | x = 3.7; 0753 | expression.value(); // 3.7 * 2 + 3 0754 | 0755 | y = -9.0; 0756 | expression.value(); // 3.7 * -9 + 3 0757 | 0758 | // 'x * -9 + 3' for x in range of [0,100) in steps of 0.0001 0759 | for (x = 0.0; x < 100.0; x += 0.0001) 0760 | { 0761 | expression.value(); // x * -9 + 3 0762 | } 0763 | 0764 | 0765 | Note02: Any variable reference provided to a given symbol_table 0766 | instance, must have a lifetime at least as long as the lifetime of the 0767 | symbol_table instance. In the event the variable reference is 0768 | invalidated before the symbol_table or any dependent expression 0769 | instances have been destructed, then any associated expression 0770 | evaluations or variable referencing via the symbol_table instance will 0771 | result in undefined behaviour. 0772 | 0773 | The following bit of code instantiates a symbol_table and expression 0774 | instance, then proceeds to demonstrate various ways in which 0775 | references to variables can be added to the symbol_table, and how 0776 | those references are subsequently invalidated resulting in various 0777 | forms of undefined behaviour. 0778 | 0779 | typedef exprtk::symbol_table<double> symbol_table_t; 0780 | typedef exprtk::expression<double> expression_t; 0781 | 0782 | symbol_table_t symbol_table; 0783 | expression_t expression; 0784 | 0785 | std::deque<double > y {1.1, 2.2, 3.3}; 0786 | std::vector<double> z {4.4, 5.5, 6.6}; 0787 | double* w = new double(123.456); 0788 | 0789 | { 0790 | double x = 123.4567; 0791 | symbol_table.add_variable("x", x); 0792 | } // Reference to variable x has been invalidated 0793 | 0794 | 0795 | symbol_table.add_variable("y", y.back()); 0796 | 0797 | y.pop_back(); // Reference to variable y has been invalidated 0798 | 0799 | 0800 | symbol_table.add_variable("z", z.front()); 0801 | 0802 | z.erase(z.begin()); 0803 | // Reference to variable z has been invalidated 0804 | 0805 | symbol_table.add_variable("w", *w); 0806 | 0807 | delete w; // Reference to variable w has been invalidated 0808 | 0809 | const std::string expression_string = "x + y / z * w" 0810 | 0811 | // Compilation of expression will succeed 0812 | parser.compile(expression_string,expression); 0813 | 0814 | expression.value(); 0815 | // Evaluation will result in undefined behaviour 0816 | // due to 'x' and 'w' having been destroyed. 0817 | 0818 | symbol_table.get_variable("x")->ref() = 135.791; 0819 | // Assignment will result in undefined behaviour 0820 | 0821 | 0822 | A compiled expression that references variables from a symbol_table is 0823 | dependent on that symbol_table instance and the variables it holds 0824 | being valid. 0825 | 0826 | typedef exprtk::symbol_table<double> symbol_table_t; 0827 | typedef exprtk::expression<double> expression_t; 0828 | 0829 | symbol_table_t symbol_table; 0830 | expression_t expression; 0831 | 0832 | double x = 123.456; 0833 | 0834 | symbol_table.add_variable("x", x); 0835 | 0836 | const std::string expression_string = "(x + 1) / 2" 0837 | 0838 | // Compilation of the expression will succeed 0839 | parser.compile(expression_string,expression); 0840 | 0841 | // Clear all variables from symbol_table 0842 | symbol_table.clear(); 0843 | 0844 | expression.value(); 0845 | // Evaluation will result in undefined behaviour 0846 | // because the reference to 'x' having been destroyed 0847 | // during the clearing of the symbol_table 0848 | 0849 | 0850 | In the above example, an expression is compiled that references 0851 | variable "x". As part of the compilation process the node holding the 0852 | variable "x" is obtained from the symbol_table and embedded in the AST 0853 | of the expression - in short the expression is now referencing the 0854 | node that holds the variable "x". The following diagram depicts the 0855 | dependencies between the variable x, the symbol table and the 0856 | expression: 0857 | 0858 | +--[Symbol Table]--+ 0859 | | | 0860 | | +- ------+ | 0861 | | | x-node | | 0862 | | +-----A--+ | +--[Expression]--+ 0863 | +---|---|----------+ | +---------+ | 0864 | v | | | A.S.T | | 0865 | | +--------<--------[.] | | 0866 | +-----+ | +---------+ | 0867 | | +----------------+ 0868 | +-v-[variable]---+ 0869 | | x: 123.456 | 0870 | +----------------+ 0871 | 0872 | 0873 | When the clear method is called on the symbol table the X-Node is 0874 | destroyed, so now the expression is referencing a node that has been 0875 | destroyed. From this point onwards any attempts to reference the 0876 | expression instance will result in undefined behaviour. Simply put the 0877 | above example violates the requirement that the lifetime of any 0878 | objects referenced by expressions should exceed the lifetime of the 0879 | expression instance. 0880 | 0881 | typedef exprtk::symbol_table<double> symbol_table_t; 0882 | typedef exprtk::expression<double> expression_t; 0883 | 0884 | symbol_table_t symbol_table; 0885 | expression_t expression; 0886 | 0887 | double x = 123.456; 0888 | 0889 | symbol_table.add_variable("x", x); 0890 | 0891 | const std::string expression_string = "(x + 1) / 2" 0892 | 0893 | // Compilation of the expression will succeed 0894 | parser.compile(expression_string,expression); 0895 | 0896 | expression.value(); 0897 | 0898 | // Release the expression and its dependents 0899 | expression.release(); 0900 | 0901 | // Clear all variables from symbol_table 0902 | symbol_table.clear(); 0903 | 0904 | expression.value(); 0905 | // Will return null_node value of NaN 0906 | 0907 | 0908 | In the above example the expression is released before the associated 0909 | symbol_table is cleared of its variables, which resolves the undefined 0910 | behaviour issue noted in the previous example. 0911 | 0912 | Note03: It is possible to register multiple symbol_tables with a 0913 | single expression object. In the event an expression has multiple 0914 | symbol tables, and where there exists conflicts between symbols, the 0915 | compilation stage will resolve the conflicts based on the order of 0916 | registration of the symbol_tables to the expression. For a more 0917 | expansive discussion please review section [17 - Hierarchies Of Symbol 0918 | Tables] 0919 | 0920 | typedef exprtk::symbol_table<double> symbol_table_t; 0921 | typedef exprtk::expression<double> expression_t; 0922 | typedef exprtk::parser<double> parser_t; 0923 | 0924 | symbol_table_t symbol_table0; 0925 | symbol_table_t symbol_table1; 0926 | 0927 | expression_t expression; 0928 | parser_t parser; 0929 | 0930 | double x0 = 123.0; 0931 | double x1 = 678.0; 0932 | 0933 | std::string expression_string = "x + 1" 0934 | 0935 | symbol_table0.add_variable("x",x0); 0936 | symbol_table1.add_variable("x",x1); 0937 | 0938 | expression.register_symbol_table(symbol_table0); 0939 | expression.register_symbol_table(symbol_table1); 0940 | 0941 | parser.compile(expression_string,expression); 0942 | 0943 | expression.value(); // 123 + 1 0944 | 0945 | 0946 | The symbol table supports adding references to external instances of 0947 | types that can be accessed within expressions via the following 0948 | methods: 0949 | 0950 | 1. bool add_variable (const std::string& name, scalar_t& ) 0951 | 2. bool add_constant (const std::string& name, const scalar_t& ) 0952 | 3. bool add_stringvar (const std::string& name, std::string& ) 0953 | 4. bool add_vector (const std::string& name, vector_type& ) 0954 | 5. bool add_function (const std::string& name, function_t& ) 0955 | 6. bool create_stringvar(const std::string& name,const std::string&) 0956 | 7. bool create_variable (const std::string& name, const T& ) 0957 | 0958 | 0959 | Note04: The 'vector' type must be comprised from a contiguous array of 0960 | scalars with a size that is larger than zero. The vector type itself 0961 | can be any one of the following: 0962 | 0963 | 1. std::vector<scalar_t> 0964 | 2. scalar_t(&v)[N] 0965 | 3. scalar_t* and array size 0966 | 4. exprtk::vector_view<scalar_t> 0967 | 0968 | 0969 | When registering a variable, vector, string or function with an 0970 | instance of a symbol_table, the call to 'add_...' may fail and return 0971 | a false result due to one or more of the following reasons: 0972 | 0973 | 1. Variable name contains invalid characters or is ill-formed 0974 | 2. Variable name conflicts with a reserved word (eg: 'while') 0975 | 3. Variable name conflicts with a previously registered variable 0976 | 4. A vector of size (length) zero is being registered 0977 | 5. A free function exceeding fifteen parameters is being registered 0978 | 6. The symbol_table instance is in an invalid state 0979 | 0980 | 0981 | Note05: The symbol_table has a method called clear, which when invoked 0982 | will clear all variables, vectors, strings and functions registered 0983 | with the symbol_table instance. If this method is to be called, then 0984 | one must make sure that all compiled expression instances that 0985 | reference variables belonging to that symbol_table instance are 0986 | released (aka call release method on expression) before calling the 0987 | clear method on the symbol_table instance, otherwise undefined 0988 | behaviours will occur. 0989 | 0990 | A further property of symbol tables is that they can be classified at 0991 | instantiation as either being mutable (by default) or immutable. This 0992 | property determines if variables, vectors or strings registered with 0993 | the symbol table can undergo modifications within expressions that 0994 | reference them. The following demonstrates construction of an 0995 | immutable symbol table instance: 0996 | 0997 | symbol_table_t immutable_symbol_table 0998 | (symbol_table_t::symtab_mutability_type::e_immutable); 0999 | 1000 | 1001 | When a symbol table, that has been constructed as being immutable, is 1002 | registered with an expression, any statements in the expression string 1003 | that modify the variables that are managed by the immutable symbol 1004 | table will result in a compilation error. The operations that trigger 1005 | the mutability constraint are the following assignment operators: 1006 | 1007 | 1. Assignment: := 1008 | 2. Assign operation: +=, -=, *=, /= , %= 1009 | 1010 | const std::string expression_str = "x += x + 123.456" 1011 | 1012 | symbol_table_t immutable_symbol_table 1013 | (symbol_table_t::symtab_mutability_type::e_immutable); 1014 | 1015 | T x = 0.0; 1016 | 1017 | immutable_symbol_table.add_variable("x" , x); 1018 | 1019 | expression_t expression; 1020 | expression.register_symbol_table(immutable_symbol_table); 1021 | 1022 | parser_t parser; 1023 | 1024 | parser.compile(expression_str, expression); 1025 | // Compile error because of assignment to variable x 1026 | 1027 | 1028 | In the above example, variable x is registered to an immutable symbol 1029 | table, making it an immutable variable within the context of any 1030 | expressions that reference it. The expression string being compiled 1031 | uses the addition assignment operator which will modify the value of 1032 | variable x. The compilation process detects this semantic violation 1033 | and proceeds to halt compilation and return the appropriate error. 1034 | 1035 | One of the main reasons for this functionality is that, one may want 1036 | the immutability properties that come with constness of a variable 1037 | such as scalars, vectors and strings, but not necessarily the 1038 | accompanying compile time const-folding optimisations, that would 1039 | result in the value of the variables being retrieved only once at 1040 | compile time, causing external updates to the variables to not be part 1041 | of the expression evaluation. 1042 | 1043 | symbol_table_t immutable_symbol_table 1044 | (symbol_table_t::symtab_mutability_type::e_immutable); 1045 | 1046 | T x = 0.0; 1047 | 1048 | const std::string expression_str = "x + (y + y)" 1049 | 1050 | immutable_symbol_table.add_variable("x" , x ); 1051 | immutable_symbol_table.add_constant("y" , 123.0); 1052 | 1053 | expression_t expression; 1054 | expression.register_symbol_table(immutable_symbol_table); 1055 | 1056 | parser_t parser; 1057 | parser.compile(expression_str, expression); 1058 | 1059 | for (; x < 10.0; ++x) 1060 | { 1061 | const auto expected_value = x + (123.0 + 123.0); 1062 | const auto result_value = expression.value(); 1063 | assert(expression.value() != expected_value); 1064 | } 1065 | 1066 | 1067 | In the above example, there are two variables X and Y. Where Y is a 1068 | constant and X is a normal variable. Both are registered with a symbol 1069 | table that is immutable. The expression when compiled will result in 1070 | the "(y + y)" part being const-folded at compile time to the literal 1071 | value of 246. Whereas the current value of X, being updated via the 1072 | for-loop, externally to the expression and the symbol table shall be 1073 | observable to the expression upon each evaluation. 1074 | 1075 | 1076 | (2) Expression 1077 | A structure that holds an Abstract Syntax Tree or AST for a specified 1078 | expression and is used to evaluate said expression. Evaluation of the 1079 | expression is accomplished by performing a post-order traversal of the 1080 | AST. If a compiled Expression uses variables or user defined 1081 | functions, it will have an associated Symbol Table, which will contain 1082 | references to said variables, functions or strings. An example AST 1083 | structure for the denoted expression is as follows: 1084 | 1085 | Expression: z := (x + y^-2.345) * sin(pi / min(w - 7.3,v)) 1086 | 1087 | [Root] 1088 | | 1089 | [Assignment] 1090 | ________/ \_____ 1091 | / \ 1092 | Variable(z) [Multiplication] 1093 | ____________/ \___________ 1094 | / \ 1095 | / [Unary-Function(sin)] 1096 | [Addition] | 1097 | ____/ \____ [Division] 1098 | / \ ___/ \___ 1099 | Variable(x) [Exponentiation] / \ 1100 | ______/ \______ Constant(pi) [Binary-Function(min)] 1101 | / \ ____/ \___ 1102 | Variable(y) [Negation] / \ 1103 | | / Variable(v) 1104 | Constant(2.345) / 1105 | / 1106 | [Subtraction] 1107 | ____/ \____ 1108 | / \ 1109 | Variable(w) Constant(7.3) 1110 | 1111 | 1112 | The above denoted AST shall be evaluated in the following order: 1113 | 1114 | (01) Load Variable (z) (10) Load Constant (7.3) 1115 | (02) Load Variable (x) (11) Subtraction (09 & 10) 1116 | (03) Load Variable (y) (12) Load Variable (v) 1117 | (04) Load Constant (2.345) (13) Min (11 & 12) 1118 | (05) Negation (04) (14) Division (08 & 13) 1119 | (06) Exponentiation (03 & 05) (15) Sin (14) 1120 | (07) Addition (02 & 06) (16) Multiplication (07 & 15) 1121 | (08) Load Constant (pi) (17) Assignment (01 & 16) 1122 | (09) Load Variable (w) 1123 | 1124 | 1125 | Generally an expression in ExprTk can be thought of as a free function 1126 | similar to those found in imperative languages. This form of pseudo 1127 | function will have a name, it may have a set of one or more inputs and 1128 | will return at least one value as its result. Furthermore the function 1129 | when invoked, may cause a side-effect that changes the state of the 1130 | host program. 1131 | 1132 | As an example the following is a pseudo-code definition of a free 1133 | function that performs a computation taking four inputs, modifying one 1134 | of them and returning a value based on some arbitrary calculation: 1135 | 1136 | ResultType foo(InputType x, InputType y, InputType z, InputType w) 1137 | { 1138 | w = 2 * x^y + z; // Side-Effect 1139 | return abs(x - y) / z; // Return Result 1140 | } 1141 | 1142 | 1143 | Given the above definition the following is a functionally equivalent 1144 | version using ExprTk: 1145 | 1146 | const std::string foo_str = 1147 | " w := 2 * x^y + z; " 1148 | " abs(x - y) / z; " 1149 | 1150 | T x, y, z, w; 1151 | 1152 | symbol_table_t symbol_table; 1153 | symbol_table.add_variable("x",x); 1154 | symbol_table.add_variable("y",y); 1155 | symbol_table.add_variable("z",z); 1156 | symbol_table.add_variable("w",w); 1157 | 1158 | expression_t foo; 1159 | foo.register_symbol_table(symbol_table); 1160 | 1161 | parser_t parser; 1162 | if (!parser.compile(foo_str,foo)) 1163 | { 1164 | // Error in expression... 1165 | return; 1166 | } 1167 | 1168 | T result = foo.value(); 1169 | 1170 | 1171 | (3) Parser 1172 | A component which takes as input a string representation of an 1173 | expression and attempts to compile said input with the result being an 1174 | instance of Expression. If an error is encountered during the 1175 | compilation process, the parser will stop compiling and return an 1176 | error status code, with a more detailed description of the error(s) 1177 | and its location within the input provided by the 'get_error' 1178 | interface. 1179 | 1180 | Note06: The exprtk::expression and exprtk::symbol_table components are 1181 | reference counted entities. Copy constructing or assigning to or from 1182 | either component will result in a shallow copy and a reference count 1183 | increment, rather than a complete replication. Furthermore the 1184 | expression and symbol_table components being Default-Constructible, 1185 | Copy-Constructible and Copy-Assignable make them compatible with 1186 | various C++ standard library containers and adaptors such as 1187 | std::vector, std::map, std::stack etc. 1188 | 1189 | The following is an example of two unique expressions, after having 1190 | been instantiated and compiled, one expression is assigned to the 1191 | other. The diagrams depict their initial and post assignment states, 1192 | including which control block each expression references and their 1193 | associated reference counts. 1194 | 1195 | 1196 | exprtk::expression e0; // constructed expression, eg: x + 1 1197 | exprtk::expression e1; // constructed expression, eg: 2z + y 1198 | 1199 | +-----[ e0 cntrl block]----+ +-----[ e1 cntrl block]-----+ 1200 | | 1. Expression Node 'x+1' | | 1. Expression Node '2z+y' | 1201 | | 2. Ref Count: 1 |<-+ | 2. Ref Count: 1 |<-+ 1202 | +--------------------------+ | +---------------------------+ | 1203 | | | 1204 | +--[ e0 expression]--+ | +--[ e1 expression]--+ | 1205 | | 1. Reference to ]------+ | 1. Reference to ]-------+ 1206 | | e0 Control Block | | e1 Control Block | 1207 | +--------------------+ +--------------------+ 1208 | 1209 | 1210 | e0 = e1; // e0 and e1 are now 2z+y 1211 | 1212 | +-----[ e1 cntrl block]-----+ 1213 | | 1. Expression Node '2z+y' | 1214 | +----------->| 2. Ref Count: 2 |<----------+ 1215 | | +---------------------------+ | 1216 | | | 1217 | | +--[ e0 expression]--+ +--[ e1 expression]--+ | 1218 | +---[ 1. Reference to | | 1. Reference to ]---+ 1219 | | e1 Control Block | | e1 Control Block | 1220 | +--------------------+ +--------------------+ 1221 | 1222 | The reason for the above complexity and restrictions of deep copies 1223 | for the expression and symbol_table components is because expressions 1224 | may include user defined variables or functions. These are embedded as 1225 | references into the expression's AST. When copying an expression, said 1226 | references need to also be copied. If the references are blindly 1227 | copied, it will then result in two or more identical expressions 1228 | utilising the exact same references for variables. This obviously is 1229 | not the default assumed scenario and will give rise to non-obvious 1230 | behaviours when using the expressions in various contexts such as 1231 | multi-threading et al. 1232 | 1233 | The prescribed method for cloning an expression is to compile it from 1234 | its string form. Doing so will allow the 'user' to properly consider 1235 | the exact source of user defined variables and functions. 1236 | 1237 | Note07: The exprtk::parser is a non-copyable and non-thread safe 1238 | component, and should only be shared via either a reference, a shared 1239 | pointer or a std::ref mechanism, and considerations relating to 1240 | synchronisation taken into account where appropriate. The parser 1241 | represents an object factory, specifically a factory of expressions, 1242 | and generally should not be instantiated solely on a per expression 1243 | compilation basis. 1244 | 1245 | The following diagram and example depicts the flow of data and 1246 | operations for compiling multiple expressions via the parser and 1247 | inserting the newly minted exprtk::expression instances into a 1248 | std::vector. 1249 | 1250 | +----[exprtk::parser]---+ 1251 | | Expression Factory | 1252 | | parser_t::compile(...)| 1253 | +--> ~.~.~.~.~.~.~.~.~.~ ->--+ 1254 | | +-----------------------+ | 1255 | Expressions in | | Expressions as 1256 | string form ^ V exprtk::expression 1257 | | | instances 1258 | [s0:'x+1']--->--+ | | +-[e0: x+1] 1259 | | | | | 1260 | [s1:'2z+y']-->--+--+ +->+-[e1: 2z+y] 1261 | | | 1262 | [s2:'sin(k+w)']-+ +-[e2: sin(k+w)] 1263 | 1264 | 1265 | const std::string expression_str[3] = 1266 | { 1267 | "x + 1", 1268 | "2x + y", 1269 | "sin(k + w)" 1270 | }; 1271 | 1272 | std::vector<expression_t> expression_list; 1273 | 1274 | parser_t parser; 1275 | expression_t expression; 1276 | symbol_table_t symbol_table; 1277 | 1278 | expression.register_symbol_table(symbol_table); 1279 | 1280 | for (std::size_t i = 0; i < 3; ++i) 1281 | { 1282 | if (parser.compile(expression_str[i],expression)) 1283 | { 1284 | expression_list.push_back(expression); 1285 | } 1286 | else 1287 | std::cout << "Error in " << expression_str[i] << "\n" 1288 | } 1289 | 1290 | for (auto& e : expression_list) 1291 | { 1292 | e.value(); 1293 | } 1294 | 1295 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1296 | 1297 | [SECTION 11 - COMPILATION OPTIONS] 1298 | The exprtk::parser when being instantiated takes as input a set of 1299 | options to be used during the compilation process of expressions. 1300 | An example instantiation of exprtk::parser where only the joiner, 1301 | commutative and strength reduction options are enabled is as follows: 1302 | 1303 | typedef exprtk::parser<NumericType>::settings_t settings_t; 1304 | 1305 | const std::size_t compile_options = 1306 | settings_t::e_joiner + 1307 | settings_t::e_commutative_check + 1308 | settings_t::e_strength_reduction; 1309 | 1310 | parser_t parser(compile_options); 1311 | 1312 | 1313 | Currently eight types of compile time options are supported, and 1314 | enabled by default. The options and their explanations are as follows: 1315 | 1316 | (1) Replacer 1317 | (2) Joiner 1318 | (3) Numeric Check 1319 | (4) Bracket Check 1320 | (5) Sequence Check 1321 | (6) Commutative Check 1322 | (7) Strength Reduction Check 1323 | (8) Stack And Node Depth Check 1324 | (9) Vector Size Check 1325 | 1326 | 1327 | (1) Replacer (e_replacer) 1328 | Enable replacement of specific tokens with other tokens. For example 1329 | the token "true" of type symbol shall be replaced with the numeric 1330 | token of value one. 1331 | 1332 | (a) (x < y) == true ---> (x < y) == 1 1333 | (b) false == (x > y) ---> 0 == (x > y) 1334 | 1335 | 1336 | (2) Joiner (e_joiner) 1337 | Enable joining of multi-character operators that may have been 1338 | incorrectly disjoint in the string representation of the specified 1339 | expression. For example the consecutive tokens of ">" "=" will become 1340 | ">=" representing the "greater than or equal to" operator. If not 1341 | properly resolved the original form will cause a compilation error. 1342 | The following is a listing of the scenarios that the joiner can 1343 | handle: 1344 | 1345 | (a) '>' '=' ---> '>=' (gte) 1346 | (b) '<' '=' ---> '<=' (lte) 1347 | (c) '=' '=' ---> '==' (equal) 1348 | (d) '!' '=' ---> '!=' (not-equal) 1349 | (e) '<' '>' ---> '<>' (not-equal) 1350 | (f) ':' '=' ---> ':=' (assignment) 1351 | (g) '+' '=' ---> '+=' (addition assignment) 1352 | (h) '-' '=' ---> '-=' (subtraction assignment) 1353 | (i) '*' '=' ---> '*=' (multiplication assignment) 1354 | (j) '/' '=' ---> '/=' (division assignment) 1355 | (k) '%' '=' ---> '%=' (modulo assignment) 1356 | (l) '+' '-' ---> '-' (subtraction) 1357 | (m) '-' '+' ---> '-' (subtraction) 1358 | (n) '-' '-' ---> '+' (addition) 1359 | (o) '<=' '>' ---> '<=>' (swap) 1360 | 1361 | 1362 | An example of the transformation that takes place is as follows: 1363 | 1364 | (a) (x > = y) and (z ! = w) ---> (x >= y) and (z != w) 1365 | 1366 | 1367 | (3) Numeric Check (e_numeric_check) 1368 | Enable validation of tokens representing numeric types so as to catch 1369 | any errors prior to the costly process of the main compilation step 1370 | commencing. 1371 | 1372 | 1373 | (4) Bracket Check (e_bracket_check) 1374 | Enable the check for validating the ordering of brackets in the 1375 | specified expression. 1376 | 1377 | 1378 | (5) Sequence Check (e_sequence_check) 1379 | Enable the check for validating that sequences of either pairs or 1380 | triplets of tokens make sense. For example the following sequence of 1381 | tokens when encountered will raise an error: 1382 | 1383 | (a) (x + * 3) ---> sequence error 1384 | 1385 | 1386 | (6) Commutative Check (e_commutative_check) 1387 | Enable the check that will transform sequences of pairs of tokens that 1388 | imply a multiplication operation. The following are some examples of 1389 | such transformations: 1390 | 1391 | (a) 2x ---> 2 * x 1392 | (b) 25x^3 ---> 25 * x^3 1393 | (c) 3(x + 1) ---> 3 * (x + 1) 1394 | (d) (x + 1)4 ---> (x + 1) * 4 1395 | (e) 5foo(x,y) ---> 5 * foo(x,y) 1396 | (f) foo(x,y)6 + 1 ---> foo(x,y) * 6 + 1 1397 | (g) (4((2x)3)) ---> 4 * ((2 * x) * 3) 1398 | (h) w / (x - y)z ---> w / (x - y) * z 1399 | 1400 | 1401 | (7) Strength Reduction Check (e_strength_reduction) 1402 | Enable the use of strength reduction optimisations during the 1403 | compilation process. In ExprTk strength reduction optimisations 1404 | predominantly involve transforming sub-expressions into other forms 1405 | that are algebraically equivalent yet less costly to compute. The 1406 | following are examples of the various transformations that can occur: 1407 | 1408 | (a) (x / y) / z ---> x / (y * z) 1409 | (b) (x / y) / (z / w) ---> (x * w) / (y * z) 1410 | (c) (2 * x) - (2 * y) ---> 2 * (x - y) 1411 | (d) (2 / x) / (3 / y) ---> (2 / 3) / (x * y) 1412 | (e) (2 * x) * (3 * y) ---> 6 * (x * y) 1413 | (f) (2 * x) * (2 - 4 / 2) ---> 0 1414 | (g) (3 - 6 / 2) / (2 * x) ---> 0 1415 | (h) avg(x,y,z) * (2 - 4 / 2) ---> 0 1416 | 1417 | 1418 | Note08: When using strength reduction in conjunction with expressions 1419 | whose inputs or sub-expressions may result in values nearing either of 1420 | the bounds of the underlying numeric type (eg: double), there may be 1421 | the possibility of a decrease in the precision of results. 1422 | 1423 | In the following example the given expression which represents an 1424 | attempt at computing the average between x and y will be transformed 1425 | as follows: 1426 | 1427 | (0.5 * x) + (y * 0.5) ---> 0.5 * (x + y) 1428 | 1429 | There may be situations where the above transformation will cause 1430 | numerical overflows and that the original form of the expression is 1431 | desired over the strength reduced form. In these situations it is best 1432 | to turn off strength reduction optimisations or to use a type with a 1433 | larger numerical bound. 1434 | 1435 | 1436 | (8) Stack And Node Depth Check 1437 | ExprTk incorporates a recursive descent parser. When parsing 1438 | expressions comprising inner sub-expressions, the recursive nature of 1439 | the parsing process causes the stack to grow. If the expression causes 1440 | the stack to grow beyond the stack size limit, this would lead to a 1441 | stackoverflow and its associated stack corruption and security 1442 | vulnerability issues. 1443 | 1444 | Similarly to parsing, evaluating an expression may cause the stack to 1445 | grow. Such things like user defined functions, composite functions and 1446 | the general nature of the AST being evaluated can cause the stack to 1447 | grow, and may result in potential stackoverflow issues as denoted 1448 | above. 1449 | 1450 | ExprTk provides a set of checks that prevent both of the above denoted 1451 | problems at compile time. These checks rely on two specific limits 1452 | being set on the parser settings instance, these limits are: 1453 | 1454 | 1. max_stack_depth (default: 400 ) 1455 | 2. max_node_depth (default: 10000) 1456 | 1457 | 1458 | The following demonstrates how these two parser parameters can be set: 1459 | 1460 | parser_t parser; 1461 | 1462 | parser.settings().set_max_stack_depth(100); 1463 | parser.settings().set_max_node_depth(200); 1464 | 1465 | 1466 | In the above code, during parsing if the stack depth reaches or 1467 | exceeds 100 levels, the parsing process will immediately halt and 1468 | return with a failure. Similarly, during synthesizing the AST nodes, 1469 | if the compilation process detects an AST tree depth exceeding 200 1470 | levels the parsing process will halt and return a parsing failure. 1471 | 1472 | 1473 | (9) Vector Size Check 1474 | When defining an expression local vector, ExprTk uses a default max 1475 | vector size of two billion elements. One may want to limit the max 1476 | vector size to be either smaller or larger than the specified default 1477 | value. The max size value can be changed via the parser settings. 1478 | 1479 | parser_t parser; 1480 | 1481 | parser.settings().set_max_local_vector_size(1000000); 1482 | 1483 | std::string expression1 = "var v[1e6] := [123]" 1484 | std::string expression2 = "var v[1e9] := [123]" 1485 | 1486 | expression_t expression; 1487 | 1488 | parser.compile(expression1, expression); // compilation success 1489 | parser.compile(expression2, expression); // compilation error 1490 | 1491 | 1492 | In the above code, the max local vector size is set to one million 1493 | elements. During compilation of an expression if there is a vector 1494 | definition where the vector size exceeds the max vector size a 1495 | compilation error shall be emitted. 1496 | 1497 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1498 | 1499 | [SECTION 12 - EXPRESSION STRUCTURES] 1500 | Exprtk supports mathematical expressions in numerous forms based on a 1501 | simple imperative programming model. This section will cover the 1502 | following topics related to general structure and programming of 1503 | expressions using ExprTk: 1504 | 1505 | (1) Multi-Statement Expressions 1506 | (2) Statements And Side-Effects 1507 | (3) Conditional Statements 1508 | (4) Special Functions 1509 | 1510 | 1511 | (1) Multi-Statement Expressions 1512 | Expressions in ExprTk can be comprised of one or more statements, which 1513 | may sometimes be called sub-expressions. The following are two 1514 | examples of expressions stored in std::string variables, the first a 1515 | single statement and the second a multi-statement expression: 1516 | 1517 | std::string single_statement = " z := x + y " 1518 | 1519 | std::string multi_statement = " var temp := x; " 1520 | " x := y + z; " 1521 | " y := temp; " 1522 | 1523 | 1524 | In a multi-statement expression, the final statement will determine 1525 | the overall result of the expression. In the following multi-statement 1526 | expression, the result of the expression when evaluated will be '2.3', 1527 | which will also be the value stored in the 'y' variable. 1528 | 1529 | z := x + y; 1530 | y := 2.3; 1531 | 1532 | 1533 | As demonstrated in the expression above, statements within an 1534 | expression are separated using the semi-colon ';' operator. In the 1535 | event two statements are not separated by a semi-colon, and the 1536 | implied multiplication feature is active (enabled by default), the 1537 | compiler will assume a multiplication operation between the two 1538 | statements. 1539 | 1540 | In the following example we have a multi-statement expression composed 1541 | of two variable definitions and initialisations for variables x and y 1542 | and two seemingly separate mathematical operations. 1543 | 1544 | var x:= 2; 1545 | var y:= 3; 1546 | x + 1 1547 | y * 2 1548 | 1549 | 1550 | However the result of the expression will not be 6 as may have been 1551 | assumed based on the calculation of 'y * 2', but rather the result 1552 | will be 8. This is because the compiler will have conjoined the two 1553 | mathematical statements into one via a multiplication operation. The 1554 | expression when compiled will actually evaluate as the following: 1555 | 1556 | var x:= 2; 1557 | var y:= 3; 1558 | x + 1 * y * 2; // 2 + 1 * 3 * 2 == 8 1559 | 1560 | 1561 | In ExprTk any valid statement will itself return a value. This value 1562 | can further be used in conjunction with other statements. This 1563 | includes language structures such as if-statements, loops (for, while) 1564 | and the switch statement. Typically the last statement executed in the 1565 | given construct (conditional, loop etc), will be the value that is 1566 | returned. 1567 | 1568 | In the following example, the return value of the expression will be 1569 | 11, which is the sum of the variable 'x' and the final value computed 1570 | within the loop body upon its last iteration: 1571 | 1572 | var x := 1; 1573 | x + for (var i := x; i < 10; i += 1) 1574 | { 1575 | i / 2; 1576 | i + 1; 1577 | } 1578 | 1579 | 1580 | (2) Statements And Side-Effects 1581 | Statements themselves may have side effects, which in-turn affect the 1582 | proceeding statements in multi-statement expressions. 1583 | 1584 | A statement is said to have a side-effect if it causes the state of 1585 | the expression to change in some way - this includes but is not 1586 | limited to the modification of the state of external variables used 1587 | within the expression. Currently the following actions being present 1588 | in a statement will cause it to have a side-effect: 1589 | 1590 | (a) Assignment operation (explicit or potentially) 1591 | (b) Invoking a user-defined function that has side-effects 1592 | 1593 | The following are examples of expressions where the side-effect status 1594 | of the statements (sub-expressions) within the expressions have been 1595 | noted: 1596 | 1597 | +-+----------------------+------------------------------+ 1598 | |#| Expression | Side Effect Status | 1599 | +-+----------------------+------------------------------+ 1600 | |0| x + y | False | 1601 | +-+----------------------+------------------------------+ 1602 | |1| z := x + y | True - Due to assignment | 1603 | +-+----------------------+------------------------------+ 1604 | |2| abs(x - y) | False | 1605 | +-+----------------------+------------------------------+ 1606 | |3| abs(x - y); | False | 1607 | | | z := (x += y); | True - Due to assignments | 1608 | +-+----------------------+------------------------------+ 1609 | |4| abs(x - y); | False | 1610 | | | z := (x += y); | True - Due to assignments | 1611 | +-+----------------------+------------------------------+ 1612 | |5| var t := abs(x - y); | True - Due to initialisation | 1613 | | | t + x; | False | 1614 | | | z := (x += y); | True - Due to assignments | 1615 | +-+----------------------+------------------------------+ 1616 | |6| foo(x - y) | True - user defined function | 1617 | +-+----------------------+------------------------------+ 1618 | 1619 | 1620 | Note09: In example 6 from the above set, it is assumed the user 1621 | defined function foo has been registered as having a side-effect. By 1622 | default all user defined functions are assumed to have side-effects, 1623 | unless they are configured in their constructors to not have side- 1624 | effects using the 'disable_has_side_effects' free function. For more 1625 | information review Section 15 - User Defined Functions sub-section 7 1626 | Function Side-Effects. 1627 | 1628 | At this point we can see that there will be expressions composed of 1629 | certain kinds of statements that when executed will not affect the 1630 | nature of the expression's result. These statements are typically 1631 | called 'dead code'. These statements though not affecting the final 1632 | result will still be executed and as such they will consume processing 1633 | time that could otherwise be saved. As such ExprTk attempts to detect 1634 | and remove such statements from expressions. 1635 | 1636 | The 'Dead Code Elimination' (DCE) optimisation process, which is 1637 | enabled by default, will remove any statements that are determined to 1638 | not have a side-effect in a multi-statement expression, excluding the 1639 | final or last statement. 1640 | 1641 | By default the final statement in an expression will always be present 1642 | regardless of its side-effect status, as it is the statement whose 1643 | value shall be used as the result of the expression. 1644 | 1645 | In order to further explain the actions taken during the DCE process, 1646 | lets review the following expression: 1647 | 1648 | var x := 2; // Statement 1 1649 | var y := x + 2; // Statement 2 1650 | x + y; // Statement 3 1651 | y := x + 3y; // Statement 4 1652 | x - y; // Statement 5 1653 | 1654 | 1655 | The above expression has five statements. Three of them (1, 2 and 4) 1656 | actively have side-effects. The first two are variable declaration and 1657 | initialisations, where as the third is due to an assignment operation. 1658 | There are two statements (3 and 5), that do not explicitly have 1659 | side-effects, however the latter, statement 5, is the final statement 1660 | in the expression and hence will be assumed to have a side-effect. 1661 | 1662 | During compilation when the DCE optimisation is applied to the above 1663 | expression, statement 3 will be removed from the expression, as it has 1664 | no bearing on the final result of expression, the rest of the 1665 | statements will all remain. The optimised form of the expression is as 1666 | follows: 1667 | 1668 | var x := 2; // Statement 1 1669 | var y := x + 2; // Statement 2 1670 | y := x + 3y; // Statement 3 1671 | x - y; // Statement 4 1672 | 1673 | 1674 | (3) Conditional Statements (If-Then-Else) 1675 | ExprTk supports two forms of conditional branching or otherwise known 1676 | as if-statements. The first form, is a simple function based 1677 | conditional statement, that takes exactly three input expressions: 1678 | condition, consequent and alternative. The following is an example 1679 | expression that utilises the function based if-statement. 1680 | 1681 | x := if (y < z, y + 1, 2 * z) 1682 | 1683 | 1684 | In the example above, if the condition 'y < z' is true, then the 1685 | consequent 'y + 1' will be evaluated, its value shall be returned and 1686 | subsequently assigned to the variable 'x'. Otherwise the alternative 1687 | '2 * z' will be evaluated and its value will be returned. This is 1688 | essentially the simplest form of an if-then-else statement. A simple 1689 | variation of the expression where the value of the if-statement is 1690 | used within another statement is as follows: 1691 | 1692 | x := 3 * if (y < z, y + 1, 2 * z) / 2 1693 | 1694 | 1695 | The second form of if-statement resembles the standard syntax found in 1696 | most imperative languages. There are two variations of the statement: 1697 | 1698 | (a) If-Statement 1699 | (b) If-Then-Else Statement 1700 | 1701 | 1702 | (a) If-Statement 1703 | This version of the conditional statement returns the value of the 1704 | consequent expression when the condition expression is true, else it 1705 | will return a quiet NaN value as its result. 1706 | 1707 | Example 1: 1708 | x := if (y < z) y + 3; 1709 | 1710 | Example 2: 1711 | x := if (y < z) 1712 | { 1713 | y + 3 1714 | }; 1715 | 1716 | The two example expressions above are equivalent. If the condition 1717 | 'y < z' is true, the 'x' variable shall be assigned the value of the 1718 | consequent 'y + 3', otherwise it will be assigned the value of quiet 1719 | NaN. As previously discussed, if-statements are value returning 1720 | constructs, and if not properly terminated using a semi-colon, will 1721 | end-up combining with the next statement via a multiplication 1722 | operation. The following example will NOT result in the expected value 1723 | of 'w + x' being returned: 1724 | 1725 | x := if (y < z) y + 3 // missing semi-colon ';' 1726 | w + x 1727 | 1728 | 1729 | When the above supposed multi-statement expression is compiled, the 1730 | expression will have a multiplication inserted between the two 1731 | 'intended' statements resulting in the unanticipated expression: 1732 | 1733 | x := (if (y < z) y + 3) * w + x 1734 | 1735 | 1736 | The solution to the above situation is to simply terminate the 1737 | conditional statement with a semi-colon as follows: 1738 | 1739 | x := if (y < z) y + 3; 1740 | w + x 1741 | 1742 | 1743 | (b) If-Then-Else Statement 1744 | The second variation of the if-statement is to allow for the use of 1745 | Else and Else-If cascading statements. Examples of such statements are 1746 | as follows: 1747 | 1748 | Example 1: Example 2: Example 3: 1749 | if (x < y) if (x < y) if (x > y + 1) 1750 | z := x + 3; { y := abs(x - z); 1751 | else y := z + x; else 1752 | y := x - z; z := x + 3; { 1753 | } y := z + x; 1754 | else z := x + 3; 1755 | y := x - z; }; 1756 | 1757 | 1758 | Example 4: Example 5: Example 6: 1759 | if (2 * x < max(y,3)) if (x < y) if (x < y or (x + z) > y) 1760 | { z := x + 3; { 1761 | y := z + x; else if (2y != z) z := x + 3; 1762 | z := x + 3; { y := x - z; 1763 | } z := x + 3; } 1764 | else if (2y - z) y := x - z; else if (abs(2y - z) >= 3) 1765 | y := x - z; } y := x - z; 1766 | else else 1767 | x * x; { 1768 | z := abs(x * x); 1769 | x * y * z; 1770 | }; 1771 | 1772 | 1773 | In the case where there is no final else statement and the flow 1774 | through the conditional arrives at this final point, the same rules 1775 | apply to this form of if-statement as to the previous. That is a quiet 1776 | NaN shall be returned as the result of the if-statement. Furthermore 1777 | the same requirements of terminating the statement with a semi-colon 1778 | apply. 1779 | 1780 | (4) Special Functions 1781 | The purpose of special functions in ExprTk is to provide compiler 1782 | generated equivalents of common mathematical expressions which can be 1783 | invoked by using the 'special function' syntax (eg: $f12(x,y,z) or 1784 | $f82(x,y,z,w)). 1785 | 1786 | Special functions dramatically decrease the total evaluation time of 1787 | expressions which would otherwise have been written using the common 1788 | form by reducing the total number of nodes in the evaluation tree of 1789 | an expression and by also leveraging the compiler's ability to 1790 | correctly optimise such expressions for a given architecture. 1791 | 1792 | 3-Parameter 4-Parameter 1793 | +-------------+-------------+ +--------------+------------------+ 1794 | | Prototype | Operation | | Prototype | Operation | 1795 | +-------------+-------------+ +--------------+------------------+ 1796 | $f00(x,y,z) | (x + y) / z $f48(x,y,z,w) | x + ((y + z) / w) 1797 | $f01(x,y,z) | (x + y) * z $f49(x,y,z,w) | x + ((y + z) * w) 1798 | $f02(x,y,z) | (x + y) - z $f50(x,y,z,w) | x + ((y - z) / w) 1799 | $f03(x,y,z) | (x + y) + z $f51(x,y,z,w) | x + ((y - z) * w) 1800 | $f04(x,y,z) | (x - y) + z $f52(x,y,z,w) | x + ((y * z) / w) 1801 | $f05(x,y,z) | (x - y) / z $f53(x,y,z,w) | x + ((y * z) * w) 1802 | $f06(x,y,z) | (x - y) * z $f54(x,y,z,w) | x + ((y / z) + w) 1803 | $f07(x,y,z) | (x * y) + z $f55(x,y,z,w) | x + ((y / z) / w) 1804 | $f08(x,y,z) | (x * y) - z $f56(x,y,z,w) | x + ((y / z) * w) 1805 | $f09(x,y,z) | (x * y) / z $f57(x,y,z,w) | x - ((y + z) / w) 1806 | $f10(x,y,z) | (x * y) * z $f58(x,y,z,w) | x - ((y + z) * w) 1807 | $f11(x,y,z) | (x / y) + z $f59(x,y,z,w) | x - ((y - z) / w) 1808 | $f12(x,y,z) | (x / y) - z $f60(x,y,z,w) | x - ((y - z) * w) 1809 | $f13(x,y,z) | (x / y) / z $f61(x,y,z,w) | x - ((y * z) / w) 1810 | $f14(x,y,z) | (x / y) * z $f62(x,y,z,w) | x - ((y * z) * w) 1811 | $f15(x,y,z) | x / (y + z) $f63(x,y,z,w) | x - ((y / z) / w) 1812 | $f16(x,y,z) | x / (y - z) $f64(x,y,z,w) | x - ((y / z) * w) 1813 | $f17(x,y,z) | x / (y * z) $f65(x,y,z,w) | ((x + y) * z) - w 1814 | $f18(x,y,z) | x / (y / z) $f66(x,y,z,w) | ((x - y) * z) - w 1815 | $f19(x,y,z) | x * (y + z) $f67(x,y,z,w) | ((x * y) * z) - w 1816 | $f20(x,y,z) | x * (y - z) $f68(x,y,z,w) | ((x / y) * z) - w 1817 | $f21(x,y,z) | x * (y * z) $f69(x,y,z,w) | ((x + y) / z) - w 1818 | $f22(x,y,z) | x * (y / z) $f70(x,y,z,w) | ((x - y) / z) - w 1819 | $f23(x,y,z) | x - (y + z) $f71(x,y,z,w) | ((x * y) / z) - w 1820 | $f24(x,y,z) | x - (y - z) $f72(x,y,z,w) | ((x / y) / z) - w 1821 | $f25(x,y,z) | x - (y / z) $f73(x,y,z,w) | (x * y) + (z * w) 1822 | $f26(x,y,z) | x - (y * z) $f74(x,y,z,w) | (x * y) - (z * w) 1823 | $f27(x,y,z) | x + (y * z) $f75(x,y,z,w) | (x * y) + (z / w) 1824 | $f28(x,y,z) | x + (y / z) $f76(x,y,z,w) | (x * y) - (z / w) 1825 | $f29(x,y,z) | x + (y + z) $f77(x,y,z,w) | (x / y) + (z / w) 1826 | $f30(x,y,z) | x + (y - z) $f78(x,y,z,w) | (x / y) - (z / w) 1827 | $f31(x,y,z) | x * y^2 + z $f79(x,y,z,w) | (x / y) - (z * w) 1828 | $f32(x,y,z) | x * y^3 + z $f80(x,y,z,w) | x / (y + (z * w)) 1829 | $f33(x,y,z) | x * y^4 + z $f81(x,y,z,w) | x / (y - (z * w)) 1830 | $f34(x,y,z) | x * y^5 + z $f82(x,y,z,w) | x * (y + (z * w)) 1831 | $f35(x,y,z) | x * y^6 + z $f83(x,y,z,w) | x * (y - (z * w)) 1832 | $f36(x,y,z) | x * y^7 + z $f84(x,y,z,w) | x*y^2 + z*w^2 1833 | $f37(x,y,z) | x * y^8 + z $f85(x,y,z,w) | x*y^3 + z*w^3 1834 | $f38(x,y,z) | x * y^9 + z $f86(x,y,z,w) | x*y^4 + z*w^4 1835 | $f39(x,y,z) | x * log(y)+z $f87(x,y,z,w) | x*y^5 + z*w^5 1836 | $f40(x,y,z) | x * log(y)-z $f88(x,y,z,w) | x*y^6 + z*w^6 1837 | $f41(x,y,z) | x * log10(y)+z $f89(x,y,z,w) | x*y^7 + z*w^7 1838 | $f42(x,y,z) | x * log10(y)-z $f90(x,y,z,w) | x*y^8 + z*w^8 1839 | $f43(x,y,z) | x * sin(y)+z $f91(x,y,z,w) | x*y^9 + z*w^9 1840 | $f44(x,y,z) | x * sin(y)-z $f92(x,y,z,w) | (x and y) ? z : w 1841 | $f45(x,y,z) | x * cos(y)+z $f93(x,y,z,w) | (x or y) ? z : w 1842 | $f46(x,y,z) | x * cos(y)-z $f94(x,y,z,w) | (x < y) ? z : w 1843 | $f47(x,y,z) | x ? y : z $f95(x,y,z,w) | (x <= y) ? z : w 1844 | $f96(x,y,z,w) | (x > y) ? z : w 1845 | $f97(x,y,z,w) | (x >= y) ? z : w 1846 | $f98(x,y,z,w) | (x == y) ? z : w 1847 | $f99(x,y,z,w) | x*sin(y)+z*cos(w) 1848 | 1849 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1850 | 1851 | [SECTION 13 - VARIABLE, VECTOR & STRING DEFINITION] 1852 | ExprTk supports the definition of expression local variables, vectors 1853 | and strings. The definitions must be unique as shadowing is not 1854 | allowed and object lifetimes are based on scope. Definitions use the 1855 | following general form: 1856 | 1857 | var <name> := <initialiser>; 1858 | 1859 | (1) Variable Definition 1860 | Variables are of numeric type denoting a single value. They can be 1861 | explicitly initialised to a value, otherwise they will be defaulted to 1862 | zero. The following are examples of variable definitions: 1863 | 1864 | (a) Initialise x to zero 1865 | var x; 1866 | 1867 | (b) Initialise y to three 1868 | var y := 3; 1869 | 1870 | (c) Initialise z to the expression 1871 | var z := if (max(1, x + y) > 2, w, v); 1872 | 1873 | (d) Initialise const literal n 1874 | var n := 12 / 3; 1875 | 1876 | 1877 | (2) Vector Definition 1878 | Vectors are arrays of a common numeric type. The elements in a vector 1879 | can be explicitly initialised, otherwise they will all be defaulted to 1880 | zero. The following are examples of vector definitions: 1881 | 1882 | (a) Initialise all values to zero 1883 | var x[3]; 1884 | 1885 | (b) Initialise all values to zero 1886 | var x[3] := {}; 1887 | 1888 | (c) Initialise all values to given value or expression 1889 | var x[3] := [ 42 ]; 1890 | var y[x[]] := [ 123 + 3y + sin(w / z) ]; 1891 | 1892 | (d) Initialise all values iota style 1893 | var v[4] := [ 0 : +1]; // 0, 1, 2, 3 1894 | var v[5] := [-3 : -2]; // -3, -5, -7, -9, -11 1895 | 1896 | (e) Initialise the first two values, all other elements to zero 1897 | var x[3] := { (1 + x[2]) / x[], (sin(y[0] / x[]) + 3) / x[] }; 1898 | 1899 | (f) Initialise the first three (all) values 1900 | const var size := 3; 1901 | var x[size] := { 1, 2, 3 }; 1902 | 1903 | (g) Initialise vector from a vector 1904 | var x[4] := { 1, 2, 3, 4 }; 1905 | var y[3] := x; 1906 | var w[5] := { 1, 2 }; // 1, 2, 0, 0, 0 1907 | 1908 | (h) Initialise vector from a smaller vector 1909 | var x[3] := { 1, 2, 3 }; 1910 | var y[5] := x; // 1, 2, 3, ??, ?? 1911 | 1912 | (i) Non-initialised vector 1913 | var x[3] := null; // ?? ?? ?? 1914 | 1915 | (j) Error as there are too many initialisers 1916 | var x[3] := { 1, 2, 3, 4 }; 1917 | 1918 | (k) Error as a vector of size zero is not allowed. 1919 | var x[0]; 1920 | 1921 | 1922 | (3) String Definition 1923 | Strings are sequences comprised of 8-bit characters. They can only be 1924 | defined with an explicit initialisation value. The following are 1925 | examples of string variable definitions: 1926 | 1927 | (a) Initialise to a string 1928 | var x := 'abc'; 1929 | 1930 | (b) Initialise to an empty string 1931 | var x := ''; 1932 | 1933 | (c) Initialise to a string expression 1934 | var x := 'abc' + '123'; 1935 | 1936 | (d) Initialise to a string range 1937 | var x := 'abc123'[2:4]; 1938 | 1939 | (e) Initialise to another string variable 1940 | var x := 'abc'; 1941 | var y := x; 1942 | 1943 | (f) Initialise to another string variable range 1944 | var x := 'abc123'; 1945 | var y := x[2:4]; 1946 | 1947 | (g) Initialise to a string expression 1948 | var x := 'abc'; 1949 | var y := x + '123'; 1950 | 1951 | (h) Initialise to a string expression range 1952 | var x := 'abc'; 1953 | var y := (x + '123')[1:3]; 1954 | 1955 | 1956 | (4) Return Value 1957 | Variable and vector definitions have a return value. In the case of 1958 | variable definitions, the value to which the variable is initialised 1959 | will be returned. Where as for vectors, the value of the first element 1960 | (eg: v[0]) shall be returned. 1961 | 1962 | 8 == ((var x := 7;) + 1) 1963 | 4 == (var y[3] := {4, 5, 6};) 1964 | 1965 | 1966 | (5) Variable/Vector Assignment 1967 | The value of a variable can be assigned to a vector and a vector or a 1968 | vector expression can be assigned to a variable. 1969 | 1970 | (a) Variable To Vector: 1971 | Every element of the vector is assigned the value of the variable 1972 | or expression. 1973 | var x := 3; 1974 | var y[3] := { 1, 2, 3 }; 1975 | y := x + 1; 1976 | 1977 | (b) Vector To Variable: 1978 | The variable is assigned the value of the first element of the 1979 | vector (aka vec[0]) 1980 | var x := 3; 1981 | var y[3] := { 1, 2, 3 }; 1982 | x := y + 1; 1983 | 1984 | 1985 | Note10: During the expression compilation phase, tokens are classified 1986 | based on the following priorities: 1987 | 1988 | (a) Reserved keywords or operators (+, -, and, or, etc) 1989 | (b) Base functions (abs, sin, cos, min, max etc) 1990 | (c) Symbol table variables 1991 | (d) Expression local defined variables 1992 | (e) Symbol table functions 1993 | (f) Unknown symbol resolver based variables 1994 | 1995 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1996 | 1997 | [SECTION 14 - VECTOR PROCESSING] 1998 | ExprTk provides support for various forms of vector oriented 1999 | arithmetic, inequalities and processing. The various supported pairs 2000 | are as follows: 2001 | 2002 | (a) vector and vector (eg: v0 + v1) 2003 | (b) vector and scalar (eg: v + 33) 2004 | (c) scalar and vector (eg: 22 * v) 2005 | 2006 | The following is a list of operations that can be used in conjunction 2007 | with vectors: 2008 | 2009 | (a) Arithmetic: +, -, *, /, % 2010 | (b) Exponentiation: vector ^ scalar 2011 | (c) Assignment: :=, +=, -=, *=, /=, %=, <=> 2012 | (d) Inequalities: <, <=, >, >=, ==, =, equal 2013 | (e) Boolean logic: and, nand, nor, or, xnor, xor 2014 | (f) Unary operations: 2015 | abs, acos, acosh, asin, asinh, atan, atanh, ceil, cos, cosh, 2016 | cot, csc, deg2grad, deg2rad, erf, erfc, exp, expm1, floor, 2017 | frac, grad2deg, log, log10, log1p, log2, rad2deg, round, sec, 2018 | sgn, sin, sinc, sinh, sqrt, swap, tan, tanh, trunc, 2019 | thresholding 2020 | (g) Aggregate and Reduce operations: 2021 | avg, max, min, mul, dot, dotk, sum, sumk, count, all_true, 2022 | all_false, any_true, any_false 2023 | (h) Transformation operations: 2024 | copy, diff, reverse, rotate-left/right, shift-left/right, sort, 2025 | nth_element 2026 | (i) BLAS-L1: 2027 | axpy, axpby, axpyz, axpbyz, axpbz 2028 | 2029 | Note11: When one of the above described operations is being performed 2030 | between two vectors, the operation will only span the size of the 2031 | smallest vector. The elements of the larger vector outside of the 2032 | range will not be included. The operation itself will be processed 2033 | element-wise over values of the smaller of the two ranges. 2034 | 2035 | The following simple example demonstrates the vector processing 2036 | capabilities by computing the dot-product of the vectors v0 and v1 and 2037 | then assigning it to the variable v0dotv1: 2038 | 2039 | var v0[3] := { 1, 2, 3 }; 2040 | var v1[3] := { 4, 5, 6 }; 2041 | var v0dotv1 := sum(v0 * v1); 2042 | 2043 | 2044 | The following is a for-loop based implementation that is equivalent to 2045 | the previously mentioned dot-product computation expression: 2046 | 2047 | var v0[3] := { 1, 2, 3 }; 2048 | var v1[3] := { 4, 5, 6 }; 2049 | var v0dotv1; 2050 | 2051 | for (var i := 0; i < min(v0[],v1[]); i += 1) 2052 | { 2053 | v0dotv1 += (v0[i] * v1[i]); 2054 | } 2055 | 2056 | 2057 | Note12: When the aggregate or reduction operations denoted above are 2058 | used in conjunction with a vector or vector expression, the return 2059 | value is not a vector but rather a single value. 2060 | 2061 | var x[3] := { 1, 2, 3 }; 2062 | 2063 | sum(x) == 6 2064 | sum(1 + 2x) == 15 2065 | avg(3x + 1) == 7 2066 | min(1 / x) == (1 / 3) 2067 | max(x / 2) == (3 / 2) 2068 | sum(x > 0 and x < 5) == x[] 2069 | 2070 | 2071 | When utilising external user defined vectors via the symbol table as 2072 | opposed to expression local defined vectors, the typical 'add_vector' 2073 | method from the symbol table will register the entirety of the vector 2074 | that is passed. The following example attempts to evaluate the sum of 2075 | elements of the external user defined vector within a typical yet 2076 | trivial expression: 2077 | 2078 | const std::string reduce_program = " sum(2 * v + 1) " 2079 | 2080 | std::vector<T> v0 { T(1.1), T(2.2), ..... , T(99.99) }; 2081 | 2082 | symbol_table_t symbol_table; 2083 | symbol_table.add_vector("v",v); 2084 | 2085 | expression_t expression; 2086 | expression.register_symbol_table(symbol_table); 2087 | 2088 | parser_t parser; 2089 | parser.compile(reduce_program,expression); 2090 | 2091 | T sum = expression.value(); 2092 | 2093 | 2094 | For the most part, this is a very common use-case. However there may 2095 | be situations where one may want to evaluate the same vector oriented 2096 | expression many times over, but using different vectors or sub ranges 2097 | of the same vector of the same size to that of the original upon every 2098 | evaluation. 2099 | 2100 | The usual solution is to either recompile the expression for the new 2101 | vector instance, or to copy the contents from the new vector to the 2102 | symbol table registered vector and then perform the evaluation. When 2103 | the vectors are large or the re-evaluation attempts are numerous, 2104 | these solutions can become rather time consuming and generally 2105 | inefficient. 2106 | 2107 | std::vector<T> v1 { T(2.2), T(2.2), ..... , T(2.2) }; 2108 | std::vector<T> v2 { T(3.3), T(3.3), ..... , T(3.3) }; 2109 | std::vector<T> v3 { T(4.4), T(4.4), ..... , T(4.4) }; 2110 | 2111 | std::vector<std::vector<T>> vv { v1, v2, v3 }; 2112 | ... 2113 | T sum = T(0); 2114 | 2115 | for (auto& new_vec : vv) 2116 | { 2117 | v = new_vec; // update vector 2118 | sum += expression.value(); 2119 | } 2120 | 2121 | 2122 | A solution to the above 'efficiency' problem, is to use the 2123 | exprtk::vector_view object. The vector_view is instantiated with a 2124 | size and backing based upon a vector. Upon evaluations if the backing 2125 | needs to be 'updated' to either another vector or sub-range, the 2126 | vector_view instance can be efficiently rebased, and the expression 2127 | evaluated as normal. 2128 | 2129 | exprtk::vector_view<T> view = exprtk::make_vector_view(v,v.size()); 2130 | 2131 | symbol_table_t symbol_table; 2132 | symbol_table.add_vector("v",view); 2133 | 2134 | ... 2135 | 2136 | T sum = T(0); 2137 | 2138 | for (auto& new_vec : vv) 2139 | { 2140 | view.rebase(new_vec.data()); // update vector 2141 | sum += expression.value(); 2142 | } 2143 | 2144 | 2145 | Another useful feature of exprtk::vector_view is that all such vectors 2146 | can have their sizes modified (or "resized"). The resizing of the 2147 | associated vectors can happen either between or during evaluations. 2148 | 2149 | std::vector<T> v = { 1, 2, 3, 4, 5, 6, 7, 8 }; 2150 | exprtk::vector_view<T> view = exprtk::make_vector_view(v,v.size()); 2151 | 2152 | symbol_table_t symbol_table; 2153 | symbol_table.add_vector("v",view); 2154 | 2155 | const std::string expression_string = "v[]" 2156 | 2157 | expression_t expression; 2158 | expression.register_symbol_table(symbol_table); 2159 | 2160 | parser_t parser; 2161 | parser.compile(expression_string, expression); 2162 | 2163 | for (std::size_t i = 1; i <= v.size(); ++i) 2164 | { 2165 | vv.set_size(i); 2166 | expression.value(); 2167 | } 2168 | 2169 | 2170 | In the example above, a vector_view is instantiated with a std::vector 2171 | instance with eight elements and registered to the given symbol_table. 2172 | An expression is then compiled, which in this case simply returns the 2173 | size of the vector at that point in time. The expression is evaluated 2174 | eight times (size of vector times), where upon each iteration the size 2175 | of the vector is changed with values ranging from one to eight. 2176 | 2177 | Note13: When modifying the size of a vector, the new size must be at 2178 | least one or larger and must not exceed the original size of the 2179 | vector_view when it was instantiated. 2180 | 2181 | Note14: The lifetime of any parser, symbol_table or expression 2182 | instance must not exceed that of any vector_view instance that has 2183 | been registered with it. Furthermore the lifetime of a vector_view 2184 | must not exceed that of the underlying vector instance it is 2185 | associated with. 2186 | 2187 | Note15: In a multi-threaded context the rebase function should not be 2188 | called during associated expression evaluation, as this will lead to 2189 | undefined behaviour (eg: torn reads and writes). 2190 | 2191 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2192 | 2193 | [SECTION 15 - USER DEFINED FUNCTIONS] 2194 | ExprTk provides a means whereby custom functions can be defined and 2195 | utilised within expressions. The concept requires the user to 2196 | provide a reference to the function coupled with an associated name 2197 | that will be invoked within expressions. Functions may take numerous 2198 | inputs but will always return a single value of the underlying numeric 2199 | type. 2200 | 2201 | During expression compilation when required the reference to the 2202 | function shall be obtained from the associated symbol_table and be 2203 | embedded into the expression. 2204 | 2205 | There are five types of function interface: 2206 | 2207 | +---+----------------------+--------------+----------------------+ 2208 | | # | Name | Return Type | Input Types | 2209 | +---+----------------------+--------------+----------------------+ 2210 | | 1 | ifunction | Scalar | Scalar | 2211 | | 2 | ivararg_function | Scalar | Scalar | 2212 | | 3 | igeneric_function | Scalar | Scalar,Vector,String | 2213 | | 4 | igeneric_function II | String | Scalar,Vector,String | 2214 | | 5 | igeneric_function III| String/Scalar| Scalar,Vector,String | 2215 | | 6 | function_compositor | Scalar | Scalar | 2216 | +---+----------------------+--------------+----------------------+ 2217 | 2218 | (1) ifunction 2219 | This interface supports zero to 20 input parameters of only the scalar 2220 | type (numbers). The usage requires a custom function be derived from 2221 | ifunction and to override one of the 21 function operators. As part of 2222 | the constructor the custom function will define how many parameters it 2223 | expects to handle. The following example defines a 3 parameter 2224 | function called 'foo': 2225 | 2226 | template <typename T> 2227 | struct foo final : public exprtk::ifunction<T> 2228 | { 2229 | foo() : exprtk::ifunction<T>(3) 2230 | {} 2231 | 2232 | T operator()(const T& v1, const T& v2, const T& v3) override 2233 | { 2234 | return T(1) + (v1 * v2) / T(v3); 2235 | } 2236 | }; 2237 | 2238 | 2239 | (2) ivararg_function 2240 | This interface supports a variable number of scalar arguments as input 2241 | into the function. The function operator interface uses a std::vector 2242 | specialised upon type T to facilitate parameter passing. The following 2243 | example defines a vararg function called 'boo': 2244 | 2245 | template <typename T> 2246 | struct boo final : public exprtk::ivararg_function<T> 2247 | { 2248 | inline T operator()(const std::vector<T>& arglist) override 2249 | { 2250 | T result = T(0); 2251 | 2252 | for (std::size_t i = 0; i < arglist.size(); ++i) 2253 | { 2254 | result += arglist[i] / arglist[i > 0 ? (i - 1) : 0]; 2255 | } 2256 | 2257 | return result; 2258 | } 2259 | }; 2260 | 2261 | 2262 | (3) igeneric_function 2263 | This interface supports a variable number of arguments and types as 2264 | input into the function. The function operator interface uses a 2265 | std::vector specialised upon the type_store type to facilitate 2266 | parameter passing. 2267 | 2268 | Scalar <-- function(i_0, i_1, i_2....., i_N) 2269 | 2270 | 2271 | The fundamental types that can be passed into the function as 2272 | parameters and their views are as follows: 2273 | 2274 | (1) Scalar - scalar_view 2275 | (2) Vector - vector_view 2276 | (3) String - string_view 2277 | 2278 | 2279 | The above denoted type views provide non-const reference-like access 2280 | to each parameter, as such modifications made to the input parameters 2281 | will persist after the function call has completed. The following 2282 | example defines a generic function called 'too': 2283 | 2284 | template <typename T> 2285 | struct too final : public exprtk::igeneric_function<T> 2286 | { 2287 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2288 | parameter_list_t; 2289 | 2290 | too() 2291 | {} 2292 | 2293 | inline T operator()(parameter_list_t parameters) override 2294 | { 2295 | for (std::size_t i = 0; i < parameters.size(); ++i) 2296 | { 2297 | ... 2298 | } 2299 | 2300 | return T(0); 2301 | } 2302 | }; 2303 | 2304 | 2305 | In the example above, the input 'parameters' to the function operator, 2306 | parameter_list_t, is a type of std::vector of type_store. Each 2307 | type_store instance has a member called 'type' which holds the 2308 | enumeration pertaining to the underlying type of the type_store. There 2309 | are three type enumerations: 2310 | 2311 | (1) e_scalar - literals, variables, vector elements, expressions 2312 | eg: 123.456, x, vec[3x + 1], 2x + 3 2313 | 2314 | (2) e_vector - vectors, vector expressions 2315 | eg: vec1, 2 * vec1 + vec2 / 3 2316 | 2317 | (3) e_string - strings, string literals and range variants of both 2318 | eg: 'AString', s0, 'AString'[x:y], s1[1 + x:] + 'AString' 2319 | 2320 | 2321 | Each of the parameters can be accessed using its designated view. 2322 | A typical loop for processing the parameters is as follows: 2323 | 2324 | inline T operator()(parameter_list_t parameters) 2325 | { 2326 | typedef typename exprtk::igeneric_function<T>::generic_type 2327 | generic_type; 2328 | 2329 | typedef typename generic_type::scalar_view scalar_t; 2330 | typedef typename generic_type::vector_view vector_t; 2331 | typedef typename generic_type::string_view string_t; 2332 | 2333 | for (std::size_t i = 0; i < parameters.size(); ++i) 2334 | { 2335 | generic_type& gt = parameters[i]; 2336 | 2337 | if (generic_type::e_scalar == gt.type) 2338 | { 2339 | scalar_t x(gt); 2340 | ... 2341 | } 2342 | else if (generic_type::e_vector == gt.type) 2343 | { 2344 | vector_t vector(gt); 2345 | ... 2346 | } 2347 | else if (generic_type::e_string == gt.type) 2348 | { 2349 | string_t string(gt); 2350 | ... 2351 | } 2352 | } 2353 | 2354 | return T(0); 2355 | } 2356 | 2357 | 2358 | Most often than not a custom generic function will require a specific 2359 | sequence of parameters, rather than some arbitrary sequence of types. 2360 | In those situations, ExprTk can perform compile-time type checking to 2361 | validate that function invocations are carried out using the correct 2362 | sequence of parameters. Furthermore performing the checks at compile 2363 | -time rather than at run-time (aka every time the function is invoked) 2364 | will result in expression evaluation performance gains. 2365 | 2366 | Compile-time type checking of input parameters can be requested by 2367 | passing a string to the constructor of the igeneric_function that 2368 | represents the required sequence of parameter types. When no parameter 2369 | sequence is provided, it is implied the function can accept a variable 2370 | number of parameters comprised of any of the fundamental types. 2371 | 2372 | Each fundamental type has an associated character. The following is a 2373 | listing of said characters and their meanings: 2374 | 2375 | (1) T - Scalar 2376 | (2) V - Vector 2377 | (3) S - String 2378 | (4) Z - Zero or no parameters 2379 | (5) ? - Any type (Scalar, Vector or String) 2380 | (6) * - Wildcard operator 2381 | (7) | - Parameter sequence delimiter 2382 | 2383 | 2384 | No other characters other than the seven denoted above may be included 2385 | in the parameter sequence definition. If any such invalid characters 2386 | do exist, registration of the associated generic function to a symbol 2387 | table ('add_function' method) will fail. If the parameter sequence is 2388 | modified resulting in it becoming invalid after having been added to 2389 | the symbol table but before the compilation step, a compilation error 2390 | will be incurred. 2391 | 2392 | The following example demonstrates a simple generic function 2393 | implementation with a user specified parameter sequence: 2394 | 2395 | template <typename T> 2396 | struct moo final : public exprtk::igeneric_function<T> 2397 | { 2398 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2399 | parameter_list_t; 2400 | 2401 | moo() 2402 | : exprtk::igeneric_function<T>("SVTT") 2403 | {} 2404 | 2405 | inline T operator()(parameter_list_t parameters) override 2406 | { 2407 | ... 2408 | } 2409 | }; 2410 | 2411 | 2412 | In the example above the generic function 'moo' expects exactly four 2413 | parameters in the following sequence: 2414 | 2415 | (1) String 2416 | (2) Vector 2417 | (3) Scalar 2418 | (4) Scalar 2419 | 2420 | Note16: The 'Z' or no parameter option may not be used in conjunction 2421 | with any other type option in a parameter sequence. When incorporated 2422 | in the parameter sequence list, the 'No Parameter' option indicates 2423 | that the function may be invoked without any parameters being passed. 2424 | For more information refer to the section: 'Zero Parameter Functions' 2425 | 2426 | 2427 | (4) igeneric_function II 2428 | This interface is identical to the igeneric_function, in that in can 2429 | consume an arbitrary number of parameters of varying type, but the 2430 | difference being that the function returns a string and as such is 2431 | treated as a string when invoked within expressions. As a result the 2432 | function call can alias a string and interact with other strings in 2433 | situations such as concatenation and equality operations. 2434 | 2435 | String <-- function(i_0, i_1, i_2....., i_N) 2436 | 2437 | 2438 | The following example defines a generic function named 'toupper' with 2439 | the string return type function operator being explicitly overridden: 2440 | 2441 | template <typename T> 2442 | struct toupper final : public exprtk::igeneric_function<T> 2443 | { 2444 | typedef exprtk::igeneric_function<T> igenfunct_t; 2445 | typedef typename igenfunct_t::generic_type generic_t; 2446 | typedef typename igenfunct_t::parameter_list_t parameter_list_t; 2447 | typedef typename generic_t::string_view string_t; 2448 | 2449 | toupper() 2450 | : exprtk::igeneric_function<T>("S",igenfunct_t::e_rtrn_string) 2451 | {} 2452 | 2453 | inline T operator()(std::string& result, 2454 | parameter_list_t parameters) override 2455 | { 2456 | result.clear(); 2457 | 2458 | string_t string(parameters[0]); 2459 | 2460 | for (std::size_t i = 0; i < string.size(); ++i) 2461 | { 2462 | result += std::toupper(string[i]); 2463 | } 2464 | 2465 | return T(0); 2466 | } 2467 | }; 2468 | 2469 | 2470 | In the example above the generic function 'toupper' expects only one 2471 | input parameter of type string, as noted by the parameter sequence 2472 | string passed during the constructor. Furthermore a second parameter 2473 | is passed to the constructor indicating that it should be treated as a 2474 | string returning function - by default it is assumed to be a scalar 2475 | returning function. 2476 | 2477 | When executed, the function will return as a result a copy of the 2478 | input string converted to uppercase form. An example expression using 2479 | the toupper function registered as the symbol 'toupper' is as follows: 2480 | 2481 | "'ABCDEF' == toupper('aBc') + toupper('DeF')" 2482 | 2483 | 2484 | Note17: When adding a string type returning generic function to a 2485 | symbol table the 'add_function' is invoked. The example below 2486 | demonstrates how this can be done: 2487 | 2488 | toupper<T> tu; 2489 | 2490 | exprtk::symbol_table<T> symbol_table; 2491 | 2492 | symbol_table.add_function("toupper",tu); 2493 | 2494 | 2495 | Note18: Two further refinements to the type checking facility are the 2496 | possibilities of a variable number of common types which can be 2497 | accomplished by using a wildcard '*' and a special 'any type' which is 2498 | done using the '?' character. It should be noted that the wildcard 2499 | operator is associated with the previous type in the sequence and 2500 | implies one or more of that type. 2501 | 2502 | template <typename T> 2503 | struct zoo final : public exprtk::igeneric_function<T> 2504 | { 2505 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2506 | parameter_list_t; 2507 | 2508 | zoo() 2509 | : exprtk::igeneric_function<T>("SVT*V?") 2510 | {} 2511 | 2512 | inline T operator()(parameter_list_t parameters) override 2513 | { 2514 | ... 2515 | } 2516 | }; 2517 | 2518 | 2519 | In the example above the generic function 'zoo' expects at least five 2520 | parameters in the following sequence: 2521 | 2522 | (1) String 2523 | (2) Vector 2524 | (3) One or more Scalars 2525 | (4) Vector 2526 | (5) Any type (one type of either a scalar, vector or string) 2527 | 2528 | 2529 | A final piece of type checking functionality is available for the 2530 | scenarios where a single function name is intended to be used for 2531 | multiple distinct parameter sequences, another name for this feature 2532 | is function overloading. The parameter sequences are passed to the 2533 | constructor as a single string delimited by the pipe '|' character. 2534 | Two specific overrides of the function operator are provided one for 2535 | standard generic functions and one for string returning functions. The 2536 | overrides are as follows: 2537 | 2538 | // Scalar <-- function(psi,i_0,i_1,....,i_N) 2539 | inline T operator()(const std::size_t& ps_index, 2540 | parameter_list_t parameters) 2541 | { 2542 | ... 2543 | } 2544 | 2545 | // String <-- function(psi,i_0,i_1,....,i_N) 2546 | inline T operator()(const std::size_t& ps_index, 2547 | std::string& result, 2548 | parameter_list_t parameters) 2549 | { 2550 | ... 2551 | } 2552 | 2553 | 2554 | When the function operator is invoked the 'ps_index' parameter will 2555 | have as its value the index of the parameter sequence that matches the 2556 | specific invocation. This way complex and time consuming type checking 2557 | conditions need not be executed in the function itself but rather a 2558 | simple and efficient dispatch to a specific implementation for that 2559 | particular parameter sequence can be performed. 2560 | 2561 | template <typename T> 2562 | struct roo final : public exprtk::igeneric_function<T> 2563 | { 2564 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2565 | parameter_list_t; 2566 | 2567 | moo() 2568 | : exprtk::igeneric_function<T>("SVTT|SS|TTV|S?V*S") 2569 | {} 2570 | 2571 | inline T operator()(const std::size_t& ps_index, 2572 | parameter_list_t parameters) override 2573 | { 2574 | ... 2575 | } 2576 | }; 2577 | 2578 | 2579 | In the example above there are four distinct parameter sequences that 2580 | can be processed by the generic function 'roo'. Any other parameter 2581 | sequences will cause a compilation error. The four valid sequences are 2582 | as follows: 2583 | 2584 | Sequence-0 Sequence-1 Sequence-2 Sequence-3 2585 | 'SVTT' 'SS' 'TTV' 'S?V*S' 2586 | (1) String (1) String (1) Scalar (1) String 2587 | (2) Vector (2) String (2) Scalar (2) Any Type 2588 | (3) Scalar (3) Vector (3) One or more Vectors 2589 | (4) Scalar (4) String 2590 | 2591 | 2592 | (5) igeneric_function III 2593 | In this section we will discuss an extension of the igeneric_function 2594 | interface that will allow for the overloading of a user defined custom 2595 | function, where by it can return either a scalar or string value type 2596 | depending on the input parameter sequence with which the function is 2597 | invoked. 2598 | 2599 | template <typename T> 2600 | struct foo final : public exprtk::igeneric_function<T> 2601 | { 2602 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2603 | parameter_list_t; 2604 | 2605 | foo() 2606 | : exprtk::igeneric_function<T> 2607 | ( 2608 | "T:T|S:TS", 2609 | igfun_t::e_rtrn_overload 2610 | ) 2611 | {} 2612 | 2613 | // Scalar value returning invocations 2614 | inline T operator()(const std::size_t& ps_index, 2615 | parameter_list_t parameters) override 2616 | { 2617 | ... 2618 | } 2619 | 2620 | // String value returning invocations 2621 | inline T operator()(const std::size_t& ps_index, 2622 | std::string& result, 2623 | parameter_list_t& parameters) override 2624 | { 2625 | ... 2626 | } 2627 | }; 2628 | 2629 | 2630 | In the example above the custom user defined function "foo" can be 2631 | invoked by using either one of two input parameter sequences, which 2632 | are defined as follows: 2633 | 2634 | Sequence-0 Sequence-1 2635 | 'T' -> T 'TS' -> S 2636 | (1) Scalar (1) Scalar 2637 | (2) String 2638 | 2639 | 2640 | The parameter sequence definitions are identical to the previously 2641 | defined igeneric_function, with the exception of the inclusion of the 2642 | return type - which can only be either a scalar T or a string S. 2643 | 2644 | 2645 | (6) function_compositor 2646 | The function compositor is a factory that allows one to define and 2647 | construct a function using ExprTk syntax. The functions are limited to 2648 | returning a single scalar value and consuming up to six parameters as 2649 | input. 2650 | 2651 | All composited functions are registered with a symbol table, allowing 2652 | them to call other functions and use variables that have been 2653 | registered with the symbol table instance. Furthermore the functions 2654 | can be recursive in nature due to the inherent function prototype 2655 | forwarding that occurs during construction. The following example 2656 | defines, by using two different methods, composited functions and 2657 | implicitly registering the functions with the denoted symbol table. 2658 | 2659 | typedef exprtk::symbol_table<T> symbol_table_t; 2660 | typedef exprtk::function_compositor<T> compositor_t; 2661 | typedef typename compositor_t::function function_t; 2662 | 2663 | T avogadro = T(6.022e23); 2664 | 2665 | symbol_table_t symbol_table; 2666 | 2667 | symbol_table.add_constant("avogadro", avogadro); 2668 | 2669 | compositor_t compositor(symbol_table); 2670 | 2671 | // Define function koo0(v1, v2) { ... } 2672 | compositor.add( 2673 | function_t("koo0"), 2674 | .vars("v1", "v2") 2675 | .expression 2676 | ( 2677 | " 1 + cos(v1 * v2) / avogadro; " 2678 | )); 2679 | 2680 | // Define function koo1(x, y, z) { ... } 2681 | compositor.add( 2682 | function_t() 2683 | .name("koo1") 2684 | .var("x").var("y").var("z") 2685 | .expression 2686 | ( 2687 | "1 + koo0(x * y, 3) / z;" 2688 | )); 2689 | 2690 | 2691 | A function compositor can also be instantiated without a symbol_table. 2692 | When this is the case an internal symbol_table is used for holding the 2693 | references to the composited functions. 2694 | 2695 | compositor_t compositor; 2696 | 2697 | // Define function koo2(v1, v2) { ... } 2698 | compositor.add( 2699 | function_t("koo2"), 2700 | .vars("v1", "v2", "v3") 2701 | .expression 2702 | ( " abs(v1 * v2) / v3; " )); 2703 | 2704 | 2705 | When wanting to reference functions from the compositor above in an 2706 | expression, the compositor's symbol_table will need to be registered 2707 | with the expression prior to compilation, as is demonstrated in the 2708 | following code: 2709 | 2710 | expression_t expression; 2711 | . 2712 | . 2713 | expression.register_symbol_table(compositor.symbol_table()); 2714 | 2715 | 2716 | In the situation where more than one symbol table's contents will be 2717 | required by the functions being composited, then those symbol tables 2718 | can be registered as auxiliary symbol tables with the compositor: 2719 | 2720 | symbol_table_t global_symbol_table; 2721 | symbol_table_t local_symbol_table; 2722 | . 2723 | . 2724 | . 2725 | compositor_t compositor; 2726 | 2727 | compositor.add_auxiliary_symtab(global_symbol_table); 2728 | compositor.add_auxiliary_symtab(local_symbol_table ); 2729 | 2730 | Note19: In the event, that two or more symbol tables contain similarly 2731 | named variables, vectors, strings or functions, the order of 2732 | registration with the compositor shall determine the symbol table from 2733 | which the target symbol will be referenced. 2734 | 2735 | 2736 | (7) Using Functions In Expressions 2737 | For the above denoted custom and composited functions to be used in an 2738 | expression, an instance of each function needs to be registered with a 2739 | symbol_table that has been associated with the expression instance. 2740 | The following demonstrates how all the pieces are put together: 2741 | 2742 | typedef exprtk::symbol_table<double> symbol_table_t; 2743 | typedef exprtk::expression<double> expression_t; 2744 | typedef exprtk::parser<double> parser_t; 2745 | typedef exprtk::function_compositor<double> compositor_t; 2746 | typedef typename compositor_t::function function_t; 2747 | 2748 | foo<double> f; 2749 | boo<double> b; 2750 | too<double> t; 2751 | toupper<double> tu; 2752 | 2753 | symbol_table_t symbol_table; 2754 | compositor_t compositor(symbol_table); 2755 | 2756 | symbol_table.add_function("foo",f); 2757 | symbol_table.add_function("boo",b); 2758 | symbol_table.add_function("too",t); 2759 | 2760 | symbol_table 2761 | .add_function("toupper", tu, symbol_table_t::e_ft_strfunc); 2762 | 2763 | compositor.add( 2764 | function_t("koo") 2765 | .var("v1") 2766 | .var("v2") 2767 | .expression 2768 | ( 2769 | "1 + cos(v1 * v2) / 3;" 2770 | )); 2771 | 2772 | expression_t expression; 2773 | expression.register_symbol_table(symbol_table); 2774 | 2775 | const std::string expression_str = 2776 | " if (foo(1,2,3) + boo(1) > boo(1/2, 2/3, 3/4, 4/5)) " 2777 | " koo(3,4); " 2778 | " else " 2779 | " too(2 * v1 + v2 / 3, 'abcdef'[2:4], 3.3); " 2780 | " " 2781 | 2782 | parser_t parser; 2783 | parser.compile(expression_str,expression); 2784 | 2785 | expression.value(); 2786 | 2787 | 2788 | (8) Function Side-Effects 2789 | All function calls are assumed to have side-effects by default. This 2790 | assumption implicitly disables constant folding optimisations when all 2791 | parameters being passed to the function are deduced as being constants 2792 | at compile time. 2793 | 2794 | If it is certain that the function being registered does not have any 2795 | side-effects and can be correctly constant folded where appropriate, 2796 | then during the construction of the function the side-effect trait of 2797 | the function can be disabled. 2798 | 2799 | template <typename T> 2800 | struct foo final : public exprtk::ifunction<T> 2801 | { 2802 | foo() : exprtk::ifunction<T>(3) 2803 | { 2804 | exprtk::disable_has_side_effects(*this); 2805 | } 2806 | 2807 | T operator()(const T& v1, const T& v2, const T& v3) override 2808 | { ... } 2809 | }; 2810 | 2811 | 2812 | (9) Zero Parameter Functions 2813 | When either an ifunction, ivararg_function or igeneric_function 2814 | derived type is defined with zero number of parameters, there are two 2815 | calling conventions within expressions that are allowed. For a 2816 | function named 'foo' with zero input parameters the calling styles are 2817 | as follows: 2818 | 2819 | (1) x + sin(foo()- 2) / y 2820 | (2) x + sin(foo - 2) / y 2821 | 2822 | 2823 | By default the zero parameter trait is disabled. In order to enable 2824 | it, a process similar to that of enabling of the side-effect trait is 2825 | carried out: 2826 | 2827 | template <typename T> 2828 | struct foo final : public exprtk::ivararg_function<T> 2829 | { 2830 | foo() 2831 | { 2832 | exprtk::enable_zero_parameters(*this); 2833 | } 2834 | 2835 | inline T operator()(const std::vector<T>& arglist) override 2836 | { ... } 2837 | }; 2838 | 2839 | 2840 | Note20: For the igeneric_function type, there also needs to be a 'Z' 2841 | parameter sequence defined in order for the zero parameter trait to 2842 | properly take effect otherwise a compilation error will occur. 2843 | 2844 | 2845 | (10) Free Functions 2846 | The ExprTk symbol table supports the registration of free functions 2847 | and lambdas (anonymous functors) for use in expressions. The basic 2848 | requirements are similar to those found in ifunction derived user 2849 | defined functions. This includes support for free functions using 2850 | anywhere from zero up to fifteen input parameters of scalar type, with 2851 | a return type that is also scalar. Furthermore such functions will by 2852 | default be assumed to have side-effects and hence will not participate 2853 | in constant folding optimisations. 2854 | 2855 | In the following example, a one input parameter free function named 2856 | 'compute1', a two input parameter template free function named 2857 | 'compute2' and a three input parameter lambda named 'compute3' will be 2858 | registered with the given symbol_table instance: 2859 | 2860 | double compute1(double v0) 2861 | { 2862 | return 2.0 * std::abs(v0); 2863 | } 2864 | 2865 | template <typename T> 2866 | T compute2(T v0, T v1) 2867 | { 2868 | return 2.0 * v0 + v1 / 3.0; 2869 | } 2870 | . 2871 | . 2872 | . 2873 | 2874 | typedef exprtk::symbol_table<double> symbol_table_t; 2875 | 2876 | symbol_table_t symbol_table; 2877 | 2878 | symbol_table.add_function("compute1", compute1); 2879 | symbol_table.add_function("compute2", compute2<double>); 2880 | 2881 | symbol_table.add_function( 2882 | "compute3", 2883 | [](double v0, double v1, double v2) -> double 2884 | { return v0 / v1 + v2; }); 2885 | 2886 | 2887 | Note21: Similar to variables registered with symbol_table instances, 2888 | for any of the following function providers: 2889 | 2890 | 1. ifunction 2891 | 2. ivararg_function 2892 | 3. igeneric_function 2893 | 4. function_compositor 2894 | 5. Free function 2895 | 7. Lambda 2896 | 2897 | 2898 | Their instance lifetimes must exceed the symbol_tables and expressions 2899 | they are registered with. In the event that is not the case, the 2900 | expected result shall be undefined behaviour. 2901 | 2902 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2903 | 2904 | [SECTION 16 - EXPRESSION DEPENDENTS] 2905 | Any expression that is not a literal (aka constant) will have 2906 | dependencies. The types of 'dependencies' an expression can have are 2907 | as follows: 2908 | 2909 | (a) Variables 2910 | (b) Vectors 2911 | (c) Strings 2912 | (d) Functions 2913 | (e) Assignments 2914 | 2915 | 2916 | In the following example the denoted expression has its various 2917 | dependencies listed: 2918 | 2919 | z := abs(x + sin(2 * pi / y)) 2920 | 2921 | (a) Variables: x, y, z and pi 2922 | (b) Functions: abs, sin 2923 | (c) Assignments: z 2924 | 2925 | 2926 | ExprTk allows for the derivation of expression dependencies via the 2927 | 'dependent_entity_collector' (DEC). When activated either through 2928 | 'compile_options' at the construction of the parser or through calls 2929 | to enabler methods just prior to compilation, the DEC will proceed to 2930 | collect any of the relevant types that are encountered during the 2931 | parsing phase. Once the compilation process has successfully 2932 | completed, the caller can then obtain a list of symbols and their 2933 | associated types from the DEC. 2934 | 2935 | The kinds of questions one can ask regarding the dependent entities 2936 | within an expression are as follows: 2937 | 2938 | * What user defined variables, vectors or strings are used? 2939 | * What functions or custom user functions are used? 2940 | * Which variables, vectors or strings have values assigned to them? 2941 | 2942 | 2943 | The following example demonstrates usage of the DEC in determining the 2944 | dependents of the given expression: 2945 | 2946 | typedef typename parser_t:: 2947 | dependent_entity_collector::symbol_t symbol_t; 2948 | 2949 | const std::string expression_string = 2950 | "z := abs(x + sin(2 * pi / y))" 2951 | 2952 | T x,y,z; 2953 | 2954 | parser_t parser; 2955 | symbol_table_t symbol_table; 2956 | 2957 | symbol_table.add_variable("x",x); 2958 | symbol_table.add_variable("y",y); 2959 | symbol_table.add_variable("z",z); 2960 | 2961 | expression_t expression; 2962 | expression.register_symbol_table(symbol_table); 2963 | 2964 | // Collect only variable and function symbols 2965 | parser.dec().collect_variables() = true; 2966 | parser.dec().collect_functions() = true; 2967 | 2968 | if (!parser.compile(expression_string,expression)) 2969 | { 2970 | // error.... 2971 | } 2972 | 2973 | std::deque<symbol_t> symbol_list; 2974 | 2975 | parser.dec().symbols(symbol_list); 2976 | 2977 | for (std::size_t i = 0; i < symbol_list.size(); ++i) 2978 | { 2979 | const symbol_t& symbol = symbol_list[i]; 2980 | 2981 | switch (symbol.second) 2982 | { 2983 | case parser_t::e_st_variable : ... break; 2984 | case parser_t::e_st_vector : ... break; 2985 | case parser_t::e_st_string : ... break; 2986 | case parser_t::e_st_function : ... break; 2987 | } 2988 | } 2989 | 2990 | 2991 | Note22: The 'symbol_t' type is a std::pair comprising of the symbol 2992 | name (std::string) and the associated type of the symbol as denoted by 2993 | the cases in the switch statement. 2994 | 2995 | Having particular symbols (variable or function) present in an 2996 | expression is one form of dependency. Another and just as interesting 2997 | and important type of dependency is that of assignments. Assignments 2998 | are the set of dependent symbols that 'may' have their values modified 2999 | within an expression. The following are example expressions and their 3000 | associated assignments: 3001 | 3002 | Assignments Expression 3003 | (1) x x := y + z 3004 | (2) x, y x += y += z 3005 | (3) x, y, z x := y += sin(z := w + 2) 3006 | (4) w, z if (x > y, z := x + 2, w := 'A String') 3007 | (5) None x + y + z 3008 | 3009 | 3010 | Note23: In expression 4, both variables 'w' and 'z' are denoted as 3011 | being assignments even though only one of them can ever be modified at 3012 | the time of evaluation. Furthermore the determination of which of the 3013 | two variables the modification will occur upon can only be known with 3014 | certainty at evaluation time and not beforehand, hence both are listed 3015 | as being candidates for assignment. 3016 | 3017 | The following builds upon the previous example demonstrating the usage 3018 | of the DEC in determining the 'assignments' of the given expression: 3019 | 3020 | // Collect assignments 3021 | parser.dec().collect_assignments() = true; 3022 | 3023 | if (!parser.compile(expression_string,expression)) 3024 | { 3025 | // error.... 3026 | } 3027 | 3028 | std::deque<symbol_t> symbol_list; 3029 | 3030 | parser.dec().assignment_symbols(symbol_list); 3031 | 3032 | for (std::size_t i = 0; i < symbol_list.size(); ++i) 3033 | { 3034 | symbol_t& symbol = symbol_list[i]; 3035 | 3036 | switch (symbol.second) 3037 | { 3038 | case parser_t::e_st_variable : ... break; 3039 | case parser_t::e_st_vector : ... break; 3040 | case parser_t::e_st_string : ... break; 3041 | } 3042 | } 3043 | 3044 | 3045 | Note24: The assignments will only consist of variable types and as 3046 | such will not contain symbols denoting functions. 3047 | 3048 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3049 | 3050 | [SECTION 17 - HIERARCHIES OF SYMBOL TABLES] 3051 | Most situations will only require a single symbol_table instance to be 3052 | associated with a given expression instance. 3053 | 3054 | However as an expression can have more than one symbol table instance 3055 | associated with itself, when building more complex systems that 3056 | utilise many expressions where each can in turn utilise one or more 3057 | variables from a large set of potential variables, functions or 3058 | constants, it becomes evident that grouping variables into layers of 3059 | symbol_tables will simplify and streamline the overall process. 3060 | 3061 | A recommended hierarchy of symbol tables is the following: 3062 | 3063 | (a) Global constant value symbol table 3064 | (b) Global non side-effect functions symbol table 3065 | (c) Global variable symbol table 3066 | (d) Expression specific variable symbol table 3067 | 3068 | 3069 | (a) Global constant value symbol table 3070 | This symbol table will contain constant variables denoting immutable 3071 | values. These variables can be made available to all expressions, and 3072 | in turn expressions will assume the values themselves will never be 3073 | modified for the duration of the process run-time. Examples of such 3074 | variables are: 3075 | 3076 | (1) pi or e 3077 | (2) speed_of_light 3078 | (3) avogadro_number 3079 | (4) num_cpus 3080 | 3081 | 3082 | (b) Global non side-effect functions symbol table 3083 | This symbol table will contain only user defined functions that will 3084 | not incur any side-effects that are observable to any of the 3085 | expressions that invoke them. These functions shall be thread-safe or 3086 | threading invariant and will not maintain any form of state between 3087 | invocations. Examples of such functions are: 3088 | 3089 | (1) calc_volume_of_sphere(r) 3090 | (2) distance(x0,y0,x1,y1) 3091 | 3092 | 3093 | (c) Global variable symbol table 3094 | This symbol table will contain variables that will be accessible to 3095 | all associated expressions and will not be specific or exclusive to 3096 | any one expression. This variant differs from (a) in that the values 3097 | of the variables can change (or be updated) between evaluations of 3098 | expressions - but through properly scheduled evaluations are 3099 | guaranteed to never change during the evaluation of any dependent 3100 | expressions. Furthermore it is assumed that these variables will be 3101 | used in a read-only context and that no expressions will attempt to 3102 | modify these variables via assignments or other means. 3103 | 3104 | (1) price_of_stock_xyz 3105 | (2) outside_temperature or inside_temperature 3106 | (3) fuel_in_tank 3107 | (4) num_customers_in_store 3108 | (5) num_items_on_shelf 3109 | 3110 | 3111 | (d) Expression specific variable symbol table 3112 | This symbol_table is the most common form, and is used to store 3113 | variables that are specific and exclusive to a particular expression. 3114 | That is to say references to variables in this symbol_table will not 3115 | be part of another expression. Though it may be possible to have 3116 | expressions that contain the variables with the same name, in that 3117 | case those variables will be distinctly different. Which would mean if 3118 | a particular expression were to be compiled twice, each expression 3119 | would have its own unique symbol_table which in turn would have its 3120 | own instances of those variables. Examples of such variables could be: 3121 | 3122 | (1) x or y 3123 | (2) customer_name 3124 | 3125 | 3126 | The following is a diagram depicting a possible variant of the denoted 3127 | symbol table hierarchies. In the diagram there are two unique 3128 | expressions, each of which have a reference to the Global constant, 3129 | functions and variables symbol tables and an exclusive reference to a 3130 | local symbol table. 3131 | 3132 | +-------------------------+ +-------------------------+ 3133 | | Global Constants | | Global Functions | 3134 | | Symbol Table | | Symbol Table | 3135 | +----o--o-----------------+ +--------------------o----+ 3136 | | | | 3137 | | | +-------+ 3138 | | +------------------->----------------------------+ | 3139 | | +----------------------------+ | | 3140 | | | Global Variables | | | 3141 | | +------o Symbol Table o-----+ | V 3142 | | | +----------------------------+ | | | 3143 | | | | | | 3144 | | | +----------------+ +----------------+ | | | 3145 | | | | Symbol Table 0 | | Symbol Table 1 | | V | 3146 | | | +--o-------------+ +--o-------------+ | | | 3147 | | | | | | | | 3148 | | | | | | | | 3149 | +--V--V----V---------+ +-V---------------V--+ | | 3150 | | Expression 0 | | Expression 1 |<--+--+ 3151 | | '2 * sin(x) - y' | | 'k + abs(x - y)' | 3152 | +--------------------+ +--------------------+ 3153 | 3154 | 3155 | Bringing all of the above together, in the following example the 3156 | hierarchy of symbol tables are instantiated and initialised. An 3157 | expression that makes use of various elements of each symbol table is 3158 | then compiled and later on evaluated: 3159 | 3160 | typedef exprtk::symbol_table<double> symbol_table_t; 3161 | typedef exprtk::expression<double> expression_t; 3162 | 3163 | // Setup global constants symbol table 3164 | symbol_table_t glbl_const_symbol_table; 3165 | glbl_const_symbtab.add_constants(); // pi, epsilon and inf 3166 | glbl_const_symbtab.add_constant("speed_of_light",299e6); 3167 | glbl_const_symbtab.add_constant("avogadro_number",6e23); 3168 | 3169 | // Setup global function symbol table 3170 | symbol_table_t glbl_funcs_symbol_table; 3171 | glbl_func_symbtab.add_function('distance',distance); 3172 | glbl_func_symbtab.add_function('calc_spherevol',calc_sphrvol); 3173 | 3174 | ...... 3175 | 3176 | // Setup global variable symbol table 3177 | symbol_table_t glbl_variable_symbol_table; 3178 | glbl_variable_symbtab.add_variable('temp_outside',thermo.outside); 3179 | glbl_variable_symbtab.add_variable('temp_inside' ,thermo.inside ); 3180 | glbl_variable_symbtab.add_variable('num_cstmrs',store.num_cstmrs); 3181 | 3182 | ...... 3183 | 3184 | double x,y,z; 3185 | 3186 | // Setup expression specific symbol table 3187 | symbol_table_t symbol_table; 3188 | symbol_table.add_variable('x',x); 3189 | symbol_table.add_variable('y',y); 3190 | symbol_table.add_variable('z',z); 3191 | 3192 | expression_t expression; 3193 | 3194 | // Register the various symbol tables 3195 | expression 3196 | .register_symbol_table(symbol_table); 3197 | 3198 | expression 3199 | .register_symbol_table(glbl_funcs_symbol_table); 3200 | 3201 | expression 3202 | .register_symbol_table(glbl_const_symbol_table); 3203 | 3204 | expression 3205 | .register_symbol_table(glbl_variable_symbol_table); 3206 | 3207 | const std::string expression_str = 3208 | "abs(temp_inside - temp_outside) + 2 * speed_of_light / x" 3209 | 3210 | parser_t parser; 3211 | parser.compile(expression_str,expression); 3212 | 3213 | ...... 3214 | 3215 | while (keep_evaluating) 3216 | { 3217 | .... 3218 | 3219 | T result = expression.value(); 3220 | 3221 | .... 3222 | } 3223 | 3224 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3225 | 3226 | [SECTION 18 - UNKNOWN UNKNOWNS] 3227 | In this section we will discuss the process of handling expressions 3228 | with a mix of known and unknown variables. Initially a discussion into 3229 | the types of expressions that exist will be provided, then a series of 3230 | possible solutions will be presented for each scenario. 3231 | 3232 | When parsing an expression, there may be situations where one is not 3233 | fully aware of what if any variables will be used prior to the 3234 | expression being compiled. 3235 | 3236 | This can become problematic, as in the default scenario it is assumed 3237 | the symbol_table that is registered with the expression instance will 3238 | already possess the externally available variables, functions and 3239 | constants needed during the compilation of the expression. 3240 | 3241 | In the event there are symbols in the expression that can't be mapped 3242 | to either a reserved word, or located in the associated 3243 | symbol_table(s), an "Undefined symbol" error will be raised and the 3244 | compilation process will fail. 3245 | 3246 | The numerous scenarios that can occur when compiling an expression 3247 | with ExprTk generally fall into one of the following three categories: 3248 | 3249 | (a) No external variables 3250 | (b) Predetermined set of external variables 3251 | (c) Unknown set of variables 3252 | 3253 | 3254 | (a) No external variables 3255 | These are expressions that contain no external variables but may 3256 | contain local variables. As local variables cannot be accessed 3257 | externally from the expression, it is assumed that such expressions 3258 | will not have a need for a symbol_table and furthermore expressions 3259 | which don't make use of functions that have side-effects will be 3260 | evaluated completely at compile time resulting in a constant return 3261 | value. The following are examples of such expressions: 3262 | 3263 | (1) 1 + 2 3264 | (2) var x := 3; 2 * x - 3 3265 | (3) var x := 3; var y := abs(x - 8); x - y / 7 3266 | 3267 | 3268 | (b) Predetermined set of external variables 3269 | These are expressions that are comprised of externally available 3270 | variables and functions and will only compile successfully if the 3271 | symbols that correspond to the variables and functions are already 3272 | defined in their associated symbol_table(s). This is by far the most 3273 | common scenario when using ExprTk. 3274 | 3275 | As an example, one may have three external variables: x, y and z which 3276 | have been registered with the associated symbol_table, and will then 3277 | need to compile and evaluate expressions comprised of any subset of 3278 | these three variables. The following are a few examples of such 3279 | expressions: 3280 | 3281 | (1) 1 + x 3282 | (2) x / y 3283 | (3) 2 * x * y / z 3284 | 3285 | 3286 | In this scenario one can use the 'dependent_entity_collector' 3287 | component as described in [Section 16] to further determine which of 3288 | the registered variables were actually used in the given expression. 3289 | As an example once the set of utilised variables are known, any 3290 | further 'attention' can be restricted to only those variables when 3291 | evaluating the expression. This can be quite useful when dealing with 3292 | expressions that can draw from a set of hundreds or even thousands of 3293 | variables. 3294 | 3295 | 3296 | (c) Unknown set of variables 3297 | These are expressions that are comprised of symbols other than the 3298 | standard ExprTk reserved words or what has been registered with their 3299 | associated symbol_table, and will normally fail compilation due to the 3300 | associated symbol_table not having a reference to them. As such this 3301 | scenario can be seen as a combination of scenario B, where one may 3302 | have a symbol_table with registered variables, but would also like to 3303 | handle the situation of variables that aren't present in said 3304 | symbol_table. 3305 | 3306 | When dealing with expressions of category (c), one must perform all of 3307 | the following: 3308 | 3309 | (1) Determine the variables used in the expression 3310 | (2) Populate a symbol_table(s) with the entities from (1) 3311 | (3) Compile the expression 3312 | (4) Provide a means by which the entities from (1) can be modified 3313 | 3314 | 3315 | Depending on the nature of processing, steps (1) and (2) can be done 3316 | either independently of each other or combined into one. The following 3317 | example will initially look at solving the problem of unknown 3318 | variables with the latter method using the 'unknown_symbol_resolver' 3319 | component. 3320 | 3321 | typedef exprtk::symbol_table<T> symbol_table_t; 3322 | typedef exprtk::expression<T> expression_t; 3323 | typedef exprtk::parser<T> parser_t; 3324 | 3325 | T x = T(123.456); 3326 | T y = T(789.123); 3327 | 3328 | symbol_table_t unknown_var_symbol_table; 3329 | 3330 | symbol_table_t symbol_table; 3331 | symbol_table.add_variable("x",x); 3332 | symbol_table.add_variable("y",y); 3333 | 3334 | expression_t expression; 3335 | expression.register_symbol_table(unknown_var_symbol_table); 3336 | expression.register_symbol_table(symbol_table); 3337 | 3338 | parser_t parser; 3339 | parser.enable_unknown_symbol_resolver(); 3340 | 3341 | const std::string expression_str = "x + abs(y / 3k) * z + 2" 3342 | 3343 | parser.compile(expression_str,expression); 3344 | 3345 | 3346 | In the example above, the symbols 'k' and 'z' will be treated as 3347 | unknown symbols. The parser in the example is set to handle unknown 3348 | symbols using the built-in default unknown_symbol_resolver (USR). The 3349 | default USR will automatically resolve any unknown symbols as a 3350 | variable (scalar type). The new variables will be added to the primary 3351 | symbol_table, which in this case is the 'unknown_var_symbol_table' 3352 | instance. Once the compilation has completed successfully, the 3353 | variables that were resolved during compilation can be accessed from 3354 | the primary symbol_table using the 'get_variable_list' and 3355 | 'variable_ref' methods and then if needed can be modified accordingly 3356 | after which the expression itself can be evaluated. 3357 | 3358 | std::vector<std::string> variable_list; 3359 | 3360 | unknown_var_symbol_table.get_variable_list(variable_list); 3361 | 3362 | for (const auto& var_name : variable_list) 3363 | { 3364 | T& v = unknown_var_symbol_table.variable_ref(var_name); 3365 | 3366 | v = ...; 3367 | } 3368 | 3369 | ... 3370 | 3371 | expression.value(); 3372 | 3373 | 3374 | Note25: As previously mentioned the default USR will automatically 3375 | assume any unknown symbol to be a valid scalar variable, and will then 3376 | proceed to add said symbol as a variable to the primary symbol_table 3377 | of the associated expression during the compilation process. However a 3378 | problem that may arise, is that expressions that are parsed with the 3379 | USR enabled, but contain 'typos' or otherwise syntactic errors may 3380 | inadvertently compile successfully due to the simplistic nature of the 3381 | default USR. The following are some example expressions: 3382 | 3383 | (1) 1 + abz(x + 1) 3384 | (2) sine(y / 2) - coz(3x) 3385 | 3386 | 3387 | The two expressions above contain misspelt symbols (abz, sine, coz) 3388 | which if implied multiplications and default USR are enabled during 3389 | compilation will result in them being assumed to be valid 'variables', 3390 | which obviously is not the intended outcome by the user. A possible 3391 | solution to this problem is for one to implement their own specific 3392 | USR that will perform a user defined business logic in determining if 3393 | an encountered unknown symbol should be treated as a variable or if it 3394 | should raise a compilation error. The following example demonstrates a 3395 | simple user defined USR: 3396 | 3397 | typedef exprtk::symbol_table<T> symbol_table_t; 3398 | typedef exprtk::expression<T> expression_t; 3399 | typedef exprtk::parser<T> parser_t; 3400 | 3401 | template <typename T> 3402 | struct my_usr final : public parser_t::unknown_symbol_resolver 3403 | { 3404 | typedef typename parser_t::unknown_symbol_resolver usr_t; 3405 | 3406 | bool process(const std::string& unknown_symbol, 3407 | typename usr_t::usr_symbol_type& st, 3408 | T& default_value, 3409 | std::string& error_message) override 3410 | { 3411 | if (0 != unknown_symbol.find("var_")) 3412 | { 3413 | error_message = "Invalid symbol: " + unknown_symbol; 3414 | return false; 3415 | } 3416 | 3417 | st = usr_t::e_usr_variable_type; 3418 | default_value = T(123.123); 3419 | 3420 | return true; 3421 | } 3422 | }; 3423 | 3424 | ... 3425 | 3426 | T x = T(123.456); 3427 | T y = T(789.123); 3428 | 3429 | symbol_table_t unknown_var_symbol_table; 3430 | 3431 | symbol_table_t symbol_table; 3432 | symbol_table.add_variable("x",x); 3433 | symbol_table.add_variable("y",y); 3434 | 3435 | expression_t expression; 3436 | expression.register_symbol_table(unknown_var_symbol_table); 3437 | expression.register_symbol_table(symbol_table); 3438 | 3439 | my_usr<T> musr; 3440 | 3441 | parser_t parser; 3442 | parser.enable_unknown_symbol_resolver(&musr); 3443 | 3444 | std::string expression_str = "var_x + abs(var_y - 3) * var_z" 3445 | 3446 | parser.compile(expression_str,expression); 3447 | 3448 | 3449 | In the example above, a user specified USR is defined, and is 3450 | registered with the parser enabling the USR functionality. 3451 | Subsequently during the compilation process when an unknown symbol is 3452 | encountered, the USR's process method will be invoked. The USR in the 3453 | example will only 'accept' unknown symbols that have a prefix of 3454 | 'var_' as being valid variables, all other unknown symbols will result 3455 | in a compilation error being raised. 3456 | 3457 | In the example above the callback of the USR that is invoked during 3458 | the unknown symbol resolution process only allows for scalar variables 3459 | to be defined and resolved - as that is the simplest and most common 3460 | form. 3461 | 3462 | There is a further extended version of the callback that can be 3463 | overridden that will allow for more control and choice over the type 3464 | of symbol being resolved. The following is an example definition of 3465 | said extended callback: 3466 | 3467 | template <typename T> 3468 | struct my_usr final : public parser_t::unknown_symbol_resolver 3469 | { 3470 | typedef typename parser_t::unknown_symbol_resolver usr_t; 3471 | 3472 | my_usr() 3473 | : usr_t(usr_t::e_usrmode_extended) 3474 | {} 3475 | 3476 | bool process(const std::string& unknown_symbol, 3477 | symbol_table_t& symbol_table, 3478 | std::string& error_message) override 3479 | { 3480 | bool result = false; 3481 | 3482 | if (0 == unknown_symbol.find("var_")) 3483 | { 3484 | // Default value of zero 3485 | result = symbol_table.create_variable(unknown_symbol,0); 3486 | 3487 | if (!result) 3488 | { 3489 | error_message = "Failed to create variable..." 3490 | } 3491 | } 3492 | else if (0 == unknown_symbol.find("str_")) 3493 | { 3494 | // Default value of empty string 3495 | result = symbol_table.create_stringvar(unknown_symbol,""); 3496 | 3497 | if (!result) 3498 | { 3499 | error_message = "Failed to create string variable..." 3500 | } 3501 | } 3502 | else 3503 | error_message = "Indeterminable symbol type." 3504 | 3505 | return result; 3506 | } 3507 | }; 3508 | 3509 | 3510 | In the example above, the USR callback when invoked will pass the 3511 | primary symbol table associated with the expression being parsed. The 3512 | symbol resolution business logic can then determine under what 3513 | conditions a symbol will be resolved including its type (scalar, 3514 | string, vector etc) and default value. When the callback successfully 3515 | returns the symbol parsing and resolution process will again be 3516 | executed by the parser. The idea here is that given the primary symbol 3517 | table will now have the previously detected unknown symbol registered, 3518 | it will be correctly resolved and the general parsing processing can 3519 | then resume as per normal. 3520 | 3521 | Note26: In order to have the USR's extended mode callback be invoked 3522 | it is necessary to pass the e_usrmode_extended enum value during the 3523 | constructor of the user defined USR. 3524 | 3525 | Note27: The primary symbol table for an expression is the first symbol 3526 | table to be registered with that instance of the expression. 3527 | 3528 | Note28: For a successful symbol resolution using the normal USR all of 3529 | the following are required: 3530 | 3531 | (1) Only if successful shall the process method return TRUE 3532 | (2) The default_value parameter will have been set 3533 | (3) The error_message parameter will be empty 3534 | (4) usr_symbol_type input parameter field will be set to either: 3535 | (*) e_usr_variable_type 3536 | (*) e_usr_constant_type 3537 | 3538 | Note29: For a successful symbol resolution using the extended USR all 3539 | of the following are required: 3540 | 3541 | (1) Only if successful shall the process method return TRUE 3542 | (2) symbol_table parameter will have had the newly resolved 3543 | variable or string added to it 3544 | (3) error_message parameter will be empty 3545 | 3546 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3547 | 3548 | [SECTION 19 - ENABLING & DISABLING FEATURES] 3549 | The parser can be configured via its settings instance to either allow 3550 | or disallow certain features that are available within the ExprTk 3551 | grammar. The features fall into one of the following six categories: 3552 | 3553 | (1) Base Functions 3554 | (2) Control Flow Structures 3555 | (3) Logical Operators 3556 | (4) Arithmetic Operators 3557 | (5) Inequality Operators 3558 | (6) Assignment Operators 3559 | 3560 | 3561 | (1) Base Functions 3562 | The list of available base functions is as follows: 3563 | 3564 | abs, acos, acosh, asin, asinh, atan, atanh, atan2, avg, ceil, 3565 | clamp, cos, cosh, cot, csc, equal, erf, erfc, exp, expm1, 3566 | floor, frac, hypot, iclamp, like, log, log10, log2, logn, 3567 | log1p, mand, max, min, mod, mor, mul, ncdf, pow, root, round, 3568 | roundn, sec, sgn, sin, sinc, sinh, sqrt, sum, swap, tan, tanh, 3569 | trunc, not_equal, inrange, deg2grad, deg2rad, rad2deg, grad2deg 3570 | 3571 | 3572 | The above mentioned base functions can be either enabled or disabled 3573 | 'all' at once, as is demonstrated below: 3574 | 3575 | parser_t parser; 3576 | expression_t expression; 3577 | 3578 | parser.settings().disable_all_base_functions(); 3579 | 3580 | parser 3581 | .compile("2 * abs(2 - 3)",expression); // compilation failure 3582 | 3583 | parser.settings().enable_all_base_functions(); 3584 | 3585 | parser 3586 | .compile("2 * abs(2 - 3)",expression); // compilation success 3587 | 3588 | 3589 | One can also enable or disable specific base functions. The following 3590 | example demonstrates the disabling of the trigonometric functions 3591 | 'sin' and 'cos': 3592 | 3593 | parser_t parser; 3594 | expression_t expression; 3595 | 3596 | parser.settings() 3597 | .disable_base_function(settings_t::e_bf_sin) 3598 | .disable_base_function(settings_t::e_bf_cos); 3599 | 3600 | parser 3601 | .compile("(sin(x) / cos(x)) == tan(x)",expression); // failure 3602 | 3603 | parser.settings() 3604 | .enable_base_function(settings_t::e_bf_sin) 3605 | .enable_base_function(settings_t::e_bf_cos); 3606 | 3607 | parser 3608 | .compile("(sin(x) / cos(x)) == tan(x)",expression); // success 3609 | 3610 | 3611 | (2) Control Flow Structures 3612 | The list of available control flow structures is as follows: 3613 | 3614 | (a) If or If-Else 3615 | (b) Switch statement 3616 | (c) For Loop 3617 | (d) While Loop 3618 | (e) Repeat Loop 3619 | 3620 | 3621 | The above mentioned control flow structures can be either enabled 3622 | or disabled 'all' at once, as is demonstrated below: 3623 | 3624 | parser_t parser; 3625 | expression_t expression; 3626 | 3627 | const std::string program = 3628 | " var x := 0; " 3629 | " for (var i := 0; i < 10; i += 1) " 3630 | " { " 3631 | " x += i; " 3632 | " } " 3633 | 3634 | parser.settings().disable_all_control_structures(); 3635 | 3636 | parser 3637 | .compile(program,expression); // compilation failure 3638 | 3639 | parser.settings().enable_all_control_structures(); 3640 | 3641 | parser 3642 | .compile(program,expression); // compilation success 3643 | 3644 | 3645 | One can also enable or disable specific control flow structures. The 3646 | following example demonstrates the disabling of the for-loop control 3647 | flow structure: 3648 | 3649 | parser_t parser; 3650 | expression_t expression; 3651 | 3652 | const std::string program = 3653 | " var x := 0; " 3654 | " for (var i := 0; i < 10; i += 1) " 3655 | " { " 3656 | " x += i; " 3657 | " } " 3658 | 3659 | parser.settings() 3660 | .disable_control_structure(settings_t::e_ctrl_for_loop); 3661 | 3662 | parser 3663 | .compile(program,expression); // failure 3664 | 3665 | parser.settings() 3666 | .enable_control_structure(settings_t::e_ctrl_for_loop); 3667 | 3668 | parser 3669 | .compile(program,expression); // success 3670 | 3671 | 3672 | (3) Logical Operators 3673 | The list of available logical operators is as follows: 3674 | 3675 | and, nand, nor, not, or, xnor, xor, &, | 3676 | 3677 | 3678 | The above mentioned logical operators can be either enabled or 3679 | disabled 'all' at once, as is demonstrated below: 3680 | 3681 | parser_t parser; 3682 | expression_t expression; 3683 | 3684 | parser.settings().disable_all_logic_ops(); 3685 | 3686 | parser 3687 | .compile("1 or not(0 and 1)",expression); // compilation failure 3688 | 3689 | parser.settings().enable_all_logic_ops(); 3690 | 3691 | parser 3692 | .compile("1 or not(0 and 1)",expression); // compilation success 3693 | 3694 | 3695 | One can also enable or disable specific logical operators. The 3696 | following example demonstrates the disabling of the 'and' logical 3697 | operator: 3698 | 3699 | parser_t parser; 3700 | expression_t expression; 3701 | 3702 | parser.settings() 3703 | .disable_logic_operation(settings_t::e_logic_and); 3704 | 3705 | parser 3706 | .compile("1 or not(0 and 1)",expression); // failure 3707 | 3708 | parser.settings() 3709 | .enable_logic_operation(settings_t::e_logic_and); 3710 | 3711 | parser 3712 | .compile("1 or not(0 and 1)",expression); // success 3713 | 3714 | 3715 | (4) Arithmetic Operators 3716 | The list of available arithmetic operators is as follows: 3717 | 3718 | +, -, *, /, %, ^ 3719 | 3720 | 3721 | The above mentioned arithmetic operators can be either enabled or 3722 | disabled 'all' at once, as is demonstrated below: 3723 | 3724 | parser_t parser; 3725 | expression_t expression; 3726 | 3727 | parser.settings().disable_all_arithmetic_ops(); 3728 | 3729 | parser 3730 | .compile("1 + 2 / 3",expression); // compilation failure 3731 | 3732 | parser.settings().enable_all_arithmetic_ops(); 3733 | 3734 | parser 3735 | .compile("1 + 2 / 3",expression); // compilation success 3736 | 3737 | 3738 | One can also enable or disable specific arithmetic operators. The 3739 | following example demonstrates the disabling of the addition '+' 3740 | arithmetic operator: 3741 | 3742 | parser_t parser; 3743 | expression_t expression; 3744 | 3745 | parser.settings() 3746 | .disable_arithmetic_operation(settings_t::e_arith_add); 3747 | 3748 | parser 3749 | .compile("1 + 2 / 3",expression); // failure 3750 | 3751 | parser.settings() 3752 | .enable_arithmetic_operation(settings_t::e_arith_add); 3753 | 3754 | parser 3755 | .compile("1 + 2 / 3",expression); // success 3756 | 3757 | 3758 | (5) Inequality Operators 3759 | The list of available inequality operators is as follows: 3760 | 3761 | <, <=, >, >=, ==, =, != <> 3762 | 3763 | 3764 | The above mentioned inequality operators can be either enabled or 3765 | disabled 'all' at once, as is demonstrated below: 3766 | 3767 | parser_t parser; 3768 | expression_t expression; 3769 | 3770 | parser.settings().disable_all_inequality_ops(); 3771 | 3772 | parser 3773 | .compile("1 < 3",expression); // compilation failure 3774 | 3775 | parser.settings().enable_all_inequality_ops(); 3776 | 3777 | parser 3778 | .compile("1 < 3",expression); // compilation success 3779 | 3780 | 3781 | One can also enable or disable specific inequality operators. The 3782 | following example demonstrates the disabling of the less-than '<' 3783 | inequality operator: 3784 | 3785 | parser_t parser; 3786 | expression_t expression; 3787 | 3788 | parser.settings() 3789 | .disable_inequality_operation(settings_t::e_ineq_lt); 3790 | 3791 | parser 3792 | .compile("1 < 3",expression); // failure 3793 | 3794 | parser.settings() 3795 | .enable_inequality_operation(settings_t::e_ineq_lt); 3796 | 3797 | parser 3798 | .compile("1 < 3",expression); // success 3799 | 3800 | 3801 | (6) Assignment Operators 3802 | The list of available assignment operators is as follows: 3803 | 3804 | :=, +=, -=, *=, /=, %= 3805 | 3806 | 3807 | The above mentioned assignment operators can be either enabled or 3808 | disabled 'all' at once, as is demonstrated below: 3809 | 3810 | T x = T(0); 3811 | 3812 | parser_t parser; 3813 | expression_t expression; 3814 | symbol_table_t symbol_table; 3815 | 3816 | symbol_table.add_variable("x",x); 3817 | 3818 | expression.register_symbol_table(symbol_table); 3819 | 3820 | parser.settings().disable_all_assignment_ops(); 3821 | 3822 | parser 3823 | .compile("x := 3",expression); // compilation failure 3824 | 3825 | parser.settings().enable_all_assignment_ops(); 3826 | 3827 | parser 3828 | .compile("x := 3",expression); // compilation success 3829 | 3830 | 3831 | One can also enable or disable specific assignment operators. The 3832 | following example demonstrates the disabling of the '+=' addition 3833 | assignment operator: 3834 | 3835 | T x = T(0); 3836 | 3837 | parser_t parser; 3838 | expression_t expression; 3839 | symbol_table_t symbol_table; 3840 | 3841 | symbol_table.add_variable("x",x); 3842 | 3843 | expression.register_symbol_table(symbol_table); 3844 | 3845 | parser.settings() 3846 | .disable_assignment_operation(settings_t::e_assign_addass); 3847 | 3848 | parser 3849 | .compile("x += 3",expression); // failure 3850 | 3851 | parser.settings() 3852 | .enable_assignment_operation(settings_t::e_assign_addass); 3853 | 3854 | parser 3855 | .compile("x += 3",expression); // success 3856 | 3857 | 3858 | Note30: In the event of a base function being disabled, one can 3859 | redefine the base function using the standard custom function 3860 | definition process. In the following example the 'sin' function is 3861 | disabled then redefined as a function taking degree input. 3862 | 3863 | template <typename T> 3864 | struct sine_deg final : public exprtk::ifunction<T> 3865 | { 3866 | sine_deg() : exprtk::ifunction<T>(1) {} 3867 | 3868 | inline T operator()(const T& v) override 3869 | { 3870 | const T pi = exprtk::details::numeric::constant::pi; 3871 | return std::sin((v * T(pi)) / T(180)); 3872 | } 3873 | }; 3874 | 3875 | ... 3876 | 3877 | typedef exprtk::symbol_table<T> symbol_table_t; 3878 | typedef exprtk::expression<T> expression_t; 3879 | typedef exprtk::parser<T> parser_t; 3880 | 3881 | typedef typename parser_t::settings_store settings_t; 3882 | 3883 | sine_deg<T> sine; 3884 | 3885 | symbol_table.add_reserved_function("sin",sine); 3886 | 3887 | expression_t expression; 3888 | 3889 | expression.register_symbol_table(symbol_table); 3890 | 3891 | parser_t parser; 3892 | 3893 | parser.settings() 3894 | .disable_base_function(settings_t::e_bf_sin); 3895 | 3896 | parser.compile("1 + sin(30)",expression); 3897 | 3898 | 3899 | In the example above, the custom 'sin' function is registered with the 3900 | symbol_table using the method 'add_reserved_function'. This is done so 3901 | as to bypass the checks for reserved words that are carried out on the 3902 | provided symbol names when calling the standard 'add_function' method. 3903 | Normally if a user specified symbol name conflicts with any of the 3904 | ExprTk reserved words, the add_function call will fail. 3905 | 3906 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3907 | 3908 | [SECTION 20 - EXPRESSION RETURN VALUES] 3909 | ExprTk expressions can return immediately from any point by utilising 3910 | the return call. Furthermore the return call can be used to transfer 3911 | out multiple return values from within the expression. 3912 | 3913 | If an expression evaluation exits using a return point, the result of 3914 | the call to the 'value' method will be NaN, and it is expected that 3915 | the return values will be available from the results_context. 3916 | 3917 | In the following example there are three return points in the 3918 | expression. If neither of the return points are hit, then the 3919 | expression will return normally. 3920 | 3921 | const std::string expression_string = 3922 | " if (x < y) " 3923 | " return [x + 1,'return-call 1']; " 3924 | " else if (x > y) " 3925 | " return [y / 2, y + 1, 'return-call 2']; " 3926 | " else if (equal(x,y)) " 3927 | " x + y; " 3928 | " return [x, y, x + y, x - y, 'return-call 3'] " 3929 | 3930 | typedef exprtk::symbol_table<double> symbol_table_t; 3931 | typedef exprtk::expression<double> expression_t; 3932 | typedef exprtk::parser<double> parser_t; 3933 | 3934 | symbol_table_t symbol_table; 3935 | expression_t expression; 3936 | parser_t parser; 3937 | 3938 | double x = 0; 3939 | double y = 0; 3940 | 3941 | symbol_table.add_variable("x",x); 3942 | symbol_table.add_variable("y",y); 3943 | 3944 | expression.register_symbol_table(symbol_table); 3945 | 3946 | parser.compile(expression_string,expression); 3947 | 3948 | T result = expression.value(); 3949 | 3950 | if (expression.return_invoked()) 3951 | { 3952 | typedef exprtk::results_context<T> results_context_t; 3953 | typedef typename results_context_t::type_store_t type_t; 3954 | typedef typename type_t::scalar_view scalar_t; 3955 | typedef typename type_t::vector_view vector_t; 3956 | typedef typename type_t::string_view string_t; 3957 | 3958 | const results_context_t& results = expression.results(); 3959 | 3960 | for (std::size_t i = 0; i < results.count(); ++i) 3961 | { 3962 | type_t t = results[i]; 3963 | 3964 | switch (t.type) 3965 | { 3966 | case type_t::e_scalar : ... 3967 | break; 3968 | 3969 | case type_t::e_vector : ... 3970 | break; 3971 | 3972 | case type_t::e_string : ... 3973 | break; 3974 | 3975 | default : continue; 3976 | } 3977 | } 3978 | 3979 | 3980 | In the above example, there are three possible "return" points and one 3981 | regular result. Only one of the four paths can ever be realised. Hence 3982 | it is necessary to capture the result of the expression value method 3983 | call. In the event, the call to return_invoked is not true then the 3984 | non-return code path was executed and the result of the evaluation 3985 | will be the result of the expression's value method. 3986 | 3987 | Note31: Processing of the return results is similar to that of the 3988 | generic function call parameters. 3989 | 3990 | The results_context provides getter methods for each of the possible 3991 | return types (scalar, vector and string) and can be used as follows: 3992 | 3993 | typedef exprtk::symbol_table<T> symbol_table_t; 3994 | typedef exprtk::expression<T> expression_t; 3995 | typedef exprtk::parser<T> parser_t; 3996 | 3997 | const std::string expression_str = 3998 | " if (x > y) " 3999 | " return [1]; " 4000 | " else " 4001 | " return [ x, x + y, 2 * v, s + 'world' ]; " 4002 | 4003 | symbol_table_t symbol_table; 4004 | expression_t expression; 4005 | parser_t parser; 4006 | 4007 | symbol_table.add_variable ("x", x); 4008 | symbol_table.add_variable ("y", y); 4009 | symbol_table.add_variable ("z", z); 4010 | symbol_table.add_vector ("v", v); 4011 | symbol_table.add_stringvar("s", s); 4012 | 4013 | parser.compile(expression_str, expression); 4014 | 4015 | expression.value(); 4016 | 4017 | typedef exprtk::results_context<T> results_context_t; 4018 | const results_context_t& results = expression.results(); 4019 | 4020 | if (results.count() == 4) 4021 | { 4022 | T result_x0; 4023 | T result_x1; 4024 | std::string result_s; 4025 | std::vector<T> result_v; 4026 | 4027 | results.get_scalar(0, result_x0); 4028 | results.get_scalar(1, result_x1); 4029 | results.get_string(3, result_s ); 4030 | results.get_vector(2, result_v ); 4031 | } 4032 | 4033 | 4034 | It is however recommended that if there is to be only a single flow of 4035 | execution through the expression, that the simpler approach of 4036 | registering external variables of appropriate type be used. 4037 | 4038 | This method simply requires the variables that are to hold the various 4039 | results that are to be computed within the expression to be registered 4040 | with an associated symbol_table instance. Then within the expression 4041 | itself to have the result variables be assigned the appropriate 4042 | values. 4043 | 4044 | typedef exprtk::symbol_table<double> symbol_table_t; 4045 | typedef exprtk::expression<double> expression_t; 4046 | typedef exprtk::parser<double> parser_t; 4047 | 4048 | const std::string expression_string = 4049 | " var x := 123.456; " 4050 | " var s := 'ijk'; " 4051 | " result0 := x + 78.90; " 4052 | " result1 := s + '123' " 4053 | 4054 | double result0; 4055 | std::string result1; 4056 | 4057 | symbol_table_t symbol_table; 4058 | symbol_table.add_variable ("result0",result0); 4059 | symbol_table.add_stringvar("result1",result1); 4060 | 4061 | expression_t expression; 4062 | expression.register_symbol_table(symbol_table); 4063 | 4064 | parser_t parser; 4065 | parser.compile(expression_string,expression); 4066 | 4067 | expression.value(); 4068 | 4069 | printf("Result0: %15.5f\n", result0 ); 4070 | printf("Result1: %s\n" , result1.c_str()); 4071 | 4072 | 4073 | In the example above, the expression will compute two results. As such 4074 | two result variables are defined to hold the values named result0 and 4075 | result1 respectively. The first is of scalar type (double), the second 4076 | is of string type. Once the expression has been evaluated, the two 4077 | variables will have been updated with the new result values, and can 4078 | then be further utilised from within the calling host program. 4079 | 4080 | There will be times when an expression may have multiple exit paths, 4081 | where not all the paths will be return-statement based. The following 4082 | example builds upon the previous examples, but this time at least one 4083 | path is not return based. 4084 | 4085 | typedef exprtk::symbol_table<double> symbol_table_t; 4086 | typedef exprtk::expression<double> expression_t; 4087 | typedef exprtk::parser<double> parser_t; 4088 | 4089 | double x = 100.0; 4090 | double y = 200.0; 4091 | 4092 | symbol_table_t symbol_table; 4093 | expression_t expression; 4094 | parser_t parser; 4095 | 4096 | symbol_table.add_variable ("x", x); 4097 | symbol_table.add_variable ("y", y); 4098 | 4099 | expression.register_symbol_table(symbol_table); 4100 | 4101 | const std::string expression_string = 4102 | " for (var i := 0; i < 10; i += 1) " 4103 | " { " 4104 | " if (i > x) " 4105 | " { " 4106 | " return [x + y, 'return-call 1']; " 4107 | " } " 4108 | " else if (i > y) " 4109 | " { " 4110 | " return [x - y, 'return-call 2']; " 4111 | " } " 4112 | " }; " 4113 | " " 4114 | " x / y " 4115 | 4116 | parser.compile(expression_str, expression); 4117 | 4118 | const auto result = expression.value(); 4119 | 4120 | if (expression.return_invoked()) 4121 | { 4122 | const auto results = expression.results(); 4123 | 4124 | for (std::size_t i = 0; i < results.count(); ++i) 4125 | { 4126 | const auto& rtrn_result = results[i]; 4127 | . 4128 | . 4129 | . 4130 | } 4131 | } 4132 | else 4133 | { 4134 | printf("result: %f\n",result); 4135 | } 4136 | 4137 | 4138 | After having called the value method on the expression, calling the 4139 | return_invoked method will determine if the expression completed due 4140 | to a return statement being invoked or if it finished normally. 4141 | 4142 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4143 | 4144 | [SECTION 21 - COMPILATION ERRORS] 4145 | When attempting to compile a malformed or otherwise erroneous ExprTk 4146 | expression, the compilation process will result in an error, as is 4147 | indicated by the 'compile' method returning a false value. A 4148 | diagnostic indicating the first error encountered and its cause can be 4149 | obtained by invoking the 'error' method, as is demonstrated in the 4150 | following example: 4151 | 4152 | if (!parser.compile(expression_string,expression)) 4153 | { 4154 | printf("Error: %s\n", parser.error().c_str()); 4155 | return false; 4156 | } 4157 | 4158 | 4159 | Any error(s) resulting from a failed compilation will be stored in the 4160 | parser instance until the next time a compilation is performed. Before 4161 | then errors can be enumerated in the order they occurred by invoking 4162 | the 'get_error' method which itself will return a 'parser_error' type. 4163 | A parser_error object will contain an error diagnostic, an error mode 4164 | (or class), and the character position of the error in the expression 4165 | string. The following example demonstrates the enumeration of error(s) 4166 | in the event of a failed compilation. 4167 | 4168 | typedef exprtk::parser<T> parser_t; 4169 | typedef exprtk::parser_error::type error_t; 4170 | 4171 | if (!parser.compile(expression_string,expression)) 4172 | { 4173 | for (std::size_t i = 0; i < parser.error_count(); ++i) 4174 | { 4175 | typedef exprtk::parser_error::type error_t; 4176 | 4177 | error_t error = parser.get_error(i); 4178 | 4179 | printf("Error[%02d] Position: %02d Type: [%14s] Msg: %s\n", 4180 | i, 4181 | error.token.position, 4182 | exprtk::parser_error::to_str(error.mode).c_str(), 4183 | error.diagnostic.c_str()); 4184 | } 4185 | 4186 | return false; 4187 | } 4188 | 4189 | 4190 | Assuming the following expression '2 + (3 / log(1 + x))' which uses a 4191 | variable named 'x' that has not been registered with the appropriate 4192 | symbol_table instance and is not a locally defined variable, once 4193 | compiled the above denoted post compilation error handling code shall 4194 | produce the following output: 4195 | 4196 | Error[00] Pos:17 Type:[Syntax] Msg: ERR184 - Undefined symbol: 'x' 4197 | 4198 | 4199 | For expressions comprised of multiple lines, the error position 4200 | provided in the parser_error object can be converted into a pair of 4201 | line and column numbers by invoking the 'update_error' function as is 4202 | demonstrated by the following example: 4203 | 4204 | if (!parser.compile(program_str,expression)) 4205 | { 4206 | for (std::size_t i = 0; i < parser.error_count(); ++i) 4207 | { 4208 | typedef exprtk::parser_error::type error_t; 4209 | 4210 | error_t error = parser.get_error(i); 4211 | 4212 | exprtk::parser_error::update_error(error,program_str); 4213 | 4214 | printf("Error[%0lu] at line: %lu column: %lu\n", 4215 | i, 4216 | error.line_no, 4217 | error.column_no); 4218 | } 4219 | 4220 | return false; 4221 | } 4222 | 4223 | 4224 | Note32: There are five distinct error modes in ExprTk which denote the 4225 | class of an error. These classes are as follows: 4226 | 4227 | (a) Syntax 4228 | (b) Token 4229 | (c) Numeric 4230 | (d) Symbol Table 4231 | (e) Lexer 4232 | 4233 | 4234 | (a) Syntax Errors 4235 | These are errors related to invalid syntax found within the denoted 4236 | expression. Examples are invalid sequences of operators and variables, 4237 | incorrect number of parameters to functions, invalid conditional or 4238 | loop structures and invalid use of keywords. 4239 | 4240 | eg: 'for := sin(x,y,z) + 2 * equal > until[2 - x,3]' 4241 | 4242 | 4243 | (b) Token Errors 4244 | Errors in this class relate to token level errors detected by one or 4245 | more of the following checkers: 4246 | 4247 | (1) Bracket Checker 4248 | (2) Numeric Checker 4249 | (3) Sequence Checker 4250 | 4251 | 4252 | (c) Numeric Errors 4253 | This class of error is related to conversion of numeric values from 4254 | their string form to the underlying numerical type (float, double 4255 | etc). 4256 | 4257 | (d) Symbol Table Errors 4258 | This is the class of errors related to failures when interacting with 4259 | the registered symbol_table instance. Errors such as not being able to 4260 | find, within the symbol_table, symbols representing variables or 4261 | functions, to being unable to create new variables in the symbol_table 4262 | via the 'unknown symbol resolver' mechanism. 4263 | 4264 | Note33: The function compositor also supports error message handling 4265 | similar to how it is done via the parser. The following demonstrates 4266 | how after a failed function composition the associated errors can be 4267 | enumerated. 4268 | 4269 | typedef exprtk::function_compositor<T> compositor_t; 4270 | typedef typename compositor_t::function function_t; 4271 | 4272 | compositor_t compositor; 4273 | 4274 | const bool compositor_result = 4275 | compositor.add( 4276 | function_t("foobar") 4277 | .vars("x","y") 4278 | .expression 4279 | ( " x + y / z " )); 4280 | 4281 | if (!compositor_result) 4282 | { 4283 | printf("Error: %s\n", compositor.error().c_str()); 4284 | 4285 | for (std::size_t i = 1; i < compositor.error_count(); ++i) 4286 | { 4287 | typedef exprtk::parser_error::type error_t; 4288 | 4289 | error_t error = compositor.get_error(i); 4290 | 4291 | printf("Err No.: %02d Pos: %02d Type: [%14s] Msg: %s\n", 4292 | static_cast<unsigned int>(i), 4293 | static_cast<unsigned int>(error.token.position), 4294 | exprtk::parser_error::to_str(error.mode).c_str(), 4295 | error.diagnostic.c_str()); 4296 | } 4297 | } 4298 | 4299 | 4300 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4301 | 4302 | [SECTION 22 - RUNTIME LIBRARY PACKAGES] 4303 | ExprTk includes a range of extensions, that provide functionalities 4304 | beyond simple numerical calculations. Currently the available packages 4305 | are: 4306 | 4307 | +---+--------------------+-----------------------------------+ 4308 | | # | Package Name | Namespace/Type | 4309 | +---+--------------------+-----------------------------------+ 4310 | | 1 | Basic I/O | exprtk::rtl::io::package<T> | 4311 | | 2 | File I/O | exprtk::rtl::io::file::package<T> | 4312 | | 3 | Vector Operations | exprtk::rtl::vecops::package<T> | 4313 | +---+--------------------+-----------------------------------+ 4314 | 4315 | 4316 | In order to make the features of a specific package available within 4317 | an expression, an instance of the package must be added to the 4318 | expression's associated symbol table. In the following example, the 4319 | file I/O package is made available for the given expression: 4320 | 4321 | typedef exprtk::symbol_table<T> symbol_table_t; 4322 | typedef exprtk::expression<T> expression_t; 4323 | typedef exprtk::parser<T> parser_t; 4324 | 4325 | exprtk::rtl::io::file::package<T> fileio_package; 4326 | 4327 | const std::string expression_string = 4328 | " var file_name := 'file.txt'; " 4329 | " var stream := null; " 4330 | " " 4331 | " stream := open(file_name,'w'); " 4332 | " " 4333 | " write(stream,'Hello world....\n'); " 4334 | " " 4335 | " close(stream); " 4336 | " " 4337 | 4338 | symbol_table_t symbol_table; 4339 | symbol_table.add_package(fileio_package); 4340 | 4341 | expression_t expression; 4342 | expression.register_symbol_table(symbol_table); 4343 | 4344 | parser_t parser; 4345 | parser.compile(expression_string,expression); 4346 | 4347 | expression.value(); 4348 | 4349 | 4350 | (1) Basic I/O functions: 4351 | 4352 | (a) print 4353 | (b) println 4354 | 4355 | (2) File I/O functions: 4356 | 4357 | (a) open (b) close 4358 | (c) write (d) read 4359 | (e) getline (f) eof 4360 | 4361 | (3) Vector Operations functions: 4362 | 4363 | (a) all_true (b) all_false 4364 | (c) any_true (d) any_false 4365 | (e) assign (f) count 4366 | (g) copy (h) reverse 4367 | (i) rotate-left (j) rotate-right 4368 | (k) shift-left (l) shift-right 4369 | (m) sort (n) nth_element 4370 | (o) iota (p) sumk 4371 | (q) axpy (r) axpby 4372 | (s) axpyz (t) axpbyz 4373 | (u) axpbz (v) dot 4374 | (w) dotk (x) diff 4375 | 4376 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4377 | 4378 | [SECTION 23 - HELPERS & UTILS] 4379 | The ExprTk library provides a series of usage simplifications via 4380 | helper routines that combine various processes into a single 'function 4381 | call' making certain actions easier to carry out though not 4382 | necessarily in the most efficient way possible. A list of the routines 4383 | are as follows: 4384 | 4385 | (a) collect_variables 4386 | (b) collect_functions 4387 | (c) compute 4388 | (d) integrate 4389 | (e) derivative 4390 | (f) second_derivative 4391 | (g) third_derivative 4392 | 4393 | 4394 | (a) collect_variables 4395 | This function will collect all the variable symbols in a given string 4396 | representation of an expression and return them in an STL compatible 4397 | sequence data structure (eg: std::vector, dequeue etc) specialised 4398 | upon a std::string type. If an error occurs during the parsing of the 4399 | expression then the return value of the function will be false, 4400 | otherwise it will be true. An example use of the given routine is as 4401 | follows: 4402 | 4403 | const std::string expression = "x + abs(y / z)" 4404 | 4405 | std::vector<std::string> variable_list; 4406 | 4407 | if (exprtk::collect_variables(expression, variable_list)) 4408 | { 4409 | for (const auto& var : variable_list) 4410 | { 4411 | ... 4412 | } 4413 | } 4414 | else 4415 | printf("An error occurred."); 4416 | 4417 | 4418 | (b) collect_functions 4419 | This function will collect all the function symbols in a given string 4420 | representation of an expression and return them in an STL compatible 4421 | sequence data structure (eg: std::vector, dequeue etc) specialised 4422 | upon a std::string type. If an error occurs during the parsing of the 4423 | expression then the return value of the function will be false, 4424 | otherwise it will be true. An example use of the given routine is as 4425 | follows: 4426 | 4427 | const std::string expression = "x + abs(y / cos(1 + z))" 4428 | 4429 | std::deque<std::string> function_list; 4430 | 4431 | if (exprtk::collect_functions(expression, function_list)) 4432 | { 4433 | for (const auto& func : function_list) 4434 | { 4435 | ... 4436 | } 4437 | } 4438 | else 4439 | printf("An error occurred."); 4440 | 4441 | 4442 | Note34: When either the 'collect_variables' or 'collect_functions' 4443 | free functions return true - that does not necessarily indicate the 4444 | expression itself is valid. It is still possible that when compiled 4445 | the expression may have certain 'type' related errors - though it is 4446 | highly likely that no semantic errors will occur if either return 4447 | true. 4448 | 4449 | Note35: The default interface provided for both the collect_variables 4450 | and collect_functions free_functions, assumes that expressions will 4451 | only be utilising the ExprTk reserved functions (eg: abs, cos, min 4452 | etc). When user defined functions are to be used in an expression, a 4453 | symbol_table instance containing said functions can be passed to 4454 | either routine, and will be incorporated during the compilation and 4455 | Dependent Entity Collection processes. In the following example, a 4456 | user defined free function named 'foo' is registered with a 4457 | symbol_table. Finally the symbol_table instance and associated 4458 | expression string are passed to the exprtk::collect_functions routine. 4459 | 4460 | template <typename T> 4461 | T foo(T v) 4462 | { 4463 | return std::abs(v + T(2)) / T(3); 4464 | } 4465 | 4466 | ...... 4467 | 4468 | exprtk::symbol_table<T> sym_tab; 4469 | 4470 | symbol_table.add_function("foo",foo); 4471 | 4472 | const std::string expression = "x + foo(y / cos(1 + z))" 4473 | 4474 | std::deque<std::string> function_list; 4475 | 4476 | if (exprtk::collect_functions(expression, sym_tab, function_list)) 4477 | { 4478 | for (const auto& func : function_list) 4479 | { 4480 | ... 4481 | } 4482 | } 4483 | else 4484 | printf("An error occurred."); 4485 | 4486 | 4487 | (c) compute 4488 | This free function will compute the value of an expression from its 4489 | string form. If an invalid expression is passed, the result of the 4490 | function will be false indicating an error, otherwise the return value 4491 | will be true indicating success. The compute function has three 4492 | overloads, the definitions of which are: 4493 | 4494 | (1) No variables 4495 | (2) One variable called x 4496 | (3) Two variables called x and y 4497 | (3) Three variables called x, y and z 4498 | 4499 | 4500 | Example uses of each of the three overloads for the compute routine 4501 | are as follows: 4502 | 4503 | T result = T(0); 4504 | 4505 | // No variables overload 4506 | const std::string no_vars = "abs(1 - (3 / pi)) * 5" 4507 | 4508 | if (!exprtk::compute(no_vars,result)) 4509 | printf("Failed to compute: %s",no_vars.c_str()); 4510 | else 4511 | printf("Result: %15.5f\n",result); 4512 | 4513 | // One variable 'x' overload 4514 | T x = T(123.456); 4515 | 4516 | const std::string one_var = "abs(x - (3 / pi)) * 5" 4517 | 4518 | if (!exprtk::compute(one_var, x, result)) 4519 | printf("Failed to compute: %s",one_var.c_str()); 4520 | else 4521 | printf("Result: %15.5f\n",result); 4522 | 4523 | // Two variables 'x' and 'y' overload 4524 | T y = T(789.012); 4525 | 4526 | const std::string two_var = "abs(x - (y / pi)) * 5" 4527 | 4528 | if (!exprtk::compute(two_var, x, y, result)) 4529 | printf("Failed to compute: %s",two_var.c_str()); 4530 | else 4531 | printf("Result: %15.5f\n",result); 4532 | 4533 | // Three variables 'x', 'y' and 'z' overload 4534 | T z = T(345.678); 4535 | 4536 | const std::string three_var = "abs(x - (y / pi)) * z" 4537 | 4538 | if (!exprtk::compute(three_var, x, y, z, result)) 4539 | printf("Failed to compute: %s",three_var.c_str()); 4540 | else 4541 | printf("Result: %15.5f\n",result); 4542 | 4543 | 4544 | (d) integrate 4545 | This free function will attempt to perform a numerical integration of 4546 | a single variable compiled expression over a specified range and step 4547 | size. The numerical integration is based on the three point form of 4548 | Simpson's rule. The integrate function has two overloads, where the 4549 | variable of integration can either be passed as a reference or as a 4550 | name in string form. Example usage of the function is as follows: 4551 | 4552 | typedef exprtk::symbol_table<T> symbol_table_t; 4553 | typedef exprtk::expression<T> expression_t; 4554 | typedef exprtk::parser<T> parser_t; 4555 | 4556 | const std::string expression_string = "sqrt(1 - (x^2))" 4557 | 4558 | T x = T(0); 4559 | 4560 | symbol_table_t symbol_table; 4561 | symbol_table.add_variable("x",x); 4562 | 4563 | expression_t expression; 4564 | expression.register_symbol_table(symbol_table); 4565 | 4566 | parser_t parser; 4567 | parser.compile(expression_string,expression); 4568 | 4569 | .... 4570 | 4571 | // Integrate in domain [-1,1] using a reference to x variable 4572 | T area1 = exprtk::integrate(expression, x, T(-1), T(1)); 4573 | 4574 | // Integrate in domain [-1,1] using name of x variable 4575 | T area2 = exprtk::integrate(expression, "x", T(-1), T(1)); 4576 | 4577 | 4578 | (e) derivative 4579 | This free function will attempt to perform a numerical differentiation 4580 | of a single variable compiled expression at a given point for a given 4581 | epsilon, using a variant of Newton's difference quotient called the 4582 | five-point stencil method. The derivative function has two overloads, 4583 | where the variable of differentiation can either be passed as a 4584 | reference or as a name in string form. Example usage of the derivative 4585 | function is as follows: 4586 | 4587 | typedef exprtk::symbol_table<T> symbol_table_t; 4588 | typedef exprtk::expression<T> expression_t; 4589 | typedef exprtk::parser<T> parser_t; 4590 | 4591 | const std::string expression_string = "sqrt(1 - (x^2))" 4592 | 4593 | T x = T(0); 4594 | 4595 | symbol_table_t symbol_table; 4596 | symbol_table.add_variable("x",x); 4597 | 4598 | expression_t expression; 4599 | expression.register_symbol_table(symbol_table); 4600 | 4601 | parser_t parser; 4602 | parser.compile(expression_string,expression); 4603 | 4604 | .... 4605 | 4606 | // Differentiate expression at value of x = 12.3 using a reference 4607 | // to the x variable 4608 | x = T(12.3); 4609 | T derivative1 = exprtk::derivative(expression, x); 4610 | 4611 | // Differentiate expression where value x = 45.6 using name 4612 | // of the x variable 4613 | x = T(45.6); 4614 | T derivative2 = exprtk::derivative(expression, "x"); 4615 | 4616 | 4617 | (f) second_derivative 4618 | This free function will attempt to perform a numerical second 4619 | derivative of a single variable compiled expression at a given point 4620 | for a given epsilon, using a variant of Newton's difference quotient 4621 | method. The second_derivative function has two overloads, where the 4622 | variable of differentiation can either be passed as a reference or as 4623 | a name in string form. Example usage of the second_derivative function 4624 | is as follows: 4625 | 4626 | typedef exprtk::symbol_table<T> symbol_table_t; 4627 | typedef exprtk::expression<T> expression_t; 4628 | typedef exprtk::parser<T> parser_t; 4629 | 4630 | const std::string expression_string = "sqrt(1 - (x^2))" 4631 | 4632 | T x = T(0); 4633 | 4634 | symbol_table_t symbol_table; 4635 | symbol_table.add_variable("x",x); 4636 | 4637 | expression_t expression; 4638 | expression.register_symbol_table(symbol_table); 4639 | 4640 | parser_t parser; 4641 | parser.compile(expression_string,expression); 4642 | 4643 | .... 4644 | 4645 | // Second derivative of expression where value of x = 12.3 using a 4646 | // reference to x variable 4647 | x = T(12.3); 4648 | T derivative1 = exprtk::second_derivative(expression,x); 4649 | 4650 | // Second derivative of expression where value of x = 45.6 using 4651 | // name of x variable 4652 | x = T(45.6); 4653 | T derivative2 = exprtk::second_derivative(expression, "x"); 4654 | 4655 | 4656 | (g) third_derivative 4657 | This free function will attempt to perform a numerical third 4658 | derivative of a single variable compiled expression at a given point 4659 | for a given epsilon, using a variant of Newton's difference quotient 4660 | method. The third_derivative function has two overloads, where the 4661 | variable of differentiation can either be passed as a reference or as 4662 | a name in string form. Example usage of the third_derivative function 4663 | is as follows: 4664 | 4665 | typedef exprtk::symbol_table<T> symbol_table_t; 4666 | typedef exprtk::expression<T> expression_t; 4667 | typedef exprtk::parser<T> parser_t; 4668 | 4669 | const std::string expression_string = "sqrt(1 - (x^2))" 4670 | 4671 | T x = T(0); 4672 | 4673 | symbol_table_t symbol_table; 4674 | symbol_table.add_variable("x",x); 4675 | 4676 | expression_t expression; 4677 | expression.register_symbol_table(symbol_table); 4678 | 4679 | parser_t parser; 4680 | parser.compile(expression_string,expression); 4681 | 4682 | .... 4683 | 4684 | // Third derivative of expression where value of x = 12.3 using a 4685 | // reference to the x variable 4686 | x = T(12.3); 4687 | T derivative1 = exprtk::third_derivative(expression, x); 4688 | 4689 | // Third derivative of expression where value of x = 45.6 using 4690 | // name of the x variable 4691 | x = T(45.6); 4692 | T derivative2 = exprtk::third_derivative(expression, "x"); 4693 | 4694 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4695 | 4696 | [SECTION 24 - RUNTIME CHECKS] 4697 | The ExprTk library provides the ability to perform runtime checks 4698 | during expression evaluation so as to ensure memory access violations 4699 | errors are caught and handled without causing further issues. The 4700 | checks typically cover: 4701 | 4702 | 1. Vector access and handling 4703 | 2. String access and handling 4704 | 3. Loop iteration checks 4705 | 4. Compilation checkpointing 4706 | 5. Assert statements 4707 | 4708 | 4709 | (1) Vector Access Runtime Checks 4710 | Expressions that contain vectors where elements of the vectors may be 4711 | accessed using indexes that can only be determined at runtime may 4712 | result in memory access violations when the index is out of the 4713 | vector's bound. Some examples of problematic expressions are as 4714 | follows: 4715 | 4716 | 1. vec[i] 4717 | 2. vec[i + j] 4718 | 3. vec[i + 10] 4719 | 4. vec[i + vec[]] := x + y 4720 | 5. vec[i + j] <=> vec[i] 4721 | 6. vec[i + j] := (vec1 + vec2)[i + j] 4722 | 4723 | 4724 | In the above expressions, it is assumed that the values used in the 4725 | index operator may either exceed the vector bounds or precede the 4726 | vector's start, In short, the indexes may not necessarily be within 4727 | the range [0,vec[]). 4728 | 4729 | ExprTk provides the ability to inject a runtime check at the point of 4730 | index evaluation and handle situations where the index violates the 4731 | vector's bounds. This capability is done by registering a user- 4732 | implemented Vector Access Runtime Check (VARTC) to the parser before 4733 | expression compilation. Initially a VARTC can be defined as follows: 4734 | 4735 | struct my_vector_access_rtc final : 4736 | public exprtk::vector_access_runtime_check 4737 | { 4738 | bool handle_runtime_violation(violation_context& context) 4739 | override 4740 | { 4741 | // Handling of the violation 4742 | return ...; 4743 | } 4744 | }; 4745 | 4746 | 4747 | Then an instance of the VARTC can be registered with a parser instance 4748 | as follows: 4749 | 4750 | my_vector_access_rtc vartc; 4751 | 4752 | exprtk::symbol_table<T> symbol_table; 4753 | 4754 | T i; 4755 | T x; 4756 | T y; 4757 | std::vector<T> vec = { 0, 1, 2, 3, 4 }; 4758 | 4759 | symbol_table.add_variable("i" , i ); 4760 | symbol_table.add_variable("x" , x ); 4761 | symbol_table.add_variable("y" , y ); 4762 | symbol_table.add_vector ("vec", vec); 4763 | 4764 | exprtk::expression<T> expression; 4765 | exprtk::parser<T> parser; 4766 | 4767 | parser.register_vector_access_runtime_check(vartc); 4768 | 4769 | std::string expression = "vec[i + vec[]] := x + y" 4770 | 4771 | parser.compile(expression_str, expression); 4772 | 4773 | try 4774 | { 4775 | expression.value(); 4776 | } 4777 | catch (std::runtime_error& rte) 4778 | { 4779 | printf("Exception: %s\n", rte.what()); 4780 | } 4781 | 4782 | 4783 | Note36: The lifetime of any parser or expression instance must not 4784 | exceed that of any VARTC instance that has been registered with it. 4785 | 4786 | When a vector access violation occurs, the registered VARTC instance's 4787 | handle_runtime_violation method will be invoked, coupled with it a 4788 | violation_context shall be provided that will contain the following 4789 | members: 4790 | 4791 | 1. base_ptr: Of type void*, which points to the first element 4792 | of the vector. The base_ptr can also be used as a key to 4793 | determine the vector upon which the access violation has 4794 | occurred. 4795 | 4796 | 2. end_ptr : Of type void*, which points to one position after 4797 | the last element of the vector 4798 | 4799 | 3. access_ptr: Of type void*, points to the memory location 4800 | which is the base_ptr offset by the derived index value. 4801 | 4802 | 4. type_size: Size of the vector's element type in bytes. This 4803 | value can be used to determine the number of elements in 4804 | the vector based on the base_ptr and end_ptr. 4805 | 4806 | 4807 | The implementation of the handle_runtime_violation method can at this 4808 | point perform various actions such as: 4809 | 4810 | 1. Log the violation 4811 | 2. Throw an exception (eg: std::runtime_error) 4812 | 3. Remedy the access_ptr to allow for the evaluation to continue 4813 | 4814 | 4815 | Note37: When employing option [3], handle_runtime_violation needs to 4816 | return true, otherwise the caller will assume an unhandled access 4817 | violation and default to using the base_ptr. 4818 | 4819 | It is recommended, at the very least, to throw an exception when 4820 | handling vector access violations and to only consider option [3] when 4821 | the the ramifications of changing the access_ptr are well understood. 4822 | 4823 | The following are simple examples of how the handle_runtime_violation 4824 | can be implemented. 4825 | 4826 | Example 1: Log the access violation to stdout and then throw a runtime 4827 | error exception: 4828 | 4829 | bool handle_runtime_violation(violation_context& context) override 4830 | { 4831 | printf("ERROR - Runtime vector access violation. " 4832 | "base: %p end: %p access: %p typesize: %lu\n", 4833 | context.base_ptr , 4834 | context.end_ptr , 4835 | context.access_ptr, 4836 | context.type_size); 4837 | 4838 | throw std::runtime_error("Runtime vector access violation."); 4839 | return false; 4840 | } 4841 | 4842 | 4843 | Example 2: Handle the access violation by resetting the access pointer 4844 | to the last value in the vector. 4845 | 4846 | bool handle_runtime_violation(violation_context& context) override 4847 | { 4848 | context.access_ptr = 4849 | static_cast<char*>(context.end_ptr) - context.type_size; 4850 | return true; 4851 | } 4852 | 4853 | 4854 | Note38: The return value of true in the above handler method signals 4855 | the caller to continue the vector access using the updated access_ptr. 4856 | 4857 | 4858 | (2) String Access Runtime Checks 4859 | Expressions that contain strings where elements or substrings of the 4860 | strings may be accessed using indexes that can only be determined at 4861 | runtime may result in memory access violations when the index or range 4862 | is out of the string's bound. Examples of problematic expressions are 4863 | as follows: 4864 | 4865 | 1. s[i : j + k] 4866 | 2. s[i : j + k][-x : y] 4867 | 3. (s1 + s2)[i : j + k] 4868 | 4. '01234'[5 + i] 4869 | 5. s += s[i : j + k] 4870 | 6. s[i : j + k] := 'chappy days'[1 : ] 4871 | 4872 | 4873 | To enable string access runtime checks all one needs to do is simply 4874 | use the following define before the ExprTk header is included or as 4875 | part of the compilation define parameters: 4876 | 4877 | exprtk_enable_range_runtime_checks 4878 | 4879 | 4880 | When the above define is used, and a string related runtime access 4881 | violation occurs a std::runtime_error exception will be thrown. The 4882 | following demonstrates the general flow of handling the access 4883 | violation: 4884 | 4885 | parser.compile(expression_string, expression) 4886 | . 4887 | . 4888 | try 4889 | { 4890 | expression.value(); 4891 | } 4892 | catch (std::runtime_error& rte) 4893 | { 4894 | printf("Exception: %s\n", rte.what()); 4895 | } 4896 | 4897 | 4898 | (3) Loop Iteration Checks 4899 | Expressions that contain loop structures (eg: for/while/repeat et al) 4900 | can be problematic from a usage point of view due to the difficulty in 4901 | determining the following: 4902 | 4903 | 1. Will the loop ever complete (aka is this an infinite loop?) 4904 | 2. Maximum loop execution time 4905 | 4906 | 4907 | ExprTk provides the ability to inject a runtime check within loop 4908 | conditionals, and to have the result of the check either signal the 4909 | loop to continue or for the check to raise a loop violation error. 4910 | 4911 | The process involves instantiating a user defined loop_runtime_check 4912 | (LRTC), registering the instance with a exprtk::parser instance and 4913 | specifying which loop types the check is to performed upon. The 4914 | following code demonstrates a how custom LRTC can be instantiated and 4915 | registered with the associated parser: 4916 | 4917 | typedef exprtk::parser<T> parser_t; 4918 | typedef exprtk::loop_runtime_check loop_runtime_check_t; 4919 | 4920 | my_loop_rtc loop_rtc; 4921 | loop_runtime_check.loop_set = loop_runtime_check_t::e_all_loops; 4922 | loop_runtime_check.max_loop_iterations = 100000; 4923 | 4924 | parser_t parser; 4925 | 4926 | parser.register_loop_runtime_check(loop_rtc); 4927 | 4928 | 4929 | The following is an example of how one could derive from and implement 4930 | a custom loop_runtime_check: 4931 | 4932 | struct my_loop_rtc final : exprtk::loop_runtime_check 4933 | { 4934 | 4935 | bool check() override 4936 | { 4937 | // 4938 | return ... 4939 | } 4940 | 4941 | void handle_runtime_violation 4942 | (const exprtk::violation_context&) override 4943 | { 4944 | throw std::runtime_error("Loop runtime violation."); 4945 | } 4946 | }; 4947 | 4948 | 4949 | In the above code, if either the check method returns false or the 4950 | loop iteration count exceeds the max_loop_iterations value, the 4951 | handle_runtime_violation method will be invoked, coupled with it a 4952 | violation_context shall be provided that will contain the following 4953 | members: 4954 | 4955 | 1. loop: Of type loop_types. This value denotes the type of 4956 | loop that triggered the violation (e_for_loop, e_while_loop, 4957 | e_repeat_until_loop). 4958 | 4959 | 2. violation: Of type type. This value denotes the type of 4960 | violation (e_iteration_count, e_timeout) 4961 | 4962 | 3. iteration_count: Of type uint64_t. The number of iterations 4963 | that the triggering loop has executed since the start of the 4964 | expression. 4965 | 4966 | 4967 | Note39: The lifetime of any parser or expression instance must not 4968 | exceed that of any LRTC instance that has been registered with it. 4969 | 4970 | The following is an example implementation of an LRTC that 4971 | supports loop timeout violations: 4972 | 4973 | struct timeout_loop_rtc final : exprtk::loop_runtime_check 4974 | { 4975 | using time_point_t = 4976 | std::chrono::time_point<std::chrono::steady_clock>; 4977 | 4978 | std::size_t iterations_ = 0; 4979 | time_point_t timeout_tp_; 4980 | 4981 | bool check() override 4982 | { 4983 | if (std::chrono::steady_clock::now() >= timeout_tp_) 4984 | { 4985 | // handle_runtime_violation shall be invoked 4986 | return false; 4987 | } 4988 | 4989 | return true; 4990 | } 4991 | 4992 | void handle_runtime_violation 4993 | (const exprtk::violation_context&) override 4994 | { 4995 | throw std::runtime_error("Loop timed out"); 4996 | } 4997 | 4998 | void set_timeout_time(const time_point_t& timeout_tp) 4999 | { 5000 | timeout_tp_ = timeout_tp; 5001 | } 5002 | }; 5003 | 5004 | 5005 | In the above code, the check method shall be invoked on each iteration 5006 | of the associated loop. Within the method the current time is compared 5007 | to the setup timeout time-point, in the event the current time exceeds 5008 | the timeout, the method returns false, triggering the violation, which 5009 | in turn will result in the handle_runtime_violation being invoked. 5010 | 5011 | The following code demonstrates how the above defined LRTC can be used 5012 | to ensure that at the very least the loop portion(s) of an expression 5013 | will never exceed a given amount of execution time. 5014 | 5015 | typedef exprtk::parser<T> parser_t; 5016 | typedef exprtk::loop_runtime_check loop_runtime_check_t; 5017 | 5018 | my_loop_rtc loop_rtc; 5019 | loop_rtc.loop_set = loop_runtime_check_t::e_all_loops; 5020 | loop_rtc.max_loop_iterations = 100000; 5021 | 5022 | parser_t parser; 5023 | 5024 | parser.register_loop_runtime_check(loop_rtc); 5025 | . 5026 | . 5027 | . 5028 | . 5029 | using std::chrono; 5030 | const auto max_duration = seconds(25); 5031 | 5032 | try 5033 | { 5034 | loop_rtc.set_timeout_time(steady_clock::now() + max_duration); 5035 | expression.value(); 5036 | 5037 | loop_rtc.set_timeout_time(steady_clock::now() + max_duration); 5038 | expression.value(); 5039 | 5040 | loop_rtc.set_timeout_time(steady_clock::now() + max_duration); 5041 | expression.value(); 5042 | 5043 | } 5044 | catch(std::runtime_error& exception) 5045 | { 5046 | printf("Exception: %s\n",exception.what()); 5047 | } 5048 | 5049 | 5050 | (4) Compilation Process Checkpointing 5051 | When compiling an expression, one may require the compilation process 5052 | to periodically checkpoint its internal state, subsequently at the 5053 | checkpoint one can then make the decision to continue the compilation 5054 | process or to immediately terminate and return. 5055 | 5056 | The following are reasons one may want to checkpoint the compilation 5057 | process: 5058 | 5059 | 1. Determine if the compilation process has run for far too long 5060 | 2. Determine if the current stack frame size exceeds a limit 5061 | 3. Enforce an external termination request 5062 | 5063 | 5064 | ExprTk provides the ability to inject a checkpoint into the 5065 | compilation process that will be evaluated periodically. This 5066 | capability is achieved by registering a user-implemented compilation 5067 | check (CCK) to the parser before expression compilation. Initially a 5068 | CCK can be defined as follows: 5069 | 5070 | struct compilation_timeout_check final : 5071 | public exprtk::compilation_check 5072 | { 5073 | bool continue_compilation(compilation_context& context) 5074 | override 5075 | { 5076 | // Determine if compilation should continue 5077 | return ...; 5078 | } 5079 | }; 5080 | 5081 | 5082 | An example checkpoint use-case could be that we do not want the 5083 | compilation process to take longer than a maximum defined period, eg: 5084 | five seconds. The associated compilation check implementation could be 5085 | as follows: 5086 | 5087 | struct my_compilation_timeout_check final : 5088 | public exprtk::compilation_check 5089 | { 5090 | 5091 | bool continue_compilation(compilation_context& context) 5092 | override 5093 | { 5094 | static constexpr std::size_t max_iters_per_check = 1000; 5095 | 5096 | if (++iterations_ >= max_iters_per_check) 5097 | { 5098 | if (std::chrono::steady_clock::now() >= timeout_tp_) 5099 | { 5100 | context.error_message = "Compilation has timed-out" 5101 | return false; 5102 | } 5103 | 5104 | iterations_ = 0; 5105 | } 5106 | 5107 | return true; 5108 | } 5109 | 5110 | using time_point_t = std::chrono::time_point<std::chrono::steady_clock>; 5111 | 5112 | void set_timeout_time(const time_point_t& timeout_tp) 5113 | { 5114 | timeout_tp_ = timeout_tp; 5115 | } 5116 | 5117 | std::size_t iterations_ = 0; 5118 | time_point_t timeout_tp_; 5119 | }; 5120 | 5121 | 5122 | Usage of the above defined compilation check will require registering 5123 | the check with the parser, setting up the expiry time and then 5124 | proceeding to compile the expression. The following is a general 5125 | outline of what will be needed: 5126 | 5127 | typedef exprtk::expression<T> expression_t; 5128 | typedef exprtk::parser<T> parser_t; 5129 | 5130 | expression_t expression; 5131 | 5132 | my_compilation_timeout_check compilation_timeout_check; 5133 | 5134 | parser_t parser; 5135 | parser. 5136 | register_compilation_timeout_check(compilation_timeout_check); 5137 | 5138 | const auto max_duration = std::chrono::seconds(5); 5139 | const auto timeout_tp = 5140 | std::chrono::steady_clock::now() + max_duration; 5141 | 5142 | compilation_timeout_check.set_timeout_time(timeout_tp); 5143 | 5144 | if (!parser.compile(large_expression_string, expression)) 5145 | { 5146 | printf("Error: %s\t\n", parser.error().c_str()); 5147 | return; 5148 | } 5149 | 5150 | 5151 | (5) Assert statements 5152 | ExprTk supports the use of assert statements to verify pre and post 5153 | conditions during the evaluation of expressions. The assert statements 5154 | are only active when a user defined assert handler is registered with 5155 | the parser before expression compilation, otherwise they are compiled 5156 | out, this is similar to how asserts are included/excluded in C++ 5157 | coupled with the definition of NDEBUG. The assert syntax has three 5158 | variations as described below: 5159 | 5160 | assert(x + y > i); 5161 | assert(x + y > i, 'assert statement 1'); 5162 | assert(x + y > i, 'assert statement 1', 'ASSERT01'); 5163 | 5164 | 5165 | The three assert statement input parameters are as follows: 5166 | 5167 | 1. assert condition (mandatory) 5168 | 2. assert message (optional) 5169 | 3. assert id (optional) 5170 | 5171 | 5172 | The assert condition is essentially a boolean statement that is 5173 | expected to be true during evaluation. The other two parameters of 5174 | assert message and ID are string values that are intended to provide 5175 | feedback to the handler and to ensure the uniqueness of assert 5176 | statement respectively. The three parameters denoted above and the 5177 | offset of the assert statement from the beginning of the expression 5178 | are placed inside assert_context that is provided as part of the 5179 | assert_check handler. A user defined assert_check handler can be 5180 | defined as follows: 5181 | 5182 | struct my_assert_handler final : public exprtk::assert_check 5183 | { 5184 | void handle_assert(const assert_context& ctxt) override 5185 | { 5186 | printf("condition: [%s] \n", ctxt.condition.c_str()); 5187 | printf("message: [%s] \n", ctxt.message .c_str()); 5188 | printf("id: [%s] \n", ctxt.id .c_str()); 5189 | printf("offset: [%lu]\n", ctxt.offet ); 5190 | // throw std::runtime_error(.....); 5191 | } 5192 | }; 5193 | 5194 | 5195 | Once the assert_check handler has been registered with the parser, 5196 | expressions that contain assert statements will have their asserts 5197 | compiled in as part final evaluable expression instance: 5198 | 5199 | typedef exprtk::symbol_table<T> symbol_table_t; 5200 | typedef exprtk::expression<T> expression_t; 5201 | typedef exprtk::parser<T> parser_t; 5202 | 5203 | const std::string program = 5204 | " var x := 4; " 5205 | " " 5206 | " for (var i := 0; i < 10; i += 1) " 5207 | " { " 5208 | " assert(i < x, 'assert statement 1'); " 5209 | " } " 5210 | 5211 | my_assert_handler handler; 5212 | 5213 | expression_t expression; 5214 | parser_t parser; 5215 | 5216 | parser.register_assert_check(handler); 5217 | parser.compile(program, expression); 5218 | 5219 | 5220 | (6) Runtime Check Overheads 5221 | All of the above mentioned runtime checks will incur an execution time 5222 | overhead during the evaluation of expressions. This is an unfortunate 5223 | but necessary side-effect of the process when runtime safety is of 5224 | concern. 5225 | 5226 | A recommendation to consider, that is not demonstrated above, is that 5227 | in the check method of the LRTC, one should not evaluate the timeout 5228 | condition on every call to check (aka on every loop iteration). 5229 | Instead a counter should be maintained and incremented on each call 5230 | and when the counter exceeds some predefined amount (eg: 10000 5231 | iterations), then the timeout based check can be preformed. The 5232 | reasoning here is that incrementing an integer should be far less 5233 | expensive than computing the current "now" time-point. 5234 | 5235 | 5236 | (7) Runtime Check Limitations 5237 | The available RTC mechanisms in ExprTk are limited to implementing 5238 | said checks only within ExprTk based syntax sections of an expression. 5239 | The RTCs will not be active within user defined functions, or 5240 | composited functions that have been compiled with parser instances 5241 | that don't have the same set of RTC configurations enabled. 5242 | 5243 | 5244 | (8) Runtime Handlers 5245 | 5246 | When implementing stateful run-time check handlers one must be careful 5247 | to ensure the handler is setup correctly or reset between calls to the 5248 | expression::value or parser::compile methods. 5249 | 5250 | The following example code utilises the compilation timeout RTC and 5251 | expression loop duration RTC examples from above to demonstrate the 5252 | need to reset the internal state of the various handlers before 5253 | compilation and valuation processes are invoked, as not doing so will 5254 | affect the ability for the next expression in the list to either be 5255 | correctly compiled or evaluated due to the potential of erroneous 5256 | timeouts occurring. 5257 | 5258 | typedef exprtk::expression<T> expression_t; 5259 | typedef exprtk::parser<T> parser_t; 5260 | 5261 | my_compilation_timeout_check compilation_timeout_check; 5262 | 5263 | my_loop_rtc loop_rtc; 5264 | loop_rtc.loop_set = loop_runtime_check_t::e_all_loops; 5265 | loop_rtc.max_loop_iterations = 100000; 5266 | 5267 | parser_t parser; 5268 | parser.register_loop_runtime_check(loop_rtc); 5269 | parser. 5270 | register_compilation_timeout_check(compilation_timeout_check); 5271 | 5272 | const auto compile_timeout_tp = []() 5273 | { 5274 | const auto max_duration = std::chrono::seconds(5); 5275 | return std::chrono::steady_clock::now() + max_duration; 5276 | }; 5277 | 5278 | const auto loop_timeout_tp = []() 5279 | { 5280 | const auto max_duration = std::chrono::seconds(10); 5281 | return std::chrono::steady_clock::now() + max_duration; 5282 | }; 5283 | 5284 | const std::vector<std::string> expressions = 5285 | { 5286 | "x + y / 2", 5287 | "sin(x) / cos(y) + 1", 5288 | "clamp(-1, sin(2 * pi * x) + cos(y / 2 * pi), +1)" 5289 | }; 5290 | 5291 | for (const auto& expr_str : expressions) 5292 | { 5293 | // Reset the timeout for the compilation RTC 5294 | compilation_timeout_check 5295 | .set_timeout_time(compile_timeout_tp()); 5296 | 5297 | expression_t expression; 5298 | 5299 | if (!parser.compile(large_expression_string, expression)) 5300 | { 5301 | printf("Error: %s\t\n", parser.error().c_str()); 5302 | continue; 5303 | } 5304 | 5305 | try 5306 | { 5307 | // Reset the timeout for the loop duration RTC 5308 | loop_rtc.set_timeout_time(loop_timeout_tp()); 5309 | 5310 | expression.value(); 5311 | } 5312 | catch(std::runtime_error& exception) 5313 | { 5314 | printf("Exception: %s\n Expression: %s\n", 5315 | exception.what(), 5316 | expr_str.c_str()); 5317 | } 5318 | } 5319 | 5320 | 5321 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5322 | 5323 | [SECTION 25 - BENCHMARKING] 5324 | As part of the ExprTk package there is an expression benchmark utility 5325 | named 'exprtk_benchmark'. The utility attempts to determine expression 5326 | evaluation speed (or rate of evaluations - evals per second), by 5327 | evaluating each expression numerous times and mutating the underlying 5328 | variables of the expression between each evaluation. The utility 5329 | assumes any valid ExprTk expression (containing conditionals, loops 5330 | etc), however it will only make use of a predefined set of scalar 5331 | variables, namely: a, b, c, x, y, z and w. That being said expressions 5332 | themselves can contain any number of local variables, vectors or 5333 | strings. There are two modes of operation: 5334 | 5335 | (1) Default 5336 | (2) User Specified Expressions 5337 | 5338 | 5339 | (1) Default 5340 | The default mode is enabled simply by executing the exprtk_benchmark 5341 | binary with no command line parameters. In this mode a predefined set 5342 | of expressions will be evaluated in three phases: 5343 | 5344 | (a) ExprTk evaluation 5345 | (b) Native evaluation 5346 | (c) ExprTk parse 5347 | 5348 | 5349 | In the first two phases (a and b) a list of predefined (hard-coded) 5350 | expressions will be evaluated using both ExprTk and native mode 5351 | implementations. This is done so as to compare evaluation times 5352 | between ExprTk and native implementations. The set of expressions used 5353 | are as follows: 5354 | 5355 | (01) (y + x) 5356 | (02) 2 * (y + x) 5357 | (03) (2 * y + 2 * x) 5358 | (04) ((1.23 * x^2) / y) - 123.123 5359 | (05) (y + x / y) * (x - y / x) 5360 | (06) x / ((x + y) + (x - y)) / y 5361 | (07) 1 - ((x * y) + (y / x)) - 3 5362 | (08) (5.5 + x) + (2 * x - 2 / 3 * y) * (x / 3 + y / 4) + (y + 7.7) 5363 | (09) 1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^15 - 5.5x^23 + 6.6y^55 5364 | (10) sin(2 * x) + cos(pi / y) 5365 | (11) 1 - sin(2 * x) + cos(pi / y) 5366 | (12) sqrt(111.111 - sin(2 * x) + cos(pi / y) / 333.333) 5367 | (13) (x^2 / sin(2 * pi / y)) - x / 2 5368 | (14) x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y 5369 | (15) clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0) 5370 | (16) max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11)) 5371 | (17) if((y + (x * 2.2)) <= (x + y + 1.1), x - y, x*y) + 2 * pi / x 5372 | 5373 | 5374 | The third and final phase (c), is used to determine average 5375 | compilation rates (compiles per second) for expressions of varying 5376 | complexity. Each expression is compiled 100K times and the average for 5377 | each expression is output. 5378 | 5379 | 5380 | (2) User Specified Expressions 5381 | In this mode two parameters are passed to the utility via the command 5382 | line: 5383 | 5384 | (a) A name of a text file containing one expression per line 5385 | (b) An integer representing the number of evaluations per expression 5386 | 5387 | 5388 | An example execution of the benchmark utility in this mode is as 5389 | follows: 5390 | 5391 | ./exprtk_benchmark my_expressions.txt 1000000 5392 | 5393 | 5394 | The above invocation will load the expressions from the file 5395 | 'my_expressions.txt' and will then proceed to evaluate each expression 5396 | one million times, varying the above mentioned variables (x, y, z 5397 | etc.) between each evaluation, and at the end of each expression round 5398 | a print out of running times, result of a single evaluation and total 5399 | sum of results is provided as demonstrated below: 5400 | 5401 | Expression 1 of 7 4.770 ns 47700 ns ( 9370368.0) '((((x+y)+z)))' 5402 | Expression 2 of 7 4.750 ns 47500 ns ( 1123455.9) '((((x+y)-z)))' 5403 | Expression 3 of 7 4.766 ns 47659 ns (21635410.7) '((((x+y)*z)))' 5404 | Expression 4 of 7 5.662 ns 56619 ns ( 1272454.9) '((((x+y)/z)))' 5405 | Expression 5 of 7 4.950 ns 49500 ns ( 4123455.9) '((((x-y)+z)))' 5406 | Expression 6 of 7 7.581 ns 75810 ns (-4123455.9) '((((x-y)-z)))' 5407 | Expression 7 of 7 4.801 ns 48010 ns ( 0.0) '((((x-y)*z)))' 5408 | 5409 | 5410 | The benchmark utility can be very useful when investigating evaluation 5411 | efficiency issues with ExprTk or simply during the prototyping of 5412 | expressions. As an example, lets take the following expression: 5413 | 5414 | 1 / sqrt(2x) * e^(3y) 5415 | 5416 | 5417 | Lets say we would like to determine which sub-part of the expression 5418 | takes the most time to evaluate and perhaps attempt to rework the 5419 | expression based on the results. In order to do this we will create a 5420 | text file called 'test.txt' and then proceed to make some educated 5421 | guesses about how to break the expression up into its more 5422 | 'interesting' sub-parts which we will then add as one expression per 5423 | line to the file. An example breakdown may be as follows: 5424 | 5425 | 1 / sqrt(2x) * e^(3y) 5426 | 1 / sqrt(2x) 5427 | e^(3y) 5428 | 5429 | 5430 | The benchmark with the given file, where each expression will be 5431 | evaluated 100K times can be executed as follows: 5432 | 5433 | ./exprtk_benchmark test.txt 100000 5434 | Expr 1 of 3 90.340 ns 9034000 ns (296417859.3) '1/sqrt(2x)*e^(3y)' 5435 | Expr 2 of 3 11.100 ns 1109999 ns ( 44267.3) '1/sqrt(2x)' 5436 | Expr 3 of 3 77.830 ns 7783000 ns (615985286.6) 'e^(3y)' 5437 | [*] Number Of Evals: 300000 5438 | [*] Total Time: 0.018sec 5439 | [*] Total Single Eval Time: 0.000ms 5440 | 5441 | 5442 | From the results above we conclude that the third expression (e^(3y)) 5443 | consumes the largest amount of time. The variable 'e', as used in both 5444 | the benchmark and in the expression, is an approximation of the 5445 | transcendental mathematical constant e (2.71828182845904...) hence the 5446 | sub-expression should perhaps be modified to use the generally more 5447 | efficient built-in 'exp' function. 5448 | 5449 | ./exprtk_benchmark test.txt 1000000 5450 | Expr 1 of 5 86.563 ns 8656300ns (296417859.6) '1/sqrt(2x)*e^(3y)' 5451 | Expr 2 of 5 40.506 ns 4050600ns (296417859.6) '1/sqrt(2x)*exp(3y)' 5452 | Expr 3 of 5 14.248 ns 1424799ns ( 44267.2) '1/sqrt(2x)' 5453 | Expr 4 of 5 88.840 ns 8884000ns (615985286.9) 'e^(3y)' 5454 | Expr 5 of 5 29.267 ns 2926699ns (615985286.9) 'exp(3y)' 5455 | [*] Number Of Evals: 5000000 5456 | [*] Total Time: 0.260sec 5457 | [*] Total Single Eval Time: 0.000ms 5458 | 5459 | 5460 | The above output demonstrates the results from making the previously 5461 | mentioned modification to the expression. As can be seen the new form 5462 | of the expression using the 'exp' function reduces the evaluation time 5463 | by over 50%, in other words increases the evaluation rate by two fold. 5464 | 5465 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5466 | 5467 | [SECTION 26 - EXPRTK NOTES] 5468 | The following is a list of facts and suggestions one may want to take 5469 | into account when using ExprTk: 5470 | 5471 | (00) Precision and performance of expression evaluations are the 5472 | dominant principles of the ExprTk library. 5473 | 5474 | (01) ExprTk uses a rudimentary imperative programming model with 5475 | syntax based on languages such as Pascal and C. Furthermore 5476 | ExprTk is an LL(2) type grammar and is processed using a 5477 | recursive descent parsing algorithm. 5478 | 5479 | (02) Supported types are float, double, long double and MPFR/GMP. 5480 | Generally any user defined numerical type that supports all the 5481 | basic floating point arithmetic operations: -,+,*,/,^,% etc; 5482 | unary and binary operations: sin,cos,min,max,equal etc and any 5483 | other ExprTk dependent operations can be used to specialise the 5484 | various components: expression, parser and symbol_table. 5485 | 5486 | (03) Standard arithmetic operator precedence is applied (BEDMAS). In 5487 | general C, Pascal or Rust equivalent unary, binary, logical and 5488 | equality/inequality operator precedence rules apply. 5489 | eg: a == b and c > d + 1 ---> (a == b) and (c > (d + 1)) 5490 | x - y <= z / 2 ---> (x - y) <= (z / 2) 5491 | a - b / c * d^2^3 ---> a - ((b / c) * d^(2^3)) 5492 | 5493 | (04) Results of expressions that are deemed as being 'valid' are to 5494 | exist within the set of Real numbers. All other results will be 5495 | of the value: Not-A-Number (NaN). However this may not 5496 | necessarily be a requirement for user defined numerical types, 5497 | eg: complex number type. 5498 | 5499 | (05) Supported user defined types are numeric and string 5500 | variables, numeric vectors and functions. 5501 | 5502 | (06) All reserved words, keywords, variable, vector, string and 5503 | function names are case-insensitive. 5504 | 5505 | (07) Variable, vector, string variable and function names must begin 5506 | with a letter (A-Z or a-z), then can be comprised of any 5507 | combination of letters, digits, underscores and dots, ending in 5508 | either a letter (A-Z or a-z), digit or underscore. (eg: x, y2, 5509 | var1, power_func99, person.age, item.size.0). The associated 5510 | regex pattern is: [a-zA-Z][a-zA-Z0-9_.]*[a-zA-Z0-9_]+ 5511 | 5512 | (08) Expression lengths and sub-expression lists are limited only by 5513 | storage capacity. 5514 | 5515 | (09) The life-time of objects registered with or created from a 5516 | specific symbol-table must span at least the lifetime of the 5517 | symbol table instance and all compiled expressions which 5518 | utilise objects, such as variables, strings, vectors, function 5519 | compositor functions and functions of that symbol-table, 5520 | otherwise the result will be undefined behaviour. 5521 | 5522 | (10) Equal and not_equal are normalised-epsilon equality routines, 5523 | which use epsilons of 0.0000000001 and 0.000001 for double and 5524 | float types respectively. 5525 | 5526 | (11) All trigonometric functions assume radian input unless stated 5527 | otherwise. 5528 | 5529 | (12) Expressions may contain white-space characters such as space, 5530 | tabs, new-lines, control-feed et al. 5531 | ('\n', '\r', '\t', '\b', '\v', '\f') 5532 | 5533 | (13) Strings may be comprised of any combination of letters, digits 5534 | special characters including (~!@#$%^&*()[]|=+ ,./?<>;:"`~_) or 5535 | hexadecimal escaped sequences (eg: \0x30) and must be enclosed 5536 | with single-quotes. 5537 | eg: 'Frankly my dear, \0x49 do n0t give a damn!' 5538 | 5539 | (14) User defined normal functions can have up to 20 parameters, 5540 | where as user defined generic-functions and vararg-functions 5541 | can have an unlimited number of parameters. 5542 | 5543 | (15) The inbuilt polynomial functions can be at most of degree 12. 5544 | 5545 | (16) Where appropriate constant folding optimisations may be applied. 5546 | (eg: The expression '2 + (3 - (x / y))' becomes '5 - (x / y)') 5547 | 5548 | (17) If the strength reduction compilation option has been enabled, 5549 | then where applicable strength reduction optimisations may be 5550 | applied. 5551 | 5552 | (18) String processing capabilities are available by default. To 5553 | turn them off, the following needs to be defined at compile 5554 | time: exprtk_disable_string_capabilities 5555 | 5556 | (19) Composited functions can call themselves or any other functions 5557 | that have been defined prior to their own definition. 5558 | 5559 | (20) Recursive calls made from within composited functions will have 5560 | a stack size bound by the stack of the executing architecture. 5561 | 5562 | (21) User defined functions by default are assumed to have side 5563 | effects. As such an "all constant parameter" invocation of such 5564 | functions wont result in constant folding. If the function has 5565 | no side-effects then that can be noted during the constructor 5566 | of the ifunction allowing it to be constant folded where 5567 | appropriate. 5568 | 5569 | (22) The entity relationship between symbol_table and an expression 5570 | is many-to-many. However the intended 'typical' use-case where 5571 | possible, is to have a single symbol table manage the variable 5572 | and function requirements of multiple expressions. 5573 | 5574 | (23) The common use-case for an expression is to have it compiled 5575 | only ONCE and then subsequently have it evaluated multiple 5576 | times. An extremely inefficient and suboptimal approach would 5577 | be to recompile an expression from its string form every time 5578 | it requires evaluating. 5579 | 5580 | (24) It is strongly recommended that the return value of method 5581 | invocations from the parser and symbol_table types be taken 5582 | into account. Specifically the 'compile' method of the parser 5583 | and the 'add_xxx' set of methods of the symbol_table as they 5584 | denote either the success or failure state of the invoked call. 5585 | Continued processing from a failed state without having first 5586 | rectified the underlying issue will in turn result in further 5587 | failures and undefined behaviours. 5588 | 5589 | (25) The following are examples of compliant floating point value 5590 | representations: 5591 | 5592 | (01) 12345 (06) -123.456 5593 | (02) +123.456e+12 (07) 123.456E-12 5594 | (03) +012.045e+07 (08) .1234 5595 | (04) 1234. (09) -56789. 5596 | (05) 123.456f (10) -321.654E+3L 5597 | 5598 | (26) Expressions may contain any of the following comment styles: 5599 | 5600 | (1) // .... \n 5601 | (2) # .... \n 5602 | (3) /* .... */ 5603 | 5604 | (27) The 'null' value type is a special non-zero type that 5605 | incorporates specific semantics when undergoing operations with 5606 | the standard numeric type. The following is a list of type and 5607 | boolean results associated with the use of 'null': 5608 | 5609 | (1) null +,-,*,/,% x --> x 5610 | (2) x +,-,*,/,% null --> x 5611 | (3) null +,-,*,/,% null --> null 5612 | (4) null == null --> true 5613 | (5) null == x --> true 5614 | (6) x == null --> true 5615 | (7) x != null --> false 5616 | (8) null != null --> false 5617 | (9) null != x --> false 5618 | 5619 | (28) The following is a list of reserved words and symbols used by 5620 | ExprTk. Attempting to add a variable or custom function to a 5621 | symbol table using any of the reserved words will result in a 5622 | failure. 5623 | 5624 | abs, acos, acosh, and, asin, asinh, assert, atan, atan2, 5625 | atanh, avg, break, case, ceil, clamp, continue, cosh, cos, 5626 | cot, csc, default, deg2grad, deg2rad, else, equal, erfc, 5627 | erf, exp, expm1, false, floor, for, frac, grad2deg, hypot, 5628 | iclamp, if, ilike, in, inrange, in, like, log, log10, log1p, 5629 | log2, logn, mand, max, min, mod, mor, mul, nand, ncdf, nor, 5630 | not, not_equal, not, null, or, pow, rad2deg, repeat, return, 5631 | root, roundn, round, sec, sgn, shl, shr, sinc, sinh, sin, 5632 | sqrt, sum, swap, switch, tanh, tan, true, trunc, until, var, 5633 | while, xnor, xor 5634 | 5635 | (29) Every valid ExprTk statement is a "value returning" expression. 5636 | Unlike some languages that limit the types of expressions that 5637 | can be performed in certain situations, in ExprTk any valid 5638 | expression can be used in any "value consuming" context. eg: 5639 | 5640 | var y := 3; 5641 | for (var x := switch 5642 | { 5643 | case 1 : 7; 5644 | case 2 : -1 + ~{var x{};}; 5645 | default : y > 2 ? 3 : 4; 5646 | }; 5647 | x != while (y > 0) { y -= 1; }; 5648 | x -= { 5649 | if (min(x,y) < 2 * max(x,y)) 5650 | x + 2; 5651 | else 5652 | x + y - 3; 5653 | } 5654 | ) 5655 | { 5656 | (x + y) / (x - y); 5657 | }; 5658 | 5659 | (30) It is recommended when prototyping expressions that the ExprTk 5660 | REPL be utilised, as it supports all the features available in 5661 | the library, including complete error analysis, benchmarking 5662 | and dependency dumps etc which allows for rapid 5663 | coding/prototyping and debug cycles without the hassle of 5664 | having to recompile test programs with expressions that have 5665 | been hard-coded. It is also a good source of truth for how the 5666 | library's various features can be applied. 5667 | 5668 | (31) For performance considerations, one should assume the actions 5669 | of expression, symbol table and parser instance instantiation 5670 | and destruction, and the expression compilation process itself 5671 | to be of high latency. Hence none of them should be part of any 5672 | performance critical code paths, and should instead occur 5673 | entirely either before or after such code paths. 5674 | 5675 | (32) Deep copying an expression instance for the purposes of 5676 | persisting to disk or otherwise transmitting elsewhere with the 5677 | intent to 'resurrect' the expression instance later on is not 5678 | possible due to the reasons described in the final note of 5679 | Section 10. The recommendation is to instead simply persist the 5680 | string form of the expression and compile the expression at 5681 | run-time on the target. 5682 | 5683 | (33) The correctness and robustness of the ExprTk library is 5684 | maintained by having a comprehensive suite of unit tests and 5685 | functional tests all of which are run using sanitizers (ASAN, 5686 | UBSAN, LSAN, MSAN, TSAN). Additionally, continuous fuzz-testing 5687 | provided by Google OSS Fuzz, and static analysis via Synopsis 5688 | Coverity. 5689 | 5690 | (34) The library name ExprTk is pronounced "Ex-Pee-Ar-Tee-Kay" or 5691 | simply "Mathematical Expression Toolkit" 5692 | 5693 | 5694 | (35) For general support, inquires or bug/issue reporting: 5695 | https://www.partow.net/programming/exprtk/index.html#support 5696 | 5697 | (36) Before jumping in and using ExprTk, do take the time to peruse 5698 | the documentation and all of the examples, both in the main and 5699 | the extras distributions. Having an informed general view of 5700 | what can and can't be done, and how something should be done 5701 | with ExprTk, will likely result in a far more productive and 5702 | enjoyable programming experience. 5703 | 5704 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5705 | 5706 | [SECTION 27 - SIMPLE EXPRTK EXAMPLE] 5707 | The following is a simple yet complete example demonstrating typical 5708 | usage of the ExprTk Library. The example instantiates a symbol table 5709 | object, adding to it three variables named x, y and z, and a custom 5710 | user defined function, that accepts only two parameters, named myfunc. 5711 | The example then proceeds to instantiate an expression object and 5712 | register to it the symbol table instance. 5713 | 5714 | A parser is then instantiated, and the string representation of the 5715 | expression and the expression object are passed to the parser's 5716 | compile method for compilation. If an error occurred during 5717 | compilation, the compile method will return false, leading to a series 5718 | of error diagnostics being printed to stdout. Otherwise the newly 5719 | compiled expression is evaluated by invoking the expression object's 5720 | value method, and subsequently printing the result of the computation 5721 | to stdout. 5722 | 5723 | 5724 | --- snip --- 5725 | #include5726 | #include 5727 | 5728 | #include "exprtk.hpp" 5729 | 5730 | template 5731 | struct myfunc final : public exprtk::ifunction<T> 5732 | { 5733 | myfunc() : exprtk::ifunction<T>(2) {} 5734 | 5735 | T operator()(const T& v1, const T& v2) override 5736 | { 5737 | return T(1) + (v1 * v2) / T(3); 5738 | } 5739 | }; 5740 | 5741 | int main() 5742 | { 5743 | typedef exprtk::symbol_table<double> symbol_table_t; 5744 | typedef exprtk::expression<double> expression_t; 5745 | typedef exprtk::parser<double> parser_t; 5746 | typedef exprtk::parser_error::type error_t; 5747 | 5748 | const std::string expression_string = 5749 | "z := 2 myfunc([4 + sin(x / pi)^3],y ^ 2)" 5750 | 5751 | double x = 1.1; 5752 | double y = 2.2; 5753 | double z = 3.3; 5754 | 5755 | myfunc<double> mf; 5756 | 5757 | symbol_table_t symbol_table; 5758 | symbol_table.add_constants(); 5759 | symbol_table.add_variable("x",x); 5760 | symbol_table.add_variable("y",y); 5761 | symbol_table.add_variable("z",z); 5762 | symbol_table.add_function("myfunc",mf); 5763 | 5764 | expression_t expression; 5765 | expression.register_symbol_table(symbol_table); 5766 | 5767 | parser_t parser; 5768 | 5769 | if (!parser.compile(expression_string,expression)) 5770 | { 5771 | // A compilation error has occurred. Attempt to 5772 | // print all errors to stdout. 5773 | 5774 | printf("Error: %s\tExpression: %s\n", 5775 | parser.error().c_str(), 5776 | expression_string.c_str()); 5777 | 5778 | for (std::size_t i = 0; i < parser.error_count(); ++i) 5779 | { 5780 | // Include the specific nature of each error 5781 | // and its position in the expression string. 5782 | 5783 | error_t error = parser.get_error(i); 5784 | 5785 | printf("Error: %02d Position: %02d " 5786 | "Type: [%s] " 5787 | "Message: %s " 5788 | "Expression: %s\n", 5789 | static_cast<int>(i), 5790 | static_cast<int>(error.token.position), 5791 | exprtk::parser_error::to_str(error.mode).c_str(), 5792 | error.diagnostic.c_str(), 5793 | expression_string.c_str()); 5794 | } 5795 | 5796 | return 1; 5797 | } 5798 | 5799 | // Evaluate the expression and obtain its result. 5800 | 5801 | double result = expression.value(); 5802 | 5803 | printf("Result: %10.5f\n",result); 5804 | 5805 | return 0; 5806 | } 5807 | --- snip --- 5808 | 5809 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5810 | 5811 | [SECTION 28 - BUILD OPTIONS] 5812 | When building ExprTk there are a number of defines that will enable or 5813 | disable certain features and capabilities. The defines can either be 5814 | part of a compiler command line switch or scoped around the include to 5815 | the ExprTk header. The defines are as follows: 5816 | 5817 | (01) exprtk_enable_debugging 5818 | (02) exprtk_disable_cardinal_pow_optimisation 5819 | (03) exprtk_disable_comments 5820 | (04) exprtk_disable_break_continue 5821 | (05) exprtk_disable_sc_andor 5822 | (06) exprtk_disable_return_statement 5823 | (07) exprtk_disable_enhanced_features 5824 | (08) exprtk_disable_string_capabilities 5825 | (09) exprtk_disable_superscalar_unroll 5826 | (10) exprtk_disable_rtl_io 5827 | (11) exprtk_disable_rtl_io_file 5828 | (12) exprtk_disable_rtl_vecops 5829 | (13) exprtk_disable_caseinsensitivity 5830 | (14) exprtk_enable_range_runtime_checks 5831 | 5832 | (01) exprtk_enable_debugging 5833 | This define will enable printing of debug information to stdout during 5834 | the compilation process. 5835 | 5836 | (02) exprtk_disable_cardinal_pow_optimisation 5837 | This define will disable the optimisation invoked when constant 5838 | integers are used as powers in exponentiation expressions (eg: x^7). 5839 | 5840 | (03) exprtk_disable_comments 5841 | This define will disable the ability for expressions to have comments. 5842 | Expressions that have comments when parsed with a build that has this 5843 | option, will result in a compilation failure. 5844 | 5845 | (04) exprtk_disable_break_continue 5846 | This define will disable the loop-wise 'break' and 'continue' 5847 | capabilities. Any expression that contains those keywords will result 5848 | in a compilation failure. 5849 | 5850 | (05) exprtk_disable_sc_andor 5851 | This define will disable the short-circuit '&' (and) and '|' (or) 5852 | operators 5853 | 5854 | (06) exprtk_disable_return_statement 5855 | This define will disable use of return statements within expressions. 5856 | 5857 | (07) exprtk_disable_enhanced_features 5858 | This define will disable all enhanced features such as strength 5859 | reduction and special function optimisations and expression specific 5860 | type instantiations. This feature will reduce compilation times and 5861 | binary sizes but will also result in massive performance degradation 5862 | of expression evaluations. 5863 | 5864 | (08) exprtk_disable_string_capabilities 5865 | This define will disable all string processing capabilities. Any 5866 | expression that contains a string or string related syntax will result 5867 | in a compilation failure. 5868 | 5869 | (09) exprtk_disable_superscalar_unroll 5870 | This define will set the loop unroll batch size to 4 operations per 5871 | loop instead of the default 8 operations. This define is used in 5872 | operations that involve vectors and aggregations over vectors. When 5873 | targeting non-superscalar architectures, it may be recommended to 5874 | build using this particular option if efficiency of evaluations is of 5875 | concern. 5876 | 5877 | (10) exprtk_disable_rtl_io 5878 | This define will disable all of basic IO RTL package features. When 5879 | present, any attempt to register the basic IO RTL package with a given 5880 | symbol table will fail causing a compilation error. 5881 | 5882 | (11) exprtk_disable_rtl_io_file 5883 | This define will disable the file I/O RTL package features. When 5884 | present, any attempts to register the file I/O package with a given 5885 | symbol table will fail causing a compilation error. 5886 | 5887 | (12) exprtk_disable_rtl_vecops 5888 | This define will disable the extended vector operations RTL package 5889 | features. When present, any attempts to register the vector operations 5890 | package with a given symbol table will fail causing a compilation 5891 | error. 5892 | 5893 | (13) exprtk_disable_caseinsensitivity 5894 | This define will disable case-insensitivity when matching variables 5895 | and functions. Furthermore all reserved and keywords will only be 5896 | acknowledged when in all lower-case. 5897 | 5898 | (14) exprtk_enable_range_runtime_checks 5899 | This define will enable run-time checks pertaining to vector indexing 5900 | operations used in any of the vector-to-vector and vector-to-scalar 5901 | operations. 5902 | 5903 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5904 | 5905 | [SECTION 29 - FILES] 5906 | The source distribution of ExprTk is comprised of the following set of 5907 | files: 5908 | 5909 | (00) Makefile 5910 | (01) readme.txt 5911 | (02) exprtk.hpp 5912 | (03) exprtk_test.cpp 5913 | (04) exprtk_benchmark.cpp 5914 | (05) exprtk_simple_example_01.cpp 5915 | (06) exprtk_simple_example_02.cpp 5916 | (07) exprtk_simple_example_03.cpp 5917 | (08) exprtk_simple_example_04.cpp 5918 | (09) exprtk_simple_example_05.cpp 5919 | (10) exprtk_simple_example_06.cpp 5920 | (11) exprtk_simple_example_07.cpp 5921 | (12) exprtk_simple_example_08.cpp 5922 | (13) exprtk_simple_example_09.cpp 5923 | (14) exprtk_simple_example_10.cpp 5924 | (15) exprtk_simple_example_11.cpp 5925 | (16) exprtk_simple_example_12.cpp 5926 | (17) exprtk_simple_example_13.cpp 5927 | (18) exprtk_simple_example_14.cpp 5928 | (19) exprtk_simple_example_15.cpp 5929 | (20) exprtk_simple_example_16.cpp 5930 | (21) exprtk_simple_example_17.cpp 5931 | (22) exprtk_simple_example_18.cpp 5932 | (23) exprtk_simple_example_19.cpp 5933 | (24) exprtk_simple_example_20.cpp 5934 | (25) exprtk_simple_example_21.cpp 5935 | (26) exprtk_simple_example_22.cpp 5936 | (27) exprtk_simple_example_23.cpp 5937 | (28) exprtk_simple_example_24.cpp 5938 | 5939 | 5940 | Details for each of the above examples can be found here: 5941 | 5942 | https://www.partow.net/programming/exprtk/index.html#examples 5943 | 5944 | 5945 | Various extended and advanced examples using ExprTk are available 5946 | via the following: 5947 | 5948 | (00) exprtk_american_option_binomial_model.cpp 5949 | (01) exprtk_binomial_coefficient.cpp 5950 | (02) exprtk_bsm_benchmark.cpp 5951 | (03) exprtk_calc.cpp 5952 | (04) exprtk_collatz.cpp 5953 | (05) exprtk_compilation_timeout.cpp 5954 | (06) exprtk_degree_trigonometry_example.cpp 5955 | (07) exprtk_exprgen.cpp 5956 | (08) exprtk_extract_dependents.cpp 5957 | (09) exprtk_e_10kdigits.cpp 5958 | (10) exprtk_factorize_fermat.cpp 5959 | (11) exprtk_factorize_pollard.cpp 5960 | (12) exprtk_fizzbuzz.cpp 5961 | (13) exprtk_funcall_benchmark.cpp 5962 | (14) exprtk_game_of_life.cpp 5963 | (15) exprtk_gcd.cpp 5964 | (16) exprtk_gnuplot.cpp 5965 | (17) exprtk_gnuplot_multi.cpp 5966 | (18) exprtk_groups_examples.cpp 5967 | (19) exprtk_immutable_symbol_table_example.cpp 5968 | (20) exprtk_import_packages.cpp 5969 | (21) exprtk_instruction_primer.cpp 5970 | (22) exprtk_jump_diffusion_process.cpp 5971 | (23) exprtk_loop_timeout_rtc.cpp 5972 | (24) exprtk_magic_square.cpp 5973 | (25) exprtk_mandelbrot.cpp 5974 | (26) exprtk_max_subarray_sum.cpp 5975 | (27) exprtk_maze_generator.cpp 5976 | (28) exprtk_miller_rabin_primality_test.cpp 5977 | (29) exprtk_montecarlo_e.cpp 5978 | (30) exprtk_montecarlo_option_pricing_model.cpp 5979 | (31) exprtk_montecarlo_pi.cpp 5980 | (32) exprtk_naive_primes.cpp 5981 | (33) exprtk_normal_random_marsaglia_method.cpp 5982 | (34) exprtk_nqueens_problem.cpp 5983 | (35) exprtk_nthroot_bisection.cpp 5984 | (36) exprtk_ornstein_uhlenbeck_process.cpp 5985 | (37) exprtk_pascals_triangle.cpp 5986 | (38) exprtk_pi_10kdigits.cpp 5987 | (39) exprtk_prime_sieve.cpp 5988 | (40) exprtk_prime_sieve_vectorized.cpp 5989 | (41) exprtk_pythagorean_triples.cpp 5990 | (42) exprtk_recursive_fibonacci.cpp 5991 | (43) exprtk_repl.cpp 5992 | (44) exprtk_riddle.cpp 5993 | (45) exprtk_rtc_overhead.cpp 5994 | (46) exprtk_sudoku_solver.cpp 5995 | (47) exprtk_sumofprimes.cpp 5996 | (48) exprtk_symtab_functions.cpp 5997 | (49) exprtk_testgen.cpp 5998 | (50) exprtk_tower_of_hanoi.cpp 5999 | (51) exprtk_truthtable_gen.cpp 6000 | (52) exprtk_vectorized_binomial_model.cpp 6001 | (53) exprtk_vectornorm.cpp 6002 | (54) exprtk_vector_benchmark.cpp 6003 | (55) exprtk_vector_benchmark_multithreaded.cpp 6004 | (56) exprtk_vector_resize_example.cpp 6005 | (57) exprtk_vector_resize_inline_example.cpp 6006 | (58) exprtk_wiener_process_pi.cpp 6007 | 6008 | 6009 | Details for each of the above examples can be found here: 6010 | 6011 | https://partow.net/programming/exprtk/index.html#variousexamples 6012 | 6013 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6014 | 6015 | [SECTION 30 - LANGUAGE STRUCTURE] 6016 | The following are the various language structures available within 6017 | ExprTk and their structural representations. 6018 | 6019 | (00) If Statement 6020 | (01) Else Statement 6021 | (02) Ternary Statement 6022 | (03) While Loop 6023 | (04) Repeat Until Loop 6024 | (05) For Loop 6025 | (06) Switch Statement 6026 | (07) Multi Subexpression Statement 6027 | (08) Multi Case-Consequent Statement 6028 | (09) Variable Definition Statement 6029 | (10) Vector Definition Statement 6030 | (11) String Definition Statement 6031 | (12) Range Statement 6032 | (13) Return Statement 6033 | 6034 | 6035 | (00) - If Statement 6036 | +-------------------------------------------------------------+ 6037 | | | 6038 | | [if] ---> [(] ---> [condition] -+-> [,] -+ | 6039 | | | | | 6040 | | +---------------<---------------+ | | 6041 | | | | | 6042 | | | +------------------<------------------+ | 6043 | | | | | 6044 | | | +--> [consequent] ---> [,] ---> [alternative] ---> [)] | 6045 | | | | 6046 | | +--> [)] --+-> [{] ---> [expression*] ---> [}] --+ | 6047 | | | | | 6048 | | | +---------<----------+ | 6049 | | +----<-----+ | | 6050 | | | v | 6051 | | +--> [consequent] --> [;] -{*}-> [else-statement] | 6052 | | | 6053 | +-------------------------------------------------------------+ 6054 | 6055 | 6056 | (01) - Else Statement 6057 | +-------------------------------------------------------------+ 6058 | | | 6059 | | [else] -+-> [alternative] ---> [;] | 6060 | | | | 6061 | | +--> [{] ---> [expression*] ---> [}] | 6062 | | | | 6063 | | +--> [if-statement] | 6064 | | | 6065 | +-------------------------------------------------------------+ 6066 | 6067 | 6068 | (02) - Ternary Statement 6069 | +-------------------------------------------------------------+ 6070 | | | 6071 | | [condition] ---> [?] ---> [consequent] ---> [:] --+ | 6072 | | | | 6073 | | +------------------------<------------------------+ | 6074 | | | | 6075 | | +--> [alternative] --> [;] | 6076 | | | 6077 | +-------------------------------------------------------------+ 6078 | 6079 | 6080 | (03) - While Loop 6081 | +-------------------------------------------------------------+ 6082 | | | 6083 | | [while] ---> [(] ---> [condition] ---> [)] ---+ | 6084 | | | | 6085 | | +----------------------<----------------------+ | 6086 | | | | 6087 | | +--> [{] ---> [expression*] ---> [}] | 6088 | | | 6089 | +-------------------------------------------------------------+ 6090 | 6091 | 6092 | (04) - Repeat Until Loop 6093 | +-------------------------------------------------------------+ 6094 | | | 6095 | | [repeat] ---> [expression*] ---+ | 6096 | | | | 6097 | | +--------------<---------------+ | 6098 | | | | 6099 | | +--> [until] ---> [(] ---> [condition] --->[)] | 6100 | | | 6101 | +-------------------------------------------------------------+ 6102 | 6103 | 6104 | (05) - For Loop 6105 | +-------------------------------------------------------------+ 6106 | | | 6107 | | [for] ---> [(] -+-> [initialise expression] --+--+ | 6108 | | | | | | 6109 | | +------------->---------------+ v | 6110 | | | | 6111 | | +-----------------------<------------------------+ | 6112 | | | | 6113 | | +--> [;] -+-> [condition] -+-> [;] ---+ | 6114 | | | | | | 6115 | | +------->--------+ v | 6116 | | | | 6117 | | +------------------<---------+--------+ | 6118 | | | | | 6119 | | +--> [increment expression] -+-> [)] --+ | 6120 | | | | 6121 | | +------------------<-------------------+ | 6122 | | | | 6123 | | +--> [{] ---> [expression*] ---> [}] | 6124 | | | 6125 | +-------------------------------------------------------------+ 6126 | 6127 | 6128 | (06) - Switch Statement 6129 | +-------------------------------------------------------------+ 6130 | | | 6131 | | [switch] ---> [{] ---+ | 6132 | | | | 6133 | | +---------<----------+-----------<-----------+ | 6134 | | | | | 6135 | | +--> [case] ---> [condition] ---> [:] ---+ | | 6136 | | | | | 6137 | | +-------------------<--------------------+ | | 6138 | | | | | 6139 | | +--> [consequent] ---> [;] --------->--------+ | 6140 | | | | | 6141 | | | | | 6142 | | +--> [default] ---> [consequent] ---> [;] ---+ | 6143 | | | | | 6144 | | +---------------------<----------------------+ | 6145 | | | | 6146 | | +--> [}] | 6147 | | | 6148 | +-------------------------------------------------------------+ 6149 | 6150 | 6151 | (07) - Multi Subexpression Statement 6152 | +-------------------------------------------------------------+ 6153 | | | 6154 | | +--------------<---------------+ | 6155 | | | | | 6156 | | [~] ---> [{\(] -+-> [expression] -+-> [;\,] ---+ | 6157 | | | | 6158 | | +----------------<----------------+ | 6159 | | | | 6160 | | +--> [}\)] | 6161 | | | 6162 | +-------------------------------------------------------------+ 6163 | 6164 | 6165 | (08) - Multi Case-Consequent Statement 6166 | +-------------------------------------------------------------+ 6167 | | | 6168 | | [[*]] ---> [{] ---+ | 6169 | | | | 6170 | | +--------<--------+--------------<----------+ | 6171 | | | | | 6172 | | +--> [case] ---> [condition] ---> [:] ---+ | | 6173 | | | | | 6174 | | +-------------------<--------------------+ | | 6175 | | | | | 6176 | | +--> [consequent] ---> [;] ---+------>------+ | 6177 | | | | 6178 | | +--> [}] | 6179 | | | 6180 | +-------------------------------------------------------------+ 6181 | 6182 | 6183 | (09) - Variable Definition Statement 6184 | +-------------------------------------------------------------+ 6185 | | | 6186 | | [var] ---> [symbol] -+-> [:=] -+-> [expression] -+-> [;] | 6187 | | | | | | 6188 | | | +-----> [{}] -->--+ | 6189 | | | | | 6190 | | +------------->-------------+ | 6191 | | | 6192 | +-------------------------------------------------------------+ 6193 | 6194 | 6195 | (10) - Vector Definition Statement 6196 | +-------------------------------------------------------------+ 6197 | | | 6198 | | [var] ---> [symbol] ---> [[] ---> [constant] ---> []] --+ | 6199 | | | | 6200 | | +---------------------------<---------------------------+ | 6201 | | | | 6202 | | | +--------->---------+ | 6203 | | | | | | 6204 | | +--> [:=] ---> [{] -+-+-> [expression] -+-> [}] ---> [;] | 6205 | | | | | 6206 | | +--<--- [,] <-----+ | 6207 | | | 6208 | +-------------------------------------------------------------+ 6209 | 6210 | 6211 | (11) - String Definition Statement 6212 | +-------------------------------------------------------------+ 6213 | | | 6214 | | [var] --> [symbol] --> [:=] --> [str-expression] ---> [;] | 6215 | | | 6216 | +-------------------------------------------------------------+ 6217 | 6218 | 6219 | (12) - Range Statement 6220 | +-------------------------------------------------------------+ 6221 | | | 6222 | | +-------->--------+ | 6223 | | | | | 6224 | | [[] -+-> [expression] -+-> [:] -+-> [expression] -+--> []] | 6225 | | | | | 6226 | | +-------->--------+ | 6227 | | | 6228 | +-------------------------------------------------------------+ 6229 | 6230 | 6231 | (13) - Return Statement 6232 | +-------------------------------------------------------------+ 6233 | | | 6234 | | [return] ---> [[] -+-> [expression] -+-> []] ---> [;] | 6235 | | | | | 6236 | | +--<--- [,] <-----+ | 6237 | | | 6238 | +-------------------------------------------------------------+