mirror of
				https://github.com/eledio-devices/thirdparty-tinyexpr.git
				synced 2025-10-31 16:14:16 +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: | TinyExpr parses the following grammar: | ||||||
|  |  | ||||||
|  |     <list>      =    <expr> {"," <expr>} | ||||||
|     <expr>      =    <term> {("+" | "-") <term>} |     <expr>      =    <term> {("+" | "-") <term>} | ||||||
|     <term>      =    <factor> {("*" | "/" | "%") <factor>} |     <term>      =    <factor> {("*" | "/" | "%") <factor>} | ||||||
|     <factor>    =    <power> {"^" <power>} |     <factor>    =    <power> {"^" <power>} | ||||||
|     <power>     =    {("-" | "+")} <base> |     <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. | In addition, whitespace between tokens is ignored. | ||||||
|  |  | ||||||
| @@ -236,7 +237,11 @@ left-to-right). | |||||||
|  |  | ||||||
| In addition, the following C math functions are also supported: | 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 | ##Hints | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								test.c
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								test.c
									
									
									
									
									
								
							| @@ -37,6 +37,10 @@ test_case cases[] = { | |||||||
|     {"1", 1}, |     {"1", 1}, | ||||||
|     {"(1)", 1}, |     {"(1)", 1}, | ||||||
|  |  | ||||||
|  |     {"pi", 3.14159}, | ||||||
|  |     {"atan(1)*4 - pi", 0}, | ||||||
|  |     {"e", 2.71828}, | ||||||
|  |  | ||||||
|     {"2+1", 2+1}, |     {"2+1", 2+1}, | ||||||
|     {"(((2+(1))))", 2+1}, |     {"(((2+(1))))", 2+1}, | ||||||
|     {"3+2", 3+2}, |     {"3+2", 3+2}, | ||||||
| @@ -78,13 +82,13 @@ test_case cases[] = { | |||||||
|     {"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}, |     {"log 1000", 3}, | ||||||
|     {"log1e3", 3}, |     {"log 1e3", 3}, | ||||||
|     {"log 1000", 3}, |     {"log 1000", 3}, | ||||||
|     {"log 1e3", 3}, |     {"log 1e3", 3}, | ||||||
|     {"log(1000)", 3}, |     {"log(1000)", 3}, | ||||||
|     {"log(1e3)", 3}, |     {"log(1e3)", 3}, | ||||||
|     {"log1.0e3", 3}, |     {"log 1.0e3", 3}, | ||||||
|     {"10^5*5e-5", 5}, |     {"10^5*5e-5", 5}, | ||||||
|  |  | ||||||
|     {"100^.5+1", 11}, |     {"100^.5+1", 11}, | ||||||
| @@ -103,6 +107,23 @@ test_case cases[] = { | |||||||
|     {"sqrt 100 * 7", 70}, |     {"sqrt 100 * 7", 70}, | ||||||
|     {"sqrt (100 * 100)", 100}, |     {"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); |         const double ev = te_interp(expr, &err); | ||||||
|         lok(!err); |         lok(!err); | ||||||
|         lfequal(ev, answer); |         lfequal(ev, answer); | ||||||
|  |  | ||||||
|  |         if (err) { | ||||||
|  |             printf("FAILED: %s (%d)\n", expr, err); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -160,6 +185,10 @@ void test_syntax() { | |||||||
|         lequal(err, e); |         lequal(err, e); | ||||||
|         lok(!n); |         lok(!n); | ||||||
|  |  | ||||||
|  |         if (err != e) { | ||||||
|  |             printf("FAILED: %s\n", expr); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         const double k = te_interp(expr, 0); |         const double k = te_interp(expr, 0); | ||||||
|         lok(k != k); |         lok(k != k); | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										125
									
								
								tinyexpr.c
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								tinyexpr.c
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ | |||||||
| #include <stdio.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}; | 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 { | typedef struct { | ||||||
|     const char *name; |     const char *name; | ||||||
|     te_fun1 f1; |     union {const void *v; double *value; te_fun1 f1; te_fun2 f2;}; | ||||||
|  |     int arity; | ||||||
| } builtin; | } builtin; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const double pi = 3.14159265358979323846; | ||||||
|  | static const double e  = 2.71828182845904523536; | ||||||
|  |  | ||||||
| static const builtin functions[] = { | static const builtin functions[] = { | ||||||
|     /* must be in alphabetical order */ |     /* must be in alphabetical order */ | ||||||
|     {"abs", fabs}, |     {"abs", {fabs}, 1}, | ||||||
|     {"acos", acos}, |     {"acos", {acos}, 1}, | ||||||
|     {"asin", asin}, |     {"asin", {asin}, 1}, | ||||||
|     {"atan", atan}, |     {"atan", {atan}, 1}, | ||||||
|     {"ceil", ceil}, |     {"atan2", {atan2}, 2}, | ||||||
|     {"cos", cos}, |     {"ceil", {ceil}, 1}, | ||||||
|     {"cosh", cosh}, |     {"cos", {cos}, 1}, | ||||||
|     {"exp", exp}, |     {"cosh", {cosh}, 1}, | ||||||
|     {"floor", floor}, |     {"e", {&e}, 0}, | ||||||
|     {"ln", log}, |     {"exp", {exp}, 1}, | ||||||
|     {"log", log10}, |     {"floor", {floor}, 1}, | ||||||
|     {"sin", sin}, |     {"ln", {log}, 1}, | ||||||
|     {"sinh", sinh}, |     {"log", {log10}, 1}, | ||||||
|     {"sqrt", sqrt}, |     {"pi", {&pi}, 0}, | ||||||
|     {"tan", tan}, |     {"pow", {pow}, 2}, | ||||||
|     {"tanh", tanh}, |     {"sin", {sin}, 1}, | ||||||
|  |     {"sinh", {sinh}, 1}, | ||||||
|  |     {"sqrt", {sqrt}, 1}, | ||||||
|  |     {"tan", {tan}, 1}, | ||||||
|  |     {"tanh", {tanh}, 1}, | ||||||
|     {0} |     {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 mul(double a, double b) {return a * b;} | ||||||
| static double divide(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 negate(double a) {return -a;} | ||||||
|  | static double comma(double a, double b) {return b;} | ||||||
|  |  | ||||||
|  |  | ||||||
| void next_token(state *s) { | void next_token(state *s) { | ||||||
| @@ -152,7 +161,7 @@ void next_token(state *s) { | |||||||
|             if (s->next[0] >= 'a' && s->next[0] <= 'z') { |             if (s->next[0] >= 'a' && s->next[0] <= 'z') { | ||||||
|                 const char *start; |                 const char *start; | ||||||
|                 start = s->next; |                 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); |                 const double *var = find_var(s, start, s->next - start); | ||||||
|                 if (var) { |                 if (var) { | ||||||
| @@ -162,12 +171,20 @@ void next_token(state *s) { | |||||||
|                     if (s->next - start > 15) { |                     if (s->next - start > 15) { | ||||||
|                         s->type = TOK_ERROR; |                         s->type = TOK_ERROR; | ||||||
|                     } else { |                     } else { | ||||||
|                         s->type = TOK_FUNCTION1; |  | ||||||
|                         const builtin *f = find_function(start, s->next - start); |                         const builtin *f = find_function(start, s->next - start); | ||||||
|                         if (!f) { |                         if (!f) { | ||||||
|                             s->type = TOK_ERROR; |                             s->type = TOK_ERROR; | ||||||
|                         } else { |                         } else { | ||||||
|  |                             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; |                                 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 { |             } else { | ||||||
|                 /* Look for an operator or special character. */ |                 /* Look for an operator or special character. */ | ||||||
|                 switch (s->next++[0]) { |                 switch (s->next++[0]) { | ||||||
|                     case '+': s->type = TOK_FUNCTION2; s->f2 = add; break; |                     case '+': s->type = TOK_INFIX; s->f2 = add; break; | ||||||
|                     case '-': s->type = TOK_FUNCTION2; s->f2 = sub; break; |                     case '-': s->type = TOK_INFIX; s->f2 = sub; break; | ||||||
|                     case '*': s->type = TOK_FUNCTION2; s->f2 = mul; break; |                     case '*': s->type = TOK_INFIX; s->f2 = mul; break; | ||||||
|                     case '/': s->type = TOK_FUNCTION2; s->f2 = divide; break; |                     case '/': s->type = TOK_INFIX; s->f2 = divide; break; | ||||||
|                     case '^': s->type = TOK_FUNCTION2; s->f2 = pow; break; |                     case '^': s->type = TOK_INFIX; s->f2 = pow; break; | ||||||
|                     case '%': s->type = TOK_FUNCTION2; s->f2 = fmod; break; |                     case '%': s->type = TOK_INFIX; s->f2 = fmod; break; | ||||||
|                     case '(': s->type = TOK_OPEN; break; |                     case '(': s->type = TOK_OPEN; break; | ||||||
|                     case ')': s->type = TOK_CLOSE; break; |                     case ')': s->type = TOK_CLOSE; break; | ||||||
|  |                     case ',': s->type = TOK_SEP; break; | ||||||
|                     case ' ': case '\t': case '\n': case '\r': break; |                     case ' ': case '\t': case '\n': case '\r': break; | ||||||
|                     default: s->type = TOK_ERROR; 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 *expr(state *s); | ||||||
| static te_expr *power(state *s); | static te_expr *power(state *s); | ||||||
|  |  | ||||||
| static te_expr *base(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; |     te_expr *ret; | ||||||
|  |  | ||||||
|     switch (s->type) { |     switch (s->type) { | ||||||
| @@ -219,9 +238,35 @@ static te_expr *base(state *s) { | |||||||
|             ret->left = power(s); |             ret->left = power(s); | ||||||
|             break; |             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: |         case TOK_OPEN: | ||||||
|             next_token(s); |             next_token(s); | ||||||
|             ret = expr(s); |             ret = list(s); | ||||||
|             if (s->type != TOK_CLOSE) { |             if (s->type != TOK_CLOSE) { | ||||||
|                 s->type = TOK_ERROR; |                 s->type = TOK_ERROR; | ||||||
|             } else { |             } else { | ||||||
| @@ -243,7 +288,7 @@ static te_expr *base(state *s) { | |||||||
| static te_expr *power(state *s) { | static te_expr *power(state *s) { | ||||||
|     /* <power>     =    {("-" | "+")} <base> */ |     /* <power>     =    {("-" | "+")} <base> */ | ||||||
|     int sign = 1; |     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; |         if (s->f2 == sub) sign = -sign; | ||||||
|         next_token(s); |         next_token(s); | ||||||
|     } |     } | ||||||
| @@ -265,7 +310,7 @@ static te_expr *factor(state *s) { | |||||||
|     /* <factor>    =    <power> {"^" <power>} */ |     /* <factor>    =    <power> {"^" <power>} */ | ||||||
|     te_expr *ret = power(s); |     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; |         te_fun2 t = s->f2; | ||||||
|         next_token(s); |         next_token(s); | ||||||
|         ret = new_expr(ret, power(s)); |         ret = new_expr(ret, power(s)); | ||||||
| @@ -280,7 +325,7 @@ static te_expr *term(state *s) { | |||||||
|     /* <term>      =    <factor> {("*" | "/" | "%") <factor>} */ |     /* <term>      =    <factor> {("*" | "/" | "%") <factor>} */ | ||||||
|     te_expr *ret = factor(s); |     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; |         te_fun2 t = s->f2; | ||||||
|         next_token(s); |         next_token(s); | ||||||
|         ret = new_expr(ret, factor(s)); |         ret = new_expr(ret, factor(s)); | ||||||
| @@ -295,7 +340,7 @@ static te_expr *expr(state *s) { | |||||||
|     /* <expr>      =    <term> {("+" | "-") <term>} */ |     /* <expr>      =    <term> {("+" | "-") <term>} */ | ||||||
|     te_expr *ret = term(s); |     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; |         te_fun2 t = s->f2; | ||||||
|         next_token(s); |         next_token(s); | ||||||
|         ret = new_expr(ret, term(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 te_eval(const te_expr *n) { | ||||||
|     double ret; |     double ret; | ||||||
|  |  | ||||||
| @@ -356,7 +415,7 @@ te_expr *te_compile(const char *expression, const te_variable *variables, int va | |||||||
|     s.lookup_len = var_count; |     s.lookup_len = var_count; | ||||||
|  |  | ||||||
|     next_token(&s); |     next_token(&s); | ||||||
|     te_expr *root = expr(&s); |     te_expr *root = list(&s); | ||||||
|  |  | ||||||
|     if (s.type != TOK_END) { |     if (s.type != TOK_END) { | ||||||
|         te_free(root); |         te_free(root); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user