Implement generators (GEN/EMIT/END GEN/FOR EACH/DO EACH) - closes #57 #61
Reference in New Issue
Block a user
Delete Branch "57-implement-generators"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements generators for issue #57: GEN / END GEN / EMIT / FOR EACH / DO EACH.
Closes #57
Summary of changes:
Build is clean; ctest is 123/123 passing, including the docs example checker.
Deviations from the plan (both noted inline in the code):
Review of this PR complete (conducted by Ishikawa, posting via Andrew's account). The design is solid — the detach/release split, the parent-chain self-recursion check, and EMIT's resume-point handling all verified correct under tracing. One confirmed bug and several semantics gaps were found; fixes are pushed to this branch as
2cb6866. All 126 tests pass, including 3 new golden cases and 5 new C tests.What was found and fixed:
Pool leak on nested-generator abandonment (confirmed bug).
akbasic_runtime_release_generator()walked the parent chain but never released a walked scope's own suspendedforGeneratorEnv— a child, off the parent chain. Abandoning a generator that was itself suspended inside aFOR EACHover another generator stranded one pool slot per abandonment; a 40-iteration repro died with "Environment pool exhausted at line 60 (12 in use)". Fixed with a guarded recursion; repro and pool-exhaustion tests added.Same blindness in two error-unwind paths.
pump_generator()'s andcall_function()'s CLEANUP blocks unwound with bareprev_environment()loops. Both now use a new shared primitive,akbasic_runtime_unwind_to_environment(), which releases each popped scope's suspended generator on the way down. MAINTENANCE.md now documents the invariant ("Abandoned generators") — the note this PR's comments cited but which didn't exist yet; same for the TODO.md namespace-decision citations (now TODO.md §1.10).RETURNinside a GEN was a runtime error. Per review discussion with Andrew: a GEN is a function at heart, so bareRETURNstanding in the GEN's own frame now ends it exactly asEND GENdoes.RETURN exprthere is refused — values leave a GEN only throughEMIT. Inherits the interpreter-wide restriction that RETURN doesn't unwind nested FOR/DO scopes (same as GOSUB/DEF; lifting that everywhere is filed separately).LOOP WHILE c | UNTIL con aDO EACHwas silently ignored — whiledocs/11-verb-reference.mddocumented it as working. It now composes: the condition is checked after each trip with the loop variable still holding that trip's value; stopping abandons the generator exactly as EXIT does (released, leak-tested).Trailing tokens on the
DO EACH/FOR EACHheader were a time bomb.DO EACH V# IN G(0) WHILE V# < 2ran the whole loop, then died with "Unknown command WHILE" pointing at the DO line — the leftover tokens surfaced only when the parent scope resumed the line mid-statement. Now a parse error, with the DO variant's message naming the likely fix ("takes its WHILE/UNTIL on the LOOP").Docs: RETURN and LOOP-condition semantics added to 04-control-flow.md (examples run by the docs checker); the self-recursion analogy corrected (this interpreter's functions are re-entrant — the old text claimed otherwise); zero-arg parameter-list limitation recorded in TODO.md §1.10.
Deferred to their own issues (opening next): unwinding RETURN through nested scopes (C64-reference behavior; touches GOSUB/DEF/handlers), and zero-argument parameter lists in
parse_def_parameters().Pull request closed