Fix generator teardown leaks, add RETURN-in-GEN and LOOP conditions on DO EACH
Review findings and follow-ups from PR #61 review: - runtime_generator.c: akbasic_runtime_release_generator() now releases the forGeneratorEnv of every scope it walks through. Abandoning a generator that was itself suspended inside a FOR EACH over another generator stranded the inner generator's pool slot; a loop doing so exhausted the twelve-slot pool and died far from the cause. - runtime.c/runtime.h: new akbasic_runtime_unwind_to_environment(), the shared teardown for the error unwinds in pump_generator() and call_function() -- both previously bare prev_environment() loops with the same suspended-generator blindness. - runtime_commands.c: bare RETURN standing in a GEN's own frame ends the generator exactly as END GEN does -- a GEN is a function at heart. RETURN with a value there is refused (values leave a GEN only through EMIT). The no-frame error message now says "GOSUB, DEF, or GEN". - runtime_structure.c: LOOP WHILE/UNTIL composes with DO EACH -- checked after each trip with the loop variable still holding that trip's value; a condition that stops the loop abandons the generator exactly as EXIT does. Previously the condition was silently ignored, while the verb reference documented it as working. - parser_commands.c: trailing tokens after the generator call on a FOR EACH/DO EACH line are refused at parse. Previously they sat unparsed and blew up only after the loop completed, when the parent scope resumed the line mid-statement -- an error at the loop's end pointing at its start. - tests/generators.c: pool-exhaustion tests for the nested-abandonment and LOOP-condition paths, RETURN semantics tests, and a direct test of the unwind primitive. Three new golden pairs cover RETURN, LOOP conditions and the misplaced-condition parse error. - docs: RETURN and LOOP-condition semantics in 04-control-flow.md and 11-verb-reference.md; corrected the self-recursion analogy (functions are re-entrant here). TODO.md 1.10 records the generator design decisions the code comments were already citing, plus the zero-arg parameter-list limitation. MAINTENANCE.md gains the abandoned-generators invariant those comments also cited. Co-Authored-By: Andrew Kesterson <andrew@aklabs.net> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -337,6 +337,19 @@ akerr_ErrorContext *akbasic_parse_do(akbasic_Parser *parser, akbasic_ASTLeaf **d
|
||||
|
||||
PASS(errctx, parse_each_clause(parser, &var, &callexpr));
|
||||
|
||||
/*
|
||||
* Same guard as akbasic_parse_for()'s EACH branch, with the likely
|
||||
* mistake named: a condition belongs on the LOOP, where it is
|
||||
* checked against each emitted value, not here on the DO.
|
||||
*/
|
||||
peeked = akbasic_parser_peek(parser);
|
||||
FAIL_NONZERO_RETURN(errctx,
|
||||
(peeked != NULL &&
|
||||
peeked->tokentype != AKBASIC_TOK_UNDEFINED &&
|
||||
peeked->tokentype != AKBASIC_TOK_COLON),
|
||||
AKBASIC_ERR_SYNTAX,
|
||||
"DO EACH takes its WHILE/UNTIL on the LOOP, and nothing else here");
|
||||
|
||||
newenv->isDoLoop = true;
|
||||
newenv->isEachLoop = true;
|
||||
newenv->loopFirstLine = firstline;
|
||||
@@ -1098,6 +1111,21 @@ akerr_ErrorContext *akbasic_parse_for(akbasic_Parser *parser, akbasic_ASTLeaf **
|
||||
|
||||
PASS(errctx, parse_each_clause(parser, &var, &callexpr));
|
||||
|
||||
/*
|
||||
* Nothing may follow the generator call but another statement.
|
||||
* Without this, a stray clause sits unparsed on the line and only
|
||||
* blows up after the whole loop has run, when the parent scope
|
||||
* resumes the line mid-statement -- an error at the loop's end
|
||||
* pointing at its beginning.
|
||||
*/
|
||||
peeked = akbasic_parser_peek(parser);
|
||||
FAIL_NONZERO_RETURN(errctx,
|
||||
(peeked != NULL &&
|
||||
peeked->tokentype != AKBASIC_TOK_UNDEFINED &&
|
||||
peeked->tokentype != AKBASIC_TOK_COLON),
|
||||
AKBASIC_ERR_SYNTAX,
|
||||
"FOR EACH takes nothing after the generator call");
|
||||
|
||||
newenv->isEachLoop = true;
|
||||
newenv->loopFirstLine = firstline;
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user