stardis

Perform coupled heat transfer calculations
git clone git://git.meso-star.fr/stardis.git
Log | Files | Refs | README | LICENSE

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:
Atinyexpr/tinyexpr.c | 877+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atinyexpr/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__*/