Compare commits
No commits in common. "916c2376a0723a8777634649cd3096931944bcdc" and "cbb5a02bd9e31a1645e2e9d9f57d246ee75818fb" have entirely different histories.
916c2376a0
...
cbb5a02bd9
143
compiler/arch.ts
143
compiler/arch.ts
@ -5,121 +5,38 @@ export type Ins = Ops | number;
|
|||||||
|
|
||||||
export type Ops = typeof Ops;
|
export type Ops = typeof Ops;
|
||||||
export const Ops = {
|
export const Ops = {
|
||||||
Nop: 0x00,
|
Nop: 0,
|
||||||
PushNull: 0x01,
|
PushNull: 1,
|
||||||
PushInt: 0x02,
|
PushInt: 2,
|
||||||
PushBool: 0x03,
|
PushBool: 3,
|
||||||
PushString: 0x04,
|
PushString: 4,
|
||||||
PushPtr: 0x05,
|
PushPtr: 5,
|
||||||
Pop: 0x06,
|
Pop: 6,
|
||||||
ReserveStatic: 0x07,
|
LoadLocal: 7,
|
||||||
LoadStatic: 0x08,
|
StoreLocal: 8,
|
||||||
StoreStatic: 0x09,
|
Call: 9,
|
||||||
LoadLocal: 0x0a,
|
Return: 10,
|
||||||
StoreLocal: 0x0b,
|
Jump: 11,
|
||||||
Call: 0x0c,
|
JumpIfTrue: 12,
|
||||||
Return: 0x0d,
|
Add: 13,
|
||||||
Jump: 0x0e,
|
Subtract: 14,
|
||||||
JumpIfTrue: 0x0f,
|
Multiply: 15,
|
||||||
Builtin: 0x10,
|
Divide: 16,
|
||||||
Add: 0x20,
|
Remainder: 17,
|
||||||
Subtract: 0x21,
|
Equal: 18,
|
||||||
Multiply: 0x22,
|
LessThan: 19,
|
||||||
Divide: 0x23,
|
And: 20,
|
||||||
Remainder: 0x24,
|
Or: 21,
|
||||||
Equal: 0x25,
|
Xor: 22,
|
||||||
LessThan: 0x26,
|
Not: 23,
|
||||||
And: 0x27,
|
SourceMap: 24,
|
||||||
Or: 0x28,
|
Builtin: 25,
|
||||||
Xor: 0x29,
|
ReserveStatics: 26,
|
||||||
Not: 0x2a,
|
LoadStatic: 27,
|
||||||
SourceMap: 0x30,
|
StoreStatic: 28,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type Builtins = typeof Builtins;
|
export type Builtins = typeof Builtins;
|
||||||
export const Builtins = {
|
export const Builtins = {
|
||||||
StringConcat: 0x10,
|
StringAdd: 0,
|
||||||
StringEqual: 0x11,
|
|
||||||
ArraySet: 0x20,
|
|
||||||
StructSet: 0x30,
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export function opToString(op: number): string {
|
|
||||||
switch (op) {
|
|
||||||
case Ops.Nop:
|
|
||||||
return "Nop";
|
|
||||||
case Ops.PushNull:
|
|
||||||
return "PushNull";
|
|
||||||
case Ops.PushInt:
|
|
||||||
return "PushInt";
|
|
||||||
case Ops.PushBool:
|
|
||||||
return "PushBool";
|
|
||||||
case Ops.PushString:
|
|
||||||
return "PushString";
|
|
||||||
case Ops.PushPtr:
|
|
||||||
return "PushPtr";
|
|
||||||
case Ops.Pop:
|
|
||||||
return "Pop";
|
|
||||||
case Ops.ReserveStatic:
|
|
||||||
return "ReserveStatic";
|
|
||||||
case Ops.LoadStatic:
|
|
||||||
return "LoadStatic";
|
|
||||||
case Ops.StoreStatic:
|
|
||||||
return "StoreStatic";
|
|
||||||
case Ops.LoadLocal:
|
|
||||||
return "LoadLocal";
|
|
||||||
case Ops.StoreLocal:
|
|
||||||
return "StoreLocal";
|
|
||||||
case Ops.Call:
|
|
||||||
return "Call";
|
|
||||||
case Ops.Return:
|
|
||||||
return "Return";
|
|
||||||
case Ops.Jump:
|
|
||||||
return "Jump";
|
|
||||||
case Ops.JumpIfTrue:
|
|
||||||
return "JumpIfTrue";
|
|
||||||
case Ops.Builtin:
|
|
||||||
return "Builtin";
|
|
||||||
case Ops.Add:
|
|
||||||
return "Add";
|
|
||||||
case Ops.Subtract:
|
|
||||||
return "Subtract";
|
|
||||||
case Ops.Multiply:
|
|
||||||
return "Multiply";
|
|
||||||
case Ops.Divide:
|
|
||||||
return "Divide";
|
|
||||||
case Ops.Remainder:
|
|
||||||
return "Remainder";
|
|
||||||
case Ops.Equal:
|
|
||||||
return "Equal";
|
|
||||||
case Ops.LessThan:
|
|
||||||
return "LessThan";
|
|
||||||
case Ops.And:
|
|
||||||
return "And";
|
|
||||||
case Ops.Or:
|
|
||||||
return "Or";
|
|
||||||
case Ops.Xor:
|
|
||||||
return "Xor";
|
|
||||||
case Ops.Not:
|
|
||||||
return "Not";
|
|
||||||
case Ops.SourceMap:
|
|
||||||
return "SourceMap";
|
|
||||||
default:
|
|
||||||
return `<unknown Op ${op}>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function builtinToString(builtin: number): string {
|
|
||||||
switch (builtin) {
|
|
||||||
case Builtins.StringConcat:
|
|
||||||
return "StringConcat";
|
|
||||||
case Builtins.StringEqual:
|
|
||||||
return "StringEqual";
|
|
||||||
case Builtins.ArraySet:
|
|
||||||
return "ArraySet";
|
|
||||||
case Builtins.StructSet:
|
|
||||||
return "StructSet";
|
|
||||||
default:
|
|
||||||
return `<unknown Builtin ${builtin}>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
import { Ops, opToString } from "./arch.ts";
|
|
||||||
|
|
||||||
export type Line = { labels?: string[]; ins: Ins };
|
|
||||||
|
|
||||||
export type Ins = Lit[];
|
|
||||||
|
|
||||||
export type Label = { label: string };
|
|
||||||
|
|
||||||
export type Lit = number | string | boolean | Label;
|
|
||||||
|
|
||||||
export class Assembler {
|
|
||||||
private lines: Line[] = [];
|
|
||||||
private addedLabels: string[] = [];
|
|
||||||
private labelCounter = 0;
|
|
||||||
|
|
||||||
public add(...ins: Ins): Assembler {
|
|
||||||
if (this.addedLabels.length > 0) {
|
|
||||||
this.lines.push({ ins, labels: this.addedLabels });
|
|
||||||
this.addedLabels = [];
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
this.lines.push({ ins });
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public concat(assembler: Assembler) {
|
|
||||||
if (assembler.lines.length < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (assembler.lines[0].labels !== undefined) {
|
|
||||||
this.addedLabels.push(...assembler.lines[0].labels);
|
|
||||||
}
|
|
||||||
this.add(...assembler.lines[0].ins);
|
|
||||||
this.lines.push(...assembler.lines.slice(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public makeLabel(): Label {
|
|
||||||
return { label: `.L${(this.labelCounter++).toString()}` };
|
|
||||||
}
|
|
||||||
|
|
||||||
public setLabel({ label }: Label) {
|
|
||||||
this.addedLabels.push(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
public assemble(): number[] {
|
|
||||||
let ip = 0;
|
|
||||||
const output: number[] = [];
|
|
||||||
const locs: { [key: string]: number } = {};
|
|
||||||
const refs: { [key: number]: string } = {};
|
|
||||||
for (const line of this.lines) {
|
|
||||||
for (const label of line.labels ?? []) {
|
|
||||||
locs[label] = ip;
|
|
||||||
}
|
|
||||||
for (const lit of line.ins as Lit[]) {
|
|
||||||
if (typeof lit === "number") {
|
|
||||||
output.push(lit);
|
|
||||||
ip += 1;
|
|
||||||
} else if (typeof lit === "boolean") {
|
|
||||||
output.push(lit ? 1 : 0);
|
|
||||||
ip += 1;
|
|
||||||
} else if (typeof lit === "string") {
|
|
||||||
for (let i = 0; i < lit.length; ++i) {
|
|
||||||
output.push(lit.charCodeAt(i));
|
|
||||||
ip += 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output.push(0);
|
|
||||||
refs[ip] = lit.label;
|
|
||||||
ip += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < output.length; ++i) {
|
|
||||||
if (!(i in refs)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!(refs[i] in locs)) {
|
|
||||||
console.error(
|
|
||||||
`Assembler: label '${refs[i]}' used at ${i} not defined`,
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
output[i] = locs[refs[i]];
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public printProgram() {
|
|
||||||
for (const line of this.lines) {
|
|
||||||
for (const label of line.labels ?? []) {
|
|
||||||
console.log(`${label}:`);
|
|
||||||
}
|
|
||||||
const op = opToString(line.ins[0] as number)
|
|
||||||
.padEnd(13, " ");
|
|
||||||
const args = (line.ins.slice(1) as Lit[]).map((lit) => {
|
|
||||||
if (typeof lit === "number") {
|
|
||||||
return lit;
|
|
||||||
} else if (typeof lit === "boolean") {
|
|
||||||
return lit.toString();
|
|
||||||
} else if (typeof lit === "string") {
|
|
||||||
return '"' +
|
|
||||||
lit.replaceAll("\\", "\\\\").replaceAll("\0", "\\0")
|
|
||||||
.replaceAll("\n", "\\n").replaceAll("\t", "\\t")
|
|
||||||
.replaceAll("\r", "\\r") +
|
|
||||||
'"';
|
|
||||||
} else {
|
|
||||||
return lit.label;
|
|
||||||
}
|
|
||||||
}).join(", ");
|
|
||||||
console.log(` ${op} ${args}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,14 +2,13 @@ import { Builtins } from "./arch.ts";
|
|||||||
import { BinaryType, Expr, Stmt } from "./ast.ts";
|
import { BinaryType, Expr, Stmt } from "./ast.ts";
|
||||||
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
|
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
|
||||||
import { Ops } from "./mod.ts";
|
import { Ops } from "./mod.ts";
|
||||||
import { Assembler, Label } from "./assembler.ts";
|
import { Assembler } from "./program_builder.ts";
|
||||||
import { VType, vtypeToString } from "./vtype.ts";
|
import { VType, vtypeToString } from "./vtype.ts";
|
||||||
|
|
||||||
export class Lowerer {
|
export class Lowerer {
|
||||||
private program = new Assembler();
|
private program = new Assembler();
|
||||||
private locals: Locals = new LocalsFnRoot();
|
private locals: Locals = new LocalsFnRoot();
|
||||||
private fnStmtIdLabelMap: { [key: number]: string } = {};
|
private fnStmtIdAddrMap: { [key: number]: number } = {};
|
||||||
private breakStack: Label[] = [];
|
|
||||||
|
|
||||||
public lower(stmts: Stmt[]) {
|
public lower(stmts: Stmt[]) {
|
||||||
for (const stmt of stmts) {
|
for (const stmt of stmts) {
|
||||||
@ -38,9 +37,7 @@ export class Lowerer {
|
|||||||
private lowerStmt(stmt: Stmt) {
|
private lowerStmt(stmt: Stmt) {
|
||||||
switch (stmt.kind.type) {
|
switch (stmt.kind.type) {
|
||||||
case "error":
|
case "error":
|
||||||
break;
|
|
||||||
case "break":
|
case "break":
|
||||||
return this.lowerBreakStmt(stmt);
|
|
||||||
case "return":
|
case "return":
|
||||||
break;
|
break;
|
||||||
case "fn":
|
case "fn":
|
||||||
@ -48,63 +45,19 @@ export class Lowerer {
|
|||||||
case "let":
|
case "let":
|
||||||
return this.lowerLetStmt(stmt);
|
return this.lowerLetStmt(stmt);
|
||||||
case "assign":
|
case "assign":
|
||||||
return this.lowerAssignStmt(stmt);
|
break;
|
||||||
case "expr":
|
case "expr":
|
||||||
this.lowerExpr(stmt.kind.expr);
|
this.lowerExpr(stmt.kind.expr);
|
||||||
this.program.add(Ops.Pop);
|
this.program.push(Ops.Pop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(`unhandled stmt '${stmt.kind.type}'`);
|
throw new Error(`unhandled stmt '${stmt.kind.type}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.program.add(Ops.PushString, stmt.kind.subject.kind.value);
|
|
||||||
this.program.add(Ops.Builtin, Builtins.StructSet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case "index": {
|
|
||||||
this.lowerExpr(stmt.kind.subject.kind.subject);
|
|
||||||
this.lowerExpr(stmt.kind.subject.kind.value);
|
|
||||||
this.program.add(Ops.Builtin, Builtins.ArraySet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case "sym": {
|
|
||||||
this.program.add(
|
|
||||||
Ops.StoreLocal,
|
|
||||||
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.add(Ops.Jump, this.breakStack.at(-1)!);
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerFnStmt(stmt: Stmt) {
|
private lowerFnStmt(stmt: Stmt) {
|
||||||
if (stmt.kind.type !== "fn") {
|
if (stmt.kind.type !== "fn") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const label = `${stmt.kind.ident}_${stmt.id}`;
|
|
||||||
this.fnStmtIdLabelMap[stmt.id] = label;
|
|
||||||
this.program.setLabel({ label });
|
|
||||||
|
|
||||||
const outerLocals = this.locals;
|
const outerLocals = this.locals;
|
||||||
this.locals = new LocalsFnRoot(outerLocals);
|
this.locals = new LocalsFnRoot(outerLocals);
|
||||||
const outerProgram = this.program;
|
const outerProgram = this.program;
|
||||||
@ -112,13 +65,13 @@ export class Lowerer {
|
|||||||
|
|
||||||
for (const { ident } of stmt.kind.params) {
|
for (const { ident } of stmt.kind.params) {
|
||||||
this.locals.allocSym(ident);
|
this.locals.allocSym(ident);
|
||||||
this.program.add(
|
this.program.push(
|
||||||
Ops.StoreLocal,
|
Ops.StoreLocal,
|
||||||
this.locals.symId(ident),
|
this.locals.symId(ident),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.lowerExpr(stmt.kind.body);
|
this.lowerExpr(stmt.kind.body);
|
||||||
this.program.add(Ops.Return);
|
this.program.push(Ops.Return);
|
||||||
|
|
||||||
this.locals = outerLocals;
|
this.locals = outerLocals;
|
||||||
outerProgram.concat(this.program);
|
outerProgram.concat(this.program);
|
||||||
@ -130,11 +83,9 @@ export class Lowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.lowerExpr(stmt.kind.value);
|
this.lowerExpr(stmt.kind.value);
|
||||||
this.locals.allocSym(stmt.kind.param.ident);
|
this.locals.allocSym(stmt.kind.param.ident),
|
||||||
this.program.add(
|
this.program.push(Ops.StoreLocal);
|
||||||
Ops.StoreLocal,
|
this.program.push(this.locals.symId(stmt.kind.param.ident));
|
||||||
this.locals.symId(stmt.kind.param.ident),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private lowerExpr(expr: Expr) {
|
private lowerExpr(expr: Expr) {
|
||||||
@ -168,7 +119,7 @@ export class Lowerer {
|
|||||||
case "if":
|
case "if":
|
||||||
return this.lowerIfExpr(expr);
|
return this.lowerIfExpr(expr);
|
||||||
case "loop":
|
case "loop":
|
||||||
return this.lowerLoopExpr(expr);
|
break;
|
||||||
case "block":
|
case "block":
|
||||||
return this.lowerBlockExpr(expr);
|
return this.lowerBlockExpr(expr);
|
||||||
}
|
}
|
||||||
@ -180,22 +131,22 @@ export class Lowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
if (expr.kind.sym.type === "let") {
|
if (expr.kind.sym.type === "let") {
|
||||||
this.program.add(
|
this.program.push(
|
||||||
Ops.LoadLocal,
|
Ops.LoadLocal,
|
||||||
this.locals.symId(expr.kind.ident),
|
this.locals.symId(expr.kind.ident),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.sym.type === "fn_param") {
|
if (expr.kind.sym.type === "fn_param") {
|
||||||
this.program.add(
|
this.program.push(
|
||||||
Ops.LoadLocal,
|
Ops.LoadLocal,
|
||||||
this.locals.symId(expr.kind.ident),
|
this.locals.symId(expr.kind.ident),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.sym.type === "fn") {
|
if (expr.kind.sym.type === "fn") {
|
||||||
const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id];
|
const addr = this.fnStmtIdAddrMap[expr.kind.sym.stmt.id];
|
||||||
this.program.add(Ops.PushPtr, label);
|
this.program.push(Ops.PushPtr, addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
|
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
|
||||||
@ -205,14 +156,17 @@ export class Lowerer {
|
|||||||
if (expr.kind.type !== "int") {
|
if (expr.kind.type !== "int") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.program.add(Ops.PushInt, expr.kind.value);
|
this.program.push(Ops.PushInt, expr.kind.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lowerStringExpr(expr: Expr) {
|
private lowerStringExpr(expr: Expr) {
|
||||||
if (expr.kind.type !== "string") {
|
if (expr.kind.type !== "string") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.program.add(Ops.PushString, expr.kind.value);
|
this.program.push(Ops.PushString);
|
||||||
|
for (let i = 0; i < expr.kind.value.length; ++i) {
|
||||||
|
this.program.push(expr.kind.value.charCodeAt(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lowerBinaryExpr(expr: Expr) {
|
private lowerBinaryExpr(expr: Expr) {
|
||||||
@ -221,43 +175,34 @@ export class Lowerer {
|
|||||||
}
|
}
|
||||||
this.lowerExpr(expr.kind.left);
|
this.lowerExpr(expr.kind.left);
|
||||||
this.lowerExpr(expr.kind.right);
|
this.lowerExpr(expr.kind.right);
|
||||||
const vtype = expr.kind.left.vtype!;
|
if (expr.vtype!.type === "int") {
|
||||||
if (vtype.type === "int") {
|
|
||||||
switch (expr.kind.binaryType) {
|
switch (expr.kind.binaryType) {
|
||||||
case "+":
|
case "+":
|
||||||
this.program.add(Ops.Add);
|
this.program.push(Ops.Add);
|
||||||
return;
|
return;
|
||||||
case "*":
|
case "*":
|
||||||
this.program.add(Ops.Multiply);
|
this.program.push(Ops.Multiply);
|
||||||
return;
|
return;
|
||||||
case "==":
|
case "==":
|
||||||
this.program.add(Ops.Equal);
|
case "-":
|
||||||
return;
|
case "/":
|
||||||
|
case "!=":
|
||||||
|
case "<":
|
||||||
|
case ">":
|
||||||
|
case "<=":
|
||||||
case ">=":
|
case ">=":
|
||||||
this.program.add(Ops.LessThan);
|
case "or":
|
||||||
this.program.add(Ops.Not);
|
case "and":
|
||||||
return;
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (vtype.type === "string") {
|
if (expr.vtype!.type === "string") {
|
||||||
if (expr.kind.binaryType === "+") {
|
if (expr.kind.binaryType === "+") {
|
||||||
this.program.add(Ops.Builtin, Builtins.StringConcat);
|
this.program.push(Ops.Builtin, Builtins.StringAdd);
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (expr.kind.binaryType === "==") {
|
|
||||||
this.program.add(Ops.Builtin, Builtins.StringEqual);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (expr.kind.binaryType === "!=") {
|
|
||||||
this.program.add(Ops.Builtin, Builtins.StringEqual);
|
|
||||||
this.program.add(Ops.Not);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`unhandled binaryType` +
|
`unhandled binaryType` +
|
||||||
` '${vtypeToString(expr.vtype!)}' aka. ` +
|
|
||||||
` '${vtypeToString(expr.kind.left.vtype!)}'` +
|
` '${vtypeToString(expr.kind.left.vtype!)}'` +
|
||||||
` ${expr.kind.binaryType}` +
|
` ${expr.kind.binaryType}` +
|
||||||
` '${vtypeToString(expr.kind.left.vtype!)}'`,
|
` '${vtypeToString(expr.kind.left.vtype!)}'`,
|
||||||
@ -284,42 +229,21 @@ export class Lowerer {
|
|||||||
|
|
||||||
this.lowerExpr(expr.kind.cond);
|
this.lowerExpr(expr.kind.cond);
|
||||||
|
|
||||||
this.program.add(Ops.Not);
|
this.program.push(Ops.Not, Ops.JumpIfTrue);
|
||||||
this.program.add(Ops.JumpIfTrue, falseLabel);
|
this.program.addRef(falseLabel);
|
||||||
|
|
||||||
this.lowerExpr(expr.kind.truthy);
|
this.lowerExpr(expr.kind.truthy);
|
||||||
|
|
||||||
this.program.add(Ops.Jump, doneLabel);
|
this.program.push(Ops.Jump);
|
||||||
|
this.program.addRef(doneLabel);
|
||||||
|
|
||||||
this.program.setLabel(falseLabel);
|
this.program.setLabel(falseLabel);
|
||||||
|
|
||||||
if (expr.kind.falsy) {
|
this.lowerExpr(expr.kind.falsy!);
|
||||||
this.lowerExpr(expr.kind.falsy!);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.program.setLabel(doneLabel);
|
this.program.setLabel(doneLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.add(Ops.Jump, breakLabel);
|
|
||||||
this.program.setLabel(breakLabel);
|
|
||||||
if (expr.vtype!.type === "null") {
|
|
||||||
this.program.add(Ops.PushNull);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.breakStack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private lowerBlockExpr(expr: Expr) {
|
private lowerBlockExpr(expr: Expr) {
|
||||||
if (expr.kind.type !== "block") {
|
if (expr.kind.type !== "block") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -332,12 +256,8 @@ export class Lowerer {
|
|||||||
if (expr.kind.expr) {
|
if (expr.kind.expr) {
|
||||||
this.lowerExpr(expr.kind.expr);
|
this.lowerExpr(expr.kind.expr);
|
||||||
} else {
|
} else {
|
||||||
this.program.add(Ops.PushNull);
|
this.program.push(Ops.PushNull);
|
||||||
}
|
}
|
||||||
this.locals = outerLocals;
|
this.locals = outerLocals;
|
||||||
}
|
}
|
||||||
|
|
||||||
public printProgram() {
|
|
||||||
this.program.printProgram();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,5 @@ new Checker().check(ast);
|
|||||||
// console.log(JSON.stringify(ast, null, 4))
|
// console.log(JSON.stringify(ast, null, 4))
|
||||||
const lowerer = new Lowerer();
|
const lowerer = new Lowerer();
|
||||||
lowerer.lower(ast);
|
lowerer.lower(ast);
|
||||||
lowerer.printProgram();
|
|
||||||
const program = lowerer.finish();
|
const program = lowerer.finish();
|
||||||
//console.log(JSON.stringify(program, null, 4));
|
console.log(JSON.stringify(program, null, 4));
|
||||||
console.log(JSON.stringify(program));
|
|
||||||
|
97
compiler/program_builder.ts
Normal file
97
compiler/program_builder.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { Ops } from "./arch.ts";
|
||||||
|
|
||||||
|
export type Line = {
|
||||||
|
labels?: number[];
|
||||||
|
} & LineKind;
|
||||||
|
|
||||||
|
export type Label = { label: number };
|
||||||
|
|
||||||
|
export type LineKind =
|
||||||
|
| { type: "op"; op: number }
|
||||||
|
| { type: "lit"; val: number }
|
||||||
|
| { type: "ref"; label: number };
|
||||||
|
|
||||||
|
export class Assembler {
|
||||||
|
private lines: Line[] = [];
|
||||||
|
private labelCounter = 0;
|
||||||
|
|
||||||
|
public push(...values: number[]) {
|
||||||
|
for (const value of values) {
|
||||||
|
this.addLit(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public addOp(op: number): Assembler {
|
||||||
|
this.lines.push({ type: "op", op });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addLit(val: number): Assembler {
|
||||||
|
this.lines.push({ type: "lit", val });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addRef({ label }: Label): Assembler {
|
||||||
|
this.lines.push({ type: "ref", label });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLabel({ label }: Label): Assembler {
|
||||||
|
const line = this.lines.at(-1);
|
||||||
|
if (line === undefined) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (line.labels === undefined) {
|
||||||
|
line.labels = [];
|
||||||
|
}
|
||||||
|
line.labels.push(label);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public makeLabel(): Label {
|
||||||
|
const label = this.labelCounter;
|
||||||
|
this.labelCounter += 1;
|
||||||
|
return { label };
|
||||||
|
}
|
||||||
|
|
||||||
|
public concat(assembler: Assembler) {
|
||||||
|
this.lines.push(...assembler.lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public assemble(): number[] {
|
||||||
|
let ip = 0;
|
||||||
|
const output: number[] = [];
|
||||||
|
const locs: { [key: number]: number } = {};
|
||||||
|
const refs: { [key: number]: number } = {};
|
||||||
|
for (const line of this.lines) {
|
||||||
|
switch (line.type) {
|
||||||
|
case "op":
|
||||||
|
output.push(line.op);
|
||||||
|
ip += 1;
|
||||||
|
break;
|
||||||
|
case "lit":
|
||||||
|
output.push(line.val);
|
||||||
|
ip += 1;
|
||||||
|
break;
|
||||||
|
case "ref":
|
||||||
|
output.push(0);
|
||||||
|
refs[ip] = line.label;
|
||||||
|
ip += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < output.length; ++i) {
|
||||||
|
if (!(i in refs)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(refs[i] in locs)) {
|
||||||
|
console.error(
|
||||||
|
`Assembler: label '${refs[i]}' used at ${i} not defined`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
output[i] = locs[refs[i]];
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user