summaryrefslogtreecommitdiff
path: root/src/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/expr.c')
-rw-r--r--src/expr.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 0000000..a808232
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,73 @@
+#include <math.h>
+#include "expr.h"
+
+double expr_eval(struct expr *expr, double time)
+{
+ switch (expr->type) {
+ case EXPR_NUMBER: return expr->number;
+ case EXPR_TIME: return time;
+ case EXPR_OP: {
+ size_t n_args = expr->op.args.len;
+ double args[n_args];
+ for (size_t i = 0; i < n_args; i++)
+ args[i] = expr_eval(&expr->op.args.ptr[i], time);
+ double accum;
+ switch (expr->op.type) {
+ case OP_ADD:
+ accum = 0.0;
+ for (size_t i = 0; i < n_args; i++)
+ accum += args[i];
+ return accum;
+ case OP_SUB:
+ if (n_args == 1) return -args[0];
+ return args[0] - args[1];
+ case OP_MUL:
+ accum = 1.0;
+ for (size_t i = 0; i < n_args; i++)
+ accum *= args[i];
+ return accum;
+ case OP_DIV:
+ if (n_args == 1) return 1.0 / args[0];
+ return args[0] / args[1];
+ case OP_MOD:
+ return fmod(args[0], args[1]);
+ case OP_MIN:
+ for (size_t i = 0; i < n_args; i++)
+ if (i == 0 || args[i] < accum)
+ accum = args[i];
+ return accum;
+ case OP_MAX:
+ for (size_t i = 0; i < n_args; i++)
+ if (i == 0 || args[i] > accum)
+ accum = args[i];
+ return accum;
+ case OP_CLAMP:
+ if (args[0] < args[1]) return args[1];
+ if (args[0] > args[2]) return args[2];
+ return args[0];
+ case OP_MIX:
+ return args[0] + (args[1] - args[0]) * args[2];
+ case OP_ABS:
+ return fabs(args[0]);
+ default: break; // unreachable
+ }
+ }
+ }
+ return 0.0;
+}
+
+void expr_free(struct expr *expr)
+{
+ if (expr->type == EXPR_OP) {
+ for (size_t i = 0; i < expr->op.args.len; i++)
+ expr_free(&expr->op.args.ptr[i]);
+ free(expr->op.args.ptr);
+ }
+}
+
+void expr_copy(struct expr *dst, struct expr *src)
+{
+ *dst = *src;
+ if (src->type == EXPR_OP)
+ array_dup(&dst->op.args, &src->op.args);
+}