"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EffectRuntime = exports.EffectRegistry = void 0;
exports.checkEffects = checkEffects;
/**
 * Algebraic effect handlers — perform / handle with delimited continuations.
 */
class EffectRegistry {
    effects = new Map();
    register(decl) {
        const ops = new Map();
        for (const op of decl.operations) {
            ops.set(op.name.value, {
                effect: decl.name.value,
                name: op.name.value,
                arity: op.parameters.length,
            });
        }
        this.effects.set(decl.name.value, ops);
    }
    lookup(effect, op) {
        if (effect) {
            return this.effects.get(effect)?.get(op);
        }
        for (const ops of this.effects.values()) {
            const found = ops.get(op);
            if (found) {
                return found;
            }
        }
        return undefined;
    }
    has(effect) {
        return this.effects.has(effect);
    }
}
exports.EffectRegistry = EffectRegistry;
function checkEffects(program) {
    const diags = [];
    const registry = new EffectRegistry();
    for (const stmt of program.statements) {
        if (stmt.type === "EffectDeclaration") {
            registry.register(stmt);
        }
    }
    const walkStmt = (stmt) => {
        switch (stmt.type) {
            case "ExpressionStatement": {
                const es = stmt;
                if (es.expression) {
                    walkExpr(es.expression);
                }
                break;
            }
            case "BlockStatement":
                for (const s of stmt.statements) {
                    walkStmt(s);
                }
                break;
            case "LetStatement":
            case "ReturnStatement":
                break;
            default:
                break;
        }
    };
    const walkExpr = (expr) => {
        if (expr.type === "PerformExpression") {
            const p = expr;
            const info = registry.lookup(p.effectName, p.operation.value);
            if (!info) {
                diags.push({
                    message: `unknown effect operation \`${p.effectName ? p.effectName + "::" : ""}${p.operation.value}\` at line ${p.token.line}, column ${p.token.column}`,
                    line: p.token.line,
                    column: p.token.column,
                });
            }
            else if (info.arity !== p.arguments.length) {
                diags.push({
                    message: `effect \`${info.name}\` expects ${info.arity} arg(s), got ${p.arguments.length} at line ${p.token.line}, column ${p.token.column}`,
                    line: p.token.line,
                    column: p.token.column,
                });
            }
        }
        else if (expr.type === "HandleExpression") {
            const h = expr;
            if (!registry.has(h.effectName.value)) {
                diags.push({
                    message: `unknown effect \`${h.effectName.value}\` in handle at line ${h.token.line}, column ${h.token.column}`,
                    line: h.token.line,
                    column: h.token.column,
                });
            }
            walkStmt(h.body);
            for (const arm of h.handlers) {
                walkExpr(arm.body);
            }
        }
    };
    for (const stmt of program.statements) {
        walkStmt(stmt);
    }
    return diags;
}
/**
 * Runtime delimited-continuation stack for algebraic effects.
 */
class EffectRuntime {
    stack = [];
    push(frame) {
        this.stack.push(frame);
    }
    pop() {
        this.stack.pop();
    }
    perform(effectName, op, args) {
        for (let i = this.stack.length - 1; i >= 0; i--) {
            const frame = this.stack[i];
            if (effectName && frame.effectName !== effectName) {
                continue;
            }
            const handler = frame.handlers.get(op);
            if (!handler) {
                continue;
            }
            let resumed = undefined;
            let didResume = false;
            const cont = {
                resume: (value) => {
                    didResume = true;
                    resumed = value;
                    return value;
                },
                abort: (value) => {
                    didResume = true;
                    resumed = value;
                    return value;
                },
            };
            const result = handler.invoke(args, cont);
            return didResume ? resumed : result;
        }
        throw new Error(`uncaught effect ${effectName ? effectName + "::" : ""}${op}`);
    }
}
exports.EffectRuntime = EffectRuntime;
//# sourceMappingURL=effects.js.map