import { Expr, Stmt } from "./ast.ts"; import { Ops } from "./mod.ts"; class Locals { private localIdCounter = 0; private symLocalMap: {[key: string]: number} = {} constructor (private parent?: Locals) {} defineSym(ident: string) { this.symLocalMap[ident] = this.localIdCounter this.localIdCounter++ } symLocalId(ident: string): number { if (ident in this.symLocalMap) { return this.symLocalMap[ident] } if (!this.parent) { throw new Error(`Could not find syn local id with ident ${ident}`) } else { return this.parent.symLocalId(ident) } } } export class Lowerer { private program: number[] = [] private locals = new Locals() lower(stmts: Stmt[]) { for(const stmt of stmts) { this.lowerStmt(stmt) } } lowerStmt(stmt: Stmt) { switch (stmt.kind.type) { case "error": case "break": case "return": case "fn": break; case "let": return this.lowerLetStmt(stmt); case "assign": case "expr": } throw new Error(`Unhandled stmt ${stmt.kind.type}`) } lowerLetStmt(stmt: Stmt) { if (stmt.kind.type !== "let") { throw new Error(); } this.lowerExpr(stmt.kind.value) this.locals.defineSym(stmt.kind.param.ident), this.program.push(Ops.StoreLocal) this.program.push(this.locals.symLocalId(stmt.kind.param.ident)) } lowerExpr(expr: Expr) { switch (expr.kind.type) { case "string": case "error": break; case "int": return this.lowerInt(expr) case "ident": case "group": case "field": case "index": case "call": case "unary": break; case "binary": return this.lowerBinaryExpr(expr) case "if": case "bool": case "null": case "loop": case "block": break; case "sym": return this.lowerSym(expr) } throw new Error(`Unhandled expr ${expr.kind.type}`) } lowerInt(expr: Expr) { if (expr.kind.type !== "int") { throw new Error(); } this.program.push(Ops.PushInt) this.program.push(expr.kind.value) } lowerSym(expr: Expr) { if (expr.kind.type !== "sym") { throw new Error(); } if (expr.kind.defType == "let") { this.program.push(Ops.LoadLocal) this.program.push(this.locals.symLocalId(expr.kind.ident)); return; } throw new Error(`Unhandled sym deftype ${expr.kind.defType}`); } lowerBinaryExpr(expr: Expr) { if (expr.kind.type !== "binary") { throw new Error(); } this.lowerExpr(expr.kind.left); this.lowerExpr(expr.kind.right); if (expr.vtype?.type == "int") { switch (expr.kind.binaryType) { case "+": this.program.push(Ops.Add); return case "*": this.program.push(Ops.Multiply); return case "==": case "-": case "/": case "!=": case "<": case ">": case "<=": case ">=": case "or": case "and": } throw new Error("Unhandled binary type") } } }