|
| 1 | +#TINYEXPR |
| 2 | + |
| 3 | + |
| 4 | +TINYEXPR is a very small recursive descent parser and evaluation engine for |
| 5 | +math expressions. It's handy when you want to add the ability to evaluation |
| 6 | +math expressions at runtime without adding a bunch of cruft to you project. |
| 7 | + |
| 8 | +In addition to the standard math operators and precedence, TINYEXPR also supports |
| 9 | +the standard C math functions and runtime binding of variables. |
| 10 | + |
| 11 | +##Features |
| 12 | + |
| 13 | +- **ANSI C with no dependencies**. |
| 14 | +- Single source file and header file. |
| 15 | +- Simple and fast. |
| 16 | +- Implements standard operators precedence. |
| 17 | +- Exposes standard C math functions (sin, sqrt, ln, etc.). |
| 18 | +- Can bind variables at eval-time. |
| 19 | +- Released under the zlib license - free for nearly any use. |
| 20 | +- Easy to use and integrate with your code |
| 21 | +- Thread-safe, provided that your *malloc* is. |
| 22 | + |
| 23 | +##Short Example |
| 24 | + |
| 25 | +Here is a minimal example to evaluate an expression at runtime. |
| 26 | + |
| 27 | + #include "tinyexpr.h" |
| 28 | + #include <stdio.h> |
| 29 | + |
| 30 | + int main(int argc, char *argv[]) |
| 31 | + { |
| 32 | + const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)"; |
| 33 | + double r = te_interp(c, 0); |
| 34 | + printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r); |
| 35 | + return 0; |
| 36 | + } |
| 37 | + |
| 38 | + |
| 39 | +That produces the following output: |
| 40 | + |
| 41 | + The expression: |
| 42 | + sqrt(5^2+7^2+11^2+(8-2)^2) |
| 43 | + evaluates to: |
| 44 | + 15.198684 |
| 45 | + |
| 46 | + |
| 47 | +##Longer Example |
| 48 | + |
| 49 | +Here is an example that will evaluate an expression passed in from the command |
| 50 | +line. It also does error checking and binds the variables *x* and *y*. |
| 51 | + |
| 52 | + #include "tinyexpr.h" |
| 53 | + #include <stdio.h> |
| 54 | + |
| 55 | + int main(int argc, char *argv[]) |
| 56 | + { |
| 57 | + if (argc < 2) { |
| 58 | + printf("Usage: example2 \"expression\"\n", argv[0]); |
| 59 | + return 0; |
| 60 | + } |
| 61 | + |
| 62 | + const char *expression = argv[1]; |
| 63 | + printf("Evaluating:\n\t%s\n", expression); |
| 64 | + |
| 65 | + /* This shows an example where the variables |
| 66 | + * x and y are bound at eval-time. */ |
| 67 | + double x, y; |
| 68 | + te_variable vars[] = {{"x", &x}, {"y", &y}}; |
| 69 | + |
| 70 | + /* This will compile the expression and check for errors. */ |
| 71 | + int err; |
| 72 | + te_expr *n = te_compile(expression, vars, 2, &err); |
| 73 | + |
| 74 | + if (!err) { |
| 75 | + /* The variables can be changed here, and eval can be called as many |
| 76 | + * times as you like. This is fairly efficient because the parsing has |
| 77 | + * already been done. */ |
| 78 | + x = 3; |
| 79 | + y = 4; |
| 80 | + const double r = te_eval(n); printf("Result:\n\t%f\n", r); } |
| 81 | + else { |
| 82 | + /* Show the user where the error is at. */ |
| 83 | + printf("\t%*s^\nError near here", err-1, ""); |
| 84 | + } |
| 85 | + |
| 86 | + /* te_free should always be called after te_compile. */ |
| 87 | + te_free(n); |
| 88 | + |
| 89 | + return 0; |
| 90 | + } |
| 91 | + |
| 92 | + |
| 93 | +This produces the output: |
| 94 | + |
| 95 | + $ example2 "sqrt(x^2+y2)" |
| 96 | + Evaluating: |
| 97 | + sqrt(x^2+y2) |
| 98 | + ^ |
| 99 | + Error near here |
| 100 | + |
| 101 | + |
| 102 | + $ example2 "sqrt(x^2+y^2)" |
| 103 | + Evaluating: |
| 104 | + sqrt(x^2+y^2) |
| 105 | + Result: |
| 106 | + 5.000000 |
| 107 | + |
| 108 | + |
| 109 | +##Usage |
| 110 | + |
| 111 | +TINYEXPR defines only five functions: |
| 112 | + |
| 113 | + double te_interp(const char *expression, int *error); |
| 114 | + te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error); |
| 115 | + double te_eval(te_expr *n); |
| 116 | + void te_print(const te_expr *n); |
| 117 | + void te_free(te_expr *n); |
| 118 | + |
| 119 | +**te_interp** takes an expression and immediately returns the result of it. If |
| 120 | +an error pointer is passed in, *te_interp* will set it to 0 for success or |
| 121 | +approximately the position of the error for failure. If you don't care about |
| 122 | +errors, just pass in 0. |
| 123 | + |
| 124 | +**te_interp example:** |
| 125 | + |
| 126 | + double x = te_interp("5+5", 0); |
| 127 | + |
| 128 | +**te_compile** will compile an expression with unbound variables, which will |
| 129 | +be suitable to evaluate later. **te_eval** can then be called on the compiled |
| 130 | +expression repeatedly to evaluate it with different variable values. **te_free** |
| 131 | +should be called after you're finished. |
| 132 | + |
| 133 | +**te_compile example:** |
| 134 | + |
| 135 | + double x, y; |
| 136 | + te_variable vars[] = {{"x", &x}, {"y", &y}}; |
| 137 | + |
| 138 | + int err; |
| 139 | + te_expr *expr = te_compile("sqrt(x^2+y^2)", vars, 2, &err); |
| 140 | + |
| 141 | + if (!err) { |
| 142 | + x = 3; y = 4; |
| 143 | + const double h1 = te_eval(expr); |
| 144 | + |
| 145 | + x = 5; y = 7; |
| 146 | + const double h2 = te_eval(expr); |
| 147 | + } |
| 148 | + |
| 149 | + te_free(expr); |
| 150 | + |
| 151 | +**te_print** will display some (possibly not so) useful debugging |
| 152 | +information about the return value of *te_compile*. |
| 153 | + |
| 154 | + |
| 155 | +##How it works |
| 156 | + |
| 157 | +**te_compile** uses a simple recursive descent parser to compile your |
| 158 | +expression into a syntax tree. For example, the expression "sin x + 1/4" |
| 159 | +parses as: |
| 160 | + |
| 161 | + |
| 162 | + |
| 163 | +**te_compile** also automatically prunes constant branches. In this example, |
| 164 | +the compiled expression returned by *te_compile* is: |
| 165 | + |
| 166 | + |
| 167 | + |
| 168 | +**te_eval** will automatically load in any variables by their pointer, then evaluate |
| 169 | +and return the result of the expression. |
| 170 | + |
| 171 | +**te_free** should always be called when you're done with the compiled expression. |
| 172 | + |
| 173 | + |
| 174 | +##Speed |
| 175 | + |
| 176 | + |
| 177 | +TINYEXPR is pretty fast compared to C when the expression is short, when the |
| 178 | +expression does hard calculations (e.g. exponentiation), and when some of the |
| 179 | +work can be simplified by *te_compile*. TINYEXPR is slow compared to C when the |
| 180 | +expression is long and involves only basic arithmetic. |
| 181 | + |
| 182 | +Here is some example performance numbers taken from the included |
| 183 | +*benchmark.c* program: |
| 184 | + |
| 185 | +| Expression | te_eval time | native C time | slowdown | |
| 186 | +| ------------- |-------------| -----| |
| 187 | +| sqrt(a^1.5+a^2.5) | 15,641 ms | 14,478 ms | 8% slower | |
| 188 | +| a+5 | 765 ms | 563 ms | 36% slower | |
| 189 | +| a+(5*2) | 765 ms | 563 ms | 36% slower | |
| 190 | +| (a+5)*2 | 1422 ms | 563 ms | 153% slower | |
| 191 | +| (1/(a+1)+2/(a+2)+3/(a+3)) | 5,516 ms | 1,266 ms | 336% slower | |
| 192 | + |
| 193 | + |
| 194 | + |
| 195 | +##Grammar |
| 196 | + |
| 197 | +TINYEXPR parses the following grammar: |
| 198 | + |
| 199 | + <expr> = <term> {("+" | "-") <term>} |
| 200 | + <term> = <factor> {("*" | "/" | "%") <factor>} |
| 201 | + <factor> = <power> {"^" <power>} |
| 202 | + <power> = {("-" | "+")} <base> |
| 203 | + <base> = <constant> | <variable> | <function> <power> | "(" <expr> ")" |
| 204 | + |
| 205 | +In addition, whitespace between tokens is ignored. |
| 206 | + |
| 207 | +Valid variable names are any combination of the lower case letters *a* through |
| 208 | +*z*. Constants can be integers, decimal numbers, or in scientific notation |
| 209 | +(e.g. *1e3* for *1000*). A leading zero is not required (e.g. *.5* for *0.5*) |
| 210 | + |
| 211 | + |
| 212 | +##Functions supported |
| 213 | + |
| 214 | +TINYEXPR supports addition (+), subtraction/negation (-), multiplication (\*), |
| 215 | +division (/), exponentiation (^) and modulus (%) with the normal operator |
| 216 | +precedence (the one exception being that exponentiation is evaluated |
| 217 | +left-to-right). |
| 218 | + |
| 219 | +In addition, the following C math functions are also supported: |
| 220 | + |
| 221 | +- abs (calls to *fabs*), acos, asin, atan, ceil, cos, cosh, exp, floor, ln (calls to *log*), log (calls to *log10*), sin, sinh, sqrt, tan, tanh |
| 222 | + |
| 223 | + |
| 224 | +##Hints |
| 225 | + |
| 226 | +- All functions/types start with the letters *te*. |
| 227 | + |
| 228 | +- Remember to always call *te_free* on the result of *te_compile*, even if |
| 229 | + there is an error. |
| 230 | + |
| 231 | +- If there is an error, you can usually still evaluate the first part of the |
| 232 | + expression. This may or may not be useful to you. |
| 233 | + |
| 234 | +- To allow constant optimization, surround constant expressions in parentheses. |
| 235 | + For example "x+(1+5)" will evaluate the "(1+5)" expression at compile time and |
| 236 | + compile the entire expression as "x+6", saving a runtime calculation. The |
| 237 | + parentheses are important, because TINYEXPR will not change the order of |
| 238 | + evaluation. If you instead compiled "x+1+5" TINYEXPR will insist that "1" is |
| 239 | + added to "x" first, and "5" is added the result second. |
| 240 | + |
0 commit comments