"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegionStack = exports.RegionArena = exports.RegionInferencer = void 0;
exports.inferRegions = inferRegions;
/**
 * Region-based memory management: infer lexical arenas and annotate
 * allocations so they are freed deterministically when the region exits.
 */
class RegionInferencer {
    regions = [];
    counter = 0;
    stack = [];
    errors = [];
    infer(program) {
        this.regions = [];
        this.counter = 0;
        this.stack = [];
        this.errors = [];
        // Implicit root region
        const root = this.pushRegion(null, 1, 1);
        for (const stmt of program.statements) {
            this.walkStatement(stmt);
        }
        this.popRegion();
        // Annotate: nothing structural required — regions list drives runtime.
        void root;
        return {
            program,
            regions: [...this.regions],
            errors: [...this.errors],
        };
    }
    pushRegion(name, line, column) {
        const id = `r${this.counter++}`;
        const info = {
            id,
            name,
            line,
            column,
            allocations: [],
            parentId: this.stack.length ? this.stack[this.stack.length - 1] : null,
        };
        this.regions.push(info);
        this.stack.push(id);
        return info;
    }
    popRegion() {
        this.stack.pop();
    }
    current() {
        const id = this.stack[this.stack.length - 1];
        return this.regions.find((r) => r.id === id);
    }
    walkStatement(stmt) {
        switch (stmt.type) {
            case "LetStatement": {
                const ls = stmt;
                if (ls.value) {
                    this.walkExpression(ls.value);
                    const cur = this.current();
                    if (cur && this.isAllocating(ls.value)) {
                        cur.allocations.push(ls.name.value);
                    }
                }
                break;
            }
            case "ExpressionStatement": {
                const es = stmt;
                if (es.expression) {
                    this.walkExpression(es.expression);
                }
                break;
            }
            case "BlockStatement":
                for (const s of stmt.statements) {
                    this.walkStatement(s);
                }
                break;
            case "ReturnStatement":
                break;
            default:
                break;
        }
    }
    walkExpression(expr) {
        if (expr.type === "RegionExpression") {
            const re = expr;
            this.pushRegion(re.name?.value ?? null, re.token.line, re.token.column);
            this.walkStatement(re.body);
            this.popRegion();
            return;
        }
        if (expr.type === "CallExpression") {
            // handled via isAllocating on parent let
        }
        // Recurse lightly into common nodes
        const anyExpr = expr;
        for (const key of ["right", "left", "condition", "argument", "value", "scrutinee"]) {
            const child = anyExpr[key];
            if (child && typeof child === "object" && "type" in child) {
                this.walkExpression(child);
            }
        }
        if (Array.isArray(anyExpr.arguments)) {
            for (const a of anyExpr.arguments) {
                this.walkExpression(a);
            }
        }
        if (anyExpr.body && typeof anyExpr.body === "object") {
            const body = anyExpr.body;
            if (body.type === "BlockStatement") {
                this.walkStatement(anyExpr.body);
            }
            else if (body.type) {
                this.walkExpression(anyExpr.body);
            }
        }
    }
    isAllocating(expr) {
        if (expr.type === "ConstructorExpression") {
            return true;
        }
        if (expr.type === "CallExpression") {
            return true;
        }
        if (expr.type === "ChanExpression") {
            return true;
        }
        if (expr.type === "RefExpression") {
            return true;
        }
        return false;
    }
}
exports.RegionInferencer = RegionInferencer;
function inferRegions(program) {
    return new RegionInferencer().infer(program);
}
/**
 * Runtime region arena — bump allocations freed as a group on exit.
 */
class RegionArena {
    slots = [];
    closed = false;
    alloc(name, drop) {
        if (this.closed) {
            throw new Error("alloc in closed region");
        }
        this.slots.push({ name, drop });
    }
    exit() {
        if (this.closed) {
            return;
        }
        this.closed = true;
        for (let i = this.slots.length - 1; i >= 0; i--) {
            try {
                this.slots[i].drop();
            }
            catch {
                // best-effort deterministic teardown
            }
        }
        this.slots.length = 0;
    }
}
exports.RegionArena = RegionArena;
class RegionStack {
    stack = [];
    enter() {
        const arena = new RegionArena();
        this.stack.push(arena);
        return arena;
    }
    exit() {
        const arena = this.stack.pop();
        arena?.exit();
    }
    current() {
        return this.stack[this.stack.length - 1];
    }
}
exports.RegionStack = RegionStack;
//# sourceMappingURL=regions.js.map