mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-05-05 03:24:05 +02:00
compiler: if + other things
This commit is contained in:
parent
87561c624d
commit
010a3e7d19
@ -26,7 +26,7 @@ export class Checker {
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private entryFileAst: ast.File,
|
||||
private resols: resolve.Resols,
|
||||
private re: resolve.Resols,
|
||||
) {
|
||||
this.currentFile = ctx.entryFile();
|
||||
}
|
||||
@ -48,20 +48,20 @@ export class Checker {
|
||||
|
||||
const exprTy = kind.expr && Ok(this.exprTy(kind.expr));
|
||||
const tyTy = kind.ty && Ok(this.tyTy(kind.ty));
|
||||
const ty = exprTy !== undefined
|
||||
? tyTy !== undefined
|
||||
? this.resolveTys(exprTy.val, tyTy.val)
|
||||
: exprTy
|
||||
: exprTy;
|
||||
const ty = exprTy !== undefined && tyTy !== undefined
|
||||
? this.resolveTys(exprTy.val, tyTy.val)
|
||||
: exprTy || tyTy;
|
||||
|
||||
this.stmtChecked.add(stmt.id);
|
||||
|
||||
if (ty === undefined) {
|
||||
this.assignPatTy(kind.pat, Ty({ tag: "error" }));
|
||||
this.report("type amfibious, specify type or value", stmt.span);
|
||||
return Ty({ tag: "error" });
|
||||
}
|
||||
|
||||
if (!ty.ok) {
|
||||
this.assignPatTy(kind.pat, Ty({ tag: "error" }));
|
||||
this.report(ty.val, stmt.span);
|
||||
return Ty({ tag: "error" });
|
||||
}
|
||||
@ -116,7 +116,7 @@ export class Checker {
|
||||
case "int":
|
||||
return Ty({ tag: "int" });
|
||||
case "bool":
|
||||
return todo();
|
||||
return Ty({ tag: "bool" });
|
||||
case "str":
|
||||
return todo();
|
||||
case "group":
|
||||
@ -141,12 +141,58 @@ export class Checker {
|
||||
return this.checkCallExpr(expr, k, expected);
|
||||
case "unary":
|
||||
return todo();
|
||||
case "binary":
|
||||
return todo();
|
||||
case "block":
|
||||
return todo();
|
||||
case "if":
|
||||
return todo();
|
||||
case "binary": {
|
||||
const res = this.resolveTys(
|
||||
this.exprTy(k.left),
|
||||
this.exprTy(k.right),
|
||||
);
|
||||
if (!res.ok) {
|
||||
this.exprTys.set(expr.id, Ty({ tag: "error" }));
|
||||
this.report(res.val, expr.span);
|
||||
return Ty({ tag: "error" });
|
||||
}
|
||||
this.exprTys.set(expr.id, res.val);
|
||||
return res.val;
|
||||
}
|
||||
case "block": {
|
||||
const ty = this.checkBlock(k.block, expected);
|
||||
return ty;
|
||||
}
|
||||
case "if": {
|
||||
const cond = this.exprTy(k.cond);
|
||||
const condRes = this.resolveTys(cond, Ty({ tag: "bool" }));
|
||||
if (!condRes.ok) {
|
||||
this.exprTys.set(expr.id, Ty({ tag: "error" }));
|
||||
this.report("if-condition must be a boolean", k.cond.span);
|
||||
return Ty({ tag: "error" });
|
||||
}
|
||||
const truthy = this.exprTy(k.truthy);
|
||||
if (!k.falsy) {
|
||||
const truthyRes = this.resolveTys(
|
||||
truthy,
|
||||
Ty({ tag: "null" }),
|
||||
);
|
||||
if (!truthyRes.ok) {
|
||||
this.exprTys.set(expr.id, Ty({ tag: "error" }));
|
||||
this.report(
|
||||
"if there isn't a falsy-clause, then the truthy clause must evaluate to null",
|
||||
k.truthy.span,
|
||||
);
|
||||
return Ty({ tag: "error" });
|
||||
}
|
||||
this.exprTys.set(expr.id, Ty({ tag: "null" }));
|
||||
return Ty({ tag: "null" });
|
||||
}
|
||||
const falsy = this.exprTy(k.falsy);
|
||||
const bothRes = this.resolveTys(truthy, falsy);
|
||||
if (!bothRes.ok) {
|
||||
this.exprTys.set(expr.id, Ty({ tag: "error" }));
|
||||
this.report(bothRes.val, k.truthy.span);
|
||||
return Ty({ tag: "error" });
|
||||
}
|
||||
this.exprTys.set(expr.id, bothRes.val);
|
||||
return bothRes.val;
|
||||
}
|
||||
case "loop":
|
||||
return todo();
|
||||
case "while":
|
||||
@ -164,7 +210,7 @@ export class Checker {
|
||||
kind: ast.PathExpr,
|
||||
expected: Ty,
|
||||
): Ty {
|
||||
const res = this.resols.exprRes(expr.id);
|
||||
const res = this.re.exprRes(expr.id);
|
||||
switch (res.kind.tag) {
|
||||
case "error":
|
||||
return Ty({ tag: "error" });
|
||||
@ -179,7 +225,7 @@ export class Checker {
|
||||
return resu.val;
|
||||
}
|
||||
case "local": {
|
||||
const patRes = this.resols.patRes(res.kind.id);
|
||||
const patRes = this.re.patRes(res.kind.id);
|
||||
const ty = this.patTy(patRes.pat);
|
||||
const resu = this.resolveTys(ty, expected);
|
||||
if (!resu.ok) {
|
||||
@ -256,7 +302,7 @@ export class Checker {
|
||||
}
|
||||
|
||||
private checkPat(pat: ast.Pat): Ty {
|
||||
const patRes = this.resols.patRes(pat.id);
|
||||
const patRes = this.re.patRes(pat.id);
|
||||
const k = pat.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
@ -303,12 +349,13 @@ export class Checker {
|
||||
if (b.kind.tag === "unknown") {
|
||||
return Res.Ok(a);
|
||||
}
|
||||
const as = tyToString(this.ctx, a);
|
||||
const bs = tyToString(this.ctx, b);
|
||||
const incompat = () =>
|
||||
Res.Err(
|
||||
const incompat = () => {
|
||||
const as = tyToString(this.ctx, a);
|
||||
const bs = tyToString(this.ctx, b);
|
||||
return Res.Err(
|
||||
`type '${as}' not compatible with type '${bs}'`,
|
||||
);
|
||||
};
|
||||
switch (a.kind.tag) {
|
||||
case "unknown":
|
||||
return this.resolveTys(b, a);
|
||||
@ -324,6 +371,12 @@ export class Checker {
|
||||
}
|
||||
return Res.Ok(a);
|
||||
}
|
||||
case "bool": {
|
||||
if (b.kind.tag !== "bool") {
|
||||
return incompat();
|
||||
}
|
||||
return Res.Ok(a);
|
||||
}
|
||||
case "fn": {
|
||||
if (b.kind.tag !== "fn") {
|
||||
return incompat();
|
||||
|
@ -3,15 +3,8 @@ import { Checker } from "@slige/check";
|
||||
import { AstId, Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common";
|
||||
import { Resols } from "@slige/resolve";
|
||||
import { Ty } from "@slige/ty";
|
||||
import {
|
||||
BinaryType,
|
||||
Operand,
|
||||
Place,
|
||||
ProjElem,
|
||||
StmtKind,
|
||||
TerKind,
|
||||
} from "./mir.ts";
|
||||
import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts";
|
||||
import { BinaryType, Operand, ProjElem, StmtKind, TerKind } from "./mir.ts";
|
||||
import { Block, BlockId, Fn, Local, LocalId, RVal } from "./mir.ts";
|
||||
import { MirFnStringifyer } from "@slige/stringify";
|
||||
|
||||
export class AstLowerer implements ast.Visitor {
|
||||
@ -55,6 +48,8 @@ export class FnLowerer {
|
||||
|
||||
private reLocals = new IdMap<AstId, LocalId>();
|
||||
|
||||
private paramLocals = new IdMap<LocalId, number>();
|
||||
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private re: Resols,
|
||||
@ -85,7 +80,10 @@ export class FnLowerer {
|
||||
label: this.ctx.identText(this.item.ident.id),
|
||||
locals: this.locals,
|
||||
blocks: this.blocks,
|
||||
entry,
|
||||
entry: entry.id,
|
||||
paramLocals: this.paramLocals,
|
||||
astItem: this.item,
|
||||
astItemKind: this.kind,
|
||||
});
|
||||
}
|
||||
|
||||
@ -112,8 +110,13 @@ export class FnLowerer {
|
||||
case "break":
|
||||
case "continue":
|
||||
case "assign":
|
||||
case "expr":
|
||||
return todo(k.tag);
|
||||
case "expr": {
|
||||
const rval = this.lowerExpr(k.expr);
|
||||
// ignore the fuck out of the value
|
||||
void rval;
|
||||
return;
|
||||
}
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
@ -122,13 +125,7 @@ export class FnLowerer {
|
||||
const val = kind.expr && this.lowerExpr(kind.expr);
|
||||
this.allocatePat(kind.pat);
|
||||
if (val) {
|
||||
const local = this.local(this.ch.patTy(kind.pat));
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local: local, proj: [] },
|
||||
rval: val,
|
||||
});
|
||||
this.assignPat(kind.pat, local, []);
|
||||
this.assignPatRVal(kind.pat, val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +136,7 @@ export class FnLowerer {
|
||||
return;
|
||||
case "bind": {
|
||||
const ty = this.ch.patTy(pat);
|
||||
const local = this.local(ty);
|
||||
const local = this.local(ty, k.ident);
|
||||
this.reLocals.set(pat.id, local);
|
||||
return;
|
||||
}
|
||||
@ -149,7 +146,27 @@ export class FnLowerer {
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private assignPat(pat: ast.Pat, local: LocalId, proj: ProjElem[]) {
|
||||
private assignPatRVal(pat: ast.Pat, rval: RVal) {
|
||||
const k = pat.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
return;
|
||||
case "bind": {
|
||||
const patLocal = this.reLocals.get(pat.id)!;
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local: patLocal, proj: [] },
|
||||
rval,
|
||||
});
|
||||
return;
|
||||
}
|
||||
case "path":
|
||||
return todo();
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private assignPat(pat: ast.Pat, local: LocalId, proj: ProjElem[] = []) {
|
||||
const k = pat.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
@ -180,19 +197,12 @@ export class FnLowerer {
|
||||
case "path":
|
||||
return this.lowerPathExpr(expr, k);
|
||||
case "null":
|
||||
return {
|
||||
tag: "use",
|
||||
operand: { tag: "const", val: { tag: "null" } },
|
||||
};
|
||||
case "int":
|
||||
case "bool":
|
||||
return {
|
||||
tag: "use",
|
||||
operand: {
|
||||
tag: "const",
|
||||
val: { tag: "int", value: k.value },
|
||||
},
|
||||
operand: this.lowerExprToOperand(expr),
|
||||
};
|
||||
case "bool":
|
||||
case "str":
|
||||
case "group":
|
||||
case "array":
|
||||
@ -211,7 +221,9 @@ export class FnLowerer {
|
||||
case "binary":
|
||||
return this.lowerBinaryExpr(expr, k);
|
||||
case "block":
|
||||
return this.lowerBlock(k.block);
|
||||
case "if":
|
||||
return this.lowerIfExpr(expr, k);
|
||||
case "loop":
|
||||
case "while":
|
||||
case "for":
|
||||
@ -226,63 +238,25 @@ export class FnLowerer {
|
||||
switch (re.kind.tag) {
|
||||
case "error":
|
||||
return { tag: "error" };
|
||||
case "fn": {
|
||||
const ty = this.ch.fnItemTy(re.kind.item, re.kind.kind);
|
||||
const local = this.local(ty);
|
||||
case "fn":
|
||||
case "local":
|
||||
return {
|
||||
tag: "use",
|
||||
operand: { tag: "move", place: { local, proj: [] } },
|
||||
operand: this.lowerPathExprToOperand(expr, kind),
|
||||
};
|
||||
}
|
||||
case "local": {
|
||||
const ty = this.ch.exprTy(expr);
|
||||
const local = this.local(ty);
|
||||
this.reLocals.set(re.kind.id, local);
|
||||
const isCopyable = (() => {
|
||||
switch (ty.kind.tag) {
|
||||
case "error":
|
||||
case "unknown":
|
||||
return false;
|
||||
case "null":
|
||||
case "int":
|
||||
return true;
|
||||
case "fn":
|
||||
return false;
|
||||
}
|
||||
exhausted(ty.kind);
|
||||
})();
|
||||
return {
|
||||
tag: "use",
|
||||
operand: {
|
||||
tag: isCopyable ? "copy" : "move",
|
||||
place: { local, proj: [] },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
exhausted(re.kind);
|
||||
}
|
||||
|
||||
private lowerCallExpr(expr: ast.Expr, kind: ast.CallExpr): RVal {
|
||||
const args = kind.args.map((arg) =>
|
||||
this.rvalAsOperand(this.lowerExpr(arg), this.ch.exprTy(arg))
|
||||
);
|
||||
const func = this.rvalAsOperand(
|
||||
this.lowerExpr(kind.expr),
|
||||
this.ch.exprTy(expr),
|
||||
);
|
||||
const args = kind.args.map((arg) => this.lowerExprToOperand(arg));
|
||||
const func = this.lowerExprToOperand(kind.expr);
|
||||
return { tag: "call", func, args };
|
||||
}
|
||||
|
||||
private lowerBinaryExpr(expr: ast.Expr, kind: ast.BinaryExpr): RVal {
|
||||
const left = this.rvalAsOperand(
|
||||
this.lowerExpr(kind.left),
|
||||
this.ch.exprTy(kind.left),
|
||||
);
|
||||
const right = this.rvalAsOperand(
|
||||
this.lowerExpr(kind.right),
|
||||
this.ch.exprTy(kind.right),
|
||||
);
|
||||
const left = this.lowerExprToOperand(kind.left);
|
||||
const right = this.lowerExprToOperand(kind.right);
|
||||
const binaryType = ((kind): BinaryType => {
|
||||
switch (kind.binaryType) {
|
||||
case "+":
|
||||
@ -315,19 +289,139 @@ export class FnLowerer {
|
||||
return { tag: "binary", binaryType, left, right };
|
||||
}
|
||||
|
||||
private rvalAsOperand(rval: RVal, ty: Ty): Operand {
|
||||
const local = this.local(ty);
|
||||
this.addStmt({ tag: "assign", place: { local, proj: [] }, rval });
|
||||
return { tag: "move", place: { local, proj: [] } };
|
||||
private lowerIfExpr(expr: ast.Expr, kind: ast.IfExpr): RVal {
|
||||
const cond = this.lowerExprToOperand(kind.cond);
|
||||
const condBlock = this.currentBlock!;
|
||||
if (kind.falsy) {
|
||||
return todo();
|
||||
} else {
|
||||
if (this.ch.exprTy(expr).kind.tag !== "null") {
|
||||
throw new Error();
|
||||
}
|
||||
const truthBlock = this.pushBlock();
|
||||
this.lowerExpr(kind.truthy);
|
||||
const exit = this.pushBlock();
|
||||
this.setTer({ tag: "goto", target: exit.id }, truthBlock);
|
||||
this.setTer({
|
||||
tag: "switch",
|
||||
discr: cond,
|
||||
targets: [{ value: 1, target: truthBlock.id }],
|
||||
otherwise: exit.id,
|
||||
}, condBlock);
|
||||
return {
|
||||
tag: "use",
|
||||
operand: { tag: "const", val: { tag: "null" } },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private local(ty: Ty): LocalId {
|
||||
private lowerExprToOperand(expr: ast.Expr): Operand {
|
||||
const k = expr.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
return { tag: "error" };
|
||||
case "path":
|
||||
return this.lowerPathExprToOperand(expr, k);
|
||||
case "null":
|
||||
return { tag: "const", val: { tag: "null" } };
|
||||
case "int":
|
||||
return {
|
||||
tag: "const",
|
||||
val: { tag: "int", value: k.value },
|
||||
};
|
||||
case "bool":
|
||||
return {
|
||||
tag: "const",
|
||||
val: { tag: "int", value: k.value ? 1 : 0 },
|
||||
};
|
||||
case "str":
|
||||
case "group":
|
||||
case "array":
|
||||
case "repeat":
|
||||
case "struct":
|
||||
case "ref":
|
||||
case "deref":
|
||||
case "elem":
|
||||
case "field":
|
||||
case "index":
|
||||
case "call":
|
||||
case "unary":
|
||||
case "binary":
|
||||
case "block":
|
||||
case "if":
|
||||
case "loop":
|
||||
case "while":
|
||||
case "for":
|
||||
case "c_for": {
|
||||
const ty = this.ch.exprTy(expr);
|
||||
const rval = this.lowerExpr(expr);
|
||||
const local = this.local(ty);
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local, proj: [] },
|
||||
rval,
|
||||
});
|
||||
return { tag: "move", place: { local, proj: [] } };
|
||||
}
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private lowerPathExprToOperand(
|
||||
expr: ast.Expr,
|
||||
kind: ast.PathExpr,
|
||||
): Operand {
|
||||
const re = this.re.exprRes(expr.id);
|
||||
switch (re.kind.tag) {
|
||||
case "error":
|
||||
return { tag: "error" };
|
||||
case "local": {
|
||||
const patRes = this.re.patRes(re.kind.id);
|
||||
const ty = this.ch.exprTy(expr);
|
||||
let local: LocalId;
|
||||
if (this.reLocals.has(re.kind.id)) {
|
||||
local = this.reLocals.get(re.kind.id)!;
|
||||
} else {
|
||||
local = this.local(ty);
|
||||
this.reLocals.set(re.kind.id, local);
|
||||
}
|
||||
if (patRes.kind.tag === "fn_param") {
|
||||
this.paramLocals.set(local, patRes.kind.paramIdx);
|
||||
}
|
||||
const isCopyable = (() => {
|
||||
switch (ty.kind.tag) {
|
||||
case "error":
|
||||
case "unknown":
|
||||
return false;
|
||||
case "null":
|
||||
case "int":
|
||||
case "bool":
|
||||
return true;
|
||||
case "fn":
|
||||
return false;
|
||||
}
|
||||
exhausted(ty.kind);
|
||||
})();
|
||||
return {
|
||||
tag: isCopyable ? "copy" : "move",
|
||||
place: { local, proj: [] },
|
||||
};
|
||||
}
|
||||
case "fn": {
|
||||
const { item, kind } = re.kind;
|
||||
return { tag: "const", val: { tag: "fn", item, kind } };
|
||||
}
|
||||
}
|
||||
exhausted(re.kind);
|
||||
}
|
||||
|
||||
private local(ty: Ty, ident?: ast.Ident): LocalId {
|
||||
const id = this.localIds.nextThenStep();
|
||||
this.locals.set(id, { id, ty });
|
||||
this.locals.set(id, { id, ty, ident });
|
||||
return id;
|
||||
}
|
||||
|
||||
private pushBlock(): BlockId {
|
||||
private pushBlock(): Block {
|
||||
const id = this.blockIds.nextThenStep();
|
||||
const block: Block = {
|
||||
id,
|
||||
@ -336,15 +430,15 @@ export class FnLowerer {
|
||||
};
|
||||
this.blocks.set(id, block);
|
||||
this.currentBlock = block;
|
||||
return id;
|
||||
return block;
|
||||
}
|
||||
|
||||
private setTer(kind: TerKind) {
|
||||
this.currentBlock!.terminator = { kind };
|
||||
private setTer(kind: TerKind, block = this.currentBlock!) {
|
||||
block.terminator = { kind };
|
||||
}
|
||||
|
||||
private addStmt(kind: StmtKind) {
|
||||
this.currentBlock!.stmts.push({ kind });
|
||||
private addStmt(kind: StmtKind, block = this.currentBlock!) {
|
||||
block.stmts.push({ kind });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { IdBase, IdMap } from "@slige/common";
|
||||
import * as ast from "@slige/ast";
|
||||
import { Ty } from "@slige/ty";
|
||||
|
||||
export type Fn = {
|
||||
@ -6,6 +7,9 @@ export type Fn = {
|
||||
locals: IdMap<LocalId, Local>;
|
||||
blocks: IdMap<BlockId, Block>;
|
||||
entry: BlockId;
|
||||
paramLocals: IdMap<LocalId, number>;
|
||||
astItem: ast.Item;
|
||||
astItemKind: ast.FnItem;
|
||||
};
|
||||
|
||||
export type LocalId = IdBase & { readonly _: unique symbol };
|
||||
@ -13,6 +17,7 @@ export type LocalId = IdBase & { readonly _: unique symbol };
|
||||
export type Local = {
|
||||
id: LocalId;
|
||||
ty: Ty;
|
||||
ident?: ast.Ident;
|
||||
};
|
||||
|
||||
export type BlockId = IdBase & { readonly _: unique symbol };
|
||||
@ -102,6 +107,7 @@ export type BinaryType =
|
||||
export type UnaryType = "not" | "neg";
|
||||
|
||||
export type Operand =
|
||||
| { tag: "error" }
|
||||
| { tag: "copy"; place: Place }
|
||||
| { tag: "move"; place: Place }
|
||||
| { tag: "const"; val: Const };
|
||||
@ -109,5 +115,5 @@ export type Operand =
|
||||
export type Const =
|
||||
| { tag: "null" }
|
||||
| { tag: "int"; value: number }
|
||||
| { tag: "bool"; value: boolean }
|
||||
| { tag: "string"; value: string };
|
||||
| { tag: "str"; value: string }
|
||||
| { tag: "fn"; item: ast.Item; kind: ast.FnItem };
|
||||
|
@ -5,7 +5,10 @@ fn add(lhs: int, rhs: int) -> int {
|
||||
|
||||
fn main() {
|
||||
let a = 5;
|
||||
let b = 7;
|
||||
let b = 7 + a;
|
||||
|
||||
if true {}
|
||||
|
||||
let c = add(a, b);
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,9 @@ export class HirStringifyer {
|
||||
this.expr(k.right)
|
||||
}`;
|
||||
case "block":
|
||||
return todo(k.tag);
|
||||
case "if":
|
||||
return `if ${this.expr(k.cond)}`;
|
||||
case "loop":
|
||||
case "while":
|
||||
case "for":
|
||||
@ -164,6 +166,8 @@ export class HirStringifyer {
|
||||
return "null";
|
||||
case "int":
|
||||
return "int";
|
||||
case "bool":
|
||||
return "bool";
|
||||
case "fn":
|
||||
return `fn ${k.item.ident}(${
|
||||
k.params.map((param) => this.ty(param)).join(", ")
|
||||
|
@ -26,11 +26,35 @@ export class MirFnStringifyer {
|
||||
) {}
|
||||
|
||||
public fn(fn: Fn): string {
|
||||
return `fn ${fn.label} {\n${
|
||||
fn.locals.values().toArray()
|
||||
.map((local) => this.localDef(local))
|
||||
.join("\n")
|
||||
}\n${
|
||||
for (
|
||||
const [idx, id] of fn.blocks
|
||||
.keys()
|
||||
.toArray()
|
||||
.entries()
|
||||
) {
|
||||
this.blockIds.set(id, idx);
|
||||
}
|
||||
for (
|
||||
const [idx, id] of fn.locals
|
||||
.keys()
|
||||
.toArray()
|
||||
.entries()
|
||||
) {
|
||||
this.localIds.set(id, idx);
|
||||
}
|
||||
const locals = fn.locals.values().toArray();
|
||||
const paramsStr = locals
|
||||
.filter((local) => fn.paramLocals.has(local.id))
|
||||
.map((local) => [local, fn.paramLocals.get(local.id)!] as const)
|
||||
.toSorted((a, b) => a[1] - b[1])
|
||||
.map(([local]) => fn.locals.get(local.id)!)
|
||||
.map((local) => this.localDef(local))
|
||||
.join(", ");
|
||||
const localsStr = locals
|
||||
.filter((local) => !fn.paramLocals.has(local.id))
|
||||
.map((local) => `#let ${this.localDef(local)}`)
|
||||
.join("\n");
|
||||
return `fn ${fn.label}(${paramsStr}) {\n${localsStr}\n${
|
||||
fn.blocks.values().toArray()
|
||||
.map((block) => this.block(block))
|
||||
.join("\n")
|
||||
@ -38,19 +62,20 @@ export class MirFnStringifyer {
|
||||
}
|
||||
|
||||
private localDef(local: Local): string {
|
||||
const id = this.localIds.size;
|
||||
this.localIds.set(local.id, id);
|
||||
return `#let %${id}: ${tyToString(this.ctx, local.ty)}`;
|
||||
const ident = local.ident && ` // ${local.ident.text}` || "";
|
||||
return `${this.local(local.id)}: ${this.ty(local.ty)}${ident}`;
|
||||
}
|
||||
|
||||
private block(block: Block): string {
|
||||
const id = this.blockIds.size;
|
||||
this.blockIds.set(block.id, id);
|
||||
const id = this.blockIds.get(block.id);
|
||||
return `#.b${id}: {\n${
|
||||
block.stmts
|
||||
.map((stmt) => this.stmt(stmt))
|
||||
[
|
||||
...block.stmts
|
||||
.map((stmt) => this.stmt(stmt)),
|
||||
this.ter(block.terminator),
|
||||
]
|
||||
.join("\n")
|
||||
}\n${this.ter(block.terminator)}\n#}`;
|
||||
}\n#}`;
|
||||
}
|
||||
|
||||
private stmt(stmt: Stmt): string {
|
||||
@ -76,8 +101,17 @@ export class MirFnStringifyer {
|
||||
case "unset":
|
||||
return "##<unset>;";
|
||||
case "goto":
|
||||
case "switch":
|
||||
return todo(k.tag);
|
||||
return `##goto ${this.blockId(k.target)}`;
|
||||
case "switch": {
|
||||
const discr = this.operand(k.discr);
|
||||
const targets = k.targets
|
||||
.map((target) =>
|
||||
`\n###${target.value} => ${this.blockId(target.target)}`
|
||||
)
|
||||
.join("");
|
||||
const otherwise = this.blockId(k.otherwise);
|
||||
return `##switch ${discr}${targets}\n###_ => ${otherwise}`;
|
||||
}
|
||||
case "return":
|
||||
return `##return;`;
|
||||
case "unreachable":
|
||||
@ -122,14 +156,16 @@ export class MirFnStringifyer {
|
||||
case "ref":
|
||||
case "ptr":
|
||||
return todo(rval.tag);
|
||||
case "binary":
|
||||
return `${this.operand(rval.left)} ${rval.binaryType} ${
|
||||
this.operand(rval.right)
|
||||
}`;
|
||||
case "binary": {
|
||||
const op = rval.binaryType;
|
||||
const left = this.operand(rval.left);
|
||||
const right = this.operand(rval.right);
|
||||
return `${op}(${left}, ${right})`;
|
||||
}
|
||||
case "unary":
|
||||
return todo(rval.tag);
|
||||
case "call":
|
||||
return `${this.operand(rval.func)}(${
|
||||
return `call ${this.operand(rval.func)}(${
|
||||
rval.args.map((arg) => this.operand(arg)).join(", ")
|
||||
})`;
|
||||
}
|
||||
@ -138,6 +174,8 @@ export class MirFnStringifyer {
|
||||
|
||||
private operand(operand: Operand): string {
|
||||
switch (operand.tag) {
|
||||
case "error":
|
||||
return `<error>`;
|
||||
case "copy":
|
||||
return `copy ${this.place(operand.place)}`;
|
||||
case "move":
|
||||
@ -154,15 +192,23 @@ export class MirFnStringifyer {
|
||||
return `null`;
|
||||
case "int":
|
||||
return `${val.value}`;
|
||||
case "bool":
|
||||
return `${val.value}`;
|
||||
case "string":
|
||||
case "str":
|
||||
return `"${val.value}"`;
|
||||
case "fn":
|
||||
return `${val.item.ident.text}`;
|
||||
}
|
||||
exhausted(val);
|
||||
}
|
||||
|
||||
private blockId(id: BlockId): string {
|
||||
return `.b${this.blockIds.get(id)!}`;
|
||||
}
|
||||
|
||||
private local(local: LocalId): string {
|
||||
return `%${this.localIds.get(local)!}`;
|
||||
return `_${this.localIds.get(local)!}`;
|
||||
}
|
||||
|
||||
private ty(ty: Ty): string {
|
||||
return tyToString(this.ctx, ty);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ export function tyToString(ctx: Ctx, ty: Ty): string {
|
||||
return `null`;
|
||||
case "int":
|
||||
return `int`;
|
||||
case "bool":
|
||||
return `bool`;
|
||||
case "fn": {
|
||||
const identText = ctx.identText(k.item.ident.id);
|
||||
const params = k.params
|
||||
|
@ -11,6 +11,7 @@ export type TyKind =
|
||||
| { tag: "unknown" }
|
||||
| { tag: "null" }
|
||||
| { tag: "int" }
|
||||
| { tag: "bool" }
|
||||
| {
|
||||
tag: "fn";
|
||||
item: ast.Item;
|
||||
|
Loading…
x
Reference in New Issue
Block a user