Implement generators: GEN, EMIT, END GEN, FOR EACH and DO EACH (issue 57)
Adds generator support per the plan in issue 57:
- environment.h: isGenerator/generatorFn on a GEN call's own environment,
isEachLoop and forGeneratorEnv on a FOR EACH/DO EACH loop's own
environment.
- runtime.c: splits akbasic_runtime_prev_environment() into
akbasic_runtime_detach_environment() (return to parent without releasing)
and akbasic_runtime_release_environment() (give variables and the pool
slot back, on any environment); prev_environment() is now the two in
sequence. akbasic_runtime_call_function() refuses to call a GEN like an
ordinary function.
- verbs.c/verbs.h/scanner: new keywords GEN, EMIT, EACH, IN and the compound
verb "END GEN" (built by a new akbasic_parse_end(), the same trick
akbasic_parse_print() uses for PRINT #).
- parser_commands.c: akbasic_parse_gen() (modelled on multi-line DEF),
akbasic_parse_end(), and EACH branches in akbasic_parse_for()/
akbasic_parse_do().
- runtime_generator.c (new): akbasic_cmd_gen, akbasic_cmd_emit,
akbasic_cmd_end_gen, and the invoke/pump/release machinery FOR EACH, DO
EACH, NEXT and LOOP share. EMIT walks up to the nearest isGenerator
ancestor rather than assuming it is standing directly in the GEN's own
call frame, because a GEN body may nest its own FOR/DO/GOSUB around an
EMIT -- the issue's own ROOMOBJECTS example does exactly that.
- runtime_commands.c/runtime_structure.c: EACH branches in cmd_for/cmd_do,
matching EACH branches in cmd_next/cmd_loop, and forGeneratorEnv release
on every path that can abandon a live generator (EXIT, a NEXT that pops
for a mismatched loop variable).
Deviates from the plan in one place: FunctionDef gained an isGenerator flag
(not in the plan's field list) because refusing a GEN called like a
function has to happen before anything is pushed. Relying on EMIT's own
isGenerator check for that case doesn't work: akbasic_runtime_call_function()
drives its own step loop the same way akbasic_runtime_pump_generator() does,
and a BASIC-level error inside that loop is swallowed by process_line_run()
as reported-but-not-propagated, so the call would silently "succeed" with a
meaningless return value instead of failing.
Also: a zero-argument parameter list is not supported by the DEF/GEN
parameter parser this reuses (a pre-existing limitation, not
generator-specific); every generator in the tests takes at least one
parameter as a result.
Tests: tests/generators.c (pool exhaustion under repeated EXIT, calling a
GEN like a function, EMIT outside a GEN, self-recursion, sibling/nested
invocations) and tests/language/flowcontrol/generators_*.bas -- the
issue's own ROOMOBJECTS example in both loop shapes, an empty generator,
non-numeric EMIT, nested/interleaved invocations, and three error-path
golden cases. Docs: control-flow chapter 4 gets a GEN/EMIT/FOR EACH/DO EACH
section, the verb reference gets GEN/EMIT/END GEN entries and updated
FOR/DO/NEXT/LOOP/EXIT rows, and architecture chapter 14 documents the
detach/release split and the two-environment generator invocation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 10:02:43 -04:00
|
|
|
AFTER FOR
|