mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Merge branch 'master' of https://github.com/codeplea/tinyexpr
This commit is contained in:
		
							
								
								
									
										42
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								README.md
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | |||||||
|  |  | ||||||
| <img alt="TinyExpr logo" src="https://codeplea.com/public/content/tinyexpr_logo.png" align="right"/> | <img alt="TinyExpr logo" src="https://codeplea.com/public/content/tinyexpr_logo.png" align="right"/> | ||||||
|  |  | ||||||
| #TinyExpr | # TinyExpr | ||||||
|  |  | ||||||
| TinyExpr is a very small recursive descent parser and evaluation engine for | 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. It's handy when you want to add the ability to evaluation | ||||||
| @@ -12,7 +12,7 @@ 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 | In addition to the standard math operators and precedence, TinyExpr also supports | ||||||
| the standard C math functions and runtime binding of variables. | the standard C math functions and runtime binding of variables. | ||||||
|  |  | ||||||
| ##Features | ## Features | ||||||
|  |  | ||||||
| - **ANSI C with no dependencies**. | - **ANSI C with no dependencies**. | ||||||
| - Single source file and header file. | - Single source file and header file. | ||||||
| @@ -25,12 +25,12 @@ the standard C math functions and runtime binding of variables. | |||||||
| - Easy to use and integrate with your code | - Easy to use and integrate with your code | ||||||
| - Thread-safe, provided that your *malloc* is. | - Thread-safe, provided that your *malloc* is. | ||||||
|  |  | ||||||
| ##Building | ## Building | ||||||
|  |  | ||||||
| TinyExpr is self-contained in two files: `tinyexpr.c` and `tinyexpr.h`. To use | TinyExpr is self-contained in two files: `tinyexpr.c` and `tinyexpr.h`. To use | ||||||
| TinyExpr, simply add those two files to your project. | TinyExpr, simply add those two files to your project. | ||||||
|  |  | ||||||
| ##Short Example | ## Short Example | ||||||
|  |  | ||||||
| Here is a minimal example to evaluate an expression at runtime. | Here is a minimal example to evaluate an expression at runtime. | ||||||
|  |  | ||||||
| @@ -40,7 +40,7 @@ Here is a minimal example to evaluate an expression at runtime. | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
| ##Usage | ## Usage | ||||||
|  |  | ||||||
| TinyExpr defines only four functions: | TinyExpr defines only four functions: | ||||||
|  |  | ||||||
| @@ -51,7 +51,7 @@ TinyExpr defines only four functions: | |||||||
|     void te_free(te_expr *expr); |     void te_free(te_expr *expr); | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ##te_interp | ## te_interp | ||||||
| ```C | ```C | ||||||
|     double te_interp(const char *expression, int *error); |     double te_interp(const char *expression, int *error); | ||||||
| ``` | ``` | ||||||
| @@ -72,7 +72,7 @@ of the parse error on failure, and set `*error` to 0 on success. | |||||||
|     double c = te_interp("(5+5", &error); /* Returns NaN, error is set to 4. */ |     double c = te_interp("(5+5", &error); /* Returns NaN, error is set to 4. */ | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ##te_compile, te_eval, te_free | ## te_compile, te_eval, te_free | ||||||
| ```C | ```C | ||||||
|     te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error); |     te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error); | ||||||
|     double te_eval(const te_expr *n); |     double te_eval(const te_expr *n); | ||||||
| @@ -117,7 +117,7 @@ After you're finished, make sure to call `te_free()`. | |||||||
|  |  | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ##Longer Example | ## Longer Example | ||||||
|  |  | ||||||
| Here is a complete example that will evaluate an expression passed in from the command | 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. | line. It also does error checking and binds the variables `x` and `y` to *3* and *4*, respectively. | ||||||
| @@ -178,7 +178,7 @@ This produces the output: | |||||||
|                 5.000000 |                 5.000000 | ||||||
|  |  | ||||||
|  |  | ||||||
| ##Binding to Custom Functions | ## Binding to Custom Functions | ||||||
|  |  | ||||||
| TinyExpr can also call to custom functions implemented in C. Here is a short example: | TinyExpr can also call to custom functions implemented in C. Here is a short example: | ||||||
|  |  | ||||||
| @@ -197,7 +197,7 @@ te_expr *n = te_compile("mysum(5, 6)", vars, 1, 0); | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
| ##How it works | ## How it works | ||||||
|  |  | ||||||
| `te_compile()` uses a simple recursive descent parser to compile your | `te_compile()` uses a simple recursive descent parser to compile your | ||||||
| expression into a syntax tree. For example, the expression `"sin x + 1/4"` | expression into a syntax tree. For example, the expression `"sin x + 1/4"` | ||||||
| @@ -216,7 +216,7 @@ and return the result of the expression. | |||||||
| `te_free()` should always be called when you're done with the compiled expression. | `te_free()` should always be called when you're done with the compiled expression. | ||||||
|  |  | ||||||
|  |  | ||||||
| ##Speed | ## Speed | ||||||
|  |  | ||||||
|  |  | ||||||
| TinyExpr is pretty fast compared to C when the expression is short, when the | TinyExpr is pretty fast compared to C when the expression is short, when the | ||||||
| @@ -237,7 +237,7 @@ Here is some example performance numbers taken from the included | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ##Grammar | ## Grammar | ||||||
|  |  | ||||||
| TinyExpr parses the following grammar: | TinyExpr parses the following grammar: | ||||||
|  |  | ||||||
| @@ -262,33 +262,39 @@ notation (e.g.  *1e3* for *1000*). A leading zero is not required (e.g. *.5* | |||||||
| for *0.5*) | for *0.5*) | ||||||
|  |  | ||||||
|  |  | ||||||
| ##Functions supported | ## Functions supported | ||||||
|  |  | ||||||
| TinyExpr supports addition (+), subtraction/negation (-), multiplication (\*), | TinyExpr supports addition (+), subtraction/negation (-), multiplication (\*), | ||||||
| division (/), exponentiation (^) and modulus (%) with the normal operator | division (/), exponentiation (^) and modulus (%) with the normal operator | ||||||
| precedence (the one exception being that exponentiation is evaluated | precedence (the one exception being that exponentiation is evaluated | ||||||
| left-to-right, but this can be changed - see below). | left-to-right, but this can be changed - see below). | ||||||
|  |  | ||||||
| In addition, the following C math functions are also supported: | 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 | - 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: | Also, the following constants are available: | ||||||
|  |  | ||||||
| - `pi`, `e` | - `pi`, `e` | ||||||
|  |  | ||||||
|  |  | ||||||
| ##Compile-time options | ## Compile-time options | ||||||
|  |  | ||||||
|  |  | ||||||
| By default, TinyExpr does exponentation from left to right. For example: | By default, TinyExpr does exponentiation from left to right. For example: | ||||||
|  |  | ||||||
| `a^b^c == (a^b)^c` and `-a^b == (-a)^b` | `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). | This is by design. It's the way that spreadsheets do it (e.g. Excel, Google Sheets). | ||||||
|  |  | ||||||
|  |  | ||||||
| If you would rather have exponentation work from right to left, you need to | 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 | 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 | commented-out define near the top of that file. With this option enabled, the | ||||||
| behaviour is: | behaviour is: | ||||||
| @@ -300,7 +306,7 @@ 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`, | Also, if you'd like `log` to default to the natural log instead of `log10`, | ||||||
| then you can define `TE_NAT_LOG`. | then you can define `TE_NAT_LOG`. | ||||||
|  |  | ||||||
| ##Hints | ## Hints | ||||||
|  |  | ||||||
| - All functions/types start with the letters *te*. | - All functions/types start with the letters *te*. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								test.c
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								test.c
									
									
									
									
									
								
							| @@ -213,6 +213,13 @@ void test_nans() { | |||||||
|         "1%0", |         "1%0", | ||||||
|         "1%(1%0)", |         "1%(1%0)", | ||||||
|         "(1%0)%1", |         "(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; |     int i; | ||||||
| @@ -234,6 +241,40 @@ void test_nans() { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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() { | void test_variables() { | ||||||
|  |  | ||||||
|     double x, y, test; |     double x, y, test; | ||||||
| @@ -587,17 +628,63 @@ void test_pow() { | |||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | 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[]) | int main(int argc, char *argv[]) | ||||||
| { | { | ||||||
|     lrun("Results", test_results); |     lrun("Results", test_results); | ||||||
|     lrun("Syntax", test_syntax); |     lrun("Syntax", test_syntax); | ||||||
|     lrun("NaNs", test_nans); |     lrun("NaNs", test_nans); | ||||||
|  |     lrun("INFs", test_infs); | ||||||
|     lrun("Variables", test_variables); |     lrun("Variables", test_variables); | ||||||
|     lrun("Functions", test_functions); |     lrun("Functions", test_functions); | ||||||
|     lrun("Dynamic", test_dynamic); |     lrun("Dynamic", test_dynamic); | ||||||
|     lrun("Closure", test_closure); |     lrun("Closure", test_closure); | ||||||
|     lrun("Optimize", test_optimize); |     lrun("Optimize", test_optimize); | ||||||
|     lrun("Pow", test_pow); |     lrun("Pow", test_pow); | ||||||
|  |     lrun("Combinatorics", test_combinatorics); | ||||||
|     lresults(); |     lresults(); | ||||||
|  |  | ||||||
|     return lfails != 0; |     return lfails != 0; | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								tinyexpr.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								tinyexpr.c
									
									
									
									
									
								
							| @@ -39,11 +39,17 @@ For log = natural log uncomment the next line. */ | |||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | #include <limits.h> | ||||||
|  |  | ||||||
| #ifndef NAN | #ifndef NAN | ||||||
| #define NAN (0.0/0.0) | #define NAN (0.0/0.0) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef INFINITY | ||||||
|  | #define INFINITY (1.0/0.0) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| typedef double (*te_fun2)(double, double); | typedef double (*te_fun2)(double, double); | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
| @@ -113,6 +119,35 @@ void te_free(te_expr *n) { | |||||||
|  |  | ||||||
| static double pi() {return 3.14159265358979323846;} | static double pi() {return 3.14159265358979323846;} | ||||||
| static double e() {return 2.71828182845904523536;} | static double e() {return 2.71828182845904523536;} | ||||||
|  | static double fac(double a) {/* simplest version of fac */ | ||||||
|  |     if (a < 0.0) | ||||||
|  |         return NAN; | ||||||
|  |     if (a > UINT_MAX) | ||||||
|  |         return INFINITY; | ||||||
|  |     unsigned int ua = (unsigned int)(a); | ||||||
|  |     unsigned long int result = 1, i; | ||||||
|  |     for (i = 1; i <= ua; i++) { | ||||||
|  |         if (i > ULONG_MAX / result) | ||||||
|  |             return INFINITY; | ||||||
|  |         result *= i; | ||||||
|  |     } | ||||||
|  |     return (double)result; | ||||||
|  | } | ||||||
|  | static double ncr(double n, double r) { | ||||||
|  |     if (n < 0.0 || r < 0.0 || n < r) return NAN; | ||||||
|  |     if (n > UINT_MAX || r > UINT_MAX) return INFINITY; | ||||||
|  |     unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; | ||||||
|  |     unsigned long int result = 1; | ||||||
|  |     if (ur > un / 2) ur = un - ur; | ||||||
|  |     for (i = 1; i <= ur; i++) { | ||||||
|  |         if (result > ULONG_MAX / (un - ur + i)) | ||||||
|  |             return INFINITY; | ||||||
|  |         result *= un - ur + i; | ||||||
|  |         result /= i; | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | static double npr(double n, double r) {return ncr(n, r) * fac(r);} | ||||||
|  |  | ||||||
| static const te_variable functions[] = { | static const te_variable functions[] = { | ||||||
|     /* must be in alphabetical order */ |     /* must be in alphabetical order */ | ||||||
| @@ -126,6 +161,7 @@ static const te_variable functions[] = { | |||||||
|     {"cosh", cosh,    TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"cosh", cosh,    TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
|     {"e", e,          TE_FUNCTION0 | TE_FLAG_PURE, 0}, |     {"e", e,          TE_FUNCTION0 | TE_FLAG_PURE, 0}, | ||||||
|     {"exp", exp,      TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"exp", exp,      TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
|  |     {"fac", fac,      TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
|     {"floor", floor,  TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"floor", floor,  TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
|     {"ln", log,       TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"ln", log,       TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
| #ifdef TE_NAT_LOG | #ifdef TE_NAT_LOG | ||||||
| @@ -134,6 +170,8 @@ static const te_variable functions[] = { | |||||||
|     {"log", log10,    TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"log", log10,    TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
| #endif | #endif | ||||||
|     {"log10", log10,  TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"log10", log10,  TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
|  |     {"ncr", ncr,      TE_FUNCTION2 | TE_FLAG_PURE, 0}, | ||||||
|  |     {"npr", npr,      TE_FUNCTION2 | TE_FLAG_PURE, 0}, | ||||||
|     {"pi", pi,        TE_FUNCTION0 | TE_FLAG_PURE, 0}, |     {"pi", pi,        TE_FUNCTION0 | TE_FLAG_PURE, 0}, | ||||||
|     {"pow", pow,      TE_FUNCTION2 | TE_FLAG_PURE, 0}, |     {"pow", pow,      TE_FUNCTION2 | TE_FLAG_PURE, 0}, | ||||||
|     {"sin", sin,      TE_FUNCTION1 | TE_FLAG_PURE, 0}, |     {"sin", sin,      TE_FUNCTION1 | TE_FLAG_PURE, 0}, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user