slige/compiler/lowerer.ts

349 lines
10 KiB
TypeScript
Raw Normal View History

2024-12-10 14:36:41 +01:00
import { Builtins } from "./arch.ts";
2024-12-10 10:39:12 +01:00
import { BinaryType, Expr, Stmt } from "./ast.ts";
2024-12-10 14:36:41 +01:00
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
2024-12-10 09:03:03 +01:00
import { Ops } from "./mod.ts";
2024-12-10 23:30:15 +01:00
import { Assembler, Label } from "./assembler.ts";
2024-12-10 21:42:15 +01:00
import { VType, vtypeToString } from "./vtype.ts";
2024-12-10 09:03:03 +01:00
2024-12-10 14:36:41 +01:00
export class Lowerer {
2024-12-10 15:45:19 +01:00
private program = new Assembler();
2024-12-10 14:36:41 +01:00
private locals: Locals = new LocalsFnRoot();
private fnStmtIdAddrMap: { [key: number]: number } = {};
2024-12-10 23:30:15 +01:00
private breakStack: Label[] = [];
2024-12-10 09:03:03 +01:00
2024-12-10 14:36:41 +01:00
public lower(stmts: Stmt[]) {
for (const stmt of stmts) {
this.lowerStaticStmt(stmt);
2024-12-10 09:03:03 +01:00
}
2024-12-10 10:39:12 +01:00
}
2024-12-10 14:36:41 +01:00
public finish(): number[] {
2024-12-10 15:45:19 +01:00
return this.program.assemble();
2024-12-10 10:39:12 +01:00
}
2024-12-10 14:36:41 +01:00
private lowerStaticStmt(stmt: Stmt) {
switch (stmt.kind.type) {
case "fn":
return this.lowerFnStmt(stmt);
case "error":
case "break":
case "return":
case "let":
case "assign":
case "expr":
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
throw new Error(`unhandled static statement '${stmt.kind.type}'`);
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
private lowerStmt(stmt: Stmt) {
2024-12-10 09:03:03 +01:00
switch (stmt.kind.type) {
case "error":
2024-12-10 23:30:15 +01:00
break;
2024-12-10 09:03:03 +01:00
case "break":
2024-12-10 23:30:15 +01:00
return this.lowerBreakStmt(stmt);
2024-12-10 09:03:03 +01:00
case "return":
break;
2024-12-10 14:36:41 +01:00
case "fn":
return this.lowerFnStmt(stmt);
2024-12-10 09:03:03 +01:00
case "let":
return this.lowerLetStmt(stmt);
case "assign":
2024-12-10 23:30:15 +01:00
return this.lowerAssignStmt(stmt);
2024-12-10 09:03:03 +01:00
case "expr":
2024-12-10 14:36:41 +01:00
this.lowerExpr(stmt.kind.expr);
this.program.push(Ops.Pop);
return;
2024-12-10 10:39:12 +01:00
}
2024-12-10 14:36:41 +01:00
throw new Error(`unhandled stmt '${stmt.kind.type}'`);
2024-12-10 09:03:03 +01:00
}
2024-12-10 23:30:15 +01:00
private lowerAssignStmt(stmt: Stmt) {
if (stmt.kind.type !== "assign") {
throw new Error();
}
this.lowerExpr(stmt.kind.value);
switch (stmt.kind.subject.kind.type) {
case "field": {
this.lowerExpr(stmt.kind.subject.kind.subject);
this.addPushStringOp(stmt.kind.subject.kind.value);
this.program.addOp(Ops.Builtin);
this.program.addLit(Builtins.StructSet);
return;
}
case "index": {
this.lowerExpr(stmt.kind.subject.kind.subject);
this.lowerExpr(stmt.kind.subject.kind.value);
this.program.addOp(Ops.Builtin);
this.program.addLit(Builtins.ArraySet);
return;
}
case "sym": {
this.program.addOp(Ops.StoreLocal);
this.program.addLit(
this.locals.symId(stmt.kind.subject.kind.sym.ident),
);
return;
}
default:
throw new Error();
}
}
private lowerBreakStmt(stmt: Stmt) {
if (stmt.kind.type !== "break") {
throw new Error();
}
if (stmt.kind.expr) {
this.lowerExpr(stmt.kind.expr);
}
this.program.addOp(Ops.Jump);
this.program.addRef(this.breakStack.at(-1)!);
}
2024-12-10 14:36:41 +01:00
private lowerFnStmt(stmt: Stmt) {
if (stmt.kind.type !== "fn") {
throw new Error();
}
const outerLocals = this.locals;
this.locals = new LocalsFnRoot(outerLocals);
const outerProgram = this.program;
2024-12-10 15:45:19 +01:00
this.program = new Assembler();
2024-12-10 14:36:41 +01:00
for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident);
this.program.push(
Ops.StoreLocal,
this.locals.symId(ident),
);
}
this.lowerExpr(stmt.kind.body);
this.program.push(Ops.Return);
this.locals = outerLocals;
2024-12-10 15:45:19 +01:00
outerProgram.concat(this.program);
2024-12-10 14:36:41 +01:00
this.program = outerProgram;
}
private lowerLetStmt(stmt: Stmt) {
2024-12-10 09:03:03 +01:00
if (stmt.kind.type !== "let") {
throw new Error();
}
2024-12-10 10:39:12 +01:00
this.lowerExpr(stmt.kind.value);
this.locals.allocSym(stmt.kind.param.ident),
this.program.push(Ops.StoreLocal);
this.program.push(this.locals.symId(stmt.kind.param.ident));
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
private lowerExpr(expr: Expr) {
2024-12-10 09:03:03 +01:00
switch (expr.kind.type) {
case "error":
break;
2024-12-10 14:36:41 +01:00
case "sym":
return this.lowerSymExpr(expr);
case "null":
break;
2024-12-10 09:03:03 +01:00
case "int":
2024-12-10 14:36:41 +01:00
return this.lowerIntExpr(expr);
case "bool":
break;
case "string":
return this.lowerStringExpr(expr);
2024-12-10 09:03:03 +01:00
case "ident":
2024-12-10 14:36:41 +01:00
break;
2024-12-10 09:03:03 +01:00
case "group":
2024-12-10 14:36:41 +01:00
break;
2024-12-10 09:03:03 +01:00
case "field":
2024-12-10 14:36:41 +01:00
break;
2024-12-10 09:03:03 +01:00
case "index":
2024-12-10 14:36:41 +01:00
break;
2024-12-10 09:03:03 +01:00
case "call":
2024-12-10 14:36:41 +01:00
return this.lowerCallExpr(expr);
2024-12-10 09:03:03 +01:00
case "unary":
break;
case "binary":
2024-12-10 10:39:12 +01:00
return this.lowerBinaryExpr(expr);
2024-12-10 09:03:03 +01:00
case "if":
2024-12-10 14:36:41 +01:00
return this.lowerIfExpr(expr);
2024-12-10 09:03:03 +01:00
case "loop":
2024-12-10 23:30:15 +01:00
return this.lowerLoopExpr(expr);
2024-12-10 14:36:41 +01:00
case "block":
return this.lowerBlockExpr(expr);
}
throw new Error(`unhandled expr '${expr.kind.type}'`);
}
private lowerSymExpr(expr: Expr) {
if (expr.kind.type !== "sym") {
throw new Error();
}
if (expr.kind.sym.type === "let") {
this.program.push(
Ops.LoadLocal,
this.locals.symId(expr.kind.ident),
);
return;
}
if (expr.kind.sym.type === "fn_param") {
this.program.push(
Ops.LoadLocal,
this.locals.symId(expr.kind.ident),
);
return;
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
if (expr.kind.sym.type === "fn") {
const addr = this.fnStmtIdAddrMap[expr.kind.sym.stmt.id];
this.program.push(Ops.PushPtr, addr);
return;
}
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
private lowerIntExpr(expr: Expr) {
2024-12-10 09:03:03 +01:00
if (expr.kind.type !== "int") {
throw new Error();
}
2024-12-10 14:36:41 +01:00
this.program.push(Ops.PushInt, expr.kind.value);
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
private lowerStringExpr(expr: Expr) {
if (expr.kind.type !== "string") {
2024-12-10 09:03:03 +01:00
throw new Error();
}
2024-12-10 23:30:15 +01:00
this.addPushStringOp(expr.kind.value);
}
private addPushStringOp(value: string) {
this.program.addOp(Ops.PushString);
for (let i = 0; i < value.length; ++i) {
this.program.addLit(value.charCodeAt(i));
2024-12-10 09:03:03 +01:00
}
}
2024-12-10 14:36:41 +01:00
private lowerBinaryExpr(expr: Expr) {
2024-12-10 09:03:03 +01:00
if (expr.kind.type !== "binary") {
throw new Error();
}
this.lowerExpr(expr.kind.left);
this.lowerExpr(expr.kind.right);
2024-12-10 23:30:15 +01:00
const vtype = expr.kind.left.vtype!;
if (vtype.type === "int") {
2024-12-10 09:03:03 +01:00
switch (expr.kind.binaryType) {
case "+":
2024-12-10 23:30:15 +01:00
this.program.addOp(Ops.Add);
2024-12-10 10:39:12 +01:00
return;
2024-12-10 09:03:03 +01:00
case "*":
2024-12-10 23:30:15 +01:00
this.program.addOp(Ops.Multiply);
2024-12-10 10:39:12 +01:00
return;
2024-12-10 09:03:03 +01:00
case "==":
2024-12-10 23:30:15 +01:00
this.program.addOp(Ops.Equal);
return;
2024-12-10 09:03:03 +01:00
case ">=":
2024-12-10 23:30:15 +01:00
this.program.addOp(Ops.LessThan);
this.program.addOp(Ops.Not);
return;
default:
2024-12-10 09:03:03 +01:00
}
}
2024-12-10 23:30:15 +01:00
if (vtype.type === "string") {
2024-12-10 14:36:41 +01:00
if (expr.kind.binaryType === "+") {
2024-12-10 23:30:15 +01:00
this.program.push(Ops.Builtin, Builtins.StringConcat);
return;
}
if (expr.kind.binaryType === "==") {
this.program.push(Ops.Builtin, Builtins.StringEqual);
return;
}
if (expr.kind.binaryType === "!=") {
this.program.push(Ops.Builtin, Builtins.StringEqual, Ops.Not);
2024-12-10 14:36:41 +01:00
return;
}
}
2024-12-10 10:39:12 +01:00
throw new Error(
2024-12-10 14:36:41 +01:00
`unhandled binaryType` +
2024-12-10 23:30:15 +01:00
` '${vtypeToString(expr.vtype!)}' aka. ` +
2024-12-10 14:36:41 +01:00
` '${vtypeToString(expr.kind.left.vtype!)}'` +
` ${expr.kind.binaryType}` +
` '${vtypeToString(expr.kind.left.vtype!)}'`,
2024-12-10 10:39:12 +01:00
);
2024-12-10 09:03:03 +01:00
}
2024-12-10 14:36:41 +01:00
private lowerCallExpr(expr: Expr) {
if (expr.kind.type !== "call") {
throw new Error();
}
for (const arg of expr.kind.args) {
this.lowerExpr(arg);
}
this.lowerExpr(expr.kind.subject);
}
private lowerIfExpr(expr: Expr) {
if (expr.kind.type !== "if") {
throw new Error();
}
2024-12-10 15:45:19 +01:00
const falseLabel = this.program.makeLabel();
const doneLabel = this.program.makeLabel();
2024-12-10 14:36:41 +01:00
this.lowerExpr(expr.kind.cond);
2024-12-10 15:45:19 +01:00
this.program.push(Ops.Not, Ops.JumpIfTrue);
this.program.addRef(falseLabel);
2024-12-10 14:36:41 +01:00
this.lowerExpr(expr.kind.truthy);
2024-12-10 15:45:19 +01:00
this.program.push(Ops.Jump);
this.program.addRef(doneLabel);
this.program.setLabel(falseLabel);
2024-12-10 23:30:15 +01:00
if (expr.kind.falsy) {
this.lowerExpr(expr.kind.falsy!);
}
2024-12-10 15:45:19 +01:00
this.program.setLabel(doneLabel);
2024-12-10 14:36:41 +01:00
}
2024-12-10 23:30:15 +01:00
private lowerLoopExpr(expr: Expr) {
if (expr.kind.type !== "loop") {
throw new Error();
}
const contineLabel = this.program.makeLabel();
const breakLabel = this.program.makeLabel();
this.breakStack.push(breakLabel);
this.program.setLabel(contineLabel);
this.lowerExpr(expr.kind.body);
this.program.addOp(Ops.Jump);
this.program.addRef(breakLabel);
this.program.setLabel(breakLabel);
if (expr.vtype!.type === "null") {
this.program.addOp(Ops.PushNull);
}
this.breakStack.pop();
}
2024-12-10 14:36:41 +01:00
private lowerBlockExpr(expr: Expr) {
if (expr.kind.type !== "block") {
throw new Error();
}
const outerLocals = this.locals;
this.locals = new LocalLeaf(this.locals);
for (const stmt of expr.kind.stmts) {
this.lowerStmt(stmt);
}
if (expr.kind.expr) {
this.lowerExpr(expr.kind.expr);
} else {
2024-12-10 23:30:15 +01:00
this.program.addOp(Ops.PushNull);
2024-12-10 14:36:41 +01:00
}
this.locals = outerLocals;
}
2024-12-10 23:30:15 +01:00
public printProgram() {
this.program.printProgram();
}
2024-12-10 10:39:12 +01:00
}