Cooper. Loop
(Cooper v0.1.0)
Copy Markdown
Expands a parsed for statement (CASC.md §5.5) into the ops it
generates.
This sits at a genuine architectural seam: loop expansion runs
before merge (Cooper.Merge) and general reference resolution
(Cooper.Resolver), yet it structurally needs to know each element
binding's actual list to know how many iterations to generate. The
resolution here: a binding's iterable is resolved eagerly, but only
against the current file's own local variable environment built so
far (ordinary @name = [...] declarations threaded through
Cooper.Actions' ctx as they're parsed) -- never against something
that can only exist after merge. A %{...} config reference as an
iterable is refused outright for exactly that reason (not a
currently-missing feature, a structural impossibility at this stage).
Once bindings are resolved, iteration proceeds like an ordinary
bottom-up eval: for each iteration, a local overlay (@idx/@domain/
@port/...) is substituted into the loop body's ops and the
destination path -- and only those loop-bound names; any other
reference (@{outer_var}, %{...}, !{...}, !Name(...)) is left
untouched for Cooper.Resolver's later resolution pass, matching
CASC.md's own "loop-bound variables ... scoped to the body" framing.
from <template> (CASC.md §5.5) stays a lazy Cooper.Ref.Config
base at each generated destination, exactly like Cooper.Merge
handles any other lazy config-ref base -- loop expansion never
reads the template's value itself.
Summary
Functions
bindings are as parsed by Cooper.Actions' own handle_rule/3
clause for :binding; from_template is nil or a key-path segment
list;
dest_segments is key_path's already-evaluated segment list
(a segment may be a plain string or an unresolved
Cooper.Interp.Text, per an interpolated destination like
"domain-@{idx}"); body is the loop's %Cooper.Block{};
outer_vars is the enclosing file's variable environment so far
(name => {value, public?}, matching Cooper.Actions' own ctx.vars
shape -- public? is unused here, only Cooper.Loader cares about it).
Types
Functions
@spec expand( [binding()], [String.t()] | nil, [term()], boolean(), Cooper.Block.t(), map() ) :: {:ok, list()} | {:error, Ichor.Error.t()}
bindings are as parsed by Cooper.Actions' own handle_rule/3
clause for :binding; from_template is nil or a key-path segment
list;
dest_segments is key_path's already-evaluated segment list
(a segment may be a plain string or an unresolved
Cooper.Interp.Text, per an interpolated destination like
"domain-@{idx}"); body is the loop's %Cooper.Block{};
outer_vars is the enclosing file's variable environment so far
(name => {value, public?}, matching Cooper.Actions' own ctx.vars
shape -- public? is unused here, only Cooper.Loader cares about it).