fix lowerer

This commit is contained in:
sfja 2024-12-11 00:03:19 +01:00
parent 2dad35c21f
commit 916c2376a0
3 changed files with 70 additions and 156 deletions

View File

@ -1,113 +1,19 @@
import { Ops, opToString } from "./arch.ts"; import { Ops, opToString } from "./arch.ts";
export type Line = { export type Line = { labels?: string[]; ins: Ins };
labels?: number[];
} & LineKind;
export type Label = { label: number }; export type Ins = Lit[];
export type LineKind = export type Label = { label: string };
| { 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;
}
}
export type Line2 = { labels?: number[]; ins: Ins2 };
export type Ins2 = [Ops, ...Lit[]];
export type Lit = number | string | boolean | Label; export type Lit = number | string | boolean | Label;
export class Assembler2 { export class Assembler {
private lines: Line2[] = []; private lines: Line[] = [];
private addedLabels: number[] = []; private addedLabels: string[] = [];
private labelCounter = 0; private labelCounter = 0;
public add(ins: Ins2): Assembler2 { public add(...ins: Ins): Assembler {
if (this.addedLabels.length > 0) { if (this.addedLabels.length > 0) {
this.lines.push({ ins, labels: this.addedLabels }); this.lines.push({ ins, labels: this.addedLabels });
this.addedLabels = []; this.addedLabels = [];
@ -117,8 +23,19 @@ export class Assembler2 {
return this; 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 { public makeLabel(): Label {
return { label: this.labelCounter++ }; return { label: `.L${(this.labelCounter++).toString()}` };
} }
public setLabel({ label }: Label) { public setLabel({ label }: Label) {
@ -128,9 +45,12 @@ export class Assembler2 {
public assemble(): number[] { public assemble(): number[] {
let ip = 0; let ip = 0;
const output: number[] = []; const output: number[] = [];
const locs: { [key: number]: number } = {}; const locs: { [key: string]: number } = {};
const refs: { [key: number]: number } = {}; const refs: { [key: number]: string } = {};
for (const line of this.lines) { for (const line of this.lines) {
for (const label of line.labels ?? []) {
locs[label] = ip;
}
for (const lit of line.ins as Lit[]) { for (const lit of line.ins as Lit[]) {
if (typeof lit === "number") { if (typeof lit === "number") {
output.push(lit); output.push(lit);
@ -168,12 +88,10 @@ export class Assembler2 {
public printProgram() { public printProgram() {
for (const line of this.lines) { for (const line of this.lines) {
for (const label of line.labels ?? []) { for (const label of line.labels ?? []) {
console.log(`.L${label}:`); console.log(`${label}:`);
} }
const op = opToString(line.ins[0] as unknown as number).padEnd( const op = opToString(line.ins[0] as number)
13, .padEnd(13, " ");
" ",
);
const args = (line.ins.slice(1) as Lit[]).map((lit) => { const args = (line.ins.slice(1) as Lit[]).map((lit) => {
if (typeof lit === "number") { if (typeof lit === "number") {
return lit; return lit;
@ -186,7 +104,7 @@ export class Assembler2 {
.replaceAll("\r", "\\r") + .replaceAll("\r", "\\r") +
'"'; '"';
} else { } else {
return `.L${lit.label}`; return lit.label;
} }
}).join(", "); }).join(", ");
console.log(` ${op} ${args}`); console.log(` ${op} ${args}`);

View File

@ -8,7 +8,7 @@ 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 fnStmtIdAddrMap: { [key: number]: number } = {}; private fnStmtIdLabelMap: { [key: number]: string } = {};
private breakStack: Label[] = []; private breakStack: Label[] = [];
public lower(stmts: Stmt[]) { public lower(stmts: Stmt[]) {
@ -51,7 +51,7 @@ export class Lowerer {
return this.lowerAssignStmt(stmt); return this.lowerAssignStmt(stmt);
case "expr": case "expr":
this.lowerExpr(stmt.kind.expr); this.lowerExpr(stmt.kind.expr);
this.program.push(Ops.Pop); this.program.add(Ops.Pop);
return; return;
} }
throw new Error(`unhandled stmt '${stmt.kind.type}'`); throw new Error(`unhandled stmt '${stmt.kind.type}'`);
@ -65,21 +65,19 @@ export class Lowerer {
switch (stmt.kind.subject.kind.type) { switch (stmt.kind.subject.kind.type) {
case "field": { case "field": {
this.lowerExpr(stmt.kind.subject.kind.subject); this.lowerExpr(stmt.kind.subject.kind.subject);
this.addPushStringOp(stmt.kind.subject.kind.value); this.program.add(Ops.PushString, stmt.kind.subject.kind.value);
this.program.addOp(Ops.Builtin); this.program.add(Ops.Builtin, Builtins.StructSet);
this.program.addLit(Builtins.StructSet);
return; return;
} }
case "index": { case "index": {
this.lowerExpr(stmt.kind.subject.kind.subject); this.lowerExpr(stmt.kind.subject.kind.subject);
this.lowerExpr(stmt.kind.subject.kind.value); this.lowerExpr(stmt.kind.subject.kind.value);
this.program.addOp(Ops.Builtin); this.program.add(Ops.Builtin, Builtins.ArraySet);
this.program.addLit(Builtins.ArraySet);
return; return;
} }
case "sym": { case "sym": {
this.program.addOp(Ops.StoreLocal); this.program.add(
this.program.addLit( Ops.StoreLocal,
this.locals.symId(stmt.kind.subject.kind.sym.ident), this.locals.symId(stmt.kind.subject.kind.sym.ident),
); );
return; return;
@ -96,14 +94,17 @@ export class Lowerer {
if (stmt.kind.expr) { if (stmt.kind.expr) {
this.lowerExpr(stmt.kind.expr); this.lowerExpr(stmt.kind.expr);
} }
this.program.addOp(Ops.Jump); this.program.add(Ops.Jump, this.breakStack.at(-1)!);
this.program.addRef(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;
@ -111,13 +112,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.push( this.program.add(
Ops.StoreLocal, Ops.StoreLocal,
this.locals.symId(ident), this.locals.symId(ident),
); );
} }
this.lowerExpr(stmt.kind.body); this.lowerExpr(stmt.kind.body);
this.program.push(Ops.Return); this.program.add(Ops.Return);
this.locals = outerLocals; this.locals = outerLocals;
outerProgram.concat(this.program); outerProgram.concat(this.program);
@ -129,9 +130,11 @@ 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.push(Ops.StoreLocal); this.program.add(
this.program.push(this.locals.symId(stmt.kind.param.ident)); Ops.StoreLocal,
this.locals.symId(stmt.kind.param.ident),
);
} }
private lowerExpr(expr: Expr) { private lowerExpr(expr: Expr) {
@ -177,22 +180,22 @@ export class Lowerer {
throw new Error(); throw new Error();
} }
if (expr.kind.sym.type === "let") { if (expr.kind.sym.type === "let") {
this.program.push( this.program.add(
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.push( this.program.add(
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 addr = this.fnStmtIdAddrMap[expr.kind.sym.stmt.id]; const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id];
this.program.push(Ops.PushPtr, addr); this.program.add(Ops.PushPtr, label);
return; return;
} }
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`); throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
@ -202,21 +205,14 @@ export class Lowerer {
if (expr.kind.type !== "int") { if (expr.kind.type !== "int") {
throw new Error(); throw new Error();
} }
this.program.push(Ops.PushInt, expr.kind.value); this.program.add(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.addPushStringOp(expr.kind.value); this.program.add(Ops.PushString, 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));
}
} }
private lowerBinaryExpr(expr: Expr) { private lowerBinaryExpr(expr: Expr) {
@ -229,32 +225,33 @@ export class Lowerer {
if (vtype.type === "int") { if (vtype.type === "int") {
switch (expr.kind.binaryType) { switch (expr.kind.binaryType) {
case "+": case "+":
this.program.addOp(Ops.Add); this.program.add(Ops.Add);
return; return;
case "*": case "*":
this.program.addOp(Ops.Multiply); this.program.add(Ops.Multiply);
return; return;
case "==": case "==":
this.program.addOp(Ops.Equal); this.program.add(Ops.Equal);
return; return;
case ">=": case ">=":
this.program.addOp(Ops.LessThan); this.program.add(Ops.LessThan);
this.program.addOp(Ops.Not); this.program.add(Ops.Not);
return; return;
default: default:
} }
} }
if (vtype.type === "string") { if (vtype.type === "string") {
if (expr.kind.binaryType === "+") { if (expr.kind.binaryType === "+") {
this.program.push(Ops.Builtin, Builtins.StringConcat); this.program.add(Ops.Builtin, Builtins.StringConcat);
return; return;
} }
if (expr.kind.binaryType === "==") { if (expr.kind.binaryType === "==") {
this.program.push(Ops.Builtin, Builtins.StringEqual); this.program.add(Ops.Builtin, Builtins.StringEqual);
return; return;
} }
if (expr.kind.binaryType === "!=") { if (expr.kind.binaryType === "!=") {
this.program.push(Ops.Builtin, Builtins.StringEqual, Ops.Not); this.program.add(Ops.Builtin, Builtins.StringEqual);
this.program.add(Ops.Not);
return; return;
} }
} }
@ -287,13 +284,12 @@ export class Lowerer {
this.lowerExpr(expr.kind.cond); this.lowerExpr(expr.kind.cond);
this.program.push(Ops.Not, Ops.JumpIfTrue); this.program.add(Ops.Not);
this.program.addRef(falseLabel); this.program.add(Ops.JumpIfTrue, falseLabel);
this.lowerExpr(expr.kind.truthy); this.lowerExpr(expr.kind.truthy);
this.program.push(Ops.Jump); this.program.add(Ops.Jump, doneLabel);
this.program.addRef(doneLabel);
this.program.setLabel(falseLabel); this.program.setLabel(falseLabel);
@ -315,11 +311,10 @@ export class Lowerer {
this.program.setLabel(contineLabel); this.program.setLabel(contineLabel);
this.lowerExpr(expr.kind.body); this.lowerExpr(expr.kind.body);
this.program.addOp(Ops.Jump); this.program.add(Ops.Jump, breakLabel);
this.program.addRef(breakLabel);
this.program.setLabel(breakLabel); this.program.setLabel(breakLabel);
if (expr.vtype!.type === "null") { if (expr.vtype!.type === "null") {
this.program.addOp(Ops.PushNull); this.program.add(Ops.PushNull);
} }
this.breakStack.pop(); this.breakStack.pop();
@ -337,7 +332,7 @@ 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.addOp(Ops.PushNull); this.program.add(Ops.PushNull);
} }
this.locals = outerLocals; this.locals = outerLocals;
} }

View File

@ -17,4 +17,5 @@ const lowerer = new Lowerer();
lowerer.lower(ast); lowerer.lower(ast);
lowerer.printProgram(); 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));