commit b3c449fa29812e728286b6461bf55f872cd891fb
parent 93b2daf39b5d285b9ea964c2e844abf5741ba3e4
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Thu, 24 Jan 2019 09:54:11 +0100
Include tinyexpr in sources tree (as it is a custom version)
Diffstat:
| A | tinyexpr/tinyexpr.c | | | 877 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | tinyexpr/tinyexpr.h | | | 195 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 1072 insertions(+), 0 deletions(-)
diff --git a/tinyexpr/tinyexpr.c b/tinyexpr/tinyexpr.c
@@ -0,0 +1,877 @@
+/*
+ * TINYEXPR - Tiny recursive descent parser and evaluation engine in C
+ *
+ * Copyright (c) 2015-2018 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 <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <assert.h>
+
+#ifndef M_E
+#define M_E 2.71828182845904523536 // e
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 // pi
+#endif
+
+#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 {
+
+#ifdef TE_WITHOUT_FUNCTION_0
+ TE_FUNCTION0 = 8,
+#endif
+#ifdef TE_WITHOUT_CLOSURES
+ TE_CLOSURE7 = 23,
+#endif
+
+ TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP,
+ TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_OFFSET, TOK_INFIX,
+
+ TE_CONDITION = 256
+};
+
+typedef struct state {
+ const char *start;
+ const char *next;
+ int type;
+ union value v;
+ void *context;
+
+ const te_variable *lookup;
+ int lookup_len;
+} state;
+
+
+#define TYPE_MASK(TYPE) ((TYPE)&0x0000003F)
+
+#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0)
+#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0)
+#ifdef TE_WITHOUT_CLOSURES
+#define ARITY(TYPE) ( ((TYPE) & TE_FUNCTION0) ? ((TYPE) & 0x00000007) : 0 )
+#else
+#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0)
+#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 )
+#endif
+#define IS_CONDITION(TYPE) (((TYPE) & TE_CONDITION) != 0)
+#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__})
+
+static te_expr *new_expr(const int type, const te_expr *parameters[]) {
+ const size_t arity = ARITY(type);
+ const size_t psize = sizeof(void*) * arity;
+ const size_t size = (sizeof(te_expr) - sizeof(void*)) + psize
+#ifndef TE_WITHOUT_CLOSURES
+ + (IS_CLOSURE(type) ? sizeof(void*) : 0)
+#endif
+ ;
+ te_expr *ret = malloc(size);
+ memset(ret, 0, size);
+ if (arity && parameters) {
+ memcpy(ret->parameters, parameters, psize);
+ }
+ ret->type = type;
+ ret->v.bound = 0;
+ return ret;
+}
+
+static te_expr *new_expr1(const int type, te_expr *p1) {
+ const size_t size = sizeof(te_expr)
+#ifndef TE_WITHOUT_CLOSURES
+ + (IS_CLOSURE(type) ? sizeof(void*) : 0)
+#endif
+ ;
+ assert(p1 && ARITY(type) == 1);
+ te_expr *ret = malloc(size);
+ ret->type = type;
+ ret->v.bound = 0;
+ ret->parameters[0] = p1;
+ return ret;
+}
+
+static te_expr *new_expr2(const int type, te_expr *p1, te_expr *p2) {
+ const size_t size = sizeof(te_expr) + sizeof(void*)
+#ifndef TE_WITHOUT_CLOSURES
+ + (IS_CLOSURE(type) ? sizeof(void*) : 0)
+#endif
+ ;
+ assert(p1 && p2 && ARITY(type) == 2);
+ te_expr *ret = malloc(size);
+ ret->type = type;
+ ret->v.bound = 0;
+ ret->parameters[0] = p1;
+ ret->parameters[1] = p2;
+ return ret;
+}
+
+static void te_free_parameters(te_expr *n) {
+ int i;
+ if (!n) return;
+ for (i = 0; i < ARITY(n->type); i++)te_free(n->parameters[i]);
+}
+
+
+void te_free(te_expr *n) {
+ if (!n) return;
+ te_free_parameters(n);
+ free(n);
+}
+
+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 (double)result;
+}
+static double npr(double n, double r) {return ncr(n, r) * fac(r);}
+
+/* Workaround for a VC 2017 problem */
+static double ceil_(double x) { return ceil(x); }
+static double floor_(double x) { return floor(x); }
+
+static const te_variable functions[] = {
+ /* must be in alphabetical order */
+ {"abs", {.f1=fabs}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"acos", {.f1=acos}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"asin", {.f1=asin}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"atan", {.f1=atan}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"atan2", {.f2=atan2}, TE_FUNCTION2 | TE_FLAG_PURE, 0},
+ {"ceil", {.f1=ceil_}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"cos", {.f1=cos}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"cosh", {.f1=cosh}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"e", {.value = M_E}, TE_CONSTANT, 0},
+ {"exp", {.f1=exp}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"fac", {.f1=fac}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"floor", {.f1=floor_}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"if", {.f3 = NULL}, /* Specific treatment, no associated C function */
+ TE_FUNCTION3 | TE_CONDITION | TE_FLAG_PURE, 0},
+ {"ln", {.f1=log}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+#ifdef TE_NAT_LOG
+ {"log", {.f1=log}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+#else
+ {"log", {.f1=log10}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+#endif
+ {"log10", {.f1=log10}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"ncr", {.f2=ncr}, TE_FUNCTION2 | TE_FLAG_PURE, 0},
+ {"npr", {.f2=npr}, TE_FUNCTION2 | TE_FLAG_PURE, 0},
+ {"pi", {.value=M_PI}, TE_CONSTANT, 0},
+ {"pow", {.f2=pow}, TE_FUNCTION2 | TE_FLAG_PURE, 0},
+ {"sin", {.f1=sin}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"sinh", {.f1=sinh}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"sqrt", {.f1=sqrt}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"tan", {.f1=tan}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {"tanh", {.f1=tanh}, TE_FUNCTION1 | TE_FLAG_PURE, 0},
+ {0, {0}, 0, 0}
+};
+
+static const te_variable *find_builtin(const char *name, size_t 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, size_t 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;}
+static double is_gt(double a, double b) { return a > b ? 1 : 0; }
+static double is_ge(double a, double b) { return a >= b ? 1 : 0; }
+static double is_lt(double a, double b) { return a < b ? 1 : 0; }
+static double is_le(double a, double b) { return a <= b ? 1 : 0; }
+static double is_eq(double a, double b) { return a == b ? 1 : 0; }
+static double is_neq(double a, double b) { return a != b ? 1 : 0; }
+
+static int next_if(state *s, const char c) {
+ int r = *s->next && s->next[0] == c;
+ if (r) s->next++;
+ return r;
+}
+
+static 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->v.value = strtod(s->next, (char**)&s->next);
+ s->type = TOK_NUMBER;
+ } else {
+ /* Look for a variable or builtin function call. */
+ if (s->next[0] >= 'a' && s->next[0] <= 'z') {
+ const char *start;
+ start = s->next;
+ while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++;
+
+ const te_variable *var = find_lookup(s, start, (size_t)(s->next - start));
+ if (!var) var = find_builtin(start, (size_t)(s->next - start));
+
+ if (!var) {
+ s->type = TOK_ERROR;
+ } else {
+ switch(TYPE_MASK(var->type))
+ {
+ case TE_VARIABLE:
+ s->type = TOK_VARIABLE;
+ s->v.bound = var->v.bound;
+ break;
+
+ case TE_OFFSET:
+ s->type = TOK_OFFSET;
+ s->v.offset = var->v.offset;
+ break;
+
+ case TE_CONSTANT:
+ s->type = TE_CONSTANT;
+ s->v.value = var->v.value;
+ break;
+
+#ifndef TE_WITHOUT_CLOSURES
+ case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3:
+ case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
+ s->context = var->context;
+#endif
+#ifndef TE_WITHOUT_FUNCTION_0
+ /* fall through */
+ case TE_FUNCTION0:
+#endif
+ /* fall through */
+ case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3:
+#if TE_MAX_FUNCTION_ARITY >= 4
+ /* fall through */
+ case TE_FUNCTION4:
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 5
+ /* fall through */
+ case TE_FUNCTION5:
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 6
+ /* fall through */
+ case TE_FUNCTION6:
+#endif
+#if TE_MAX_FUNCTION_ARITY == 7
+ /* fall through */
+ case TE_FUNCTION7:
+#endif
+ s->type = var->type;
+ s->v.any = var->v.any;
+ break;
+ }
+ }
+
+ } else {
+ /* Look for an operator or special character. */
+ switch (s->next++[0]) {
+ case '+': s->type = TOK_INFIX; s->v.f2 = add; break;
+ case '-': s->type = TOK_INFIX; s->v.f2 = sub; break;
+ case '*': s->type = TOK_INFIX; s->v.f2 = mul; break;
+ case '/': s->type = TOK_INFIX; s->v.f2 = divide; break;
+ case '^': s->type = TOK_INFIX; s->v.f2 = pow; break;
+ case '%': s->type = TOK_INFIX; s->v.f2 = fmod; break;
+ case '>': s->type = TOK_INFIX;
+ s->v.f2 = next_if(s, '=') ? is_ge : is_gt; break;
+ case '<': s->type = TOK_INFIX;
+ s->v.f2 = next_if(s, '=') ? is_le : is_lt; break;
+ case '=': s->type = TOK_INFIX;
+ /* The only valid char after = is = */
+ if (next_if(s, '=')) s->v.f2 = is_eq; else s->type = TOK_ERROR;
+ break;
+ case '!': s->type = TOK_INFIX;
+ /* The only valid char after ! is = */
+ if (next_if(s, '=')) s->v.f2 = is_neq; else s->type = TOK_ERROR;
+ 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 *eql(state *s);
+static te_expr *power(state *s);
+
+static te_expr *base(state *s) {
+ /* <base> = <constant> | <variable> | <function-0> {"(" ")"} | <function-1> <power> | <function-X> "(" <expr> {"," <expr>} ")" | "(" <list> ")" */
+ te_expr *ret;
+ int arity;
+
+ switch (TYPE_MASK(s->type)) {
+ case TOK_NUMBER:
+ ret = new_expr(TE_CONSTANT, 0);
+ ret->v.value = s->v.value;
+ next_token(s);
+ break;
+
+ case TOK_VARIABLE:
+ ret = new_expr(TE_VARIABLE, 0);
+ ret->v.bound = s->v.bound;
+ next_token(s);
+ break;
+
+ case TOK_OFFSET:
+ ret = new_expr(TE_OFFSET, 0);
+ ret->v.offset = s->v.offset;
+ next_token(s);
+ break;
+
+ case TE_CONSTANT:
+ ret = new_expr(TE_CONSTANT, 0);
+ ret->v.value = s->v.value;
+ next_token(s);
+ break;
+
+#if ! defined(TE_WITHOUT_FUNCTION_0) || ! defined(TE_WITHOUT_CLOSURES)
+#ifndef TE_WITHOUT_FUNCTION_0
+ case TE_FUNCTION0:
+#endif
+#ifndef TE_WITHOUT_CLOSURES
+ case TE_CLOSURE0:
+#endif
+ ret = new_expr(s->type, 0);
+ ret->v.any = s->v.any;
+#ifndef TE_WITHOUT_CLOSURES
+ if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context;
+#endif
+ 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;
+#endif
+
+ case TE_FUNCTION1:
+#ifndef TE_WITHOUT_CLOSURES
+ case TE_CLOSURE1:
+#endif
+ ret = new_expr(s->type, 0);
+ ret->v.any = s->v.any;
+#ifndef TE_WITHOUT_CLOSURES
+ if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context;
+#endif
+ next_token(s);
+ ret->parameters[0] = power(s);
+ break;
+
+ case TE_FUNCTION2: case TE_FUNCTION3:
+#if TE_MAX_FUNCTION_ARITY >= 4
+ case TE_FUNCTION4:
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 5
+ case TE_FUNCTION5:
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 6
+ case TE_FUNCTION6:
+#endif
+#if TE_MAX_FUNCTION_ARITY == 7
+ case TE_FUNCTION7:
+#endif
+#ifndef TE_WITHOUT_CLOSURES
+ case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4:
+ case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
+#endif
+ arity = ARITY(s->type);
+
+ ret = new_expr(s->type, 0);
+ ret->v.any = s->v.any;
+#ifndef TE_WITHOUT_CLOSURES
+ if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context;
+#endif
+ 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] = eql(s);
+ 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);
+ if (s->type != TOK_CLOSE) {
+ s->type = TOK_ERROR;
+ } else {
+ next_token(s);
+ }
+ break;
+
+ default:
+ ret = new_expr(0, 0);
+ s->type = TOK_ERROR;
+ ret->v.value = NAN;
+ break;
+ }
+
+ return ret;
+}
+
+
+static te_expr *power(state *s) {
+ /* <power> = {("-" | "+")} <base> */
+ int sign = 1;
+ while (s->type == TOK_INFIX && (s->v.f2 == add || s->v.f2 == sub)) {
+ if (s->v.f2 == sub) sign = -sign;
+ next_token(s);
+ }
+
+ te_expr *ret;
+
+ if (sign == 1) {
+ ret = base(s);
+ } else {
+ ret = new_expr1(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
+ ret->v.f1 = negate;
+ }
+
+ return ret;
+}
+
+#ifdef TE_POW_FROM_RIGHT
+static te_expr *factor(state *s) {
+ /* <factor> = <power> {"^" <power>} */
+ te_expr *ret = power(s);
+
+ int neg = 0;
+ te_expr *insertion = 0;
+
+ if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->v.f1 == negate) {
+ te_expr *se = ret->parameters[0];
+ free(ret);
+ ret = se;
+ neg = 1;
+ }
+
+ while (s->type == TOK_INFIX && (s->v.f2 == pow)) {
+ te_fun2 t = s->v.f2;
+ next_token(s);
+
+ if (insertion) {
+ /* Make exponentiation go right-to-left. */
+ te_expr *insert = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s));
+ insert->v.f2 = t;
+ insertion->parameters[1] = insert;
+ insertion = insert;
+ } else {
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s));
+ ret->v.f2 = t;
+ insertion = ret;
+ }
+ }
+
+ if (neg) {
+ ret = new_expr1(TE_FUNCTION1 | TE_FLAG_PURE, ret);
+ ret->v.f1 = negate;
+ }
+
+ return ret;
+}
+#else
+static te_expr *factor(state *s) {
+ /* <factor> = <power> {"^" <power>} */
+ te_expr *ret = power(s);
+
+ while (s->type == TOK_INFIX && (s->v.f2 == pow)) {
+ te_fun2 t = s->v.f2;
+ next_token(s);
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s));
+ ret->v.f2 = t;
+ }
+
+ return ret;
+}
+#endif
+
+
+
+static te_expr *term(state *s) {
+ /* <term> = <factor> {("*" | "/" | "%") <factor>} */
+ te_expr *ret = factor(s);
+
+ while (s->type == TOK_INFIX && (s->v.f2 == mul || s->v.f2 == divide || s->v.f2 == fmod)) {
+ te_fun2 t = s->v.f2;
+ next_token(s);
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s));
+ ret->v.f2 = t;
+ }
+
+ return ret;
+}
+
+
+static te_expr *expr(state *s) {
+ /* <expr> = <term> {("+" | "-") <term>} */
+ te_expr *ret = term(s);
+
+ while (s->type == TOK_INFIX && (s->v.f2 == add || s->v.f2 == sub)) {
+ te_fun2 t = s->v.f2;
+ next_token(s);
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s));
+ ret->v.f2 = t;
+ }
+
+ return ret;
+}
+
+
+static te_expr *cmp(state *s) {
+ /* <cmp> = <expr> {(">" | "<" | ">=" | "<=") <expr>} */
+ te_expr *ret = expr(s);
+
+ while (s->type == TOK_INFIX
+ && (s->v.f2 == is_lt || s->v.f2 == is_le || s->v.f2 == is_gt || s->v.f2 == is_ge)) {
+ te_fun2 t = s->v.f2;
+ next_token(s);
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s));
+ ret->v.f2 = t;
+ }
+
+ return ret;
+}
+
+
+static te_expr *eql(state *s) {
+ /* <eql> = <cmp> {("==" | "!=") <cmp>} */
+ te_expr *ret = cmp(s);
+
+ while (s->type == TOK_INFIX && (s->v.f2 == is_eq || s->v.f2 == is_neq)) {
+ te_fun2 t = s->v.f2;
+ next_token(s);
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, cmp(s));
+ ret->v.f2 = t;
+ }
+
+ return ret;
+}
+
+
+static te_expr *list(state *s) {
+ /* <list> = <eql> {"," <eql>} */
+ te_expr *ret = eql(s);
+
+ while (s->type == TOK_SEP) {
+ next_token(s);
+ ret = new_expr2(TE_FUNCTION2 | TE_FLAG_PURE, ret, eql(s));
+ ret->v.f2 = comma;
+ }
+
+ return ret;
+}
+
+
+#define M(e) te_eval(n->parameters[e], base_addr)
+
+
+double te_eval(const te_expr *n, const void* base_addr) {
+ if (!n) return NAN;
+
+ switch(TYPE_MASK(n->type)) {
+ case TE_CONSTANT: return n->v.value;
+ case TE_VARIABLE: return *n->v.bound;
+ case TE_OFFSET: assert(base_addr);
+ return *(double*)(((char*)base_addr)+n->v.offset);
+
+#ifndef TE_WITHOUT_FUNCTION_0
+ case TE_FUNCTION0: return n->v.f0();
+#endif
+ case TE_FUNCTION1: return n->v.f1(M(0));
+ case TE_FUNCTION2: return n->v.f2(M(0), M(1));
+ case TE_FUNCTION3:
+ if (IS_CONDITION(n->type)) return M(0) ? M(1) : M(2);
+ else return n->v.f3(M(0), M(1), M(2));
+#if TE_MAX_FUNCTION_ARITY >= 4
+ case TE_FUNCTION4: return n->v.f4(M(0), M(1), M(2), M(3));
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 5
+ case TE_FUNCTION5: return n->v.f5(M(0), M(1), M(2), M(3), M(4));
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 6
+ case TE_FUNCTION6: return n->v.f6(M(0), M(1), M(2), M(3), M(4), M(5));
+#endif
+#if TE_MAX_FUNCTION_ARITY == 7
+ case TE_FUNCTION7: return n->v.f7(M(0), M(1), M(2), M(3), M(4), M(5), M(6));
+#endif
+
+#ifndef TE_WITHOUT_CLOSURES
+ case TE_CLOSURE0: return n->v.cl0(n->parameters[0]);
+ case TE_CLOSURE1: return n->v.cl1(n->parameters[1], M(0));
+ case TE_CLOSURE2: return n->v.cl2(n->parameters[2], M(0), M(1));
+ case TE_CLOSURE3: return n->v.cl3(n->parameters[3], M(0), M(1), M(2));
+ case TE_CLOSURE4: return n->v.cl4(n->parameters[4], M(0), M(1), M(2), M(3));
+ case TE_CLOSURE5: return n->v.cl5(n->parameters[5], M(0), M(1), M(2), M(3), M(4));
+ case TE_CLOSURE6: return n->v.cl6(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5));
+ case TE_CLOSURE7: return n->v.cl7(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6));
+#endif
+
+ default: return NAN;
+ }
+
+}
+
+#undef M
+
+static int expr_equal(const te_expr* e1, const te_expr* e2) {
+ int i;
+ if (e1->type != e2->type) return 0;
+ if (e1->v.any != e2->v.any) return 0;
+ for (i = 0; i < ARITY(e1->type); i++) {
+ if(!expr_equal(e1->parameters[i], e2->parameters[i])) return 0;
+ }
+ return 1;
+}
+
+static void optimize(te_expr *n) {
+ /* Only optimize out conditions and functions flagged as pure. */
+ if (IS_CONDITION(n->type)) {
+ te_expr* cond = n->parameters[0];
+ assert(IS_FUNCTION(n->type) && ARITY(n->type) == 3);
+ optimize(cond);
+ if (cond->type == TE_CONSTANT) {
+ te_expr* keep = (cond->v.value) ? n->parameters[1] : n->parameters[2];
+ /* Can keep either param[1] or param[2] */
+ optimize(keep);
+ n->type = keep->type;
+ n->v.any = keep->v.any;
+ te_free_parameters(n);
+ }
+ else { /* c ? x : x is x */
+ te_expr* if_branch = n->parameters[1];
+ te_expr* else_branch = n->parameters[2];
+ optimize(if_branch);
+ optimize(else_branch);
+ if (expr_equal(if_branch, else_branch)) {
+ n->type = if_branch->type;
+ n->v.any = if_branch->v.any;
+ te_free_parameters(n);
+ }
+ }
+ }
+ else if (IS_PURE(n->type)) {
+ const int arity = ARITY(n->type);
+ int known = 1;
+ int i;
+ assert(IS_FUNCTION(n->type));
+ 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, NULL);
+ te_free_parameters(n);
+ n->type = TE_CONSTANT;
+ n->v.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 (s.type != TOK_END) {
+ te_free(root);
+ if (error) {
+ *error = (int)(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, NULL);
+ te_free(n);
+ } else {
+ ret = NAN;
+ }
+ return ret;
+}
+
+static void pn (const te_expr *n, int depth) {
+ int i, arity;
+ printf("%*s", depth, "");
+
+ switch(TYPE_MASK(n->type)) {
+ case TE_CONSTANT: printf("%f\n", n->v.value); break;
+ case TE_VARIABLE: printf("bound %p\n", (void*) n->v.bound); break;
+ case TE_OFFSET: printf("offset %zu\n", n->v.offset); break;
+
+#ifndef TE_WITHOUT_FUNCTION_0
+ case TE_FUNCTION0:
+#endif
+ case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3:
+#if TE_MAX_FUNCTION_ARITY >= 4
+ case TE_FUNCTION4:
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 5
+ case TE_FUNCTION5:
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 6
+ case TE_FUNCTION6:
+#endif
+#if TE_MAX_FUNCTION_ARITY == 7
+ case TE_FUNCTION7:
+#endif
+#ifndef TE_WITHOUT_CLOSURES
+ case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3:
+ case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
+#endif
+ arity = ARITY(n->type);
+ printf("f%d", arity);
+ for(i = 0; i < arity; i++) {
+ printf(" %p", n->parameters[i]);
+ }
+ printf("\n");
+ for(i = 0; i < arity; i++) {
+ pn(n->parameters[i], depth + 1);
+ }
+ break;
+ }
+}
+
+
+void te_print(const te_expr *n) {
+ pn(n, 0);
+}
diff --git a/tinyexpr/tinyexpr.h b/tinyexpr/tinyexpr.h
@@ -0,0 +1,195 @@
+/*
+ * TINYEXPR - Tiny recursive descent parser and evaluation engine in C
+ *
+ * Copyright (c) 2015-2018 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__
+
+#include <stddef.h>
+
+/* Helper macro to be used when defining a te_variable */
+#define TE_DEF_VARIABLE(Name, Var) {(Name), {.var=&(Var)}, TE_VARIABLE, NULL}
+#define TE_DEF_OFFSET(Name, Offset) {(Name), {.offset=(Offset)}, TE_OFFSET, NULL}
+#define TE_DEF_CONSTANT(Name, Value) {(Name), {.value=(Value)}, TE_CONSTANT, NULL}
+#define TE_DEF_FUNCTION(Name, Fun, Arity) {(Name), {.f##Arity=(Fun)}, TE_FUNCTION##Arity, NULL}
+#ifndef TE_WITHOUT_CLOSURES
+#define TE_DEF_CLOSURE(Name, Closure, Arity, Ctx) {(Name), {.cl##Arity=(Closure)}, TE_CLOSURE##Arity, (Ctx)}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef TE_MAX_FUNCTION_ARITY
+#if !(TE_MAX_FUNCTION_ARITY >= 3 && TE_MAX_FUNCTION_ARITY <= 7)
+#error Valid range for TE_MAX_FUNCTION_ARITY is [3 7]
+#endif
+#else
+#define TE_MAX_FUNCTION_ARITY 7
+#endif
+
+
+#ifndef TE_WITHOUT_FUNCTION_0
+#define FUN_TYPE_0 \
+ double(*f0)(void);
+#else
+#define FUN_TYPE_0
+#endif
+#define FUN_TYPES_1_3 \
+ void *any;\
+ double(*f1)(double);\
+ double(*f2)(double, double);\
+ double(*f3)(double, double, double);
+#if TE_MAX_FUNCTION_ARITY >= 4
+#define FUN_TYPE_4 \
+ double(*f4)(double, double, double, double);
+#else
+#define FUN_TYPE_4
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 5
+#define FUN_TYPE_5 \
+ double(*f5)(double, double, double, double, double);
+#else
+#define FUN_TYPE_5
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 6
+#define FUN_TYPE_6 \
+ double(*f6)(double, double, double, double, double, double);
+#else
+#define FUN_TYPE_6
+#endif
+#if TE_MAX_FUNCTION_ARITY == 7
+#define FUN_TYPE_7 \
+ double(*f7)(double, double, double, double, double, double, double);
+#else
+#define FUN_TYPE_7
+#endif
+
+#ifndef TE_WITHOUT_CLOSURES
+#define CLOSURE_TYPES \
+ double(*cl0)(void*);\
+ double(*cl1)(void*, double);\
+ double(*cl2)(void*, double, double);\
+ double(*cl3)(void*, double, double, double);\
+ double(*cl4)(void*, double, double, double, double);\
+ double(*cl5)(void*, double, double, double, double, double);\
+ double(*cl6)(void*, double, double, double, double, double, double);\
+ double(*cl7)(void*, double, double, double, double, double, double, double);
+#else
+#define CLOSURE_TYPES
+#endif
+
+union fun {
+ FUN_TYPE_0
+ FUN_TYPES_1_3
+ FUN_TYPE_4
+ FUN_TYPE_5
+ FUN_TYPE_6
+ FUN_TYPE_7
+ CLOSURE_TYPES
+};
+
+union value {
+ FUN_TYPE_0
+ FUN_TYPES_1_3
+ FUN_TYPE_4
+ FUN_TYPE_5
+ FUN_TYPE_6
+ FUN_TYPE_7
+ CLOSURE_TYPES
+ double value;
+ const double *var;
+ size_t offset;
+ const double *bound;
+};
+
+typedef struct te_expr {
+ int type;
+ union value v;
+ void *parameters[1];
+} te_expr;
+
+
+enum {
+ TE_VARIABLE = 0,
+ TE_OFFSET = 1,
+ TE_CONSTANT = 2,
+
+#ifndef TE_WITHOUT_FUNCTION_0
+ TE_FUNCTION0 = 8,
+#endif
+ TE_FUNCTION1 = 9, TE_FUNCTION2, TE_FUNCTION3,
+#if TE_MAX_FUNCTION_ARITY >= 4
+ TE_FUNCTION4,
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 5
+ TE_FUNCTION5,
+#endif
+#if TE_MAX_FUNCTION_ARITY >= 6
+ TE_FUNCTION6,
+#endif
+#if TE_MAX_FUNCTION_ARITY == 7
+ TE_FUNCTION7,
+#endif
+#ifndef TE_WITHOUT_CLOSURES
+ TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3,
+ TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7,
+#endif
+ TE_FLAG_PURE = 64
+};
+
+typedef struct te_variable {
+ const char *name;
+ const union value v;
+ int type;
+ /* context could be member only if TE_WITHOUT_CLOSURES is undefined
+ * we don't take the opportunity as it would add too much complexity */
+ 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, const void* base_addr);
+
+/* Prints debugging information on the syntax tree. */
+void te_print(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__*/