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