2024-12-15 00:07:36 +01:00
|
|
|
import { Builtins, Ops } from "./arch.ts";
|
2024-12-13 23:22:08 +01:00
|
|
|
import { Expr, Stmt } from "./ast.ts";
|
2024-12-10 14:36:41 +01:00
|
|
|
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
|
2024-12-10 23:30:15 +01:00
|
|
|
import { Assembler, Label } from "./assembler.ts";
|
2024-12-11 12:36:19 +01:00
|
|
|
import { vtypeToString } from "./vtype.ts";
|
2024-12-13 09:55:09 +01:00
|
|
|
import { Pos } from "./token.ts";
|
2024-12-10 09:03:03 +01:00
|
|
|
|
2024-12-15 00:07:36 +01:00
|
|
|
export type FnNamesMap = { [pc: number]: string };
|
|
|
|
|
2024-12-10 14:36:41 +01:00
|
|
|
export class Lowerer {
|
2024-12-17 02:10:11 +01:00
|
|
|
private program = Assembler.newRoot();
|
2024-12-10 14:36:41 +01:00
|
|
|
private locals: Locals = new LocalsFnRoot();
|
2024-12-15 00:07:36 +01:00
|
|
|
private fnStmtIdLabelMap: { [stmtId: number]: string } = {};
|
|
|
|
private fnLabelNameMap: { [name: string]: string } = {};
|
2024-12-15 04:24:07 +01:00
|
|
|
private returnStack: Label[] = [];
|
2024-12-10 23:30:15 +01:00
|
|
|
private breakStack: Label[] = [];
|
2024-12-10 09:03:03 +01:00
|
|
|
|
2024-12-13 09:55:09 +01:00
|
|
|
public constructor(private lastPos: Pos) {}
|
|
|
|
|
2024-12-10 14:36:41 +01:00
|
|
|
public lower(stmts: Stmt[]) {
|
2024-12-13 11:09:16 +01:00
|
|
|
this.addClearingSourceMap();
|
2024-12-17 02:10:11 +01:00
|
|
|
this.program.add(Ops.PushPtr, { label: "main" });
|
|
|
|
this.program.add(Ops.Call, 0);
|
|
|
|
this.program.add(Ops.PushPtr, { label: "_exit" });
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.Jump);
|
|
|
|
this.scoutFnHeaders(stmts);
|
2024-12-10 14:36:41 +01:00
|
|
|
for (const stmt of stmts) {
|
|
|
|
this.lowerStaticStmt(stmt);
|
2024-12-10 09:03:03 +01:00
|
|
|
}
|
2024-12-17 02:10:11 +01:00
|
|
|
this.program.setLabel({ label: "_exit" });
|
2024-12-13 09:55:09 +01:00
|
|
|
this.addSourceMap(this.lastPos);
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.Pop);
|
2024-12-10 10:39:12 +01:00
|
|
|
}
|
|
|
|
|
2024-12-15 00:07:36 +01:00
|
|
|
public finish(): { program: number[]; fnNames: FnNamesMap } {
|
|
|
|
const { program, locs } = this.program.assemble();
|
|
|
|
const fnNames: FnNamesMap = {};
|
|
|
|
for (const label in locs) {
|
|
|
|
if (label in this.fnLabelNameMap) {
|
|
|
|
fnNames[locs[label]] = this.fnLabelNameMap[label];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { program, fnNames };
|
2024-12-10 10:39:12 +01:00
|
|
|
}
|
|
|
|
|
2024-12-13 09:55:09 +01:00
|
|
|
private addSourceMap({ index, line, col }: Pos) {
|
|
|
|
this.program.add(Ops.SourceMap, index, line, col);
|
|
|
|
}
|
|
|
|
|
2024-12-13 11:09:16 +01:00
|
|
|
private addClearingSourceMap() {
|
|
|
|
this.program.add(Ops.SourceMap, 0, 1, 1);
|
|
|
|
}
|
|
|
|
|
2024-12-11 03:11:00 +01:00
|
|
|
private scoutFnHeaders(stmts: Stmt[]) {
|
|
|
|
for (const stmt of stmts) {
|
|
|
|
if (stmt.kind.type !== "fn") {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const label = stmt.kind.ident === "main"
|
|
|
|
? "main"
|
|
|
|
: `${stmt.kind.ident}_${stmt.id}`;
|
|
|
|
this.fnStmtIdLabelMap[stmt.id] = label;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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":
|
2024-12-15 04:24:07 +01:00
|
|
|
return this.lowerReturnStmt(stmt);
|
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);
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Pop);
|
2024-12-10 14:36:41 +01:00
|
|
|
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);
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.PushString, stmt.kind.subject.kind.value);
|
|
|
|
this.program.add(Ops.Builtin, Builtins.StructSet);
|
2024-12-10 23:30:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case "index": {
|
|
|
|
this.lowerExpr(stmt.kind.subject.kind.subject);
|
|
|
|
this.lowerExpr(stmt.kind.subject.kind.value);
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Builtin, Builtins.ArraySet);
|
2024-12-10 23:30:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case "sym": {
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(
|
|
|
|
Ops.StoreLocal,
|
2024-12-10 23:30:15 +01:00
|
|
|
this.locals.symId(stmt.kind.subject.kind.sym.ident),
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-15 04:24:07 +01:00
|
|
|
private lowerReturnStmt(stmt: Stmt) {
|
|
|
|
if (stmt.kind.type !== "return") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
if (stmt.kind.expr) {
|
|
|
|
this.lowerExpr(stmt.kind.expr);
|
|
|
|
}
|
|
|
|
this.addClearingSourceMap();
|
|
|
|
this.program.add(Ops.PushPtr, this.returnStack.at(-1)!);
|
|
|
|
this.program.add(Ops.Jump);
|
|
|
|
}
|
|
|
|
|
2024-12-10 23:30:15 +01:00
|
|
|
private lowerBreakStmt(stmt: Stmt) {
|
|
|
|
if (stmt.kind.type !== "break") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
if (stmt.kind.expr) {
|
|
|
|
this.lowerExpr(stmt.kind.expr);
|
|
|
|
}
|
2024-12-13 11:09:16 +01:00
|
|
|
this.addClearingSourceMap();
|
2024-12-13 06:09:10 +01:00
|
|
|
this.program.add(Ops.PushPtr, this.breakStack.at(-1)!);
|
|
|
|
this.program.add(Ops.Jump);
|
2024-12-10 23:30:15 +01:00
|
|
|
}
|
|
|
|
|
2024-12-10 14:36:41 +01:00
|
|
|
private lowerFnStmt(stmt: Stmt) {
|
|
|
|
if (stmt.kind.type !== "fn") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
2024-12-11 03:11:00 +01:00
|
|
|
const label = stmt.kind.ident === "main"
|
|
|
|
? "main"
|
|
|
|
: `${stmt.kind.ident}_${stmt.id}`;
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.setLabel({ label });
|
2024-12-15 00:07:36 +01:00
|
|
|
this.fnLabelNameMap[label] = stmt.kind.ident;
|
2024-12-13 10:26:34 +01:00
|
|
|
this.addSourceMap(stmt.pos);
|
2024-12-11 00:03:19 +01:00
|
|
|
|
2024-12-10 14:36:41 +01:00
|
|
|
const outerLocals = this.locals;
|
2024-12-11 03:11:00 +01:00
|
|
|
const fnRoot = new LocalsFnRoot(outerLocals);
|
2024-12-10 14:36:41 +01:00
|
|
|
const outerProgram = this.program;
|
|
|
|
|
2024-12-15 04:24:07 +01:00
|
|
|
const returnLabel = this.program.makeLabel();
|
|
|
|
this.returnStack.push(returnLabel);
|
|
|
|
|
2024-12-17 02:10:11 +01:00
|
|
|
this.program = outerProgram.fork();
|
2024-12-11 03:11:00 +01:00
|
|
|
this.locals = fnRoot;
|
2024-12-10 14:36:41 +01:00
|
|
|
for (const { ident } of stmt.kind.params) {
|
|
|
|
this.locals.allocSym(ident);
|
|
|
|
}
|
2024-12-11 13:37:26 +01:00
|
|
|
if (stmt.kind.anno?.ident === "builtin") {
|
2024-12-13 09:55:09 +01:00
|
|
|
this.lowerFnBuiltinBody(stmt.kind.anno.values);
|
2024-12-15 04:24:07 +01:00
|
|
|
} else if (stmt.kind.anno?.ident === "remainder") {
|
|
|
|
this.program.add(Ops.Remainder);
|
2024-12-11 13:37:26 +01:00
|
|
|
} else {
|
|
|
|
this.lowerExpr(stmt.kind.body);
|
|
|
|
}
|
2024-12-11 03:11:00 +01:00
|
|
|
this.locals = outerLocals;
|
|
|
|
|
2024-12-11 12:36:19 +01:00
|
|
|
const localAmount = fnRoot.stackReserved() -
|
|
|
|
stmt.kind.params.length;
|
|
|
|
for (let i = 0; i < localAmount; ++i) {
|
2024-12-11 03:11:00 +01:00
|
|
|
outerProgram.add(Ops.PushNull);
|
|
|
|
}
|
|
|
|
|
2024-12-17 02:10:11 +01:00
|
|
|
this.returnStack.pop();
|
|
|
|
this.program.setLabel(returnLabel);
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Return);
|
2024-12-10 14:36:41 +01:00
|
|
|
|
2024-12-17 02:10:11 +01:00
|
|
|
outerProgram.join(this.program);
|
2024-12-10 14:36:41 +01:00
|
|
|
this.program = outerProgram;
|
|
|
|
}
|
|
|
|
|
2024-12-13 09:55:09 +01:00
|
|
|
private lowerFnBuiltinBody(annoArgs: Expr[]) {
|
|
|
|
if (annoArgs.length !== 1) {
|
|
|
|
throw new Error("invalid # of arguments to builtin annotation");
|
|
|
|
}
|
|
|
|
const anno = annoArgs[0];
|
|
|
|
if (anno.kind.type !== "ident") {
|
|
|
|
throw new Error(
|
|
|
|
`unexpected argument type '${anno.kind.type}' expected 'ident'`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const value = anno.kind.value;
|
|
|
|
const builtin = Object.entries(Builtins).find((entry) =>
|
|
|
|
entry[0] === value
|
|
|
|
)?.[1];
|
|
|
|
if (builtin === undefined) {
|
|
|
|
throw new Error(
|
|
|
|
`unrecognized builtin '${value}'`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
this.program.add(Ops.Builtin, builtin);
|
|
|
|
}
|
|
|
|
|
2024-12-10 14:36:41 +01:00
|
|
|
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);
|
2024-12-11 00:03:19 +01:00
|
|
|
this.locals.allocSym(stmt.kind.param.ident);
|
|
|
|
this.program.add(
|
|
|
|
Ops.StoreLocal,
|
|
|
|
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":
|
2024-12-15 04:24:07 +01:00
|
|
|
return this.lowerBoolExpr(expr);
|
2024-12-10 14:36:41 +01:00
|
|
|
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-13 06:09:10 +01:00
|
|
|
return void this.lowerExpr(expr.kind.expr);
|
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-13 23:22:08 +01:00
|
|
|
return this.lowerIndexExpr(expr);
|
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":
|
2024-12-13 06:09:10 +01:00
|
|
|
return this.lowerUnaryExpr(expr);
|
2024-12-10 09:03:03 +01:00
|
|
|
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}'`);
|
|
|
|
}
|
|
|
|
|
2024-12-13 16:03:01 +01:00
|
|
|
private lowerIndexExpr(expr: Expr) {
|
|
|
|
if (expr.kind.type !== "index") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
2024-12-13 23:22:08 +01:00
|
|
|
this.lowerExpr(expr.kind.subject);
|
|
|
|
this.lowerExpr(expr.kind.value);
|
2024-12-13 16:03:01 +01:00
|
|
|
|
|
|
|
if (expr.kind.subject.vtype?.type == "array") {
|
|
|
|
this.program.add(Ops.Builtin, Builtins.ArrayAt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (expr.kind.subject.vtype?.type == "string") {
|
|
|
|
this.program.add(Ops.Builtin, Builtins.StringCharAt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
throw new Error(`unhandled index subject type '${expr.kind.subject}'`);
|
|
|
|
}
|
|
|
|
|
2024-12-10 14:36:41 +01:00
|
|
|
private lowerSymExpr(expr: Expr) {
|
|
|
|
if (expr.kind.type !== "sym") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
if (expr.kind.sym.type === "let") {
|
2024-12-13 09:55:09 +01:00
|
|
|
const symId = this.locals.symId(expr.kind.ident);
|
|
|
|
this.program.add(Ops.LoadLocal, symId);
|
2024-12-10 14:36:41 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (expr.kind.sym.type === "fn_param") {
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(
|
2024-12-10 14:36:41 +01:00
|
|
|
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") {
|
2024-12-11 00:03:19 +01:00
|
|
|
const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id];
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.PushPtr, { label });
|
2024-12-10 14:36:41 +01:00
|
|
|
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-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.PushInt, expr.kind.value);
|
2024-12-10 09:03:03 +01:00
|
|
|
}
|
|
|
|
|
2024-12-15 04:24:07 +01:00
|
|
|
private lowerBoolExpr(expr: Expr) {
|
|
|
|
if (expr.kind.type !== "bool") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
this.program.add(Ops.PushBool, expr.kind.value);
|
|
|
|
}
|
|
|
|
|
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-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.PushString, expr.kind.value);
|
2024-12-10 09:03:03 +01:00
|
|
|
}
|
|
|
|
|
2024-12-13 06:09:10 +01:00
|
|
|
private lowerUnaryExpr(expr: Expr) {
|
|
|
|
if (expr.kind.type !== "unary") {
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
this.lowerExpr(expr.kind.subject);
|
|
|
|
const vtype = expr.kind.subject.vtype!;
|
|
|
|
if (vtype.type === "bool") {
|
|
|
|
switch (expr.kind.unaryType) {
|
|
|
|
case "not":
|
|
|
|
this.program.add(Ops.Not);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
2024-12-16 21:56:36 +01:00
|
|
|
if (vtype.type === "int") {
|
|
|
|
switch (expr.kind.unaryType) {
|
|
|
|
case "-": {
|
|
|
|
this.program.add(Ops.PushInt, 0);
|
2024-12-17 02:10:11 +01:00
|
|
|
this.program.add(Ops.Swap);
|
2024-12-16 21:56:36 +01:00
|
|
|
this.program.add(Ops.Subtract);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
2024-12-13 06:09:10 +01:00
|
|
|
throw new Error(
|
|
|
|
`unhandled unary` +
|
|
|
|
` '${vtypeToString(expr.vtype!)}' aka. ` +
|
|
|
|
` ${expr.kind.unaryType}` +
|
|
|
|
` '${vtypeToString(expr.kind.subject.vtype!)}'`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|
2024-12-17 02:10:11 +01:00
|
|
|
const vtype = expr.kind.left.vtype!;
|
|
|
|
if (vtype.type === "bool") {
|
|
|
|
if (["or", "and"].includes(expr.kind.binaryType)) {
|
|
|
|
const shortCircuitLabel = this.program.makeLabel();
|
|
|
|
this.lowerExpr(expr.kind.left);
|
|
|
|
this.program.add(Ops.Duplicate);
|
|
|
|
if (expr.kind.binaryType === "and") {
|
|
|
|
this.program.add(Ops.Not);
|
|
|
|
}
|
|
|
|
this.program.add(Ops.PushPtr, shortCircuitLabel);
|
|
|
|
this.program.add(Ops.JumpIfTrue);
|
|
|
|
this.program.add(Ops.Pop);
|
|
|
|
this.lowerExpr(expr.kind.right);
|
|
|
|
this.program.setLabel(shortCircuitLabel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2024-12-10 09:03:03 +01:00
|
|
|
this.lowerExpr(expr.kind.left);
|
|
|
|
this.lowerExpr(expr.kind.right);
|
2024-12-10 23:30:15 +01:00
|
|
|
if (vtype.type === "int") {
|
2024-12-10 09:03:03 +01:00
|
|
|
switch (expr.kind.binaryType) {
|
|
|
|
case "+":
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Add);
|
2024-12-10 10:39:12 +01:00
|
|
|
return;
|
2024-12-12 16:07:59 +01:00
|
|
|
case "-":
|
|
|
|
this.program.add(Ops.Subtract);
|
|
|
|
return;
|
2024-12-10 09:03:03 +01:00
|
|
|
case "*":
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Multiply);
|
2024-12-10 10:39:12 +01:00
|
|
|
return;
|
2024-12-17 02:10:11 +01:00
|
|
|
case "/":
|
|
|
|
this.program.add(Ops.Multiply);
|
|
|
|
return;
|
2024-12-10 09:03:03 +01:00
|
|
|
case "==":
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Equal);
|
2024-12-10 23:30:15 +01:00
|
|
|
return;
|
2024-12-16 21:56:36 +01:00
|
|
|
case "!=":
|
|
|
|
this.program.add(Ops.Equal);
|
|
|
|
this.program.add(Ops.Not);
|
|
|
|
return;
|
2024-12-13 06:09:10 +01:00
|
|
|
case "<":
|
|
|
|
this.program.add(Ops.LessThan);
|
|
|
|
return;
|
2024-12-17 02:10:11 +01:00
|
|
|
case ">":
|
|
|
|
this.program.add(Ops.Swap);
|
|
|
|
this.program.add(Ops.LessThan);
|
|
|
|
return;
|
|
|
|
case "<=":
|
|
|
|
this.program.add(Ops.Swap);
|
|
|
|
this.program.add(Ops.LessThan);
|
|
|
|
this.program.add(Ops.Not);
|
|
|
|
return;
|
2024-12-10 09:03:03 +01:00
|
|
|
case ">=":
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.LessThan);
|
|
|
|
this.program.add(Ops.Not);
|
2024-12-10 23:30:15 +01:00
|
|
|
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-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Builtin, Builtins.StringConcat);
|
2024-12-10 23:30:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (expr.kind.binaryType === "==") {
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Builtin, Builtins.StringEqual);
|
2024-12-10 23:30:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (expr.kind.binaryType === "!=") {
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Builtin, Builtins.StringEqual);
|
|
|
|
this.program.add(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);
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.Call, expr.kind.args.length);
|
2024-12-10 14:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.Not);
|
2024-12-13 11:09:16 +01:00
|
|
|
this.addClearingSourceMap();
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.PushPtr, falseLabel);
|
|
|
|
this.program.add(Ops.JumpIfTrue);
|
2024-12-10 15:45:19 +01:00
|
|
|
|
2024-12-13 10:26:34 +01:00
|
|
|
this.addSourceMap(expr.kind.truthy.pos);
|
2024-12-10 14:36:41 +01:00
|
|
|
this.lowerExpr(expr.kind.truthy);
|
2024-12-10 15:45:19 +01:00
|
|
|
|
2024-12-13 11:09:16 +01:00
|
|
|
this.addClearingSourceMap();
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.PushPtr, doneLabel);
|
|
|
|
this.program.add(Ops.Jump);
|
2024-12-10 15:45:19 +01:00
|
|
|
|
|
|
|
this.program.setLabel(falseLabel);
|
|
|
|
|
2024-12-10 23:30:15 +01:00
|
|
|
if (expr.kind.falsy) {
|
2024-12-13 10:33:51 +01:00
|
|
|
this.addSourceMap(expr.kind.falsy.pos);
|
|
|
|
this.lowerExpr(expr.kind.falsy);
|
2024-12-11 12:36:19 +01:00
|
|
|
} else {
|
|
|
|
this.program.add(Ops.PushNull);
|
2024-12-10 23:30:15 +01:00
|
|
|
}
|
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();
|
|
|
|
}
|
2024-12-13 12:12:16 +01:00
|
|
|
const continueLabel = this.program.makeLabel();
|
2024-12-10 23:30:15 +01:00
|
|
|
const breakLabel = this.program.makeLabel();
|
|
|
|
|
|
|
|
this.breakStack.push(breakLabel);
|
|
|
|
|
2024-12-13 12:12:16 +01:00
|
|
|
this.program.setLabel(continueLabel);
|
2024-12-13 10:26:34 +01:00
|
|
|
this.addSourceMap(expr.kind.body.pos);
|
2024-12-10 23:30:15 +01:00
|
|
|
this.lowerExpr(expr.kind.body);
|
2024-12-13 06:09:10 +01:00
|
|
|
this.program.add(Ops.Pop);
|
2024-12-13 11:09:16 +01:00
|
|
|
this.addClearingSourceMap();
|
2024-12-13 12:12:16 +01:00
|
|
|
this.program.add(Ops.PushPtr, continueLabel);
|
2024-12-11 03:11:00 +01:00
|
|
|
this.program.add(Ops.Jump);
|
2024-12-10 23:30:15 +01:00
|
|
|
this.program.setLabel(breakLabel);
|
|
|
|
if (expr.vtype!.type === "null") {
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(Ops.PushNull);
|
2024-12-10 23:30:15 +01:00
|
|
|
}
|
|
|
|
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);
|
2024-12-11 03:11:00 +01:00
|
|
|
this.scoutFnHeaders(expr.kind.stmts);
|
2024-12-10 14:36:41 +01:00
|
|
|
for (const stmt of expr.kind.stmts) {
|
2024-12-13 12:12:16 +01:00
|
|
|
this.addSourceMap(stmt.pos);
|
2024-12-10 14:36:41 +01:00
|
|
|
this.lowerStmt(stmt);
|
|
|
|
}
|
|
|
|
if (expr.kind.expr) {
|
2024-12-13 12:12:16 +01:00
|
|
|
this.addSourceMap(expr.kind.expr.pos);
|
2024-12-10 14:36:41 +01:00
|
|
|
this.lowerExpr(expr.kind.expr);
|
|
|
|
} else {
|
2024-12-11 00:03:19 +01:00
|
|
|
this.program.add(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
|
|
|
}
|