# `Ichor.Actions`
[🔗](https://github.com/joetjen/ichor_runtime/blob/v0.2.0/lib/ichor/actions.ex#L1)

How a parsed Aether AST turns into an actual result -- a sandboxed
program's final value, a config's map/struct, a query's result set, or
transpiled output. One mechanism, used differently per grammar: a
grammar's Actions module only implements the rules/tokens it needs
custom behavior for (`@optional_callbacks`); everything else falls back
to the defaults below.

`handle_rule`/`handle_token` are never invoked directly by user code --
`Grammar.VM` builds the raw capture tree during matching (an
`Ichor.Capture.raw_captures/0` list, in true source order at every
level -- never a plain map, which Elixir/Erlang make no cross-version
iteration-order guarantee about), and this module's `evaluate/5` walks
it, calling into the actions module (or the default fallback) as it
goes, threading `context` throughout.

`evaluate_node/3` is the companion entry point for a raw capture node
that *didn't* come from the original parse -- a grammar with
macro-like features (LISP's own `defmacro`/expansion is the reference
case) needs to evaluate the result of its own expansion through this
same `handle_rule`/`handle_token` dispatch, not skip it. Not
Lisp-specific in principle: any grammar with macro-like features needs
this same re-entry point. Genuinely a runtime concern (called from
generated/hand-written Actions code while actually running a parse),
unlike `Ichor.generate/3`/`Ichor.__using__/1`, which only ever run at
compile time -- that split is exactly why this lives here, in
`ichor_runtime`, rather than alongside those two in `ichor` proper.

# `capture_shapes`

```elixir
@type capture_shapes() :: %{optional(atom()) =&gt; MapSet.t(atom())}
```

# `captures`

```elixir
@type captures() :: %{optional(atom()) =&gt; Ichor.Capture.t() | [Ichor.Capture.t()]}
```

# `context`

```elixir
@type context() :: term()
```

# `finalize`
*optional* 

```elixir
@callback finalize(context :: context()) :: :ok | {:error, [Ichor.Error.t()]}
```

# `handle_rule`
*optional* 

```elixir
@callback handle_rule(rule :: atom(), captures :: captures(), context :: context()) ::
  {:ok, term(), context()} | {:error, Ichor.Error.t()}
```

# `handle_token`
*optional* 

```elixir
@callback handle_token(token :: atom(), text :: String.t(), context :: context()) ::
  {:ok, term()} | {:error, Ichor.Error.t()}
```

# `eval_all`

```elixir
@spec eval_all(captures(), context()) ::
  {:ok, %{optional(atom()) =&gt; term()}, context()} | {:error, Ichor.Error.t()}
```

Eagerly invokes every capture's `eval`, in true source order (each
`%Capture{}`'s own `position`, not `captures`' incidental map
iteration order -- see `Ichor.Capture`'s moduledoc for why those two
can differ), threading `context` through -- the plain bottom-up fold
that's correct for grammars with no special-form semantics (most
config/markup/query grammars, as opposed to something like LISP's
`if`/`quote`, which need to *not* evaluate an untaken branch at all).
A list-valued capture (the same name captured more than once, e.g.
inside a `*`) resolves to a list of values in the same order. Halts
and returns immediately on the first error.

A `%Capture{}` built by hand rather than via `dispatch_rule/5`
(directly constructing this map for a test, say) defaults every
`position` to `0` -- ties between those fall back to whatever order
`captures` itself happens to enumerate in, same as this function's
behavior before `position` existed. Give hand-built captures distinct
`position`s if you need this function's ordering guarantee to hold
for them too.

# `evaluate`

```elixir
@spec evaluate(
  atom(),
  Ichor.Capture.raw_captures(),
  module(),
  context(),
  capture_shapes()
) ::
  {:ok, term(), context()} | {:error, Ichor.Error.t() | [Ichor.Error.t()]}
```

The single entry point tying `Grammar.VM`'s raw capture tree to a
grammar's Actions module: evaluates the root rule, then runs
`finalize/1` over whatever context that produced. Returns the final
context alongside the value -- needed whenever more than one top-level
match must thread context forward (e.g. loading a multi-form source
file one top-level form at a time); callers that only need the value
can just ignore it.

`capture_shapes` (from `Grammar.VM.RuleCompiler.capture_shapes/1`) says,
per rule, which capture names sit under a `*`/`+`/`{n,m}` -- needed so
e.g. an `op:` that matched zero times still shows up as `[]`, not as a
missing key indistinguishable from "this rule shape never has an
`:op`" (see that function's docs for why this can't just be inferred
from the raw tree at dispatch time).

# `evaluate_node`

```elixir
@spec evaluate_node(Ichor.Capture.node_t(), module(), context()) ::
  {:ok, term(), context()} | {:error, Ichor.Error.t()}
```

Evaluates a raw capture node (as found on any `Ichor.Capture.node`, or
built directly by something like a macro's `unreify`) against
`actions_module`, starting from `context`. See this module's own
moduledoc for when you need this instead of an ordinary `.eval` thunk.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
