mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-28 08:44:06 +02:00
compiler: add loops
This commit is contained in:
parent
3d161efb99
commit
a97a128336
@ -37,7 +37,7 @@ export class Checker {
|
|||||||
Ty({ tag: "null" });
|
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)) {
|
if (this.stmtChecked.has(stmt.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -84,6 +84,42 @@ export class Checker {
|
|||||||
exhausted(k);
|
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) {
|
public checkAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
|
||||||
if (this.stmtChecked.has(stmt.id)) {
|
if (this.stmtChecked.has(stmt.id)) {
|
||||||
return;
|
return;
|
||||||
@ -207,9 +243,9 @@ export class Checker {
|
|||||||
return Ty({ tag: "fn", item, kind, params, returnTy });
|
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) ||
|
return this.exprTys.get(expr.id) ||
|
||||||
this.checkExpr(expr, Ty({ tag: "unknown" }));
|
this.checkExpr(expr, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkExpr(expr: ast.Expr, expected: Ty): Ty {
|
private checkExpr(expr: ast.Expr, expected: Ty): Ty {
|
||||||
@ -256,13 +292,13 @@ export class Checker {
|
|||||||
case "if":
|
case "if":
|
||||||
return this.checkIfExpr(expr, k, expected);
|
return this.checkIfExpr(expr, k, expected);
|
||||||
case "loop":
|
case "loop":
|
||||||
return todo();
|
return this.checkLoopExpr(expr, k, expected);
|
||||||
case "while":
|
case "while":
|
||||||
return todo();
|
return this.checkWhileExpr(expr, k, expected);
|
||||||
case "for":
|
case "for":
|
||||||
return todo();
|
return todo();
|
||||||
case "c_for":
|
case "c_for":
|
||||||
return todo();
|
return this.checkCForExpr(expr, k, expected);
|
||||||
}
|
}
|
||||||
exhausted(k);
|
exhausted(k);
|
||||||
}
|
}
|
||||||
@ -427,6 +463,80 @@ export class Checker {
|
|||||||
return bothRes.val;
|
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 {
|
private tyTy(ty: ast.Ty): Ty {
|
||||||
return this.tyTys.get(ty.id) ||
|
return this.tyTys.get(ty.id) ||
|
||||||
this.checkTy(ty);
|
this.checkTy(ty);
|
||||||
@ -484,7 +594,7 @@ export class Checker {
|
|||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
case "let": {
|
case "let": {
|
||||||
this.checkLetStmtTy(patRes.kind.stmt, patRes.kind.kind);
|
this.checkLetStmt(patRes.kind.stmt, patRes.kind.kind);
|
||||||
const ty = this.patTy(pat);
|
const ty = this.patTy(pat);
|
||||||
this.patTys.set(pat.id, ty);
|
this.patTys.set(pat.id, ty);
|
||||||
return ty;
|
return ty;
|
||||||
|
@ -50,6 +50,12 @@ export class FnLowerer {
|
|||||||
|
|
||||||
private paramLocals = new IdMap<LocalId, number>();
|
private paramLocals = new IdMap<LocalId, number>();
|
||||||
|
|
||||||
|
private loopInfos = new IdMap<AstId, {
|
||||||
|
loopBlock: BlockId;
|
||||||
|
endBlock: BlockId;
|
||||||
|
resultLocal?: LocalId;
|
||||||
|
}>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private ctx: Ctx,
|
private ctx: Ctx,
|
||||||
private re: Resols,
|
private re: Resols,
|
||||||
@ -107,7 +113,9 @@ export class FnLowerer {
|
|||||||
case "let":
|
case "let":
|
||||||
return this.lowerLetStmt(stmt, k);
|
return this.lowerLetStmt(stmt, k);
|
||||||
case "return":
|
case "return":
|
||||||
|
return todo(k.tag);
|
||||||
case "break":
|
case "break":
|
||||||
|
return this.lowerBreakStmt(stmt, k);
|
||||||
case "continue":
|
case "continue":
|
||||||
return todo(k.tag);
|
return todo(k.tag);
|
||||||
case "assign":
|
case "assign":
|
||||||
@ -169,6 +177,27 @@ export class FnLowerer {
|
|||||||
exhausted(k);
|
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) {
|
private lowerAssignStmt(stmt: ast.Stmt, kind: ast.AssignStmt) {
|
||||||
this.ch.checkAssignStmt(stmt, kind);
|
this.ch.checkAssignStmt(stmt, kind);
|
||||||
const rval = this.lowerExpr(kind.value);
|
const rval = this.lowerExpr(kind.value);
|
||||||
@ -262,10 +291,13 @@ export class FnLowerer {
|
|||||||
case "if":
|
case "if":
|
||||||
return this.lowerIfExpr(expr, k);
|
return this.lowerIfExpr(expr, k);
|
||||||
case "loop":
|
case "loop":
|
||||||
|
return this.lowerLoopExpr(expr, k);
|
||||||
case "while":
|
case "while":
|
||||||
|
return this.lowerWhileExpr(expr, k);
|
||||||
case "for":
|
case "for":
|
||||||
case "c_for":
|
|
||||||
return todo(k.tag);
|
return todo(k.tag);
|
||||||
|
case "c_for":
|
||||||
|
return this.lowerCForExpr(expr, k);
|
||||||
}
|
}
|
||||||
exhausted(k);
|
exhausted(k);
|
||||||
}
|
}
|
||||||
@ -367,8 +399,11 @@ export class FnLowerer {
|
|||||||
}
|
}
|
||||||
const truthBlock = this.pushBlock();
|
const truthBlock = this.pushBlock();
|
||||||
this.lowerExpr(kind.truthy);
|
this.lowerExpr(kind.truthy);
|
||||||
const exit = this.pushBlock();
|
const exit = this.createBlock();
|
||||||
this.setTer({ tag: "goto", target: exit.id }, truthBlock);
|
|
||||||
|
this.setTer({ tag: "goto", target: exit.id });
|
||||||
|
this.pushCreatedBlock(exit);
|
||||||
|
|
||||||
this.setTer({
|
this.setTer({
|
||||||
tag: "switch",
|
tag: "switch",
|
||||||
discr,
|
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 {
|
private lowerExprToOperand(expr: ast.Expr): Operand {
|
||||||
const k = expr.kind;
|
const k = expr.kind;
|
||||||
switch (k.tag) {
|
switch (k.tag) {
|
||||||
@ -510,6 +685,21 @@ export class FnLowerer {
|
|||||||
return block;
|
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!) {
|
private setTer(kind: TerKind, block = this.currentBlock!) {
|
||||||
block.terminator = { kind };
|
block.terminator = { kind };
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut a = 5;
|
for (let mut i = 0; i < 10; i = i + 1) {}
|
||||||
|
|
||||||
a = 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,6 +33,18 @@ export const ResolveError = (ident: ast.Ident): Resolve => ({
|
|||||||
kind: { tag: "error" },
|
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 = {
|
export type Redef = {
|
||||||
ident: ast.Ident;
|
ident: ast.Ident;
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
FnSyms,
|
FnSyms,
|
||||||
LocalSyms,
|
LocalSyms,
|
||||||
|
LoopBreakResolve,
|
||||||
|
LoopResolve,
|
||||||
PatResolve,
|
PatResolve,
|
||||||
PatResolveKind,
|
PatResolveKind,
|
||||||
Redef,
|
Redef,
|
||||||
@ -26,6 +28,8 @@ export class Resols {
|
|||||||
public constructor(
|
public constructor(
|
||||||
private exprResols: IdMap<AstId, Resolve>,
|
private exprResols: IdMap<AstId, Resolve>,
|
||||||
private patResols: IdMap<AstId, PatResolve>,
|
private patResols: IdMap<AstId, PatResolve>,
|
||||||
|
private loopsResols: IdMap<AstId, LoopResolve>,
|
||||||
|
private loopBreakResols: IdMap<AstId, LoopBreakResolve[]>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public exprRes(id: AstId): Resolve {
|
public exprRes(id: AstId): Resolve {
|
||||||
@ -41,6 +45,20 @@ export class Resols {
|
|||||||
}
|
}
|
||||||
return this.patResols.get(id)!;
|
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 {
|
export class Resolver implements ast.Visitor {
|
||||||
@ -49,10 +67,14 @@ export class Resolver implements ast.Visitor {
|
|||||||
private syms: Syms = this.rootSyms;
|
private syms: Syms = this.rootSyms;
|
||||||
|
|
||||||
private exprResols = new IdMap<AstId, Resolve>();
|
private exprResols = new IdMap<AstId, Resolve>();
|
||||||
private patResols = new IdMap<AstId, PatResolve>();
|
|
||||||
|
|
||||||
|
private patResols = new IdMap<AstId, PatResolve>();
|
||||||
private patResolveStack: PatResolveKind[] = [];
|
private patResolveStack: PatResolveKind[] = [];
|
||||||
|
|
||||||
|
private loopsResols = new IdMap<AstId, LoopResolve>();
|
||||||
|
private loopBreakResols = new IdMap<AstId, LoopBreakResolve[]>();
|
||||||
|
private loopResolveStack: LoopResolve[] = [{ tag: "error" }];
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private ctx: Ctx,
|
private ctx: Ctx,
|
||||||
private entryFileAst: ast.File,
|
private entryFileAst: ast.File,
|
||||||
@ -63,6 +85,8 @@ export class Resolver implements ast.Visitor {
|
|||||||
return new Resols(
|
return new Resols(
|
||||||
this.exprResols,
|
this.exprResols,
|
||||||
this.patResols,
|
this.patResols,
|
||||||
|
this.loopsResols,
|
||||||
|
this.loopBreakResols,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +125,25 @@ export class Resolver implements ast.Visitor {
|
|||||||
todo();
|
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 {
|
visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes {
|
||||||
todo();
|
todo();
|
||||||
}
|
}
|
||||||
@ -176,6 +219,42 @@ export class Resolver implements ast.Visitor {
|
|||||||
todo();
|
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 {
|
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
|
||||||
this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! });
|
this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! });
|
||||||
const res = this.syms.defVal(kind.ident, {
|
const res = this.syms.defVal(kind.ident, {
|
||||||
|
@ -22,10 +22,11 @@ export class HirStringifyer {
|
|||||||
return "<error>;";
|
return "<error>;";
|
||||||
case "item":
|
case "item":
|
||||||
return this.item(k.item);
|
return this.item(k.item);
|
||||||
case "let":
|
case "let": {
|
||||||
return `let ${this.pat(k.pat)}${
|
return `let ${this.pat(k.pat)}${
|
||||||
k.expr && ` = ${this.expr(k.expr, d)}` || ""
|
k.expr && ` = ${this.expr(k.expr, d)}` || ""
|
||||||
};`;
|
};`;
|
||||||
|
}
|
||||||
case "return":
|
case "return":
|
||||||
return `return${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
|
return `return${k.expr && ` ${this.expr(k.expr, d)}` || ""};`;
|
||||||
case "break":
|
case "break":
|
||||||
@ -33,7 +34,9 @@ export class HirStringifyer {
|
|||||||
case "continue":
|
case "continue":
|
||||||
return `continue;`;
|
return `continue;`;
|
||||||
case "assign":
|
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":
|
case "expr":
|
||||||
return `${this.expr(k.expr, d)};`;
|
return `${this.expr(k.expr, d)};`;
|
||||||
}
|
}
|
||||||
@ -119,10 +122,17 @@ export class HirStringifyer {
|
|||||||
k.falsy && ` else ${this.expr(k.falsy, d)}` || ""
|
k.falsy && ` else ${this.expr(k.falsy, d)}` || ""
|
||||||
}`;
|
}`;
|
||||||
case "loop":
|
case "loop":
|
||||||
|
return `loop ${this.expr(k.body, d)}`;
|
||||||
case "while":
|
case "while":
|
||||||
|
return `while ${this.expr(k.cond, d)} ${this.expr(k.body, d)}`;
|
||||||
case "for":
|
case "for":
|
||||||
case "c_for":
|
|
||||||
return todo(k.tag);
|
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);
|
exhausted(k);
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,13 @@ export class MirFnStringifyer {
|
|||||||
.filter((local) => !fn.paramLocals.has(local.id))
|
.filter((local) => !fn.paramLocals.has(local.id))
|
||||||
.map((local) => `#let ${this.localDef(local)}`)
|
.map((local) => `#let ${this.localDef(local)}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
return `fn ${fn.label}(${paramsStr}) {\n${localsStr}\n${
|
const blocks = fn.blocks
|
||||||
fn.blocks.values().toArray()
|
.values()
|
||||||
|
.toArray()
|
||||||
.map((block) => this.block(block))
|
.map((block) => this.block(block))
|
||||||
.join("\n")
|
.join("\n");
|
||||||
}\n}`.replaceAll("#", " ");
|
return `fn ${fn.label}(${paramsStr}) {\n${localsStr}\n${blocks}\n}`
|
||||||
|
.replaceAll("#", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
private localDef(local: Local): string {
|
private localDef(local: Local): string {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user