add interpreter

This commit is contained in:
sfja 2026-03-10 19:45:00 +01:00
parent ef269442f1
commit 3b3a189020
4 changed files with 119 additions and 2 deletions

View File

@ -6,9 +6,8 @@ fn add(a: int, b: int) -> int
fn main() fn main()
{ {
let sum = add(2, 3); let sum: void = add(2, 3);
print_int(sum); print_int(sum);
} }
// vim: syntax=rust

View File

@ -1,6 +1,7 @@
import * as ast from "./ast.ts"; import * as ast from "./ast.ts";
import * as front from "./front.ts"; import * as front from "./front.ts";
import * as middle from "./middle.ts"; import * as middle from "./middle.ts";
import { MirInterpreter } from "./mir_interpreter.ts";
const filename = Deno.args[0]; const filename = Deno.args[0];
const text = await Deno.readTextFile(filename); const text = await Deno.readTextFile(filename);
@ -34,3 +35,6 @@ const m = new middle.MiddleLowerer(resols, checker);
const mainMiddleFn = m.lowerFn(mainFn); const mainMiddleFn = m.lowerFn(mainFn);
console.log(mainMiddleFn.pretty()); console.log(mainMiddleFn.pretty());
const interp = new MirInterpreter();
interp.eval(mainMiddleFn);

View File

@ -34,6 +34,7 @@ class FnLowerer {
lower(): Fn { lower(): Fn {
const ty = this.checker.check(this.stmt); const ty = this.checker.check(this.stmt);
this.lowerBlock(this.stmt.kind.body.as("Block")); this.lowerBlock(this.stmt.kind.body.as("Block"));
this.pushInst(Ty.Void, "Return", { source: this.makeVoid() });
return new Fn(this.stmt, ty, this.bbs); return new Fn(this.stmt, ty, this.bbs);
} }

113
src/mir_interpreter.ts Normal file
View File

@ -0,0 +1,113 @@
import * as mir from "./middle.ts";
export class MirInterpreter {
constructor() {}
eval(fn: mir.Fn) {
this.evalFn(fn, []);
}
private evalFn(fn: mir.Fn, args: Val[]): Val {
const regs = new Map<mir.Inst, Val>();
const locals: (Val | null)[] = [];
const localMap = new Map<mir.Inst, number>();
let bb = fn.bbs[0];
for (const inst of bb.insts) {
const k = inst.kind;
switch (k.tag) {
case "Error":
throw new Error();
case "Void":
case "Int":
case "Fn":
regs.set(inst, new Val(k));
continue;
case "Param":
regs.set(inst, args[k.idx]);
continue;
case "Call": {
const fn = regs.get(k.callee);
if (!fn || fn.kind.tag !== "Fn") {
throw new Error();
}
const args = k.args.map((arg) => regs.get(arg)!);
const val = this.evalFn(fn.kind.fn, args);
regs.set(inst, val);
continue;
}
case "AllocLocal":
localMap.set(inst, locals.length);
locals.push(null);
continue;
case "LocalLoad":
if (!localMap.has(k.source)) {
throw new Error();
}
if (locals[localMap.get(k.source)!] === null) {
throw new Error();
}
regs.set(inst, locals[localMap.get(k.source)!]!);
continue;
case "LocalStore":
if (!localMap.has(k.target)) {
throw new Error();
}
locals[localMap.get(k.target)!] = regs.get(k.source)!;
continue;
case "Return":
return regs.get(k.source)!;
case "Add": {
const left = regs.get(k.left)!;
const right = regs.get(k.right)!;
if (left.kind.tag === "Int" && right.kind.tag == "Int") {
regs.set(
inst,
new Val({
tag: "Int",
value: left.kind.value + right.kind.value,
}),
);
continue;
}
throw new Error();
}
case "DebugPrint":
console.log(
`debug: ${
k.args.map((a) => regs.get(a)!.pretty()).join(", ")
}`,
);
continue;
}
const _: never = k;
}
return Val.Void;
}
}
class Val {
constructor(
public kind: ValKind,
) {}
static Void = new Val({ tag: "Void" });
pretty() {
const k = this.kind;
switch (k.tag) {
case "Void":
return "void";
case "Int":
return `${k.value}`;
case "Fn":
return `<${k.fn.ty.pretty()}>`;
}
const _: never = k;
}
}
type ValKind =
| { tag: "Void" }
| { tag: "Int"; value: number }
| { tag: "Fn"; fn: mir.Fn };