• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

doc/H03-May-2022-

.travis.ymlH A D24-Jul-201855

CONTRIBUTINGH A D24-Jul-2018529

LICENSEH A D24-Jul-2018877

MakefileH A D24-Jul-2018675

README.mdH A D24-Jul-20189.8 KiB

benchmark.cH A D24-Jul-20182.8 KiB

example.cH A D24-Jul-2018241

example2.cH A D24-Jul-20181,007

example3.cH A D24-Jul-2018722

minctest.hH A D24-Jul-20183.2 KiB

test.cH A D24-Jul-201815.8 KiB

tinyexpr.cH A D24-Jul-201820.8 KiB

tinyexpr.hH A D24-Jul-20182.3 KiB

README.md

1[![Build Status](https://travis-ci.org/codeplea/tinyexpr.svg?branch=master)](https://travis-ci.org/codeplea/tinyexpr)
2
3
4<img alt="TinyExpr logo" src="https://codeplea.com/public/content/tinyexpr_logo.png" align="right"/>
5
6# TinyExpr
7
8TinyExpr is a very small recursive descent parser and evaluation engine for
9math expressions. It's handy when you want to add the ability to evaluation
10math expressions at runtime without adding a bunch of cruft to you project.
11
12In addition to the standard math operators and precedence, TinyExpr also supports
13the standard C math functions and runtime binding of variables.
14
15## Features
16
17- **ANSI C with no dependencies**.
18- Single source file and header file.
19- Simple and fast.
20- Implements standard operators precedence.
21- Exposes standard C math functions (sin, sqrt, ln, etc.).
22- Can add custom functions and variables easily.
23- Can bind variables at eval-time.
24- Released under the zlib license - free for nearly any use.
25- Easy to use and integrate with your code
26- Thread-safe, provided that your *malloc* is.
27
28## Building
29
30TinyExpr is self-contained in two files: `tinyexpr.c` and `tinyexpr.h`. To use
31TinyExpr, simply add those two files to your project.
32
33## Short Example
34
35Here is a minimal example to evaluate an expression at runtime.
36
37```C
38    #include "tinyexpr.h"
39    printf("%f\n", te_interp("5*5", 0)); /* Prints 25. */
40```
41
42
43## Usage
44
45TinyExpr defines only four functions:
46
47```C
48    double te_interp(const char *expression, int *error);
49    te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error);
50    double te_eval(const te_expr *expr);
51    void te_free(te_expr *expr);
52```
53
54## te_interp
55```C
56    double te_interp(const char *expression, int *error);
57```
58
59`te_interp()` takes an expression and immediately returns the result of it. If there
60is a parse error, `te_interp()` returns NaN.
61
62If the `error` pointer argument is not 0, then `te_interp()` will set `*error` to the position
63of the parse error on failure, and set `*error` to 0 on success.
64
65**example usage:**
66
67```C
68    int error;
69
70    double a = te_interp("(5+5)", 0); /* Returns 10. */
71    double b = te_interp("(5+5)", &error); /* Returns 10, error is set to 0. */
72    double c = te_interp("(5+5", &error); /* Returns NaN, error is set to 4. */
73```
74
75## te_compile, te_eval, te_free
76```C
77    te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
78    double te_eval(const te_expr *n);
79    void te_free(te_expr *n);
80```
81
82Give `te_compile()` an expression with unbound variables and a list of
83variable names and pointers. `te_compile()` will return a `te_expr*` which can
84be evaluated later using `te_eval()`. On failure, `te_compile()` will return 0
85and optionally set the passed in `*error` to the location of the parse error.
86
87You may also compile expressions without variables by passing `te_compile()`'s second
88and thrid arguments as 0.
89
90Give `te_eval()` a `te_expr*` from `te_compile()`. `te_eval()` will evaluate the expression
91using the current variable values.
92
93After you're finished, make sure to call `te_free()`.
94
95**example usage:**
96
97```C
98    double x, y;
99    /* Store variable names and pointers. */
100    te_variable vars[] = {{"x", &x}, {"y", &y}};
101
102    int err;
103    /* Compile the expression with variables. */
104    te_expr *expr = te_compile("sqrt(x^2+y^2)", vars, 2, &err);
105
106    if (expr) {
107        x = 3; y = 4;
108        const double h1 = te_eval(expr); /* Returns 5. */
109
110        x = 5; y = 12;
111        const double h2 = te_eval(expr); /* Returns 13. */
112
113        te_free(expr);
114    } else {
115        printf("Parse error at %d\n", err);
116    }
117
118```
119
120## Longer Example
121
122Here is a complete example that will evaluate an expression passed in from the command
123line. It also does error checking and binds the variables `x` and `y` to *3* and *4*, respectively.
124
125```C
126    #include "tinyexpr.h"
127    #include <stdio.h>
128
129    int main(int argc, char *argv[])
130    {
131        if (argc < 2) {
132            printf("Usage: example2 \"expression\"\n");
133            return 0;
134        }
135
136        const char *expression = argv[1];
137        printf("Evaluating:\n\t%s\n", expression);
138
139        /* This shows an example where the variables
140         * x and y are bound at eval-time. */
141        double x, y;
142        te_variable vars[] = {{"x", &x}, {"y", &y}};
143
144        /* This will compile the expression and check for errors. */
145        int err;
146        te_expr *n = te_compile(expression, vars, 2, &err);
147
148        if (n) {
149            /* The variables can be changed here, and eval can be called as many
150             * times as you like. This is fairly efficient because the parsing has
151             * already been done. */
152            x = 3; y = 4;
153            const double r = te_eval(n); printf("Result:\n\t%f\n", r);
154            te_free(n);
155        } else {
156            /* Show the user where the error is at. */
157            printf("\t%*s^\nError near here", err-1, "");
158        }
159
160        return 0;
161    }
162```
163
164
165This produces the output:
166
167    $ example2 "sqrt(x^2+y2)"
168        Evaluating:
169                sqrt(x^2+y2)
170                          ^
171        Error near here
172
173
174    $ example2 "sqrt(x^2+y^2)"
175        Evaluating:
176                sqrt(x^2+y^2)
177        Result:
178                5.000000
179
180
181## Binding to Custom Functions
182
183TinyExpr can also call to custom functions implemented in C. Here is a short example:
184
185```C
186double my_sum(double a, double b) {
187    /* Example C function that adds two numbers together. */
188    return a + b;
189}
190
191te_variable vars[] = {
192    {"mysum", my_sum, TE_FUNCTION2} /* TE_FUNCTION2 used because my_sum takes two arguments. */
193};
194
195te_expr *n = te_compile("mysum(5, 6)", vars, 1, 0);
196
197```
198
199
200## How it works
201
202`te_compile()` uses a simple recursive descent parser to compile your
203expression into a syntax tree. For example, the expression `"sin x + 1/4"`
204parses as:
205
206![example syntax tree](doc/e1.png?raw=true)
207
208`te_compile()` also automatically prunes constant branches. In this example,
209the compiled expression returned by `te_compile()` would become:
210
211![example syntax tree](doc/e2.png?raw=true)
212
213`te_eval()` will automatically load in any variables by their pointer, and then evaluate
214and return the result of the expression.
215
216`te_free()` should always be called when you're done with the compiled expression.
217
218
219## Speed
220
221
222TinyExpr is pretty fast compared to C when the expression is short, when the
223expression does hard calculations (e.g. exponentiation), and when some of the
224work can be simplified by `te_compile()`. TinyExpr is slow compared to C when the
225expression is long and involves only basic arithmetic.
226
227Here is some example performance numbers taken from the included
228**benchmark.c** program:
229
230| Expression | te_eval time | native C time | slowdown  |
231| :------------- |-------------:| -----:|----:|
232| sqrt(a^1.5+a^2.5) | 15,641 ms | 14,478 ms | 8% slower |
233| a+5 | 765 ms | 563 ms | 36% slower |
234| a+(5*2) | 765 ms | 563 ms | 36% slower |
235| (a+5)*2 | 1422 ms | 563 ms | 153% slower |
236| (1/(a+1)+2/(a+2)+3/(a+3)) | 5,516 ms | 1,266 ms | 336% slower |
237
238
239
240## Grammar
241
242TinyExpr parses the following grammar:
243
244    <list>      =    <expr> {"," <expr>}
245    <expr>      =    <term> {("+" | "-") <term>}
246    <term>      =    <factor> {("*" | "/" | "%") <factor>}
247    <factor>    =    <power> {"^" <power>}
248    <power>     =    {("-" | "+")} <base>
249    <base>      =    <constant>
250                   | <variable>
251                   | <function-0> {"(" ")"}
252                   | <function-1> <power>
253                   | <function-X> "(" <expr> {"," <expr>} ")"
254                   | "(" <list> ")"
255
256In addition, whitespace between tokens is ignored.
257
258Valid variable names consist of a lower case letter followed by any combination
259of: lower case letters *a* through *z*, the digits *0* through *9*, and
260underscore. Constants can be integers, decimal numbers, or in scientific
261notation (e.g.  *1e3* for *1000*). A leading zero is not required (e.g. *.5*
262for *0.5*)
263
264
265## Functions supported
266
267TinyExpr supports addition (+), subtraction/negation (-), multiplication (\*),
268division (/), exponentiation (^) and modulus (%) with the normal operator
269precedence (the one exception being that exponentiation is evaluated
270left-to-right, but this can be changed - see below).
271
272The following C math functions are also supported:
273
274- abs (calls to *fabs*), acos, asin, atan, atan2, ceil, cos, cosh, exp, floor, ln (calls to *log*), log (calls to *log10* by default, see below), log10, pow, sin, sinh, sqrt, tan, tanh
275
276The following functions are also built-in and provided by TinyExpr:
277
278- fac (factorials e.g. `fac 5` == 120)
279- ncr (combinations e.g. `ncr(6,2)` == 15)
280- npr (permutations e.g. `npr(6,2)` == 30)
281
282Also, the following constants are available:
283
284- `pi`, `e`
285
286
287## Compile-time options
288
289
290By default, TinyExpr does exponentiation from left to right. For example:
291
292`a^b^c == (a^b)^c` and `-a^b == (-a)^b`
293
294This is by design. It's the way that spreadsheets do it (e.g. Excel, Google Sheets).
295
296
297If you would rather have exponentiation work from right to left, you need to
298define `TE_POW_FROM_RIGHT` when compiling `tinyexpr.c`. There is a
299commented-out define near the top of that file. With this option enabled, the
300behaviour is:
301
302`a^b^c == a^(b^c)` and `-a^b == -(a^b)`
303
304That will match how many scripting languages do it (e.g. Python, Ruby).
305
306Also, if you'd like `log` to default to the natural log instead of `log10`,
307then you can define `TE_NAT_LOG`.
308
309## Hints
310
311- All functions/types start with the letters *te*.
312
313- To allow constant optimization, surround constant expressions in parentheses.
314  For example "x+(1+5)" will evaluate the "(1+5)" expression at compile time and
315  compile the entire expression as "x+6", saving a runtime calculation. The
316  parentheses are important, because TinyExpr will not change the order of
317  evaluation. If you instead compiled "x+1+5" TinyExpr will insist that "1" is
318  added to "x" first, and "5" is added the result second.
319
320