From b9d1fc0d64c271e65a65560a89d443b763e9e25f Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Fri, 10 Aug 2018 21:38:25 +0200 Subject: [PATCH] + add: complete copy of TinyExpr fork for NP3 adaption --- tinyexpr/.travis.yml | 7 + tinyexpr/CONTRIBUTING | 10 + tinyexpr/Makefile | 33 ++ tinyexpr/README.md | 319 +++++++++++++++++++ tinyexpr/benchmark.c | 125 ++++++++ tinyexpr/doc/e1.dot | 8 + tinyexpr/doc/e1.png | Bin 0 -> 11883 bytes tinyexpr/doc/e2.dot | 5 + tinyexpr/doc/e2.png | Bin 0 -> 10032 bytes tinyexpr/example.c | 10 + tinyexpr/example2.c | 38 +++ tinyexpr/example3.c | 35 +++ tinyexpr/minctest.h | 128 ++++++++ tinyexpr/test.c | 691 ++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 1409 insertions(+) create mode 100644 tinyexpr/.travis.yml create mode 100644 tinyexpr/CONTRIBUTING create mode 100644 tinyexpr/Makefile create mode 100644 tinyexpr/README.md create mode 100644 tinyexpr/benchmark.c create mode 100644 tinyexpr/doc/e1.dot create mode 100644 tinyexpr/doc/e1.png create mode 100644 tinyexpr/doc/e2.dot create mode 100644 tinyexpr/doc/e2.png create mode 100644 tinyexpr/example.c create mode 100644 tinyexpr/example2.c create mode 100644 tinyexpr/example3.c create mode 100644 tinyexpr/minctest.h create mode 100644 tinyexpr/test.c diff --git a/tinyexpr/.travis.yml b/tinyexpr/.travis.yml new file mode 100644 index 000000000..d275c1a7b --- /dev/null +++ b/tinyexpr/.travis.yml @@ -0,0 +1,7 @@ +language: c + +compiler: + - clang + - gcc + +script: make diff --git a/tinyexpr/CONTRIBUTING b/tinyexpr/CONTRIBUTING new file mode 100644 index 000000000..f27fc96f8 --- /dev/null +++ b/tinyexpr/CONTRIBUTING @@ -0,0 +1,10 @@ +A core strength of TinyExpr is that it is small and simple. This makes it easy +to add new features. However, if we keep adding new features, it'll no longer +be small or simple. In other words, each new feature corrodes away at the core +strength of TinyExpr. + +If you want to add a new feature, and you expect me to merge it, please discuss +it with me before you go to that work. Open an issue at +https://github.com/codeplea/tinyexpr and let us know what you're proposing. + +Bug fixes are always welcome and appreciated, of course. diff --git a/tinyexpr/Makefile b/tinyexpr/Makefile new file mode 100644 index 000000000..cc9765c88 --- /dev/null +++ b/tinyexpr/Makefile @@ -0,0 +1,33 @@ +CCFLAGS = -ansi -Wall -Wshadow -O2 +LFLAGS = -lm + +.PHONY = all clean + +all: test test_pr bench example example2 example3 + + +test: test.c tinyexpr.c + $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) + ./$@ + +test_pr: test.c tinyexpr.c + $(CC) $(CCFLAGS) -DTE_POW_FROM_RIGHT -DTE_NAT_LOG -o $@ $^ $(LFLAGS) + ./$@ + +bench: benchmark.o tinyexpr.o + $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) + +example: example.o tinyexpr.o + $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) + +example2: example2.o tinyexpr.o + $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) + +example3: example3.o tinyexpr.o + $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS) + +.c.o: + $(CC) -c $(CCFLAGS) $< -o $@ + +clean: + rm -f *.o *.exe example example2 example3 bench test_pr test diff --git a/tinyexpr/README.md b/tinyexpr/README.md new file mode 100644 index 000000000..ad9f45a45 --- /dev/null +++ b/tinyexpr/README.md @@ -0,0 +1,319 @@ +[![Build Status](https://travis-ci.org/codeplea/tinyexpr.svg?branch=master)](https://travis-ci.org/codeplea/tinyexpr) + + +TinyExpr logo + +# 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 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 your *malloc* is. + +## Building + +TinyExpr is self-contained in two files: `tinyexpr.c` and `tinyexpr.h`. To use +TinyExpr, simply add those two files to your project. + +## 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 + + 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 + + +## Binding to Custom Functions + +TinyExpr can also call to custom functions implemented in C. Here is a short example: + +```C +double my_sum(double a, double b) { + /* Example C function that adds two numbers together. */ + return a + b; +} + +te_variable vars[] = { + {"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 your +expression into a syntax tree. For example, the expression `"sin x + 1/4"` +parses as: + +![example syntax tree](doc/e1.png?raw=true) + +`te_compile()` also automatically prunes constant branches. In this example, +the compiled expression returned by `te_compile()` would become: + +![example syntax tree](doc/e2.png?raw=true) + +`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: + + = {"," } + = {("+" | "-") } + = {("*" | "/" | "%") } + = {"^" } + = {("-" | "+")} + = + | + | {"(" ")"} + | + | "(" {"," } ")" + | "(" ")" + +In addition, whitespace between tokens is ignored. + +Valid variable names consist of a lower case letter followed by any combination +of: lower case letters *a* through *z*, the digits *0* through *9*, and +underscore. 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, but this can be changed - see below). + +The following C math functions are also supported: + +- 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 + +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 to +define `TE_POW_FROM_RIGHT` when compiling `tinyexpr.c`. There is a +commented-out define near the top of that file. With this option enabled, the +behaviour 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 like `log` to default to the natural log instead of `log10`, +then you can define `TE_NAT_LOG`. + +## 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. + diff --git a/tinyexpr/benchmark.c b/tinyexpr/benchmark.c new file mode 100644 index 000000000..31d18faf8 --- /dev/null +++ b/tinyexpr/benchmark.c @@ -0,0 +1,125 @@ +/* + * 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 +#include +#include +#include "tinyexpr.h" + + + +#define loops 10000 + + + +typedef double (*function1)(double); + +void bench(const char *expr, function1 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%5dmfps\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%5dmfps\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; +} diff --git a/tinyexpr/doc/e1.dot b/tinyexpr/doc/e1.dot new file mode 100644 index 000000000..6a631aba6 --- /dev/null +++ b/tinyexpr/doc/e1.dot @@ -0,0 +1,8 @@ +digraph G { + "+" -> "sin"; + "+" -> div; + "sin" -> "x"; + div -> "1"; + div -> "4"; + div [label="÷"] +} diff --git a/tinyexpr/doc/e1.png b/tinyexpr/doc/e1.png new file mode 100644 index 0000000000000000000000000000000000000000..7f7a4500b173697057d247a96834cb88a0341fb1 GIT binary patch literal 11883 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 b36$TOm=n1+D}>?pI70QNx?-`sng9O){!(!~ literal 0 HcmV?d00001 diff --git a/tinyexpr/doc/e2.dot b/tinyexpr/doc/e2.dot new file mode 100644 index 000000000..d6067228b --- /dev/null +++ b/tinyexpr/doc/e2.dot @@ -0,0 +1,5 @@ +digraph G { + "+" -> "sin"; + "+" -> "0.25"; + "sin" -> "x"; +} diff --git a/tinyexpr/doc/e2.png b/tinyexpr/doc/e2.png new file mode 100644 index 0000000000000000000000000000000000000000..cebb1dbc4fb3b0679fc4753c29a4509d54e805c6 GIT binary patch literal 10032 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 PIE1XEqD1*4gMj}7`r*d= literal 0 HcmV?d00001 diff --git a/tinyexpr/example.c b/tinyexpr/example.c new file mode 100644 index 000000000..040093fd2 --- /dev/null +++ b/tinyexpr/example.c @@ -0,0 +1,10 @@ +#include "tinyexpr.h" +#include + +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; +} diff --git a/tinyexpr/example2.c b/tinyexpr/example2.c new file mode 100644 index 000000000..70d05d190 --- /dev/null +++ b/tinyexpr/example2.c @@ -0,0 +1,38 @@ +#include "tinyexpr.h" +#include + +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; +} diff --git a/tinyexpr/example3.c b/tinyexpr/example3.c new file mode 100644 index 000000000..80fe9da44 --- /dev/null +++ b/tinyexpr/example3.c @@ -0,0 +1,35 @@ +#include "tinyexpr.h" +#include + + +/* An example of calling a C function. */ +double my_sum(double a, double b) { + printf("Called C function with %f and %f.\n", a, b); + return a + b; +} + + +int main(int argc, char *argv[]) +{ + te_variable vars[] = { + {"mysum", my_sum, TE_FUNCTION2} + }; + + const char *expression = "mysum(5, 6)"; + printf("Evaluating:\n\t%s\n", expression); + + int err; + te_expr *n = te_compile(expression, vars, 1, &err); + + if (n) { + 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; +} diff --git a/tinyexpr/minctest.h b/tinyexpr/minctest.h new file mode 100644 index 000000000..6180de840 --- /dev/null +++ b/tinyexpr/minctest.h @@ -0,0 +1,128 @@ +/* + * + * 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 +#include +#include + + +/* 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;\ + const double __LF_COMPARE = fabs((double)(a)-(double)(b));\ + if (__LF_COMPARE > LTEST_FLOAT_TOLERANCE || (__LF_COMPARE != __LF_COMPARE)) {\ + ++lfails;\ + printf("%s:%d (%f != %f)\n", __FILE__, __LINE__, (double)(a), (double)(b));\ + }} while (0) + + +#endif /*__MINCTEST_H__*/ diff --git a/tinyexpr/test.c b/tinyexpr/test.c new file mode 100644 index 000000000..c772950a8 --- /dev/null +++ b/tinyexpr/test.c @@ -0,0 +1,691 @@ +/* + * 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 +#include "minctest.h" + + +typedef struct { + const char *expr; + double answer; +} test_case; + +typedef struct { + const char *expr1; + const char *expr2; +} test_equ; + + + +void test_results() { + test_case cases[] = { + {"1", 1}, + {"1 ", 1}, + {"(1)", 1}, + + {"pi", 3.14159}, + {"atan(1)*4 - pi", 0}, + {"e", 2.71828}, + + {"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}, + + {"log10 1000", 3}, + {"log10 1e3", 3}, + {"log10 1000", 3}, + {"log10 1e3", 3}, + {"log10(1000)", 3}, + {"log10(1e3)", 3}, + {"log10 1.0e3", 3}, + {"10^5*5e-5", 5}, + +#ifdef TE_NAT_LOG + {"log 1000", 6.9078}, + {"log e", 1}, + {"log (e^10)", 10}, +#else + {"log 1000", 3}, +#endif + + {"ln (e^10)", 10}, + {"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}, + + {"1,2", 2}, + {"1,2+1", 3}, + {"1+1,2+2,2+1", 3}, + {"1,2,3", 3}, + {"(1,2),3", 3}, + {"1,(2,3)", 3}, + {"-(1,(2,3))", -3}, + + {"2^2", 4}, + {"pow(2,2)", 4}, + + {"atan2(1,1)", 0.7854}, + {"atan2(1,2)", 0.4636}, + {"atan2(2,1)", 1.1071}, + {"atan2(3,4)", 0.6435}, + {"atan2(3+3,4*2)", 0.6435}, + {"atan2(3+3,(4*2))", 0.6435}, + {"atan2((3+3),4*2)", 0.6435}, + {"atan2((3+3),(4*2))", 0.6435}, + + }; + + + 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); + + if (err) { + printf("FAILED: %s (%d)\n", expr, err); + } + } +} + + +void test_syntax() { + test_case errors[] = { + {"", 1}, + {"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}, + }; + + + 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; + const double r = te_interp(expr, &err); + lequal(err, e); + lok(r != r); + + te_expr *n = te_compile(expr, 0, 0, &err); + lequal(err, e); + lok(!n); + + if (err != e) { + printf("FAILED: %s\n", expr); + } + + const double k = te_interp(expr, 0); + lok(k != k); + } +} + + +void test_nans() { + + const char *nans[] = { + "0/0", + "1%0", + "1%(1%0)", + "(1%0)%1", + "fac(-1)", + "ncr(2, 4)", + "ncr(-2, 4)", + "ncr(2, -4)", + "npr(2, 4)", + "npr(-2, 4)", + "npr(2, -4)", + }; + + int i; + for (i = 0; i < sizeof(nans) / sizeof(const char *); ++i) { + const char *expr = nans[i]; + + int err; + const double r = te_interp(expr, &err); + lequal(err, 0); + lok(r != r); + + te_expr *n = te_compile(expr, 0, 0, &err); + lok(n); + lequal(err, 0); + const double c = te_eval(n); + lok(c != c); + te_free(n); + } +} + + +void test_infs() { + + const char *infs[] = { + "1/0", + "log(0)", + "pow(2,10000000)", + "fac(300)", + "ncr(300,100)", + "ncr(300000,100)", + "ncr(300000,100)*8", + "npr(3,2)*ncr(300000,100)", + "npr(100,90)", + "npr(30,25)", + }; + + int i; + for (i = 0; i < sizeof(infs) / sizeof(const char *); ++i) { + const char *expr = infs[i]; + + int err; + const double r = te_interp(expr, &err); + lequal(err, 0); + lok(r == r + 1); + + te_expr *n = te_compile(expr, 0, 0, &err); + lok(n); + lequal(err, 0); + const double c = te_eval(n); + lok(c == c + 1); + te_free(n); + } +} + + +void test_variables() { + + double x, y, test; + te_variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &test}}; + + int err; + + te_expr *expr1 = te_compile("cos x + sin y", lookup, 2, &err); + lok(expr1); + lok(!err); + + te_expr *expr2 = te_compile("x+x+x-y", lookup, 2, &err); + lok(expr2); + lok(!err); + + te_expr *expr3 = te_compile("x*y^3", lookup, 2, &err); + lok(expr3); + lok(!err); + + te_expr *expr4 = te_compile("te_st+5", lookup, 3, &err); + lok(expr4); + lok(!err); + + for (y = 2; y < 3; ++y) { + for (x = 0; x < 5; ++x) { + double ev; + + 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); + + test = x; + ev = te_eval(expr4); + lfequal(ev, x+5); + } + } + + te_free(expr1); + te_free(expr2); + te_free(expr3); + te_free(expr4); + + + + te_expr *expr5 = te_compile("xx*y^3", lookup, 2, &err); + lok(!expr5); + lok(err); + + te_expr *expr6 = te_compile("tes", lookup, 3, &err); + lok(!expr6); + lok(err); + + te_expr *expr7 = te_compile("sinn x", lookup, 2, &err); + lok(!expr7); + lok(err); + + te_expr *expr8 = te_compile("si x", lookup, 2, &err); + lok(!expr8); + lok(err); +} + + + +#define cross_check(a, b) do {\ + if ((b)!=(b)) break;\ + expr = te_compile((a), lookup, 2, &err);\ + lfequal(te_eval(expr), (b));\ + lok(!err);\ + te_free(expr);\ +}while(0) + +void test_functions() { + + double x, y; + te_variable lookup[] = {{"x", &x}, {"y", &y}}; + + 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("log10 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)); + + for (y = -2; y < 2; y += .2) { + if (fabs(x) < 0.01) break; + cross_check("atan2(x,y)", atan2(x, y)); + cross_check("pow(x,y)", pow(x, y)); + } + } +} + + +double sum0() { + return 6; +} +double sum1(double a) { + return a * 2; +} +double sum2(double a, double b) { + return a + b; +} +double sum3(double a, double b, double c) { + return a + b + c; +} +double sum4(double a, double b, double c, double d) { + return a + b + c + d; +} +double sum5(double a, double b, double c, double d, double e) { + return a + b + c + d + e; +} +double sum6(double a, double b, double c, double d, double e, double f) { + return a + b + c + d + e + f; +} +double sum7(double a, double b, double c, double d, double e, double f, double g) { + return a + b + c + d + e + f + g; +} + + +void test_dynamic() { + + double x, f; + te_variable lookup[] = { + {"x", &x}, + {"f", &f}, + {"sum0", sum0, TE_FUNCTION0}, + {"sum1", sum1, TE_FUNCTION1}, + {"sum2", sum2, TE_FUNCTION2}, + {"sum3", sum3, TE_FUNCTION3}, + {"sum4", sum4, TE_FUNCTION4}, + {"sum5", sum5, TE_FUNCTION5}, + {"sum6", sum6, TE_FUNCTION6}, + {"sum7", sum7, TE_FUNCTION7}, + }; + + test_case cases[] = { + {"x", 2}, + {"f+x", 7}, + {"x+x", 4}, + {"x+f", 7}, + {"f+f", 10}, + {"f+sum0", 11}, + {"sum0+sum0", 12}, + {"sum0()+sum0", 12}, + {"sum0+sum0()", 12}, + {"sum0()+(0)+sum0()", 12}, + {"sum1 sum0", 12}, + {"sum1(sum0)", 12}, + {"sum1 f", 10}, + {"sum1 x", 4}, + {"sum2 (sum0, x)", 8}, + {"sum3 (sum0, x, 2)", 10}, + {"sum2(2,3)", 5}, + {"sum3(2,3,4)", 9}, + {"sum4(2,3,4,5)", 14}, + {"sum5(2,3,4,5,6)", 20}, + {"sum6(2,3,4,5,6,7)", 27}, + {"sum7(2,3,4,5,6,7,8)", 35}, + }; + + x = 2; + f = 5; + + 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; + te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err); + lok(ex); + lfequal(te_eval(ex), answer); + te_free(ex); + } +} + + +double clo0(void *context) { + if (context) return *((double*)context) + 6; + return 6; +} +double clo1(void *context, double a) { + if (context) return *((double*)context) + a * 2; + return a * 2; +} +double clo2(void *context, double a, double b) { + if (context) return *((double*)context) + a + b; + return a + b; +} + +double cell(void *context, double a) { + double *c = context; + return c[(int)a]; +} + +void test_closure() { + + double extra; + double c[] = {5,6,7,8,9}; + + te_variable lookup[] = { + {"c0", clo0, TE_CLOSURE0, &extra}, + {"c1", clo1, TE_CLOSURE1, &extra}, + {"c2", clo2, TE_CLOSURE2, &extra}, + {"cell", cell, TE_CLOSURE1, c}, + }; + + test_case cases[] = { + {"c0", 6}, + {"c1 4", 8}, + {"c2 (10, 20)", 30}, + }; + + 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; + te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err); + lok(ex); + + extra = 0; + lfequal(te_eval(ex), answer + extra); + + extra = 10; + lfequal(te_eval(ex), answer + extra); + + te_free(ex); + } + + + test_case cases2[] = { + {"cell 0", 5}, + {"cell 1", 6}, + {"cell 0 + cell 1", 11}, + {"cell 1 * cell 3 + cell 4", 57}, + }; + + for (i = 0; i < sizeof(cases2) / sizeof(test_case); ++i) { + const char *expr = cases2[i].expr; + const double answer = cases2[i].answer; + + int err; + te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err); + lok(ex); + lfequal(te_eval(ex), answer); + te_free(ex); + } +} + +void test_optimize() { + + test_case cases[] = { + {"5+5", 10}, + {"pow(2,2)", 4}, + {"sqrt 100", 10}, + {"pi * 2", 6.2832}, + }; + + 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; + te_expr *ex = te_compile(expr, 0, 0, &err); + lok(ex); + + /* The answer should be know without + * even running eval. */ + lfequal(ex->value, answer); + lfequal(te_eval(ex), answer); + + te_free(ex); + } +} + +void test_pow() { +#ifdef TE_POW_FROM_RIGHT + test_equ cases[] = { + {"2^3^4", "2^(3^4)"}, + {"-2^2", "-(2^2)"}, + {"--2^2", "(2^2)"}, + {"---2^2", "-(2^2)"}, + {"-(2)^2", "-(2^2)"}, + {"-(2*1)^2", "-(2^2)"}, + {"-2^2", "-4"}, + {"2^1.1^1.2^1.3", "2^(1.1^(1.2^1.3))"}, + {"-a^b", "-(a^b)"}, + {"-a^-b", "-(a^-b)"} + }; +#else + test_equ cases[] = { + {"2^3^4", "(2^3)^4"}, + {"-2^2", "(-2)^2"}, + {"--2^2", "2^2"}, + {"---2^2", "(-2)^2"}, + {"-2^2", "4"}, + {"2^1.1^1.2^1.3", "((2^1.1)^1.2)^1.3"}, + {"-a^b", "(-a)^b"}, + {"-a^-b", "(-a)^(-b)"} + }; +#endif + + double a = 2, b = 3; + + te_variable lookup[] = { + {"a", &a}, + {"b", &b} + }; + + int i; + for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) { + const char *expr1 = cases[i].expr1; + const char *expr2 = cases[i].expr2; + + te_expr *ex1 = te_compile(expr1, lookup, sizeof(lookup)/sizeof(te_variable), 0); + te_expr *ex2 = te_compile(expr2, lookup, sizeof(lookup)/sizeof(te_variable), 0); + + lok(ex1); + lok(ex2); + + double r1 = te_eval(ex1); + double r2 = te_eval(ex2); + + fflush(stdout); + lfequal(r1, r2); + + te_free(ex1); + te_free(ex2); + } + +} + +void test_combinatorics() { + test_case cases[] = { + {"fac(0)", 1}, + {"fac(0.2)", 1}, + {"fac(1)", 1}, + {"fac(2)", 2}, + {"fac(3)", 6}, + {"fac(4.8)", 24}, + {"fac(10)", 3628800}, + + {"ncr(0,0)", 1}, + {"ncr(10,1)", 10}, + {"ncr(10,0)", 1}, + {"ncr(10,10)", 1}, + {"ncr(16,7)", 11440}, + {"ncr(16,9)", 11440}, + {"ncr(100,95)", 75287520}, + + {"npr(0,0)", 1}, + {"npr(10,1)", 10}, + {"npr(10,0)", 1}, + {"npr(10,10)", 3628800}, + {"npr(20,5)", 1860480}, + {"npr(100,4)", 94109400}, + }; + + + 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); + + if (err) { + printf("FAILED: %s (%d)\n", expr, err); + } + } +} + + +int main(int argc, char *argv[]) +{ + lrun("Results", test_results); + lrun("Syntax", test_syntax); + lrun("NaNs", test_nans); + lrun("INFs", test_infs); + lrun("Variables", test_variables); + lrun("Functions", test_functions); + lrun("Dynamic", test_dynamic); + lrun("Closure", test_closure); + lrun("Optimize", test_optimize); + lrun("Pow", test_pow); + lrun("Combinatorics", test_combinatorics); + lresults(); + + return lfails != 0; +}