compile/yield
Symbol: compile/yield
Syntax: (yield VAL ...) – variadic.
Form |
Result |
|---|---|
|
yields |
|
yields |
|
yields a |
|
yields a |
Use-case: Yield from a generator (or coroutine). Suspends the current
frame and returns the value to the driver; resumption is via send(value) or
throw(). yield is variadic. With no argument it yields None; with one it
yields that value; with two or more positional arguments it yields a tuple;
and with keyword arguments it yields a multi-value values object.
Bare form: the lone symbol yield expands to (yield), so the
Python-style spelling yields None. Governed by the
compiler/bare-control-words pragma of the
module being compiled (convert by default, or warn / error); because the
bare symbol always expands, yield can never name a variable.
Behaviour: Compiles each positional argument and emits YIELD N, where N
is the argument count. YIELD 0 yields None, YIELD 1 yields the single
value, and YIELD N (for N > 1) packages the values as a tuple. A keyword
argument re-heads the form as (values ...), so (yield a: 1 b: 2) yields a
values object; positional and keyword arguments must not be mixed.
At runtime, YIELD finds the running frame’s generator node
(kos_dyn_find_gen) and freezes the chain above it into the node
(kos_dyn_gen_freeze); the open extents inside the generator are kept,
not unwound, so no finally or __exit__ runs at a yield. When the
generator is consumed on the same chain (a for-each, a NEXT, or a
delegating yield-from), the value is delivered to that consumer and
evaluation continues there without leaving koskari_eval. When the
generator is driven from outside (a Python next or send), the opcode
returns EVAL_YIELD(val), SUSPENSION_EVENT = YIELD is set, and
koskari_eval returns KOS_RES_YIELD. On resume, revive_yield pushes
send_val onto the stack.
A StopIteration or StopAsyncIteration that escapes a generator’s
body is not the generator’s own end signal. As in Python (PEP 479, PEP
525), it leaves the generator as RuntimeError("generator raised StopIteration") or RuntimeError("generator raised StopAsyncIteration"), with the original exception as __cause__, on
every surface: a Python next or list, a Python async for, and a
Koskari for-each over the same generator. A function awaited from
Python that raises StopIteration likewise raises
RuntimeError("coroutine raised StopIteration"). A generator ends
normally only by returning; an unguarded (next it) in its body that
runs out is an error, not the end of the generator.
Examples:
(yield) ;; -> None
(yield (compute-next)) ;; -> the computed value
(yield a b c) ;; -> #(a b c) (a tuple)
(yield a: a b: b c: c) ;; -> values(a=a, b=b, c=c)