mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Added comma operator, builtin functions of arity 0 and 2.
This commit is contained in:
		| @@ -214,11 +214,12 @@ Here is some example performance numbers taken from the included | ||||
|  | ||||
| TinyExpr parses the following grammar: | ||||
|  | ||||
|     <list>      =    <expr> {"," <expr>} | ||||
|     <expr>      =    <term> {("+" | "-") <term>} | ||||
|     <term>      =    <factor> {("*" | "/" | "%") <factor>} | ||||
|     <factor>    =    <power> {"^" <power>} | ||||
|     <power>     =    {("-" | "+")} <base> | ||||
|     <base>      =    <constant> | <variable> | <function> <power> | "(" <expr> ")" | ||||
|     <base>      =    <constant> | <variable> | <function-1> <power> | <function-2> "(" <expr> "," <expr> ")" | "(" <list> ")" | ||||
|  | ||||
| In addition, whitespace between tokens is ignored. | ||||
|  | ||||
| @@ -236,7 +237,11 @@ 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 | ||||
| - abs (calls to *fabs*), acos, asin, atan, atan2, ceil, cos, cosh, exp, floor, ln (calls to *log*), log (calls to *log10*), pow, sin, sinh, sqrt, tan, tanh | ||||
|  | ||||
| Also, the following constants are available: | ||||
|  | ||||
| - `pi`, `e` | ||||
|  | ||||
|  | ||||
| ##Hints | ||||
|   | ||||
							
								
								
									
										35
									
								
								test.c
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								test.c
									
									
									
									
									
								
							| @@ -37,6 +37,10 @@ test_case cases[] = { | ||||
|     {"1", 1}, | ||||
|     {"(1)", 1}, | ||||
|  | ||||
|     {"pi", 3.14159}, | ||||
|     {"atan(1)*4 - pi", 0}, | ||||
|     {"e", 2.71828}, | ||||
|  | ||||
|     {"2+1", 2+1}, | ||||
|     {"(((2+(1))))", 2+1}, | ||||
|     {"3+2", 3+2}, | ||||
| @@ -78,13 +82,13 @@ test_case cases[] = { | ||||
|     {"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}, | ||||
|     {"log(1000)", 3}, | ||||
|     {"log(1e3)", 3}, | ||||
|     {"log1.0e3", 3}, | ||||
|     {"log 1.0e3", 3}, | ||||
|     {"10^5*5e-5", 5}, | ||||
|  | ||||
|     {"100^.5+1", 11}, | ||||
| @@ -103,6 +107,23 @@ test_case cases[] = { | ||||
|     {"sqrt 100 * 7", 70}, | ||||
|     {"sqrt (100 * 100)", 100}, | ||||
|  | ||||
|     {"1,2", 2}, | ||||
|     {"1,2,3", 3}, | ||||
|     {"(1,2),3", 3}, | ||||
|     {"1,(2,3)", 3}, | ||||
|  | ||||
|     {"2^2", 4}, | ||||
|     {"pow(2,2)", 4}, | ||||
|  | ||||
|     {"atan2(1,1)", 0.7854}, | ||||
|     {"atan2(1,2)", 0.4636}, | ||||
|     {"atan2(2,1)", 1.1071}, | ||||
|     {"atan2(3,4)", 0.6435}, | ||||
|     {"atan2(3+3,4*2)", 0.6435}, | ||||
|     {"atan2(3+3,(4*2))", 0.6435}, | ||||
|     {"atan2((3+3),4*2)", 0.6435}, | ||||
|     {"atan2((3+3),(4*2))", 0.6435}, | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| @@ -141,6 +162,10 @@ void test_results() { | ||||
|         const double ev = te_interp(expr, &err); | ||||
|         lok(!err); | ||||
|         lfequal(ev, answer); | ||||
|  | ||||
|         if (err) { | ||||
|             printf("FAILED: %s (%d)\n", expr, err); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -160,6 +185,10 @@ void test_syntax() { | ||||
|         lequal(err, e); | ||||
|         lok(!n); | ||||
|  | ||||
|         if (err != e) { | ||||
|             printf("FAILED: %s\n", expr); | ||||
|         } | ||||
|  | ||||
|         const double k = te_interp(expr, 0); | ||||
|         lok(k != k); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										127
									
								
								tinyexpr.c
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								tinyexpr.c
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ | ||||
| #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}; | ||||
| enum {TOK_NULL, TOK_END, TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX, TOK_FUNCTION1, TOK_FUNCTION2, TOK_VARIABLE, TOK_SEP, TOK_ERROR}; | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -65,28 +65,36 @@ void te_free(te_expr *n) { | ||||
|  | ||||
| typedef struct { | ||||
|     const char *name; | ||||
|     te_fun1 f1; | ||||
|     union {const void *v; double *value; te_fun1 f1; te_fun2 f2;}; | ||||
|     int arity; | ||||
| } builtin; | ||||
|  | ||||
|  | ||||
| static const double pi = 3.14159265358979323846; | ||||
| static const double e  = 2.71828182845904523536; | ||||
|  | ||||
| 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}, | ||||
|     {"abs", {fabs}, 1}, | ||||
|     {"acos", {acos}, 1}, | ||||
|     {"asin", {asin}, 1}, | ||||
|     {"atan", {atan}, 1}, | ||||
|     {"atan2", {atan2}, 2}, | ||||
|     {"ceil", {ceil}, 1}, | ||||
|     {"cos", {cos}, 1}, | ||||
|     {"cosh", {cosh}, 1}, | ||||
|     {"e", {&e}, 0}, | ||||
|     {"exp", {exp}, 1}, | ||||
|     {"floor", {floor}, 1}, | ||||
|     {"ln", {log}, 1}, | ||||
|     {"log", {log10}, 1}, | ||||
|     {"pi", {&pi}, 0}, | ||||
|     {"pow", {pow}, 2}, | ||||
|     {"sin", {sin}, 1}, | ||||
|     {"sinh", {sinh}, 1}, | ||||
|     {"sqrt", {sqrt}, 1}, | ||||
|     {"tan", {tan}, 1}, | ||||
|     {"tanh", {tanh}, 1}, | ||||
|     {0} | ||||
| }; | ||||
|  | ||||
| @@ -131,6 +139,7 @@ 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 negate(double a) {return -a;} | ||||
| static double comma(double a, double b) {return b;} | ||||
|  | ||||
|  | ||||
| void next_token(state *s) { | ||||
| @@ -152,7 +161,7 @@ void next_token(state *s) { | ||||
|             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++; | ||||
|                 while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9')) s->next++; | ||||
|  | ||||
|                 const double *var = find_var(s, start, s->next - start); | ||||
|                 if (var) { | ||||
| @@ -162,12 +171,20 @@ void next_token(state *s) { | ||||
|                     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; | ||||
|                             if (f->arity == 0) { | ||||
|                                 s->type = TOK_NUMBER; | ||||
|                                 s->value = *f->value; | ||||
|                             } else if (f->arity == 1) { | ||||
|                                 s->type = TOK_FUNCTION1; | ||||
|                                 s->f1 = f->f1; | ||||
|                             } else if (f->arity == 2) { | ||||
|                                 s->type = TOK_FUNCTION2; | ||||
|                                 s->f2 = f->f2; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| @@ -175,14 +192,15 @@ void next_token(state *s) { | ||||
|             } 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 = fmod; break; | ||||
|                     case '+': s->type = TOK_INFIX; s->f2 = add; break; | ||||
|                     case '-': s->type = TOK_INFIX; s->f2 = sub; break; | ||||
|                     case '*': s->type = TOK_INFIX; s->f2 = mul; break; | ||||
|                     case '/': s->type = TOK_INFIX; s->f2 = divide; break; | ||||
|                     case '^': s->type = TOK_INFIX; s->f2 = pow; break; | ||||
|                     case '%': s->type = TOK_INFIX; s->f2 = fmod; break; | ||||
|                     case '(': s->type = TOK_OPEN; break; | ||||
|                     case ')': s->type = TOK_CLOSE; break; | ||||
|                     case ',': s->type = TOK_SEP; break; | ||||
|                     case ' ': case '\t': case '\n': case '\r': break; | ||||
|                     default: s->type = TOK_ERROR; break; | ||||
|                 } | ||||
| @@ -192,11 +210,12 @@ void next_token(state *s) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static te_expr *list(state *s); | ||||
| static te_expr *expr(state *s); | ||||
| static te_expr *power(state *s); | ||||
|  | ||||
| static te_expr *base(state *s) { | ||||
|     /* <base>      =    <constant> | <variable> | <function> <power> | "(" <expr> ")" */ | ||||
|     /* <base>      =    <constant> | <variable> | <function-1> <power> | <function-2> "(" <expr> "," <expr> ")" | "(" <list> ")" */ | ||||
|     te_expr *ret; | ||||
|  | ||||
|     switch (s->type) { | ||||
| @@ -219,9 +238,35 @@ static te_expr *base(state *s) { | ||||
|             ret->left = power(s); | ||||
|             break; | ||||
|  | ||||
|         case TOK_FUNCTION2: | ||||
|             ret = new_expr(0, 0); | ||||
|             ret->f2 = s->f2; | ||||
|             next_token(s); | ||||
|  | ||||
|             if (s->type != TOK_OPEN) { | ||||
|                 s->type = TOK_ERROR; | ||||
|             } else { | ||||
|                 next_token(s); | ||||
|                 ret->left = expr(s); | ||||
|  | ||||
|                 if (s->type != TOK_SEP) { | ||||
|                     s->type = TOK_ERROR; | ||||
|                 } else { | ||||
|                     next_token(s); | ||||
|                     ret->right = expr(s); | ||||
|                     if (s->type != TOK_CLOSE) { | ||||
|                         s->type = TOK_ERROR; | ||||
|                     } else { | ||||
|                         next_token(s); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|  | ||||
|         case TOK_OPEN: | ||||
|             next_token(s); | ||||
|             ret = expr(s); | ||||
|             ret = list(s); | ||||
|             if (s->type != TOK_CLOSE) { | ||||
|                 s->type = TOK_ERROR; | ||||
|             } else { | ||||
| @@ -243,7 +288,7 @@ static te_expr *base(state *s) { | ||||
| static te_expr *power(state *s) { | ||||
|     /* <power>     =    {("-" | "+")} <base> */ | ||||
|     int sign = 1; | ||||
|     while (s->type == TOK_FUNCTION2 && (s->f2 == add || s->f2 == sub)) { | ||||
|     while (s->type == TOK_INFIX && (s->f2 == add || s->f2 == sub)) { | ||||
|         if (s->f2 == sub) sign = -sign; | ||||
|         next_token(s); | ||||
|     } | ||||
| @@ -265,7 +310,7 @@ static te_expr *factor(state *s) { | ||||
|     /* <factor>    =    <power> {"^" <power>} */ | ||||
|     te_expr *ret = power(s); | ||||
|  | ||||
|     while (s->type == TOK_FUNCTION2 && (s->f2 == pow)) { | ||||
|     while (s->type == TOK_INFIX && (s->f2 == pow)) { | ||||
|         te_fun2 t = s->f2; | ||||
|         next_token(s); | ||||
|         ret = new_expr(ret, power(s)); | ||||
| @@ -280,7 +325,7 @@ 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 == fmod)) { | ||||
|     while (s->type == TOK_INFIX && (s->f2 == mul || s->f2 == divide || s->f2 == fmod)) { | ||||
|         te_fun2 t = s->f2; | ||||
|         next_token(s); | ||||
|         ret = new_expr(ret, factor(s)); | ||||
| @@ -295,7 +340,7 @@ 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)) { | ||||
|     while (s->type == TOK_INFIX && (s->f2 == add || s->f2 == sub)) { | ||||
|         te_fun2 t = s->f2; | ||||
|         next_token(s); | ||||
|         ret = new_expr(ret, term(s)); | ||||
| @@ -306,6 +351,20 @@ static te_expr *expr(state *s) { | ||||
| } | ||||
|  | ||||
|  | ||||
| static te_expr *list(state *s) { | ||||
|     /* <list>      =    <expr> {"," <expr>} */ | ||||
|     te_expr *ret = expr(s); | ||||
|  | ||||
|     while (s->type == TOK_SEP) { | ||||
|         next_token(s); | ||||
|         ret = new_expr(ret, term(s)); | ||||
|         ret->f2 = comma; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| double te_eval(const te_expr *n) { | ||||
|     double ret; | ||||
|  | ||||
| @@ -356,7 +415,7 @@ te_expr *te_compile(const char *expression, const te_variable *variables, int va | ||||
|     s.lookup_len = var_count; | ||||
|  | ||||
|     next_token(&s); | ||||
|     te_expr *root = expr(&s); | ||||
|     te_expr *root = list(&s); | ||||
|  | ||||
|     if (s.type != TOK_END) { | ||||
|         te_free(root); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user