summaryrefslogtreecommitdiff
path: root/src/expr.c
blob: a8082320bd5efb1174a48c9104b537bbe7442df2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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);
}