import { BinaryType, Stmt } from "../ast.ts"; import { VType, vtypeToString } from "../vtype.ts"; export type Mir = { fns: Fn[]; }; export type Fn = { stmt: Stmt; locals: Local[]; blocks: Block[]; }; export type LocalId = number; export type Local = { id: LocalId; vtype: VType; }; export type BlockId = number; export type Block = { id: BlockId; ops: Op[]; ter: Ter; label?: string; }; export type Op = { kind: OpKind; }; type L = LocalId; export type OpKind = | { type: "error" } | { type: "return" } | { type: "drop"; val: L } | { type: "assign"; dst: L; src: L } | { type: "assign_error"; dst: L } | { type: "assign_null"; dst: L } | { type: "assign_bool"; dst: L; val: boolean } | { type: "assign_int"; dst: L; val: number } | { type: "assign_string"; dst: L; val: string } | { type: "assign_fn"; dst: L; stmt: Stmt } | { type: "field"; dst: L; subject: L; ident: string } | { type: "assign_field"; subject: L; ident: string; src: L } | { type: "index"; dst: L; subject: L; index: number } | { type: "assign_field"; subject: L; index: L; src: L } | { type: "call_val"; dst: L; subject: L; args: L[] } | { type: "binary"; binaryType: BinaryType; dst: L; left: L; right: L }; export type Ter = { kind: TerKind; }; export type TerKind = | { type: "error" } | { type: "jump"; target: BlockId } | { type: "if"; cond: L; truthy: BlockId; falsy: BlockId }; export function printMir(mir: Mir) { for (const fn of mir.fns) { const stmt = fn.stmt; if (stmt.kind.type !== "fn") { throw new Error(); } const name = stmt.kind.ident; const vtype = stmt.kind.vtype; if (vtype?.type !== "fn") { throw new Error(); } const generics = vtype.genericParams ?.map(({ ident }) => `${ident}`).join(", ") ?? ""; const params = vtype.params .map(({ vtype }, i) => `_${fn.locals[i + 1].id}: ${vtypeToString(vtype)}` ) .join(", "); const returnType = vtypeToString(vtype.returnType); console.log(`${name}${generics}(${params}) -> ${returnType}:`); for (const { id, vtype } of fn.locals) { console.log(` let _${id}: ${vtypeToString(vtype)};`); } for (const block of fn.blocks) { console.log(`.${block.label ?? block.id}:`); for (const op of block.ops) { const k = op.kind; switch (k.type) { case "error": console.log(` ;`); break; case "return": console.log(` return;`); break; case "drop": console.log(` drop _${k.val};`); break; case "assign": console.log(` _${k.dst} = _${k.src};`); break; case "assign_error": console.log(` _${k.dst} = ;`); break; case "assign_null": console.log(` _${k.dst} = null;`); break; case "assign_bool": console.log(` _${k.dst} = ${k.val};`); break; case "assign_int": console.log(` _${k.dst} = ${k.val};`); break; case "assign_string": console.log(` _${k.dst} = "${k.val}";`); break; case "assign_fn": { const stmt = k.stmt; if (stmt.kind.type !== "fn") { throw new Error(); } console.log(` _${k.dst} = ${stmt.kind.ident};`); break; } case "field": console.log( ` _${k.dst} = _${k.subject}.${k.ident};`, ); break; case "index": console.log( ` _${k.dst} = _${k.subject}[_${k.index}];`, ); break; case "call_val": { const args = k.args.map((arg) => `_${arg}`).join(", "); console.log(` _${k.dst} = _${k.subject}(${args});`); break; } case "binary": { console.log( ` _${k.dst} = _${k.left} ${k.binaryType} _${k.right};`, ); break; } } } } } }