2025-03-28 00:05:59 +01:00

170 lines
3.9 KiB
TypeScript

import { Ty, tyToString } from "./ty.ts";
import * as ast from "./ast.ts";
export type Fn = {
stmt: ast.Stmt;
locals: Local[];
paramLocals: Map<number, Local>;
returnLocal: Local;
blocks: Block[];
entry: Block;
exit: Block;
};
export type Block = {
id: number;
stmts: Stmt[];
ter: Ter;
};
export type Local = {
id: number;
ty: Ty;
ident?: string;
stmt?: ast.Stmt;
};
export type Stmt = {
kind: StmtKind;
};
export const Stmt = (kind: StmtKind): Stmt => ({ kind });
export type StmtKind =
| { tag: "error" }
| { tag: "push"; val: Val; ty: Ty }
| { tag: "pop" }
| { tag: "load"; local: Local }
| { tag: "store"; local: Local }
| { tag: "call"; args: number }
| { tag: BinaryOp };
export type BinaryOp =
| "lt"
| "gt"
| "le"
| "ge"
| "eq"
| "ne"
| "add"
| "sub"
| "mul"
| "div"
| "mod";
export type Ter = {
kind: TerKind;
};
export const Ter = (kind: TerKind): Ter => ({ kind });
export type TerKind =
| { tag: "error" }
| { tag: "unset" }
| { tag: "return" }
| { tag: "goto"; target: Block }
| {
tag: "if";
truthy: Block;
falsy: Block;
};
export type Val =
| { tag: "int"; val: number }
| { tag: "str"; val: string }
| { tag: "fn"; stmt: ast.Stmt };
export class FnStringifyer {
public constructor(
private fn: Fn,
) {}
public stringify(): string {
const kind = this.fn.stmt.kind;
if (kind.tag !== "fn") {
throw new Error();
}
return `${kind.ident}:\n${
this.fn.locals
.map((local) =>
` %${local.id} :: ${tyToString(local.ty)}${
local.ident ? ` '${local.ident}'` : ""
}\n`
)
.join("")
}${
this.fn.blocks
.map((block) =>
` .b${block.id}:\n${
block.stmts
.map((stmt) => ` ${this.stmt(stmt)}\n`)
.join("")
} ${this.ter(block.ter)}\n`
)
.join("")
}`;
}
private stmt(stmt: Stmt): string {
const k = stmt.kind;
switch (k.tag) {
case "error":
return "<error>";
case "push":
return `push (${tyToString(k.ty, { short: true })}) ${
this.val(k.val)
}`;
case "pop":
return "pop";
case "load":
return `load %${k.local.id}`;
case "store":
return `store %${k.local.id}`;
case "call":
return `call ${k.args}`;
case "lt":
case "gt":
case "le":
case "ge":
case "eq":
case "ne":
case "add":
case "sub":
case "mul":
case "div":
case "mod":
return k.tag;
}
}
private ter(ter: Ter): string {
const k = ter.kind;
switch (k.tag) {
case "error":
return "<error>";
case "unset":
return "<unset>";
case "return":
return "return";
case "goto":
return `goto .b${k.target.id}`;
case "if":
return `goto .b${k.truthy.id}, .b${k.falsy.id}`;
}
}
private val(val: Val): string {
switch (val.tag) {
case "str":
return JSON.stringify(val.val);
case "int":
return `${val.val}`;
case "fn":
if (val.stmt.kind.tag !== "fn") {
throw new Error();
}
return val.stmt.kind.ident;
}
}
}