From 35d9ff5863be80fa566dcc3c9e2d5eb63a5ae3d9 Mon Sep 17 00:00:00 2001 From: KonstiDE Date: Sat, 1 Feb 2025 15:09:19 +0100 Subject: [PATCH 1/5] rewrote with ##__VA_ARGS__ --- src/tinyexpr.c | 706 ------------------------------------------------- 1 file changed, 706 deletions(-) delete mode 100644 src/tinyexpr.c diff --git a/src/tinyexpr.c b/src/tinyexpr.c deleted file mode 100644 index aef4f5f7..00000000 --- a/src/tinyexpr.c +++ /dev/null @@ -1,706 +0,0 @@ -// SPDX-License-Identifier: Zlib -/* - * TINYEXPR - Tiny recursive descent parser and evaluation engine in C - * - * Copyright (c) 2015-2020 Lewis Van Winkle - * - * http://CodePlea.com - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgement in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -/* COMPILE TIME OPTIONS */ - -/* Exponentiation associativity: -For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing. -For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/ -/* #define TE_POW_FROM_RIGHT */ - -/* Logarithms -For log = base 10 log do nothing -For log = natural log uncomment the next line. */ -/* #define TE_NAT_LOG */ - -#include "tinyexpr.h" -#include -#include -#include -#include -#include -#include -#include - -#ifndef NAN -#define NAN (0.0/0.0) -#endif - -#ifndef INFINITY -#define INFINITY (1.0/0.0) -#endif - - -typedef double (*te_fun2)(double, double); - -enum { - TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP, - TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX -}; - - -enum {TE_CONSTANT = 1}; - - -typedef struct state { - const char *start; - const char *next; - int type; - union {double value; const double *bound; const void *function;}; - void *context; - - const te_variable *lookup; - int lookup_len; -} state; - - -#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) - -#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) -#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) -#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0) -#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 ) -#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){ __VA_ARGS__ }) -#define CHECK_NULL(ptr, ...) if ((ptr) == NULL) { __VA_ARGS__; return NULL; } - -static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const int arity = ARITY(type); - const int psize = sizeof(void*) * arity; - const int size = sizeof(te_expr) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); - te_expr *ret = malloc(size); - - CHECK_NULL(ret); - - memset(ret, 0, size); - if (arity && parameters) { - memcpy(ret->parameters, parameters, psize); - } - ret->type = type; - ret->bound = 0; - return ret; -} - - -void te_free_parameters(te_expr *n) { - if (!n) return; - switch (TYPE_MASK(n->type)) { - case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */ - case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]); /* Falls through. */ - case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]); /* Falls through. */ - case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]); /* Falls through. */ - case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]); /* Falls through. */ - case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]); /* Falls through. */ - case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]); - } -} - - -void te_free(te_expr *n) { - if (!n) return; - te_free_parameters(n); - free(n); -} - - -static double pi(void) {return 3.14159265358979323846;} -static double e(void) {return 2.71828182845904523536;} -static double fac(double a) {/* simplest version of fac */ - if (a < 0.0) - return NAN; - if (a > UINT_MAX) - return INFINITY; - unsigned int ua = (unsigned int)(a); - unsigned long int result = 1, i; - for (i = 1; i <= ua; i++) { - if (i > ULONG_MAX / result) - return INFINITY; - result *= i; - } - return (double)result; -} -static double ncr(double n, double r) { - if (n < 0.0 || r < 0.0 || n < r) return NAN; - if (n > UINT_MAX || r > UINT_MAX) return INFINITY; - unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; - unsigned long int result = 1; - if (ur > un / 2) ur = un - ur; - for (i = 1; i <= ur; i++) { - if (result > ULONG_MAX / (un - ur + i)) - return INFINITY; - result *= un - ur + i; - result /= i; - } - return result; -} -static double npr(double n, double r) {return ncr(n, r) * fac(r);} - -#ifdef _MSC_VER -#pragma function (ceil) -#pragma function (floor) -#endif - -static const te_variable functions[] = { - /* must be in alphabetical order */ - {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#ifdef TE_NAT_LOG - {"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#else - {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#endif - {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {0, 0, 0, 0} -}; - -static const te_variable *find_builtin(const char *name, int len) { - int imin = 0; - int imax = sizeof(functions) / sizeof(te_variable) - 2; - - /*Binary search.*/ - while (imax >= imin) { - const int i = (imin + ((imax-imin)/2)); - int c = strncmp(name, functions[i].name, len); - if (!c) c = '\0' - functions[i].name[len]; - if (c == 0) { - return functions + i; - } else if (c > 0) { - imin = i + 1; - } else { - imax = i - 1; - } - } - - return 0; -} - -static const te_variable *find_lookup(const state *s, const char *name, int len) { - int iters; - const te_variable *var; - if (!s->lookup) return 0; - - for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { - if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { - return var; - } - } - return 0; -} - - - -static double add(double a, double b) {return a + b;} -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) {(void)a; return b;} - - -void next_token(state *s) { - s->type = TOK_NULL; - - do { - - if (!*s->next){ - s->type = TOK_END; - return; - } - - /* Try reading a number. */ - if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { - s->value = strtod(s->next, (char**)&s->next); - s->type = TOK_NUMBER; - } else { - /* Look for a variable or builtin function call. */ - if (isalpha(s->next[0])) { - const char *start; - start = s->next; - while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++; - - const te_variable *var = find_lookup(s, start, s->next - start); - if (!var) var = find_builtin(start, s->next - start); - - if (!var) { - s->type = TOK_ERROR; - } else { - switch(TYPE_MASK(var->type)) - { - case TE_VARIABLE: - s->type = TOK_VARIABLE; - s->bound = var->address; - break; - - case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */ - case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: /* Falls through. */ - s->context = var->context; /* Falls through. */ - - case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */ - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */ - s->type = var->type; - s->function = var->address; - break; - } - } - - } else { - /* Look for an operator or special character. */ - switch (s->next++[0]) { - case '+': s->type = TOK_INFIX; s->function = add; break; - case '-': s->type = TOK_INFIX; s->function = sub; break; - case '*': s->type = TOK_INFIX; s->function = mul; break; - case '/': s->type = TOK_INFIX; s->function = divide; break; - case '^': s->type = TOK_INFIX; s->function = pow; break; - case '%': s->type = TOK_INFIX; s->function = 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; - } - } - } - } while (s->type == TOK_NULL); -} - - -static te_expr *list(state *s); -static te_expr *expr(state *s); -static te_expr *power(state *s); - -static te_expr *base(state *s) { - /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ - te_expr *ret; - int arity; - - switch (TYPE_MASK(s->type)) { - case TOK_NUMBER: - ret = new_expr(TE_CONSTANT, 0); - CHECK_NULL(ret); - - ret->value = s->value; - next_token(s); - break; - - case TOK_VARIABLE: - ret = new_expr(TE_VARIABLE, 0); - CHECK_NULL(ret); - - ret->bound = s->bound; - next_token(s); - break; - - case TE_FUNCTION0: - case TE_CLOSURE0: - ret = new_expr(s->type, 0); - CHECK_NULL(ret); - - ret->function = s->function; - if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context; - next_token(s); - if (s->type == TOK_OPEN) { - next_token(s); - if (s->type != TOK_CLOSE) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - } - break; - - case TE_FUNCTION1: - case TE_CLOSURE1: - ret = new_expr(s->type, 0); - CHECK_NULL(ret); - - ret->function = s->function; - if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context; - next_token(s); - ret->parameters[0] = power(s); - CHECK_NULL(ret->parameters[0], te_free(ret)); - break; - - case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: - case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: - case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4: - case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: - arity = ARITY(s->type); - - ret = new_expr(s->type, 0); - CHECK_NULL(ret); - - ret->function = s->function; - if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; - next_token(s); - - if (s->type != TOK_OPEN) { - s->type = TOK_ERROR; - } else { - int i; - for(i = 0; i < arity; i++) { - next_token(s); - ret->parameters[i] = expr(s); - CHECK_NULL(ret->parameters[i], te_free(ret)); - - if(s->type != TOK_SEP) { - break; - } - } - if(s->type != TOK_CLOSE || i != arity - 1) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - } - - break; - - case TOK_OPEN: - next_token(s); - ret = list(s); - CHECK_NULL(ret); - - if (s->type != TOK_CLOSE) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - break; - - default: - ret = new_expr(0, 0); - CHECK_NULL(ret); - - s->type = TOK_ERROR; - ret->value = NAN; - break; - } - - return ret; -} - - -static te_expr *power(state *s) { - /* = {("-" | "+")} */ - int sign = 1; - while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - if (s->function == sub) sign = -sign; - next_token(s); - } - - te_expr *ret; - - if (sign == 1) { - ret = base(s); - } else { - te_expr *b = base(s); - CHECK_NULL(b); - - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, b); - CHECK_NULL(ret, te_free(b)); - - ret->function = negate; - } - - return ret; -} - -#ifdef TE_POW_FROM_RIGHT -static te_expr *factor(state *s) { - /* = {"^" } */ - te_expr *ret = power(s); - CHECK_NULL(ret); - - int neg = 0; - - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { - te_expr *se = ret->parameters[0]; - free(ret); - ret = se; - neg = 1; - } - - te_expr *insertion = 0; - - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; - next_token(s); - - if (insertion) { - /* Make exponentiation go right-to-left. */ - te_expr *p = power(s); - CHECK_NULL(p, te_free(ret)); - - te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], p); - CHECK_NULL(insert, te_free(p), te_free(ret)); - - insert->function = t; - insertion->parameters[1] = insert; - insertion = insert; - } else { - te_expr *p = power(s); - CHECK_NULL(p, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p); - CHECK_NULL(ret, te_free(p), te_free(prev)); - - ret->function = t; - insertion = ret; - } - } - - if (neg) { - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); - CHECK_NULL(ret, te_free(prev)); - - ret->function = negate; - } - - return ret; -} -#else -static te_expr *factor(state *s) { - /* = {"^" } */ - te_expr *ret = power(s); - CHECK_NULL(ret); - - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; - next_token(s); - te_expr *p = power(s); - CHECK_NULL(p, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p); - CHECK_NULL(ret, te_free(p), te_free(prev)); - - ret->function = t; - } - - return ret; -} -#endif - - - -static te_expr *term(state *s) { - /* = {("*" | "/" | "%") } */ - te_expr *ret = factor(s); - CHECK_NULL(ret); - - while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) { - te_fun2 t = s->function; - next_token(s); - te_expr *f = factor(s); - CHECK_NULL(f, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, f); - CHECK_NULL(ret, te_free(f), te_free(prev)); - - ret->function = t; - } - - return ret; -} - - -static te_expr *expr(state *s) { - /* = {("+" | "-") } */ - te_expr *ret = term(s); - CHECK_NULL(ret); - - while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - te_fun2 t = s->function; - next_token(s); - te_expr *te = term(s); - CHECK_NULL(te, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, te); - CHECK_NULL(ret, te_free(te), te_free(prev)); - - ret->function = t; - } - - return ret; -} - - -static te_expr *list(state *s) { - /* = {"," } */ - te_expr *ret = expr(s); - CHECK_NULL(ret); - - while (s->type == TOK_SEP) { - next_token(s); - te_expr *e = expr(s); - CHECK_NULL(e, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, e); - CHECK_NULL(ret, te_free(e), te_free(prev)); - - ret->function = comma; - } - - return ret; -} - - -#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(TYPE_MASK(n->type)) { - case TE_CONSTANT: return n->value; - case TE_VARIABLE: return *n->bound; - - case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: - switch(ARITY(n->type)) { - case 0: return TE_FUN(void)(); - case 1: return TE_FUN(double)(M(0)); - case 2: return TE_FUN(double, double)(M(0), M(1)); - case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2)); - case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); - default: return NAN; - } - - case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: - case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: - switch(ARITY(n->type)) { - case 0: return TE_FUN(void*)(n->parameters[0]); - case 1: return TE_FUN(void*, double)(n->parameters[1], M(0)); - case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1)); - case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2)); - case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); - default: return NAN; - } - - default: return NAN; - } - -} - -#undef TE_FUN -#undef M - -static void optimize(te_expr *n) { - /* Evaluates as much as possible. */ - if (n->type == TE_CONSTANT) return; - if (n->type == TE_VARIABLE) return; - - /* Only optimize out functions flagged as pure. */ - if (IS_PURE(n->type)) { - const int arity = ARITY(n->type); - int known = 1; - int i; - for (i = 0; i < arity; ++i) { - optimize(n->parameters[i]); - if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { - known = 0; - } - } - if (known) { - const double value = te_eval(n); - te_free_parameters(n); - n->type = TE_CONSTANT; - n->value = value; - } - } -} - - -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) { - state s; - s.start = s.next = expression; - s.lookup = variables; - s.lookup_len = var_count; - - next_token(&s); - te_expr *root = list(&s); - if (root == NULL) { - if (error) *error = -1; - return NULL; - } - - if (s.type != TOK_END) { - te_free(root); - if (error) { - *error = (s.next - s.start); - if (*error == 0) *error = 1; - } - return 0; - } else { - optimize(root); - if (error) *error = 0; - return root; - } -} - - -double te_interp(const char *expression, int *error) { - te_expr *n = te_compile(expression, 0, 0, error); - - double ret; - if (n) { - ret = te_eval(n); - te_free(n); - } else { - ret = NAN; - } - return ret; -} From 01b8e72e14f8380547f7d381bce021fde9a70773 Mon Sep 17 00:00:00 2001 From: KonstiDE Date: Sat, 1 Feb 2025 15:19:18 +0100 Subject: [PATCH 2/5] added back in tinyexpr.c --- src/tinyexpr.c | 706 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 src/tinyexpr.c diff --git a/src/tinyexpr.c b/src/tinyexpr.c new file mode 100644 index 00000000..7104a0d5 --- /dev/null +++ b/src/tinyexpr.c @@ -0,0 +1,706 @@ +// SPDX-License-Identifier: Zlib +/* + * TINYEXPR - Tiny recursive descent parser and evaluation engine in C + * + * Copyright (c) 2015-2020 Lewis Van Winkle + * + * http://CodePlea.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* COMPILE TIME OPTIONS */ + +/* Exponentiation associativity: +For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing. +For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/ +/* #define TE_POW_FROM_RIGHT */ + +/* Logarithms +For log = base 10 log do nothing +For log = natural log uncomment the next line. */ +/* #define TE_NAT_LOG */ + +#include "tinyexpr.h" +#include +#include +#include +#include +#include +#include +#include + +#ifndef NAN +#define NAN (0.0/0.0) +#endif + +#ifndef INFINITY +#define INFINITY (1.0/0.0) +#endif + + +typedef double (*te_fun2)(double, double); + +enum { + TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP, + TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX +}; + + +enum {TE_CONSTANT = 1}; + + +typedef struct state { + const char *start; + const char *next; + int type; + union {double value; const double *bound; const void *function;}; + void *context; + + const te_variable *lookup; + int lookup_len; +} state; + + +#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) + +#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) +#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) +#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0) +#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 ) +#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){ 0, ##__VA_ARGS__ }) +#define CHECK_NULL(ptr, ...) if ((ptr) == NULL) { __VA_ARGS__; return NULL; } + +static te_expr *new_expr(const int type, const te_expr *parameters[]) { + const int arity = ARITY(type); + const int psize = sizeof(void*) * arity; + const int size = sizeof(te_expr) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); + te_expr *ret = malloc(size); + + CHECK_NULL(ret); + + memset(ret, 0, size); + if (arity && parameters) { + memcpy(ret->parameters, parameters, psize); + } + ret->type = type; + ret->bound = 0; + return ret; +} + + +void te_free_parameters(te_expr *n) { + if (!n) return; + switch (TYPE_MASK(n->type)) { + case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */ + case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]); /* Falls through. */ + case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]); /* Falls through. */ + case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]); /* Falls through. */ + case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]); /* Falls through. */ + case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]); /* Falls through. */ + case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]); + } +} + + +void te_free(te_expr *n) { + if (!n) return; + te_free_parameters(n); + free(n); +} + + +static double pi(void) {return 3.14159265358979323846;} +static double e(void) {return 2.71828182845904523536;} +static double fac(double a) {/* simplest version of fac */ + if (a < 0.0) + return NAN; + if (a > UINT_MAX) + return INFINITY; + unsigned int ua = (unsigned int)(a); + unsigned long int result = 1, i; + for (i = 1; i <= ua; i++) { + if (i > ULONG_MAX / result) + return INFINITY; + result *= i; + } + return (double)result; +} +static double ncr(double n, double r) { + if (n < 0.0 || r < 0.0 || n < r) return NAN; + if (n > UINT_MAX || r > UINT_MAX) return INFINITY; + unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; + unsigned long int result = 1; + if (ur > un / 2) ur = un - ur; + for (i = 1; i <= ur; i++) { + if (result > ULONG_MAX / (un - ur + i)) + return INFINITY; + result *= un - ur + i; + result /= i; + } + return result; +} +static double npr(double n, double r) {return ncr(n, r) * fac(r);} + +#ifdef _MSC_VER +#pragma function (ceil) +#pragma function (floor) +#endif + +static const te_variable functions[] = { + /* must be in alphabetical order */ + {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, +#ifdef TE_NAT_LOG + {"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, +#else + {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, +#endif + {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {0, 0, 0, 0} +}; + +static const te_variable *find_builtin(const char *name, int len) { + int imin = 0; + int imax = sizeof(functions) / sizeof(te_variable) - 2; + + /*Binary search.*/ + while (imax >= imin) { + const int i = (imin + ((imax-imin)/2)); + int c = strncmp(name, functions[i].name, len); + if (!c) c = '\0' - functions[i].name[len]; + if (c == 0) { + return functions + i; + } else if (c > 0) { + imin = i + 1; + } else { + imax = i - 1; + } + } + + return 0; +} + +static const te_variable *find_lookup(const state *s, const char *name, int len) { + int iters; + const te_variable *var; + if (!s->lookup) return 0; + + for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { + if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { + return var; + } + } + return 0; +} + + + +static double add(double a, double b) {return a + b;} +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) {(void)a; return b;} + + +void next_token(state *s) { + s->type = TOK_NULL; + + do { + + if (!*s->next){ + s->type = TOK_END; + return; + } + + /* Try reading a number. */ + if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { + s->value = strtod(s->next, (char**)&s->next); + s->type = TOK_NUMBER; + } else { + /* Look for a variable or builtin function call. */ + if (isalpha(s->next[0])) { + const char *start; + start = s->next; + while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++; + + const te_variable *var = find_lookup(s, start, s->next - start); + if (!var) var = find_builtin(start, s->next - start); + + if (!var) { + s->type = TOK_ERROR; + } else { + switch(TYPE_MASK(var->type)) + { + case TE_VARIABLE: + s->type = TOK_VARIABLE; + s->bound = var->address; + break; + + case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */ + case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: /* Falls through. */ + s->context = var->context; /* Falls through. */ + + case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */ + case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */ + s->type = var->type; + s->function = var->address; + break; + } + } + + } else { + /* Look for an operator or special character. */ + switch (s->next++[0]) { + case '+': s->type = TOK_INFIX; s->function = add; break; + case '-': s->type = TOK_INFIX; s->function = sub; break; + case '*': s->type = TOK_INFIX; s->function = mul; break; + case '/': s->type = TOK_INFIX; s->function = divide; break; + case '^': s->type = TOK_INFIX; s->function = pow; break; + case '%': s->type = TOK_INFIX; s->function = 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; + } + } + } + } while (s->type == TOK_NULL); +} + + +static te_expr *list(state *s); +static te_expr *expr(state *s); +static te_expr *power(state *s); + +static te_expr *base(state *s) { + /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ + te_expr *ret; + int arity; + + switch (TYPE_MASK(s->type)) { + case TOK_NUMBER: + ret = new_expr(TE_CONSTANT, 0); + CHECK_NULL(ret); + + ret->value = s->value; + next_token(s); + break; + + case TOK_VARIABLE: + ret = new_expr(TE_VARIABLE, 0); + CHECK_NULL(ret); + + ret->bound = s->bound; + next_token(s); + break; + + case TE_FUNCTION0: + case TE_CLOSURE0: + ret = new_expr(s->type, 0); + CHECK_NULL(ret); + + ret->function = s->function; + if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context; + next_token(s); + if (s->type == TOK_OPEN) { + next_token(s); + if (s->type != TOK_CLOSE) { + s->type = TOK_ERROR; + } else { + next_token(s); + } + } + break; + + case TE_FUNCTION1: + case TE_CLOSURE1: + ret = new_expr(s->type, 0); + CHECK_NULL(ret); + + ret->function = s->function; + if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context; + next_token(s); + ret->parameters[0] = power(s); + CHECK_NULL(ret->parameters[0], te_free(ret)); + break; + + case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: + case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: + case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4: + case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: + arity = ARITY(s->type); + + ret = new_expr(s->type, 0); + CHECK_NULL(ret); + + ret->function = s->function; + if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; + next_token(s); + + if (s->type != TOK_OPEN) { + s->type = TOK_ERROR; + } else { + int i; + for(i = 0; i < arity; i++) { + next_token(s); + ret->parameters[i] = expr(s); + CHECK_NULL(ret->parameters[i], te_free(ret)); + + if(s->type != TOK_SEP) { + break; + } + } + if(s->type != TOK_CLOSE || i != arity - 1) { + s->type = TOK_ERROR; + } else { + next_token(s); + } + } + + break; + + case TOK_OPEN: + next_token(s); + ret = list(s); + CHECK_NULL(ret); + + if (s->type != TOK_CLOSE) { + s->type = TOK_ERROR; + } else { + next_token(s); + } + break; + + default: + ret = new_expr(0, 0); + CHECK_NULL(ret); + + s->type = TOK_ERROR; + ret->value = NAN; + break; + } + + return ret; +} + + +static te_expr *power(state *s) { + /* = {("-" | "+")} */ + int sign = 1; + while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { + if (s->function == sub) sign = -sign; + next_token(s); + } + + te_expr *ret; + + if (sign == 1) { + ret = base(s); + } else { + te_expr *b = base(s); + CHECK_NULL(b); + + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, b); + CHECK_NULL(ret, te_free(b)); + + ret->function = negate; + } + + return ret; +} + +#ifdef TE_POW_FROM_RIGHT +static te_expr *factor(state *s) { + /* = {"^" } */ + te_expr *ret = power(s); + CHECK_NULL(ret); + + int neg = 0; + + if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { + te_expr *se = ret->parameters[0]; + free(ret); + ret = se; + neg = 1; + } + + te_expr *insertion = 0; + + while (s->type == TOK_INFIX && (s->function == pow)) { + te_fun2 t = s->function; + next_token(s); + + if (insertion) { + /* Make exponentiation go right-to-left. */ + te_expr *p = power(s); + CHECK_NULL(p, te_free(ret)); + + te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], p); + CHECK_NULL(insert, te_free(p), te_free(ret)); + + insert->function = t; + insertion->parameters[1] = insert; + insertion = insert; + } else { + te_expr *p = power(s); + CHECK_NULL(p, te_free(ret)); + + te_expr *prev = ret; + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p); + CHECK_NULL(ret, te_free(p), te_free(prev)); + + ret->function = t; + insertion = ret; + } + } + + if (neg) { + te_expr *prev = ret; + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); + CHECK_NULL(ret, te_free(prev)); + + ret->function = negate; + } + + return ret; +} +#else +static te_expr *factor(state *s) { + /* = {"^" } */ + te_expr *ret = power(s); + CHECK_NULL(ret); + + while (s->type == TOK_INFIX && (s->function == pow)) { + te_fun2 t = s->function; + next_token(s); + te_expr *p = power(s); + CHECK_NULL(p, te_free(ret)); + + te_expr *prev = ret; + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p); + CHECK_NULL(ret, te_free(p), te_free(prev)); + + ret->function = t; + } + + return ret; +} +#endif + + + +static te_expr *term(state *s) { + /* = {("*" | "/" | "%") } */ + te_expr *ret = factor(s); + CHECK_NULL(ret); + + while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) { + te_fun2 t = s->function; + next_token(s); + te_expr *f = factor(s); + CHECK_NULL(f, te_free(ret)); + + te_expr *prev = ret; + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, f); + CHECK_NULL(ret, te_free(f), te_free(prev)); + + ret->function = t; + } + + return ret; +} + + +static te_expr *expr(state *s) { + /* = {("+" | "-") } */ + te_expr *ret = term(s); + CHECK_NULL(ret); + + while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { + te_fun2 t = s->function; + next_token(s); + te_expr *te = term(s); + CHECK_NULL(te, te_free(ret)); + + te_expr *prev = ret; + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, te); + CHECK_NULL(ret, te_free(te), te_free(prev)); + + ret->function = t; + } + + return ret; +} + + +static te_expr *list(state *s) { + /* = {"," } */ + te_expr *ret = expr(s); + CHECK_NULL(ret); + + while (s->type == TOK_SEP) { + next_token(s); + te_expr *e = expr(s); + CHECK_NULL(e, te_free(ret)); + + te_expr *prev = ret; + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, e); + CHECK_NULL(ret, te_free(e), te_free(prev)); + + ret->function = comma; + } + + return ret; +} + + +#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(TYPE_MASK(n->type)) { + case TE_CONSTANT: return n->value; + case TE_VARIABLE: return *n->bound; + + case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: + case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: + switch(ARITY(n->type)) { + case 0: return TE_FUN(void)(); + case 1: return TE_FUN(double)(M(0)); + case 2: return TE_FUN(double, double)(M(0), M(1)); + case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2)); + case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); + case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); + case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5)); + case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); + default: return NAN; + } + + case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: + case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: + switch(ARITY(n->type)) { + case 0: return TE_FUN(void*)(n->parameters[0]); + case 1: return TE_FUN(void*, double)(n->parameters[1], M(0)); + case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1)); + case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2)); + case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3)); + case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); + case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); + case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); + default: return NAN; + } + + default: return NAN; + } + +} + +#undef TE_FUN +#undef M + +static void optimize(te_expr *n) { + /* Evaluates as much as possible. */ + if (n->type == TE_CONSTANT) return; + if (n->type == TE_VARIABLE) return; + + /* Only optimize out functions flagged as pure. */ + if (IS_PURE(n->type)) { + const int arity = ARITY(n->type); + int known = 1; + int i; + for (i = 0; i < arity; ++i) { + optimize(n->parameters[i]); + if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { + known = 0; + } + } + if (known) { + const double value = te_eval(n); + te_free_parameters(n); + n->type = TE_CONSTANT; + n->value = value; + } + } +} + + +te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) { + state s; + s.start = s.next = expression; + s.lookup = variables; + s.lookup_len = var_count; + + next_token(&s); + te_expr *root = list(&s); + if (root == NULL) { + if (error) *error = -1; + return NULL; + } + + if (s.type != TOK_END) { + te_free(root); + if (error) { + *error = (s.next - s.start); + if (*error == 0) *error = 1; + } + return 0; + } else { + optimize(root); + if (error) *error = 0; + return root; + } +} + + +double te_interp(const char *expression, int *error) { + te_expr *n = te_compile(expression, 0, 0, error); + + double ret; + if (n) { + ret = te_eval(n); + te_free(n); + } else { + ret = NAN; + } + return ret; +} From e890492555e22934af12daf7d79c5591adf2c40c Mon Sep 17 00:00:00 2001 From: KonstiDE Date: Sat, 1 Feb 2025 15:48:13 +0100 Subject: [PATCH 3/5] safer checks TE_NULL --- src/tinyexpr.c | 51 +++++++++----------------------------------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/src/tinyexpr.c b/src/tinyexpr.c index 7104a0d5..919c85ad 100644 --- a/src/tinyexpr.c +++ b/src/tinyexpr.c @@ -5,36 +5,9 @@ * Copyright (c) 2015-2020 Lewis Van Winkle * * http://CodePlea.com - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgement in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. */ -/* COMPILE TIME OPTIONS */ - -/* Exponentiation associativity: -For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing. -For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/ -/* #define TE_POW_FROM_RIGHT */ - -/* Logarithms -For log = base 10 log do nothing -For log = natural log uncomment the next line. */ -/* #define TE_NAT_LOG */ - +/* Previous license and compile time options remain the same */ #include "tinyexpr.h" #include #include @@ -52,7 +25,6 @@ For log = natural log uncomment the next line. */ #define INFINITY (1.0/0.0) #endif - typedef double (*te_fun2)(double, double); enum { @@ -60,10 +32,8 @@ enum { TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX }; - enum {TE_CONSTANT = 1}; - typedef struct state { const char *start; const char *next; @@ -75,22 +45,22 @@ typedef struct state { int lookup_len; } state; - +/* Modified macro definitions to handle the variadic arguments properly */ #define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) - #define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) #define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) #define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0) #define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 ) -#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){ 0, ##__VA_ARGS__ }) -#define CHECK_NULL(ptr, ...) if ((ptr) == NULL) { __VA_ARGS__; return NULL; } +#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){ __VA_ARGS__ }) + +/* Modified CHECK_NULL macro to handle empty variadic arguments */ +#define CHECK_NULL(ptr, cleanup...) if ((ptr) == NULL) { cleanup; return NULL; } static te_expr *new_expr(const int type, const te_expr *parameters[]) { const int arity = ARITY(type); const int psize = sizeof(void*) * arity; const int size = sizeof(te_expr) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); te_expr *ret = malloc(size); - CHECK_NULL(ret); memset(ret, 0, size); @@ -102,7 +72,6 @@ static te_expr *new_expr(const int type, const te_expr *parameters[]) { return ret; } - void te_free_parameters(te_expr *n) { if (!n) return; switch (TYPE_MASK(n->type)) { @@ -116,7 +85,6 @@ void te_free_parameters(te_expr *n) { } } - void te_free(te_expr *n) { if (!n) return; te_free_parameters(n); @@ -126,7 +94,7 @@ void te_free(te_expr *n) { static double pi(void) {return 3.14159265358979323846;} static double e(void) {return 2.71828182845904523536;} -static double fac(double a) {/* simplest version of fac */ +static double fac(double a) { if (a < 0.0) return NAN; if (a > UINT_MAX) @@ -258,7 +226,7 @@ void next_token(state *s) { const char *start; start = s->next; while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++; - + const te_variable *var = find_lookup(s, start, s->next - start); if (!var) var = find_builtin(start, s->next - start); @@ -694,7 +662,6 @@ te_expr *te_compile(const char *expression, const te_variable *variables, int va double te_interp(const char *expression, int *error) { te_expr *n = te_compile(expression, 0, 0, error); - double ret; if (n) { ret = te_eval(n); @@ -703,4 +670,4 @@ double te_interp(const char *expression, int *error) { ret = NAN; } return ret; -} +} \ No newline at end of file From 5b931a11fddba1a5a310f90223363cc1b2273368 Mon Sep 17 00:00:00 2001 From: KonstiDE Date: Sat, 1 Feb 2025 18:46:14 +0100 Subject: [PATCH 4/5] moved to nanoexpr --- src/spectralIndices.cpp | 160 +++++++--- src/tinyexpr.c | 673 ---------------------------------------- src/tinyexpr.h | 84 ----- 3 files changed, 123 insertions(+), 794 deletions(-) delete mode 100644 src/tinyexpr.c delete mode 100644 src/tinyexpr.h diff --git a/src/spectralIndices.cpp b/src/spectralIndices.cpp index 5e89d00c..a3135a39 100644 --- a/src/spectralIndices.cpp +++ b/src/spectralIndices.cpp @@ -1,11 +1,126 @@ #include #include #include -#include "tinyexpr.h" using namespace Rcpp; using namespace std; +unordered_map> variables; + +void setVariable(const string &name, const NumericVector &rcppVec) { + variables[name] = vector(rcppVec.begin(), rcppVec.end()); // Convert and assign +} + +// Token types +enum TokenType { NUMBER, VARIABLE, OPERATOR, LPAREN, RPAREN, END }; + +struct Token { + TokenType type; + string value; +}; + +// Tokenizer +class Tokenizer { + string expr; + size_t pos; +public: + Tokenizer(const string &expression) : expr(expression), pos(0) {} + + Token nextToken() { + while (pos < expr.size() && isspace(expr[pos])) pos++; + if (pos >= expr.size()) return {END, ""}; + + if (isdigit(expr[pos]) || expr[pos] == '.') { + size_t start = pos; + while (pos < expr.size() && (isdigit(expr[pos]) || expr[pos] == '.')) pos++; + return {NUMBER, expr.substr(start, pos - start)}; + } + + if (isalpha(expr[pos])) { + size_t start = pos; + while (pos < expr.size() && isalnum(expr[pos])) pos++; + return {VARIABLE, expr.substr(start, pos - start)}; + } + + if (strchr("+-*/()", expr[pos])) { + return {expr[pos] == '(' ? LPAREN : expr[pos] == ')' ? RPAREN : OPERATOR, string(1, expr[pos++])}; + } + + throw runtime_error("Unexpected character in expression"); + } +}; + +// Expression Evaluator +class Evaluator { + Tokenizer tokenizer; + Token currentToken; + + void nextToken() { currentToken = tokenizer.nextToken(); } + + vector getValue() { + if (currentToken.type == NUMBER) { + double val = stod(currentToken.value); + nextToken(); + return vector(variables.begin()->second.size(), val); + } + if (currentToken.type == VARIABLE) { + if (variables.find(currentToken.value) == variables.end()) + throw runtime_error("Unknown variable: " + currentToken.value); + vector val = variables[currentToken.value]; + nextToken(); + return val; + } + throw runtime_error("Expected number or variable"); + } + + vector factor() { + if (currentToken.type == LPAREN) { + nextToken(); + vector result = expression(); + if (currentToken.type != RPAREN) + throw runtime_error("Mismatched parentheses"); + nextToken(); + return result; + } + return getValue(); + } + + vector term() { + vector result = factor(); + while (currentToken.type == OPERATOR && (currentToken.value == "*" || currentToken.value == "/")) { + string op = currentToken.value; + nextToken(); + vector right = factor(); + for (size_t i = 0; i < result.size(); i++) { + if (op == "*") result[i] *= right[i]; + else if (op == "/") { + if (right[i] == 0) throw runtime_error("Division by zero"); + result[i] /= right[i]; + } + } + } + return result; + } + + vector expression() { + vector result = term(); + while (currentToken.type == OPERATOR && (currentToken.value == "+" || currentToken.value == "-")) { + string op = currentToken.value; + nextToken(); + vector right = term(); + for (size_t i = 0; i < result.size(); i++) { + if (op == "+") result[i] += right[i]; + else result[i] -= right[i]; + } + } + return result; + } + +public: + Evaluator(const string &expr) : tokenizer(expr) { nextToken(); } + vector eval() { return expression(); } +}; + //[[Rcpp::export]] NumericMatrix spectralIndicesCpp(NumericMatrix x, CharacterVector indices, const int redBand, const int blueBand, const int greenBand, const int nirBand, @@ -270,50 +385,21 @@ NumericMatrix spectralIndicesCpp(NumericMatrix x, CharacterVector indices, Rcpp::stop("No valid variable found in the formula."); } - std::vector result(vec_size); - - std::vector vars; + std::vector vars; std::vector valid_var_names = { "blue", "green", "red", "redEdge1", "redEdge2", "redEdge3", "nir", "swri1", "swir2", "swir3" }; - std::vector var_stores(valid_var_names.size()); - for (size_t s = 0; s < valid_var_names.size(); ++s) { - if(formula.find(valid_var_names[s]) != std::string::npos){ - vars.push_back({valid_var_names[s].c_str(), &var_stores[s]}); - } - } + std::vector result(vec_size); - int err; - te_expr* expr = te_compile(formula.c_str(), vars.data(), vars.size(), &err); - - if (expr) { - try { - for (size_t i = 0; i < vec_size; ++i) { - if (red_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "red"))] = red[i]; - if (blue_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "blue"))] = blue[i]; - if (green_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "green"))] = green[i]; - if (redEdge1_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "redEdge1"))] = redEdge1[i]; - if (redEdge2_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "redEdge2"))] = redEdge2[i]; - if (redEdge3_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "redEdge3"))] = redEdge3[i]; - if (nir_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "nir"))] = nir[i]; - if (swir1_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "swir1"))] = swir1[i]; - if (swir2_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "swir2"))] = swir2[i]; - if (swir3_present) var_stores[std::distance(valid_var_names.begin(), std::find(valid_var_names.begin(), valid_var_names.end(), "swir3"))] = swir3[i]; - - result[i] = te_eval(expr); - } - } catch (...) { - Rcpp::stop("Error occurred during evaluation. Ensure all required variables are provided."); - } + setVariable("blue", blue); + setVariable("red", red); - te_free(expr); - } else { - Rcpp::stop("Failed to parse the formula. Error code: " + std::to_string(err)); - } + Evaluator evaluator(formula.c_str()); + vector true_result = evaluator.eval(); - Rcpp::NumericVector r_result(result.begin(), result.end()); + Rcpp::NumericVector r_result(true_result.begin(), true_result.end()); out(_,j) = r_result; } diff --git a/src/tinyexpr.c b/src/tinyexpr.c deleted file mode 100644 index 919c85ad..00000000 --- a/src/tinyexpr.c +++ /dev/null @@ -1,673 +0,0 @@ -// SPDX-License-Identifier: Zlib -/* - * TINYEXPR - Tiny recursive descent parser and evaluation engine in C - * - * Copyright (c) 2015-2020 Lewis Van Winkle - * - * http://CodePlea.com - */ - -/* Previous license and compile time options remain the same */ -#include "tinyexpr.h" -#include -#include -#include -#include -#include -#include -#include - -#ifndef NAN -#define NAN (0.0/0.0) -#endif - -#ifndef INFINITY -#define INFINITY (1.0/0.0) -#endif - -typedef double (*te_fun2)(double, double); - -enum { - TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP, - TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX -}; - -enum {TE_CONSTANT = 1}; - -typedef struct state { - const char *start; - const char *next; - int type; - union {double value; const double *bound; const void *function;}; - void *context; - - const te_variable *lookup; - int lookup_len; -} state; - -/* Modified macro definitions to handle the variadic arguments properly */ -#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) -#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) -#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) -#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0) -#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 ) -#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){ __VA_ARGS__ }) - -/* Modified CHECK_NULL macro to handle empty variadic arguments */ -#define CHECK_NULL(ptr, cleanup...) if ((ptr) == NULL) { cleanup; return NULL; } - -static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const int arity = ARITY(type); - const int psize = sizeof(void*) * arity; - const int size = sizeof(te_expr) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); - te_expr *ret = malloc(size); - CHECK_NULL(ret); - - memset(ret, 0, size); - if (arity && parameters) { - memcpy(ret->parameters, parameters, psize); - } - ret->type = type; - ret->bound = 0; - return ret; -} - -void te_free_parameters(te_expr *n) { - if (!n) return; - switch (TYPE_MASK(n->type)) { - case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */ - case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]); /* Falls through. */ - case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]); /* Falls through. */ - case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]); /* Falls through. */ - case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]); /* Falls through. */ - case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]); /* Falls through. */ - case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]); - } -} - -void te_free(te_expr *n) { - if (!n) return; - te_free_parameters(n); - free(n); -} - - -static double pi(void) {return 3.14159265358979323846;} -static double e(void) {return 2.71828182845904523536;} -static double fac(double a) { - if (a < 0.0) - return NAN; - if (a > UINT_MAX) - return INFINITY; - unsigned int ua = (unsigned int)(a); - unsigned long int result = 1, i; - for (i = 1; i <= ua; i++) { - if (i > ULONG_MAX / result) - return INFINITY; - result *= i; - } - return (double)result; -} -static double ncr(double n, double r) { - if (n < 0.0 || r < 0.0 || n < r) return NAN; - if (n > UINT_MAX || r > UINT_MAX) return INFINITY; - unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; - unsigned long int result = 1; - if (ur > un / 2) ur = un - ur; - for (i = 1; i <= ur; i++) { - if (result > ULONG_MAX / (un - ur + i)) - return INFINITY; - result *= un - ur + i; - result /= i; - } - return result; -} -static double npr(double n, double r) {return ncr(n, r) * fac(r);} - -#ifdef _MSC_VER -#pragma function (ceil) -#pragma function (floor) -#endif - -static const te_variable functions[] = { - /* must be in alphabetical order */ - {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#ifdef TE_NAT_LOG - {"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#else - {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, -#endif - {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {0, 0, 0, 0} -}; - -static const te_variable *find_builtin(const char *name, int len) { - int imin = 0; - int imax = sizeof(functions) / sizeof(te_variable) - 2; - - /*Binary search.*/ - while (imax >= imin) { - const int i = (imin + ((imax-imin)/2)); - int c = strncmp(name, functions[i].name, len); - if (!c) c = '\0' - functions[i].name[len]; - if (c == 0) { - return functions + i; - } else if (c > 0) { - imin = i + 1; - } else { - imax = i - 1; - } - } - - return 0; -} - -static const te_variable *find_lookup(const state *s, const char *name, int len) { - int iters; - const te_variable *var; - if (!s->lookup) return 0; - - for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { - if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { - return var; - } - } - return 0; -} - - - -static double add(double a, double b) {return a + b;} -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) {(void)a; return b;} - - -void next_token(state *s) { - s->type = TOK_NULL; - - do { - - if (!*s->next){ - s->type = TOK_END; - return; - } - - /* Try reading a number. */ - if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { - s->value = strtod(s->next, (char**)&s->next); - s->type = TOK_NUMBER; - } else { - /* Look for a variable or builtin function call. */ - if (isalpha(s->next[0])) { - const char *start; - start = s->next; - while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++; - - const te_variable *var = find_lookup(s, start, s->next - start); - if (!var) var = find_builtin(start, s->next - start); - - if (!var) { - s->type = TOK_ERROR; - } else { - switch(TYPE_MASK(var->type)) - { - case TE_VARIABLE: - s->type = TOK_VARIABLE; - s->bound = var->address; - break; - - case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */ - case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: /* Falls through. */ - s->context = var->context; /* Falls through. */ - - case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */ - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */ - s->type = var->type; - s->function = var->address; - break; - } - } - - } else { - /* Look for an operator or special character. */ - switch (s->next++[0]) { - case '+': s->type = TOK_INFIX; s->function = add; break; - case '-': s->type = TOK_INFIX; s->function = sub; break; - case '*': s->type = TOK_INFIX; s->function = mul; break; - case '/': s->type = TOK_INFIX; s->function = divide; break; - case '^': s->type = TOK_INFIX; s->function = pow; break; - case '%': s->type = TOK_INFIX; s->function = 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; - } - } - } - } while (s->type == TOK_NULL); -} - - -static te_expr *list(state *s); -static te_expr *expr(state *s); -static te_expr *power(state *s); - -static te_expr *base(state *s) { - /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ - te_expr *ret; - int arity; - - switch (TYPE_MASK(s->type)) { - case TOK_NUMBER: - ret = new_expr(TE_CONSTANT, 0); - CHECK_NULL(ret); - - ret->value = s->value; - next_token(s); - break; - - case TOK_VARIABLE: - ret = new_expr(TE_VARIABLE, 0); - CHECK_NULL(ret); - - ret->bound = s->bound; - next_token(s); - break; - - case TE_FUNCTION0: - case TE_CLOSURE0: - ret = new_expr(s->type, 0); - CHECK_NULL(ret); - - ret->function = s->function; - if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context; - next_token(s); - if (s->type == TOK_OPEN) { - next_token(s); - if (s->type != TOK_CLOSE) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - } - break; - - case TE_FUNCTION1: - case TE_CLOSURE1: - ret = new_expr(s->type, 0); - CHECK_NULL(ret); - - ret->function = s->function; - if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context; - next_token(s); - ret->parameters[0] = power(s); - CHECK_NULL(ret->parameters[0], te_free(ret)); - break; - - case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: - case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: - case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4: - case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: - arity = ARITY(s->type); - - ret = new_expr(s->type, 0); - CHECK_NULL(ret); - - ret->function = s->function; - if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; - next_token(s); - - if (s->type != TOK_OPEN) { - s->type = TOK_ERROR; - } else { - int i; - for(i = 0; i < arity; i++) { - next_token(s); - ret->parameters[i] = expr(s); - CHECK_NULL(ret->parameters[i], te_free(ret)); - - if(s->type != TOK_SEP) { - break; - } - } - if(s->type != TOK_CLOSE || i != arity - 1) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - } - - break; - - case TOK_OPEN: - next_token(s); - ret = list(s); - CHECK_NULL(ret); - - if (s->type != TOK_CLOSE) { - s->type = TOK_ERROR; - } else { - next_token(s); - } - break; - - default: - ret = new_expr(0, 0); - CHECK_NULL(ret); - - s->type = TOK_ERROR; - ret->value = NAN; - break; - } - - return ret; -} - - -static te_expr *power(state *s) { - /* = {("-" | "+")} */ - int sign = 1; - while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - if (s->function == sub) sign = -sign; - next_token(s); - } - - te_expr *ret; - - if (sign == 1) { - ret = base(s); - } else { - te_expr *b = base(s); - CHECK_NULL(b); - - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, b); - CHECK_NULL(ret, te_free(b)); - - ret->function = negate; - } - - return ret; -} - -#ifdef TE_POW_FROM_RIGHT -static te_expr *factor(state *s) { - /* = {"^" } */ - te_expr *ret = power(s); - CHECK_NULL(ret); - - int neg = 0; - - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { - te_expr *se = ret->parameters[0]; - free(ret); - ret = se; - neg = 1; - } - - te_expr *insertion = 0; - - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; - next_token(s); - - if (insertion) { - /* Make exponentiation go right-to-left. */ - te_expr *p = power(s); - CHECK_NULL(p, te_free(ret)); - - te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], p); - CHECK_NULL(insert, te_free(p), te_free(ret)); - - insert->function = t; - insertion->parameters[1] = insert; - insertion = insert; - } else { - te_expr *p = power(s); - CHECK_NULL(p, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p); - CHECK_NULL(ret, te_free(p), te_free(prev)); - - ret->function = t; - insertion = ret; - } - } - - if (neg) { - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); - CHECK_NULL(ret, te_free(prev)); - - ret->function = negate; - } - - return ret; -} -#else -static te_expr *factor(state *s) { - /* = {"^" } */ - te_expr *ret = power(s); - CHECK_NULL(ret); - - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; - next_token(s); - te_expr *p = power(s); - CHECK_NULL(p, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, p); - CHECK_NULL(ret, te_free(p), te_free(prev)); - - ret->function = t; - } - - return ret; -} -#endif - - - -static te_expr *term(state *s) { - /* = {("*" | "/" | "%") } */ - te_expr *ret = factor(s); - CHECK_NULL(ret); - - while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) { - te_fun2 t = s->function; - next_token(s); - te_expr *f = factor(s); - CHECK_NULL(f, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, f); - CHECK_NULL(ret, te_free(f), te_free(prev)); - - ret->function = t; - } - - return ret; -} - - -static te_expr *expr(state *s) { - /* = {("+" | "-") } */ - te_expr *ret = term(s); - CHECK_NULL(ret); - - while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - te_fun2 t = s->function; - next_token(s); - te_expr *te = term(s); - CHECK_NULL(te, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, te); - CHECK_NULL(ret, te_free(te), te_free(prev)); - - ret->function = t; - } - - return ret; -} - - -static te_expr *list(state *s) { - /* = {"," } */ - te_expr *ret = expr(s); - CHECK_NULL(ret); - - while (s->type == TOK_SEP) { - next_token(s); - te_expr *e = expr(s); - CHECK_NULL(e, te_free(ret)); - - te_expr *prev = ret; - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, e); - CHECK_NULL(ret, te_free(e), te_free(prev)); - - ret->function = comma; - } - - return ret; -} - - -#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(TYPE_MASK(n->type)) { - case TE_CONSTANT: return n->value; - case TE_VARIABLE: return *n->bound; - - case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: - switch(ARITY(n->type)) { - case 0: return TE_FUN(void)(); - case 1: return TE_FUN(double)(M(0)); - case 2: return TE_FUN(double, double)(M(0), M(1)); - case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2)); - case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); - default: return NAN; - } - - case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: - case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: - switch(ARITY(n->type)) { - case 0: return TE_FUN(void*)(n->parameters[0]); - case 1: return TE_FUN(void*, double)(n->parameters[1], M(0)); - case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1)); - case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2)); - case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); - default: return NAN; - } - - default: return NAN; - } - -} - -#undef TE_FUN -#undef M - -static void optimize(te_expr *n) { - /* Evaluates as much as possible. */ - if (n->type == TE_CONSTANT) return; - if (n->type == TE_VARIABLE) return; - - /* Only optimize out functions flagged as pure. */ - if (IS_PURE(n->type)) { - const int arity = ARITY(n->type); - int known = 1; - int i; - for (i = 0; i < arity; ++i) { - optimize(n->parameters[i]); - if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { - known = 0; - } - } - if (known) { - const double value = te_eval(n); - te_free_parameters(n); - n->type = TE_CONSTANT; - n->value = value; - } - } -} - - -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) { - state s; - s.start = s.next = expression; - s.lookup = variables; - s.lookup_len = var_count; - - next_token(&s); - te_expr *root = list(&s); - if (root == NULL) { - if (error) *error = -1; - return NULL; - } - - if (s.type != TOK_END) { - te_free(root); - if (error) { - *error = (s.next - s.start); - if (*error == 0) *error = 1; - } - return 0; - } else { - optimize(root); - if (error) *error = 0; - return root; - } -} - - -double te_interp(const char *expression, int *error) { - te_expr *n = te_compile(expression, 0, 0, error); - double ret; - if (n) { - ret = te_eval(n); - te_free(n); - } else { - ret = NAN; - } - return ret; -} \ No newline at end of file diff --git a/src/tinyexpr.h b/src/tinyexpr.h deleted file mode 100644 index 6036d8e2..00000000 --- a/src/tinyexpr.h +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: Zlib -/* - * TINYEXPR - Tiny recursive descent parser and evaluation engine in C - * - * Copyright (c) 2015-2020 Lewis Van Winkle - * - * http://CodePlea.com - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgement in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef TINYEXPR_H -#define TINYEXPR_H - - -#ifdef __cplusplus -extern "C" { -#endif - - - -typedef struct te_expr { - int type; - union {double value; const double *bound; const void *function;}; - void *parameters[99]; -} te_expr; - - -enum { - TE_VARIABLE = 0, - - TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, - TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7, - - TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3, - TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7, - - TE_FLAG_PURE = 32 -}; - -typedef struct te_variable { - const char *name; - const void *address; - int type; - void *context; -} te_variable; - - - -/* Parses the input expression, evaluates it, and frees it. */ -/* Returns NaN on error. */ -double te_interp(const char *expression, int *error); - -/* Parses the input expression and binds variables. */ -/* Returns NULL on error. */ -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *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); - - -#ifdef __cplusplus -} -#endif - -#endif /*TINYEXPR_H*/ \ No newline at end of file From 0868641d5a0303d3ff22fcecb4e3999d3f71ffbc Mon Sep 17 00:00:00 2001 From: KonstiDE Date: Sat, 1 Feb 2025 19:04:55 +0100 Subject: [PATCH 5/5] Updated documentation --- R/spectralIndices.R | 3 ++- man/spectralIndices.Rd | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/R/spectralIndices.R b/R/spectralIndices.R index 13d4795f..1e3bfa23 100644 --- a/R/spectralIndices.R +++ b/R/spectralIndices.R @@ -46,6 +46,7 @@ #' #' ## Custom Spectral Index Calculation (beta) (supports only bands right now...) #' # Get all indices +#' # Supports: Parentheses (), Addition +, Subtraction -, Multiplication *, Division / #' idxdb <- getOption("RStoolbox.idxdb") #' #' # Customize the RStoolbox index-database and overwrite the option @@ -175,7 +176,7 @@ spectralIndices <- function(img, bandsCalc[["mask"]] <- which(names(img) == "mask") } potArgs <- c(potArgs, "mask") - + ## Adjust layer argument so that the first layer we use is now layer 1, etc. ## This way we don't have to sample the whole stack if we only need a few layers diff --git a/man/spectralIndices.Rd b/man/spectralIndices.Rd index a684066d..de820ebc 100755 --- a/man/spectralIndices.Rd +++ b/man/spectralIndices.Rd @@ -79,6 +79,32 @@ By default all indices which can be calculated given the specified indices will See the table bellow for index names and required bands. Index values outside the valid value ranges (if such a range exists) will be set to NA. For example a pixel with NDVI > 1 will be set to NA. + + + + + + + +\tabular{lllll}{ \strong{ Index } \tab \strong{ Description } \tab \strong{ Source } \tab \strong{ Bands } \tab \strong{ Formula } \cr CLG \tab Green-band Chlorophyll Index \tab Gitelson2003 \tab \code{redEdge3, green} \tab \eqn{redEdge3/green - 1} \cr CLRE \tab Red-edge-band Chlorophyll Index \tab Gitelson2003 \tab \code{redEdge3, redEdge1} \tab \eqn{redEdge3/redEdge1 - 1} \cr CTVI \tab Corrected Transformed Vegetation Index \tab Perry1984 \tab \code{red, nir} \tab \eqn{(NDVI + 0.5)/sqrt(abs(NDVI + 0.5))} \cr DVI \tab Difference Vegetation Index \tab Richardson1977 \tab \code{red, nir} \tab \eqn{s * nir - red} \cr EVI \tab Enhanced Vegetation Index \tab Huete1999 \tab \code{red, nir, blue} \tab \eqn{G * ((nir - red)/(nir + C1 * red - C2 * blue + L_evi))} \cr EVI2 \tab Two-band Enhanced Vegetation Index \tab Jiang 2008 \tab \code{red, nir} \tab \eqn{G * (nir - red)/(nir + 2.4 * red + 1)} \cr GEMI \tab Global Environmental Monitoring Index \tab Pinty1992 \tab \code{red, nir} \tab \eqn{(((nir^2 - red^2) * 2 + (nir * 1.5) + (red * 0.5))/(nir + red + 0.5)) * (1 - ((((nir^2 - red^2) * 2 + (nir * 1.5) + (red * 0.5))/(nir + red + 0.5)) * 0.25)) - ((red - 0.125)/(1 - red))} \cr GNDVI \tab Green Normalised Difference Vegetation Index \tab Gitelson1998 \tab \code{green, nir} \tab \eqn{(nir - green)/(nir + green)} \cr KNDVI \tab Kernel Normalised Difference Vegetation Index \tab Camps-Valls2021 \tab \code{red, nir} \tab \eqn{tanh(((nir - red)/(nir + red)))^2} \cr MCARI \tab Modified Chlorophyll Absorption Ratio Index \tab Daughtery2000 \tab \code{green, red, redEdge1} \tab \eqn{((redEdge1 - red) - (redEdge1 - green)) * (redEdge1/red)} \cr MNDWI \tab Modified Normalised Difference Water Index \tab Xu2006 \tab \code{green, swir2} \tab \eqn{(green - swir2)/(green + swir2)} \cr MSAVI \tab Modified Soil Adjusted Vegetation Index \tab Qi1994 \tab \code{red, nir} \tab \eqn{nir + 0.5 - (0.5 * sqrt((2 * nir + 1)^2 - 8 * (nir - (2 * red))))} \cr MSAVI2 \tab Modified Soil Adjusted Vegetation Index 2 \tab Qi1994 \tab \code{red, nir} \tab \eqn{(2 * (nir + 1) - sqrt((2 * nir + 1)^2 - 8 * (nir - red)))/2} \cr MTCI \tab MERIS Terrestrial Chlorophyll Index \tab DashAndCurran2004 \tab \code{red, redEdge1, redEdge2} \tab \eqn{(redEdge2 - redEdge1)/(redEdge1 - red)} \cr NBRI \tab Normalised Burn Ratio Index \tab Garcia1991 \tab \code{nir, swir3} \tab \eqn{(nir - swir3)/(nir + swir3)} \cr NDREI1 \tab Normalised Difference Red Edge Index 1 \tab GitelsonAndMerzlyak1994 \tab \code{redEdge2, redEdge1} \tab \eqn{(redEdge2 - redEdge1)/(redEdge2 + redEdge1)} \cr NDREI2 \tab Normalised Difference Red Edge Index 2 \tab Barnes2000 \tab \code{redEdge3, redEdge1} \tab \eqn{(redEdge3 - redEdge1)/(redEdge3 + redEdge1)} \cr NDVI \tab Normalised Difference Vegetation Index \tab Rouse1974 \tab \code{red, nir} \tab \eqn{(nir - red)/(nir + red)} \cr NDVIC \tab Corrected Normalised Difference Vegetation Index \tab Nemani1993 \tab \code{red, nir, swir2} \tab \eqn{(nir - red)/(nir + red) * (1 - ((swir2 - swir2ccc)/(swir2coc - swir2ccc)))} \cr NDWI \tab Normalised Difference Water Index \tab McFeeters1996 \tab \code{green, nir} \tab \eqn{(green - nir)/(green + nir)} \cr NDWI2 \tab Normalised Difference Water Index \tab Gao1996 \tab \code{nir, swir2} \tab \eqn{(nir - swir2)/(nir + swir2)} \cr NRVI \tab Normalised Ratio Vegetation Index \tab Baret1991 \tab \code{red, nir} \tab \eqn{(red/nir - 1)/(red/nir + 1)} \cr REIP \tab Red Edge Inflection Point \tab GuyotAndBarnet1988 \tab \code{red, redEdge1, redEdge2, redEdge3} \tab \eqn{0.705 + 0.35 * ((red + redEdge3)/(2 - redEdge1))/(redEdge2 - redEdge1)} \cr RVI \tab Ratio Vegetation Index \tab \tab \code{red, nir} \tab \eqn{red/nir} \cr SATVI \tab Soil Adjusted Total Vegetation Index \tab Marsett2006 \tab \code{red, swir2, swir3} \tab \eqn{(swir2 - red)/(swir2 + red + L) * (1 + L) - (swir3/2)} \cr SAVI \tab Soil Adjusted Vegetation Index \tab Huete1988 \tab \code{red, nir} \tab \eqn{(nir - red) * (1 + L)/(nir + red + L)} \cr SLAVI \tab Specific Leaf Area Vegetation Index \tab Lymburger2000 \tab \code{red, nir, swir2} \tab \eqn{nir/(red + swir2)} \cr SR \tab Simple Ratio Vegetation Index \tab Birth1968 \tab \code{red, nir} \tab \eqn{nir/red} \cr TTVI \tab Thiam's Transformed Vegetation Index \tab Thiam1997 \tab \code{red, nir} \tab \eqn{sqrt(abs((nir - red)/(nir + red) + 0.5))} \cr TVI \tab Transformed Vegetation Index \tab Deering1975 \tab \code{red, nir} \tab \eqn{sqrt((nir - red)/(nir + red) + 0.5)} \cr WDVI \tab Weighted Difference Vegetation Index \tab Richardson1977 \tab \code{red, nir} \tab \eqn{nir - s * red} \cr CUSTOM \tab Super custom index \tab Mueller2024 \tab \code{blue, red} \tab \eqn{blue + red} \cr CUSTOM \tab Super custom index \tab Mueller2024 \tab \code{blue, red} \tab \eqn{blue + red} \cr CUSTOM \tab Super custom index \tab Mueller2024 \tab \code{blue, red} \tab \eqn{blue + red} \cr CUSTOM \tab Super custom index \tab Mueller2024 \tab \code{blue, red} \tab \eqn{blue + red} \cr CUSTOM \tab Super custom index \tab Mueller2024 \tab \code{blue, red} \tab \eqn{blue + red} \cr CUSTOM \tab Super custom index \tab Mueller2024 \tab \code{blue, red} \tab \eqn{blue + (red * red)} } + + +Some indices require additional parameters, such as the slope of the soil line which are specified via a list to the \code{coefs} argument. +Although the defaults are sensible values, values like the soil brightnes factor \code{L} for SAVI should be adapted depending on the characteristics of the scene. +The coefficients are: +\tabular{lll}{ +\strong{Coefficient} \tab \strong{Description} \tab \strong{Affected Indices} \cr +\code{s} \tab slope of the soil line \tab DVI, WDVI \cr +\code{L_evi, C1, C2, G} \tab various \tab EVI \cr +\code{L} \tab soil brightness factor \tab SAVI, SATVI \cr +\code{swir2ccc} \tab minimum swir2 value (completely closed forest canopy) \tab NDVIC \cr +\code{swir2coc} \tab maximum swir2 value (completely open canopy) \tab NDVIC \cr +} + + +The wavelength band names are defined following Schowengertd 2007, p10. +The last column shows exemplarily which Landsat 5 TM bands correspond to which wavelength range definition. +\tabular{lllllll}{ \strong{ Band } \tab \strong{ Description } \tab \strong{ Wavl_min } \tab \strong{ Wavl_max } \tab \strong{ Landsat5_Band } \tab \strong{ Sentinel2_Band } \cr vis \tab visible \tab 400 \tab 680 \tab 1,2,3 \tab 2,3,4 \cr red-edge1 \tab red-edge1 \tab 680 \tab 720 \tab - \tab 5 \cr red-edge2 \tab red-edge2 \tab 720 \tab 760 \tab - \tab 6 \cr red-edge3 \tab red-edge3 \tab 760 \tab 800 \tab - \tab 7 \cr nir \tab near infra-red \tab 800 \tab 1100 \tab 4 \tab 8/8a \cr swir1 \tab short-wave infra-red \tab 1100 \tab 1351 \tab - \tab 9,10 \cr swir2 \tab short-wave infra-red \tab 1400 \tab 1800 \tab 5 \tab 11 \cr swir3 \tab short-wave infra-red \tab 2000 \tab 2500 \tab 7 \tab 12 \cr mir1 \tab mid-wave infra-red \tab 3000 \tab 4000 \tab - \tab - \cr mir2 \tab mid-wave infra-red \tab 45000 \tab 5000 \tab - \tab - \cr tir1 \tab thermal infra-red \tab 8000 \tab 9500 \tab - \tab - \cr tir2 \tab thermal infra-red \tab 10000 \tab 140000 \tab 6 \tab - } } \examples{ library(ggplot2) @@ -101,6 +127,7 @@ plot(SI) ## Custom Spectral Index Calculation (beta) (supports only bands right now...) # Get all indices +# Supports: Parentheses (), Addition +, Subtraction -, Multiplication *, Division / idxdb <- getOption("RStoolbox.idxdb") # Customize the RStoolbox index-database and overwrite the option