mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 00:32:38 +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