142 lines
3.7 KiB
TypeScript
142 lines
3.7 KiB
TypeScript
|
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")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|