Add return. We need a flag to say "I've returned, unwind everything" and a slot for the value. Add to Context:
returnFlag: bool = false,
returnValue: i64 = 0,
Handle return in executeStatement:
if (stringsEqual(firstWord, "return")) {
ctx.returnValue = parseExpression(ctx);
if (ctx.source[ctx.pos] == ';') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
}
ctx.returnFlag = true;
return ctx.returnValue;
}
Update executeBlock to bail out when the flag is set:
fn executeBlock(ctx: *Context) i64 {
if (ctx.source[ctx.pos] == '{') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
}
var last: i64 = 0;
while (ctx.source[ctx.pos] != '}' and ctx.source[ctx.pos] != 0) {
last = executeStatement(ctx);
if (ctx.returnFlag) break;
}
if (ctx.source[ctx.pos] == '}') {
ctx.pos += 1;
skipSpaces(ctx.source, &ctx.pos);
}
return if (ctx.returnFlag) ctx.returnValue else last;
}
And the while handler: check ctx.returnFlag after each body execution, break out of the loop if it's set.
(The implementation is in the problem text.)
The flag is a cheap propagation mechanism. Any enclosing executor (block, while) checks it and bails. ctx travels up the call chain, so the flag is visible wherever it matters. callFunction (next problem) will reset the flag when it catches the return.