import { Ty, tyToString } from "./ty.ts"; import * as ast from "./ast.ts"; export type Fn = { stmt: ast.Stmt; locals: Local[]; paramLocals: Map; 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 ""; 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 ""; case "unset": return ""; 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; } } }