fix break

This commit is contained in:
nora 2023-07-30 21:04:29 +02:00
parent 0bf9aed35e
commit 1dad80f4c1
6 changed files with 70 additions and 33 deletions

View file

@ -127,19 +127,17 @@ export type ExprIf = {
else?: Expr; else?: Expr;
}; };
export type LoopId = number;
export type ExprLoop = { export type ExprLoop = {
kind: "loop"; kind: "loop";
body: Expr; body: Expr;
loopId: LoopId;
}; };
export type ExprBreak = { export type ExprBreak = {
kind: "break"; kind: "break";
/** target?: LoopId;
* The break target block.
* May be any control flow block, labelled from inside out.
* TODO: This is not a good solution at all and pretty broken.
*/
target?: number;
}; };
export type ExprStructLiteral = { export type ExprStructLiteral = {

View file

@ -12,6 +12,7 @@ import { exec } from "child_process";
const input = ` const input = `
function main() = ( function main() = (
let i = 0; let i = 0;
loop break;
loop ( loop (
if i > 10 then break; if i > 10 then break;

View file

@ -5,12 +5,13 @@ import {
FunctionDef, FunctionDef,
ImportDef, ImportDef,
Item, Item,
LoopId,
Resolution, Resolution,
Ty, Ty,
TyFn, TyFn,
varUnreachable, varUnreachable,
} from "./ast"; } from "./ast";
import { encodeUtf8 } from "./utils"; import { encodeUtf8, unwrap } from "./utils";
import * as wasm from "./wasm/defs"; import * as wasm from "./wasm/defs";
const USIZE: wasm.ValType = "i32"; const USIZE: wasm.ValType = "i32";
@ -204,6 +205,8 @@ type FuncContext = {
func: FunctionDef; func: FunctionDef;
wasm: wasm.Func; wasm: wasm.Func;
varLocations: VarLocation[]; varLocations: VarLocation[];
loopDepths: Map<LoopId, number>;
currentBlockDepth: number;
}; };
type FnAbi = { params: ArgRetAbi[]; ret: ArgRetAbi }; type FnAbi = { params: ArgRetAbi[]; ret: ArgRetAbi };
@ -230,6 +233,8 @@ function lowerFunc(cx: Context, item: Item, func: FunctionDef) {
func, func,
wasm: wasmFunc, wasm: wasmFunc,
varLocations: paramLocations, varLocations: paramLocations,
loopDepths: new Map(),
currentBlockDepth: 0,
}; };
lowerExpr(fcx, wasmFunc.body, fcx.func.body); lowerExpr(fcx, wasmFunc.body, fcx.func.body);
@ -545,6 +550,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
case "if": { case "if": {
lowerExpr(fcx, instrs, expr.cond!); lowerExpr(fcx, instrs, expr.cond!);
fcx.currentBlockDepth++;
const thenInstrs: wasm.Instr[] = []; const thenInstrs: wasm.Instr[] = [];
lowerExpr(fcx, thenInstrs, expr.then); lowerExpr(fcx, thenInstrs, expr.then);
@ -553,6 +559,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
if (expr.else) { if (expr.else) {
lowerExpr(fcx, elseInstrs, expr.else); lowerExpr(fcx, elseInstrs, expr.else);
} }
fcx.currentBlockDepth--;
instrs.push({ instrs.push({
kind: "if", kind: "if",
@ -566,12 +573,15 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
case "loop": { case "loop": {
const outerBlockInstrs: wasm.Instr[] = []; const outerBlockInstrs: wasm.Instr[] = [];
fcx.loopDepths.set(expr.loopId, fcx.currentBlockDepth);
fcx.currentBlockDepth += 2;
const bodyInstrs: wasm.Instr[] = []; const bodyInstrs: wasm.Instr[] = [];
lowerExpr(fcx, bodyInstrs, expr.body); lowerExpr(fcx, bodyInstrs, expr.body);
bodyInstrs.push({ bodyInstrs.push({
kind: "br", kind: "br",
label: /*innermost control structure, the loop*/ 0, label: /*innermost control structure, the loop*/ 0,
}); });
fcx.currentBlockDepth -= 2;
outerBlockInstrs.push({ outerBlockInstrs.push({
kind: "loop", kind: "loop",
@ -584,13 +594,18 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
instrs: outerBlockInstrs, instrs: outerBlockInstrs,
type: blockTypeForBody(fcx.cx, expr.ty!), type: blockTypeForBody(fcx.cx, expr.ty!),
}); });
fcx.loopDepths.delete(expr.loopId);
break; break;
} }
case "break": { case "break": {
const loopDepth = unwrap(fcx.loopDepths.get(expr.target!));
console.log(loopDepth, fcx.currentBlockDepth);
instrs.push({ instrs.push({
kind: "br", kind: "br",
label: expr.target! + /* the block outside the loop */ 1, label: fcx.currentBlockDepth - loopDepth - 1,
}); });
break; break;
} }
@ -612,6 +627,7 @@ function lowerExprBlockBody(
fcx: FuncContext, fcx: FuncContext,
expr: ExprBlock & Expr expr: ExprBlock & Expr
): wasm.Instr[] { ): wasm.Instr[] {
fcx.currentBlockDepth++;
const innerInstrs: wasm.Instr[] = []; const innerInstrs: wasm.Instr[] = [];
const headExprs = expr.exprs.slice(0, -1); const headExprs = expr.exprs.slice(0, -1);
@ -625,6 +641,8 @@ function lowerExprBlockBody(
lowerExpr(fcx, innerInstrs, tailExpr); lowerExpr(fcx, innerInstrs, tailExpr);
fcx.currentBlockDepth--;
return innerInstrs; return innerInstrs;
} }

View file

@ -6,6 +6,7 @@ import {
COMPARISON_KINDS, COMPARISON_KINDS,
DEFAULT_FOLDER, DEFAULT_FOLDER,
Expr, Expr,
ExprLoop,
ExprStructLiteral, ExprStructLiteral,
FieldDef, FieldDef,
Folder, Folder,
@ -24,13 +25,8 @@ import {
superFoldExpr, superFoldExpr,
} from "./ast"; } from "./ast";
import { CompilerError, Span, spanMerge } from "./error"; import { CompilerError, Span, spanMerge } from "./error";
import { import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
BaseToken, import { Ids } from "./utils";
Token,
TokenIdent,
TokenLit,
TokenLitString,
} from "./lexer";
type Parser<T> = (t: Token[]) => [Token[], T]; type Parser<T> = (t: Token[]) => [Token[], T];
@ -43,9 +39,7 @@ export function parse(t: Token[]): Ast {
items.push(item); items.push(item);
} }
const withIds = items.map((item, i) => ({ ...item, id: i })); const ast = assignIds({ items: items });
const ast = { items: withIds };
validateAst(ast); validateAst(ast);
@ -406,7 +400,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
if (tok.kind === "loop") { if (tok.kind === "loop") {
let body; let body;
[t, body] = parseExpr(t); [t, body] = parseExpr(t);
return [t, { kind: "loop", body, span: tok.span }]; return [t, { kind: "loop", body, span: tok.span, loopId: 0 }];
} }
if (tok.kind === "break") { if (tok.kind === "break") {
@ -614,3 +608,24 @@ function validateAst(ast: Ast) {
foldAst(ast, validator); foldAst(ast, validator);
} }
function assignIds(ast: Ast): Ast {
let loopId = new Ids();
const astItems = { items: ast.items.map((item, i) => ({ ...item, id: i })) };
const assigner: Folder = {
...DEFAULT_FOLDER,
expr(expr: Expr): Expr {
if (expr.kind === "loop") {
return {
...(superFoldExpr(expr, this) as ExprLoop & Expr),
loopId: loopId.next(),
};
}
return superFoldExpr(expr, this);
},
};
return foldAst(astItems, assigner);
}

View file

@ -13,6 +13,7 @@ import {
Identifier, Identifier,
ItemId, ItemId,
LOGICAL_KINDS, LOGICAL_KINDS,
LoopId,
Resolution, Resolution,
Ty, Ty,
TY_BOOL, TY_BOOL,
@ -483,8 +484,7 @@ export function checkBody(
typeOfItem: (index: number) => Ty typeOfItem: (index: number) => Ty
): Expr { ): Expr {
const localTys = [...fnTy.params]; const localTys = [...fnTy.params];
const loopState: { hasBreak: boolean; nestingDepth: number }[] = []; const loopState: { hasBreak: boolean; loopId: LoopId }[] = [];
let currentNestingDepth = 0;
const infcx = new InferContext(); const infcx = new InferContext();
@ -586,7 +586,6 @@ export function checkBody(
}; };
} }
case "block": { case "block": {
currentNestingDepth++;
const prevLocalTysLen = localTys.length; const prevLocalTysLen = localTys.length;
const exprs = expr.exprs.map((expr) => this.expr(expr)); const exprs = expr.exprs.map((expr) => this.expr(expr));
@ -595,8 +594,6 @@ export function checkBody(
localTys.length = prevLocalTysLen; localTys.length = prevLocalTysLen;
currentNestingDepth--;
return { return {
...expr, ...expr,
exprs, exprs,
@ -680,10 +677,8 @@ export function checkBody(
} }
case "if": { case "if": {
const cond = this.expr(expr.cond); const cond = this.expr(expr.cond);
currentNestingDepth++;
const then = this.expr(expr.then); const then = this.expr(expr.then);
const elsePart = expr.else && this.expr(expr.else); const elsePart = expr.else && this.expr(expr.else);
currentNestingDepth--;
infcx.assign(TY_BOOL, cond.ty!, cond.span); infcx.assign(TY_BOOL, cond.ty!, cond.span);
@ -699,10 +694,9 @@ export function checkBody(
return { ...expr, cond, then, else: elsePart, ty }; return { ...expr, cond, then, else: elsePart, ty };
} }
case "loop": { case "loop": {
currentNestingDepth++;
loopState.push({ loopState.push({
hasBreak: false, hasBreak: false,
nestingDepth: currentNestingDepth, loopId: expr.loopId,
}); });
const body = this.expr(expr.body); const body = this.expr(expr.body);
@ -711,8 +705,6 @@ export function checkBody(
const hadBreak = loopState.pop(); const hadBreak = loopState.pop();
const ty = hadBreak ? TY_UNIT : TY_NEVER; const ty = hadBreak ? TY_UNIT : TY_NEVER;
currentNestingDepth--;
return { return {
...expr, ...expr,
body, body,
@ -723,11 +715,9 @@ export function checkBody(
if (loopState.length === 0) { if (loopState.length === 0) {
throw new CompilerError("break outside loop", expr.span); throw new CompilerError("break outside loop", expr.span);
} }
const loopDepth = loopState[loopState.length - 1].nestingDepth; const target = loopState[loopState.length - 1].loopId;
loopState[loopState.length - 1].hasBreak = true; loopState[loopState.length - 1].hasBreak = true;
const target = currentNestingDepth - loopDepth;
return { return {
...expr, ...expr,
ty: TY_NEVER, ty: TY_NEVER,

View file

@ -1,3 +1,18 @@
export function encodeUtf8(s: string): Uint8Array { export function encodeUtf8(s: string): Uint8Array {
return new TextEncoder().encode(s); return new TextEncoder().encode(s);
} }
export class Ids {
nextId: number = 0;
public next(): number {
return this.nextId++;
}
}
export function unwrap<T>(value: T | undefined): T {
if (value === undefined) {
throw new Error("tried to unwrap undefined value");
}
return value;
}