slige/compiler/middle/mir.ts
2025-01-02 09:14:24 +01:00

153 lines
4.9 KiB
TypeScript

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(` <error>;`);
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} = <error>;`);
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;
}
}
}
}
}
}