Skip to content

Commit ec3dde1

Browse files
committed
Initial commit
0 parents  commit ec3dde1

14 files changed

+1316
-0
lines changed

LICENSE.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
TINYEXPR - Tiny recursive descent parser and evaluation engine in C
2+
3+
Copyright (c) 2015, 2016 Lewis Van Winkle
4+
5+
http://CodePlea.com
6+
7+
This software is provided 'as-is', without any express or implied
8+
warranty. In no event will the authors be held liable for any damages
9+
arising from the use of this software.
10+
11+
Permission is granted to anyone to use this software for any purpose,
12+
including commercial applications, and to alter it and redistribute it
13+
freely, subject to the following restrictions:
14+
15+
1. The origin of this software must not be misrepresented; you must not
16+
claim that you wrote the original software. If you use this software
17+
in a product, an acknowledgement in the product documentation would be
18+
appreciated but is not required.
19+
2. Altered source versions must be plainly marked as such, and must not be
20+
misrepresented as being the original software.
21+
3. This notice may not be removed or altered from any source distribution.
22+

Makefile

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
CC = gcc
2+
CCFLAGS = -ansi -Wall -Wshadow -O2 $(EXTRAS)
3+
4+
5+
all: test bench example example2
6+
7+
8+
test: test.o tinyexpr.o
9+
$(CC) $(CCFLAGS) -o $@ $^
10+
./$@
11+
12+
13+
bench: benchmark.o tinyexpr.o
14+
$(CC) $(CCFLAGS) -o $@ $^
15+
16+
example: example.o tinyexpr.o
17+
$(CC) $(CCFLAGS) -o $@ $^
18+
19+
example2: example2.o tinyexpr.o
20+
$(CC) $(CCFLAGS) -o $@ $^
21+
22+
.c.o:
23+
$(CC) -c $(CCFLAGS) $< -o $@
24+
25+
26+
clean:
27+
rm *.o
28+
rm *.exe

README.md

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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+
![example syntax tree](doc/e1.png?raw=true)
162+
163+
**te_compile** also automatically prunes constant branches. In this example,
164+
the compiled expression returned by *te_compile* is:
165+
166+
![example syntax tree](doc/e2.png?raw=true)
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

Comments
 (0)