mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 08:42:39 +01:00 
			
		
		
		
	Changed error handling a bit. te_interp returns NaN for bad expressions.
This commit is contained in:
		| @@ -86,7 +86,7 @@ line. It also does error checking and binds the variables *x* and *y*. | ||||
|             printf("\t%*s^\nError near here", err-1, ""); | ||||
|         } | ||||
|  | ||||
|         /* te_free should always be called after te_compile. */ | ||||
|         /* te_free is safe to call on null. */ | ||||
|         te_free(n); | ||||
|  | ||||
|         return 0; | ||||
| @@ -235,9 +235,6 @@ In addition, the following C math functions are also supported: | ||||
|  | ||||
| - 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. | ||||
|  | ||||
|   | ||||
| @@ -32,7 +32,7 @@ int main(int argc, char *argv[]) | ||||
|         printf("\t%*s^\nError near here", err-1, ""); | ||||
|     } | ||||
|  | ||||
|     /* te_free should always be called after te_compile. */ | ||||
|     /* te_free is safe to call on null. */ | ||||
|     te_free(n); | ||||
|  | ||||
|     return 0; | ||||
|   | ||||
							
								
								
									
										50
									
								
								test.c
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								test.c
									
									
									
									
									
								
							| @@ -122,7 +122,26 @@ test_case errors[] = { | ||||
| }; | ||||
|  | ||||
|  | ||||
| void test1() { | ||||
| const char *nans[] = { | ||||
|     "1+", | ||||
|     "1)", | ||||
|     "(1", | ||||
|     "1**1", | ||||
|     "1*2(+4", | ||||
|     "1*2(1+4", | ||||
|     "a+5", | ||||
|     "A+5", | ||||
|     "Aa+5", | ||||
|     "1^^5", | ||||
|     "1**5", | ||||
|     "sin(cos5", | ||||
|     "5+5error", | ||||
|     "5+5+error", | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| void test_results() { | ||||
|     int i; | ||||
|     for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { | ||||
|         const char *expr = cases[i].expr; | ||||
| @@ -136,7 +155,7 @@ void test1() { | ||||
| } | ||||
|  | ||||
|  | ||||
| void test2() { | ||||
| void test_syntax() { | ||||
|     int i; | ||||
|     for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) { | ||||
|         const char *expr = errors[i].expr; | ||||
| @@ -150,7 +169,7 @@ void test2() { | ||||
|  | ||||
|  | ||||
|  | ||||
| void test3() { | ||||
| void test_variables() { | ||||
|  | ||||
|     double x, y; | ||||
|     te_variable lookup[] = {{"x", &x}, {"y", &y}}; | ||||
| @@ -185,6 +204,7 @@ void test3() { | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| #define cross_check(a, b) do {\ | ||||
|     expr = te_compile((a), &lookup, 1, &err);\ | ||||
|     lfequal(te_eval(expr), (b));\ | ||||
| @@ -192,7 +212,7 @@ void test3() { | ||||
|     te_free(expr);\ | ||||
| }while(0) | ||||
|  | ||||
| void test4() { | ||||
| void test_functions() { | ||||
|  | ||||
|     double x; | ||||
|     te_variable lookup = {"x", &x}; | ||||
| @@ -221,12 +241,26 @@ void test4() { | ||||
| } | ||||
|  | ||||
|  | ||||
| void test_nans() { | ||||
|     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); | ||||
|         lok(err); | ||||
|         lok(r != r); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|     lrun("Results", test1); | ||||
|     lrun("Syntax", test2); | ||||
|     lrun("Bind", test3); | ||||
|     lrun("Functions", test4); | ||||
|     lrun("Results", test_results); | ||||
|     lrun("Syntax", test_syntax); | ||||
|     lrun("Variables", test_variables); | ||||
|     lrun("Functions", test_functions); | ||||
|     lrun("NaNs", test_nans); | ||||
|     lresults(); | ||||
|  | ||||
|     return lfails != 0; | ||||
|   | ||||
							
								
								
									
										21
									
								
								tinyexpr.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								tinyexpr.c
									
									
									
									
									
								
							| @@ -56,6 +56,7 @@ static te_expr *new_expr(te_expr *l, te_expr *r) { | ||||
|  | ||||
|  | ||||
| void te_free(te_expr *n) { | ||||
|     if (!n) return; | ||||
|     if (n->left) te_free(n->left); | ||||
|     if (n->right) te_free(n->right); | ||||
|     free(n); | ||||
| @@ -232,7 +233,7 @@ static te_expr *base(state *s) { | ||||
|         default: | ||||
|             ret = new_expr(0, 0); | ||||
|             s->type = TOK_ERROR; | ||||
|             ret->value = 1.0/0.0; | ||||
|             ret->value = 0.0/0.0; | ||||
|             break; | ||||
|     } | ||||
|  | ||||
| @@ -358,24 +359,30 @@ te_expr *te_compile(const char *expression, const te_variable *lookup, int looku | ||||
|     next_token(&s); | ||||
|     te_expr *root = expr(&s); | ||||
|  | ||||
|  | ||||
|     if (s.type != TOK_END) { | ||||
|         if (error) *error = (s.next - s.start); | ||||
|         te_free(root); | ||||
|         if (error) { | ||||
|             *error = (s.next - s.start); | ||||
|             if (*error == 0) *error = 1; | ||||
|         } | ||||
|         return 0; | ||||
|     } 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); | ||||
|     double ret; | ||||
|     if (n) { | ||||
|         ret = te_eval(n); | ||||
|         free(n); | ||||
|     } else { | ||||
|         ret = 0.0/0.0; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										12
									
								
								tinyexpr.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								tinyexpr.h
									
									
									
									
									
								
							| @@ -48,29 +48,23 @@ typedef struct { | ||||
| } 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. */ | ||||
| /* Returns NULL on error. */ | ||||
| te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error); | ||||
|  | ||||
| /* Evaluates the expression. */ | ||||
| /* Returns NaN on error. */ | ||||
| double te_eval(const te_expr *n); | ||||
|  | ||||
| /* Prints debugging information on the syntax tree. */ | ||||
| void te_print(const te_expr *n); | ||||
|  | ||||
| /* Frees the expression. */ | ||||
| /* This is safe to call on NULL pointers. */ | ||||
| void te_free(te_expr *n); | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user