diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 356ed90bb..95cc71f3c 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -33,7 +33,6 @@ #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); @@ -47,34 +46,18 @@ enum { TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX }; -class te_expr { -public: +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 { int type; - 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; - } - - parameters.reserve(arity); - - // 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) {} -}; + union {double value; const void *function;}; + te_expr *parameters[1]; +} te_expr; // TODO: Rename since variables have been removed. typedef struct te_builtin { @@ -94,10 +77,51 @@ 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); +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); +} + static double pi() {return 3.14159265358979323846;} static double e() {return 2.71828182845904523536;} @@ -237,21 +261,24 @@ 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(0, NULL, NULL); + te_expr *ret; + int arity; switch (s->type) { case TOK_NUMBER: - ret = te_expr(TE_CONSTANT, s->value); + ret = new_expr(TE_CONSTANT, 0); + ret->value = s->value; next_token(s); break; case TE_FUNCTION0: - ret = te_expr(s->type, s->function, NULL); + ret = new_expr(s->type, 0); + ret->function = s->function; next_token(s); if (s->type == TOK_OPEN) { next_token(s); @@ -267,24 +294,27 @@ static te_expr base(state *s) { case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - ret = te_expr(s->type, s->function, NULL); + arity = get_arity(s->type); + + ret = new_expr(s->type, 0); + ret->function = s->function; next_token(s); if (s->type == TOK_OPEN) { int i; - for(i = 0; i < ret.arity; i++) { + for(i = 0; i < arity; i++) { next_token(s); - ret.parameters.push_back(expr(s)); + ret->parameters[i] = expr(s); if(s->type != TOK_SEP) { break; } } - if(s->type == TOK_CLOSE && i == ret.arity - 1) { + if(s->type == TOK_CLOSE && i == arity - 1) { next_token(s); } else if (s->type != TOK_ERROR || s->error == TE_ERROR_UNKNOWN) { s->type = TOK_ERROR; - s->error = i < ret.arity ? TE_ERROR_TOO_FEW_ARGS + s->error = i < arity ? TE_ERROR_TOO_FEW_ARGS : TE_ERROR_TOO_MANY_ARGS; } } else if (s->type != TOK_ERROR @@ -313,14 +343,16 @@ 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 = te_expr(TE_CONSTANT, NAN); + ret = new_expr(0, 0); s->type = TOK_ERROR; s->error = TE_ERROR_TOO_FEW_ARGS; + ret->value = NAN; break; default: - ret = te_expr(TE_CONSTANT, NAN); + ret = new_expr(0, 0); s->type = TOK_ERROR; s->error = TE_ERROR_UNKNOWN; + ret->value = NAN; break; } @@ -328,7 +360,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)) { @@ -336,70 +368,72 @@ static te_expr power(state *s) { next_token(s); } + te_expr *ret; + if (sign == 1) { - te_expr ret = base(s); - return ret; + ret = base(s); } else { - te_expr params[1] = { base(s) }; - te_expr ret = te_expr(TE_FUNCTION1, (const void *) negate, params); - return ret; + ret = NEW_EXPR(TE_FUNCTION1, base(s)); + ret->function = (const void *) negate; } + + 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); - - te_expr param[2] = { ret, power(s) }; - ret = te_expr(TE_FUNCTION2, (const void *) t, param); + ret = NEW_EXPR(TE_FUNCTION2, ret, power(s)); + ret->function = (const void *) t; } 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); - te_expr params[2] = { ret, factor(s) }; - ret = te_expr(TE_FUNCTION2, (const void *) t, params); + ret = NEW_EXPR(TE_FUNCTION2, ret, factor(s)); + ret->function = (const void *) t; } 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); - te_expr params[2] = { ret, term(s) }; - ret = te_expr(TE_FUNCTION2, (const void *) t, params); + ret = NEW_EXPR(TE_FUNCTION2, ret, term(s)); + ret->function = (const void *) t; } 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) { - switch(n.type) { - case TE_CONSTANT: - return n.value; +double te_eval(const te_expr *n) { + if (!n) return NAN; + + switch(n->type) { + case TE_CONSTANT: return n->value; case TE_FUNCTION0: return TE_FUN(void)(); case TE_FUNCTION1: @@ -410,39 +444,43 @@ 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 < n.arity; ++i) { - optimize(n.parameters[i]); - if ((n.parameters[i]).type != TE_CONSTANT) { + for (int i = 0; i < arity; ++i) { + optimize(n->parameters[i]); + if ((n->parameters[i])->type != TE_CONSTANT) { known = false; } } if (known) { const double value = te_eval(n); - n.type = TE_CONSTANT; - n.value = value; + te_free_parameters(n); + 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) { @@ -457,16 +495,22 @@ 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; - ret = te_eval(n); + if (n) { + ret = te_eval(n); + te_free(n); + } else { + ret = NAN; + } return ret; }