mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
fix break
This commit is contained in:
parent
0bf9aed35e
commit
1dad80f4c1
6 changed files with 70 additions and 33 deletions
10
src/ast.ts
10
src/ast.ts
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
22
src/lower.ts
22
src/lower.ts
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
15
src/utils.ts
15
src/utils.ts
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue