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, ""); |             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); |         te_free(n); | ||||||
|  |  | ||||||
|         return 0; |         return 0; | ||||||
| @@ -235,9 +235,6 @@ In addition, the following C math functions are also supported: | |||||||
|  |  | ||||||
| - All functions/types start with the letters *te*. | - 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 | - 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. |   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, ""); |         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); |     te_free(n); | ||||||
|  |  | ||||||
|     return 0; |     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; |     int i; | ||||||
|     for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { |     for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { | ||||||
|         const char *expr = cases[i].expr; |         const char *expr = cases[i].expr; | ||||||
| @@ -136,7 +155,7 @@ void test1() { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void test2() { | void test_syntax() { | ||||||
|     int i; |     int i; | ||||||
|     for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) { |     for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) { | ||||||
|         const char *expr = errors[i].expr; |         const char *expr = errors[i].expr; | ||||||
| @@ -150,7 +169,7 @@ void test2() { | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| void test3() { | void test_variables() { | ||||||
|  |  | ||||||
|     double x, y; |     double x, y; | ||||||
|     te_variable lookup[] = {{"x", &x}, {"y", &y}}; |     te_variable lookup[] = {{"x", &x}, {"y", &y}}; | ||||||
| @@ -185,6 +204,7 @@ void test3() { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #define cross_check(a, b) do {\ | #define cross_check(a, b) do {\ | ||||||
|     expr = te_compile((a), &lookup, 1, &err);\ |     expr = te_compile((a), &lookup, 1, &err);\ | ||||||
|     lfequal(te_eval(expr), (b));\ |     lfequal(te_eval(expr), (b));\ | ||||||
| @@ -192,7 +212,7 @@ void test3() { | |||||||
|     te_free(expr);\ |     te_free(expr);\ | ||||||
| }while(0) | }while(0) | ||||||
|  |  | ||||||
| void test4() { | void test_functions() { | ||||||
|  |  | ||||||
|     double x; |     double x; | ||||||
|     te_variable lookup = {"x", &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[]) | int main(int argc, char *argv[]) | ||||||
| { | { | ||||||
|     lrun("Results", test1); |     lrun("Results", test_results); | ||||||
|     lrun("Syntax", test2); |     lrun("Syntax", test_syntax); | ||||||
|     lrun("Bind", test3); |     lrun("Variables", test_variables); | ||||||
|     lrun("Functions", test4); |     lrun("Functions", test_functions); | ||||||
|  |     lrun("NaNs", test_nans); | ||||||
|     lresults(); |     lresults(); | ||||||
|  |  | ||||||
|     return lfails != 0; |     return lfails != 0; | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								tinyexpr.c
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								tinyexpr.c
									
									
									
									
									
								
							| @@ -56,6 +56,7 @@ static te_expr *new_expr(te_expr *l, te_expr *r) { | |||||||
|  |  | ||||||
|  |  | ||||||
| void te_free(te_expr *n) { | void te_free(te_expr *n) { | ||||||
|  |     if (!n) return; | ||||||
|     if (n->left) te_free(n->left); |     if (n->left) te_free(n->left); | ||||||
|     if (n->right) te_free(n->right); |     if (n->right) te_free(n->right); | ||||||
|     free(n); |     free(n); | ||||||
| @@ -232,7 +233,7 @@ static te_expr *base(state *s) { | |||||||
|         default: |         default: | ||||||
|             ret = new_expr(0, 0); |             ret = new_expr(0, 0); | ||||||
|             s->type = TOK_ERROR; |             s->type = TOK_ERROR; | ||||||
|             ret->value = 1.0/0.0; |             ret->value = 0.0/0.0; | ||||||
|             break; |             break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -358,24 +359,30 @@ te_expr *te_compile(const char *expression, const te_variable *lookup, int looku | |||||||
|     next_token(&s); |     next_token(&s); | ||||||
|     te_expr *root = expr(&s); |     te_expr *root = expr(&s); | ||||||
|  |  | ||||||
|  |  | ||||||
|     if (s.type != TOK_END) { |     if (s.type != TOK_END) { | ||||||
|         if (error) *error = (s.next - s.start); |         te_free(root); | ||||||
|         if (*error == 0) *error = 1; |         if (error) { | ||||||
|  |             *error = (s.next - s.start); | ||||||
|  |             if (*error == 0) *error = 1; | ||||||
|  |         } | ||||||
|  |         return 0; | ||||||
|     } else { |     } else { | ||||||
|         optimize(root); |         optimize(root); | ||||||
|         if (error) *error = 0; |         if (error) *error = 0; | ||||||
|  |         return root; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     return root; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| double te_interp(const char *expression, int *error) { | double te_interp(const char *expression, int *error) { | ||||||
|     te_expr *n = te_compile(expression, 0, 0, error); |     te_expr *n = te_compile(expression, 0, 0, error); | ||||||
|     double ret = te_eval(n); |     double ret; | ||||||
|     free(n); |     if (n) { | ||||||
|  |         ret = te_eval(n); | ||||||
|  |         free(n); | ||||||
|  |     } else { | ||||||
|  |         ret = 0.0/0.0; | ||||||
|  |     } | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								tinyexpr.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								tinyexpr.h
									
									
									
									
									
								
							| @@ -48,29 +48,23 @@ typedef struct { | |||||||
| } te_variable; | } 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. */ | /* Parses the input expression, evaluates it, and frees it. */ | ||||||
| double te_interp(const char *expression, int *error); | double te_interp(const char *expression, int *error); | ||||||
|  |  | ||||||
| /* Parses the input expression and binds variables. */ | /* 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); | te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error); | ||||||
|  |  | ||||||
| /* Evaluates the expression. */ | /* Evaluates the expression. */ | ||||||
|  | /* Returns NaN on error. */ | ||||||
| double te_eval(const te_expr *n); | double te_eval(const te_expr *n); | ||||||
|  |  | ||||||
| /* Prints debugging information on the syntax tree. */ | /* Prints debugging information on the syntax tree. */ | ||||||
| void te_print(const te_expr *n); | void te_print(const te_expr *n); | ||||||
|  |  | ||||||
| /* Frees the expression. */ | /* Frees the expression. */ | ||||||
|  | /* This is safe to call on NULL pointers. */ | ||||||
| void te_free(te_expr *n); | void te_free(te_expr *n); | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user