diff --git a/test.c b/test.c index ffbfbf3..5ce73ba 100644 --- a/test.c +++ b/test.c @@ -338,6 +338,10 @@ double test2(double a, double b) { return a + b; } +double test3(double a, double b, double c) { + return (a + b) / c; +} + void test_dynamic() { @@ -345,9 +349,10 @@ void test_dynamic() { te_variable lookup[] = { {"x", &x}, {"f", &f}, - {"test0", test0, TE_FUNCTION0}, - {"test1", test1, TE_FUNCTION1}, - {"test2", test2, TE_FUNCTION2}, + {"test0", test0, TE_FUN | 0}, + {"test1", test1, TE_FUN | 1}, + {"test2", test2, TE_FUN | 2}, + {"test3", test3, TE_FUN | 3}, }; test_case cases[] = { @@ -362,6 +367,7 @@ void test_dynamic() { {"test1 f", 10}, {"test1 x", 4}, {"test2 (test0, x)", 8}, + {"test3 (test0, x, 2)", 4}, }; x = 2; diff --git a/tinyexpr.c b/tinyexpr.c index 4f3bee2..3ce461a 100644 --- a/tinyexpr.c +++ b/tinyexpr.c @@ -29,8 +29,11 @@ #include -enum {TOK_NULL, TOK_END, TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX, TOK_FUNCTION0, TOK_FUNCTION1, TOK_FUNCTION2, TOK_VARIABLE, TOK_SEP, TOK_ERROR}; -enum {TE_CONSTANT = -2}; +enum {TOK_NULL, TOK_END, TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX, + TOK_VARIABLE, TOK_SEP, TOK_ERROR, TOK_FUNCTION0, TOK_FUNCTION1, + TOK_FUNCTION2, TOK_FUNCTION3, TOK_FUNCTION4, TOK_FUNCTION5, TOK_FUNCTION6, + TOK_FUNCTION7 +}; @@ -38,29 +41,43 @@ typedef struct state { const char *start; const char *next; int type; - union {double value; te_fun0 f0; te_fun1 f1; te_fun2 f2; const double *bound;}; + union {double value; te_fun fun; const double *bound;}; const te_variable *lookup; int lookup_len; } state; +static int te_get_type(const int type) { + if(type == 0) return TE_VAR; + return type & TE_FLAG_TYPE; +} -static te_expr *new_expr(int type, te_expr *l, te_expr *r) { - te_expr *ret = malloc(sizeof(te_expr)); +static int te_get_arity(const int type) { + return type & TE_MASK_ARIT; +} + + +static te_expr *new_expr(const int type, const te_expr *members[]) { + int member_count = te_get_arity(type); + size_t member_size = sizeof(te_expr*) * member_count; + te_expr *ret = malloc(sizeof(te_expr) + member_size); + if(!members) { + memset(ret->members, 0, member_size); + } else { + memcpy(ret->members, members, member_size); + } ret->type = type; - ret->left = l; - ret->right = r; ret->bound = 0; return ret; } void te_free(te_expr *n) { + int i; if (!n) return; - if (n->left) te_free(n->left); - if (n->right) te_free(n->right); + for(i = n->member_count - 1; i >= 0; i--) te_free(n->members[i]); free(n); } @@ -70,30 +87,29 @@ static const double e = 2.71828182845904523536; static const te_variable functions[] = { /* must be in alphabetical order */ - {"abs", fabs, TE_FUNCTION1}, - {"acos", acos, TE_FUNCTION1}, - {"asin", asin, TE_FUNCTION1}, - {"atan", atan, TE_FUNCTION1}, - {"atan2", atan2, TE_FUNCTION2}, - {"ceil", ceil, TE_FUNCTION1}, - {"cos", cos, TE_FUNCTION1}, - {"cosh", cosh, TE_FUNCTION1}, - {"e", &e, TE_VARIABLE}, - {"exp", exp, TE_FUNCTION1}, - {"floor", floor, TE_FUNCTION1}, - {"ln", log, TE_FUNCTION1}, - {"log", log10, TE_FUNCTION1}, - {"pi", &pi, TE_VARIABLE}, - {"pow", pow, TE_FUNCTION2}, - {"sin", sin, TE_FUNCTION1}, - {"sinh", sinh, TE_FUNCTION1}, - {"sqrt", sqrt, TE_FUNCTION1}, - {"tan", tan, TE_FUNCTION1}, - {"tanh", tanh, TE_FUNCTION1}, + {"abs", fabs, TE_FUN | 1}, + {"acos", acos, TE_FUN | 1}, + {"asin", asin, TE_FUN | 1}, + {"atan", atan, TE_FUN | 1}, + {"atan2", atan2, TE_FUN | 2}, + {"ceil", ceil, TE_FUN | 1}, + {"cos", cos, TE_FUN | 1}, + {"cosh", cosh, TE_FUN | 1}, + {"e", &e, TE_VAR}, + {"exp", exp, TE_FUN | 1}, + {"floor", floor, TE_FUN | 1}, + {"ln", log, TE_FUN | 1}, + {"log", log10, TE_FUN | 1}, + {"pi", &pi, TE_VAR}, + {"pow", pow, TE_FUN | 2}, + {"sin", sin, TE_FUN | 1}, + {"sinh", sinh, TE_FUN | 1}, + {"sqrt", sqrt, TE_FUN | 1}, + {"tan", tan, TE_FUN | 1}, + {"tanh", tanh, TE_FUN | 1}, {0} }; - static const te_variable *find_function(const char *name, int len) { int imin = 0; int imax = sizeof(functions) / sizeof(te_variable) - 2; @@ -154,6 +170,7 @@ void next_token(state *s) { } else { /* Look for a variable or builtin function call. */ if (s->next[0] >= 'a' && s->next[0] <= 'z') { + int arity, type; const char *start; start = s->next; while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9')) s->next++; @@ -164,30 +181,28 @@ void next_token(state *s) { if (!var) { s->type = TOK_ERROR; } else { - if (var->type == TE_VARIABLE) { - s->type = TOK_VARIABLE; - s->bound = var->value; - } else if (var->type == TE_FUNCTION0) { - s->type = TOK_FUNCTION0; - s->f0 = var->value; - } else if (var->type == TE_FUNCTION1) { - s->type = TOK_FUNCTION1; - s->f1 = var->value; - } else if (var->type == TE_FUNCTION2) { - s->type = TOK_FUNCTION2; - s->f2 = var->value; + type = te_get_type(var->type); + arity = te_get_arity(var->type); + switch(type) + { + case TE_VAR: + s->type = TOK_VARIABLE; + s->bound = var->value; break; + case TE_FUN: + s->type = TOK_FUNCTION0 + arity; + s->fun.f0 = (void*)var->value; } } } else { /* Look for an operator or special character. */ switch (s->next++[0]) { - 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_INFIX; s->fun.f2 = add; break; + case '-': s->type = TOK_INFIX; s->fun.f2 = sub; break; + case '*': s->type = TOK_INFIX; s->fun.f2 = mul; break; + case '/': s->type = TOK_INFIX; s->fun.f2 = divide; break; + case '^': s->type = TOK_INFIX; s->fun.f2 = pow; break; + case '%': s->type = TOK_INFIX; s->fun.f2 = fmod; break; case '(': s->type = TOK_OPEN; break; case ')': s->type = TOK_CLOSE; break; case ',': s->type = TOK_SEP; break; @@ -207,23 +222,24 @@ static te_expr *power(state *s); static te_expr *base(state *s) { /* = | | {"(" ")"} | | "(" "," ")" | "(" ")" */ te_expr *ret; + int arity; switch (s->type) { case TOK_NUMBER: - ret = new_expr(TE_CONSTANT, 0, 0); + ret = new_expr(TE_CONST, 0); ret->value = s->value; next_token(s); break; case TOK_VARIABLE: - ret = new_expr(TE_VARIABLE, 0, 0); + ret = new_expr(TE_VAR, 0); ret->bound = s->bound; next_token(s); break; case TOK_FUNCTION0: - ret = new_expr(TE_FUNCTION0, 0, 0); - ret->f0 = s->f0; + ret = new_expr(TE_FUN, 0); + ret->fun.f0 = s->fun.f0; next_token(s); if (s->type == TOK_OPEN) { next_token(s); @@ -235,34 +251,37 @@ static te_expr *base(state *s) { } break; - case TOK_FUNCTION1: - ret = new_expr(TE_FUNCTION1, 0, 0); - ret->f1 = s->f1; + case TOK_FUNCTION1: + ret = new_expr(TE_FUN | 1, 0); + ret->fun.f0 = s->fun.f0; + next_token(s); - ret->left = power(s); + ret->members[0] = power(s); break; - case TOK_FUNCTION2: - ret = new_expr(TE_FUNCTION2, 0, 0); - ret->f2 = s->f2; + case TOK_FUNCTION2: case TOK_FUNCTION3: case TOK_FUNCTION4: + case TOK_FUNCTION5: case TOK_FUNCTION6: case TOK_FUNCTION7: + arity = s->type - TOK_FUNCTION0; + + ret = new_expr(TE_FUN | arity, 0); + ret->fun.f0 = s->fun.f0; 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) { + int i; + for(i = 0; i < arity; i++) { + next_token(s); + ret->members[i] = expr(s); + if(s->type != TOK_SEP) { + break; + } + } + if(s->type != TOK_CLOSE || i < arity - 1) { 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); - } } } @@ -279,7 +298,7 @@ static te_expr *base(state *s) { break; default: - ret = new_expr(0, 0, 0); + ret = new_expr(0, 0); s->type = TOK_ERROR; ret->value = 0.0/0.0; break; @@ -292,8 +311,8 @@ static te_expr *base(state *s) { static te_expr *power(state *s) { /* = {("-" | "+")} */ int sign = 1; - while (s->type == TOK_INFIX && (s->f2 == add || s->f2 == sub)) { - if (s->f2 == sub) sign = -sign; + while (s->type == TOK_INFIX && (s->fun.f2 == add || s->fun.f2 == sub)) { + if (s->fun.f2 == sub) sign = -sign; next_token(s); } @@ -302,8 +321,8 @@ static te_expr *power(state *s) { if (sign == 1) { ret = base(s); } else { - ret = new_expr(TE_FUNCTION1, base(s), 0); - ret->f1 = negate; + ret = new_expr(TE_FUN | 1, (const te_expr*[]){base(s)}); + ret->fun.f1 = negate; } return ret; @@ -314,11 +333,11 @@ static te_expr *factor(state *s) { /* = {"^" } */ te_expr *ret = power(s); - while (s->type == TOK_INFIX && (s->f2 == pow)) { - te_fun2 t = s->f2; + while (s->type == TOK_INFIX && (s->fun.f2 == pow)) { + te_fun2 t = s->fun.f2; next_token(s); - ret = new_expr(TE_FUNCTION2, ret, power(s)); - ret->f2 = t; + ret = new_expr(TE_FUN | 2, (const te_expr*[]){ret, power(s)}); + ret->fun.f2 = t; } return ret; @@ -329,11 +348,11 @@ static te_expr *term(state *s) { /* = {("*" | "/" | "%") } */ te_expr *ret = factor(s); - while (s->type == TOK_INFIX && (s->f2 == mul || s->f2 == divide || s->f2 == fmod)) { - te_fun2 t = s->f2; + while (s->type == TOK_INFIX && (s->fun.f2 == mul || s->fun.f2 == divide || s->fun.f2 == fmod)) { + te_fun2 t = s->fun.f2; next_token(s); - ret = new_expr(TE_FUNCTION2, ret, factor(s)); - ret->f2 = t; + ret = new_expr(TE_FUN | 2, (const te_expr*[]){ret, factor(s)}); + ret->fun.f2 = t; } return ret; @@ -344,11 +363,11 @@ static te_expr *expr(state *s) { /* = {("+" | "-") } */ te_expr *ret = term(s); - while (s->type == TOK_INFIX && (s->f2 == add || s->f2 == sub)) { - te_fun2 t = s->f2; + while (s->type == TOK_INFIX && (s->fun.f2 == add || s->fun.f2 == sub)) { + te_fun2 t = s->fun.f2; next_token(s); - ret = new_expr(TE_FUNCTION2, ret, term(s)); - ret->f2 = t; + ret = new_expr(TE_FUN | 2, (const te_expr*[]){ret, term(s)}); + ret->fun.f2 = t; } return ret; @@ -361,8 +380,8 @@ static te_expr *list(state *s) { while (s->type == TOK_SEP) { next_token(s); - ret = new_expr(TE_FUNCTION2, ret, term(s)); - ret->f2 = comma; + ret = new_expr(TE_FUN | 2, (const te_expr*[]){ret, term(s)}); + ret->fun.f2 = comma; } return ret; @@ -370,12 +389,23 @@ static te_expr *list(state *s) { double te_eval(const te_expr *n) { - switch (n->type) { - case TE_CONSTANT: return n->value; - case TE_VARIABLE: return *n->bound; - case TE_FUNCTION0: return n->f0(); - case TE_FUNCTION1: return n->f1(te_eval(n->left)); - case TE_FUNCTION2: return n->f2(te_eval(n->left), te_eval(n->right)); + switch(te_get_type(n->type)) { + case TE_CONST: return n->value; + case TE_VAR: return *n->bound; + case TE_FUN: + switch(te_get_arity(n->type)) { + #define m(e) te_eval(n->members[e]) + case 0: return n->fun.f0(); + case 1: return n->fun.f1(m(0)); + case 2: return n->fun.f2(m(0), m(1)); + case 3: return n->fun.f3(m(0), m(1), m(2)); + case 4: return n->fun.f4(m(0), m(1), m(2), m(3)); + case 5: return n->fun.f5(m(0), m(1), m(2), m(3), m(4)); + case 6: return n->fun.f6(m(0), m(1), m(2), m(3), m(4), m(5)); + case 7: return n->fun.f7(m(0), m(1), m(2), m(3), m(4), m(5), m(6)); + default: return 0.0/0.0; + #undef m + } default: return 0.0/0.0; } } @@ -447,21 +477,24 @@ double te_interp(const char *expression, int *error) { return ret; } - static void pn (const te_expr *n, int depth) { + int i, arity; printf("%*s", depth, ""); - 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); + switch(te_get_type(n->type)) { + case TE_CONST: printf("%f\n", n->value); break; + case TE_VAR: printf("bound %p\n", n->bound); break; + case TE_FUN: + arity = te_get_arity(n->type); + printf("f%d", arity); + for(i = 0; i < arity; i++) { + printf(" %p", n->members[i]); + } + printf("\n"); + for(i = 0; i < arity; i++) { + pn(n->members[i], depth + 1); + } + break; } } diff --git a/tinyexpr.h b/tinyexpr.h index bf515fe..7ad0a26 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -34,18 +34,29 @@ extern "C" { typedef double (*te_fun0)(void); typedef double (*te_fun1)(double); typedef double (*te_fun2)(double, double); +typedef double (*te_fun3)(double, double, double); +typedef double (*te_fun4)(double, double, double, double); +typedef double (*te_fun5)(double, double, double, double, double); +typedef double (*te_fun6)(double, double, double, double, double, double); +typedef double (*te_fun7)(double, double, double, double, double, double, double); +typedef union +{ + te_fun0 f0; te_fun1 f1; te_fun2 f2; te_fun3 f3; te_fun4 f4; te_fun5 f5; te_fun6 f6; te_fun7 f7; +} te_fun; typedef struct te_expr { - struct te_expr *left, *right; int type; - union {double value; const double *bound; te_fun0 f0; te_fun1 f1; te_fun2 f2;}; + union {double value; const double *bound; te_fun fun; }; + int member_count; + struct te_expr *members[]; } te_expr; +#define TE_MASK_ARIT 0x00000007 /* Three bits, Arity, max is 8 */ +#define TE_FLAG_TYPE 0x00000018 /* Two bits, 1 = constant, 2 = variable, 3 = function */ - -enum {TE_FUNCTION0 = -1, TE_VARIABLE = 0, TE_FUNCTION1 = 1, TE_FUNCTION2 = 2}; +enum { TE_CONST = 1 << 3, TE_VAR = 2 << 3, TE_FUN = 3 << 3}; typedef struct te_variable { const char *name;