diff --git a/program.ethlang b/program.ethlang index 8ad6b2d..b43b843 100644 --- a/program.ethlang +++ b/program.ethlang @@ -6,9 +6,8 @@ fn add(a: int, b: int) -> int fn main() { - let sum = add(2, 3); + let sum: void = add(2, 3); print_int(sum); } -// vim: syntax=rust diff --git a/src/main.ts b/src/main.ts index 9461669..e00a6f7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,7 @@ import * as ast from "./ast.ts"; import * as front from "./front.ts"; import * as middle from "./middle.ts"; +import { MirInterpreter } from "./mir_interpreter.ts"; const filename = Deno.args[0]; const text = await Deno.readTextFile(filename); @@ -34,3 +35,6 @@ const m = new middle.MiddleLowerer(resols, checker); const mainMiddleFn = m.lowerFn(mainFn); console.log(mainMiddleFn.pretty()); + +const interp = new MirInterpreter(); +interp.eval(mainMiddleFn); diff --git a/src/middle.ts b/src/middle.ts index 0768ac7..0bd8d24 100644 --- a/src/middle.ts +++ b/src/middle.ts @@ -34,6 +34,7 @@ class FnLowerer { lower(): Fn { const ty = this.checker.check(this.stmt); this.lowerBlock(this.stmt.kind.body.as("Block")); + this.pushInst(Ty.Void, "Return", { source: this.makeVoid() }); return new Fn(this.stmt, ty, this.bbs); } diff --git a/src/mir_interpreter.ts b/src/mir_interpreter.ts new file mode 100644 index 0000000..e049c4a --- /dev/null +++ b/src/mir_interpreter.ts @@ -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(); + const locals: (Val | null)[] = []; + const localMap = new Map(); + + 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 };