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 nine 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) Expression 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) Expression Size Check 1474 | ExprTk allows for expression local variables, vectors and strings to 1475 | be defined. As such the amount of data in terms of bytes consumed by 1476 | the expression locals can also be limited, so as to prevent 1477 | expressions once compiled from consuming large amounts of data. 1478 | 1479 | The maximum number of bytes an expression's locally defined variables 1480 | may use can be set via the parser settings prior to compilation as 1481 | follows: 1482 | 1483 | using expression_t = exprtk::expression<double>; 1484 | using parser_t = exprtk::parser<double>; 1485 | 1486 | expression_t expression; 1487 | parser_t parser; 1488 | 1489 | parser.settings().set_max_total_local_symbol_size_bytes(16); 1490 | 1491 | const std::string expression1 = "var x := 1; var y := 1;" 1492 | const std::string expression2 = "var x := 1; var v[2] := [1];" 1493 | 1494 | expression_t expression; 1495 | 1496 | parser.compile(expression1, expression); // compilation success 1497 | parser.compile(expression2, expression); // compilation error 1498 | 1499 | In the above example, the expression's max total local symbol size has 1500 | been set to 16 bytes. The numeric type being used is double which is 8 1501 | bytes per instance. The first expression (expression1) compiles 1502 | successfully because the total local symbol size is 16 bytes, 8 bytes 1503 | for each of the variables x and y. 1504 | 1505 | However the second expression (expression2) fails to compile. This is 1506 | because the total number of bytes needed for the expression is 24 1507 | bytes, 8 bytes for the variable x and 16 bytes for the vector v, as 1508 | such exceeding the previously set limit of 16 bytes. The error 1509 | diagnostic generated by the parser on compilation failure will look 1510 | like the following: 1511 | 1512 | ERR161 - Adding vector 'v' of size 2 bytes will exceed max total 1513 | local symbol size of: 16 bytes, current total size: 8 bytes 1514 | 1515 | 1516 | (10) Vector Size Check 1517 | When defining an expression local vector, ExprTk uses a default 1518 | maximum vector size of two billion elements. One may want to limit the 1519 | maximum vector size to be either smaller or larger than the specified 1520 | default value. The maximum size value can be changed via the parser 1521 | settings. 1522 | 1523 | parser_t parser; 1524 | 1525 | parser.settings().set_max_local_vector_size(1000000); 1526 | 1527 | std::string expression1 = "var v[1e6] := [123]" 1528 | std::string expression2 = "var v[1e9] := [123]" 1529 | 1530 | expression_t expression; 1531 | 1532 | parser.compile(expression1, expression); // compilation success 1533 | parser.compile(expression2, expression); // compilation error 1534 | 1535 | 1536 | In the above code, the maximum local vector size is set to one million 1537 | elements. During compilation of an expression if there is a vector 1538 | definition where the vector's size exceeds the maximum allowed vector 1539 | size a compilation error shall be emitted. The error diagnostic 1540 | generated by the parser on compilation failure will look like the 1541 | following: 1542 | 1543 | ERR160 - Invalid vector size. Must be an integer in the 1544 | range [0,1000000], size: 1000000000 1545 | 1546 | 1547 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1548 | 1549 | [SECTION 12 - EXPRESSION STRUCTURES] 1550 | Exprtk supports mathematical expressions in numerous forms based on a 1551 | simple imperative programming model. This section will cover the 1552 | following topics related to general structure and programming of 1553 | expressions using ExprTk: 1554 | 1555 | (1) Multi-Statement Expressions 1556 | (2) Statements And Side-Effects 1557 | (3) Conditional Statements 1558 | (4) Special Functions 1559 | 1560 | 1561 | (1) Multi-Statement Expressions 1562 | Expressions in ExprTk can be comprised of one or more statements, which 1563 | may sometimes be called sub-expressions. The following are two 1564 | examples of expressions stored in std::string variables, the first a 1565 | single statement and the second a multi-statement expression: 1566 | 1567 | std::string single_statement = " z := x + y " 1568 | 1569 | std::string multi_statement = " var temp := x; " 1570 | " x := y + z; " 1571 | " y := temp; " 1572 | 1573 | 1574 | In a multi-statement expression, the final statement will determine 1575 | the overall result of the expression. In the following multi-statement 1576 | expression, the result of the expression when evaluated will be '2.3', 1577 | which will also be the value stored in the 'y' variable. 1578 | 1579 | z := x + y; 1580 | y := 2.3; 1581 | 1582 | 1583 | As demonstrated in the expression above, statements within an 1584 | expression are separated using the semi-colon ';' operator. In the 1585 | event two statements are not separated by a semi-colon, and the 1586 | implied multiplication feature is active (enabled by default), the 1587 | compiler will assume a multiplication operation between the two 1588 | statements. 1589 | 1590 | In the following example we have a multi-statement expression composed 1591 | of two variable definitions and initialisations for variables x and y 1592 | and two seemingly separate mathematical operations. 1593 | 1594 | var x:= 2; 1595 | var y:= 3; 1596 | x + 1 1597 | y * 2 1598 | 1599 | 1600 | However the result of the expression will not be 6 as may have been 1601 | assumed based on the calculation of 'y * 2', but rather the result 1602 | will be 8. This is because the compiler will have conjoined the two 1603 | mathematical statements into one via a multiplication operation. The 1604 | expression when compiled will actually evaluate as the following: 1605 | 1606 | var x:= 2; 1607 | var y:= 3; 1608 | x + 1 * y * 2; // 2 + 1 * 3 * 2 == 8 1609 | 1610 | 1611 | In ExprTk any valid statement will itself return a value. This value 1612 | can further be used in conjunction with other statements. This 1613 | includes language structures such as if-statements, loops (for, while) 1614 | and the switch statement. Typically the last statement executed in the 1615 | given construct (conditional, loop etc), will be the value that is 1616 | returned. 1617 | 1618 | In the following example, the return value of the expression will be 1619 | 11, which is the sum of the variable 'x' and the final value computed 1620 | within the loop body upon its last iteration: 1621 | 1622 | var x := 1; 1623 | x + for (var i := x; i < 10; i += 1) 1624 | { 1625 | i / 2; 1626 | i + 1; 1627 | } 1628 | 1629 | 1630 | (2) Statements And Side-Effects 1631 | Statements themselves may have side effects, which in-turn affects the 1632 | proceeding statements in multi-statement expressions. 1633 | 1634 | A statement is said to have a side-effect if it causes the state of 1635 | the expression to change in some way - this includes but is not 1636 | limited to the modification of the state of external variables used 1637 | within the expression. Currently the following actions being present 1638 | in a statement will cause it to have a side-effect: 1639 | 1640 | (a) Assignment operation (explicit or potentially) 1641 | (b) Invoking a user-defined function that has side-effects 1642 | 1643 | The following are examples of expressions where the side-effect status 1644 | of the statements (sub-expressions) within the expressions have been 1645 | noted: 1646 | 1647 | +-+----------------------+------------------------------+ 1648 | |#| Expression | Side Effect Status | 1649 | +-+----------------------+------------------------------+ 1650 | |0| x + y | False | 1651 | +-+----------------------+------------------------------+ 1652 | |1| z := x + y | True - Due to assignment | 1653 | +-+----------------------+------------------------------+ 1654 | |2| abs(x - y) | False | 1655 | +-+----------------------+------------------------------+ 1656 | |3| abs(x - y); | False | 1657 | | | z := (x += y); | True - Due to assignments | 1658 | +-+----------------------+------------------------------+ 1659 | |4| var t := abs(x - y); | True - Due to initialisation | 1660 | | | t + x; | False | 1661 | | | z := (x += y); | True - Due to assignments | 1662 | +-+----------------------+------------------------------+ 1663 | |5| foo(x - y) | True - user defined function | 1664 | +-+----------------------+------------------------------+ 1665 | 1666 | 1667 | Note09: In example 5 from the above set, it is assumed the user 1668 | defined function foo has been registered as having a side-effect. By 1669 | default all user defined functions are assumed to have side-effects, 1670 | unless they are configured in their constructors to not have side- 1671 | effects using the 'disable_has_side_effects' free function. For more 1672 | information review Section 15 - User Defined Functions sub-section 7 1673 | Function Side-Effects. 1674 | 1675 | At this point we can see that there will be expressions composed of 1676 | certain kinds of statements that when executed will not affect the 1677 | nature of the expression's result. These statements are typically 1678 | called 'dead code'. These statements though not influencing the final 1679 | result will still be executed and as such they will consume processing 1680 | time that could otherwise be saved. Consequently ExprTk attempts to 1681 | detect and remove such statements from expressions. 1682 | 1683 | The 'Dead Code Elimination' (DCE) optimisation process, which is 1684 | enabled by default, will remove any statements that are determined to 1685 | not have a side-effect in a multi-statement expression, excluding the 1686 | final or last statement. 1687 | 1688 | By default the final statement in an expression will always be present 1689 | regardless of its side-effect status, as it is the statement whose 1690 | value shall be used as the result of the expression. 1691 | 1692 | In order to further explain the actions taken during the DCE process, 1693 | lets review the following expression: 1694 | 1695 | var x := 2; // Statement 1 1696 | var y := x + 2; // Statement 2 1697 | x + y; // Statement 3 1698 | y := x + 3y; // Statement 4 1699 | x - y; // Statement 5 1700 | 1701 | 1702 | The above expression has five statements. Three of them (1, 2 and 4) 1703 | actively have side-effects. The first two are variable declaration and 1704 | initialisations, where as the third is due to an assignment operation. 1705 | There are two statements (3 and 5), that do not explicitly have 1706 | side-effects, however the latter, statement 5, is the final statement 1707 | in the expression and hence will be assumed to have a side-effect. 1708 | 1709 | During compilation when the DCE optimisation is applied to the above 1710 | expression, statement 3 will be removed from the expression, as it has 1711 | no bearing on the final result of expression, the rest of the 1712 | statements will all remain. The optimised form of the expression is as 1713 | follows: 1714 | 1715 | var x := 2; // Statement 1 1716 | var y := x + 2; // Statement 2 1717 | y := x + 3y; // Statement 3 1718 | x - y; // Statement 4 1719 | 1720 | 1721 | (3) Conditional Statements (If-Then-Else) 1722 | ExprTk supports two forms of conditional branching or otherwise known 1723 | as if-statements. The first form, is a simple function based 1724 | conditional statement, that takes exactly three input expressions: 1725 | condition, consequent and alternative. The following is an example 1726 | expression that utilises the function based if-statement. 1727 | 1728 | x := if (y < z, y + 1, 2 * z) 1729 | 1730 | 1731 | In the example above, if the condition 'y < z' is true, then the 1732 | consequent 'y + 1' will be evaluated, its value shall be returned and 1733 | subsequently assigned to the variable 'x'. Otherwise the alternative 1734 | '2 * z' will be evaluated and its value will be returned. This is 1735 | essentially the simplest form of an if-then-else statement. A simple 1736 | variation of the expression where the value of the if-statement is 1737 | used within another statement is as follows: 1738 | 1739 | x := 3 * if (y < z, y + 1, 2 * z) / 2 1740 | 1741 | 1742 | The second form of if-statement resembles the standard syntax found in 1743 | most imperative languages. There are two variations of the statement: 1744 | 1745 | (a) If-Statement 1746 | (b) If-Then-Else Statement 1747 | 1748 | 1749 | (a) If-Statement 1750 | This version of the conditional statement returns the value of the 1751 | consequent expression when the condition expression is true, else it 1752 | will return a quiet NaN value as its result. 1753 | 1754 | Example 1: 1755 | x := if (y < z) y + 3; 1756 | 1757 | Example 2: 1758 | x := if (y < z) 1759 | { 1760 | y + 3 1761 | }; 1762 | 1763 | The two example expressions above are equivalent. If the condition 1764 | 'y < z' is true, the 'x' variable shall be assigned the value of the 1765 | consequent 'y + 3', otherwise it will be assigned the value of quiet 1766 | NaN. As previously discussed, if-statements are value returning 1767 | constructs, and if not properly terminated using a semi-colon, will 1768 | end-up combining with the next statement via a multiplication 1769 | operation. The following example will NOT result in the expected value 1770 | of 'w + x' being returned: 1771 | 1772 | x := if (y < z) y + 3 // missing semi-colon ';' 1773 | w + x 1774 | 1775 | 1776 | When the above supposed multi-statement expression is compiled, the 1777 | expression will have a multiplication inserted between the two 1778 | 'intended' statements resulting in the unanticipated expression: 1779 | 1780 | x := (if (y < z) y + 3) * w + x 1781 | 1782 | 1783 | The solution to the above situation is to simply terminate the 1784 | conditional statement with a semi-colon as follows: 1785 | 1786 | x := if (y < z) y + 3; 1787 | w + x 1788 | 1789 | 1790 | (b) If-Then-Else Statement 1791 | The second variation of the if-statement is to allow for the use of 1792 | Else and Else-If cascading statements. Examples of such statements are 1793 | as follows: 1794 | 1795 | Example 1: Example 2: Example 3: 1796 | if (x < y) if (x < y) if (x > y + 1) 1797 | z := x + 3; { y := abs(x - z); 1798 | else y := z + x; else 1799 | y := x - z; z := x + 3; { 1800 | } y := z + x; 1801 | else z := x + 3; 1802 | y := x - z; }; 1803 | 1804 | 1805 | Example 4: Example 5: Example 6: 1806 | if (2 * x < max(y,3)) if (x < y) if (x < y or (x + z) > y) 1807 | { z := x + 3; { 1808 | y := z + x; else if (2y != z) z := x + 3; 1809 | z := x + 3; { y := x - z; 1810 | } z := x + 3; } 1811 | else if (2y - z) y := x - z; else if (abs(2y - z) >= 3) 1812 | y := x - z; } y := x - z; 1813 | else else 1814 | x * x; { 1815 | z := abs(x * x); 1816 | x * y * z; 1817 | }; 1818 | 1819 | 1820 | In the case where there is no final else statement and the flow 1821 | through the conditional arrives at this final point, the same rules 1822 | apply to this form of if-statement as to the previous. That is a quiet 1823 | NaN shall be returned as the result of the if-statement. Furthermore 1824 | the same requirements of terminating the statement with a semi-colon 1825 | apply. 1826 | 1827 | (4) Special Functions 1828 | The purpose of special functions in ExprTk is to provide compiler 1829 | generated equivalents of common mathematical expressions which can be 1830 | invoked by using the 'special function' syntax (eg: $f12(x,y,z) or 1831 | $f82(x,y,z,w)). 1832 | 1833 | Special functions dramatically decrease the total evaluation time of 1834 | expressions which would otherwise have been written using the common 1835 | form by reducing the total number of nodes in the evaluation tree of 1836 | an expression and by also leveraging the compiler's ability to 1837 | correctly optimise such expressions for a given architecture. 1838 | 1839 | 3-Parameter 4-Parameter 1840 | +-------------+-------------+ +--------------+------------------+ 1841 | | Prototype | Operation | | Prototype | Operation | 1842 | +-------------+-------------+ +--------------+------------------+ 1843 | $f00(x,y,z) | (x + y) / z $f48(x,y,z,w) | x + ((y + z) / w) 1844 | $f01(x,y,z) | (x + y) * z $f49(x,y,z,w) | x + ((y + z) * w) 1845 | $f02(x,y,z) | (x + y) - z $f50(x,y,z,w) | x + ((y - z) / w) 1846 | $f03(x,y,z) | (x + y) + z $f51(x,y,z,w) | x + ((y - z) * w) 1847 | $f04(x,y,z) | (x - y) + z $f52(x,y,z,w) | x + ((y * z) / w) 1848 | $f05(x,y,z) | (x - y) / z $f53(x,y,z,w) | x + ((y * z) * w) 1849 | $f06(x,y,z) | (x - y) * z $f54(x,y,z,w) | x + ((y / z) + w) 1850 | $f07(x,y,z) | (x * y) + z $f55(x,y,z,w) | x + ((y / z) / w) 1851 | $f08(x,y,z) | (x * y) - z $f56(x,y,z,w) | x + ((y / z) * w) 1852 | $f09(x,y,z) | (x * y) / z $f57(x,y,z,w) | x - ((y + z) / w) 1853 | $f10(x,y,z) | (x * y) * z $f58(x,y,z,w) | x - ((y + z) * w) 1854 | $f11(x,y,z) | (x / y) + z $f59(x,y,z,w) | x - ((y - z) / w) 1855 | $f12(x,y,z) | (x / y) - z $f60(x,y,z,w) | x - ((y - z) * w) 1856 | $f13(x,y,z) | (x / y) / z $f61(x,y,z,w) | x - ((y * z) / w) 1857 | $f14(x,y,z) | (x / y) * z $f62(x,y,z,w) | x - ((y * z) * w) 1858 | $f15(x,y,z) | x / (y + z) $f63(x,y,z,w) | x - ((y / z) / w) 1859 | $f16(x,y,z) | x / (y - z) $f64(x,y,z,w) | x - ((y / z) * w) 1860 | $f17(x,y,z) | x / (y * z) $f65(x,y,z,w) | ((x + y) * z) - w 1861 | $f18(x,y,z) | x / (y / z) $f66(x,y,z,w) | ((x - y) * z) - w 1862 | $f19(x,y,z) | x * (y + z) $f67(x,y,z,w) | ((x * y) * z) - w 1863 | $f20(x,y,z) | x * (y - z) $f68(x,y,z,w) | ((x / y) * z) - w 1864 | $f21(x,y,z) | x * (y * z) $f69(x,y,z,w) | ((x + y) / z) - w 1865 | $f22(x,y,z) | x * (y / z) $f70(x,y,z,w) | ((x - y) / z) - w 1866 | $f23(x,y,z) | x - (y + z) $f71(x,y,z,w) | ((x * y) / z) - w 1867 | $f24(x,y,z) | x - (y - z) $f72(x,y,z,w) | ((x / y) / z) - w 1868 | $f25(x,y,z) | x - (y / z) $f73(x,y,z,w) | (x * y) + (z * w) 1869 | $f26(x,y,z) | x - (y * z) $f74(x,y,z,w) | (x * y) - (z * w) 1870 | $f27(x,y,z) | x + (y * z) $f75(x,y,z,w) | (x * y) + (z / w) 1871 | $f28(x,y,z) | x + (y / z) $f76(x,y,z,w) | (x * y) - (z / w) 1872 | $f29(x,y,z) | x + (y + z) $f77(x,y,z,w) | (x / y) + (z / w) 1873 | $f30(x,y,z) | x + (y - z) $f78(x,y,z,w) | (x / y) - (z / w) 1874 | $f31(x,y,z) | x * y^2 + z $f79(x,y,z,w) | (x / y) - (z * w) 1875 | $f32(x,y,z) | x * y^3 + z $f80(x,y,z,w) | x / (y + (z * w)) 1876 | $f33(x,y,z) | x * y^4 + z $f81(x,y,z,w) | x / (y - (z * w)) 1877 | $f34(x,y,z) | x * y^5 + z $f82(x,y,z,w) | x * (y + (z * w)) 1878 | $f35(x,y,z) | x * y^6 + z $f83(x,y,z,w) | x * (y - (z * w)) 1879 | $f36(x,y,z) | x * y^7 + z $f84(x,y,z,w) | x*y^2 + z*w^2 1880 | $f37(x,y,z) | x * y^8 + z $f85(x,y,z,w) | x*y^3 + z*w^3 1881 | $f38(x,y,z) | x * y^9 + z $f86(x,y,z,w) | x*y^4 + z*w^4 1882 | $f39(x,y,z) | x * log(y)+z $f87(x,y,z,w) | x*y^5 + z*w^5 1883 | $f40(x,y,z) | x * log(y)-z $f88(x,y,z,w) | x*y^6 + z*w^6 1884 | $f41(x,y,z) | x * log10(y)+z $f89(x,y,z,w) | x*y^7 + z*w^7 1885 | $f42(x,y,z) | x * log10(y)-z $f90(x,y,z,w) | x*y^8 + z*w^8 1886 | $f43(x,y,z) | x * sin(y)+z $f91(x,y,z,w) | x*y^9 + z*w^9 1887 | $f44(x,y,z) | x * sin(y)-z $f92(x,y,z,w) | (x and y) ? z : w 1888 | $f45(x,y,z) | x * cos(y)+z $f93(x,y,z,w) | (x or y) ? z : w 1889 | $f46(x,y,z) | x * cos(y)-z $f94(x,y,z,w) | (x < y) ? z : w 1890 | $f47(x,y,z) | x ? y : z $f95(x,y,z,w) | (x <= y) ? z : w 1891 | $f96(x,y,z,w) | (x > y) ? z : w 1892 | $f97(x,y,z,w) | (x >= y) ? z : w 1893 | $f98(x,y,z,w) | (x == y) ? z : w 1894 | $f99(x,y,z,w) | x*sin(y)+z*cos(w) 1895 | 1896 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1897 | 1898 | [SECTION 13 - VARIABLE, VECTOR & STRING DEFINITION] 1899 | ExprTk supports the definition of expression local variables, vectors 1900 | and strings. The definitions must be unique as shadowing is not 1901 | allowed and object lifetimes are based on scope. Definitions use the 1902 | following general form: 1903 | 1904 | var <name> := <initialiser>; 1905 | 1906 | (1) Variable Definition 1907 | Variables are of numeric type denoting a single value. They can be 1908 | explicitly initialised to a value, otherwise they will be defaulted to 1909 | zero. The following are examples of variable definitions: 1910 | 1911 | (a) Initialise x to zero 1912 | var x; 1913 | 1914 | (b) Initialise y to three 1915 | var y := 3; 1916 | 1917 | (c) Initialise z to the expression 1918 | var z := if (max(1, x + y) > 2, w, v); 1919 | 1920 | (d) Initialise const literal n 1921 | var n := 12 / 3; 1922 | 1923 | 1924 | (2) Vector Definition 1925 | Vectors are arrays of a common numeric type. The elements in a vector 1926 | can be explicitly initialised, otherwise they will all be defaulted to 1927 | zero. The following are examples of vector definitions: 1928 | 1929 | (a) Initialise all values to zero 1930 | var x[3]; 1931 | 1932 | (b) Initialise all values to zero 1933 | var x[3] := {}; 1934 | 1935 | (c) Initialise all values to given value or expression 1936 | var x[3] := [ 42 ]; 1937 | var y[x[]] := [ 123 + 3y + sin(w / z) ]; 1938 | 1939 | (d) Initialise all values iota style 1940 | var v[4] := [ 0 : +1]; // 0, 1, 2, 3 1941 | var v[5] := [-3 : -2]; // -3, -5, -7, -9, -11 1942 | 1943 | (e) Initialise the first two values, all other elements to zero 1944 | var x[3] := { (1 + x[2]) / x[], (sin(y[0] / x[]) + 3) / x[] }; 1945 | 1946 | (f) Initialise the first three (all) values 1947 | const var size := 3; 1948 | var x[size] := { 1, 2, 3 }; 1949 | 1950 | (g) Initialise vector from a vector 1951 | var x[4] := { 1, 2, 3, 4 }; 1952 | var y[3] := x; 1953 | var w[5] := { 1, 2 }; // 1, 2, 0, 0, 0 1954 | 1955 | (h) Initialise vector from a smaller vector 1956 | var x[3] := { 1, 2, 3 }; 1957 | var y[5] := x; // 1, 2, 3, ??, ?? 1958 | 1959 | (i) Non-initialised vector 1960 | var x[3] := null; // ?? ?? ?? 1961 | 1962 | (j) Error as there are too many initialisers 1963 | var x[3] := { 1, 2, 3, 4 }; 1964 | 1965 | (k) Error as a vector of size zero is not allowed. 1966 | var x[0]; 1967 | 1968 | 1969 | (3) String Definition 1970 | Strings are sequences comprised of 8-bit characters. They can only be 1971 | defined with an explicit initialisation value. The following are 1972 | examples of string variable definitions: 1973 | 1974 | (a) Initialise to a string 1975 | var x := 'abc'; 1976 | 1977 | (b) Initialise to an empty string 1978 | var x := ''; 1979 | 1980 | (c) Initialise to a string expression 1981 | var x := 'abc' + '123'; 1982 | 1983 | (d) Initialise to a string range 1984 | var x := 'abc123'[2:4]; 1985 | 1986 | (e) Initialise to another string variable 1987 | var x := 'abc'; 1988 | var y := x; 1989 | 1990 | (f) Initialise to another string variable range 1991 | var x := 'abc123'; 1992 | var y := x[2:4]; 1993 | 1994 | (g) Initialise to a string expression 1995 | var x := 'abc'; 1996 | var y := x + '123'; 1997 | 1998 | (h) Initialise to a string expression range 1999 | var x := 'abc'; 2000 | var y := (x + '123')[1:3]; 2001 | 2002 | 2003 | (4) Return Value 2004 | Variable and vector definitions have a return value. In the case of 2005 | variable definitions, the value to which the variable is initialised 2006 | will be returned. Where as for vectors, the value of the first element 2007 | (eg: v[0]) shall be returned. 2008 | 2009 | 8 == ((var x := 7;) + 1) 2010 | 4 == (var y[3] := {4, 5, 6};) 2011 | 2012 | 2013 | (5) Variable/Vector Assignment 2014 | The value of a variable can be assigned to a vector and a vector or a 2015 | vector expression can be assigned to a variable. 2016 | 2017 | (a) Variable To Vector: 2018 | Every element of the vector is assigned the value of the variable 2019 | or expression. 2020 | var x := 3; 2021 | var y[3] := { 1, 2, 3 }; 2022 | y := x + 1; 2023 | 2024 | (b) Vector To Variable: 2025 | The variable is assigned the value of the first element of the 2026 | vector (aka vec[0]) 2027 | var x := 3; 2028 | var y[3] := { 1, 2, 3 }; 2029 | x := y + 1; 2030 | 2031 | 2032 | Note10: During the expression compilation phase, tokens are classified 2033 | based on the following priorities: 2034 | 2035 | (a) Reserved keywords or operators (+, -, and, or, etc) 2036 | (b) Base functions (abs, sin, cos, min, max etc) 2037 | (c) Symbol table variables 2038 | (d) Expression local defined variables 2039 | (e) Symbol table functions 2040 | (f) Unknown symbol resolver based variables 2041 | 2042 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2043 | 2044 | [SECTION 14 - VECTOR PROCESSING] 2045 | ExprTk provides support for various forms of vector oriented 2046 | arithmetic, inequalities and processing. The various supported pairs 2047 | are as follows: 2048 | 2049 | (a) vector and vector (eg: v0 + v1) 2050 | (b) vector and scalar (eg: v + 33) 2051 | (c) scalar and vector (eg: 22 * v) 2052 | 2053 | The following is a list of operations that can be used in conjunction 2054 | with vectors: 2055 | 2056 | (a) Arithmetic: +, -, *, /, % 2057 | (b) Exponentiation: vector ^ scalar 2058 | (c) Assignment: :=, +=, -=, *=, /=, %=, <=> 2059 | (d) Inequalities: <, <=, >, >=, ==, =, equal 2060 | (e) Boolean logic: and, nand, nor, or, xnor, xor 2061 | (f) Unary operations: 2062 | abs, acos, acosh, asin, asinh, atan, atanh, ceil, cos, cosh, 2063 | cot, csc, deg2grad, deg2rad, erf, erfc, exp, expm1, floor, 2064 | frac, grad2deg, log, log10, log1p, log2, rad2deg, round, sec, 2065 | sgn, sin, sinc, sinh, sqrt, swap, tan, tanh, trunc, 2066 | thresholding 2067 | (g) Aggregate and Reduce operations: 2068 | avg, max, min, mul, dot, dotk, sum, sumk, count, all_true, 2069 | all_false, any_true, any_false 2070 | (h) Transformation operations: 2071 | copy, diff, reverse, rotate-left/right, shift-left/right, sort, 2072 | nth_element 2073 | (i) BLAS-L1: 2074 | axpy, axpby, axpyz, axpbyz, axpbz 2075 | 2076 | Note11: When one of the above described operations is being performed 2077 | between two vectors, the operation will only span the size of the 2078 | smallest vector. The elements of the larger vector outside of the 2079 | range will not be included. The operation itself will be processed 2080 | element-wise over values of the smaller of the two ranges. 2081 | 2082 | The following simple example demonstrates the vector processing 2083 | capabilities by computing the dot-product of the vectors v0 and v1 and 2084 | then assigning it to the variable v0dotv1: 2085 | 2086 | var v0[3] := { 1, 2, 3 }; 2087 | var v1[3] := { 4, 5, 6 }; 2088 | var v0dotv1 := sum(v0 * v1); 2089 | 2090 | 2091 | The following is a for-loop based implementation that is equivalent to 2092 | the previously mentioned dot-product computation expression: 2093 | 2094 | var v0[3] := { 1, 2, 3 }; 2095 | var v1[3] := { 4, 5, 6 }; 2096 | var v0dotv1; 2097 | 2098 | for (var i := 0; i < min(v0[],v1[]); i += 1) 2099 | { 2100 | v0dotv1 += (v0[i] * v1[i]); 2101 | } 2102 | 2103 | 2104 | Note12: When the aggregate or reduction operations denoted above are 2105 | used in conjunction with a vector or vector expression, the return 2106 | value is not a vector but rather a single value. 2107 | 2108 | var x[3] := { 1, 2, 3 }; 2109 | 2110 | sum(x) == 6 2111 | sum(1 + 2x) == 15 2112 | avg(3x + 1) == 7 2113 | min(1 / x) == (1 / 3) 2114 | max(x / 2) == (3 / 2) 2115 | sum(x > 0 and x < 5) == x[] 2116 | 2117 | 2118 | When utilising external user defined vectors via the symbol table as 2119 | opposed to expression local defined vectors, the typical 'add_vector' 2120 | method from the symbol table will register the entirety of the vector 2121 | that is passed. The following example attempts to evaluate the sum of 2122 | elements of the external user defined vector within a typical yet 2123 | trivial expression: 2124 | 2125 | const std::string reduce_program = " sum(2 * v + 1) " 2126 | 2127 | std::vector<T> v0 { T(1.1), T(2.2), ..... , T(99.99) }; 2128 | 2129 | symbol_table_t symbol_table; 2130 | symbol_table.add_vector("v",v); 2131 | 2132 | expression_t expression; 2133 | expression.register_symbol_table(symbol_table); 2134 | 2135 | parser_t parser; 2136 | parser.compile(reduce_program,expression); 2137 | 2138 | T sum = expression.value(); 2139 | 2140 | 2141 | For the most part, this is a very common use-case. However there may 2142 | be situations where one may want to evaluate the same vector oriented 2143 | expression many times over, but using different vectors or sub ranges 2144 | of the same vector of the same size to that of the original upon every 2145 | evaluation. 2146 | 2147 | The usual solution is to either recompile the expression for the new 2148 | vector instance, or to copy the contents from the new vector to the 2149 | symbol table registered vector and then perform the evaluation. When 2150 | the vectors are large or the re-evaluation attempts are numerous, 2151 | these solutions can become rather time consuming and generally 2152 | inefficient. 2153 | 2154 | std::vector<T> v1 { T(2.2), T(2.2), ..... , T(2.2) }; 2155 | std::vector<T> v2 { T(3.3), T(3.3), ..... , T(3.3) }; 2156 | std::vector<T> v3 { T(4.4), T(4.4), ..... , T(4.4) }; 2157 | 2158 | std::vector<std::vector<T>> vv { v1, v2, v3 }; 2159 | ... 2160 | T sum = T(0); 2161 | 2162 | for (auto& new_vec : vv) 2163 | { 2164 | v = new_vec; // update vector 2165 | sum += expression.value(); 2166 | } 2167 | 2168 | 2169 | A solution to the above 'efficiency' problem, is to use the 2170 | exprtk::vector_view object. The vector_view is instantiated with a 2171 | size and backing based upon a vector. Upon evaluations if the backing 2172 | needs to be 'updated' to either another vector or sub-range, the 2173 | vector_view instance can be efficiently rebased, and the expression 2174 | evaluated as normal. 2175 | 2176 | exprtk::vector_view<T> view = exprtk::make_vector_view(v,v.size()); 2177 | 2178 | symbol_table_t symbol_table; 2179 | symbol_table.add_vector("v",view); 2180 | 2181 | ... 2182 | 2183 | T sum = T(0); 2184 | 2185 | for (auto& new_vec : vv) 2186 | { 2187 | view.rebase(new_vec.data()); // update vector 2188 | sum += expression.value(); 2189 | } 2190 | 2191 | 2192 | Another useful feature of exprtk::vector_view is that all such vectors 2193 | can have their sizes modified (or "resized"). The resizing of the 2194 | associated vectors can happen either between or during evaluations. 2195 | 2196 | std::vector<T> v = { 1, 2, 3, 4, 5, 6, 7, 8 }; 2197 | exprtk::vector_view<T> view = exprtk::make_vector_view(v,v.size()); 2198 | 2199 | symbol_table_t symbol_table; 2200 | symbol_table.add_vector("v",view); 2201 | 2202 | const std::string expression_string = "v[]" 2203 | 2204 | expression_t expression; 2205 | expression.register_symbol_table(symbol_table); 2206 | 2207 | parser_t parser; 2208 | parser.compile(expression_string, expression); 2209 | 2210 | for (std::size_t i = 1; i <= v.size(); ++i) 2211 | { 2212 | vv.set_size(i); 2213 | expression.value(); 2214 | } 2215 | 2216 | 2217 | In the example above, a vector_view is instantiated with a std::vector 2218 | instance with eight elements and registered to the given symbol_table. 2219 | An expression is then compiled, which in this case simply returns the 2220 | size of the vector at that point in time. The expression is evaluated 2221 | eight times (size of vector times), where upon each iteration the size 2222 | of the vector is changed with values ranging from one to eight. 2223 | 2224 | Note13: When modifying the size of a vector, the new size must be at 2225 | least one or larger and must not exceed the original size of the 2226 | vector_view when it was instantiated. 2227 | 2228 | Note14: The lifetime of any parser, symbol_table or expression 2229 | instance must not exceed that of any vector_view instance that has 2230 | been registered with it. Furthermore the lifetime of a vector_view 2231 | must not exceed that of the underlying vector instance it is 2232 | associated with. 2233 | 2234 | Note15: In a multi-threaded context the rebase function should not be 2235 | called during associated expression evaluation, as this will lead to 2236 | undefined behaviour (eg: torn reads and writes). 2237 | 2238 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2239 | 2240 | [SECTION 15 - USER DEFINED FUNCTIONS] 2241 | ExprTk provides a means whereby custom functions can be defined and 2242 | utilised within expressions. The concept requires the user to 2243 | provide a reference to the function coupled with an associated name 2244 | that will be invoked within expressions. Functions may take numerous 2245 | inputs but will always return a single value of the underlying numeric 2246 | type. 2247 | 2248 | During expression compilation when required the reference to the 2249 | function shall be obtained from the associated symbol_table and be 2250 | embedded into the expression. 2251 | 2252 | There are six types of function interface: 2253 | 2254 | +---+----------------------+--------------+----------------------+ 2255 | | # | Name | Return Type | Input Types | 2256 | +---+----------------------+--------------+----------------------+ 2257 | | 1 | ifunction | Scalar | Scalar | 2258 | | 2 | ivararg_function | Scalar | Scalar | 2259 | | 3 | igeneric_function | Scalar | Scalar,Vector,String | 2260 | | 4 | igeneric_function II | String | Scalar,Vector,String | 2261 | | 5 | igeneric_function III| String/Scalar| Scalar,Vector,String | 2262 | | 6 | function_compositor | Scalar | Scalar | 2263 | +---+----------------------+--------------+----------------------+ 2264 | 2265 | (1) ifunction 2266 | This interface supports zero to 20 input parameters of only the scalar 2267 | type (numbers). The usage requires a custom function be derived from 2268 | ifunction and to override one of the 21 function operators. As part of 2269 | the constructor the custom function will define how many parameters it 2270 | expects to handle. The following example defines a 3 parameter 2271 | function called 'foo': 2272 | 2273 | template <typename T> 2274 | struct foo final : public exprtk::ifunction<T> 2275 | { 2276 | foo() : exprtk::ifunction<T>(3) 2277 | {} 2278 | 2279 | T operator()(const T& v1, const T& v2, const T& v3) override 2280 | { 2281 | return T(1) + (v1 * v2) / T(v3); 2282 | } 2283 | }; 2284 | 2285 | 2286 | (2) ivararg_function 2287 | This interface supports a variable number of scalar arguments as input 2288 | into the function. The function operator interface uses a std::vector 2289 | specialised upon type T to facilitate parameter passing. The following 2290 | example defines a vararg function called 'boo': 2291 | 2292 | template <typename T> 2293 | struct boo final : public exprtk::ivararg_function<T> 2294 | { 2295 | inline T operator()(const std::vector<T>& arglist) override 2296 | { 2297 | T result = T(0); 2298 | 2299 | for (std::size_t i = 0; i < arglist.size(); ++i) 2300 | { 2301 | result += arglist[i] / arglist[i > 0 ? (i - 1) : 0]; 2302 | } 2303 | 2304 | return result; 2305 | } 2306 | }; 2307 | 2308 | 2309 | (3) igeneric_function 2310 | This interface supports a variable number of arguments and types as 2311 | input into the function. The function operator interface uses a 2312 | std::vector specialised upon the type_store type to facilitate 2313 | parameter passing. 2314 | 2315 | Scalar <-- function(i_0, i_1, i_2....., i_N) 2316 | 2317 | 2318 | The fundamental types that can be passed into the function as 2319 | parameters and their views are as follows: 2320 | 2321 | (1) Scalar - scalar_view 2322 | (2) Vector - vector_view 2323 | (3) String - string_view 2324 | 2325 | 2326 | The above denoted type views provide non-const reference-like access 2327 | to each parameter, as such modifications made to the input parameters 2328 | will persist after the function call has completed. The following 2329 | example defines a generic function called 'too': 2330 | 2331 | template <typename T> 2332 | struct too final : public exprtk::igeneric_function<T> 2333 | { 2334 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2335 | parameter_list_t; 2336 | 2337 | too() 2338 | {} 2339 | 2340 | inline T operator()(parameter_list_t parameters) override 2341 | { 2342 | for (std::size_t i = 0; i < parameters.size(); ++i) 2343 | { 2344 | ... 2345 | } 2346 | 2347 | return T(0); 2348 | } 2349 | }; 2350 | 2351 | 2352 | In the example above, the input 'parameters' to the function operator, 2353 | parameter_list_t, is a type of std::vector of type_store. Each 2354 | type_store instance has a member called 'type' which holds the 2355 | enumeration pertaining to the underlying type of the type_store. There 2356 | are three type enumerations: 2357 | 2358 | (1) e_scalar - literals, variables, vector elements, expressions 2359 | eg: 123.456, x, vec[3x + 1], 2x + 3 2360 | 2361 | (2) e_vector - vectors, vector expressions 2362 | eg: vec1, 2 * vec1 + vec2 / 3 2363 | 2364 | (3) e_string - strings, string literals and range variants of both 2365 | eg: 'AString', s0, 'AString'[x:y], s1[1 + x:] + 'AString' 2366 | 2367 | 2368 | Each of the parameters can be accessed using its designated view. 2369 | A typical loop for processing the parameters is as follows: 2370 | 2371 | inline T operator()(parameter_list_t parameters) 2372 | { 2373 | typedef typename exprtk::igeneric_function<T>::generic_type 2374 | generic_type; 2375 | 2376 | typedef typename generic_type::scalar_view scalar_t; 2377 | typedef typename generic_type::vector_view vector_t; 2378 | typedef typename generic_type::string_view string_t; 2379 | 2380 | for (std::size_t i = 0; i < parameters.size(); ++i) 2381 | { 2382 | generic_type& gt = parameters[i]; 2383 | 2384 | if (generic_type::e_scalar == gt.type) 2385 | { 2386 | scalar_t x(gt); 2387 | ... 2388 | } 2389 | else if (generic_type::e_vector == gt.type) 2390 | { 2391 | vector_t vector(gt); 2392 | ... 2393 | } 2394 | else if (generic_type::e_string == gt.type) 2395 | { 2396 | string_t string(gt); 2397 | ... 2398 | } 2399 | } 2400 | 2401 | return T(0); 2402 | } 2403 | 2404 | 2405 | Most often than not a custom generic function will require a specific 2406 | sequence of parameters, rather than some arbitrary sequence of types. 2407 | In those situations, ExprTk can perform compile-time type checking to 2408 | validate that function invocations are carried out using the correct 2409 | sequence of parameters. Furthermore performing the checks at compile 2410 | -time rather than at run-time (aka every time the function is invoked) 2411 | will result in expression evaluation performance gains. 2412 | 2413 | Compile-time type checking of input parameters can be requested by 2414 | passing a string to the constructor of the igeneric_function that 2415 | represents the required sequence of parameter types. When no parameter 2416 | sequence is provided, it is implied the function can accept a variable 2417 | number of parameters comprised of any of the fundamental types. 2418 | 2419 | Each fundamental type has an associated character. The following is a 2420 | listing of said characters and their meanings: 2421 | 2422 | (1) T - Scalar 2423 | (2) V - Vector 2424 | (3) S - String 2425 | (4) Z - Zero or no parameters 2426 | (5) ? - Any type (Scalar, Vector or String) 2427 | (6) * - Wildcard operator 2428 | (7) | - Parameter sequence delimiter 2429 | 2430 | 2431 | No other characters other than the seven denoted above may be included 2432 | in the parameter sequence definition. If any such invalid characters 2433 | do exist, registration of the associated generic function to a symbol 2434 | table ('add_function' method) will fail. If the parameter sequence is 2435 | modified resulting in it becoming invalid after having been added to 2436 | the symbol table but before the compilation step, a compilation error 2437 | will be incurred. 2438 | 2439 | The following example demonstrates a simple generic function 2440 | implementation with a user specified parameter sequence: 2441 | 2442 | template <typename T> 2443 | struct moo final : public exprtk::igeneric_function<T> 2444 | { 2445 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2446 | parameter_list_t; 2447 | 2448 | moo() 2449 | : exprtk::igeneric_function<T>("SVTT") 2450 | {} 2451 | 2452 | inline T operator()(parameter_list_t parameters) override 2453 | { 2454 | ... 2455 | } 2456 | }; 2457 | 2458 | 2459 | In the example above the generic function 'moo' expects exactly four 2460 | parameters in the following sequence: 2461 | 2462 | (1) String 2463 | (2) Vector 2464 | (3) Scalar 2465 | (4) Scalar 2466 | 2467 | Note16: The 'Z' or no parameter option may not be used in conjunction 2468 | with any other type option in a parameter sequence. When incorporated 2469 | in the parameter sequence list, the 'No Parameter' option indicates 2470 | that the function may be invoked without any parameters being passed. 2471 | For more information refer to the section: 'Zero Parameter Functions' 2472 | 2473 | 2474 | (4) igeneric_function II 2475 | This interface is identical to the igeneric_function, in that in can 2476 | consume an arbitrary number of parameters of varying type, but the 2477 | difference being that the function returns a string and as such is 2478 | treated as a string when invoked within expressions. As a result the 2479 | function call can alias a string and interact with other strings in 2480 | situations such as concatenation and equality operations. 2481 | 2482 | String <-- function(i_0, i_1, i_2....., i_N) 2483 | 2484 | 2485 | The following example defines a generic function named 'toupper' with 2486 | the string return type function operator being explicitly overridden: 2487 | 2488 | template <typename T> 2489 | struct toupper final : public exprtk::igeneric_function<T> 2490 | { 2491 | typedef exprtk::igeneric_function<T> igenfunct_t; 2492 | typedef typename igenfunct_t::generic_type generic_t; 2493 | typedef typename igenfunct_t::parameter_list_t parameter_list_t; 2494 | typedef typename generic_t::string_view string_t; 2495 | 2496 | toupper() 2497 | : exprtk::igeneric_function<T>("S",igenfunct_t::e_rtrn_string) 2498 | {} 2499 | 2500 | inline T operator()(std::string& result, 2501 | parameter_list_t parameters) override 2502 | { 2503 | result.clear(); 2504 | 2505 | string_t string(parameters[0]); 2506 | 2507 | for (std::size_t i = 0; i < string.size(); ++i) 2508 | { 2509 | result += std::toupper(string[i]); 2510 | } 2511 | 2512 | return T(0); 2513 | } 2514 | }; 2515 | 2516 | 2517 | In the example above the generic function 'toupper' expects only one 2518 | input parameter of type string, as noted by the parameter sequence 2519 | string passed during the constructor. Furthermore a second parameter 2520 | is passed to the constructor indicating that it should be treated as a 2521 | string returning function - by default it is assumed to be a scalar 2522 | returning function. 2523 | 2524 | When executed, the function will return as a result a copy of the 2525 | input string converted to uppercase form. An example expression using 2526 | the toupper function registered as the symbol 'toupper' is as follows: 2527 | 2528 | "'ABCDEF' == toupper('aBc') + toupper('DeF')" 2529 | 2530 | 2531 | Note17: When adding a string type returning generic function to a 2532 | symbol table the 'add_function' is invoked. The example below 2533 | demonstrates how this can be done: 2534 | 2535 | toupper<T> tu; 2536 | 2537 | exprtk::symbol_table<T> symbol_table; 2538 | 2539 | symbol_table.add_function("toupper",tu); 2540 | 2541 | 2542 | Note18: Two further refinements to the type checking facility are the 2543 | possibilities of a variable number of common types which can be 2544 | accomplished by using a wildcard '*' and a special 'any type' which is 2545 | done using the '?' character. It should be noted that the wildcard 2546 | operator is associated with the previous type in the sequence and 2547 | implies one or more of that type. 2548 | 2549 | template <typename T> 2550 | struct zoo final : public exprtk::igeneric_function<T> 2551 | { 2552 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2553 | parameter_list_t; 2554 | 2555 | zoo() 2556 | : exprtk::igeneric_function<T>("SVT*V?") 2557 | {} 2558 | 2559 | inline T operator()(parameter_list_t parameters) override 2560 | { 2561 | ... 2562 | } 2563 | }; 2564 | 2565 | 2566 | In the example above the generic function 'zoo' expects at least five 2567 | parameters in the following sequence: 2568 | 2569 | (1) String 2570 | (2) Vector 2571 | (3) One or more Scalars 2572 | (4) Vector 2573 | (5) Any type (one type of either a scalar, vector or string) 2574 | 2575 | 2576 | A final piece of type checking functionality is available for the 2577 | scenarios where a single function name is intended to be used for 2578 | multiple distinct parameter sequences, another name for this feature 2579 | is function overloading. The parameter sequences are passed to the 2580 | constructor as a single string delimited by the pipe '|' character. 2581 | Two specific overrides of the function operator are provided one for 2582 | standard generic functions and one for string returning functions. The 2583 | overrides are as follows: 2584 | 2585 | // Scalar <-- function(psi,i_0,i_1,....,i_N) 2586 | inline T operator()(const std::size_t& ps_index, 2587 | parameter_list_t parameters) 2588 | { 2589 | ... 2590 | } 2591 | 2592 | // String <-- function(psi,i_0,i_1,....,i_N) 2593 | inline T operator()(const std::size_t& ps_index, 2594 | std::string& result, 2595 | parameter_list_t parameters) 2596 | { 2597 | ... 2598 | } 2599 | 2600 | 2601 | When the function operator is invoked the 'ps_index' parameter will 2602 | have as its value the index of the parameter sequence that matches the 2603 | specific invocation. This way complex and time consuming type checking 2604 | conditions need not be executed in the function itself but rather a 2605 | simple and efficient dispatch to a specific implementation for that 2606 | particular parameter sequence can be performed. 2607 | 2608 | template <typename T> 2609 | struct roo final : public exprtk::igeneric_function<T> 2610 | { 2611 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2612 | parameter_list_t; 2613 | 2614 | moo() 2615 | : exprtk::igeneric_function<T>("SVTT|SS|TTV|S?V*S") 2616 | {} 2617 | 2618 | inline T operator()(const std::size_t& ps_index, 2619 | parameter_list_t parameters) override 2620 | { 2621 | ... 2622 | } 2623 | }; 2624 | 2625 | 2626 | In the example above there are four distinct parameter sequences that 2627 | can be processed by the generic function 'roo'. Any other parameter 2628 | sequences will cause a compilation error. The four valid sequences are 2629 | as follows: 2630 | 2631 | Sequence-0 Sequence-1 Sequence-2 Sequence-3 2632 | 'SVTT' 'SS' 'TTV' 'S?V*S' 2633 | (1) String (1) String (1) Scalar (1) String 2634 | (2) Vector (2) String (2) Scalar (2) Any Type 2635 | (3) Scalar (3) Vector (3) One or more Vectors 2636 | (4) Scalar (4) String 2637 | 2638 | 2639 | (5) igeneric_function III 2640 | In this section we will discuss an extension of the igeneric_function 2641 | interface that will allow for the overloading of a user defined custom 2642 | function, where by it can return either a scalar or string value type 2643 | depending on the input parameter sequence with which the function is 2644 | invoked. 2645 | 2646 | template <typename T> 2647 | struct foo final : public exprtk::igeneric_function<T> 2648 | { 2649 | typedef typename exprtk::igeneric_function<T>::parameter_list_t 2650 | parameter_list_t; 2651 | 2652 | foo() 2653 | : exprtk::igeneric_function<T> 2654 | ( 2655 | "T:T|S:TS", 2656 | igfun_t::e_rtrn_overload 2657 | ) 2658 | {} 2659 | 2660 | // Scalar value returning invocations 2661 | inline T operator()(const std::size_t& ps_index, 2662 | parameter_list_t parameters) override 2663 | { 2664 | ... 2665 | } 2666 | 2667 | // String value returning invocations 2668 | inline T operator()(const std::size_t& ps_index, 2669 | std::string& result, 2670 | parameter_list_t& parameters) override 2671 | { 2672 | ... 2673 | } 2674 | }; 2675 | 2676 | 2677 | In the example above the custom user defined function "foo" can be 2678 | invoked by using either one of two input parameter sequences, which 2679 | are defined as follows: 2680 | 2681 | Sequence-0 Sequence-1 2682 | 'T' -> T 'TS' -> S 2683 | (1) Scalar (1) Scalar 2684 | (2) String 2685 | 2686 | 2687 | The parameter sequence definitions are identical to the previously 2688 | defined igeneric_function, with the exception of the inclusion of the 2689 | return type - which can only be either a scalar T or a string S. 2690 | 2691 | 2692 | (6) function_compositor 2693 | The function compositor is a factory that allows one to define and 2694 | construct a function using ExprTk syntax. The functions are limited to 2695 | returning a single scalar value and consuming up to six parameters as 2696 | input. 2697 | 2698 | All composited functions are registered with a symbol table, allowing 2699 | them to call other functions and use variables that have been 2700 | registered with the symbol table instance. Furthermore the functions 2701 | can be recursive in nature due to the inherent function prototype 2702 | forwarding that occurs during construction. The following example 2703 | defines, by using two different methods, composited functions and 2704 | implicitly registering the functions with the denoted symbol table. 2705 | 2706 | typedef exprtk::symbol_table<T> symbol_table_t; 2707 | typedef exprtk::function_compositor<T> compositor_t; 2708 | typedef typename compositor_t::function function_t; 2709 | 2710 | T avogadro = T(6.022e23); 2711 | 2712 | symbol_table_t symbol_table; 2713 | 2714 | symbol_table.add_constant("avogadro", avogadro); 2715 | 2716 | compositor_t compositor(symbol_table); 2717 | 2718 | // Define function koo0(v1, v2) { ... } 2719 | compositor.add( 2720 | function_t("koo0"), 2721 | .vars("v1", "v2") 2722 | .expression 2723 | ( 2724 | " 1 + cos(v1 * v2) / avogadro; " 2725 | )); 2726 | 2727 | // Define function koo1(x, y, z) { ... } 2728 | compositor.add( 2729 | function_t() 2730 | .name("koo1") 2731 | .var("x").var("y").var("z") 2732 | .expression 2733 | ( 2734 | "1 + koo0(x * y, 3) / z;" 2735 | )); 2736 | 2737 | 2738 | A function compositor can also be instantiated without a symbol_table. 2739 | When this is the case an internal symbol_table is used for holding the 2740 | references to the composited functions. 2741 | 2742 | compositor_t compositor; 2743 | 2744 | // Define function koo2(v1, v2) { ... } 2745 | compositor.add( 2746 | function_t("koo2"), 2747 | .vars("v1", "v2", "v3") 2748 | .expression 2749 | ( " abs(v1 * v2) / v3; " )); 2750 | 2751 | 2752 | When wanting to reference functions from the compositor above in an 2753 | expression, the compositor's symbol_table will need to be registered 2754 | with the expression prior to compilation, as is demonstrated in the 2755 | following code: 2756 | 2757 | expression_t expression; 2758 | . 2759 | . 2760 | expression.register_symbol_table(compositor.symbol_table()); 2761 | 2762 | 2763 | In the situation where more than one symbol table's contents will be 2764 | required by the functions being composited, then those symbol tables 2765 | can be registered as auxiliary symbol tables with the compositor: 2766 | 2767 | symbol_table_t global_symbol_table; 2768 | symbol_table_t local_symbol_table; 2769 | . 2770 | . 2771 | . 2772 | compositor_t compositor; 2773 | 2774 | compositor.add_auxiliary_symtab(global_symbol_table); 2775 | compositor.add_auxiliary_symtab(local_symbol_table ); 2776 | 2777 | Note19: In the event, that two or more symbol tables contain similarly 2778 | named variables, vectors, strings or functions, the order of 2779 | registration with the compositor shall determine the symbol table from 2780 | which the target symbol will be referenced. 2781 | 2782 | 2783 | (7) Using Functions In Expressions 2784 | For the above denoted custom and composited functions to be used in an 2785 | expression, an instance of each function needs to be registered with a 2786 | symbol_table that has been associated with the expression instance. 2787 | The following demonstrates how all the pieces are put together: 2788 | 2789 | typedef exprtk::symbol_table<double> symbol_table_t; 2790 | typedef exprtk::expression<double> expression_t; 2791 | typedef exprtk::parser<double> parser_t; 2792 | typedef exprtk::function_compositor<double> compositor_t; 2793 | typedef typename compositor_t::function function_t; 2794 | 2795 | foo<double> f; 2796 | boo<double> b; 2797 | too<double> t; 2798 | toupper<double> tu; 2799 | 2800 | symbol_table_t symbol_table; 2801 | compositor_t compositor(symbol_table); 2802 | 2803 | symbol_table.add_function("foo",f); 2804 | symbol_table.add_function("boo",b); 2805 | symbol_table.add_function("too",t); 2806 | 2807 | symbol_table 2808 | .add_function("toupper", tu, symbol_table_t::e_ft_strfunc); 2809 | 2810 | compositor.add( 2811 | function_t("koo") 2812 | .var("v1") 2813 | .var("v2") 2814 | .expression 2815 | ( 2816 | "1 + cos(v1 * v2) / 3;" 2817 | )); 2818 | 2819 | expression_t expression; 2820 | expression.register_symbol_table(symbol_table); 2821 | 2822 | const std::string expression_str = 2823 | " if (foo(1,2,3) + boo(1) > boo(1/2, 2/3, 3/4, 4/5)) " 2824 | " koo(3,4); " 2825 | " else " 2826 | " too(2 * v1 + v2 / 3, 'abcdef'[2:4], 3.3); " 2827 | " " 2828 | 2829 | parser_t parser; 2830 | parser.compile(expression_str,expression); 2831 | 2832 | expression.value(); 2833 | 2834 | 2835 | (8) Function Side-Effects 2836 | All function calls are assumed to have side-effects by default. This 2837 | assumption implicitly disables constant folding optimisations when all 2838 | parameters being passed to the function are deduced as being constants 2839 | at compile time. 2840 | 2841 | If it is certain that the function being registered does not have any 2842 | side-effects and can be correctly constant folded where appropriate, 2843 | then during the construction of the function the side-effect trait of 2844 | the function can be disabled. 2845 | 2846 | template <typename T> 2847 | struct foo final : public exprtk::ifunction<T> 2848 | { 2849 | foo() : exprtk::ifunction<T>(3) 2850 | { 2851 | exprtk::disable_has_side_effects(*this); 2852 | } 2853 | 2854 | T operator()(const T& v1, const T& v2, const T& v3) override 2855 | { ... } 2856 | }; 2857 | 2858 | 2859 | (9) Zero Parameter Functions 2860 | When either an ifunction, ivararg_function or igeneric_function 2861 | derived type is defined with zero number of parameters, there are two 2862 | calling conventions within expressions that are allowed. For a 2863 | function named 'foo' with zero input parameters the calling styles are 2864 | as follows: 2865 | 2866 | (1) x + sin(foo()- 2) / y 2867 | (2) x + sin(foo - 2) / y 2868 | 2869 | 2870 | By default the zero parameter trait is disabled. In order to enable 2871 | it, a process similar to that of enabling of the side-effect trait is 2872 | carried out: 2873 | 2874 | template <typename T> 2875 | struct foo final : public exprtk::ivararg_function<T> 2876 | { 2877 | foo() 2878 | { 2879 | exprtk::enable_zero_parameters(*this); 2880 | } 2881 | 2882 | inline T operator()(const std::vector<T>& arglist) override 2883 | { ... } 2884 | }; 2885 | 2886 | 2887 | Note20: For the igeneric_function type, there also needs to be a 'Z' 2888 | parameter sequence defined in order for the zero parameter trait to 2889 | properly take effect otherwise a compilation error will occur. 2890 | 2891 | 2892 | (10) Free Functions 2893 | The ExprTk symbol table supports the registration of free functions 2894 | and lambdas (anonymous functors) for use in expressions. The basic 2895 | requirements are similar to those found in ifunction derived user 2896 | defined functions. This includes support for free functions using 2897 | anywhere from zero up to fifteen input parameters of scalar type, with 2898 | a return type that is also scalar. Furthermore such functions will by 2899 | default be assumed to have side-effects and hence will not participate 2900 | in constant folding optimisations. 2901 | 2902 | In the following example, a one input parameter free function named 2903 | 'compute1', a two input parameter template free function named 2904 | 'compute2' and a three input parameter lambda named 'compute3' will be 2905 | registered with the given symbol_table instance: 2906 | 2907 | double compute1(double v0) 2908 | { 2909 | return 2.0 * std::abs(v0); 2910 | } 2911 | 2912 | template <typename T> 2913 | T compute2(T v0, T v1) 2914 | { 2915 | return 2.0 * v0 + v1 / 3.0; 2916 | } 2917 | . 2918 | . 2919 | . 2920 | 2921 | typedef exprtk::symbol_table<double> symbol_table_t; 2922 | 2923 | symbol_table_t symbol_table; 2924 | 2925 | symbol_table.add_function("compute1", compute1); 2926 | symbol_table.add_function("compute2", compute2<double>); 2927 | 2928 | symbol_table.add_function( 2929 | "compute3", 2930 | [](double v0, double v1, double v2) -> double 2931 | { return v0 / v1 + v2; }); 2932 | 2933 | 2934 | Note21: Similar to variables registered with symbol_table instances, 2935 | for any of the following function providers: 2936 | 2937 | 1. ifunction 2938 | 2. ivararg_function 2939 | 3. igeneric_function 2940 | 4. function_compositor 2941 | 5. Free function 2942 | 7. Lambda 2943 | 2944 | 2945 | Their instance lifetimes must exceed the symbol_tables and expressions 2946 | they are registered with. In the event that is not the case, the 2947 | expected result shall be undefined behaviour. 2948 | 2949 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2950 | 2951 | [SECTION 16 - EXPRESSION DEPENDENTS] 2952 | Any expression that is not a literal (aka constant) will have 2953 | dependencies. The types of 'dependencies' an expression can have are 2954 | as follows: 2955 | 2956 | (a) Variables 2957 | (b) Vectors 2958 | (c) Strings 2959 | (d) Functions 2960 | (e) Assignments 2961 | 2962 | 2963 | In the following example the denoted expression has its various 2964 | dependencies listed: 2965 | 2966 | z := abs(x + sin(2 * pi / y)) 2967 | 2968 | (a) Variables: x, y, z and pi 2969 | (b) Functions: abs, sin 2970 | (c) Assignments: z 2971 | 2972 | 2973 | ExprTk allows for the derivation of expression dependencies via the 2974 | 'dependent_entity_collector' (DEC). When activated either through 2975 | 'compile_options' at the construction of the parser or through calls 2976 | to enabler methods just prior to compilation, the DEC will proceed to 2977 | collect any of the relevant types that are encountered during the 2978 | parsing phase. Once the compilation process has successfully 2979 | completed, the caller can then obtain a list of symbols and their 2980 | associated types from the DEC. 2981 | 2982 | The kinds of questions one can ask regarding the dependent entities 2983 | within an expression are as follows: 2984 | 2985 | * What user defined variables, vectors or strings are used? 2986 | * What functions or custom user functions are used? 2987 | * Which variables, vectors or strings have values assigned to them? 2988 | 2989 | 2990 | The following example demonstrates usage of the DEC in determining the 2991 | dependents of the given expression: 2992 | 2993 | typedef typename parser_t:: 2994 | dependent_entity_collector::symbol_t symbol_t; 2995 | 2996 | const std::string expression_string = 2997 | "z := abs(x + sin(2 * pi / y))" 2998 | 2999 | T x,y,z; 3000 | 3001 | parser_t parser; 3002 | symbol_table_t symbol_table; 3003 | 3004 | symbol_table.add_variable("x",x); 3005 | symbol_table.add_variable("y",y); 3006 | symbol_table.add_variable("z",z); 3007 | 3008 | expression_t expression; 3009 | expression.register_symbol_table(symbol_table); 3010 | 3011 | // Collect only variable and function symbols 3012 | parser.dec().collect_variables() = true; 3013 | parser.dec().collect_functions() = true; 3014 | 3015 | if (!parser.compile(expression_string,expression)) 3016 | { 3017 | // error.... 3018 | } 3019 | 3020 | std::deque<symbol_t> symbol_list; 3021 | 3022 | parser.dec().symbols(symbol_list); 3023 | 3024 | for (std::size_t i = 0; i < symbol_list.size(); ++i) 3025 | { 3026 | const symbol_t& symbol = symbol_list[i]; 3027 | 3028 | switch (symbol.second) 3029 | { 3030 | case parser_t::e_st_variable : ... break; 3031 | case parser_t::e_st_vector : ... break; 3032 | case parser_t::e_st_string : ... break; 3033 | case parser_t::e_st_function : ... break; 3034 | } 3035 | } 3036 | 3037 | 3038 | Note22: The 'symbol_t' type is a std::pair comprising of the symbol 3039 | name (std::string) and the associated type of the symbol as denoted by 3040 | the cases in the switch statement. 3041 | 3042 | Having particular symbols (variable or function) present in an 3043 | expression is one form of dependency. Another and just as interesting 3044 | and important type of dependency is that of assignments. Assignments 3045 | are the set of dependent symbols that 'may' have their values modified 3046 | within an expression. The following are example expressions and their 3047 | associated assignments: 3048 | 3049 | Assignments Expression 3050 | (1) x x := y + z 3051 | (2) x, y x += y += z 3052 | (3) x, y, z x := y += sin(z := w + 2) 3053 | (4) w, z if (x > y, z := x + 2, w := 'A String') 3054 | (5) None x + y + z 3055 | 3056 | 3057 | Note23: In expression 4, both variables 'w' and 'z' are denoted as 3058 | being assignments even though only one of them can ever be modified at 3059 | the time of evaluation. Furthermore the determination of which of the 3060 | two variables the modification will occur upon can only be known with 3061 | certainty at evaluation time and not beforehand, hence both are listed 3062 | as being candidates for assignment. 3063 | 3064 | The following builds upon the previous example demonstrating the usage 3065 | of the DEC in determining the 'assignments' of the given expression: 3066 | 3067 | // Collect assignments 3068 | parser.dec().collect_assignments() = true; 3069 | 3070 | if (!parser.compile(expression_string,expression)) 3071 | { 3072 | // error.... 3073 | } 3074 | 3075 | std::deque<symbol_t> symbol_list; 3076 | 3077 | parser.dec().assignment_symbols(symbol_list); 3078 | 3079 | for (std::size_t i = 0; i < symbol_list.size(); ++i) 3080 | { 3081 | symbol_t& symbol = symbol_list[i]; 3082 | 3083 | switch (symbol.second) 3084 | { 3085 | case parser_t::e_st_variable : ... break; 3086 | case parser_t::e_st_vector : ... break; 3087 | case parser_t::e_st_string : ... break; 3088 | } 3089 | } 3090 | 3091 | 3092 | Note24: The assignments will only consist of variable types and as 3093 | such will not contain symbols denoting functions. 3094 | 3095 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3096 | 3097 | [SECTION 17 - HIERARCHIES OF SYMBOL TABLES] 3098 | Most situations will only require a single symbol_table instance to be 3099 | associated with a given expression instance. 3100 | 3101 | However as an expression can have more than one symbol table instance 3102 | associated with itself, when building more complex systems that 3103 | utilise many expressions where each can in turn utilise one or more 3104 | variables from a large set of potential variables, functions or 3105 | constants, it becomes evident that grouping variables into layers of 3106 | symbol_tables will simplify and streamline the overall process. 3107 | 3108 | A recommended hierarchy of symbol tables is the following: 3109 | 3110 | (a) Global constant value symbol table 3111 | (b) Global non side-effect functions symbol table 3112 | (c) Global variable symbol table 3113 | (d) Expression specific variable symbol table 3114 | 3115 | 3116 | (a) Global constant value symbol table 3117 | This symbol table will contain constant variables denoting immutable 3118 | values. These variables can be made available to all expressions, and 3119 | in turn expressions will assume the values themselves will never be 3120 | modified for the duration of the process run-time. Examples of such 3121 | variables are: 3122 | 3123 | (1) pi or e 3124 | (2) speed_of_light 3125 | (3) avogadro_number 3126 | (4) num_cpus 3127 | 3128 | 3129 | (b) Global non side-effect functions symbol table 3130 | This symbol table will contain only user defined functions that will 3131 | not incur any side-effects that are observable to any of the 3132 | expressions that invoke them. These functions shall be thread-safe or 3133 | threading invariant and will not maintain any form of state between 3134 | invocations. Examples of such functions are: 3135 | 3136 | (1) calc_volume_of_sphere(r) 3137 | (2) distance(x0,y0,x1,y1) 3138 | 3139 | 3140 | (c) Global variable symbol table 3141 | This symbol table will contain variables that will be accessible to 3142 | all associated expressions and will not be specific or exclusive to 3143 | any one expression. This variant differs from (a) in that the values 3144 | of the variables can change (or be updated) between evaluations of 3145 | expressions - but through properly scheduled evaluations are 3146 | guaranteed to never change during the evaluation of any dependent 3147 | expressions. Furthermore it is assumed that these variables will be 3148 | used in a read-only context and that no expressions will attempt to 3149 | modify these variables via assignments or other means. 3150 | 3151 | (1) price_of_stock_xyz 3152 | (2) outside_temperature or inside_temperature 3153 | (3) fuel_in_tank 3154 | (4) num_customers_in_store 3155 | (5) num_items_on_shelf 3156 | 3157 | 3158 | (d) Expression specific variable symbol table 3159 | This symbol_table is the most common form, and is used to store 3160 | variables that are specific and exclusive to a particular expression. 3161 | That is to say references to variables in this symbol_table will not 3162 | be part of another expression. Though it may be possible to have 3163 | expressions that contain the variables with the same name, in that 3164 | case those variables will be distinctly different. Which would mean if 3165 | a particular expression were to be compiled twice, each expression 3166 | would have its own unique symbol_table which in turn would have its 3167 | own instances of those variables. Examples of such variables could be: 3168 | 3169 | (1) x or y 3170 | (2) customer_name 3171 | 3172 | 3173 | The following is a diagram depicting a possible variant of the denoted 3174 | symbol table hierarchies. In the diagram there are two unique 3175 | expressions, each of which have a reference to the Global constant, 3176 | functions and variables symbol tables and an exclusive reference to a 3177 | local symbol table. 3178 | 3179 | +-------------------------+ +-------------------------+ 3180 | | Global Constants | | Global Functions | 3181 | | Symbol Table | | Symbol Table | 3182 | +----o--o-----------------+ +--------------------o----+ 3183 | | | | 3184 | | | +-------+ 3185 | | +------------------->----------------------------+ | 3186 | | +----------------------------+ | | 3187 | | | Global Variables | | | 3188 | | +------o Symbol Table o-----+ | V 3189 | | | +----------------------------+ | | | 3190 | | | | | | 3191 | | | +----------------+ +----------------+ | | | 3192 | | | | Symbol Table 0 | | Symbol Table 1 | | V | 3193 | | | +--o-------------+ +--o-------------+ | | | 3194 | | | | | | | | 3195 | | | | | | | | 3196 | +--V--V----V---------+ +-V---------------V--+ | | 3197 | | Expression 0 | | Expression 1 |<--+--+ 3198 | | '2 * sin(x) - y' | | 'k + abs(x - y)' | 3199 | +--------------------+ +--------------------+ 3200 | 3201 | 3202 | Bringing all of the above together, in the following example the 3203 | hierarchy of symbol tables are instantiated and initialised. An 3204 | expression that makes use of various elements of each symbol table is 3205 | then compiled and later on evaluated: 3206 | 3207 | typedef exprtk::symbol_table<double> symbol_table_t; 3208 | typedef exprtk::expression<double> expression_t; 3209 | 3210 | // Setup global constants symbol table 3211 | symbol_table_t glbl_const_symbol_table; 3212 | glbl_const_symbtab.add_constants(); // pi, epsilon and inf 3213 | glbl_const_symbtab.add_constant("speed_of_light",299e6); 3214 | glbl_const_symbtab.add_constant("avogadro_number",6e23); 3215 | 3216 | // Setup global function symbol table 3217 | symbol_table_t glbl_funcs_symbol_table; 3218 | glbl_func_symbtab.add_function('distance',distance); 3219 | glbl_func_symbtab.add_function('calc_spherevol',calc_sphrvol); 3220 | 3221 | ...... 3222 | 3223 | // Setup global variable symbol table 3224 | symbol_table_t glbl_variable_symbol_table; 3225 | glbl_variable_symbtab.add_variable('temp_outside',thermo.outside); 3226 | glbl_variable_symbtab.add_variable('temp_inside' ,thermo.inside ); 3227 | glbl_variable_symbtab.add_variable('num_cstmrs',store.num_cstmrs); 3228 | 3229 | ...... 3230 | 3231 | double x,y,z; 3232 | 3233 | // Setup expression specific symbol table 3234 | symbol_table_t symbol_table; 3235 | symbol_table.add_variable('x',x); 3236 | symbol_table.add_variable('y',y); 3237 | symbol_table.add_variable('z',z); 3238 | 3239 | expression_t expression; 3240 | 3241 | // Register the various symbol tables 3242 | expression 3243 | .register_symbol_table(symbol_table); 3244 | 3245 | expression 3246 | .register_symbol_table(glbl_funcs_symbol_table); 3247 | 3248 | expression 3249 | .register_symbol_table(glbl_const_symbol_table); 3250 | 3251 | expression 3252 | .register_symbol_table(glbl_variable_symbol_table); 3253 | 3254 | const std::string expression_str = 3255 | "abs(temp_inside - temp_outside) + 2 * speed_of_light / x" 3256 | 3257 | parser_t parser; 3258 | parser.compile(expression_str,expression); 3259 | 3260 | ...... 3261 | 3262 | while (keep_evaluating) 3263 | { 3264 | .... 3265 | 3266 | T result = expression.value(); 3267 | 3268 | .... 3269 | } 3270 | 3271 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3272 | 3273 | [SECTION 18 - UNKNOWN UNKNOWNS] 3274 | In this section we will discuss the process of handling expressions 3275 | with a mix of known and unknown variables. Initially a discussion into 3276 | the types of expressions that exist will be provided, then a series of 3277 | possible solutions will be presented for each scenario. 3278 | 3279 | When parsing an expression, there may be situations where one is not 3280 | fully aware of what if any variables will be used prior to the 3281 | expression being compiled. 3282 | 3283 | This can become problematic, as in the default scenario it is assumed 3284 | the symbol_table that is registered with the expression instance will 3285 | already possess the externally available variables, functions and 3286 | constants needed during the compilation of the expression. 3287 | 3288 | In the event there are symbols in the expression that can't be mapped 3289 | to either a reserved word, or located in the associated 3290 | symbol_table(s), an "Undefined symbol" error will be raised and the 3291 | compilation process will fail. 3292 | 3293 | The numerous scenarios that can occur when compiling an expression 3294 | with ExprTk generally fall into one of the following three categories: 3295 | 3296 | (a) No external variables 3297 | (b) Predetermined set of external variables 3298 | (c) Unknown set of variables 3299 | 3300 | 3301 | (a) No external variables 3302 | These are expressions that contain no external variables but may 3303 | contain local variables. As local variables cannot be accessed 3304 | externally from the expression, it is assumed that such expressions 3305 | will not have a need for a symbol_table and furthermore expressions 3306 | which don't make use of functions that have side-effects will be 3307 | evaluated completely at compile time resulting in a constant return 3308 | value. The following are examples of such expressions: 3309 | 3310 | (1) 1 + 2 3311 | (2) var x := 3; 2 * x - 3 3312 | (3) var x := 3; var y := abs(x - 8); x - y / 7 3313 | 3314 | 3315 | (b) Predetermined set of external variables 3316 | These are expressions that are comprised of externally available 3317 | variables and functions and will only compile successfully if the 3318 | symbols that correspond to the variables and functions are already 3319 | defined in their associated symbol_table(s). This is by far the most 3320 | common scenario when using ExprTk. 3321 | 3322 | As an example, one may have three external variables: x, y and z which 3323 | have been registered with the associated symbol_table, and will then 3324 | need to compile and evaluate expressions comprised of any subset of 3325 | these three variables. The following are a few examples of such 3326 | expressions: 3327 | 3328 | (1) 1 + x 3329 | (2) x / y 3330 | (3) 2 * x * y / z 3331 | 3332 | 3333 | In this scenario one can use the 'dependent_entity_collector' 3334 | component as described in [Section 16] to further determine which of 3335 | the registered variables were actually used in the given expression. 3336 | As an example once the set of utilised variables are known, any 3337 | further 'attention' can be restricted to only those variables when 3338 | evaluating the expression. This can be quite useful when dealing with 3339 | expressions that can draw from a set of hundreds or even thousands of 3340 | variables. 3341 | 3342 | 3343 | (c) Unknown set of variables 3344 | These are expressions that are comprised of symbols other than the 3345 | standard ExprTk reserved words or what has been registered with their 3346 | associated symbol_table, and will normally fail compilation due to the 3347 | associated symbol_table not having a reference to them. As such this 3348 | scenario can be seen as a combination of scenario B, where one may 3349 | have a symbol_table with registered variables, but would also like to 3350 | handle the situation of variables that aren't present in said 3351 | symbol_table. 3352 | 3353 | When dealing with expressions of category (c), one must perform all of 3354 | the following: 3355 | 3356 | (1) Determine the variables used in the expression 3357 | (2) Populate a symbol_table(s) with the entities from (1) 3358 | (3) Compile the expression 3359 | (4) Provide a means by which the entities from (1) can be modified 3360 | 3361 | 3362 | Depending on the nature of processing, steps (1) and (2) can be done 3363 | either independently of each other or combined into one. The following 3364 | example will initially look at solving the problem of unknown 3365 | variables with the latter method using the 'unknown_symbol_resolver' 3366 | component. 3367 | 3368 | typedef exprtk::symbol_table<T> symbol_table_t; 3369 | typedef exprtk::expression<T> expression_t; 3370 | typedef exprtk::parser<T> parser_t; 3371 | 3372 | T x = T(123.456); 3373 | T y = T(789.123); 3374 | 3375 | symbol_table_t unknown_var_symbol_table; 3376 | 3377 | symbol_table_t symbol_table; 3378 | symbol_table.add_variable("x",x); 3379 | symbol_table.add_variable("y",y); 3380 | 3381 | expression_t expression; 3382 | expression.register_symbol_table(unknown_var_symbol_table); 3383 | expression.register_symbol_table(symbol_table); 3384 | 3385 | parser_t parser; 3386 | parser.enable_unknown_symbol_resolver(); 3387 | 3388 | const std::string expression_str = "x + abs(y / 3k) * z + 2" 3389 | 3390 | parser.compile(expression_str,expression); 3391 | 3392 | 3393 | In the example above, the symbols 'k' and 'z' will be treated as 3394 | unknown symbols. The parser in the example is set to handle unknown 3395 | symbols using the built-in default unknown_symbol_resolver (USR). The 3396 | default USR will automatically resolve any unknown symbols as a 3397 | variable (scalar type). The new variables will be added to the primary 3398 | symbol_table, which in this case is the 'unknown_var_symbol_table' 3399 | instance. Once the compilation has completed successfully, the 3400 | variables that were resolved during compilation can be accessed from 3401 | the primary symbol_table using the 'get_variable_list' and 3402 | 'variable_ref' methods and then if needed can be modified accordingly 3403 | after which the expression itself can be evaluated. 3404 | 3405 | std::vector<std::string> variable_list; 3406 | 3407 | unknown_var_symbol_table.get_variable_list(variable_list); 3408 | 3409 | for (const auto& var_name : variable_list) 3410 | { 3411 | T& v = unknown_var_symbol_table.variable_ref(var_name); 3412 | 3413 | v = ...; 3414 | } 3415 | 3416 | ... 3417 | 3418 | expression.value(); 3419 | 3420 | 3421 | Note25: As previously mentioned the default USR will automatically 3422 | assume any unknown symbol to be a valid scalar variable, and will then 3423 | proceed to add said symbol as a variable to the primary symbol_table 3424 | of the associated expression during the compilation process. However a 3425 | problem that may arise, is that expressions that are parsed with the 3426 | USR enabled, but contain 'typos' or otherwise syntactic errors may 3427 | inadvertently compile successfully due to the simplistic nature of the 3428 | default USR. The following are some example expressions: 3429 | 3430 | (1) 1 + abz(x + 1) 3431 | (2) sine(y / 2) - coz(3x) 3432 | 3433 | 3434 | The two expressions above contain misspelt symbols (abz, sine, coz) 3435 | which if implied multiplications and default USR are enabled during 3436 | compilation will result in them being assumed to be valid 'variables', 3437 | which obviously is not the intended outcome by the user. A possible 3438 | solution to this problem is for one to implement their own specific 3439 | USR that will perform a user defined business logic in determining if 3440 | an encountered unknown symbol should be treated as a variable or if it 3441 | should raise a compilation error. The following example demonstrates a 3442 | simple user defined USR: 3443 | 3444 | typedef exprtk::symbol_table<T> symbol_table_t; 3445 | typedef exprtk::expression<T> expression_t; 3446 | typedef exprtk::parser<T> parser_t; 3447 | 3448 | template <typename T> 3449 | struct my_usr final : public parser_t::unknown_symbol_resolver 3450 | { 3451 | typedef typename parser_t::unknown_symbol_resolver usr_t; 3452 | 3453 | bool process(const std::string& unknown_symbol, 3454 | typename usr_t::usr_symbol_type& st, 3455 | T& default_value, 3456 | std::string& error_message) override 3457 | { 3458 | if (0 != unknown_symbol.find("var_")) 3459 | { 3460 | error_message = "Invalid symbol: " + unknown_symbol; 3461 | return false; 3462 | } 3463 | 3464 | st = usr_t::e_usr_variable_type; 3465 | default_value = T(123.123); 3466 | 3467 | return true; 3468 | } 3469 | }; 3470 | 3471 | ... 3472 | 3473 | T x = T(123.456); 3474 | T y = T(789.123); 3475 | 3476 | symbol_table_t unknown_var_symbol_table; 3477 | 3478 | symbol_table_t symbol_table; 3479 | symbol_table.add_variable("x",x); 3480 | symbol_table.add_variable("y",y); 3481 | 3482 | expression_t expression; 3483 | expression.register_symbol_table(unknown_var_symbol_table); 3484 | expression.register_symbol_table(symbol_table); 3485 | 3486 | my_usr<T> musr; 3487 | 3488 | parser_t parser; 3489 | parser.enable_unknown_symbol_resolver(&musr); 3490 | 3491 | std::string expression_str = "var_x + abs(var_y - 3) * var_z" 3492 | 3493 | parser.compile(expression_str,expression); 3494 | 3495 | 3496 | In the example above, a user specified USR is defined, and is 3497 | registered with the parser enabling the USR functionality. 3498 | Subsequently during the compilation process when an unknown symbol is 3499 | encountered, the USR's process method will be invoked. The USR in the 3500 | example will only 'accept' unknown symbols that have a prefix of 3501 | 'var_' as being valid variables, all other unknown symbols will result 3502 | in a compilation error being raised. 3503 | 3504 | In the example above the callback of the USR that is invoked during 3505 | the unknown symbol resolution process only allows for scalar variables 3506 | to be defined and resolved - as that is the simplest and most common 3507 | form. 3508 | 3509 | There is a further extended version of the callback that can be 3510 | overridden that will allow for more control and choice over the type 3511 | of symbol being resolved. The following is an example definition of 3512 | said extended callback: 3513 | 3514 | template <typename T> 3515 | struct my_usr final : public parser_t::unknown_symbol_resolver 3516 | { 3517 | typedef typename parser_t::unknown_symbol_resolver usr_t; 3518 | 3519 | my_usr() 3520 | : usr_t(usr_t::e_usrmode_extended) 3521 | {} 3522 | 3523 | bool process(const std::string& unknown_symbol, 3524 | symbol_table_t& symbol_table, 3525 | std::string& error_message) override 3526 | { 3527 | bool result = false; 3528 | 3529 | if (0 == unknown_symbol.find("var_")) 3530 | { 3531 | // Default value of zero 3532 | result = symbol_table.create_variable(unknown_symbol,0); 3533 | 3534 | if (!result) 3535 | { 3536 | error_message = "Failed to create variable..." 3537 | } 3538 | } 3539 | else if (0 == unknown_symbol.find("str_")) 3540 | { 3541 | // Default value of empty string 3542 | result = symbol_table.create_stringvar(unknown_symbol,""); 3543 | 3544 | if (!result) 3545 | { 3546 | error_message = "Failed to create string variable..." 3547 | } 3548 | } 3549 | else 3550 | error_message = "Indeterminable symbol type." 3551 | 3552 | return result; 3553 | } 3554 | }; 3555 | 3556 | 3557 | In the example above, the USR callback when invoked will pass the 3558 | primary symbol table associated with the expression being parsed. The 3559 | symbol resolution business logic can then determine under what 3560 | conditions a symbol will be resolved including its type (scalar, 3561 | string, vector etc) and default value. When the callback successfully 3562 | returns the symbol parsing and resolution process will again be 3563 | executed by the parser. The idea here is that given the primary symbol 3564 | table will now have the previously detected unknown symbol registered, 3565 | it will be correctly resolved and the general parsing processing can 3566 | then resume as per normal. 3567 | 3568 | Note26: In order to have the USR's extended mode callback be invoked 3569 | it is necessary to pass the e_usrmode_extended enum value during the 3570 | constructor of the user defined USR. 3571 | 3572 | Note27: The primary symbol table for an expression is the first symbol 3573 | table to be registered with that instance of the expression. 3574 | 3575 | Note28: For a successful symbol resolution using the normal USR all of 3576 | the following are required: 3577 | 3578 | (1) Only if successful shall the process method return TRUE 3579 | (2) The default_value parameter will have been set 3580 | (3) The error_message parameter will be empty 3581 | (4) usr_symbol_type input parameter field will be set to either: 3582 | (*) e_usr_variable_type 3583 | (*) e_usr_constant_type 3584 | 3585 | Note29: For a successful symbol resolution using the extended USR all 3586 | of the following are required: 3587 | 3588 | (1) Only if successful shall the process method return TRUE 3589 | (2) symbol_table parameter will have had the newly resolved 3590 | variable or string added to it 3591 | (3) error_message parameter will be empty 3592 | 3593 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3594 | 3595 | [SECTION 19 - ENABLING & DISABLING FEATURES] 3596 | The parser can be configured via its settings instance to either allow 3597 | or disallow certain features that are available within the ExprTk 3598 | grammar. The features fall into one of the following six categories: 3599 | 3600 | (1) Base Functions 3601 | (2) Control Flow Structures 3602 | (3) Logical Operators 3603 | (4) Arithmetic Operators 3604 | (5) Inequality Operators 3605 | (6) Assignment Operators 3606 | 3607 | 3608 | (1) Base Functions 3609 | The list of available base functions is as follows: 3610 | 3611 | abs, acos, acosh, asin, asinh, atan, atanh, atan2, avg, ceil, 3612 | clamp, cos, cosh, cot, csc, equal, erf, erfc, exp, expm1, 3613 | floor, frac, hypot, iclamp, like, log, log10, log2, logn, 3614 | log1p, mand, max, min, mod, mor, mul, ncdf, pow, root, round, 3615 | roundn, sec, sgn, sin, sinc, sinh, sqrt, sum, swap, tan, tanh, 3616 | trunc, not_equal, inrange, deg2grad, deg2rad, rad2deg, grad2deg 3617 | 3618 | 3619 | The above mentioned base functions can be either enabled or disabled 3620 | 'all' at once, as is demonstrated below: 3621 | 3622 | parser_t parser; 3623 | expression_t expression; 3624 | 3625 | parser.settings().disable_all_base_functions(); 3626 | 3627 | parser 3628 | .compile("2 * abs(2 - 3)",expression); // compilation failure 3629 | 3630 | parser.settings().enable_all_base_functions(); 3631 | 3632 | parser 3633 | .compile("2 * abs(2 - 3)",expression); // compilation success 3634 | 3635 | 3636 | One can also enable or disable specific base functions. The following 3637 | example demonstrates the disabling of the trigonometric functions 3638 | 'sin' and 'cos': 3639 | 3640 | parser_t parser; 3641 | expression_t expression; 3642 | 3643 | parser.settings() 3644 | .disable_base_function(settings_t::e_bf_sin) 3645 | .disable_base_function(settings_t::e_bf_cos); 3646 | 3647 | parser 3648 | .compile("(sin(x) / cos(x)) == tan(x)",expression); // failure 3649 | 3650 | parser.settings() 3651 | .enable_base_function(settings_t::e_bf_sin) 3652 | .enable_base_function(settings_t::e_bf_cos); 3653 | 3654 | parser 3655 | .compile("(sin(x) / cos(x)) == tan(x)",expression); // success 3656 | 3657 | 3658 | (2) Control Flow Structures 3659 | The list of available control flow structures is as follows: 3660 | 3661 | (a) If or If-Else 3662 | (b) Switch statement 3663 | (c) For Loop 3664 | (d) While Loop 3665 | (e) Repeat Loop 3666 | 3667 | 3668 | The above mentioned control flow structures can be either enabled 3669 | or disabled 'all' at once, as is demonstrated below: 3670 | 3671 | parser_t parser; 3672 | expression_t expression; 3673 | 3674 | const std::string program = 3675 | " var x := 0; " 3676 | " for (var i := 0; i < 10; i += 1) " 3677 | " { " 3678 | " x += i; " 3679 | " } " 3680 | 3681 | parser.settings().disable_all_control_structures(); 3682 | 3683 | parser 3684 | .compile(program,expression); // compilation failure 3685 | 3686 | parser.settings().enable_all_control_structures(); 3687 | 3688 | parser 3689 | .compile(program,expression); // compilation success 3690 | 3691 | 3692 | One can also enable or disable specific control flow structures. The 3693 | following example demonstrates the disabling of the for-loop control 3694 | flow structure: 3695 | 3696 | parser_t parser; 3697 | expression_t expression; 3698 | 3699 | const std::string program = 3700 | " var x := 0; " 3701 | " for (var i := 0; i < 10; i += 1) " 3702 | " { " 3703 | " x += i; " 3704 | " } " 3705 | 3706 | parser.settings() 3707 | .disable_control_structure(settings_t::e_ctrl_for_loop); 3708 | 3709 | parser 3710 | .compile(program,expression); // failure 3711 | 3712 | parser.settings() 3713 | .enable_control_structure(settings_t::e_ctrl_for_loop); 3714 | 3715 | parser 3716 | .compile(program,expression); // success 3717 | 3718 | 3719 | (3) Logical Operators 3720 | The list of available logical operators is as follows: 3721 | 3722 | and, nand, nor, not, or, xnor, xor, &, | 3723 | 3724 | 3725 | The above mentioned logical operators can be either enabled or 3726 | disabled 'all' at once, as is demonstrated below: 3727 | 3728 | parser_t parser; 3729 | expression_t expression; 3730 | 3731 | parser.settings().disable_all_logic_ops(); 3732 | 3733 | parser 3734 | .compile("1 or not(0 and 1)",expression); // compilation failure 3735 | 3736 | parser.settings().enable_all_logic_ops(); 3737 | 3738 | parser 3739 | .compile("1 or not(0 and 1)",expression); // compilation success 3740 | 3741 | 3742 | One can also enable or disable specific logical operators. The 3743 | following example demonstrates the disabling of the 'and' logical 3744 | operator: 3745 | 3746 | parser_t parser; 3747 | expression_t expression; 3748 | 3749 | parser.settings() 3750 | .disable_logic_operation(settings_t::e_logic_and); 3751 | 3752 | parser 3753 | .compile("1 or not(0 and 1)",expression); // failure 3754 | 3755 | parser.settings() 3756 | .enable_logic_operation(settings_t::e_logic_and); 3757 | 3758 | parser 3759 | .compile("1 or not(0 and 1)",expression); // success 3760 | 3761 | 3762 | (4) Arithmetic Operators 3763 | The list of available arithmetic operators is as follows: 3764 | 3765 | +, -, *, /, %, ^ 3766 | 3767 | 3768 | The above mentioned arithmetic operators can be either enabled or 3769 | disabled 'all' at once, as is demonstrated below: 3770 | 3771 | parser_t parser; 3772 | expression_t expression; 3773 | 3774 | parser.settings().disable_all_arithmetic_ops(); 3775 | 3776 | parser 3777 | .compile("1 + 2 / 3",expression); // compilation failure 3778 | 3779 | parser.settings().enable_all_arithmetic_ops(); 3780 | 3781 | parser 3782 | .compile("1 + 2 / 3",expression); // compilation success 3783 | 3784 | 3785 | One can also enable or disable specific arithmetic operators. The 3786 | following example demonstrates the disabling of the addition '+' 3787 | arithmetic operator: 3788 | 3789 | parser_t parser; 3790 | expression_t expression; 3791 | 3792 | parser.settings() 3793 | .disable_arithmetic_operation(settings_t::e_arith_add); 3794 | 3795 | parser 3796 | .compile("1 + 2 / 3",expression); // failure 3797 | 3798 | parser.settings() 3799 | .enable_arithmetic_operation(settings_t::e_arith_add); 3800 | 3801 | parser 3802 | .compile("1 + 2 / 3",expression); // success 3803 | 3804 | 3805 | (5) Inequality Operators 3806 | The list of available inequality operators is as follows: 3807 | 3808 | <, <=, >, >=, ==, =, != <> 3809 | 3810 | 3811 | The above mentioned inequality operators can be either enabled or 3812 | disabled 'all' at once, as is demonstrated below: 3813 | 3814 | parser_t parser; 3815 | expression_t expression; 3816 | 3817 | parser.settings().disable_all_inequality_ops(); 3818 | 3819 | parser 3820 | .compile("1 < 3",expression); // compilation failure 3821 | 3822 | parser.settings().enable_all_inequality_ops(); 3823 | 3824 | parser 3825 | .compile("1 < 3",expression); // compilation success 3826 | 3827 | 3828 | One can also enable or disable specific inequality operators. The 3829 | following example demonstrates the disabling of the less-than '<' 3830 | inequality operator: 3831 | 3832 | parser_t parser; 3833 | expression_t expression; 3834 | 3835 | parser.settings() 3836 | .disable_inequality_operation(settings_t::e_ineq_lt); 3837 | 3838 | parser 3839 | .compile("1 < 3",expression); // failure 3840 | 3841 | parser.settings() 3842 | .enable_inequality_operation(settings_t::e_ineq_lt); 3843 | 3844 | parser 3845 | .compile("1 < 3",expression); // success 3846 | 3847 | 3848 | (6) Assignment Operators 3849 | The list of available assignment operators is as follows: 3850 | 3851 | :=, +=, -=, *=, /=, %= 3852 | 3853 | 3854 | The above mentioned assignment operators can be either enabled or 3855 | disabled 'all' at once, as is demonstrated below: 3856 | 3857 | T x = T(0); 3858 | 3859 | parser_t parser; 3860 | expression_t expression; 3861 | symbol_table_t symbol_table; 3862 | 3863 | symbol_table.add_variable("x",x); 3864 | 3865 | expression.register_symbol_table(symbol_table); 3866 | 3867 | parser.settings().disable_all_assignment_ops(); 3868 | 3869 | parser 3870 | .compile("x := 3",expression); // compilation failure 3871 | 3872 | parser.settings().enable_all_assignment_ops(); 3873 | 3874 | parser 3875 | .compile("x := 3",expression); // compilation success 3876 | 3877 | 3878 | One can also enable or disable specific assignment operators. The 3879 | following example demonstrates the disabling of the '+=' addition 3880 | assignment operator: 3881 | 3882 | T x = T(0); 3883 | 3884 | parser_t parser; 3885 | expression_t expression; 3886 | symbol_table_t symbol_table; 3887 | 3888 | symbol_table.add_variable("x",x); 3889 | 3890 | expression.register_symbol_table(symbol_table); 3891 | 3892 | parser.settings() 3893 | .disable_assignment_operation(settings_t::e_assign_addass); 3894 | 3895 | parser 3896 | .compile("x += 3",expression); // failure 3897 | 3898 | parser.settings() 3899 | .enable_assignment_operation(settings_t::e_assign_addass); 3900 | 3901 | parser 3902 | .compile("x += 3",expression); // success 3903 | 3904 | 3905 | Note30: In the event of a base function being disabled, one can 3906 | redefine the base function using the standard custom function 3907 | definition process. In the following example the 'sin' function is 3908 | disabled then redefined as a function taking degree input. 3909 | 3910 | template <typename T> 3911 | struct sine_deg final : public exprtk::ifunction<T> 3912 | { 3913 | sine_deg() : exprtk::ifunction<T>(1) {} 3914 | 3915 | inline T operator()(const T& v) override 3916 | { 3917 | const T pi = exprtk::details::numeric::constant::pi; 3918 | return std::sin((v * T(pi)) / T(180)); 3919 | } 3920 | }; 3921 | 3922 | ... 3923 | 3924 | typedef exprtk::symbol_table<T> symbol_table_t; 3925 | typedef exprtk::expression<T> expression_t; 3926 | typedef exprtk::parser<T> parser_t; 3927 | 3928 | typedef typename parser_t::settings_store settings_t; 3929 | 3930 | sine_deg<T> sine; 3931 | 3932 | symbol_table.add_reserved_function("sin",sine); 3933 | 3934 | expression_t expression; 3935 | 3936 | expression.register_symbol_table(symbol_table); 3937 | 3938 | parser_t parser; 3939 | 3940 | parser.settings() 3941 | .disable_base_function(settings_t::e_bf_sin); 3942 | 3943 | parser.compile("1 + sin(30)",expression); 3944 | 3945 | 3946 | In the example above, the custom 'sin' function is registered with the 3947 | symbol_table using the method 'add_reserved_function'. This is done so 3948 | as to bypass the checks for reserved words that are carried out on the 3949 | provided symbol names when calling the standard 'add_function' method. 3950 | Normally if a user specified symbol name conflicts with any of the 3951 | ExprTk reserved words, the add_function call will fail. 3952 | 3953 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3954 | 3955 | [SECTION 20 - EXPRESSION RETURN VALUES] 3956 | ExprTk expressions can return immediately from any point by utilising 3957 | the return call. Furthermore the return call can be used to transfer 3958 | out multiple return values from within the expression. 3959 | 3960 | If an expression evaluation exits using a return point, the result of 3961 | the call to the 'value' method will be NaN, and it is expected that 3962 | the return values will be available from the results_context. 3963 | 3964 | In the following example there are three return points in the 3965 | expression. If neither of the return points are hit, then the 3966 | expression will return normally. 3967 | 3968 | const std::string expression_string = 3969 | " if (x < y) " 3970 | " return [x + 1,'return-call 1']; " 3971 | " else if (x > y) " 3972 | " return [y / 2, y + 1, 'return-call 2']; " 3973 | " else if (equal(x,y)) " 3974 | " x + y; " 3975 | " return [x, y, x + y, x - y, 'return-call 3'] " 3976 | 3977 | typedef exprtk::symbol_table<double> symbol_table_t; 3978 | typedef exprtk::expression<double> expression_t; 3979 | typedef exprtk::parser<double> parser_t; 3980 | 3981 | symbol_table_t symbol_table; 3982 | expression_t expression; 3983 | parser_t parser; 3984 | 3985 | double x = 0; 3986 | double y = 0; 3987 | 3988 | symbol_table.add_variable("x",x); 3989 | symbol_table.add_variable("y",y); 3990 | 3991 | expression.register_symbol_table(symbol_table); 3992 | 3993 | parser.compile(expression_string,expression); 3994 | 3995 | T result = expression.value(); 3996 | 3997 | if (expression.return_invoked()) 3998 | { 3999 | typedef exprtk::results_context<T> results_context_t; 4000 | typedef typename results_context_t::type_store_t type_t; 4001 | typedef typename type_t::scalar_view scalar_t; 4002 | typedef typename type_t::vector_view vector_t; 4003 | typedef typename type_t::string_view string_t; 4004 | 4005 | const results_context_t& results = expression.results(); 4006 | 4007 | for (std::size_t i = 0; i < results.count(); ++i) 4008 | { 4009 | type_t t = results[i]; 4010 | 4011 | switch (t.type) 4012 | { 4013 | case type_t::e_scalar : ... 4014 | break; 4015 | 4016 | case type_t::e_vector : ... 4017 | break; 4018 | 4019 | case type_t::e_string : ... 4020 | break; 4021 | 4022 | default : continue; 4023 | } 4024 | } 4025 | 4026 | 4027 | In the above example, there are three possible "return" points and one 4028 | regular result. Only one of the four paths can ever be realised. Hence 4029 | it is necessary to capture the result of the expression value method 4030 | call. In the event, the call to return_invoked is not true then the 4031 | non-return code path was executed and the result of the evaluation 4032 | will be the result of the expression's value method. 4033 | 4034 | Note31: Processing of the return results is similar to that of the 4035 | generic function call parameters. 4036 | 4037 | The results_context provides getter methods for each of the possible 4038 | return types (scalar, vector and string) and can be used as follows: 4039 | 4040 | typedef exprtk::symbol_table<T> symbol_table_t; 4041 | typedef exprtk::expression<T> expression_t; 4042 | typedef exprtk::parser<T> parser_t; 4043 | 4044 | const std::string expression_str = 4045 | " if (x > y) " 4046 | " return [1]; " 4047 | " else " 4048 | " return [ x, x + y, 2 * v, s + 'world' ]; " 4049 | 4050 | symbol_table_t symbol_table; 4051 | expression_t expression; 4052 | parser_t parser; 4053 | 4054 | symbol_table.add_variable ("x", x); 4055 | symbol_table.add_variable ("y", y); 4056 | symbol_table.add_variable ("z", z); 4057 | symbol_table.add_vector ("v", v); 4058 | symbol_table.add_stringvar("s", s); 4059 | 4060 | parser.compile(expression_str, expression); 4061 | 4062 | expression.value(); 4063 | 4064 | typedef exprtk::results_context<T> results_context_t; 4065 | const results_context_t& results = expression.results(); 4066 | 4067 | if (results.count() == 4) 4068 | { 4069 | T result_x0; 4070 | T result_x1; 4071 | std::string result_s; 4072 | std::vector<T> result_v; 4073 | 4074 | results.get_scalar(0, result_x0); 4075 | results.get_scalar(1, result_x1); 4076 | results.get_string(3, result_s ); 4077 | results.get_vector(2, result_v ); 4078 | } 4079 | 4080 | 4081 | It is however recommended that if there is to be only a single flow of 4082 | execution through the expression, that the simpler approach of 4083 | registering external variables of appropriate type be used. 4084 | 4085 | This method simply requires the variables that are to hold the various 4086 | results that are to be computed within the expression to be registered 4087 | with an associated symbol_table instance. Then within the expression 4088 | itself to have the result variables be assigned the appropriate 4089 | values. 4090 | 4091 | typedef exprtk::symbol_table<double> symbol_table_t; 4092 | typedef exprtk::expression<double> expression_t; 4093 | typedef exprtk::parser<double> parser_t; 4094 | 4095 | const std::string expression_string = 4096 | " var x := 123.456; " 4097 | " var s := 'ijk'; " 4098 | " result0 := x + 78.90; " 4099 | " result1 := s + '123' " 4100 | 4101 | double result0; 4102 | std::string result1; 4103 | 4104 | symbol_table_t symbol_table; 4105 | symbol_table.add_variable ("result0",result0); 4106 | symbol_table.add_stringvar("result1",result1); 4107 | 4108 | expression_t expression; 4109 | expression.register_symbol_table(symbol_table); 4110 | 4111 | parser_t parser; 4112 | parser.compile(expression_string,expression); 4113 | 4114 | expression.value(); 4115 | 4116 | printf("Result0: %15.5f\n", result0 ); 4117 | printf("Result1: %s\n" , result1.c_str()); 4118 | 4119 | 4120 | In the example above, the expression will compute two results. As such 4121 | two result variables are defined to hold the values named result0 and 4122 | result1 respectively. The first is of scalar type (double), the second 4123 | is of string type. Once the expression has been evaluated, the two 4124 | variables will have been updated with the new result values, and can 4125 | then be further utilised from within the calling host program. 4126 | 4127 | There will be times when an expression may have multiple exit paths, 4128 | where not all the paths will be return-statement based. The following 4129 | example builds upon the previous examples, but this time at least one 4130 | path is not return based. 4131 | 4132 | typedef exprtk::symbol_table<double> symbol_table_t; 4133 | typedef exprtk::expression<double> expression_t; 4134 | typedef exprtk::parser<double> parser_t; 4135 | 4136 | double x = 100.0; 4137 | double y = 200.0; 4138 | 4139 | symbol_table_t symbol_table; 4140 | expression_t expression; 4141 | parser_t parser; 4142 | 4143 | symbol_table.add_variable ("x", x); 4144 | symbol_table.add_variable ("y", y); 4145 | 4146 | expression.register_symbol_table(symbol_table); 4147 | 4148 | const std::string expression_string = 4149 | " for (var i := 0; i < 10; i += 1) " 4150 | " { " 4151 | " if (i > x) " 4152 | " { " 4153 | " return [x + y, 'return-call 1']; " 4154 | " } " 4155 | " else if (i > y) " 4156 | " { " 4157 | " return [x - y, 'return-call 2']; " 4158 | " } " 4159 | " }; " 4160 | " " 4161 | " x / y " 4162 | 4163 | parser.compile(expression_str, expression); 4164 | 4165 | const auto result = expression.value(); 4166 | 4167 | if (expression.return_invoked()) 4168 | { 4169 | const auto results = expression.results(); 4170 | 4171 | for (std::size_t i = 0; i < results.count(); ++i) 4172 | { 4173 | const auto& rtrn_result = results[i]; 4174 | . 4175 | . 4176 | . 4177 | } 4178 | } 4179 | else 4180 | { 4181 | printf("result: %f\n",result); 4182 | } 4183 | 4184 | 4185 | After having called the value method on the expression, calling the 4186 | return_invoked method will determine if the expression completed due 4187 | to a return statement being invoked or if it finished normally. 4188 | 4189 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4190 | 4191 | [SECTION 21 - COMPILATION ERRORS] 4192 | When attempting to compile a malformed or otherwise erroneous ExprTk 4193 | expression, the compilation process will result in an error, as is 4194 | indicated by the 'compile' method returning a false value. A 4195 | diagnostic indicating the first error encountered and its cause can be 4196 | obtained by invoking the 'error' method, as is demonstrated in the 4197 | following example: 4198 | 4199 | if (!parser.compile(expression_string,expression)) 4200 | { 4201 | printf("Error: %s\n", parser.error().c_str()); 4202 | return false; 4203 | } 4204 | 4205 | 4206 | Any error(s) resulting from a failed compilation will be stored in the 4207 | parser instance until the next time a compilation is performed. Before 4208 | then errors can be enumerated in the order they occurred by invoking 4209 | the 'get_error' method which itself will return a 'parser_error' type. 4210 | A parser_error object will contain an error diagnostic, an error mode 4211 | (or class), and the character position of the error in the expression 4212 | string. The following example demonstrates the enumeration of error(s) 4213 | in the event of a failed compilation. 4214 | 4215 | typedef exprtk::parser<T> parser_t; 4216 | typedef exprtk::parser_error::type error_t; 4217 | 4218 | if (!parser.compile(expression_string,expression)) 4219 | { 4220 | for (std::size_t i = 0; i < parser.error_count(); ++i) 4221 | { 4222 | typedef exprtk::parser_error::type error_t; 4223 | 4224 | error_t error = parser.get_error(i); 4225 | 4226 | printf("Error[%02d] Position: %02d Type: [%14s] Msg: %s\n", 4227 | i, 4228 | error.token.position, 4229 | exprtk::parser_error::to_str(error.mode).c_str(), 4230 | error.diagnostic.c_str()); 4231 | } 4232 | 4233 | return false; 4234 | } 4235 | 4236 | 4237 | Assuming the following expression '2 + (3 / log(1 + x))' which uses a 4238 | variable named 'x' that has not been registered with the appropriate 4239 | symbol_table instance and is not a locally defined variable, once 4240 | compiled the above denoted post compilation error handling code shall 4241 | produce the following output: 4242 | 4243 | Error[00] Pos:17 Type:[Syntax] Msg: ERR184 - Undefined symbol: 'x' 4244 | 4245 | 4246 | For expressions comprised of multiple lines, the error position 4247 | provided in the parser_error object can be converted into a pair of 4248 | line and column numbers by invoking the 'update_error' function as is 4249 | demonstrated by the following example: 4250 | 4251 | if (!parser.compile(program_str,expression)) 4252 | { 4253 | for (std::size_t i = 0; i < parser.error_count(); ++i) 4254 | { 4255 | typedef exprtk::parser_error::type error_t; 4256 | 4257 | error_t error = parser.get_error(i); 4258 | 4259 | exprtk::parser_error::update_error(error,program_str); 4260 | 4261 | printf("Error[%0lu] at line: %lu column: %lu\n", 4262 | i, 4263 | error.line_no, 4264 | error.column_no); 4265 | } 4266 | 4267 | return false; 4268 | } 4269 | 4270 | 4271 | Note32: There are five distinct error modes in ExprTk which denote the 4272 | class of an error. These classes are as follows: 4273 | 4274 | (a) Syntax 4275 | (b) Token 4276 | (c) Numeric 4277 | (d) Symbol Table 4278 | (e) Lexer 4279 | 4280 | 4281 | (a) Syntax Errors 4282 | These are errors related to invalid syntax found within the denoted 4283 | expression. Examples are invalid sequences of operators and variables, 4284 | incorrect number of parameters to functions, invalid conditional or 4285 | loop structures and invalid use of keywords. 4286 | 4287 | eg: 'for := sin(x,y,z) + 2 * equal > until[2 - x,3]' 4288 | 4289 | 4290 | (b) Token Errors 4291 | Errors in this class relate to token level errors detected by one or 4292 | more of the following checkers: 4293 | 4294 | (1) Bracket Checker 4295 | (2) Numeric Checker 4296 | (3) Sequence Checker 4297 | 4298 | 4299 | (c) Numeric Errors 4300 | This class of error is related to conversion of numeric values from 4301 | their string form to the underlying numerical type (float, double 4302 | etc). 4303 | 4304 | (d) Symbol Table Errors 4305 | This is the class of errors related to failures when interacting with 4306 | the registered symbol_table instance. Errors such as not being able to 4307 | find, within the symbol_table, symbols representing variables or 4308 | functions, to being unable to create new variables in the symbol_table 4309 | via the 'unknown symbol resolver' mechanism. 4310 | 4311 | Note33: The function compositor also supports error message handling 4312 | similar to how it is done via the parser. The following demonstrates 4313 | how after a failed function composition the associated errors can be 4314 | enumerated. 4315 | 4316 | typedef exprtk::function_compositor<T> compositor_t; 4317 | typedef typename compositor_t::function function_t; 4318 | 4319 | compositor_t compositor; 4320 | 4321 | const bool compositor_result = 4322 | compositor.add( 4323 | function_t("foobar") 4324 | .vars("x","y") 4325 | .expression 4326 | ( " x + y / z " )); 4327 | 4328 | if (!compositor_result) 4329 | { 4330 | printf("Error: %s\n", compositor.error().c_str()); 4331 | 4332 | for (std::size_t i = 1; i < compositor.error_count(); ++i) 4333 | { 4334 | typedef exprtk::parser_error::type error_t; 4335 | 4336 | error_t error = compositor.get_error(i); 4337 | 4338 | printf("Err No.: %02d Pos: %02d Type: [%14s] Msg: %s\n", 4339 | static_cast<unsigned int>(i), 4340 | static_cast<unsigned int>(error.token.position), 4341 | exprtk::parser_error::to_str(error.mode).c_str(), 4342 | error.diagnostic.c_str()); 4343 | } 4344 | } 4345 | 4346 | 4347 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4348 | 4349 | [SECTION 22 - RUNTIME LIBRARY PACKAGES] 4350 | ExprTk includes a range of extensions, that provide functionalities 4351 | beyond simple numerical calculations. Currently the available packages 4352 | are: 4353 | 4354 | +---+--------------------+-----------------------------------+ 4355 | | # | Package Name | Namespace/Type | 4356 | +---+--------------------+-----------------------------------+ 4357 | | 1 | Basic I/O | exprtk::rtl::io::package<T> | 4358 | | 2 | File I/O | exprtk::rtl::io::file::package<T> | 4359 | | 3 | Vector Operations | exprtk::rtl::vecops::package<T> | 4360 | +---+--------------------+-----------------------------------+ 4361 | 4362 | 4363 | In order to make the features of a specific package available within 4364 | an expression, an instance of the package must be added to the 4365 | expression's associated symbol table. In the following example, the 4366 | file I/O package is made available for the given expression: 4367 | 4368 | typedef exprtk::symbol_table<T> symbol_table_t; 4369 | typedef exprtk::expression<T> expression_t; 4370 | typedef exprtk::parser<T> parser_t; 4371 | 4372 | exprtk::rtl::io::file::package<T> fileio_package; 4373 | 4374 | const std::string expression_string = 4375 | " var file_name := 'file.txt'; " 4376 | " var stream := null; " 4377 | " " 4378 | " stream := open(file_name,'w'); " 4379 | " " 4380 | " write(stream,'Hello world....\n'); " 4381 | " " 4382 | " close(stream); " 4383 | " " 4384 | 4385 | symbol_table_t symbol_table; 4386 | symbol_table.add_package(fileio_package); 4387 | 4388 | expression_t expression; 4389 | expression.register_symbol_table(symbol_table); 4390 | 4391 | parser_t parser; 4392 | parser.compile(expression_string,expression); 4393 | 4394 | expression.value(); 4395 | 4396 | 4397 | (1) Basic I/O functions: 4398 | 4399 | (a) print 4400 | (b) println 4401 | 4402 | (2) File I/O functions: 4403 | 4404 | (a) open (b) close 4405 | (c) write (d) read 4406 | (e) getline (f) eof 4407 | 4408 | (3) Vector Operations functions: 4409 | 4410 | (a) all_true (b) all_false 4411 | (c) any_true (d) any_false 4412 | (e) assign (f) count 4413 | (g) copy (h) reverse 4414 | (i) rotate-left (j) rotate-right 4415 | (k) shift-left (l) shift-right 4416 | (m) sort (n) nth_element 4417 | (o) iota (p) sumk 4418 | (q) axpy (r) axpby 4419 | (s) axpyz (t) axpbyz 4420 | (u) axpbz (v) dot 4421 | (w) dotk (x) diff 4422 | 4423 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4424 | 4425 | [SECTION 23 - HELPERS & UTILS] 4426 | The ExprTk library provides a series of usage simplifications via 4427 | helper routines that combine various processes into a single 'function 4428 | call' making certain actions easier to carry out though not 4429 | necessarily in the most efficient way possible. A list of the routines 4430 | are as follows: 4431 | 4432 | (a) collect_variables 4433 | (b) collect_functions 4434 | (c) compute 4435 | (d) integrate 4436 | (e) derivative 4437 | (f) second_derivative 4438 | (g) third_derivative 4439 | 4440 | 4441 | (a) collect_variables 4442 | This function will collect all the variable symbols in a given string 4443 | representation of an expression and return them in an STL compatible 4444 | sequence data structure (eg: std::vector, dequeue etc) specialised 4445 | upon a std::string type. If an error occurs during the parsing of the 4446 | expression then the return value of the function will be false, 4447 | otherwise it will be true. An example use of the given routine is as 4448 | follows: 4449 | 4450 | const std::string expression = "x + abs(y / z)" 4451 | 4452 | std::vector<std::string> variable_list; 4453 | 4454 | if (exprtk::collect_variables(expression, variable_list)) 4455 | { 4456 | for (const auto& var : variable_list) 4457 | { 4458 | ... 4459 | } 4460 | } 4461 | else 4462 | printf("An error occurred."); 4463 | 4464 | 4465 | (b) collect_functions 4466 | This function will collect all the function symbols in a given string 4467 | representation of an expression and return them in an STL compatible 4468 | sequence data structure (eg: std::vector, dequeue etc) specialised 4469 | upon a std::string type. If an error occurs during the parsing of the 4470 | expression then the return value of the function will be false, 4471 | otherwise it will be true. An example use of the given routine is as 4472 | follows: 4473 | 4474 | const std::string expression = "x + abs(y / cos(1 + z))" 4475 | 4476 | std::deque<std::string> function_list; 4477 | 4478 | if (exprtk::collect_functions(expression, function_list)) 4479 | { 4480 | for (const auto& func : function_list) 4481 | { 4482 | ... 4483 | } 4484 | } 4485 | else 4486 | printf("An error occurred."); 4487 | 4488 | 4489 | Note34: When either the 'collect_variables' or 'collect_functions' 4490 | free functions return true - that does not necessarily indicate the 4491 | expression itself is valid. It is still possible that when compiled 4492 | the expression may have certain 'type' related errors - though it is 4493 | highly likely that no semantic errors will occur if either return 4494 | true. 4495 | 4496 | Note35: The default interface provided for both the collect_variables 4497 | and collect_functions free_functions, assumes that expressions will 4498 | only be utilising the ExprTk reserved functions (eg: abs, cos, min 4499 | etc). When user defined functions are to be used in an expression, a 4500 | symbol_table instance containing said functions can be passed to 4501 | either routine, and will be incorporated during the compilation and 4502 | Dependent Entity Collection processes. In the following example, a 4503 | user defined free function named 'foo' is registered with a 4504 | symbol_table. Finally the symbol_table instance and associated 4505 | expression string are passed to the exprtk::collect_functions routine. 4506 | 4507 | template <typename T> 4508 | T foo(T v) 4509 | { 4510 | return std::abs(v + T(2)) / T(3); 4511 | } 4512 | 4513 | ...... 4514 | 4515 | exprtk::symbol_table<T> sym_tab; 4516 | 4517 | symbol_table.add_function("foo",foo); 4518 | 4519 | const std::string expression = "x + foo(y / cos(1 + z))" 4520 | 4521 | std::deque<std::string> function_list; 4522 | 4523 | if (exprtk::collect_functions(expression, sym_tab, function_list)) 4524 | { 4525 | for (const auto& func : function_list) 4526 | { 4527 | ... 4528 | } 4529 | } 4530 | else 4531 | printf("An error occurred."); 4532 | 4533 | 4534 | (c) compute 4535 | This free function will compute the value of an expression from its 4536 | string form. If an invalid expression is passed, the result of the 4537 | function will be false indicating an error, otherwise the return value 4538 | will be true indicating success. The compute function has three 4539 | overloads, the definitions of which are: 4540 | 4541 | (1) No variables 4542 | (2) One variable called x 4543 | (3) Two variables called x and y 4544 | (3) Three variables called x, y and z 4545 | 4546 | 4547 | Example uses of each of the three overloads for the compute routine 4548 | are as follows: 4549 | 4550 | T result = T(0); 4551 | 4552 | // No variables overload 4553 | const std::string no_vars = "abs(1 - (3 / pi)) * 5" 4554 | 4555 | if (!exprtk::compute(no_vars,result)) 4556 | printf("Failed to compute: %s",no_vars.c_str()); 4557 | else 4558 | printf("Result: %15.5f\n",result); 4559 | 4560 | // One variable 'x' overload 4561 | T x = T(123.456); 4562 | 4563 | const std::string one_var = "abs(x - (3 / pi)) * 5" 4564 | 4565 | if (!exprtk::compute(one_var, x, result)) 4566 | printf("Failed to compute: %s",one_var.c_str()); 4567 | else 4568 | printf("Result: %15.5f\n",result); 4569 | 4570 | // Two variables 'x' and 'y' overload 4571 | T y = T(789.012); 4572 | 4573 | const std::string two_var = "abs(x - (y / pi)) * 5" 4574 | 4575 | if (!exprtk::compute(two_var, x, y, result)) 4576 | printf("Failed to compute: %s",two_var.c_str()); 4577 | else 4578 | printf("Result: %15.5f\n",result); 4579 | 4580 | // Three variables 'x', 'y' and 'z' overload 4581 | T z = T(345.678); 4582 | 4583 | const std::string three_var = "abs(x - (y / pi)) * z" 4584 | 4585 | if (!exprtk::compute(three_var, x, y, z, result)) 4586 | printf("Failed to compute: %s",three_var.c_str()); 4587 | else 4588 | printf("Result: %15.5f\n",result); 4589 | 4590 | 4591 | (d) integrate 4592 | This free function will attempt to perform a numerical integration of 4593 | a single variable compiled expression over a specified range and step 4594 | size. The numerical integration is based on the three point form of 4595 | Simpson's rule. The integrate function has two overloads, where the 4596 | variable of integration can either be passed as a reference or as a 4597 | name in string form. Example usage of the function is as follows: 4598 | 4599 | typedef exprtk::symbol_table<T> symbol_table_t; 4600 | typedef exprtk::expression<T> expression_t; 4601 | typedef exprtk::parser<T> parser_t; 4602 | 4603 | const std::string expression_string = "sqrt(1 - (x^2))" 4604 | 4605 | T x = T(0); 4606 | 4607 | symbol_table_t symbol_table; 4608 | symbol_table.add_variable("x",x); 4609 | 4610 | expression_t expression; 4611 | expression.register_symbol_table(symbol_table); 4612 | 4613 | parser_t parser; 4614 | parser.compile(expression_string,expression); 4615 | 4616 | .... 4617 | 4618 | // Integrate in domain [-1,1] using a reference to x variable 4619 | T area1 = exprtk::integrate(expression, x, T(-1), T(1)); 4620 | 4621 | // Integrate in domain [-1,1] using name of x variable 4622 | T area2 = exprtk::integrate(expression, "x", T(-1), T(1)); 4623 | 4624 | 4625 | (e) derivative 4626 | This free function will attempt to perform a numerical differentiation 4627 | of a single variable compiled expression at a given point for a given 4628 | epsilon, using a variant of Newton's difference quotient called the 4629 | five-point stencil method. The derivative function has two overloads, 4630 | where the variable of differentiation can either be passed as a 4631 | reference or as a name in string form. Example usage of the derivative 4632 | function is as follows: 4633 | 4634 | typedef exprtk::symbol_table<T> symbol_table_t; 4635 | typedef exprtk::expression<T> expression_t; 4636 | typedef exprtk::parser<T> parser_t; 4637 | 4638 | const std::string expression_string = "sqrt(1 - (x^2))" 4639 | 4640 | T x = T(0); 4641 | 4642 | symbol_table_t symbol_table; 4643 | symbol_table.add_variable("x",x); 4644 | 4645 | expression_t expression; 4646 | expression.register_symbol_table(symbol_table); 4647 | 4648 | parser_t parser; 4649 | parser.compile(expression_string,expression); 4650 | 4651 | .... 4652 | 4653 | // Differentiate expression at value of x = 12.3 using a reference 4654 | // to the x variable 4655 | x = T(12.3); 4656 | T derivative1 = exprtk::derivative(expression, x); 4657 | 4658 | // Differentiate expression where value x = 45.6 using name 4659 | // of the x variable 4660 | x = T(45.6); 4661 | T derivative2 = exprtk::derivative(expression, "x"); 4662 | 4663 | 4664 | (f) second_derivative 4665 | This free function will attempt to perform a numerical second 4666 | derivative of a single variable compiled expression at a given point 4667 | for a given epsilon, using a variant of Newton's difference quotient 4668 | method. The second_derivative function has two overloads, where the 4669 | variable of differentiation can either be passed as a reference or as 4670 | a name in string form. Example usage of the second_derivative function 4671 | is as follows: 4672 | 4673 | typedef exprtk::symbol_table<T> symbol_table_t; 4674 | typedef exprtk::expression<T> expression_t; 4675 | typedef exprtk::parser<T> parser_t; 4676 | 4677 | const std::string expression_string = "sqrt(1 - (x^2))" 4678 | 4679 | T x = T(0); 4680 | 4681 | symbol_table_t symbol_table; 4682 | symbol_table.add_variable("x",x); 4683 | 4684 | expression_t expression; 4685 | expression.register_symbol_table(symbol_table); 4686 | 4687 | parser_t parser; 4688 | parser.compile(expression_string,expression); 4689 | 4690 | .... 4691 | 4692 | // Second derivative of expression where value of x = 12.3 using a 4693 | // reference to x variable 4694 | x = T(12.3); 4695 | T derivative1 = exprtk::second_derivative(expression,x); 4696 | 4697 | // Second derivative of expression where value of x = 45.6 using 4698 | // name of x variable 4699 | x = T(45.6); 4700 | T derivative2 = exprtk::second_derivative(expression, "x"); 4701 | 4702 | 4703 | (g) third_derivative 4704 | This free function will attempt to perform a numerical third 4705 | derivative of a single variable compiled expression at a given point 4706 | for a given epsilon, using a variant of Newton's difference quotient 4707 | method. The third_derivative function has two overloads, where the 4708 | variable of differentiation can either be passed as a reference or as 4709 | a name in string form. Example usage of the third_derivative function 4710 | is as follows: 4711 | 4712 | typedef exprtk::symbol_table<T> symbol_table_t; 4713 | typedef exprtk::expression<T> expression_t; 4714 | typedef exprtk::parser<T> parser_t; 4715 | 4716 | const std::string expression_string = "sqrt(1 - (x^2))" 4717 | 4718 | T x = T(0); 4719 | 4720 | symbol_table_t symbol_table; 4721 | symbol_table.add_variable("x",x); 4722 | 4723 | expression_t expression; 4724 | expression.register_symbol_table(symbol_table); 4725 | 4726 | parser_t parser; 4727 | parser.compile(expression_string,expression); 4728 | 4729 | .... 4730 | 4731 | // Third derivative of expression where value of x = 12.3 using a 4732 | // reference to the x variable 4733 | x = T(12.3); 4734 | T derivative1 = exprtk::third_derivative(expression, x); 4735 | 4736 | // Third derivative of expression where value of x = 45.6 using 4737 | // name of the x variable 4738 | x = T(45.6); 4739 | T derivative2 = exprtk::third_derivative(expression, "x"); 4740 | 4741 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4742 | 4743 | [SECTION 24 - RUNTIME CHECKS] 4744 | The ExprTk library provides the ability to perform runtime checks 4745 | during expression evaluation so as to ensure memory access violations 4746 | errors are caught and handled without causing further issues. The 4747 | checks typically cover: 4748 | 4749 | 1. Vector access and handling 4750 | 2. String access and handling 4751 | 3. Loop iteration checks 4752 | 4. Compilation checkpointing 4753 | 5. Assert statements 4754 | 4755 | 4756 | (1) Vector Access Runtime Checks 4757 | Expressions that contain vectors where elements of the vectors may be 4758 | accessed using indexes that can only be determined at runtime may 4759 | result in memory access violations when the index is out of the 4760 | vector's bound. Some examples of problematic expressions are as 4761 | follows: 4762 | 4763 | 1. vec[i] 4764 | 2. vec[i + j] 4765 | 3. vec[i + 10] 4766 | 4. vec[i + vec[]] := x + y 4767 | 5. vec[i + j] <=> vec[i] 4768 | 6. vec[i + j] := (vec1 + vec2)[i + j] 4769 | 4770 | 4771 | In the above expressions, it is assumed that the values used in the 4772 | index operator may either exceed the vector bounds or precede the 4773 | vector's start, In short, the indexes may not necessarily be within 4774 | the range [0,vec[]). 4775 | 4776 | ExprTk provides the ability to inject a runtime check at the point of 4777 | index evaluation and handle situations where the index violates the 4778 | vector's bounds. This capability is done by registering a user- 4779 | implemented Vector Access Runtime Check (VARTC) to the parser before 4780 | expression compilation. Initially a VARTC can be defined as follows: 4781 | 4782 | struct my_vector_access_rtc final : 4783 | public exprtk::vector_access_runtime_check 4784 | { 4785 | bool handle_runtime_violation(violation_context& context) 4786 | override 4787 | { 4788 | // Handling of the violation 4789 | return ...; 4790 | } 4791 | }; 4792 | 4793 | 4794 | Then an instance of the VARTC can be registered with a parser instance 4795 | as follows: 4796 | 4797 | my_vector_access_rtc vartc; 4798 | 4799 | exprtk::symbol_table<T> symbol_table; 4800 | 4801 | T i; 4802 | T x; 4803 | T y; 4804 | std::vector<T> vec = { 0, 1, 2, 3, 4 }; 4805 | 4806 | symbol_table.add_variable("i" , i ); 4807 | symbol_table.add_variable("x" , x ); 4808 | symbol_table.add_variable("y" , y ); 4809 | symbol_table.add_vector ("vec", vec); 4810 | 4811 | exprtk::expression<T> expression; 4812 | exprtk::parser<T> parser; 4813 | 4814 | parser.register_vector_access_runtime_check(vartc); 4815 | 4816 | std::string expression = "vec[i + vec[]] := x + y" 4817 | 4818 | parser.compile(expression_str, expression); 4819 | 4820 | try 4821 | { 4822 | expression.value(); 4823 | } 4824 | catch (std::runtime_error& rte) 4825 | { 4826 | printf("Exception: %s\n", rte.what()); 4827 | } 4828 | 4829 | 4830 | Note36: The lifetime of any parser or expression instance must not 4831 | exceed that of any VARTC instance that has been registered with it. 4832 | 4833 | When a vector access violation occurs, the registered VARTC instance's 4834 | handle_runtime_violation method will be invoked, coupled with it a 4835 | violation_context shall be provided that will contain the following 4836 | members: 4837 | 4838 | 1. base_ptr: Of type void*, which points to the first element 4839 | of the vector. The base_ptr can also be used as a key to 4840 | determine the vector upon which the access violation has 4841 | occurred. 4842 | 4843 | 2. end_ptr : Of type void*, which points to one position after 4844 | the last element of the vector 4845 | 4846 | 3. access_ptr: Of type void*, points to the memory location 4847 | which is the base_ptr offset by the derived index value. 4848 | 4849 | 4. type_size: Size of the vector's element type in bytes. This 4850 | value can be used to determine the number of elements in 4851 | the vector based on the base_ptr and end_ptr. 4852 | 4853 | 4854 | The implementation of the handle_runtime_violation method can at this 4855 | point perform various actions such as: 4856 | 4857 | 1. Log the violation 4858 | 2. Throw an exception (eg: std::runtime_error) 4859 | 3. Remedy the access_ptr to allow for the evaluation to continue 4860 | 4861 | 4862 | Note37: When employing option [3], handle_runtime_violation needs to 4863 | return true, otherwise the caller will assume an unhandled access 4864 | violation and default to using the base_ptr. 4865 | 4866 | It is recommended, at the very least, to throw an exception when 4867 | handling vector access violations and to only consider option [3] when 4868 | the the ramifications of changing the access_ptr are well understood. 4869 | 4870 | The following are simple examples of how the handle_runtime_violation 4871 | can be implemented. 4872 | 4873 | Example 1: Log the access violation to stdout and then throw a runtime 4874 | error exception: 4875 | 4876 | bool handle_runtime_violation(violation_context& context) override 4877 | { 4878 | printf("ERROR - Runtime vector access violation. " 4879 | "base: %p end: %p access: %p typesize: %lu\n", 4880 | context.base_ptr , 4881 | context.end_ptr , 4882 | context.access_ptr, 4883 | context.type_size); 4884 | 4885 | throw std::runtime_error("Runtime vector access violation."); 4886 | return false; 4887 | } 4888 | 4889 | 4890 | Example 2: Handle the access violation by resetting the access pointer 4891 | to the last value in the vector. 4892 | 4893 | bool handle_runtime_violation(violation_context& context) override 4894 | { 4895 | context.access_ptr = 4896 | static_cast<char*>(context.end_ptr) - context.type_size; 4897 | return true; 4898 | } 4899 | 4900 | 4901 | Note38: The return value of true in the above handler method signals 4902 | the caller to continue the vector access using the updated access_ptr. 4903 | 4904 | 4905 | (2) String Access Runtime Checks 4906 | Expressions that contain strings where elements or substrings of the 4907 | strings may be accessed using indexes that can only be determined at 4908 | runtime may result in memory access violations when the index or range 4909 | is out of the string's bound. Examples of problematic expressions are 4910 | as follows: 4911 | 4912 | 1. s[i : j + k] 4913 | 2. s[i : j + k][-x : y] 4914 | 3. (s1 + s2)[i : j + k] 4915 | 4. '01234'[5 + i] 4916 | 5. s += s[i : j + k] 4917 | 6. s[i : j + k] := 'chappy days'[1 : ] 4918 | 4919 | 4920 | To enable string access runtime checks all one needs to do is simply 4921 | use the following define before the ExprTk header is included or as 4922 | part of the compilation define parameters: 4923 | 4924 | exprtk_enable_range_runtime_checks 4925 | 4926 | 4927 | When the above define is used, and a string related runtime access 4928 | violation occurs a std::runtime_error exception will be thrown. The 4929 | following demonstrates the general flow of handling the access 4930 | violation: 4931 | 4932 | parser.compile(expression_string, expression) 4933 | . 4934 | . 4935 | try 4936 | { 4937 | expression.value(); 4938 | } 4939 | catch (std::runtime_error& rte) 4940 | { 4941 | printf("Exception: %s\n", rte.what()); 4942 | } 4943 | 4944 | 4945 | (3) Loop Iteration Checks 4946 | Expressions that contain loop structures (eg: for/while/repeat et al) 4947 | can be problematic from a usage point of view due to the difficulty in 4948 | determining the following: 4949 | 4950 | 1. Will the loop ever complete (aka is this an infinite loop?) 4951 | 2. Maximum loop execution time 4952 | 4953 | 4954 | ExprTk provides the ability to inject a runtime check within loop 4955 | conditionals, and to have the result of the check either signal the 4956 | loop to continue or for the check to raise a loop violation error. 4957 | 4958 | The process involves instantiating a user defined loop_runtime_check 4959 | (LRTC), registering the instance with a exprtk::parser instance and 4960 | specifying which loop types the check is to performed upon. The 4961 | following code demonstrates a how custom LRTC can be instantiated and 4962 | registered with the associated parser: 4963 | 4964 | typedef exprtk::parser<T> parser_t; 4965 | typedef exprtk::loop_runtime_check loop_runtime_check_t; 4966 | 4967 | my_loop_rtc loop_rtc; 4968 | loop_runtime_check.loop_set = loop_runtime_check_t::e_all_loops; 4969 | loop_runtime_check.max_loop_iterations = 100000; 4970 | 4971 | parser_t parser; 4972 | 4973 | parser.register_loop_runtime_check(loop_rtc); 4974 | 4975 | 4976 | The following is an example of how one could derive from and implement 4977 | a custom loop_runtime_check: 4978 | 4979 | struct my_loop_rtc final : exprtk::loop_runtime_check 4980 | { 4981 | 4982 | bool check() override 4983 | { 4984 | // 4985 | return ... 4986 | } 4987 | 4988 | void handle_runtime_violation 4989 | (const exprtk::violation_context&) override 4990 | { 4991 | throw std::runtime_error("Loop runtime violation."); 4992 | } 4993 | }; 4994 | 4995 | 4996 | In the above code, if either the check method returns false or the 4997 | loop iteration count exceeds the max_loop_iterations value, the 4998 | handle_runtime_violation method will be invoked, coupled with it a 4999 | violation_context shall be provided that will contain the following 5000 | members: 5001 | 5002 | 1. loop: Of type loop_types. This value denotes the type of 5003 | loop that triggered the violation (e_for_loop, e_while_loop, 5004 | e_repeat_until_loop). 5005 | 5006 | 2. violation: Of type type. This value denotes the type of 5007 | violation (e_iteration_count, e_timeout) 5008 | 5009 | 3. iteration_count: Of type uint64_t. The number of iterations 5010 | that the triggering loop has executed since the start of the 5011 | expression. 5012 | 5013 | 5014 | Note39: The lifetime of any parser or expression instance must not 5015 | exceed that of any LRTC instance that has been registered with it. 5016 | 5017 | The following is an example implementation of an LRTC that 5018 | supports loop timeout violations: 5019 | 5020 | struct timeout_loop_rtc final : exprtk::loop_runtime_check 5021 | { 5022 | using time_point_t = 5023 | std::chrono::time_point<std::chrono::steady_clock>; 5024 | 5025 | std::size_t iterations_ = 0; 5026 | time_point_t timeout_tp_; 5027 | 5028 | bool check() override 5029 | { 5030 | if (std::chrono::steady_clock::now() >= timeout_tp_) 5031 | { 5032 | // handle_runtime_violation shall be invoked 5033 | return false; 5034 | } 5035 | 5036 | return true; 5037 | } 5038 | 5039 | void handle_runtime_violation 5040 | (const exprtk::violation_context&) override 5041 | { 5042 | throw std::runtime_error("Loop timed out"); 5043 | } 5044 | 5045 | void set_timeout_time(const time_point_t& timeout_tp) 5046 | { 5047 | timeout_tp_ = timeout_tp; 5048 | } 5049 | }; 5050 | 5051 | 5052 | In the above code, the check method shall be invoked on each iteration 5053 | of the associated loop. Within the method the current time is compared 5054 | to the setup timeout time-point, in the event the current time exceeds 5055 | the timeout, the method returns false, triggering the violation, which 5056 | in turn will result in the handle_runtime_violation being invoked. 5057 | 5058 | The following code demonstrates how the above defined LRTC can be used 5059 | to ensure that at the very least the loop portion(s) of an expression 5060 | will never exceed a given amount of execution time. 5061 | 5062 | typedef exprtk::parser<T> parser_t; 5063 | typedef exprtk::loop_runtime_check loop_runtime_check_t; 5064 | 5065 | my_loop_rtc loop_rtc; 5066 | loop_rtc.loop_set = loop_runtime_check_t::e_all_loops; 5067 | loop_rtc.max_loop_iterations = 100000; 5068 | 5069 | parser_t parser; 5070 | 5071 | parser.register_loop_runtime_check(loop_rtc); 5072 | . 5073 | . 5074 | . 5075 | . 5076 | using std::chrono; 5077 | const auto max_duration = seconds(25); 5078 | 5079 | try 5080 | { 5081 | loop_rtc.set_timeout_time(steady_clock::now() + max_duration); 5082 | expression.value(); 5083 | 5084 | loop_rtc.set_timeout_time(steady_clock::now() + max_duration); 5085 | expression.value(); 5086 | 5087 | loop_rtc.set_timeout_time(steady_clock::now() + max_duration); 5088 | expression.value(); 5089 | 5090 | } 5091 | catch(std::runtime_error& exception) 5092 | { 5093 | printf("Exception: %s\n",exception.what()); 5094 | } 5095 | 5096 | 5097 | (4) Compilation Process Checkpointing 5098 | When compiling an expression, one may require the compilation process 5099 | to periodically checkpoint its internal state, subsequently at the 5100 | checkpoint one can then make the decision to continue the compilation 5101 | process or to immediately terminate and return. 5102 | 5103 | The following are reasons one may want to checkpoint the compilation 5104 | process: 5105 | 5106 | 1. Determine if the compilation process has run for far too long 5107 | 2. Determine if the current stack frame size exceeds a limit 5108 | 3. Enforce an external termination request 5109 | 5110 | 5111 | ExprTk provides the ability to inject a checkpoint into the 5112 | compilation process that will be evaluated periodically. This 5113 | capability is achieved by registering a user-implemented compilation 5114 | check (CCK) to the parser before expression compilation. Initially a 5115 | CCK can be defined as follows: 5116 | 5117 | struct compilation_timeout_check final : 5118 | public exprtk::compilation_check 5119 | { 5120 | bool continue_compilation(compilation_context& context) 5121 | override 5122 | { 5123 | // Determine if compilation should continue 5124 | return ...; 5125 | } 5126 | }; 5127 | 5128 | 5129 | An example checkpoint use-case could be that we do not want the 5130 | compilation process to take longer than a maximum defined period, eg: 5131 | five seconds. The associated compilation check implementation could be 5132 | as follows: 5133 | 5134 | struct my_compilation_timeout_check final : 5135 | public exprtk::compilation_check 5136 | { 5137 | 5138 | bool continue_compilation(compilation_context& context) 5139 | override 5140 | { 5141 | static constexpr std::size_t max_iters_per_check = 1000; 5142 | 5143 | if (++iterations_ >= max_iters_per_check) 5144 | { 5145 | if (std::chrono::steady_clock::now() >= timeout_tp_) 5146 | { 5147 | context.error_message = "Compilation has timed-out" 5148 | return false; 5149 | } 5150 | 5151 | iterations_ = 0; 5152 | } 5153 | 5154 | return true; 5155 | } 5156 | 5157 | using time_point_t = std::chrono::time_point<std::chrono::steady_clock>; 5158 | 5159 | void set_timeout_time(const time_point_t& timeout_tp) 5160 | { 5161 | timeout_tp_ = timeout_tp; 5162 | } 5163 | 5164 | std::size_t iterations_ = 0; 5165 | time_point_t timeout_tp_; 5166 | }; 5167 | 5168 | 5169 | Usage of the above defined compilation check will require registering 5170 | the check with the parser, setting up the expiry time and then 5171 | proceeding to compile the expression. The following is a general 5172 | outline of what will be needed: 5173 | 5174 | typedef exprtk::expression<T> expression_t; 5175 | typedef exprtk::parser<T> parser_t; 5176 | 5177 | expression_t expression; 5178 | 5179 | my_compilation_timeout_check compilation_timeout_check; 5180 | 5181 | parser_t parser; 5182 | parser. 5183 | register_compilation_timeout_check(compilation_timeout_check); 5184 | 5185 | const auto max_duration = std::chrono::seconds(5); 5186 | const auto timeout_tp = 5187 | std::chrono::steady_clock::now() + max_duration; 5188 | 5189 | compilation_timeout_check.set_timeout_time(timeout_tp); 5190 | 5191 | if (!parser.compile(large_expression_string, expression)) 5192 | { 5193 | printf("Error: %s\t\n", parser.error().c_str()); 5194 | return; 5195 | } 5196 | 5197 | 5198 | (5) Assert statements 5199 | ExprTk supports the use of assert statements to verify pre and post 5200 | conditions during the evaluation of expressions. The assert statements 5201 | are only active when a user defined assert handler is registered with 5202 | the parser before expression compilation, otherwise they are compiled 5203 | out, this is similar to how asserts are included/excluded in C++ 5204 | coupled with the definition of NDEBUG. The assert syntax has three 5205 | variations as described below: 5206 | 5207 | assert(x + y > i); 5208 | assert(x + y > i, 'assert statement 1'); 5209 | assert(x + y > i, 'assert statement 1', 'ASSERT01'); 5210 | 5211 | 5212 | The three assert statement input parameters are as follows: 5213 | 5214 | 1. assert condition (mandatory) 5215 | 2. assert message (optional) 5216 | 3. assert id (optional) 5217 | 5218 | 5219 | The assert condition is essentially a boolean statement that is 5220 | expected to be true during evaluation. The other two parameters of 5221 | assert message and ID are string values that are intended to provide 5222 | feedback to the handler and to ensure the uniqueness of assert 5223 | statement respectively. The three parameters denoted above and the 5224 | offset of the assert statement from the beginning of the expression 5225 | are placed inside assert_context that is provided as part of the 5226 | assert_check handler. A user defined assert_check handler can be 5227 | defined as follows: 5228 | 5229 | struct my_assert_handler final : public exprtk::assert_check 5230 | { 5231 | void handle_assert(const assert_context& ctxt) override 5232 | { 5233 | printf("condition: [%s] \n", ctxt.condition.c_str()); 5234 | printf("message: [%s] \n", ctxt.message .c_str()); 5235 | printf("id: [%s] \n", ctxt.id .c_str()); 5236 | printf("offset: [%lu]\n", ctxt.offet ); 5237 | // throw std::runtime_error(.....); 5238 | } 5239 | }; 5240 | 5241 | 5242 | Once the assert_check handler has been registered with the parser, 5243 | expressions that contain assert statements will have their asserts 5244 | compiled in as part final evaluable expression instance: 5245 | 5246 | typedef exprtk::symbol_table<T> symbol_table_t; 5247 | typedef exprtk::expression<T> expression_t; 5248 | typedef exprtk::parser<T> parser_t; 5249 | 5250 | const std::string program = 5251 | " var x := 4; " 5252 | " " 5253 | " for (var i := 0; i < 10; i += 1) " 5254 | " { " 5255 | " assert(i < x, 'assert statement 1'); " 5256 | " } " 5257 | 5258 | my_assert_handler handler; 5259 | 5260 | expression_t expression; 5261 | parser_t parser; 5262 | 5263 | parser.register_assert_check(handler); 5264 | parser.compile(program, expression); 5265 | 5266 | 5267 | (6) Runtime Check Overheads 5268 | All of the above mentioned runtime checks will incur an execution time 5269 | overhead during the evaluation of expressions. This is an unfortunate 5270 | but necessary side-effect of the process when runtime safety is of 5271 | concern. 5272 | 5273 | A recommendation to consider, that is not demonstrated above, is that 5274 | in the check method of the LRTC, one should not evaluate the timeout 5275 | condition on every call to check (aka on every loop iteration). 5276 | Instead a counter should be maintained and incremented on each call 5277 | and when the counter exceeds some predefined amount (eg: 10000 5278 | iterations), then the timeout based check can be preformed. The 5279 | reasoning here is that incrementing an integer should be far less 5280 | expensive than computing the current "now" time-point. 5281 | 5282 | 5283 | (7) Runtime Check Limitations 5284 | The available RTC mechanisms in ExprTk are limited to implementing 5285 | said checks only within ExprTk based syntax sections of an expression. 5286 | The RTCs will not be active within user defined functions, or 5287 | composited functions that have been compiled with parser instances 5288 | that don't have the same set of RTC configurations enabled. 5289 | 5290 | 5291 | (8) Runtime Handlers 5292 | When implementing stateful run-time check handlers one must be careful 5293 | to ensure the handler is setup correctly or reset between calls to the 5294 | expression::value or parser::compile methods. 5295 | 5296 | The following example code utilises the compilation timeout RTC and 5297 | expression loop duration RTC examples from above to demonstrate the 5298 | need to reset the internal state of the various handlers before 5299 | compilation and valuation processes are invoked, as not doing so will 5300 | affect the ability for the next expression in the list to either be 5301 | correctly compiled or evaluated due to the potential of erroneous 5302 | timeouts occurring. 5303 | 5304 | typedef exprtk::expression<T> expression_t; 5305 | typedef exprtk::parser<T> parser_t; 5306 | 5307 | my_compilation_timeout_check compilation_timeout_check; 5308 | 5309 | my_loop_rtc loop_rtc; 5310 | loop_rtc.loop_set = loop_runtime_check_t::e_all_loops; 5311 | loop_rtc.max_loop_iterations = 100000; 5312 | 5313 | parser_t parser; 5314 | parser.register_loop_runtime_check(loop_rtc); 5315 | parser. 5316 | register_compilation_timeout_check(compilation_timeout_check); 5317 | 5318 | const auto compile_timeout_tp = []() 5319 | { 5320 | const auto max_duration = std::chrono::seconds(5); 5321 | return std::chrono::steady_clock::now() + max_duration; 5322 | }; 5323 | 5324 | const auto loop_timeout_tp = []() 5325 | { 5326 | const auto max_duration = std::chrono::seconds(10); 5327 | return std::chrono::steady_clock::now() + max_duration; 5328 | }; 5329 | 5330 | const std::vector<std::string> expressions = 5331 | { 5332 | "x + y / 2", 5333 | "sin(x) / cos(y) + 1", 5334 | "clamp(-1, sin(2 * pi * x) + cos(y / 2 * pi), +1)" 5335 | }; 5336 | 5337 | for (const auto& expr_str : expressions) 5338 | { 5339 | // Reset the timeout for the compilation RTC 5340 | compilation_timeout_check 5341 | .set_timeout_time(compile_timeout_tp()); 5342 | 5343 | expression_t expression; 5344 | 5345 | if (!parser.compile(large_expression_string, expression)) 5346 | { 5347 | printf("Error: %s\t\n", parser.error().c_str()); 5348 | continue; 5349 | } 5350 | 5351 | try 5352 | { 5353 | // Reset the timeout for the loop duration RTC 5354 | loop_rtc.set_timeout_time(loop_timeout_tp()); 5355 | 5356 | expression.value(); 5357 | } 5358 | catch(std::runtime_error& exception) 5359 | { 5360 | printf("Exception: %s\n Expression: %s\n", 5361 | exception.what(), 5362 | expr_str.c_str()); 5363 | } 5364 | } 5365 | 5366 | 5367 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5368 | 5369 | [SECTION 25 - BENCHMARKING] 5370 | As part of the ExprTk package there is an expression benchmark utility 5371 | named 'exprtk_benchmark'. The utility attempts to determine expression 5372 | evaluation speed (or rate of evaluations - evals per second), by 5373 | evaluating each expression numerous times and mutating the underlying 5374 | variables of the expression between each evaluation. The utility 5375 | assumes any valid ExprTk expression (containing conditionals, loops 5376 | etc), however it will only make use of a predefined set of scalar 5377 | variables, namely: a, b, c, x, y, z and w. That being said expressions 5378 | themselves can contain any number of local variables, vectors or 5379 | strings. There are two modes of operation: 5380 | 5381 | (1) Default 5382 | (2) User Specified Expressions 5383 | 5384 | 5385 | (1) Default 5386 | The default mode is enabled simply by executing the exprtk_benchmark 5387 | binary with no command line parameters. In this mode a predefined set 5388 | of expressions will be evaluated in three phases: 5389 | 5390 | (a) ExprTk evaluation 5391 | (b) Native evaluation 5392 | (c) ExprTk parse 5393 | 5394 | 5395 | In the first two phases (a and b) a list of predefined (hard-coded) 5396 | expressions will be evaluated using both ExprTk and native mode 5397 | implementations. This is done so as to compare evaluation times 5398 | between ExprTk and native implementations. The set of expressions used 5399 | are as follows: 5400 | 5401 | (01) (y + x) 5402 | (02) 2 * (y + x) 5403 | (03) (2 * y + 2 * x) 5404 | (04) ((1.23 * x^2) / y) - 123.123 5405 | (05) (y + x / y) * (x - y / x) 5406 | (06) x / ((x + y) + (x - y)) / y 5407 | (07) 1 - ((x * y) + (y / x)) - 3 5408 | (08) (5.5 + x) + (2 * x - 2 / 3 * y) * (x / 3 + y / 4) + (y + 7.7) 5409 | (09) 1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^15 - 5.5x^23 + 6.6y^55 5410 | (10) sin(2 * x) + cos(pi / y) 5411 | (11) 1 - sin(2 * x) + cos(pi / y) 5412 | (12) sqrt(111.111 - sin(2 * x) + cos(pi / y) / 333.333) 5413 | (13) (x^2 / sin(2 * pi / y)) - x / 2 5414 | (14) x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y 5415 | (15) clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0) 5416 | (16) max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11)) 5417 | (17) if((y + (x * 2.2)) <= (x + y + 1.1), x - y, x*y) + 2 * pi / x 5418 | 5419 | 5420 | The third and final phase (c), is used to determine average 5421 | compilation rates (compiles per second) for expressions of varying 5422 | complexity. Each expression is compiled 100K times and the average for 5423 | each expression is output. 5424 | 5425 | 5426 | (2) User Specified Expressions 5427 | In this mode two parameters are passed to the utility via the command 5428 | line: 5429 | 5430 | (a) A name of a text file containing one expression per line 5431 | (b) An integer representing the number of evaluations per expression 5432 | 5433 | 5434 | An example execution of the benchmark utility in this mode is as 5435 | follows: 5436 | 5437 | ./exprtk_benchmark my_expressions.txt 1000000 5438 | 5439 | 5440 | The above invocation will load the expressions from the file 5441 | 'my_expressions.txt' and will then proceed to evaluate each expression 5442 | one million times, varying the above mentioned variables (x, y, z 5443 | etc.) between each evaluation, and at the end of each expression round 5444 | a print out of running times, result of a single evaluation and total 5445 | sum of results is provided as demonstrated below: 5446 | 5447 | Expression 1 of 7 4.770 ns 47700 ns ( 9370368.0) '((((x+y)+z)))' 5448 | Expression 2 of 7 4.750 ns 47500 ns ( 1123455.9) '((((x+y)-z)))' 5449 | Expression 3 of 7 4.766 ns 47659 ns (21635410.7) '((((x+y)*z)))' 5450 | Expression 4 of 7 5.662 ns 56619 ns ( 1272454.9) '((((x+y)/z)))' 5451 | Expression 5 of 7 4.950 ns 49500 ns ( 4123455.9) '((((x-y)+z)))' 5452 | Expression 6 of 7 7.581 ns 75810 ns (-4123455.9) '((((x-y)-z)))' 5453 | Expression 7 of 7 4.801 ns 48010 ns ( 0.0) '((((x-y)*z)))' 5454 | 5455 | 5456 | The benchmark utility can be very useful when investigating evaluation 5457 | efficiency issues with ExprTk or simply during the prototyping of 5458 | expressions. As an example, lets take the following expression: 5459 | 5460 | 1 / sqrt(2x) * e^(3y) 5461 | 5462 | 5463 | Lets say we would like to determine which sub-part of the expression 5464 | takes the most time to evaluate and perhaps attempt to rework the 5465 | expression based on the results. In order to do this we will create a 5466 | text file called 'test.txt' and then proceed to make some educated 5467 | guesses about how to break the expression up into its more 5468 | 'interesting' sub-parts which we will then add as one expression per 5469 | line to the file. An example breakdown may be as follows: 5470 | 5471 | 1 / sqrt(2x) * e^(3y) 5472 | 1 / sqrt(2x) 5473 | e^(3y) 5474 | 5475 | 5476 | The benchmark with the given file, where each expression will be 5477 | evaluated 100K times can be executed as follows: 5478 | 5479 | ./exprtk_benchmark test.txt 100000 5480 | Expr 1 of 3 90.340 ns 9034000 ns (296417859.3) '1/sqrt(2x)*e^(3y)' 5481 | Expr 2 of 3 11.100 ns 1109999 ns ( 44267.3) '1/sqrt(2x)' 5482 | Expr 3 of 3 77.830 ns 7783000 ns (615985286.6) 'e^(3y)' 5483 | [*] Number Of Evals: 300000 5484 | [*] Total Time: 0.018sec 5485 | [*] Total Single Eval Time: 0.000ms 5486 | 5487 | 5488 | From the results above we conclude that the third expression (e^(3y)) 5489 | consumes the largest amount of time. The variable 'e', as used in both 5490 | the benchmark and in the expression, is an approximation of the 5491 | transcendental mathematical constant e (2.71828182845904...) hence the 5492 | sub-expression should perhaps be modified to use the generally more 5493 | efficient built-in 'exp' function. 5494 | 5495 | ./exprtk_benchmark test.txt 1000000 5496 | Expr 1 of 5 86.563 ns 8656300ns (296417859.6) '1/sqrt(2x)*e^(3y)' 5497 | Expr 2 of 5 40.506 ns 4050600ns (296417859.6) '1/sqrt(2x)*exp(3y)' 5498 | Expr 3 of 5 14.248 ns 1424799ns ( 44267.2) '1/sqrt(2x)' 5499 | Expr 4 of 5 88.840 ns 8884000ns (615985286.9) 'e^(3y)' 5500 | Expr 5 of 5 29.267 ns 2926699ns (615985286.9) 'exp(3y)' 5501 | [*] Number Of Evals: 5000000 5502 | [*] Total Time: 0.260sec 5503 | [*] Total Single Eval Time: 0.000ms 5504 | 5505 | 5506 | The above output demonstrates the results from making the previously 5507 | mentioned modification to the expression. As can be seen the new form 5508 | of the expression using the 'exp' function reduces the evaluation time 5509 | by over 50%, in other words increases the evaluation rate by two fold. 5510 | 5511 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5512 | 5513 | [SECTION 26 - EXPRTK NOTES] 5514 | The following is a list of facts and suggestions one may want to take 5515 | into account when using ExprTk: 5516 | 5517 | (00) Precision and performance of expression evaluations are the 5518 | dominant principles of the ExprTk library. 5519 | 5520 | (01) ExprTk uses a rudimentary imperative programming model with 5521 | syntax based on languages such as Pascal and C. Furthermore 5522 | ExprTk is an LL(2) type grammar and is processed using a 5523 | recursive descent parsing algorithm. 5524 | 5525 | (02) Supported types are float, double, long double and MPFR/GMP. 5526 | Generally any user defined numerical type that supports all the 5527 | basic floating point arithmetic operations: -,+,*,/,^,% etc; 5528 | unary and binary operations: sin,cos,min,max,equal etc and any 5529 | other ExprTk dependent operations can be used to specialise the 5530 | various components: expression, parser and symbol_table. 5531 | 5532 | (03) Standard arithmetic operator precedence is applied (BEDMAS). In 5533 | general C, Pascal or Rust equivalent unary, binary, logical and 5534 | equality/inequality operator precedence rules apply. 5535 | eg: a == b and c > d + 1 ---> (a == b) and (c > (d + 1)) 5536 | x - y <= z / 2 ---> (x - y) <= (z / 2) 5537 | a - b / c * d^2^3 ---> a - ((b / c) * d^(2^3)) 5538 | 5539 | (04) Results of expressions that are deemed as being 'valid' are to 5540 | exist within the set of Real numbers. All other results will be 5541 | of the value: Not-A-Number (NaN). However this may not 5542 | necessarily be a requirement for user defined numerical types, 5543 | eg: complex number type. 5544 | 5545 | (05) Supported user defined types are numeric and string 5546 | variables, numeric vectors and functions. 5547 | 5548 | (06) All reserved words, keywords, variable, vector, string and 5549 | function names are case-insensitive. 5550 | 5551 | (07) Variable, vector, string variable and function names must begin 5552 | with a letter (A-Z or a-z), then can be comprised of any 5553 | combination of letters, digits, underscores and dots, ending in 5554 | either a letter (A-Z or a-z), digit or underscore. (eg: x, y2, 5555 | var1, power_func99, person.age, item.size.0). The associated 5556 | regex pattern is: [a-zA-Z][a-zA-Z0-9_.]*[a-zA-Z0-9_]+ 5557 | 5558 | (08) Expression lengths and sub-expression lists are limited only by 5559 | storage capacity. 5560 | 5561 | (09) The life-time of objects registered with or created from a 5562 | specific symbol-table must span at least the lifetime of the 5563 | symbol table instance and all compiled expressions which 5564 | utilise objects, such as variables, strings, vectors, function 5565 | compositor functions and functions of that symbol-table, 5566 | otherwise the result will be undefined behaviour. 5567 | 5568 | (10) Equal and not_equal are normalised-epsilon equality routines, 5569 | which use epsilons of 0.0000000001 and 0.000001 for double and 5570 | float types respectively. 5571 | 5572 | (11) All trigonometric functions assume radian input unless stated 5573 | otherwise. 5574 | 5575 | (12) Expressions may contain white-space characters such as space, 5576 | tabs, new-lines, control-feed et al. 5577 | ('\n', '\r', '\t', '\b', '\v', '\f') 5578 | 5579 | (13) Strings may be comprised of any combination of letters, digits 5580 | special characters including (~!@#$%^&*()[]|=+ ,./?<>;:"`~_) or 5581 | hexadecimal escaped sequences (eg: \0x30) and must be enclosed 5582 | with single-quotes. 5583 | eg: 'Frankly my dear, \0x49 do n0t give a damn!' 5584 | 5585 | (14) User defined normal functions can have up to 20 parameters, 5586 | where as user defined generic-functions and vararg-functions 5587 | can have an unlimited number of parameters. 5588 | 5589 | (15) The inbuilt polynomial functions can be at most of degree 12. 5590 | 5591 | (16) Where appropriate constant folding optimisations may be applied. 5592 | (eg: The expression '2 + (3 - (x / y))' becomes '5 - (x / y)') 5593 | 5594 | (17) If the strength reduction compilation option has been enabled, 5595 | then where applicable strength reduction optimisations may be 5596 | applied. 5597 | 5598 | (18) String processing capabilities are available by default. To 5599 | turn them off, the following needs to be defined at compile 5600 | time: exprtk_disable_string_capabilities 5601 | 5602 | (19) Composited functions can call themselves or any other functions 5603 | that have been defined prior to their own definition. 5604 | 5605 | (20) Recursive calls made from within composited functions will have 5606 | a stack size bound by the stack of the executing architecture. 5607 | 5608 | (21) User defined functions by default are assumed to have side 5609 | effects. As such an "all constant parameter" invocation of such 5610 | functions wont result in constant folding. If the function has 5611 | no side-effects then that can be noted during the constructor 5612 | of the ifunction allowing it to be constant folded where 5613 | appropriate. 5614 | 5615 | (22) The entity relationship between symbol_table and an expression 5616 | is many-to-many. However the intended 'typical' use-case where 5617 | possible, is to have a single symbol table manage the variable 5618 | and function requirements of multiple expressions. 5619 | 5620 | (23) The common use-case for an expression is to have it compiled 5621 | only ONCE and then subsequently have it evaluated multiple 5622 | times. An extremely inefficient and suboptimal approach would 5623 | be to recompile an expression from its string form every time 5624 | it requires evaluating. 5625 | 5626 | (24) It is strongly recommended that the return value of method 5627 | invocations from the parser and symbol_table types be taken 5628 | into account. Specifically the 'compile' method of the parser 5629 | and the 'add_xxx' set of methods of the symbol_table as they 5630 | denote either the success or failure state of the invoked call. 5631 | Continued processing from a failed state without having first 5632 | rectified the underlying issue will in turn result in further 5633 | failures and undefined behaviours. 5634 | 5635 | (25) The following are examples of compliant floating point value 5636 | representations: 5637 | 5638 | (01) 12345 (06) -123.456 5639 | (02) +123.456e+12 (07) 123.456E-12 5640 | (03) +012.045e+07 (08) .1234 5641 | (04) 1234. (09) -56789. 5642 | (05) 123.456f (10) -321.654E+3L 5643 | 5644 | (26) Expressions may contain any of the following comment styles: 5645 | 5646 | (1) // .... \n 5647 | (2) # .... \n 5648 | (3) /* .... */ 5649 | 5650 | (27) The 'null' value type is a special non-zero type that 5651 | incorporates specific semantics when undergoing operations with 5652 | the standard numeric type. The following is a list of type and 5653 | boolean results associated with the use of 'null': 5654 | 5655 | (1) null +,-,*,/,% x --> x 5656 | (2) x +,-,*,/,% null --> x 5657 | (3) null +,-,*,/,% null --> null 5658 | (4) null == null --> true 5659 | (5) null == x --> true 5660 | (6) x == null --> true 5661 | (7) x != null --> false 5662 | (8) null != null --> false 5663 | (9) null != x --> false 5664 | 5665 | (28) The following is a list of reserved words and symbols used by 5666 | ExprTk. Attempting to add a variable or custom function to a 5667 | symbol table using any of the reserved words will result in a 5668 | failure. 5669 | 5670 | abs, acos, acosh, and, asin, asinh, assert, atan, atan2, 5671 | atanh, avg, break, case, ceil, clamp, continue, cosh, cos, 5672 | cot, csc, default, deg2grad, deg2rad, else, equal, erfc, 5673 | erf, exp, expm1, false, floor, for, frac, grad2deg, hypot, 5674 | iclamp, if, ilike, in, inrange, in, like, log, log10, log1p, 5675 | log2, logn, mand, max, min, mod, mor, mul, nand, ncdf, nor, 5676 | not, not_equal, not, null, or, pow, rad2deg, repeat, return, 5677 | root, roundn, round, sec, sgn, shl, shr, sinc, sinh, sin, 5678 | sqrt, sum, swap, switch, tanh, tan, true, trunc, until, var, 5679 | while, xnor, xor 5680 | 5681 | (29) Every valid ExprTk statement is a "value returning" expression. 5682 | Unlike some languages that limit the types of expressions that 5683 | can be performed in certain situations, in ExprTk any valid 5684 | expression can be used in any "value consuming" context. eg: 5685 | 5686 | var y := 3; 5687 | for (var x := switch 5688 | { 5689 | case 1 : 7; 5690 | case 2 : -1 + ~{var x{};}; 5691 | default : y > 2 ? 3 : 4; 5692 | }; 5693 | x != while (y > 0) { y -= 1; }; 5694 | x -= { 5695 | if (min(x,y) < 2 * max(x,y)) 5696 | x + 2; 5697 | else 5698 | x + y - 3; 5699 | } 5700 | ) 5701 | { 5702 | (x + y) / (x - y); 5703 | }; 5704 | 5705 | (30) It is recommended when prototyping expressions that the ExprTk 5706 | REPL be utilised, as it supports all the features available in 5707 | the library, including complete error analysis, benchmarking 5708 | and dependency dumps etc which allows for rapid 5709 | coding/prototyping and debug cycles without the hassle of 5710 | having to recompile test programs with expressions that have 5711 | been hard-coded. It is also a good source of truth for how the 5712 | library's various features can be applied. 5713 | 5714 | (31) For performance considerations, one should assume the actions 5715 | of expression, symbol table and parser instance instantiation 5716 | and destruction, and the expression compilation process itself 5717 | to be of high latency. Hence none of them should be part of any 5718 | performance critical code paths, and should instead occur 5719 | entirely either before or after such code paths. 5720 | 5721 | (32) Deep copying an expression instance for the purposes of 5722 | persisting to disk or otherwise transmitting elsewhere with the 5723 | intent to 'resurrect' the expression instance later on is not 5724 | possible due to the reasons described in the final note of 5725 | Section 10. The recommendation is to instead simply persist the 5726 | string form of the expression and compile the expression at 5727 | run-time on the target. 5728 | 5729 | (33) The correctness and robustness of the ExprTk library is 5730 | maintained by having a comprehensive suite of unit tests and 5731 | functional tests all of which are run using sanitizers (ASAN, 5732 | UBSAN, LSAN, MSAN, TSAN). Additionally, continuous fuzz-testing 5733 | provided by Google OSS Fuzz, and static analysis via Synopsis 5734 | Coverity. 5735 | 5736 | (34) The library name ExprTk is pronounced "Ex-Pee-Ar-Tee-Kay" or 5737 | simply "Mathematical Expression Toolkit" 5738 | 5739 | 5740 | (35) For general support, inquires or bug/issue reporting: 5741 | https://www.partow.net/programming/exprtk/index.html#support 5742 | 5743 | (36) Before jumping in and using ExprTk, do take the time to peruse 5744 | the documentation and all of the examples, both in the main and 5745 | the extras distributions. Having an informed general view of 5746 | what can and can't be done, and how something should be done 5747 | with ExprTk, will likely result in a far more productive and 5748 | enjoyable programming experience. 5749 | 5750 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5751 | 5752 | [SECTION 27 - SIMPLE EXPRTK EXAMPLE] 5753 | The following is a simple yet complete example demonstrating typical 5754 | usage of the ExprTk Library. The example instantiates a symbol table 5755 | object, adding to it three variables named x, y and z, and a custom 5756 | user defined function, that accepts only two parameters, named myfunc. 5757 | The example then proceeds to instantiate an expression object and 5758 | register to it the symbol table instance. 5759 | 5760 | A parser is then instantiated, and the string representation of the 5761 | expression and the expression object are passed to the parser's 5762 | compile method for compilation. If an error occurred during 5763 | compilation, the compile method will return false, leading to a series 5764 | of error diagnostics being printed to stdout. Otherwise the newly 5765 | compiled expression is evaluated by invoking the expression object's 5766 | value method, and subsequently printing the result of the computation 5767 | to stdout. 5768 | 5769 | 5770 | --- snip --- 5771 | #include5772 | #include 5773 | 5774 | #include "exprtk.hpp" 5775 | 5776 | template 5777 | struct myfunc final : public exprtk::ifunction<T> 5778 | { 5779 | myfunc() : exprtk::ifunction<T>(2) {} 5780 | 5781 | T operator()(const T& v1, const T& v2) override 5782 | { 5783 | return T(1) + (v1 * v2) / T(3); 5784 | } 5785 | }; 5786 | 5787 | int main() 5788 | { 5789 | typedef exprtk::symbol_table<double> symbol_table_t; 5790 | typedef exprtk::expression<double> expression_t; 5791 | typedef exprtk::parser<double> parser_t; 5792 | typedef exprtk::parser_error::type error_t; 5793 | 5794 | const std::string expression_string = 5795 | "z := 2 myfunc([4 + sin(x / pi)^3],y ^ 2)" 5796 | 5797 | double x = 1.1; 5798 | double y = 2.2; 5799 | double z = 3.3; 5800 | 5801 | myfunc<double> mf; 5802 | 5803 | symbol_table_t symbol_table; 5804 | symbol_table.add_constants(); 5805 | symbol_table.add_variable("x",x); 5806 | symbol_table.add_variable("y",y); 5807 | symbol_table.add_variable("z",z); 5808 | symbol_table.add_function("myfunc",mf); 5809 | 5810 | expression_t expression; 5811 | expression.register_symbol_table(symbol_table); 5812 | 5813 | parser_t parser; 5814 | 5815 | if (!parser.compile(expression_string,expression)) 5816 | { 5817 | // A compilation error has occurred. Attempt to 5818 | // print all errors to stdout. 5819 | 5820 | printf("Error: %s\tExpression: %s\n", 5821 | parser.error().c_str(), 5822 | expression_string.c_str()); 5823 | 5824 | for (std::size_t i = 0; i < parser.error_count(); ++i) 5825 | { 5826 | // Include the specific nature of each error 5827 | // and its position in the expression string. 5828 | 5829 | error_t error = parser.get_error(i); 5830 | 5831 | printf("Error: %02d Position: %02d " 5832 | "Type: [%s] " 5833 | "Message: %s " 5834 | "Expression: %s\n", 5835 | static_cast<int>(i), 5836 | static_cast<int>(error.token.position), 5837 | exprtk::parser_error::to_str(error.mode).c_str(), 5838 | error.diagnostic.c_str(), 5839 | expression_string.c_str()); 5840 | } 5841 | 5842 | return 1; 5843 | } 5844 | 5845 | // Evaluate the expression and obtain its result. 5846 | 5847 | double result = expression.value(); 5848 | 5849 | printf("Result: %10.5f\n",result); 5850 | 5851 | return 0; 5852 | } 5853 | --- snip --- 5854 | 5855 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5856 | 5857 | [SECTION 28 - BUILD OPTIONS] 5858 | When building ExprTk there are a number of defines that will enable or 5859 | disable certain features and capabilities. The defines can either be 5860 | part of a compiler command line switch or scoped around the include to 5861 | the ExprTk header. The defines are as follows: 5862 | 5863 | (01) exprtk_enable_debugging 5864 | (02) exprtk_disable_cardinal_pow_optimisation 5865 | (03) exprtk_disable_comments 5866 | (04) exprtk_disable_break_continue 5867 | (05) exprtk_disable_sc_andor 5868 | (06) exprtk_disable_return_statement 5869 | (07) exprtk_disable_enhanced_features 5870 | (08) exprtk_disable_string_capabilities 5871 | (09) exprtk_disable_superscalar_unroll 5872 | (10) exprtk_disable_rtl_io 5873 | (11) exprtk_disable_rtl_io_file 5874 | (12) exprtk_disable_rtl_vecops 5875 | (13) exprtk_disable_caseinsensitivity 5876 | (14) exprtk_enable_range_runtime_checks 5877 | 5878 | (01) exprtk_enable_debugging 5879 | This define will enable printing of debug information to stdout during 5880 | the compilation process. 5881 | 5882 | (02) exprtk_disable_cardinal_pow_optimisation 5883 | This define will disable the optimisation invoked when constant 5884 | integers are used as powers in exponentiation expressions (eg: x^7). 5885 | 5886 | (03) exprtk_disable_comments 5887 | This define will disable the ability for expressions to have comments. 5888 | Expressions that have comments when parsed with a build that has this 5889 | option, will result in a compilation failure. 5890 | 5891 | (04) exprtk_disable_break_continue 5892 | This define will disable the loop-wise 'break' and 'continue' 5893 | capabilities. Any expression that contains those keywords will result 5894 | in a compilation failure. 5895 | 5896 | (05) exprtk_disable_sc_andor 5897 | This define will disable the short-circuit '&' (and) and '|' (or) 5898 | operators 5899 | 5900 | (06) exprtk_disable_return_statement 5901 | This define will disable use of return statements within expressions. 5902 | 5903 | (07) exprtk_disable_enhanced_features 5904 | This define will disable all enhanced features such as strength 5905 | reduction and special function optimisations and expression specific 5906 | type instantiations. This feature will reduce compilation times and 5907 | binary sizes but will also result in massive performance degradation 5908 | of expression evaluations. 5909 | 5910 | (08) exprtk_disable_string_capabilities 5911 | This define will disable all string processing capabilities. Any 5912 | expression that contains a string or string related syntax will result 5913 | in a compilation failure. 5914 | 5915 | (09) exprtk_disable_superscalar_unroll 5916 | This define will set the loop unroll batch size to 4 operations per 5917 | loop instead of the default 8 operations. This define is used in 5918 | operations that involve vectors and aggregations over vectors. When 5919 | targeting non-superscalar architectures, it may be recommended to 5920 | build using this particular option if efficiency of evaluations is of 5921 | concern. 5922 | 5923 | (10) exprtk_disable_rtl_io 5924 | This define will disable all of basic IO RTL package features. When 5925 | present, any attempt to register the basic IO RTL package with a given 5926 | symbol table will fail causing a compilation error. 5927 | 5928 | (11) exprtk_disable_rtl_io_file 5929 | This define will disable the file I/O RTL package features. When 5930 | present, any attempts to register the file I/O package with a given 5931 | symbol table will fail causing a compilation error. 5932 | 5933 | (12) exprtk_disable_rtl_vecops 5934 | This define will disable the extended vector operations RTL package 5935 | features. When present, any attempts to register the vector operations 5936 | package with a given symbol table will fail causing a compilation 5937 | error. 5938 | 5939 | (13) exprtk_disable_caseinsensitivity 5940 | This define will disable case-insensitivity when matching variables 5941 | and functions. Furthermore all reserved and keywords will only be 5942 | acknowledged when in all lower-case. 5943 | 5944 | (14) exprtk_enable_range_runtime_checks 5945 | This define will enable run-time checks pertaining to vector indexing 5946 | operations used in any of the vector-to-vector and vector-to-scalar 5947 | operations. 5948 | 5949 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5950 | 5951 | [SECTION 29 - FILES] 5952 | The source distribution of ExprTk is comprised of the following set of 5953 | files: 5954 | 5955 | (00) Makefile 5956 | (01) readme.txt 5957 | (02) exprtk.hpp 5958 | (03) exprtk_test.cpp 5959 | (04) exprtk_benchmark.cpp 5960 | (05) exprtk_simple_example_01.cpp 5961 | (06) exprtk_simple_example_02.cpp 5962 | (07) exprtk_simple_example_03.cpp 5963 | (08) exprtk_simple_example_04.cpp 5964 | (09) exprtk_simple_example_05.cpp 5965 | (10) exprtk_simple_example_06.cpp 5966 | (11) exprtk_simple_example_07.cpp 5967 | (12) exprtk_simple_example_08.cpp 5968 | (13) exprtk_simple_example_09.cpp 5969 | (14) exprtk_simple_example_10.cpp 5970 | (15) exprtk_simple_example_11.cpp 5971 | (16) exprtk_simple_example_12.cpp 5972 | (17) exprtk_simple_example_13.cpp 5973 | (18) exprtk_simple_example_14.cpp 5974 | (19) exprtk_simple_example_15.cpp 5975 | (20) exprtk_simple_example_16.cpp 5976 | (21) exprtk_simple_example_17.cpp 5977 | (22) exprtk_simple_example_18.cpp 5978 | (23) exprtk_simple_example_19.cpp 5979 | (24) exprtk_simple_example_20.cpp 5980 | (25) exprtk_simple_example_21.cpp 5981 | (26) exprtk_simple_example_22.cpp 5982 | (27) exprtk_simple_example_23.cpp 5983 | (28) exprtk_simple_example_24.cpp 5984 | 5985 | 5986 | Details for each of the above examples can be found here: 5987 | 5988 | https://www.partow.net/programming/exprtk/index.html#examples 5989 | 5990 | 5991 | Various extended and advanced examples using ExprTk are available 5992 | via the following: 5993 | 5994 | (00) exprtk_american_option_binomial_model.cpp 5995 | (01) exprtk_archimedes_pi.cpp 5996 | (02) exprtk_binomial_coefficient.cpp 5997 | (03) exprtk_bsm_benchmark.cpp 5998 | (04) exprtk_calc.cpp 5999 | (05) exprtk_collatz.cpp 6000 | (06) exprtk_compilation_timeout.cpp 6001 | (07) exprtk_degree_trigonometry_example.cpp 6002 | (08) exprtk_exprgen.cpp 6003 | (09) exprtk_extract_dependents.cpp 6004 | (00) exprtk_e_10kdigits.cpp 6005 | (11) exprtk_factorize_fermat.cpp 6006 | (12) exprtk_factorize_pollard.cpp 6007 | (13) exprtk_fizzbuzz.cpp 6008 | (14) exprtk_funcall_benchmark.cpp 6009 | (15) exprtk_game_of_life.cpp 6010 | (16) exprtk_gcd.cpp 6011 | (17) exprtk_gnuplot.cpp 6012 | (18) exprtk_gnuplot_multi.cpp 6013 | (19) exprtk_groups_examples.cpp 6014 | (10) exprtk_immutable_symbol_table_example.cpp 6015 | (21) exprtk_import_packages.cpp 6016 | (22) exprtk_instruction_primer.cpp 6017 | (23) exprtk_jump_diffusion_process.cpp 6018 | (24) exprtk_loop_timeout_rtc.cpp 6019 | (25) exprtk_magic_square.cpp 6020 | (26) exprtk_mandelbrot.cpp 6021 | (27) exprtk_max_subarray_sum.cpp 6022 | (28) exprtk_maze_generator.cpp 6023 | (29) exprtk_miller_rabin_primality_test.cpp 6024 | (20) exprtk_montecarlo_e.cpp 6025 | (31) exprtk_montecarlo_option_pricing_model.cpp 6026 | (32) exprtk_montecarlo_pi.cpp 6027 | (33) exprtk_naive_primes.cpp 6028 | (34) exprtk_normal_random_marsaglia_method.cpp 6029 | (35) exprtk_nqueens_problem.cpp 6030 | (36) exprtk_nthroot_bisection.cpp 6031 | (37) exprtk_ornstein_uhlenbeck_process.cpp 6032 | (38) exprtk_pascals_triangle.cpp 6033 | (39) exprtk_pi_10kdigits.cpp 6034 | (30) exprtk_prime_sieve.cpp 6035 | (41) exprtk_prime_sieve_vectorized.cpp 6036 | (42) exprtk_pyramid.cpp 6037 | (43) exprtk_pythagorean_triples.cpp 6038 | (44) exprtk_recursive_fibonacci.cpp 6039 | (45) exprtk_repl.cpp 6040 | (46) exprtk_riddle.cpp 6041 | (47) exprtk_rtc_overhead.cpp 6042 | (48) exprtk_sudoku_solver.cpp 6043 | (49) exprtk_sumofprimes.cpp 6044 | (50) exprtk_symtab_functions.cpp 6045 | (51) exprtk_testgen.cpp 6046 | (52) exprtk_tower_of_hanoi.cpp 6047 | (53) exprtk_truthtable_gen.cpp 6048 | (54) exprtk_vectorized_binomial_model.cpp 6049 | (55) exprtk_vectornorm.cpp 6050 | (56) exprtk_vector_benchmark.cpp 6051 | (57) exprtk_vector_benchmark_multithreaded.cpp 6052 | (58) exprtk_vector_resize_example.cpp 6053 | (59) exprtk_vector_resize_inline_example.cpp 6054 | (60) exprtk_wiener_process_pi.cpp 6055 | 6056 | 6057 | Details for each of the above examples can be found here: 6058 | 6059 | https://partow.net/programming/exprtk/index.html#variousexamples 6060 | 6061 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6062 | 6063 | [SECTION 30 - LANGUAGE STRUCTURE] 6064 | The following are the various language structures available within 6065 | ExprTk and their structural representations. 6066 | 6067 | (00) If Statement 6068 | (01) Else Statement 6069 | (02) Ternary Statement 6070 | (03) While Loop 6071 | (04) Repeat Until Loop 6072 | (05) For Loop 6073 | (06) Switch Statement 6074 | (07) Multi Subexpression Statement 6075 | (08) Multi Case-Consequent Statement 6076 | (09) Variable Definition Statement 6077 | (10) Vector Definition Statement 6078 | (11) String Definition Statement 6079 | (12) Range Statement 6080 | (13) Return Statement 6081 | 6082 | 6083 | (00) - If Statement 6084 | +-------------------------------------------------------------+ 6085 | | | 6086 | | [if] ---> [(] ---> [condition] -+-> [,] -+ | 6087 | | | | | 6088 | | +---------------<---------------+ | | 6089 | | | | | 6090 | | | +------------------<------------------+ | 6091 | | | | | 6092 | | | +--> [consequent] ---> [,] ---> [alternative] ---> [)] | 6093 | | | | 6094 | | +--> [)] --+-> [{] ---> [expression*] ---> [}] --+ | 6095 | | | | | 6096 | | | +---------<----------+ | 6097 | | +----<-----+ | | 6098 | | | v | 6099 | | +--> [consequent] --> [;] -{*}-> [else-statement] | 6100 | | | 6101 | +-------------------------------------------------------------+ 6102 | 6103 | 6104 | (01) - Else Statement 6105 | +-------------------------------------------------------------+ 6106 | | | 6107 | | [else] -+-> [alternative] ---> [;] | 6108 | | | | 6109 | | +--> [{] ---> [expression*] ---> [}] | 6110 | | | | 6111 | | +--> [if-statement] | 6112 | | | 6113 | +-------------------------------------------------------------+ 6114 | 6115 | 6116 | (02) - Ternary Statement 6117 | +-------------------------------------------------------------+ 6118 | | | 6119 | | [condition] ---> [?] ---> [consequent] ---> [:] --+ | 6120 | | | | 6121 | | +------------------------<------------------------+ | 6122 | | | | 6123 | | +--> [alternative] --> [;] | 6124 | | | 6125 | +-------------------------------------------------------------+ 6126 | 6127 | 6128 | (03) - While Loop 6129 | +-------------------------------------------------------------+ 6130 | | | 6131 | | [while] ---> [(] ---> [condition] ---> [)] ---+ | 6132 | | | | 6133 | | +----------------------<----------------------+ | 6134 | | | | 6135 | | +--> [{] ---> [expression*] ---> [}] | 6136 | | | 6137 | +-------------------------------------------------------------+ 6138 | 6139 | 6140 | (04) - Repeat Until Loop 6141 | +-------------------------------------------------------------+ 6142 | | | 6143 | | [repeat] ---> [expression*] ---+ | 6144 | | | | 6145 | | +--------------<---------------+ | 6146 | | | | 6147 | | +--> [until] ---> [(] ---> [condition] --->[)] | 6148 | | | 6149 | +-------------------------------------------------------------+ 6150 | 6151 | 6152 | (05) - For Loop 6153 | +-------------------------------------------------------------+ 6154 | | | 6155 | | [for] ---> [(] -+-> [initialise expression] --+--+ | 6156 | | | | | | 6157 | | +------------->---------------+ v | 6158 | | | | 6159 | | +-----------------------<------------------------+ | 6160 | | | | 6161 | | +--> [;] -+-> [condition] -+-> [;] ---+ | 6162 | | | | | | 6163 | | +------->--------+ v | 6164 | | | | 6165 | | +------------------<---------+--------+ | 6166 | | | | | 6167 | | +--> [increment expression] -+-> [)] --+ | 6168 | | | | 6169 | | +------------------<-------------------+ | 6170 | | | | 6171 | | +--> [{] ---> [expression*] ---> [}] | 6172 | | | 6173 | +-------------------------------------------------------------+ 6174 | 6175 | 6176 | (06) - Switch Statement 6177 | +-------------------------------------------------------------+ 6178 | | | 6179 | | [switch] ---> [{] ---+ | 6180 | | | | 6181 | | +---------<----------+-----------<-----------+ | 6182 | | | | | 6183 | | +--> [case] ---> [condition] ---> [:] ---+ | | 6184 | | | | | 6185 | | +-------------------<--------------------+ | | 6186 | | | | | 6187 | | +--> [consequent] ---> [;] --------->--------+ | 6188 | | | | | 6189 | | | | | 6190 | | +--> [default] ---> [consequent] ---> [;] ---+ | 6191 | | | | | 6192 | | +---------------------<----------------------+ | 6193 | | | | 6194 | | +--> [}] | 6195 | | | 6196 | +-------------------------------------------------------------+ 6197 | 6198 | 6199 | (07) - Multi Subexpression Statement 6200 | +-------------------------------------------------------------+ 6201 | | | 6202 | | +--------------<---------------+ | 6203 | | | | | 6204 | | [~] ---> [{\(] -+-> [expression] -+-> [;\,] ---+ | 6205 | | | | 6206 | | +----------------<----------------+ | 6207 | | | | 6208 | | +--> [}\)] | 6209 | | | 6210 | +-------------------------------------------------------------+ 6211 | 6212 | 6213 | (08) - Multi Case-Consequent Statement 6214 | +-------------------------------------------------------------+ 6215 | | | 6216 | | [[*]] ---> [{] ---+ | 6217 | | | | 6218 | | +--------<--------+--------------<----------+ | 6219 | | | | | 6220 | | +--> [case] ---> [condition] ---> [:] ---+ | | 6221 | | | | | 6222 | | +-------------------<--------------------+ | | 6223 | | | | | 6224 | | +--> [consequent] ---> [;] ---+------>------+ | 6225 | | | | 6226 | | +--> [}] | 6227 | | | 6228 | +-------------------------------------------------------------+ 6229 | 6230 | 6231 | (09) - Variable Definition Statement 6232 | +-------------------------------------------------------------+ 6233 | | | 6234 | | [var] ---> [symbol] -+-> [:=] -+-> [expression] -+-> [;] | 6235 | | | | | | 6236 | | | +-----> [{}] -->--+ | 6237 | | | | | 6238 | | +------------->-------------+ | 6239 | | | 6240 | +-------------------------------------------------------------+ 6241 | 6242 | 6243 | (10) - Vector Definition Statement 6244 | +-------------------------------------------------------------+ 6245 | | | 6246 | | [var] ---> [symbol] ---> [[] ---> [constant] ---> []] --+ | 6247 | | | | 6248 | | +---------------------------<---------------------------+ | 6249 | | | | 6250 | | | +--------->---------+ | 6251 | | | | | | 6252 | | +--> [:=] ---> [{] -+-+-> [expression] -+-> [}] ---> [;] | 6253 | | | | | 6254 | | +--<--- [,] <-----+ | 6255 | | | 6256 | +-------------------------------------------------------------+ 6257 | 6258 | 6259 | (11) - String Definition Statement 6260 | +-------------------------------------------------------------+ 6261 | | | 6262 | | [var] --> [symbol] --> [:=] --> [str-expression] ---> [;] | 6263 | | | 6264 | +-------------------------------------------------------------+ 6265 | 6266 | 6267 | (12) - Range Statement 6268 | +-------------------------------------------------------------+ 6269 | | | 6270 | | +-------->--------+ | 6271 | | | | | 6272 | | [[] -+-> [expression] -+-> [:] -+-> [expression] -+--> []] | 6273 | | | | | 6274 | | +-------->--------+ | 6275 | | | 6276 | +-------------------------------------------------------------+ 6277 | 6278 | 6279 | (13) - Return Statement 6280 | +-------------------------------------------------------------+ 6281 | | | 6282 | | [return] ---> [[] -+-> [expression] -+-> []] ---> [;] | 6283 | | | | | 6284 | | +--<--- [,] <-----+ | 6285 | | | 6286 | +-------------------------------------------------------------+