Cooper. Resolver
(Cooper v0.1.0)
Copy Markdown
Resolves everything Cooper.Merge.assemble/1 left unresolved --
Cooper.Ref.Var (@{}, eager), Cooper.Ref.Env (${}, eager),
Cooper.Ref.Config (%{}, lazy against the final tree),
Cooper.Ref.Resolver (!{resolver:payload}, dispatch), Cooper.Ref.Tagged
(!Name(arg), dispatch), and Cooper.Merge.Layered (a for loop's
lazy from base with overrides on top, see Cooper.Loop).
@{}/${} could in principle resolve before merge, "so no
Ref.Var nodes remain by merge time" -- but this module does it the
other way -- after merge, walking the assembled tree -- which is
deliberate, not an oversight: @{}/${} never depend on the merged
tree's own shape (only on the variable/env environment, independent
of merge outcome), so resolving them before or after merge produces
identical results. Doing it here, uniformly with %{}'s
genuinely-must-be-lazy resolution, keeps one walker instead of two,
and matches Cooper.Grammar.run/2's own choice to leave
Cooper.Ref.* nodes unresolved until this stage runs.
${?NAME} (CASC.md §7.2's conditional statement skip) is explicitly
not handled here -- it removes an entire op rather than resolving a
value, so it's evaluated back in Cooper.Actions'
:conditional_statement handler instead, before this module ever
sees the statement. Backslash-continued strings (CASC.md §6.5) remain
a genuine open gap -- see test/SPEC_COVERAGE.md.
Summary
Functions
@spec resolve( term(), keyword() ) :: {:ok, term()} | {:error, Ichor.Error.t()}
opts:
:vars--%{name => value}, the file's own resolved variable environment (Cooper.Grammar.run_tree/2builds this fromCooper.Actions'ctx.vars, already public/private-filtered byCooper.Loaderacross imports).:env--%{name => value}, defaults toSystem.get_env/0.:resolvers--%{name => (payload :: String.t() -> {:ok, term()} | {:error, term()})}, for!{resolver:payload}(CASC.md §7.4/§9.2).:tags-- same shape, for!Name(arg)beyond the five built-ins (int/float/bool/duration/bytes, CASC.md §7.5/§9.1).