import * as ast from "./ast.ts";
import { Ty } from "./ty.ts";
import * as stringify from "./stringify.ts";

export interface Visitor {
    visitFn?(fn: Fn): void;
    visitBasicBlock?(bb: BasicBlock): void;
    visitInst?(inst: Inst): void;
}

export class Fn {
    constructor(
        public stmt: ast.FnStmt,
        public ty: Ty,
        public bbs: BasicBlock[],
    ) {}

    visit(v: Visitor) {
        v.visitFn?.(this);
        for (const bb of this.bbs) {
            bb.visit(v);
        }
    }

    pretty(colors?: stringify.PrettyColors): string {
        return stringify.mirFnPretty(this, colors);
    }
}

export class BasicBlock {
    public insts: Inst[] = [];

    visit(v: Visitor) {
        v.visitBasicBlock?.(this);
        for (const inst of this.insts) {
            inst.visit(v);
        }
    }
}

export class Inst {
    constructor(
        public ty: Ty,
        public kind: InstKind,
    ) {}

    visit(v: Visitor) {
        v.visitInst?.(this);
    }
}

export type InstKind =
    | { tag: "Error" }
    | { tag: "Void" }
    | { tag: "Int"; value: number; intTy: ast.IntTy }
    | { tag: "Bool"; value: boolean }
    | { tag: "Str"; value: string }
    | { tag: "Array"; values: Inst[] }
    | { tag: "Fn"; fn: Fn }
    | { tag: "Param"; idx: number }
    | { tag: "GetElemPtr"; base: Inst; offset: Inst }
    | { tag: "Slice"; value: Inst; begin: Inst | null; end: Inst | null }
    | { tag: "Call"; callee: Inst; args: Inst[] }
    | { tag: "Alloca" }
    | { tag: "Load"; source: Inst }
    | { tag: "Store"; target: Inst; source: Inst }
    | { tag: "Jump"; target: BasicBlock }
    | { tag: "Branch"; cond: Inst; truthy: BasicBlock; falsy: BasicBlock }
    | { tag: "Return"; source: Inst }
    | { tag: UnaryOp; source: Inst }
    | { tag: BinaryOp; left: Inst; right: Inst }
    | { tag: "Len"; source: Inst }
    | { tag: "DebugPrint"; args: Inst[] };

export type UnaryOp = "Not" | "Negate";

export type BinaryOp =
    | "Eq"
    | "Ne"
    | "Lt"
    | "Gt"
    | "Lte"
    | "Gte"
    | "BitOr"
    | "BitXor"
    | "BitAnd"
    | "Shl"
    | "Shr"
    | "Add"
    | "Sub"
    | "Mul"
    | "Div"
    | "Rem";
