export function evaluate()

in packages/core/nano/src/interpreter.ts [34:110]


export function evaluate<O, I, Delay>(ctx: EvalContext, origin: O, rt: Runtime<Delay>, resolver: Resolver<O, I, Delay>, expr: ExpressionNode<I>): CalcValue<O> | Delay {
    switch (expr.kind) {
        case NodeKind.Literal:
            return expr.value;

        case NodeKind.Ident:
            return resolver.resolve(origin, expr.value, ctx.errors.resolveError);

        case NodeKind.Paren:
            return evaluate(ctx, origin, rt, resolver, expr.value);

        case NodeKind.Fun:
            throw "TODO: funs";

        case NodeKind.App:
            const appArgs = expr.children;
            if (appArgs.length === 0) {
                return ctx.errors.functionArity;
            }
            const head = evaluate(ctx, origin, rt, resolver, appArgs[0]);
            const evaluatedArgs: (CalcValue<O> | Delay)[] = [];
            for (let i = 1; i < appArgs.length; i += 1) {
                evaluatedArgs.push(evaluate(ctx, origin, rt, resolver, appArgs[i]));
            }
            return rt.appN(origin, head, evaluatedArgs, ctx.errors.appOnNonFunction);

        case NodeKind.Conditional:
            const condArgs = expr.children;
            if (condArgs.length === 0) {
                return ctx.errors.functionArity;
            }
            const cond = evaluate(ctx, origin, rt, resolver, condArgs[0]);
            if (rt.isDelayed(cond)) {
                return cond;
            }
            // TODO: coercion
            if (cond) {
                return condArgs[1] ? evaluate(ctx, origin, rt, resolver, condArgs[1]) : true;
            }
            return condArgs[2] ? evaluate(ctx, origin, rt, resolver, condArgs[2]) : false;

        case NodeKind.Dot:
            const obj = evaluate(ctx, origin, rt, resolver, expr.operand1);
            if (expr.operand2.kind === NodeKind.Ident && typeof expr.operand2.value === "string") {
                return rt.read(origin, obj, expr.operand2.value, ctx.errors.readOnNonObject);
            }
            return ctx.errors.nonStringField;

        case NodeKind.BinaryOp:
            return rt.app2(
                origin,
                ctx.binOps[expr.op],
                evaluate(ctx, origin, rt, resolver, expr.operand1),
                evaluate(ctx, origin, rt, resolver, expr.operand2)
            );

        case NodeKind.UnaryOp:
            return rt.app1(
                origin,
                ctx.unaryOps[expr.op],
                evaluate(ctx, origin, rt, resolver, expr.operand1)
            );

        case NodeKind.Missing:
            throw "TODO: missing";

        case NodeKind.Sequence:
            if (expr.children.length > 0) {
                return evaluate(ctx, origin, rt, resolver, expr.children[0]);
            }
            throw "TODO: missing";

        default:
            // TODO: assert never;
            throw `Unreachable: ${JSON.stringify(expr)}`;
    }
}