From 2e11e6c692f4a7d1c865637d27a24cd2f79f526b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 4 Nov 2018 13:13:03 +0100 Subject: [PATCH] tinyexpr: Make te_expr a class Removes some #define weirdness. --- src/tinyexpr.cpp | 202 ++++++++++++++++++----------------------------- 1 file changed, 78 insertions(+), 124 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 95cc71f3c..a3c03ca21 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -33,6 +33,7 @@ #include #include #include +#include // TODO: It would be nice not to rely on a typedef for this, especially one that can only do functions with two args. typedef double (*te_fun2)(double, double); @@ -46,18 +47,32 @@ enum { TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX }; -int get_arity(const int type) { - if (type == TE_FUNCTION3) return 3; - if (type == TE_FUNCTION2) return 2; - if (type == TE_FUNCTION1) return 1; - return 0; -} - -typedef struct te_expr { +class te_expr { +public: int type; - union {double value; const void *function;}; - te_expr *parameters[1]; -} te_expr; + double value; + const void *function; + int arity = 0; + std::vector parameters; + te_expr(int t, const void *fun, te_expr *params) : type(t), value(0), function(fun) + { + // We fill in the arity automatically, because we need it here anyway. + switch (t) { + case TE_FUNCTION0: arity = 0; break; + case TE_FUNCTION1: arity = 1; break; + case TE_FUNCTION2: arity = 2; break; + case TE_FUNCTION3: arity = 3; break; + default: arity = 0; + } + + // We allow filling the parameters later. + if (params) { + for (int i = 0; i < arity; i++) parameters.push_back(params[i]); + } + } + + te_expr(int t, double val) : type(t), value(val), function(NULL) {} +}; // TODO: Rename since variables have been removed. typedef struct te_builtin { @@ -77,51 +92,10 @@ typedef struct state { /* Parses the input expression and binds variables. */ /* Returns NULL on error. */ -te_expr *te_compile(const char *expression, te_error_t *error); +te_expr te_compile(const char *expression, te_error_t *error); /* Evaluates the expression. */ -double te_eval(const te_expr *n); - -/* Frees the expression. */ -/* This is safe to call on NULL pointers. */ -void te_free(te_expr *n); - -// TODO: That move there? Ouch. Replace with a proper class with a constructor. -#define NEW_EXPR(type, ...) new_expr((type), std::move((const te_expr*[]){__VA_ARGS__})) - -static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const int arity = get_arity(type); - const int psize = sizeof(te_expr*) * arity; - const int size = (sizeof(te_expr) - sizeof(void*)) + psize; - te_expr *ret = (te_expr *)malloc(size); - // This sets float to 0, which depends on the implementation. - // We rely on IEEE-754 floats anyway, so it's okay. - memset(ret, 0, size); - if (arity && parameters) { - memcpy(ret->parameters, parameters, psize); - } - ret->type = type; - return ret; -} - - -void te_free_parameters(te_expr *n) { - if (!n) return; - int arity = get_arity(n->type); - // Free all parameters from the back to the front. - while (arity > 0) { - te_free(n->parameters[arity - 1]); - arity--; - } -} - - -void te_free(te_expr *n) { - if (!n) return; - te_free_parameters(n); - free(n); -} - +double te_eval(const te_expr &n); static double pi() {return 3.14159265358979323846;} static double e() {return 2.71828182845904523536;} @@ -261,24 +235,21 @@ void next_token(state *s) { } -static te_expr *expr(state *s); -static te_expr *power(state *s); +static te_expr expr(state *s); +static te_expr power(state *s); -static te_expr *base(state *s) { +static te_expr base(state *s) { /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ - te_expr *ret; - int arity; + te_expr ret(0, NULL, NULL); switch (s->type) { case TOK_NUMBER: - ret = new_expr(TE_CONSTANT, 0); - ret->value = s->value; + ret = te_expr(TE_CONSTANT, s->value); next_token(s); break; case TE_FUNCTION0: - ret = new_expr(s->type, 0); - ret->function = s->function; + ret = te_expr(s->type, s->function, NULL); next_token(s); if (s->type == TOK_OPEN) { next_token(s); @@ -294,27 +265,24 @@ static te_expr *base(state *s) { case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - arity = get_arity(s->type); - - ret = new_expr(s->type, 0); - ret->function = s->function; + ret = te_expr(s->type, s->function, NULL); next_token(s); if (s->type == TOK_OPEN) { int i; - for(i = 0; i < arity; i++) { + for(i = 0; i < ret.arity; i++) { next_token(s); - ret->parameters[i] = expr(s); + ret.parameters.push_back(expr(s)); if(s->type != TOK_SEP) { break; } } - if(s->type == TOK_CLOSE && i == arity - 1) { + if(s->type == TOK_CLOSE && i == ret.arity - 1) { next_token(s); } else if (s->type != TOK_ERROR || s->error == TE_ERROR_UNKNOWN) { s->type = TOK_ERROR; - s->error = i < arity ? TE_ERROR_TOO_FEW_ARGS + s->error = i < ret.arity ? TE_ERROR_TOO_FEW_ARGS : TE_ERROR_TOO_MANY_ARGS; } } else if (s->type != TOK_ERROR @@ -343,16 +311,14 @@ static te_expr *base(state *s) { // This means we have too few things. // Instead of introducing another error, just call it // "too few args". - ret = new_expr(0, 0); + ret = te_expr(TE_CONSTANT, NAN); s->type = TOK_ERROR; s->error = TE_ERROR_TOO_FEW_ARGS; - ret->value = NAN; break; default: - ret = new_expr(0, 0); + ret = te_expr(TE_CONSTANT, NAN); s->type = TOK_ERROR; s->error = TE_ERROR_UNKNOWN; - ret->value = NAN; break; } @@ -360,7 +326,7 @@ static te_expr *base(state *s) { } -static te_expr *power(state *s) { +static te_expr power(state *s) { /* = {("-" | "+")} */ int sign = 1; while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { @@ -368,72 +334,70 @@ static te_expr *power(state *s) { next_token(s); } - te_expr *ret; - if (sign == 1) { - ret = base(s); + te_expr ret = base(s); + return ret; } else { - ret = NEW_EXPR(TE_FUNCTION1, base(s)); - ret->function = (const void *) negate; + te_expr params[1] = { base(s) }; + te_expr ret = te_expr(TE_FUNCTION1, (const void *) negate, params); + return ret; } - - return ret; } -static te_expr *factor(state *s) { +static te_expr factor(state *s) { /* = {"^" } */ - te_expr *ret = power(s); + te_expr ret = power(s); while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)pow)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2, ret, power(s)); - ret->function = (const void *) t; + + te_expr param[2] = { ret, power(s) }; + ret = te_expr(TE_FUNCTION2, (const void *) t, param); } return ret; } -static te_expr *term(state *s) { +static te_expr term(state *s) { /* = {("*" | "/" | "%") } */ - te_expr *ret = factor(s); + te_expr ret = factor(s); while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)mul || s->function == (const void*)(te_fun2)divide || s->function == (const void*)(te_fun2)fmod)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2, ret, factor(s)); - ret->function = (const void *) t; + te_expr params[2] = { ret, factor(s) }; + ret = te_expr(TE_FUNCTION2, (const void *) t, params); } return ret; } -static te_expr *expr(state *s) { +static te_expr expr(state *s) { /* = {("+" | "-") } */ - te_expr *ret = term(s); + te_expr ret = term(s); while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2, ret, term(s)); - ret->function = (const void *) t; + te_expr params[2] = { ret, term(s) }; + ret = te_expr(TE_FUNCTION2, (const void *) t, params); } return ret; } -#define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) -#define M(e) te_eval(n->parameters[e]) +#define TE_FUN(...) ((double(*)(__VA_ARGS__))n.function) +#define M(e) te_eval(n.parameters[e]) -double te_eval(const te_expr *n) { - if (!n) return NAN; - - switch(n->type) { - case TE_CONSTANT: return n->value; +double te_eval(const te_expr &n) { + switch(n.type) { + case TE_CONSTANT: + return n.value; case TE_FUNCTION0: return TE_FUN(void)(); case TE_FUNCTION1: @@ -444,43 +408,39 @@ double te_eval(const te_expr *n) { return TE_FUN(double, double, double)(M(0), M(1), M(2)); default: return NAN; } - } #undef TE_FUN #undef M -static void optimize(te_expr *n) { +static void optimize(te_expr &n) { /* Evaluates as much as possible. */ - if (n->type == TE_CONSTANT) return; + if (n.type == TE_CONSTANT) return; - const int arity = get_arity(n->type); bool known = true; - for (int i = 0; i < arity; ++i) { - optimize(n->parameters[i]); - if ((n->parameters[i])->type != TE_CONSTANT) { + for (int i = 0; i < n.arity; ++i) { + optimize(n.parameters[i]); + if ((n.parameters[i]).type != TE_CONSTANT) { known = false; } } if (known) { const double value = te_eval(n); - te_free_parameters(n); - n->type = TE_CONSTANT; - n->value = value; + n.type = TE_CONSTANT; + n.value = value; } } -te_expr *te_compile(const char *expression, te_error_t *error) { +te_expr te_compile(const char *expression, te_error_t *error) { state s; s.start = s.next = expression; s.error = TE_ERROR_NONE; next_token(&s); - te_expr *root = expr(&s); + te_expr root = expr(&s); if (s.type != TOK_END) { - te_free(root); if (error) { error->position = (s.next - s.start) + 1; if (s.error != TE_ERROR_NONE) { @@ -495,22 +455,16 @@ te_expr *te_compile(const char *expression, te_error_t *error) { error->type = TE_ERROR_TOO_MANY_ARGS; } } - return 0; } else { optimize(root); if (error) error->position = 0; - return root; } + return root; } double te_interp(const char *expression, te_error_t *error) { - te_expr *n = te_compile(expression, error); + te_expr n = te_compile(expression, error); double ret; - if (n) { - ret = te_eval(n); - te_free(n); - } else { - ret = NAN; - } + ret = te_eval(n); return ret; }