This commit is contained in:
parent
54ee879b45
commit
d2a49f3be0
@ -84,6 +84,10 @@ export class Node {
|
||||
return visit(k.param, k.expr);
|
||||
case "IfStmt":
|
||||
return visit(k.cond, k.truthy, k.falsy);
|
||||
case "WhileStmt":
|
||||
return visit(k.cond, k.body);
|
||||
case "BreakStmt":
|
||||
return visit();
|
||||
case "Param":
|
||||
return visit(k.ty);
|
||||
case "IdentExpr":
|
||||
@ -132,6 +136,8 @@ export type NodeKind =
|
||||
| { tag: "ReturnStmt"; expr: Node | null }
|
||||
| { tag: "LetStmt"; param: Node; expr: Node }
|
||||
| { tag: "IfStmt"; cond: Node; truthy: Node; falsy: Node | null }
|
||||
| { tag: "WhileStmt"; cond: Node; body: Node }
|
||||
| { tag: "BreakStmt" }
|
||||
| { tag: "Param"; ident: string; ty: Node | null }
|
||||
| { tag: "IdentExpr"; ident: string }
|
||||
| { tag: "IntExpr"; value: number }
|
||||
|
||||
@ -51,6 +51,10 @@ export class Parser {
|
||||
return this.parseLetStmt();
|
||||
} else if (this.test("if")) {
|
||||
return this.parseIfStmt();
|
||||
} else if (this.test("while")) {
|
||||
return this.parseWhileStmt();
|
||||
} else if (this.test("break")) {
|
||||
return this.parseBreakStmt();
|
||||
} else {
|
||||
const place = this.parseExpr();
|
||||
if (this.eat("=")) {
|
||||
@ -120,6 +124,21 @@ export class Parser {
|
||||
return ast.Node.create(loc, "IfStmt", { cond, truthy, falsy });
|
||||
}
|
||||
|
||||
parseWhileStmt(): ast.Node {
|
||||
const loc = this.loc();
|
||||
this.step();
|
||||
const cond = this.parseExpr();
|
||||
const body = this.parseBlock();
|
||||
return ast.Node.create(loc, "WhileStmt", { cond, body });
|
||||
}
|
||||
|
||||
parseBreakStmt(): ast.Node {
|
||||
const loc = this.loc();
|
||||
this.step();
|
||||
this.mustEat(";");
|
||||
return ast.Node.create(loc, "BreakStmt", {});
|
||||
}
|
||||
|
||||
parseParam(): ast.Node {
|
||||
const loc = this.loc();
|
||||
const ident = this.mustEat("ident").value;
|
||||
@ -322,22 +341,24 @@ export class Parser {
|
||||
private mustEat(type: string, loc = this.loc()): Tok {
|
||||
const tok = this.current;
|
||||
if (tok.type !== type) {
|
||||
this.error(
|
||||
this.reporter.error(
|
||||
loc,
|
||||
`expected '${type}', got '${
|
||||
this.done ? "eof" : this.current.type
|
||||
}'`,
|
||||
loc,
|
||||
);
|
||||
if (type === ";" && this.idx > 0) {
|
||||
this.reporter.info(
|
||||
this.toks[this.idx - 1].loc,
|
||||
`try adding '${type}' here`,
|
||||
);
|
||||
}
|
||||
this.reporter.abort();
|
||||
}
|
||||
this.step();
|
||||
return tok;
|
||||
}
|
||||
|
||||
private error(message: string, loc: Loc): never {
|
||||
this.reporter.error(loc, message);
|
||||
this.reporter.abort();
|
||||
}
|
||||
|
||||
private eat(type: string): boolean {
|
||||
if (this.test(type)) {
|
||||
this.step();
|
||||
|
||||
@ -30,7 +30,8 @@ export type Sym =
|
||||
tag: "Let";
|
||||
stmt: ast.NodeWithKind<"LetStmt">;
|
||||
param: ast.NodeWithKind<"Param">;
|
||||
};
|
||||
}
|
||||
| { tag: "Loop"; stmt: ast.Node };
|
||||
|
||||
export function resolve(
|
||||
file: ast.Node,
|
||||
@ -39,6 +40,8 @@ export function resolve(
|
||||
let syms = ResolverSyms.root();
|
||||
const resols = new Map<number, Sym>();
|
||||
|
||||
const loopStack: ast.Node[] = [];
|
||||
|
||||
file.visit({
|
||||
visit(node) {
|
||||
const k = node.kind;
|
||||
@ -85,6 +88,22 @@ export function resolve(
|
||||
}
|
||||
resols.set(node.id, sym);
|
||||
}
|
||||
|
||||
if (k.tag === "WhileStmt") {
|
||||
loopStack.push(node);
|
||||
node.visitBelow(this);
|
||||
loopStack.pop();
|
||||
return "break";
|
||||
}
|
||||
|
||||
if (k.tag === "BreakStmt") {
|
||||
const loopNode = loopStack.at(-1);
|
||||
if (!loopNode) {
|
||||
reporter.error(node.loc, `break outside loop`);
|
||||
reporter.abort();
|
||||
}
|
||||
resols.set(node.id, { tag: "Loop", stmt: loopNode });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ class FnLowerer {
|
||||
private allocs: Inst[] = [];
|
||||
private bbs: BasicBlock[] = [new BasicBlock()];
|
||||
private localMap = new Map<number, Inst>();
|
||||
private loopEndMap = new Map<number, BasicBlock>();
|
||||
|
||||
constructor(
|
||||
private lowerer: MiddleLowerer,
|
||||
@ -68,6 +69,7 @@ class FnLowerer {
|
||||
? this.lowerExpr(stmt.kind.expr)
|
||||
: this.makeVoid();
|
||||
this.pushInst(Ty.Void, "Return", { source });
|
||||
this.bbs.push(new BasicBlock());
|
||||
return;
|
||||
}
|
||||
if (stmt.is("IfStmt")) {
|
||||
@ -108,6 +110,54 @@ class FnLowerer {
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (stmt.is("WhileStmt")) {
|
||||
const before = this.bbs.at(-1)!;
|
||||
|
||||
this.bbs.push(new BasicBlock());
|
||||
const body = this.bbs.at(-1)!;
|
||||
|
||||
const after = new BasicBlock();
|
||||
this.loopEndMap.set(stmt.id, after);
|
||||
this.lowerBlock(stmt.kind.body.as("Block"));
|
||||
|
||||
const bodyEnd = this.bbs.at(-1)!;
|
||||
|
||||
this.bbs.push(new BasicBlock());
|
||||
const condBlock = this.bbs.at(-1)!;
|
||||
const cond = this.lowerExpr(stmt.kind.cond);
|
||||
const condBlockEnd = this.bbs.at(-1)!;
|
||||
|
||||
this.bbs.push(after);
|
||||
|
||||
before.insts.push(
|
||||
new Inst(Ty.Void, { tag: "Jump", target: condBlock }),
|
||||
);
|
||||
condBlockEnd.insts.push(
|
||||
new Inst(Ty.Void, {
|
||||
tag: "Branch",
|
||||
cond: cond,
|
||||
truthy: body,
|
||||
falsy: after,
|
||||
}),
|
||||
);
|
||||
bodyEnd.insts.push(
|
||||
new Inst(Ty.Void, { tag: "Jump", target: condBlock }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (stmt.is("BreakStmt")) {
|
||||
const sym = this.syms.get(stmt);
|
||||
if (sym.tag !== "Loop") {
|
||||
throw new Error();
|
||||
}
|
||||
const loopEnd = this.loopEndMap.get(sym.stmt.id);
|
||||
if (!loopEnd) {
|
||||
throw new Error();
|
||||
}
|
||||
this.pushInst(Ty.Void, "Jump", { target: loopEnd });
|
||||
this.bbs.push(new BasicBlock());
|
||||
return;
|
||||
}
|
||||
if (stmt.is("AssignStmt")) {
|
||||
const source = this.lowerExpr(stmt.kind.expr);
|
||||
const target = this.lowerPlace(stmt.kind.place);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// expect: 2
|
||||
// expect: 5
|
||||
|
||||
fn main()
|
||||
{
|
||||
@ -8,5 +9,17 @@ fn main()
|
||||
a = 2;
|
||||
}
|
||||
|
||||
if false {
|
||||
a = 3;
|
||||
}
|
||||
|
||||
print_int(a);
|
||||
|
||||
if false {
|
||||
a = 4;
|
||||
} else {
|
||||
a = 5;
|
||||
}
|
||||
|
||||
print_int(a);
|
||||
}
|
||||
|
||||
22
tests/loop.ethlang
Normal file
22
tests/loop.ethlang
Normal file
@ -0,0 +1,22 @@
|
||||
// expect: 3
|
||||
// expect: 8
|
||||
|
||||
fn main()
|
||||
{
|
||||
let a = 0;
|
||||
|
||||
while a < 3 {
|
||||
a = a + 1;
|
||||
}
|
||||
|
||||
print_int(a);
|
||||
|
||||
while a < 10 {
|
||||
if a >= 8 {
|
||||
break;
|
||||
}
|
||||
a = a + 1;
|
||||
}
|
||||
|
||||
print_int(a);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user