mirror of
https://github.com/eledio-devices/thirdparty-tinyexpr.git
synced 2025-10-30 16:15:41 +01:00
Initial commit
This commit is contained in:
22
LICENSE.md
Normal file
22
LICENSE.md
Normal file
@@ -0,0 +1,22 @@
|
||||
TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
|
||||
Copyright (c) 2015, 2016 Lewis Van Winkle
|
||||
|
||||
http://CodePlea.com
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgement in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
28
Makefile
Normal file
28
Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
CC = gcc
|
||||
CCFLAGS = -ansi -Wall -Wshadow -O2 $(EXTRAS)
|
||||
|
||||
|
||||
all: test bench example example2
|
||||
|
||||
|
||||
test: test.o tinyexpr.o
|
||||
$(CC) $(CCFLAGS) -o $@ $^
|
||||
./$@
|
||||
|
||||
|
||||
bench: benchmark.o tinyexpr.o
|
||||
$(CC) $(CCFLAGS) -o $@ $^
|
||||
|
||||
example: example.o tinyexpr.o
|
||||
$(CC) $(CCFLAGS) -o $@ $^
|
||||
|
||||
example2: example2.o tinyexpr.o
|
||||
$(CC) $(CCFLAGS) -o $@ $^
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CCFLAGS) $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
rm *.o
|
||||
rm *.exe
|
||||
240
README.md
Normal file
240
README.md
Normal file
@@ -0,0 +1,240 @@
|
||||
#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.
|
||||
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)";
|
||||
double r = te_interp(c, 0);
|
||||
printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
That produces the following output:
|
||||
|
||||
The expression:
|
||||
sqrt(5^2+7^2+11^2+(8-2)^2)
|
||||
evaluates to:
|
||||
15.198684
|
||||
|
||||
|
||||
##Longer Example
|
||||
|
||||
Here is an example that will evaluate an expression passed in from the command
|
||||
line. It also does error checking and binds the variables *x* and *y*.
|
||||
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("Usage: example2 \"expression\"\n", argv[0]);
|
||||
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 (!err) {
|
||||
/* 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); }
|
||||
else {
|
||||
/* Show the user where the error is at. */
|
||||
printf("\t%*s^\nError near here", err-1, "");
|
||||
}
|
||||
|
||||
/* te_free should always be called after te_compile. */
|
||||
te_free(n);
|
||||
|
||||
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
|
||||
|
||||
|
||||
##Usage
|
||||
|
||||
TINYEXPR defines only five functions:
|
||||
|
||||
double te_interp(const char *expression, int *error);
|
||||
te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
|
||||
double te_eval(te_expr *n);
|
||||
void te_print(const te_expr *n);
|
||||
void te_free(te_expr *n);
|
||||
|
||||
**te_interp** takes an expression and immediately returns the result of it. If
|
||||
an error pointer is passed in, *te_interp* will set it to 0 for success or
|
||||
approximately the position of the error for failure. If you don't care about
|
||||
errors, just pass in 0.
|
||||
|
||||
**te_interp example:**
|
||||
|
||||
double x = te_interp("5+5", 0);
|
||||
|
||||
**te_compile** will compile an expression with unbound variables, which will
|
||||
be suitable to evaluate later. **te_eval** can then be called on the compiled
|
||||
expression repeatedly to evaluate it with different variable values. **te_free**
|
||||
should be called after you're finished.
|
||||
|
||||
**te_compile example:**
|
||||
|
||||
double x, y;
|
||||
te_variable vars[] = {{"x", &x}, {"y", &y}};
|
||||
|
||||
int err;
|
||||
te_expr *expr = te_compile("sqrt(x^2+y^2)", vars, 2, &err);
|
||||
|
||||
if (!err) {
|
||||
x = 3; y = 4;
|
||||
const double h1 = te_eval(expr);
|
||||
|
||||
x = 5; y = 7;
|
||||
const double h2 = te_eval(expr);
|
||||
}
|
||||
|
||||
te_free(expr);
|
||||
|
||||
**te_print** will display some (possibly not so) useful debugging
|
||||
information about the return value of *te_compile*.
|
||||
|
||||
|
||||
##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* is:
|
||||
|
||||

|
||||
|
||||
**te_eval** will automatically load in any variables by their pointer, 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*.
|
||||
|
||||
- Remember to always call *te_free* on the result of *te_compile*, even if
|
||||
there is an error.
|
||||
|
||||
- If there is an error, you can usually still evaluate the first part of the
|
||||
expression. This may or may not be useful to you.
|
||||
|
||||
- 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.
|
||||
|
||||
124
benchmark.c
Normal file
124
benchmark.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
*
|
||||
* Copyright (c) 2015, 2016 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include "tinyexpr.h"
|
||||
|
||||
|
||||
|
||||
#define loops 10000
|
||||
|
||||
|
||||
|
||||
|
||||
void bench(const char *expr, te_fun1 func) {
|
||||
int i, j;
|
||||
volatile double d;
|
||||
double tmp;
|
||||
clock_t start;
|
||||
|
||||
te_variable lk = {"a", &tmp};
|
||||
|
||||
printf("Expression: %s\n", expr);
|
||||
|
||||
printf("native ");
|
||||
start = clock();
|
||||
d = 0;
|
||||
for (j = 0; j < loops; ++j)
|
||||
for (i = 0; i < loops; ++i) {
|
||||
tmp = i;
|
||||
d += func(tmp);
|
||||
}
|
||||
const int nelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC;
|
||||
|
||||
/*Million floats per second input.*/
|
||||
printf(" %.5g", d);
|
||||
if (nelapsed)
|
||||
printf("\t%5dms\t%5lumfps\n", nelapsed, loops * loops / nelapsed / 1000);
|
||||
else
|
||||
printf("\tinf\n");
|
||||
|
||||
|
||||
|
||||
|
||||
printf("interp ");
|
||||
te_expr *n = te_compile(expr, &lk, 1, 0);
|
||||
start = clock();
|
||||
d = 0;
|
||||
for (j = 0; j < loops; ++j)
|
||||
for (i = 0; i < loops; ++i) {
|
||||
tmp = i;
|
||||
d += te_eval(n);
|
||||
}
|
||||
const int eelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC;
|
||||
te_free(n);
|
||||
|
||||
/*Million floats per second input.*/
|
||||
printf(" %.5g", d);
|
||||
if (eelapsed)
|
||||
printf("\t%5dms\t%5lumfps\n", eelapsed, loops * loops / eelapsed / 1000);
|
||||
else
|
||||
printf("\tinf\n");
|
||||
|
||||
|
||||
printf("%.2f%% longer\n", (((double)eelapsed / nelapsed) - 1.0) * 100.0);
|
||||
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
double a5(double a) {
|
||||
return a+5;
|
||||
}
|
||||
|
||||
double a52(double a) {
|
||||
return (a+5)*2;
|
||||
}
|
||||
|
||||
double a10(double a) {
|
||||
return a+(5*2);
|
||||
}
|
||||
|
||||
double as(double a) {
|
||||
return sqrt(pow(a, 1.5) + pow(a, 2.5));
|
||||
}
|
||||
|
||||
double al(double a) {
|
||||
return (1/(a+1)+2/(a+2)+3/(a+3));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
bench("sqrt(a^1.5+a^2.5)", as);
|
||||
bench("a+5", a5);
|
||||
bench("a+(5*2)", a10);
|
||||
bench("(a+5)*2", a52);
|
||||
bench("(1/(a+1)+2/(a+2)+3/(a+3))", al);
|
||||
|
||||
return 0;
|
||||
}
|
||||
8
doc/e1.dot
Normal file
8
doc/e1.dot
Normal file
@@ -0,0 +1,8 @@
|
||||
digraph G {
|
||||
"+" -> "sin";
|
||||
"+" -> div;
|
||||
"sin" -> "x";
|
||||
div -> "1";
|
||||
div -> "4";
|
||||
div [label="÷"]
|
||||
}
|
||||
BIN
doc/e1.png
Normal file
BIN
doc/e1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
5
doc/e2.dot
Normal file
5
doc/e2.dot
Normal file
@@ -0,0 +1,5 @@
|
||||
digraph G {
|
||||
"+" -> "sin";
|
||||
"+" -> "0.25";
|
||||
"sin" -> "x";
|
||||
}
|
||||
BIN
doc/e2.png
Normal file
BIN
doc/e2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
10
example.c
Normal file
10
example.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)";
|
||||
double r = te_interp(c, 0);
|
||||
printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r);
|
||||
return 0;
|
||||
}
|
||||
39
example2.c
Normal file
39
example2.c
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("Usage: example2 \"expression\"\n", argv[0]);
|
||||
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 (!err) {
|
||||
/* 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); }
|
||||
else {
|
||||
/* Show the user where the error is at. */
|
||||
printf("\t%*s^\nError near here", err-1, "");
|
||||
}
|
||||
|
||||
/* te_free should always be called after te_compile. */
|
||||
te_free(n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
127
minctest.h
Normal file
127
minctest.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
*
|
||||
* MINCTEST - Minimal C Test Library - 0.1
|
||||
*
|
||||
* Copyright (c) 2014, 2015 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* MINCTEST - Minimal testing library for C
|
||||
*
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void test1() {
|
||||
* lok('a' == 'a');
|
||||
* }
|
||||
*
|
||||
* void test2() {
|
||||
* lequal(5, 6);
|
||||
* lfequal(5.5, 5.6);
|
||||
* }
|
||||
*
|
||||
* int main() {
|
||||
* lrun("test1", test1);
|
||||
* lrun("test2", test2);
|
||||
* lresults();
|
||||
* return lfails != 0;
|
||||
* }
|
||||
*
|
||||
*
|
||||
*
|
||||
* Hints:
|
||||
* All functions/variables start with the letter 'l'.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __MINCTEST_H__
|
||||
#define __MINCTEST_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* How far apart can floats be before we consider them unequal. */
|
||||
#define LTEST_FLOAT_TOLERANCE 0.001
|
||||
|
||||
|
||||
/* Track the number of passes, fails. */
|
||||
/* NB this is made for all tests to be in one file. */
|
||||
static int ltests = 0;
|
||||
static int lfails = 0;
|
||||
|
||||
|
||||
/* Display the test results. */
|
||||
#define lresults() do {\
|
||||
if (lfails == 0) {\
|
||||
printf("ALL TESTS PASSED (%d/%d)\n", ltests, ltests);\
|
||||
} else {\
|
||||
printf("SOME TESTS FAILED (%d/%d)\n", ltests-lfails, ltests);\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Run a test. Name can be any string to print out, test is the function name to call. */
|
||||
#define lrun(name, test) do {\
|
||||
const int ts = ltests;\
|
||||
const int fs = lfails;\
|
||||
const clock_t start = clock();\
|
||||
printf("\t%-14s", name);\
|
||||
test();\
|
||||
printf("pass:%2d fail:%2d %4dms\n",\
|
||||
(ltests-ts)-(lfails-fs), lfails-fs,\
|
||||
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC));\
|
||||
} while (0)
|
||||
|
||||
|
||||
/* Assert a true statement. */
|
||||
#define lok(test) do {\
|
||||
++ltests;\
|
||||
if (!(test)) {\
|
||||
++lfails;\
|
||||
printf("%s:%d error \n", __FILE__, __LINE__);\
|
||||
}} while (0)
|
||||
|
||||
|
||||
/* Assert two integers are equal. */
|
||||
#define lequal(a, b) do {\
|
||||
++ltests;\
|
||||
if ((a) != (b)) {\
|
||||
++lfails;\
|
||||
printf("%s:%d (%d != %d)\n", __FILE__, __LINE__, (a), (b));\
|
||||
}} while (0)
|
||||
|
||||
|
||||
/* Assert two floats are equal (Within LTEST_FLOAT_TOLERANCE). */
|
||||
#define lfequal(a, b) do {\
|
||||
++ltests;\
|
||||
if (fabs((double)(a)-(double)(b)) > LTEST_FLOAT_TOLERANCE) {\
|
||||
++lfails;\
|
||||
printf("%s:%d (%f != %f)\n", __FILE__, __LINE__, (double)(a), (double)(b));\
|
||||
}} while (0)
|
||||
|
||||
|
||||
#endif /*__MINCTEST_H__*/
|
||||
233
test.c
Normal file
233
test.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
*
|
||||
* Copyright (c) 2015, 2016 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
#include "minctest.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *expr;
|
||||
double answer;
|
||||
} test_case;
|
||||
|
||||
|
||||
test_case cases[] = {
|
||||
{"1", 1},
|
||||
{"(1)", 1},
|
||||
|
||||
{"2+1", 2+1},
|
||||
{"(((2+(1))))", 2+1},
|
||||
{"3+2", 3+2},
|
||||
|
||||
{"3+2+4", 3+2+4},
|
||||
{"(3+2)+4", 3+2+4},
|
||||
{"3+(2+4)", 3+2+4},
|
||||
{"(3+2+4)", 3+2+4},
|
||||
|
||||
{"3*2*4", 3*2*4},
|
||||
{"(3*2)*4", 3*2*4},
|
||||
{"3*(2*4)", 3*2*4},
|
||||
{"(3*2*4)", 3*2*4},
|
||||
|
||||
{"3-2-4", 3-2-4},
|
||||
{"(3-2)-4", (3-2)-4},
|
||||
{"3-(2-4)", 3-(2-4)},
|
||||
{"(3-2-4)", 3-2-4},
|
||||
|
||||
{"3/2/4", 3.0/2.0/4.0},
|
||||
{"(3/2)/4", (3.0/2.0)/4.0},
|
||||
{"3/(2/4)", 3.0/(2.0/4.0)},
|
||||
{"(3/2/4)", 3.0/2.0/4.0},
|
||||
|
||||
{"(3*2/4)", 3.0*2.0/4.0},
|
||||
{"(3/2*4)", 3.0/2.0*4.0},
|
||||
{"3*(2/4)", 3.0*(2.0/4.0)},
|
||||
|
||||
{"asin sin .5", 0.5},
|
||||
{"sin asin .5", 0.5},
|
||||
{"ln exp .5", 0.5},
|
||||
{"exp ln .5", 0.5},
|
||||
|
||||
{"asin sin-.5", -0.5},
|
||||
{"asin sin-0.5", -0.5},
|
||||
{"asin sin -0.5", -0.5},
|
||||
{"asin (sin -0.5)", -0.5},
|
||||
{"asin (sin (-0.5))", -0.5},
|
||||
{"asin sin (-0.5)", -0.5},
|
||||
{"(asin sin (-0.5))", -0.5},
|
||||
|
||||
{"log1000", 3},
|
||||
{"log1e3", 3},
|
||||
{"log 1000", 3},
|
||||
{"log 1e3", 3},
|
||||
{"log(1000)", 3},
|
||||
{"log(1e3)", 3},
|
||||
{"log1.0e3", 3},
|
||||
{"10^5*5e-5", 5},
|
||||
|
||||
{"100^.5+1", 11},
|
||||
{"100 ^.5+1", 11},
|
||||
{"100^+.5+1", 11},
|
||||
{"100^--.5+1", 11},
|
||||
{"100^---+-++---++-+-+-.5+1", 11},
|
||||
|
||||
{"100^-.5+1", 1.1},
|
||||
{"100^---.5+1", 1.1},
|
||||
{"100^+---.5+1", 1.1},
|
||||
{"1e2^+---.5e0+1e0", 1.1},
|
||||
{"--(1e2^(+(-(-(-.5e0))))+1e0)", 1.1},
|
||||
|
||||
{"sqrt 100 + 7", 17},
|
||||
{"sqrt 100 * 7", 70},
|
||||
{"sqrt (100 * 100)", 100},
|
||||
|
||||
};
|
||||
|
||||
|
||||
test_case errors[] = {
|
||||
{"1+", 2},
|
||||
{"1)", 2},
|
||||
{"(1", 2},
|
||||
{"1**1", 3},
|
||||
{"1*2(+4", 4},
|
||||
{"1*2(1+4", 4},
|
||||
{"a+5", 1},
|
||||
{"A+5", 1},
|
||||
{"Aa+5", 1},
|
||||
{"1^^5", 3},
|
||||
{"1**5", 3},
|
||||
{"sin(cos5", 8},
|
||||
};
|
||||
|
||||
|
||||
void test1() {
|
||||
int i;
|
||||
for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
|
||||
const char *expr = cases[i].expr;
|
||||
const double answer = cases[i].answer;
|
||||
|
||||
int err;
|
||||
const double ev = te_interp(expr, &err);
|
||||
lok(!err);
|
||||
lfequal(ev, answer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test2() {
|
||||
int i;
|
||||
for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) {
|
||||
const char *expr = errors[i].expr;
|
||||
const int e = errors[i].answer;
|
||||
|
||||
int err;
|
||||
te_interp(expr, &err);
|
||||
lequal(err, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void test3() {
|
||||
|
||||
double x, y;
|
||||
te_variable lookup[] = {{"x", &x}, {"y", &y}};
|
||||
|
||||
int err;
|
||||
|
||||
te_expr *expr1 = te_compile("cos x + sin y", lookup, 2, &err);
|
||||
lok(!err);
|
||||
|
||||
te_expr *expr2 = te_compile("x+x+x-y", lookup, 2, &err);
|
||||
lok(!err);
|
||||
|
||||
te_expr *expr3 = te_compile("x*y^3", lookup, 2, &err);
|
||||
lok(!err);
|
||||
|
||||
for (y = 2; y < 3; ++y) {
|
||||
for (x = 0; x < 5; ++x) {
|
||||
double ev = te_eval(expr1);
|
||||
lfequal(ev, cos(x) + sin(y));
|
||||
|
||||
ev = te_eval(expr2);
|
||||
lfequal(ev, x+x+x-y);
|
||||
|
||||
ev = te_eval(expr3);
|
||||
lfequal(ev, x*y*y*y);
|
||||
}
|
||||
}
|
||||
|
||||
te_free(expr1);
|
||||
te_free(expr2);
|
||||
te_free(expr3);
|
||||
}
|
||||
|
||||
|
||||
#define cross_check(a, b) do {\
|
||||
expr = te_compile((a), &lookup, 1, &err);\
|
||||
lfequal(te_eval(expr), (b));\
|
||||
lok(!err);\
|
||||
te_free(expr);\
|
||||
}while(0)
|
||||
|
||||
void test4() {
|
||||
|
||||
double x;
|
||||
te_variable lookup = {"x", &x};
|
||||
|
||||
int err;
|
||||
te_expr *expr;
|
||||
|
||||
for (x = -5; x < 5; x += .2) {
|
||||
cross_check("abs x", fabs(x));
|
||||
cross_check("acos x", acos(x));
|
||||
cross_check("asin x", asin(x));
|
||||
cross_check("atan x", atan(x));
|
||||
cross_check("ceil x", ceil(x));
|
||||
cross_check("cos x", cos(x));
|
||||
cross_check("cosh x", cosh(x));
|
||||
cross_check("exp x", exp(x));
|
||||
cross_check("floor x", floor(x));
|
||||
cross_check("ln x", log(x));
|
||||
cross_check("log x", log10(x));
|
||||
cross_check("sin x", sin(x));
|
||||
cross_check("sinh x", sinh(x));
|
||||
cross_check("sqrt x", sqrt(x));
|
||||
cross_check("tan x", tan(x));
|
||||
cross_check("tanh x", tanh(x));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
lrun("Results", test1);
|
||||
lrun("Syntax", test2);
|
||||
lrun("Bind", test3);
|
||||
lrun("Functions", test4);
|
||||
lresults();
|
||||
|
||||
return lfails != 0;
|
||||
}
|
||||
406
tinyexpr.c
Normal file
406
tinyexpr.c
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
*
|
||||
* Copyright (c) 2015, 2016 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "tinyexpr.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
enum {TOK_NULL, TOK_END, TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_ADD, TOK_SUB, TOK_MUL, TOK_DIV, TOK_FUNCTION1, TOK_FUNCTION2, TOK_VARIABLE, TOK_ERROR};
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
const char *next;
|
||||
int type;
|
||||
union {double value; te_fun1 f1; te_fun2 f2; const double *var;};
|
||||
|
||||
const te_variable *lookup;
|
||||
int lookup_len;
|
||||
} state;
|
||||
|
||||
|
||||
|
||||
|
||||
static te_expr *new_expr(te_expr *l, te_expr *r) {
|
||||
te_expr *ret = malloc(sizeof(te_expr));
|
||||
ret->left = l;
|
||||
ret->right = r;
|
||||
ret->bound = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void te_free(te_expr *n) {
|
||||
if (n->left) te_free(n->left);
|
||||
if (n->right) te_free(n->right);
|
||||
free(n);
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
te_fun1 f1;
|
||||
} builtin;
|
||||
|
||||
|
||||
static const builtin functions[] = {
|
||||
/* must be in alphabetical order */
|
||||
{"abs", fabs},
|
||||
{"acos", acos},
|
||||
{"asin", asin},
|
||||
{"atan", atan},
|
||||
{"ceil", ceil},
|
||||
{"cos", cos},
|
||||
{"cosh", cosh},
|
||||
{"exp", exp},
|
||||
{"floor", floor},
|
||||
{"ln", log},
|
||||
{"log", log10},
|
||||
{"sin", sin},
|
||||
{"sinh", sinh},
|
||||
{"sqrt", sqrt},
|
||||
{"tan", tan},
|
||||
{"tanh", tanh},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
static const builtin *find_function(const char *name, int len) {
|
||||
int imin = 0;
|
||||
int imax = sizeof(functions) / sizeof(builtin) - 2;
|
||||
|
||||
/*Binary search.*/
|
||||
while (imax >= imin) {
|
||||
const int i = (imin + ((imax-imin)/2));
|
||||
int c = strncmp(name, functions[i].name, len);
|
||||
if (!c) c = len - strlen(functions[i].name);
|
||||
if (c == 0) {
|
||||
return functions + i;
|
||||
} else if (c > 0) {
|
||||
imin = i + 1;
|
||||
} else {
|
||||
imax = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const double *find_var(const state *s, const char *name, int len) {
|
||||
int i;
|
||||
if (!s->lookup) return 0;
|
||||
for (i = 0; i < s->lookup_len; ++i) {
|
||||
if (strlen(s->lookup[i].name) == len && strncmp(name, s->lookup[i].name, len) == 0) {
|
||||
return s->lookup[i].value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static double add(double a, double b) {return a + b;}
|
||||
static double sub(double a, double b) {return a - b;}
|
||||
static double mul(double a, double b) {return a * b;}
|
||||
static double divide(double a, double b) {return a / b;}
|
||||
static double mod(double a, double b) {return (long long)a % (long long)b;}
|
||||
static double negate(double a) {return -a;}
|
||||
|
||||
|
||||
void next_token(state *s) {
|
||||
s->type = TOK_NULL;
|
||||
|
||||
if (!*s->next){
|
||||
s->type = TOK_END;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
/* Try reading a number. */
|
||||
if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') {
|
||||
s->value = strtod(s->next, (char**)&s->next);
|
||||
s->type = TOK_NUMBER;
|
||||
} else {
|
||||
/* Look for a variable or builtin function call. */
|
||||
if (s->next[0] >= 'a' && s->next[0] <= 'z') {
|
||||
const char *start;
|
||||
start = s->next;
|
||||
while (s->next[0] >= 'a' && s->next[0] <= 'z') s->next++;
|
||||
|
||||
const double *var = find_var(s, start, s->next - start);
|
||||
if (var) {
|
||||
s->type = TOK_VARIABLE;
|
||||
s->var = var;
|
||||
} else {
|
||||
if (s->next - start > 15) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
s->type = TOK_FUNCTION1;
|
||||
const builtin *f = find_function(start, s->next - start);
|
||||
if (!f) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
s->f1 = f->f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Look for an operator or special character. */
|
||||
switch (s->next++[0]) {
|
||||
case '+': s->type = TOK_FUNCTION2; s->f2 = add; break;
|
||||
case '-': s->type = TOK_FUNCTION2; s->f2 = sub; break;
|
||||
case '*': s->type = TOK_FUNCTION2; s->f2 = mul; break;
|
||||
case '/': s->type = TOK_FUNCTION2; s->f2 = divide; break;
|
||||
case '^': s->type = TOK_FUNCTION2; s->f2 = pow; break;
|
||||
case '%': s->type = TOK_FUNCTION2; s->f2 = mod; break;
|
||||
case '(': s->type = TOK_OPEN; break;
|
||||
case ')': s->type = TOK_CLOSE; break;
|
||||
case ' ': case '\t': case '\n': case '\r': break;
|
||||
default: s->type = TOK_ERROR; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (s->type == TOK_NULL);
|
||||
}
|
||||
|
||||
|
||||
static te_expr *expr(state *s);
|
||||
static te_expr *power(state *s);
|
||||
|
||||
static te_expr *base(state *s) {
|
||||
/* <base> = <constant> | <variable> | <function> <power> | "(" <expr> ")" */
|
||||
te_expr *ret;
|
||||
|
||||
switch (s->type) {
|
||||
case TOK_NUMBER:
|
||||
ret = new_expr(0, 0);
|
||||
ret->value = s->value;
|
||||
next_token(s);
|
||||
break;
|
||||
|
||||
case TOK_VARIABLE:
|
||||
ret = new_expr(0, 0);
|
||||
ret->bound = s->var;
|
||||
next_token(s);
|
||||
break;
|
||||
|
||||
case TOK_FUNCTION1:
|
||||
ret = new_expr(0, 0);
|
||||
ret->f1 = s->f1;
|
||||
next_token(s);
|
||||
ret->left = power(s);
|
||||
break;
|
||||
|
||||
case TOK_OPEN:
|
||||
next_token(s);
|
||||
ret = expr(s);
|
||||
if (s->type != TOK_CLOSE) {
|
||||
s->type = TOK_ERROR;
|
||||
} else {
|
||||
next_token(s);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = new_expr(0, 0);
|
||||
s->type = TOK_ERROR;
|
||||
ret->value = 1.0/0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *power(state *s) {
|
||||
/* <power> = {("-" | "+")} <base> */
|
||||
int sign = 1;
|
||||
while (s->type == TOK_FUNCTION2 && (s->f2 == add || s->f2 == sub)) {
|
||||
if (s->f2 == sub) sign = -sign;
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
te_expr *ret;
|
||||
|
||||
if (sign == 1) {
|
||||
ret = base(s);
|
||||
} else {
|
||||
ret = new_expr(base(s), 0);
|
||||
ret->f1 = negate;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *factor(state *s) {
|
||||
/* <factor> = <power> {"^" <power>} */
|
||||
te_expr *ret = power(s);
|
||||
|
||||
while (s->type == TOK_FUNCTION2 && (s->f2 == pow)) {
|
||||
te_fun2 t = s->f2;
|
||||
next_token(s);
|
||||
ret = new_expr(ret, power(s));
|
||||
ret->f2 = t;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *term(state *s) {
|
||||
/* <term> = <factor> {("*" | "/" | "%") <factor>} */
|
||||
te_expr *ret = factor(s);
|
||||
|
||||
while (s->type == TOK_FUNCTION2 && (s->f2 == mul || s->f2 == divide || s->f2 == mod)) {
|
||||
te_fun2 t = s->f2;
|
||||
next_token(s);
|
||||
ret = new_expr(ret, factor(s));
|
||||
ret->f2 = t;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static te_expr *expr(state *s) {
|
||||
/* <expr> = <term> {("+" | "-") <term>} */
|
||||
te_expr *ret = term(s);
|
||||
|
||||
while (s->type == TOK_FUNCTION2 && (s->f2 == add || s->f2 == sub)) {
|
||||
te_fun2 t = s->f2;
|
||||
next_token(s);
|
||||
ret = new_expr(ret, term(s));
|
||||
ret->f2 = t;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
double te_eval(te_expr *n) {
|
||||
double ret;
|
||||
|
||||
if (n->bound) {
|
||||
ret = *n->bound;
|
||||
} else if (n->left == 0 && n->right == 0) {
|
||||
ret = n->value;
|
||||
} else if (n->left && n->right == 0) {
|
||||
ret = n->f1(te_eval(n->left));
|
||||
} else {
|
||||
ret = n->f2(te_eval(n->left), te_eval(n->right));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void optimize(te_expr *n) {
|
||||
/* Evaluates as much as possible. */
|
||||
if (n->bound) return;
|
||||
|
||||
if (n->left) optimize(n->left);
|
||||
if (n->right) optimize(n->right);
|
||||
|
||||
if (n->left && n->right)
|
||||
{
|
||||
if (n->left->left == 0 && n->left->right == 0 && n->right->left == 0 && n->right->right == 0 && n->right->bound == 0 && n->left->bound == 0)
|
||||
{
|
||||
const double r = n->f2(n->left->value, n->right->value);
|
||||
free(n->left); free(n->right);
|
||||
n->left = 0; n->right = 0;
|
||||
n->value = r;
|
||||
}
|
||||
} else if (n->left && !n->right) {
|
||||
if (n->left->left == 0 && n->left->right == 0 && n->left->bound == 0) {
|
||||
const double r = n->f1(n->left->value);
|
||||
free(n->left);
|
||||
n->left = 0;
|
||||
n->value = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error) {
|
||||
state s;
|
||||
s.start = s.next = expression;
|
||||
s.lookup = lookup;
|
||||
s.lookup_len = lookup_len;
|
||||
|
||||
next_token(&s);
|
||||
te_expr *root = expr(&s);
|
||||
|
||||
|
||||
if (s.type != TOK_END) {
|
||||
if (error) *error = (s.next - s.start);
|
||||
if (*error == 0) *error = 1;
|
||||
} else {
|
||||
optimize(root);
|
||||
if (error) *error = 0;
|
||||
}
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
double te_interp(const char *expression, int *error) {
|
||||
te_expr *n = te_compile(expression, 0, 0, error);
|
||||
double ret = te_eval(n);
|
||||
free(n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void pn (const te_expr *n, int depth) {
|
||||
int i;
|
||||
for (i = 0; i < depth; ++i) {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
if (n->bound) {
|
||||
printf("bound %p\n", n->bound);
|
||||
} else if (n->left == 0 && n->right == 0) {
|
||||
printf("%f\n", n->value);
|
||||
} else if (n->left && n->right == 0) {
|
||||
printf("f1 %p\n", n->left);
|
||||
pn(n->left, depth+1);
|
||||
} else {
|
||||
printf("f2 %p %p\n", n->left, n->right);
|
||||
pn(n->left, depth+1);
|
||||
pn(n->right, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void te_print(const te_expr *n) {
|
||||
pn(n, 0);
|
||||
}
|
||||
74
tinyexpr.h
Normal file
74
tinyexpr.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
|
||||
*
|
||||
* Copyright (c) 2015, 2016 Lewis Van Winkle
|
||||
*
|
||||
* http://CodePlea.com
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef __TINYEXPR_H__
|
||||
#define __TINYEXPR_H__
|
||||
|
||||
|
||||
|
||||
typedef double (*te_fun1)(double);
|
||||
typedef double (*te_fun2)(double, double);
|
||||
|
||||
|
||||
typedef struct te_expr {
|
||||
struct te_expr *left, *right;
|
||||
union {double value; te_fun1 f1; te_fun2 f2;};
|
||||
const double *bound;
|
||||
} te_expr;
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const double *value;
|
||||
} te_variable;
|
||||
|
||||
|
||||
/* Note on error handling:
|
||||
* If the parser encounters an error, it will still return
|
||||
* an expression up to that point (which may be worthless or useful.
|
||||
* If the error pointer parameter is passed in and not null, the
|
||||
* parser will set it to roughly the index of the error in the
|
||||
* input expression. If there is no error, the parse sets
|
||||
* the error pointer to 0.
|
||||
*/
|
||||
|
||||
|
||||
/* Parses the input expression, evaluates it, and frees it. */
|
||||
double te_interp(const char *expression, int *error);
|
||||
|
||||
/* Parses the input expression and binds variables. */
|
||||
te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
|
||||
|
||||
/* Evaluates the expression. */
|
||||
double te_eval(te_expr *n);
|
||||
|
||||
/* Prints debugging information on the syntax tree. */
|
||||
void te_print(const te_expr *n);
|
||||
|
||||
/* Frees the expression. */
|
||||
void te_free(te_expr *n);
|
||||
|
||||
|
||||
|
||||
#endif /*__TINYEXPR_H__*/
|
||||
Reference in New Issue
Block a user