You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
TinyExpr is a very small recursive descent parser and evaluation engine formath expressions. It's handy when you want to add the ability to evaluatemath expressions at runtime without adding a bunch of cruft to your project.
In addition to the standard math operators and precedence, TinyExpr also supportsthe standard C math functions and runtime binding of variables.
Features
C99 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 add custom functions and variables easily.
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 yourmalloc is.
Building
TinyExpr is self-contained in two files:tinyexpr.c andtinyexpr.h. To useTinyExpr, simply add those two files to your project.
Short Example
Here is a minimal example to evaluate an expression at runtime.
#include"tinyexpr.h"printf("%f\n",te_interp("5*5",0));/* Prints 25. */
te_interp() takes an expression and immediately returns the result of it. If thereis a parse error,te_interp() returns NaN.
If theerror pointer argument is not 0, thente_interp() will set*error to the positionof the parse error on failure, and set*error to 0 on success.
example usage:
interror;doublea=te_interp("(5+5)",0);/* Returns 10. */doubleb=te_interp("(5+5)",&error);/* Returns 10, error is set to 0. */doublec=te_interp("(5+5",&error);/* Returns NaN, error is set to 4. */
Givete_compile() an expression with unbound variables and a list ofvariable names and pointers.te_compile() will return ate_expr* which canbe evaluated later usingte_eval(). On failure,te_compile() will return 0and optionally set the passed in*error to the location of the parse error.
You may also compile expressions without variables by passingte_compile()'s secondand third arguments as 0.
Givete_eval() ate_expr* fromte_compile().te_eval() will evaluate the expressionusing the current variable values.
After you're finished, make sure to callte_free().
example usage:
doublex,y;/* Store variable names and pointers. */te_variablevars[]= {{"x",&x}, {"y",&y}};interr;/* Compile the expression with variables. */te_expr*expr=te_compile("sqrt(x^2+y^2)",vars,2,&err);if (expr) {x=3;y=4;constdoubleh1=te_eval(expr);/* Returns 5. */x=5;y=12;constdoubleh2=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 commandline. It also does error checking and binds the variablesx andy to3 and4, respectively.
#include"tinyexpr.h"#include<stdio.h>intmain(intargc,char*argv[]) {if (argc<2) {printf("Usage: example2 \"expression\"\n");return0; }constchar*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. */doublex,y;te_variablevars[]= {{"x",&x}, {"y",&y}};/* This will compile the expression and check for errors. */interr;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;constdoubler=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,""); }return0; }
TinyExpr can also call to custom functions implemented in C. Here is a short example:
doublemy_sum(doublea,doubleb) {/* Example C function that adds two numbers together. */returna+b;}te_variablevars[]= { {"mysum",my_sum,TE_FUNCTION2}/* TE_FUNCTION2 used because my_sum takes two arguments. */};te_expr*n=te_compile("mysum(5, 6)",vars,1,0);
How it works
te_compile() uses a simple recursive descent parser to compile yourexpression 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 byte_compile() would become:
te_eval() will automatically load in any variables by their pointer, and then evaluateand 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 theexpression does hard calculations (e.g. exponentiation), and when some of thework can be simplified byte_compile(). TinyExpr is slow compared to C when theexpression is long and involves only basic arithmetic.
Here is some example performance numbers taken from the includedbenchmark.c program:
In addition, whitespace between tokens is ignored.
Valid variable names consist of a letter followed by any combination of:letters, the digits0 through9, and underscore. Constants can be integersor floating-point numbers, and can be in decimal, hexadecimal (e.g.,0x57CEF7),or scientific notation (e.g.,1e3 for1000).A leading zero is not required (e.g.,.5 for0.5).
Functions supported
TinyExpr supports addition (+), subtraction/negation (-), multiplication (*),division (/), exponentiation (^) and modulus (%) with the normal operatorprecedence (the one exception being that exponentiation is evaluatedleft-to-right, but this can be changed - see below).
The following C math functions are also supported:
abs (calls tofabs), acos, asin, atan, atan2, ceil, cos, cosh, exp, floor, ln (calls tolog), log (calls tolog10 by default, see below), log10, pow, sin, sinh, sqrt, tan, tanh
The following functions are also built-in and provided by TinyExpr:
fac (factorials e.g.fac 5 == 120)
ncr (combinations e.g.ncr(6,2) == 15)
npr (permutations e.g.npr(6,2) == 30)
Also, the following constants are available:
pi,e
Compile-time options
By default, TinyExpr does exponentiation from left to right. For example:
a^b^c == (a^b)^c and-a^b == (-a)^b
This is by design. It's the way that spreadsheets do it (e.g. Excel, Google Sheets).
If you would rather have exponentiation work from right to left, you need todefineTE_POW_FROM_RIGHT when compilingtinyexpr.c. There is acommented-out define near the top of that file. With this option enabled, thebehaviour is:
a^b^c == a^(b^c) and-a^b == -(a^b)
That will match how many scripting languages do it (e.g. Python, Ruby).
Also, if you'd likelog to default to the natural log instead oflog10,then you can defineTE_NAT_LOG.
Hints
All functions/types start with the letterste.
To allow constant optimization, surround constant expressions in parentheses.For example "x+(1+5)" will evaluate the "(1+5)" expression at compile time andcompile the entire expression as "x+6", saving a runtime calculation. Theparentheses are important, because TinyExpr will not change the order ofevaluation. If you instead compiled "x+1+5" TinyExpr will insist that "1" isadded to "x" first, and "5" is added the result second.
About
tiny recursive descent expression parser, compiler, and evaluation engine for math expressions