mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
compiler: add loops
This commit is contained in:
parent
3d161efb99
commit
a97a128336
@ -37,7 +37,7 @@ export class Checker {
|
||||
Ty({ tag: "null" });
|
||||
}
|
||||
|
||||
private checkLetStmtTy(stmt: ast.Stmt, kind: ast.LetStmt) {
|
||||
private checkLetStmt(stmt: ast.Stmt, kind: ast.LetStmt) {
|
||||
if (this.stmtChecked.has(stmt.id)) {
|
||||
return;
|
||||
}
|
||||
@ -84,6 +84,42 @@ export class Checker {
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
public checkBreakStmt(stmt: ast.Stmt, kind: ast.BreakStmt) {
|
||||
if (this.stmtChecked.has(stmt.id)) {
|
||||
return;
|
||||
}
|
||||
this.stmtChecked.add(stmt.id);
|
||||
const re = this.re.loopRes(stmt.id);
|
||||
if (re.tag === "error") {
|
||||
return;
|
||||
}
|
||||
if (re.tag !== "loop") {
|
||||
if (kind.expr) {
|
||||
this.report(
|
||||
`'${re.tag}'-style loop cannot break with value`,
|
||||
stmt.span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const exTy = this.exprTys.get(re.expr.id)!;
|
||||
if (!kind.expr) {
|
||||
const ty = Ty({ tag: "null" });
|
||||
const tyRes = this.resolveTys(ty, exTy);
|
||||
if (!tyRes.ok) {
|
||||
this.report(tyRes.val, stmt.span);
|
||||
return;
|
||||
}
|
||||
this.exprTys.set(re.expr.id, tyRes.val)!;
|
||||
return;
|
||||
}
|
||||
const ty = this.exprTy(kind.expr, exTy);
|
||||
if (ty.kind.tag !== "error") {
|
||||
this.exprTys.set(re.expr.id, ty);
|
||||
}
|
||||
}
|
||||
|
||||
public checkAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
|
||||
if (this.stmtChecked.has(stmt.id)) {
|
||||
return;
|
||||
@ -207,9 +243,9 @@ export class Checker {
|
||||
return Ty({ tag: "fn", item, kind, params, returnTy });
|
||||
}
|
||||
|
||||
public exprTy(expr: ast.Expr): Ty {
|
||||
public exprTy(expr: ast.Expr, expected = Ty({ tag: "unknown" })): Ty {
|
||||
return this.exprTys.get(expr.id) ||
|
||||
this.checkExpr(expr, Ty({ tag: "unknown" }));
|
||||
this.checkExpr(expr, expected);
|
||||
}
|
||||
|
||||
private checkExpr(expr: ast.Expr, expected: Ty): Ty {
|
||||
@ -256,13 +292,13 @@ export class Checker {
|
||||
case "if":
|
||||
return this.checkIfExpr(expr, k, expected);
|
||||
case "loop":
|
||||
return todo();
|
||||
return this.checkLoopExpr(expr, k, expected);
|
||||
case "while":
|
||||
return todo();
|
||||
return this.checkWhileExpr(expr, k, expected);
|
||||
case "for":
|
||||
return todo();
|
||||
case "c_for":
|
||||
return todo();
|
||||
return this.checkCForExpr(expr, k, expected);
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
@ -427,6 +463,80 @@ export class Checker {
|
||||
return bothRes.val;
|
||||
}
|
||||
|
||||
private checkLoopExpr(
|
||||
expr: ast.Expr,
|
||||
kind: ast.LoopExpr,
|
||||
expected: Ty,
|
||||
): Ty {
|
||||
this.exprTys.set(expr.id, expected);
|
||||
|
||||
const body = this.exprTy(kind.body, Ty({ tag: "unknown" }));
|
||||
if (body.kind.tag !== "null") {
|
||||
if (body.kind.tag !== "error") {
|
||||
this.report("loop body must not yield a value", kind.body.span);
|
||||
}
|
||||
const ty = Ty({ tag: "error" });
|
||||
this.exprTys.set(expr.id, ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
for (const { stmt, kind } of this.re.loopBreaks(expr.id)) {
|
||||
this.checkBreakStmt(stmt, kind);
|
||||
}
|
||||
|
||||
return this.exprTys.get(expr.id)!;
|
||||
}
|
||||
|
||||
private checkWhileExpr(
|
||||
expr: ast.Expr,
|
||||
kind: ast.LoopExpr,
|
||||
expected: Ty,
|
||||
): Ty {
|
||||
const ty = Ty({ tag: "null" });
|
||||
this.exprTys.set(expr.id, ty);
|
||||
|
||||
const body = this.exprTy(kind.body, Ty({ tag: "unknown" }));
|
||||
if (body.kind.tag !== "null") {
|
||||
if (body.kind.tag !== "error") {
|
||||
this.report("loop body must not yield a value", kind.body.span);
|
||||
}
|
||||
const ty = Ty({ tag: "error" });
|
||||
this.exprTys.set(expr.id, ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
for (const { stmt, kind } of this.re.loopBreaks(expr.id)) {
|
||||
this.checkBreakStmt(stmt, kind);
|
||||
}
|
||||
|
||||
return ty;
|
||||
}
|
||||
|
||||
private checkCForExpr(
|
||||
expr: ast.Expr,
|
||||
kind: ast.LoopExpr,
|
||||
expected: Ty,
|
||||
): Ty {
|
||||
const ty = Ty({ tag: "null" });
|
||||
this.exprTys.set(expr.id, ty);
|
||||
|
||||
const body = this.exprTy(kind.body, Ty({ tag: "unknown" }));
|
||||
if (body.kind.tag !== "null") {
|
||||
if (body.kind.tag !== "error") {
|
||||
this.report("loop body must not yield a value", kind.body.span);
|
||||
}
|
||||
const ty = Ty({ tag: "error" });
|
||||
this.exprTys.set(expr.id, ty);
|
||||
return ty;
|
||||
}
|
||||
|
||||
for (const { stmt, kind } of this.re.loopBreaks(expr.id)) {
|
||||
this.checkBreakStmt(stmt, kind);
|
||||
}
|
||||
|
||||
return ty;
|
||||
}
|
||||
|
||||
private tyTy(ty: ast.Ty): Ty {
|
||||
return this.tyTys.get(ty.id) ||
|
||||
this.checkTy(ty);
|
||||
@ -484,7 +594,7 @@ export class Checker {
|
||||
return ty;
|
||||
}
|
||||
case "let": {
|
||||
this.checkLetStmtTy(patRes.kind.stmt, patRes.kind.kind);
|
||||
this.checkLetStmt(patRes.kind.stmt, patRes.kind.kind);
|
||||
const ty = this.patTy(pat);
|
||||
this.patTys.set(pat.id, ty);
|
||||
return ty;
|
||||
|
@ -50,6 +50,12 @@ export class FnLowerer {
|
||||
|
||||
private paramLocals = new IdMap<LocalId, number>();
|
||||
|
||||
private loopInfos = new IdMap<AstId, {
|
||||
loopBlock: BlockId;
|
||||
endBlock: BlockId;
|
||||
resultLocal?: LocalId;
|
||||
}>();
|
||||
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private re: Resols,
|
||||
@ -107,7 +113,9 @@ export class FnLowerer {
|
||||
case "let":
|
||||
return this.lowerLetStmt(stmt, k);
|
||||
case "return":
|
||||
return todo(k.tag);
|
||||
case "break":
|
||||
return this.lowerBreakStmt(stmt, k);
|
||||
case "continue":
|
||||
return todo(k.tag);
|
||||
case "assign":
|
||||
@ -169,6 +177,27 @@ export class FnLowerer {
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private lowerBreakStmt(stmt: ast.Stmt, kind: ast.BreakStmt) {
|
||||
this.ch.checkBreakStmt(stmt, kind);
|
||||
const re = this.re.loopRes(stmt.id);
|
||||
if (re.tag === "error") {
|
||||
return;
|
||||
}
|
||||
const info = this.loopInfos.get(re.expr.id)!;
|
||||
if (kind.expr) {
|
||||
const ty = this.ch.exprTy(kind.expr);
|
||||
info.resultLocal = info.resultLocal ?? this.local(ty);
|
||||
const rval = this.lowerExpr(kind.expr);
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local: info.resultLocal, proj: [] },
|
||||
rval,
|
||||
});
|
||||
}
|
||||
this.setTer({ tag: "goto", target: info.endBlock });
|
||||
this.pushBlock();
|
||||
}
|
||||
|
||||
private lowerAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
|
||||
this.ch.checkAssignStmt(stmt, kind);
|
||||
const rval = this.lowerExpr(kind.value);
|
||||
@ -262,10 +291,13 @@ export class FnLowerer {
|
||||
case "if":
|
||||
return this.lowerIfExpr(expr, k);
|
||||
case "loop":
|
||||
return this.lowerLoopExpr(expr, k);
|
||||
case "while":
|
||||
return this.lowerWhileExpr(expr, k);
|
||||
case "for":
|
||||
case "c_for":
|
||||
return todo(k.tag);
|
||||
case "c_for":
|
||||
return this.lowerCForExpr(expr, k);
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
@ -367,8 +399,11 @@ export class FnLowerer {
|
||||
}
|
||||
const truthBlock = this.pushBlock();
|
||||
this.lowerExpr(kind.truthy);
|
||||
const exit = this.pushBlock();
|
||||
this.setTer({ tag: "goto", target: exit.id }, truthBlock);
|
||||
const exit = this.createBlock();
|
||||
|
||||
this.setTer({ tag: "goto", target: exit.id });
|
||||
this.pushCreatedBlock(exit);
|
||||
|
||||
this.setTer({
|
||||
tag: "switch",
|
||||
discr,
|
||||
@ -382,6 +417,146 @@ export class FnLowerer {
|
||||
}
|
||||
}
|
||||
|
||||
private lowerLoopExpr(expr: ast.Expr, kind: ast.LoopExpr): RVal {
|
||||
const entryBlock = this.currentBlock!;
|
||||
const loopBlock = this.pushBlock();
|
||||
|
||||
const endBlock = this.createBlock();
|
||||
|
||||
const info = {
|
||||
loopBlock: loopBlock.id,
|
||||
endBlock: endBlock.id,
|
||||
resultLocal: undefined,
|
||||
};
|
||||
this.loopInfos.set(expr.id, info);
|
||||
|
||||
const rval = this.lowerExpr(kind.body);
|
||||
// ignore value;
|
||||
void rval;
|
||||
|
||||
this.setTer({ tag: "goto", target: loopBlock.id });
|
||||
this.setTer({ tag: "goto", target: loopBlock.id }, entryBlock);
|
||||
|
||||
this.pushCreatedBlock(endBlock);
|
||||
|
||||
if (info.resultLocal) {
|
||||
const ty = this.ch.exprTy(expr);
|
||||
return {
|
||||
tag: "use",
|
||||
operand: this.copyOrMoveLocal(info.resultLocal, ty),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
tag: "use",
|
||||
operand: { tag: "const", val: { tag: "null" } },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private lowerWhileExpr(expr: ast.Expr, kind: ast.WhileExpr): RVal {
|
||||
const enterBlock = this.currentBlock!;
|
||||
const condBlock = this.pushBlock();
|
||||
this.setTer({ tag: "goto", target: condBlock.id }, enterBlock);
|
||||
const condTy = this.ch.exprTy(kind.cond);
|
||||
const condLocal = this.localMut(condTy);
|
||||
const condVal = this.lowerExpr(kind.cond);
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local: condLocal, proj: [] },
|
||||
rval: condVal,
|
||||
});
|
||||
|
||||
if (this.ch.exprTy(expr).kind.tag !== "null") {
|
||||
throw new Error();
|
||||
}
|
||||
const bodyBlock = this.pushBlock();
|
||||
const exitBlock = this.createBlock();
|
||||
|
||||
this.loopInfos.set(expr.id, {
|
||||
loopBlock: condBlock.id,
|
||||
endBlock: exitBlock.id,
|
||||
});
|
||||
|
||||
this.lowerExpr(kind.body);
|
||||
this.setTer({ tag: "goto", target: condBlock.id });
|
||||
|
||||
this.pushCreatedBlock(exitBlock);
|
||||
|
||||
this.setTer({
|
||||
tag: "switch",
|
||||
discr: this.copyOrMoveLocal(condLocal, condTy),
|
||||
targets: [{ value: 1, target: bodyBlock.id }],
|
||||
otherwise: exitBlock.id,
|
||||
}, condBlock);
|
||||
return {
|
||||
tag: "use",
|
||||
operand: { tag: "const", val: { tag: "null" } },
|
||||
};
|
||||
}
|
||||
|
||||
private lowerCForExpr(expr: ast.Expr, kind: ast.CForExpr): RVal {
|
||||
kind.decl && this.lowerStmt(kind.decl);
|
||||
const enterBlock = this.currentBlock!;
|
||||
|
||||
if (this.ch.exprTy(expr).kind.tag !== "null") {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
let loopBlock: Block;
|
||||
const exitBlock = this.createBlock();
|
||||
|
||||
if (kind.cond) {
|
||||
const condBlock = this.pushBlock();
|
||||
this.setTer({ tag: "goto", target: condBlock.id }, enterBlock);
|
||||
const condTy = this.ch.exprTy(kind.cond);
|
||||
const condLocal = this.localMut(condTy);
|
||||
const condVal = this.lowerExpr(kind.cond);
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local: condLocal, proj: [] },
|
||||
rval: condVal,
|
||||
});
|
||||
|
||||
const bodyBlock = this.pushBlock();
|
||||
|
||||
this.setTer({
|
||||
tag: "switch",
|
||||
discr: this.copyOrMoveLocal(condLocal, condTy),
|
||||
targets: [{ value: 1, target: bodyBlock.id }],
|
||||
otherwise: exitBlock.id,
|
||||
}, condBlock);
|
||||
|
||||
loopBlock = condBlock;
|
||||
|
||||
this.loopInfos.set(expr.id, {
|
||||
loopBlock: condBlock.id,
|
||||
endBlock: exitBlock.id,
|
||||
});
|
||||
} else {
|
||||
loopBlock = this.pushBlock();
|
||||
|
||||
this.setTer({ tag: "goto", target: loopBlock.id }, loopBlock);
|
||||
|
||||
this.loopInfos.set(expr.id, {
|
||||
loopBlock: loopBlock.id,
|
||||
endBlock: exitBlock.id,
|
||||
});
|
||||
}
|
||||
|
||||
this.lowerExpr(kind.body);
|
||||
if (kind.incr) {
|
||||
this.lowerStmt(kind.incr);
|
||||
}
|
||||
|
||||
this.setTer({ tag: "goto", target: loopBlock.id });
|
||||
|
||||
this.pushCreatedBlock(exitBlock);
|
||||
return {
|
||||
tag: "use",
|
||||
operand: { tag: "const", val: { tag: "null" } },
|
||||
};
|
||||
}
|
||||
|
||||
private lowerExprToOperand(expr: ast.Expr): Operand {
|
||||
const k = expr.kind;
|
||||
switch (k.tag) {
|
||||
@ -510,6 +685,21 @@ export class FnLowerer {
|
||||
return block;
|
||||
}
|
||||
|
||||
private createBlock(): Block {
|
||||
const id = this.blockIds.nextThenStep();
|
||||
const block: Block = {
|
||||
id,
|
||||
stmts: [],
|
||||
terminator: { kind: { tag: "unset" } },
|
||||
};
|
||||
return block;
|
||||
}
|
||||
|
||||
private pushCreatedBlock(block: Block) {
|
||||
this.blocks.set(block.id, block);
|
||||
this.currentBlock = block;
|
||||
}
|
||||
|
||||
private setTer(kind: TerKind, block = this.currentBlock!) {
|
||||
block.terminator = { kind };
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
|
||||
fn main() {
|
||||
let mut a = 5;
|
||||
|
||||
a = 10;
|
||||
for (let mut i = 0; i < 10; i = i + 1) {}
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,6 +33,18 @@ export const ResolveError = (ident: ast.Ident): Resolve => ({
|
||||
kind: { tag: "error" },
|
||||
});
|
||||
|
||||
export type LoopResolve =
|
||||
| { tag: "error" }
|
||||
| { tag: "loop"; expr: ast.Expr; kind: ast.LoopExpr }
|
||||
| { tag: "while"; expr: ast.Expr; kind: ast.WhileExpr }
|
||||
| { tag: "for"; expr: ast.Expr; kind: ast.ForExpr }
|
||||
| { tag: "cfor"; expr: ast.Expr; kind: ast.CForExpr };
|
||||
|
||||
export type LoopBreakResolve = {
|
||||
stmt: ast.Stmt;
|
||||
kind: ast.BreakStmt;
|
||||
};
|
||||
|
||||
export type Redef = {
|
||||
ident: ast.Ident;
|
||||
};
|
||||
|
@ -13,6 +13,8 @@ import {
|
||||
import {
|
||||
FnSyms,
|
||||
LocalSyms,
|
||||
LoopBreakResolve,
|
||||
LoopResolve,
|
||||
PatResolve,
|
||||
PatResolveKind,
|
||||
Redef,
|
||||
@ -26,6 +28,8 @@ export class Resols {
|
||||
public constructor(
|
||||
private exprResols: IdMap<AstId, Resolve>,
|
||||
private patResols: IdMap<AstId, PatResolve>,
|
||||
private loopsResols: IdMap<AstId, LoopResolve>,
|
||||
private loopBreakResols: IdMap<AstId, LoopBreakResolve[]>,
|
||||
) {}
|
||||
|
||||
public exprRes(id: AstId): Resolve {
|
||||
@ -41,6 +45,20 @@ export class Resols {
|
||||
}
|
||||
return this.patResols.get(id)!;
|
||||
}
|
||||
|
||||
public loopRes(id: AstId): LoopResolve {
|
||||
if (!this.loopsResols.has(id)) {
|
||||
throw new Error();
|
||||
}
|
||||
return this.loopsResols.get(id)!;
|
||||
}
|
||||
|
||||
public loopBreaks(id: AstId): LoopBreakResolve[] {
|
||||
if (!this.loopBreakResols.has(id)) {
|
||||
throw new Error();
|
||||
}
|
||||
return this.loopBreakResols.get(id)!;
|
||||
}
|
||||
}
|
||||
|
||||
export class Resolver implements ast.Visitor {
|
||||
@ -49,10 +67,14 @@ export class Resolver implements ast.Visitor {
|
||||
private syms: Syms = this.rootSyms;
|
||||
|
||||
private exprResols = new IdMap<AstId, Resolve>();
|
||||
private patResols = new IdMap<AstId, PatResolve>();
|
||||
|
||||
private patResols = new IdMap<AstId, PatResolve>();
|
||||
private patResolveStack: PatResolveKind[] = [];
|
||||
|
||||
private loopsResols = new IdMap<AstId, LoopResolve>();
|
||||
private loopBreakResols = new IdMap<AstId, LoopBreakResolve[]>();
|
||||
private loopResolveStack: LoopResolve[] = [{ tag: "error" }];
|
||||
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private entryFileAst: ast.File,
|
||||
@ -63,6 +85,8 @@ export class Resolver implements ast.Visitor {
|
||||
return new Resols(
|
||||
this.exprResols,
|
||||
this.patResols,
|
||||
this.loopsResols,
|
||||
this.loopBreakResols,
|
||||
);
|
||||
}
|
||||
|
||||
@ -101,6 +125,25 @@ export class Resolver implements ast.Visitor {
|
||||
todo();
|
||||
}
|
||||
|
||||
visitBreakStmt(stmt: ast.Stmt, kind: ast.BreakStmt): ast.VisitRes {
|
||||
const res = this.loopResolveStack.at(-1)!;
|
||||
if (res.tag === "error") {
|
||||
this.report("no loop to break", stmt.span);
|
||||
return;
|
||||
}
|
||||
this.loopsResols.set(stmt.id, res);
|
||||
this.loopBreakResols.get(res.expr.id)!.push({ stmt, kind });
|
||||
}
|
||||
|
||||
visitContinueStmt(stmt: ast.Stmt): ast.VisitRes {
|
||||
const res = this.loopResolveStack.at(-1)!;
|
||||
if (res.tag === "error") {
|
||||
this.report("no loop to continue", stmt.span);
|
||||
return;
|
||||
}
|
||||
this.loopsResols.set(stmt.id, res);
|
||||
}
|
||||
|
||||
visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes {
|
||||
todo();
|
||||
}
|
||||
@ -176,6 +219,42 @@ export class Resolver implements ast.Visitor {
|
||||
todo();
|
||||
}
|
||||
|
||||
visitLoopExpr(expr: ast.Expr, kind: ast.LoopExpr): ast.VisitRes {
|
||||
this.genericVisitLoop(expr, kind.body, { tag: "loop", expr, kind });
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitWhileExpr(expr: ast.Expr, kind: ast.WhileExpr): ast.VisitRes {
|
||||
ast.visitExpr(this, kind.cond);
|
||||
this.genericVisitLoop(expr, kind.body, { tag: "while", expr, kind });
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitForExpr(expr: ast.Expr, kind: ast.ForExpr): ast.VisitRes {
|
||||
todo();
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitCForExpr(expr: ast.Expr, kind: ast.CForExpr): ast.VisitRes {
|
||||
const outerSyms = this.syms;
|
||||
this.syms = new LocalSyms(this.syms);
|
||||
|
||||
kind.decl && ast.visitStmt(this, kind.decl);
|
||||
kind.cond && ast.visitExpr(this, kind.cond);
|
||||
kind.incr && ast.visitStmt(this, kind.incr);
|
||||
this.genericVisitLoop(expr, kind.body, { tag: "cfor", expr, kind });
|
||||
this.syms = outerSyms;
|
||||
return "stop";
|
||||
}
|
||||
|
||||
private genericVisitLoop(expr: ast.Expr, body: ast.Expr, res: LoopResolve) {
|
||||
this.loopResolveStack.push(res);
|
||||
this.loopBreakResols.set(expr.id, []);
|
||||
ast.visitExpr(this, body);
|
||||
this.loopResolveStack.pop();
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
|
||||
this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! });
|
||||
const res = this.syms.defVal(kind.ident, {
|
||||
|
@ -22,10 +22,11 @@ export class HirStringifyer {
|
||||
return "<error>;";
|
||||
case "item":
|
||||
return this.item(k.item);
|
||||
case "let":
|
||||
case "let": {
|
||||
return `let ${this.pat(k.pat)}${
|
||||
k.expr && ` = ${this.expr(k.expr, d)}` || ""
|
||||
};`;
|
||||
}
|
||||
case "return":
|
||||
return `return${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
|
||||
case "break":
|
||||
@ -33,7 +34,9 @@ export class HirStringifyer {
|
||||
case "continue":
|
||||
return `continue;`;
|
||||
case "assign":
|
||||
return `${this.expr(k.subject, d)} = ${this.expr(k.value, d)};`;
|
||||
return `${this.expr(k.subject, d)} ${k.assignType} ${
|
||||
this.expr(k.value, d)
|
||||
};`;
|
||||
case "expr":
|
||||
return `${this.expr(k.expr, d)};`;
|
||||
}
|
||||
@ -119,10 +122,17 @@ export class HirStringifyer {
|
||||
k.falsy && ` else ${this.expr(k.falsy, d)}` || ""
|
||||
}`;
|
||||
case "loop":
|
||||
return `loop ${this.expr(k.body, d)}`;
|
||||
case "while":
|
||||
return `while ${this.expr(k.cond, d)} ${this.expr(k.body, d)}`;
|
||||
case "for":
|
||||
case "c_for":
|
||||
return todo(k.tag);
|
||||
case "c_for":
|
||||
return `for (${k.decl && this.stmt(k.decl, d) || ";"}${
|
||||
k.cond && ` ${this.expr(k.cond, d)}` || ""
|
||||
};${k.incr && ` ${this.stmt(k.incr, d)}` || ""}) ${
|
||||
this.expr(k.body, d)
|
||||
}`;
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
|
@ -52,11 +52,13 @@ export class MirFnStringifyer {
|
||||
.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")
|
||||
}\n}`.replaceAll("#", " ");
|
||||
const blocks = fn.blocks
|
||||
.values()
|
||||
.toArray()
|
||||
.map((block) => this.block(block))
|
||||
.join("\n");
|
||||
return `fn ${fn.label}(${paramsStr}) {\n${localsStr}\n${blocks}\n}`
|
||||
.replaceAll("#", " ");
|
||||
}
|
||||
|
||||
private localDef(local: Local): string {
|
||||
|
Loading…
x
Reference in New Issue
Block a user