mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 08:24:05 +02:00
170 lines
3.9 KiB
TypeScript
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;
|
|
}
|
|
}
|
|
}
|