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