mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 08:42:39 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			253 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| #TINYEXPR
 | |
| 
 | |
| 
 | |
| TINYEXPR is a very small recursive descent parser and evaluation engine for
 | |
| math expressions. It's handy when you want to add the ability to evaluation
 | |
| math expressions at runtime without adding a bunch of cruft to you project.
 | |
| 
 | |
| In addition to the standard math operators and precedence, TINYEXPR also supports
 | |
| the standard C math functions and runtime binding of variables.
 | |
| 
 | |
| ##Features
 | |
| 
 | |
| - **ANSI C with no dependencies**.
 | |
| - Single source file and header file.
 | |
| - Simple and fast.
 | |
| - Implements standard operators precedence.
 | |
| - Exposes standard C math functions (sin, sqrt, ln, etc.).
 | |
| - Can bind variables at eval-time.
 | |
| - Released under the zlib license - free for nearly any use.
 | |
| - Easy to use and integrate with your code
 | |
| - Thread-safe, provided that your *malloc* is.
 | |
| 
 | |
| ##Short Example
 | |
| 
 | |
| Here is a minimal example to evaluate an expression at runtime.
 | |
| 
 | |
| ```C
 | |
|     #include "tinyexpr.h"
 | |
|     printf("%f\n", te_interp("5*5", 0)); /* Prints 25. */
 | |
| ```
 | |
| 
 | |
| 
 | |
| ##Usage
 | |
| 
 | |
| TINYEXPR defines only four functions:
 | |
| 
 | |
| ```C
 | |
|     double te_interp(const char *expression, int *error);
 | |
|     te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error);
 | |
|     double te_eval(const te_expr *expr);
 | |
|     void te_free(te_expr *expr);
 | |
| ```
 | |
| 
 | |
| ##te_interp
 | |
| ```C
 | |
|     double te_interp(const char *expression, int *error);
 | |
| ```
 | |
| 
 | |
| `te_interp()` takes an expression and immediately returns the result of it. If there
 | |
| is a parse error, `te_interp()` returns NaN.
 | |
| 
 | |
| If the `error` pointer argument is not 0, then `te_interp()` will set `*error` to the position
 | |
| of the parse error on failure, and set `*error` to 0 on success.
 | |
| 
 | |
| **example usage:**
 | |
| 
 | |
| ```C
 | |
|     int error;
 | |
| 
 | |
|     double a = te_interp("(5+5)", 0); /* Returns 10. */
 | |
|     double b = te_interp("(5+5)", &error); /* Returns 10, error is set to 0. */
 | |
|     double c = te_interp("(5+5", &error); /* Returns NaN, error is set to 4. */
 | |
| ```
 | |
| 
 | |
| ##te_compile, te_eval, te_free
 | |
| ```C
 | |
|     te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
 | |
|     double te_eval(const te_expr *n);
 | |
|     void te_free(te_expr *n);
 | |
| ```
 | |
| 
 | |
| Give `te_compile()` an expression with unbound variables and a list of
 | |
| variable names and pointers. `te_compile()` will return a `te_expr*` which can
 | |
| be evaluated later using `te_eval()`. On failure, `te_compile()` will return 0
 | |
| and optionally set the passed in `*error` to the location of the parse error.
 | |
| 
 | |
| You may also compile expressions without variables by passing `te_compile()`'s second
 | |
| and thrid arguments as 0.
 | |
| 
 | |
| Give `te_eval()` a `te_expr*` from `te_compile()`. `te_eval()` will evaluate the expression
 | |
| using the current variable values.
 | |
| 
 | |
| After you're finished, make sure to call `te_free()`.
 | |
| 
 | |
| **example usage:**
 | |
| 
 | |
| ```C
 | |
|     double x, y;
 | |
|     /* Store variable names and pointers. */
 | |
|     te_variable vars[] = {{"x", &x}, {"y", &y}};
 | |
| 
 | |
|     int err;
 | |
|     /* Compile the expression with variables. */
 | |
|     te_expr *expr = te_compile("sqrt(x^2+y^2)", vars, 2, &err);
 | |
| 
 | |
|     if (expr) {
 | |
|         x = 3; y = 4;
 | |
|         const double h1 = te_eval(expr); /* Returns 5. */
 | |
| 
 | |
|         x = 5; y = 12;
 | |
|         const double h2 = te_eval(expr); /* Returns 13. */
 | |
| 
 | |
|         te_free(expr);
 | |
|     } else {
 | |
|         printf("Parse error at %d\n", err);
 | |
|     }
 | |
| 
 | |
| ```
 | |
| 
 | |
| ##Longer Example
 | |
| 
 | |
| Here is a complete example that will evaluate an expression passed in from the command
 | |
| line. It also does error checking and binds the variables `x` and `y` to *3* and *4*, respectively.
 | |
| 
 | |
| ```C
 | |
|     #include "tinyexpr.h"
 | |
|     #include <stdio.h>
 | |
| 
 | |
|     int main(int argc, char *argv[])
 | |
|     {
 | |
|         if (argc < 2) {
 | |
|             printf("Usage: example2 \"expression\"\n");
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         const char *expression = argv[1];
 | |
|         printf("Evaluating:\n\t%s\n", expression);
 | |
| 
 | |
|         /* This shows an example where the variables
 | |
|          * x and y are bound at eval-time. */
 | |
|         double x, y;
 | |
|         te_variable vars[] = {{"x", &x}, {"y", &y}};
 | |
| 
 | |
|         /* This will compile the expression and check for errors. */
 | |
|         int err;
 | |
|         te_expr *n = te_compile(expression, vars, 2, &err);
 | |
| 
 | |
|         if (n) {
 | |
|             /* The variables can be changed here, and eval can be called as many
 | |
|              * times as you like. This is fairly efficient because the parsing has
 | |
|              * already been done. */
 | |
|             x = 3; y = 4;
 | |
|             const double r = te_eval(n); printf("Result:\n\t%f\n", r);
 | |
|             te_free(n);
 | |
|         } else {
 | |
|             /* Show the user where the error is at. */
 | |
|             printf("\t%*s^\nError near here", err-1, "");
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
|     }
 | |
| ```
 | |
| 
 | |
| 
 | |
| This produces the output:
 | |
| 
 | |
|     $ example2 "sqrt(x^2+y2)"
 | |
|         Evaluating:
 | |
|                 sqrt(x^2+y2)
 | |
|                           ^
 | |
|         Error near here
 | |
| 
 | |
| 
 | |
|     $ example2 "sqrt(x^2+y^2)"
 | |
|         Evaluating:
 | |
|                 sqrt(x^2+y^2)
 | |
|         Result:
 | |
|                 5.000000
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ##How it works
 | |
| 
 | |
| `te_compile()` uses a simple recursive descent parser to compile your
 | |
| expression into a syntax tree. For example, the expression `"sin x + 1/4"`
 | |
| parses as:
 | |
| 
 | |
| 
 | |
| 
 | |
| `te_compile()` also automatically prunes constant branches. In this example,
 | |
| the compiled expression returned by `te_compile()` would become:
 | |
| 
 | |
| 
 | |
| 
 | |
| `te_eval()` will automatically load in any variables by their pointer, and then evaluate
 | |
| and return the result of the expression.
 | |
| 
 | |
| `te_free()` should always be called when you're done with the compiled expression.
 | |
| 
 | |
| 
 | |
| ##Speed
 | |
| 
 | |
| 
 | |
| TINYEXPR is pretty fast compared to C when the expression is short, when the
 | |
| expression does hard calculations (e.g. exponentiation), and when some of the
 | |
| work can be simplified by `te_compile()`. TINYEXPR is slow compared to C when the
 | |
| expression is long and involves only basic arithmetic.
 | |
| 
 | |
| Here is some example performance numbers taken from the included
 | |
| **benchmark.c** program:
 | |
| 
 | |
| | Expression | te_eval time | native C time | slowdown  |
 | |
| | :------------- |-------------:| -----:|----:|
 | |
| | sqrt(a^1.5+a^2.5) | 15,641 ms | 14,478 ms | 8% slower |
 | |
| | a+5 | 765 ms | 563 ms | 36% slower |
 | |
| | a+(5*2) | 765 ms | 563 ms | 36% slower |
 | |
| | (a+5)*2 | 1422 ms | 563 ms | 153% slower |
 | |
| | (1/(a+1)+2/(a+2)+3/(a+3)) | 5,516 ms | 1,266 ms | 336% slower |
 | |
| 
 | |
| 
 | |
| 
 | |
| ##Grammar
 | |
| 
 | |
| TINYEXPR parses the following grammar:
 | |
| 
 | |
|     <expr>      =    <term> {("+" | "-") <term>}
 | |
|     <term>      =    <factor> {("*" | "/" | "%") <factor>}
 | |
|     <factor>    =    <power> {"^" <power>}
 | |
|     <power>     =    {("-" | "+")} <base>
 | |
|     <base>      =    <constant> | <variable> | <function> <power> | "(" <expr> ")"
 | |
| 
 | |
| In addition, whitespace between tokens is ignored.
 | |
| 
 | |
| Valid variable names are any combination of the lower case letters *a* through
 | |
| *z*. Constants can be integers, decimal numbers, or in scientific notation
 | |
| (e.g. *1e3* for *1000*). A leading zero is not required (e.g. *.5* for *0.5*)
 | |
| 
 | |
| 
 | |
| ##Functions supported
 | |
| 
 | |
| TINYEXPR supports addition (+), subtraction/negation (-), multiplication (\*),
 | |
| division (/), exponentiation (^) and modulus (%) with the normal operator
 | |
| precedence (the one exception being that exponentiation is evaluated
 | |
| left-to-right).
 | |
| 
 | |
| In addition, the following C math functions are also supported:
 | |
| 
 | |
| - 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
 | |
| 
 | |
| 
 | |
| ##Hints
 | |
| 
 | |
| - All functions/types start with the letters *te*.
 | |
| 
 | |
| - To allow constant optimization, surround constant expressions in parentheses.
 | |
|   For example "x+(1+5)" will evaluate the "(1+5)" expression at compile time and
 | |
|   compile the entire expression as "x+6", saving a runtime calculation. The
 | |
|   parentheses are important, because TINYEXPR will not change the order of
 | |
|   evaluation. If you instead compiled "x+1+5" TINYEXPR will insist that "1" is
 | |
|   added to "x" first, and "5" is added the result second.
 | |
| 
 |