add middle
This commit is contained in:
parent
82a2f259e1
commit
f3da09d9c2
@ -10,11 +10,13 @@ import { FnNamesMap, Lowerer } from "./lowerer.ts";
|
|||||||
import { Parser } from "./parser.ts";
|
import { Parser } from "./parser.ts";
|
||||||
import { Resolver } from "./resolver.ts";
|
import { Resolver } from "./resolver.ts";
|
||||||
import { AstVisitor, VisitRes, visitStmts } from "./ast_visitor.ts";
|
import { AstVisitor, VisitRes, visitStmts } from "./ast_visitor.ts";
|
||||||
|
|
||||||
import * as path from "jsr:@std/path";
|
|
||||||
import { Pos } from "./token.ts";
|
import { Pos } from "./token.ts";
|
||||||
import { ArrayLiteralDesugarer } from "./desugar/array_literal.ts";
|
import { ArrayLiteralDesugarer } from "./desugar/array_literal.ts";
|
||||||
|
|
||||||
|
import * as path from "jsr:@std/path";
|
||||||
|
import { AstLowerer } from "./middle/lower_ast.ts";
|
||||||
|
import { printMir } from "./middle/mir.ts";
|
||||||
|
|
||||||
export type CompileResult = {
|
export type CompileResult = {
|
||||||
program: number[];
|
program: number[];
|
||||||
fnNames: FnNamesMap;
|
fnNames: FnNamesMap;
|
||||||
@ -29,28 +31,32 @@ export class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async compile(): Promise<CompileResult> {
|
public async compile(): Promise<CompileResult> {
|
||||||
const mod = new ModTree(
|
const { ast } = new ModTree(
|
||||||
this.startFilePath,
|
this.startFilePath,
|
||||||
this.astCreator,
|
this.astCreator,
|
||||||
this.reporter,
|
this.reporter,
|
||||||
).resolve();
|
).resolve();
|
||||||
|
|
||||||
new SpecialLoopDesugarer(this.astCreator).desugar(mod.ast);
|
new SpecialLoopDesugarer(this.astCreator).desugar(ast);
|
||||||
new ArrayLiteralDesugarer(this.astCreator).desugar(mod.ast);
|
new ArrayLiteralDesugarer(this.astCreator).desugar(ast);
|
||||||
new StructLiteralDesugarer(this.astCreator).desugar(mod.ast);
|
new StructLiteralDesugarer(this.astCreator).desugar(ast);
|
||||||
|
|
||||||
new Resolver(this.reporter).resolve(mod.ast);
|
new Resolver(this.reporter).resolve(ast);
|
||||||
|
|
||||||
new CompoundAssignDesugarer(this.astCreator).desugar(mod.ast);
|
new CompoundAssignDesugarer(this.astCreator).desugar(ast);
|
||||||
|
|
||||||
new Checker(this.reporter).check(mod.ast);
|
new Checker(this.reporter).check(ast);
|
||||||
|
|
||||||
|
const mir = new AstLowerer(ast).lower();
|
||||||
|
|
||||||
|
printMir(mir);
|
||||||
|
|
||||||
if (this.reporter.errorOccured()) {
|
if (this.reporter.errorOccured()) {
|
||||||
console.error("Errors occurred, stopping compilation.");
|
console.error("Errors occurred, stopping compilation.");
|
||||||
Deno.exit(1);
|
Deno.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { monoFns, callMap } = new Monomorphizer(mod.ast).monomorphize();
|
const { monoFns, callMap } = new Monomorphizer(ast).monomorphize();
|
||||||
|
|
||||||
const lastPos = await lastPosInTextFile(this.startFilePath);
|
const lastPos = await lastPosInTextFile(this.startFilePath);
|
||||||
|
|
||||||
|
469
compiler/middle/lower_ast.ts
Normal file
469
compiler/middle/lower_ast.ts
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
import * as Ast from "../ast.ts";
|
||||||
|
import { AllFnsCollector } from "../mono.ts";
|
||||||
|
import { VType, vtypesEqual } from "../vtype.ts";
|
||||||
|
import {
|
||||||
|
Block,
|
||||||
|
BlockId,
|
||||||
|
Fn,
|
||||||
|
Local,
|
||||||
|
LocalId,
|
||||||
|
Mir,
|
||||||
|
Op,
|
||||||
|
OpKind,
|
||||||
|
Ter,
|
||||||
|
TerKind,
|
||||||
|
} from "./mir.ts";
|
||||||
|
|
||||||
|
export class AstLowerer {
|
||||||
|
public constructor(private ast: Ast.Stmt[]) {}
|
||||||
|
|
||||||
|
public lower(): Mir {
|
||||||
|
const fnAsts = new AllFnsCollector().collect(this.ast).values();
|
||||||
|
const fns = fnAsts
|
||||||
|
.map((fnAst) => new FnAstLowerer(fnAst).lower())
|
||||||
|
.toArray();
|
||||||
|
return { fns };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalAllocator {
|
||||||
|
private locals: Local[] = [];
|
||||||
|
|
||||||
|
public alloc(vtype: VType): LocalId {
|
||||||
|
const id = this.locals.length;
|
||||||
|
this.locals.push({ id, vtype });
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public finish(): Local[] {
|
||||||
|
return this.locals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FnAstLowerer {
|
||||||
|
private locals = new LocalAllocator();
|
||||||
|
private blockIdCounter = 0;
|
||||||
|
private currentBlockId = 0;
|
||||||
|
private blocks = new Map<BlockId, Block>();
|
||||||
|
|
||||||
|
private fnParamIndexLocals = new Map<number, LocalId>();
|
||||||
|
private letStmtIdLocals = new Map<number, LocalId>();
|
||||||
|
|
||||||
|
private breakStack: { local: LocalId; block: BlockId }[] = [];
|
||||||
|
|
||||||
|
public constructor(private ast: Ast.Stmt) {}
|
||||||
|
|
||||||
|
public lower(): Fn {
|
||||||
|
const stmt = this.ast;
|
||||||
|
if (stmt.kind.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const vtype = stmt.kind.vtype;
|
||||||
|
if (vtype?.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locals.alloc(stmt.kind.vtype!);
|
||||||
|
for (const param of stmt.kind.params) {
|
||||||
|
const id = this.locals.alloc(param.vtype!);
|
||||||
|
this.fnParamIndexLocals.set(param.index!, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pushBlock("entry");
|
||||||
|
this.lowerBlockExpr(stmt.kind.body);
|
||||||
|
this.pushBlock("exit");
|
||||||
|
|
||||||
|
const locals = this.locals.finish();
|
||||||
|
const blocks = this.blocks.values().toArray();
|
||||||
|
return { stmt, locals, blocks };
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerStmt(stmt: Ast.Stmt) {
|
||||||
|
switch (stmt.kind.type) {
|
||||||
|
case "error":
|
||||||
|
case "mod_block":
|
||||||
|
case "mod_file":
|
||||||
|
case "mod":
|
||||||
|
break;
|
||||||
|
case "break": {
|
||||||
|
const { local, block } = this.breakStack.at(-1)!;
|
||||||
|
if (stmt.kind.expr) {
|
||||||
|
const val = this.lowerExpr(stmt.kind.expr);
|
||||||
|
this.addOp({ type: "assign", dst: local, src: val });
|
||||||
|
} else {
|
||||||
|
this.addOp({ type: "assign_null", dst: local });
|
||||||
|
}
|
||||||
|
this.setTer({ type: "jump", target: block });
|
||||||
|
this.pushBlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "return":
|
||||||
|
break;
|
||||||
|
case "fn":
|
||||||
|
// nothing
|
||||||
|
return;
|
||||||
|
case "let":
|
||||||
|
this.lowerLetStmt(stmt);
|
||||||
|
return;
|
||||||
|
case "type_alias":
|
||||||
|
break;
|
||||||
|
case "assign":
|
||||||
|
return this.lowerAssign(stmt);
|
||||||
|
case "expr": {
|
||||||
|
const val = this.lowerExpr(stmt.kind.expr);
|
||||||
|
this.addOp({ type: "drop", val });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`statement type '${stmt.kind.type}' not covered`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerAssign(stmt: Ast.Stmt) {
|
||||||
|
if (stmt.kind.type !== "assign") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
if (stmt.kind.assignType !== "=") {
|
||||||
|
throw new Error("incomplete desugar");
|
||||||
|
}
|
||||||
|
const src = this.lowerExpr(stmt.kind.value);
|
||||||
|
const s = stmt.kind.subject;
|
||||||
|
switch (s.kind.type) {
|
||||||
|
case "field": {
|
||||||
|
const subject = this.lowerExpr(s.kind.subject);
|
||||||
|
const ident = s.kind.ident;
|
||||||
|
this.addOp({ type: "assign_field", subject, ident, src });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "index": {
|
||||||
|
const subject = this.lowerExpr(s.kind.subject);
|
||||||
|
const index = this.lowerExpr(s.kind.value);
|
||||||
|
this.addOp({ type: "assign_field", subject, index, src });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "sym": {
|
||||||
|
const sym = s.kind.sym;
|
||||||
|
switch (sym.type) {
|
||||||
|
case "let": {
|
||||||
|
const dst = this.letStmtIdLocals.get(sym.stmt.id)!;
|
||||||
|
this.addOp({ type: "assign", dst, src });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "fn_param": {
|
||||||
|
const dst = this.fnParamIndexLocals.get(
|
||||||
|
sym.param.index!,
|
||||||
|
)!;
|
||||||
|
this.addOp({ type: "assign", dst, src });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`symbol type '${sym.type}' not covered`);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerLetStmt(stmt: Ast.Stmt) {
|
||||||
|
if (stmt.kind.type !== "let") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const src = this.lowerExpr(stmt.kind.value);
|
||||||
|
const dst = this.locals.alloc(stmt.kind.param.vtype!);
|
||||||
|
this.addOp({ type: "assign", dst, src });
|
||||||
|
this.letStmtIdLocals.set(stmt.id, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerExpr(expr: Ast.Expr): LocalId {
|
||||||
|
switch (expr.kind.type) {
|
||||||
|
case "error": {
|
||||||
|
const dst = this.locals.alloc({ type: "error" });
|
||||||
|
this.addOp({ type: "assign_error", dst });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
case "null": {
|
||||||
|
const dst = this.locals.alloc({ type: "null" });
|
||||||
|
this.addOp({ type: "assign_null", dst });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
case "bool": {
|
||||||
|
const val = expr.kind.value;
|
||||||
|
const dst = this.locals.alloc({ type: "bool" });
|
||||||
|
this.addOp({ type: "assign_bool", dst, val });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
case "int": {
|
||||||
|
const val = expr.kind.value;
|
||||||
|
const dst = this.locals.alloc({ type: "int" });
|
||||||
|
this.addOp({ type: "assign_int", dst, val });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
case "string": {
|
||||||
|
const val = expr.kind.value;
|
||||||
|
const dst = this.locals.alloc({ type: "string" });
|
||||||
|
this.addOp({ type: "assign_string", dst, val });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
case "ident":
|
||||||
|
throw new Error("should've been resolved");
|
||||||
|
case "sym":
|
||||||
|
return this.lowerSymExpr(expr);
|
||||||
|
case "group":
|
||||||
|
return this.lowerExpr(expr.kind.expr);
|
||||||
|
case "array":
|
||||||
|
throw new Error("incomplete desugar");
|
||||||
|
case "struct":
|
||||||
|
throw new Error("incomplete desugar");
|
||||||
|
case "field":
|
||||||
|
return this.lowerFieldExpr(expr);
|
||||||
|
case "index":
|
||||||
|
return this.lowerIndexExpr(expr);
|
||||||
|
case "call":
|
||||||
|
return this.lowerCallExpr(expr);
|
||||||
|
case "path":
|
||||||
|
case "etype_args":
|
||||||
|
case "unary":
|
||||||
|
break;
|
||||||
|
case "binary":
|
||||||
|
return this.lowerBinaryExpr(expr);
|
||||||
|
case "if":
|
||||||
|
return this.lowerIfExpr(expr);
|
||||||
|
case "loop":
|
||||||
|
return this.lowerLoopExpr(expr);
|
||||||
|
case "block":
|
||||||
|
return this.lowerBlockExpr(expr);
|
||||||
|
case "while":
|
||||||
|
case "for_in":
|
||||||
|
case "for":
|
||||||
|
throw new Error("incomplete desugar");
|
||||||
|
}
|
||||||
|
throw new Error(`expression type '${expr.kind.type}' not covered`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerSymExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "sym") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const sym = expr.kind.sym;
|
||||||
|
switch (sym.type) {
|
||||||
|
case "let":
|
||||||
|
return this.letStmtIdLocals.get(sym.stmt.id)!;
|
||||||
|
case "let_static":
|
||||||
|
case "type_alias":
|
||||||
|
break;
|
||||||
|
case "fn": {
|
||||||
|
const stmt = sym.stmt;
|
||||||
|
if (sym.stmt.kind.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const dst = this.locals.alloc(sym.stmt.kind.vtype!);
|
||||||
|
this.addOp({ type: "assign_fn", dst, stmt });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
case "fn_param": {
|
||||||
|
return this.fnParamIndexLocals.get(sym.param.index!)!;
|
||||||
|
}
|
||||||
|
case "closure":
|
||||||
|
case "generic":
|
||||||
|
case "mod":
|
||||||
|
}
|
||||||
|
throw new Error(`symbol type '${sym.type}' not covered`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerFieldExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "field") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const ident = expr.kind.ident;
|
||||||
|
const subject = this.lowerExpr(expr.kind.subject);
|
||||||
|
|
||||||
|
const subjectVType = expr.kind.subject.vtype!;
|
||||||
|
if (subjectVType.type !== "struct") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const fieldVType = subjectVType.fields.find((field) =>
|
||||||
|
field.ident === ident
|
||||||
|
);
|
||||||
|
if (fieldVType === undefined) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dst = this.locals.alloc(fieldVType.vtype);
|
||||||
|
this.addOp({ type: "field", dst, subject, ident });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerIndexExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "index") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const subject = this.lowerExpr(expr.kind.subject);
|
||||||
|
const index = this.lowerExpr(expr.kind.value);
|
||||||
|
|
||||||
|
const dstVType = ((): VType => {
|
||||||
|
const outer = expr.kind.subject.vtype!;
|
||||||
|
if (outer.type === "array") {
|
||||||
|
return outer.inner;
|
||||||
|
}
|
||||||
|
if (outer.type === "string") {
|
||||||
|
return { type: "int" };
|
||||||
|
}
|
||||||
|
throw new Error();
|
||||||
|
})();
|
||||||
|
|
||||||
|
const dst = this.locals.alloc(dstVType);
|
||||||
|
this.addOp({ type: "index", dst, subject, index });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerCallExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "call") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = expr.kind.args.map((arg) => this.lowerExpr(arg));
|
||||||
|
|
||||||
|
const subject = this.lowerExpr(expr.kind.subject);
|
||||||
|
|
||||||
|
const subjectVType = expr.kind.subject.vtype!;
|
||||||
|
if (subjectVType.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dst = this.locals.alloc(subjectVType.returnType);
|
||||||
|
this.addOp({ type: "call_val", dst, subject, args });
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerBinaryExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "binary") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const leftVType = expr.kind.left.vtype!;
|
||||||
|
const rightVType = expr.kind.right.vtype!;
|
||||||
|
if (!vtypesEqual(leftVType, rightVType)) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
//const vtype = leftVType.type === "error" && rightVType || leftVType;
|
||||||
|
|
||||||
|
const binaryType = expr.kind.binaryType;
|
||||||
|
const left = this.lowerExpr(expr.kind.left);
|
||||||
|
const right = this.lowerExpr(expr.kind.right);
|
||||||
|
|
||||||
|
const dst = this.locals.alloc(expr.vtype!);
|
||||||
|
|
||||||
|
this.addOp({ type: "binary", binaryType, dst, left, right });
|
||||||
|
return dst;
|
||||||
|
|
||||||
|
//throw new Error(
|
||||||
|
// `binary vtype '${vtypeToString(leftVType)}' not covered`,
|
||||||
|
//);
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerIfExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "if") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const condBlock = this.currentBlock();
|
||||||
|
const cond = this.lowerExpr(expr.kind.cond);
|
||||||
|
const end = this.reserveBlock();
|
||||||
|
|
||||||
|
const val = this.locals.alloc(expr.vtype!);
|
||||||
|
|
||||||
|
const truthy = this.pushBlock();
|
||||||
|
const truthyVal = this.lowerExpr(expr.kind.truthy);
|
||||||
|
this.addOp({ type: "assign", dst: val, src: truthyVal });
|
||||||
|
this.setTer({ type: "jump", target: end });
|
||||||
|
|
||||||
|
if (expr.kind.falsy) {
|
||||||
|
const falsy = this.pushBlock();
|
||||||
|
const falsyVal = this.lowerExpr(expr.kind.falsy);
|
||||||
|
this.addOp({ type: "assign", dst: val, src: falsyVal });
|
||||||
|
this.setTer({ type: "jump", target: end });
|
||||||
|
|
||||||
|
this.setTerOn(condBlock, { type: "if", cond, truthy, falsy });
|
||||||
|
} else {
|
||||||
|
this.setTerOn(condBlock, { type: "if", cond, truthy, falsy: end });
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerLoopExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "loop") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = this.locals.alloc(expr.vtype!);
|
||||||
|
const breakBlock = this.reserveBlock();
|
||||||
|
this.breakStack.push({ local: val, block: breakBlock });
|
||||||
|
|
||||||
|
const body = this.pushBlock();
|
||||||
|
this.setTer({ type: "jump", target: body });
|
||||||
|
|
||||||
|
this.lowerExpr(expr.kind.body);
|
||||||
|
this.setTer({ type: "jump", target: body });
|
||||||
|
|
||||||
|
this.breakStack.pop();
|
||||||
|
|
||||||
|
this.pushBlockWithId(breakBlock);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerBlockExpr(expr: Ast.Expr): LocalId {
|
||||||
|
if (expr.kind.type !== "block") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const stmt of expr.kind.stmts) {
|
||||||
|
this.lowerStmt(stmt);
|
||||||
|
}
|
||||||
|
if (expr.kind.expr) {
|
||||||
|
return this.lowerExpr(expr.kind.expr);
|
||||||
|
} else {
|
||||||
|
const local = this.locals.alloc({ type: "null" });
|
||||||
|
this.addOp({ type: "assign_null", dst: local });
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addOp(kind: OpKind) {
|
||||||
|
this.blocks.get(this.currentBlockId)!.ops.push({ kind });
|
||||||
|
}
|
||||||
|
|
||||||
|
private addOpOn(blockId: BlockId, kind: OpKind) {
|
||||||
|
this.blocks.get(blockId)!.ops.push({ kind });
|
||||||
|
}
|
||||||
|
|
||||||
|
private setTer(kind: TerKind) {
|
||||||
|
this.blocks.get(this.currentBlockId)!.ter = { kind };
|
||||||
|
}
|
||||||
|
|
||||||
|
private setTerOn(blockId: BlockId, kind: TerKind) {
|
||||||
|
this.blocks.get(blockId)!.ter = { kind };
|
||||||
|
}
|
||||||
|
|
||||||
|
private currentBlock(): BlockId {
|
||||||
|
return this.currentBlockId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private reserveBlock(): BlockId {
|
||||||
|
const id = this.blockIdCounter;
|
||||||
|
this.blockIdCounter += 1;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private pushBlock(label?: string): BlockId {
|
||||||
|
const id = this.blockIdCounter;
|
||||||
|
this.blockIdCounter += 1;
|
||||||
|
const ter: Ter = { kind: { type: "error" } };
|
||||||
|
this.blocks.set(id, { id, ops: [], ter, label });
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private pushBlockWithId(id: BlockId): BlockId {
|
||||||
|
this.blockIdCounter += 1;
|
||||||
|
const ter: Ter = { kind: { type: "error" } };
|
||||||
|
this.blocks.set(id, { id, ops: [], ter });
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
152
compiler/middle/mir.ts
Normal file
152
compiler/middle/mir.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import { BinaryType, Stmt } from "../ast.ts";
|
||||||
|
import { VType, vtypeToString } from "../vtype.ts";
|
||||||
|
|
||||||
|
export type Mir = {
|
||||||
|
fns: Fn[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Fn = {
|
||||||
|
stmt: Stmt;
|
||||||
|
locals: Local[];
|
||||||
|
blocks: Block[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LocalId = number;
|
||||||
|
|
||||||
|
export type Local = {
|
||||||
|
id: LocalId;
|
||||||
|
vtype: VType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BlockId = number;
|
||||||
|
|
||||||
|
export type Block = {
|
||||||
|
id: BlockId;
|
||||||
|
ops: Op[];
|
||||||
|
ter: Ter;
|
||||||
|
label?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Op = {
|
||||||
|
kind: OpKind;
|
||||||
|
};
|
||||||
|
|
||||||
|
type L = LocalId;
|
||||||
|
|
||||||
|
export type OpKind =
|
||||||
|
| { type: "error" }
|
||||||
|
| { type: "return" }
|
||||||
|
| { type: "drop"; val: L }
|
||||||
|
| { type: "assign"; dst: L; src: L }
|
||||||
|
| { type: "assign_error"; dst: L }
|
||||||
|
| { type: "assign_null"; dst: L }
|
||||||
|
| { type: "assign_bool"; dst: L; val: boolean }
|
||||||
|
| { type: "assign_int"; dst: L; val: number }
|
||||||
|
| { type: "assign_string"; dst: L; val: string }
|
||||||
|
| { type: "assign_fn"; dst: L; stmt: Stmt }
|
||||||
|
| { type: "field"; dst: L; subject: L; ident: string }
|
||||||
|
| { type: "assign_field"; subject: L; ident: string; src: L }
|
||||||
|
| { type: "index"; dst: L; subject: L; index: number }
|
||||||
|
| { type: "assign_field"; subject: L; index: L; src: L }
|
||||||
|
| { type: "call_val"; dst: L; subject: L; args: L[] }
|
||||||
|
| { type: "binary"; binaryType: BinaryType; dst: L; left: L; right: L };
|
||||||
|
|
||||||
|
export type Ter = {
|
||||||
|
kind: TerKind;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TerKind =
|
||||||
|
| { type: "error" }
|
||||||
|
| { type: "jump"; target: BlockId }
|
||||||
|
| { type: "if"; cond: L; truthy: BlockId; falsy: BlockId };
|
||||||
|
|
||||||
|
export function printMir(mir: Mir) {
|
||||||
|
for (const fn of mir.fns) {
|
||||||
|
const stmt = fn.stmt;
|
||||||
|
if (stmt.kind.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const name = stmt.kind.ident;
|
||||||
|
|
||||||
|
const vtype = stmt.kind.vtype;
|
||||||
|
if (vtype?.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const generics = vtype.genericParams
|
||||||
|
?.map(({ ident }) => `${ident}`).join(", ") ?? "";
|
||||||
|
const params = vtype.params
|
||||||
|
.map(({ vtype }, i) =>
|
||||||
|
`_${fn.locals[i + 1].id}: ${vtypeToString(vtype)}`
|
||||||
|
)
|
||||||
|
.join(", ");
|
||||||
|
const returnType = vtypeToString(vtype.returnType);
|
||||||
|
console.log(`${name}${generics}(${params}) -> ${returnType}:`);
|
||||||
|
for (const { id, vtype } of fn.locals) {
|
||||||
|
console.log(` let _${id}: ${vtypeToString(vtype)};`);
|
||||||
|
}
|
||||||
|
for (const block of fn.blocks) {
|
||||||
|
console.log(`.${block.label ?? block.id}:`);
|
||||||
|
for (const op of block.ops) {
|
||||||
|
const k = op.kind;
|
||||||
|
switch (k.type) {
|
||||||
|
case "error":
|
||||||
|
console.log(` <error>;`);
|
||||||
|
break;
|
||||||
|
case "return":
|
||||||
|
console.log(` return;`);
|
||||||
|
break;
|
||||||
|
case "drop":
|
||||||
|
console.log(` drop _${k.val};`);
|
||||||
|
break;
|
||||||
|
case "assign":
|
||||||
|
console.log(` _${k.dst} = _${k.src};`);
|
||||||
|
break;
|
||||||
|
case "assign_error":
|
||||||
|
console.log(` _${k.dst} = <error>;`);
|
||||||
|
break;
|
||||||
|
case "assign_null":
|
||||||
|
console.log(` _${k.dst} = null;`);
|
||||||
|
break;
|
||||||
|
case "assign_bool":
|
||||||
|
console.log(` _${k.dst} = ${k.val};`);
|
||||||
|
break;
|
||||||
|
case "assign_int":
|
||||||
|
console.log(` _${k.dst} = ${k.val};`);
|
||||||
|
break;
|
||||||
|
case "assign_string":
|
||||||
|
console.log(` _${k.dst} = "${k.val}";`);
|
||||||
|
break;
|
||||||
|
case "assign_fn": {
|
||||||
|
const stmt = k.stmt;
|
||||||
|
if (stmt.kind.type !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
console.log(` _${k.dst} = ${stmt.kind.ident};`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "field":
|
||||||
|
console.log(
|
||||||
|
` _${k.dst} = _${k.subject}.${k.ident};`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "index":
|
||||||
|
console.log(
|
||||||
|
` _${k.dst} = _${k.subject}[_${k.index}];`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "call_val": {
|
||||||
|
const args = k.args.map((arg) => `_${arg}`).join(", ");
|
||||||
|
console.log(` _${k.dst} = _${k.subject}(${args});`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "binary": {
|
||||||
|
console.log(
|
||||||
|
` _${k.dst} = _${k.left} ${k.binaryType} _${k.right};`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -181,7 +181,7 @@ function vtypeNameGenPart(vtype: VType): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AllFnsCollector implements AstVisitor {
|
export class AllFnsCollector implements AstVisitor {
|
||||||
private allFns = new Map<number, Stmt>();
|
private allFns = new Map<number, Stmt>();
|
||||||
|
|
||||||
public collect(ast: Stmt[]): Map<number, Stmt> {
|
public collect(ast: Stmt[]): Map<number, Stmt> {
|
||||||
|
@ -130,7 +130,7 @@ export function vtypeToString(vtype: VType): string {
|
|||||||
`${param.ident}: ${vtypeToString(param.vtype)}`
|
`${param.ident}: ${vtypeToString(param.vtype)}`
|
||||||
)
|
)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
return `fn (${paramString}) -> ${vtypeToString(vtype.returnType)}`;
|
return `fn(${paramString}) -> ${vtypeToString(vtype.returnType)}`;
|
||||||
}
|
}
|
||||||
if (vtype.type === "generic") {
|
if (vtype.type === "generic") {
|
||||||
return `generic`;
|
return `generic`;
|
||||||
|
Loading…
Reference in New Issue
Block a user