View Source Alembic.Evaluator (alembic v0.1.0)
Tree-walking interpreter: walks an Alembic.AST.t() against a
Alembic.Context, resolves variables and expressions, applies filters,
and emits the rendered output as a string.
Output is accumulated as an iolist (nested lists of binaries), flattened
once at the end via IO.iodata_to_binary/1 — O(n) rather than the O(n²)
cost of repeated <> concatenation.
{:assign, var, expr} must be able to affect every node evaluated after
it at the same template level, so eval_nodes/2 threads Context through
an Enum.reduce_while/3 fold rather than a plain Enum.map/2.
Whitespace control (strip_left/strip_right) is already resolved by
Alembic.Parser before the AST reaches this module — see that module's
moduledoc. There is nothing left for the Evaluator to strip.
{:extends, _} / {:block, _, _} nodes are never seen here: they are
resolved away by Alembic.Inheritance.preprocess/2 one level up, in the
top-level render pipeline, before eval/2 is called.
{:include, name, variables} loads and evaluates a partial via
ctx.loader_fn (see Alembic.Context.loader/2) — the included template
runs in a child scope seeded with variables, so it can still see the
including template's own scopes/assigns (classic {% include %}
semantics, not an isolated {% render %}). It is not run through
Alembic.Inheritance.preprocess/2 — a partial is not expected to use
extends/block itself.
Summary
Functions
Renders an AST against a Alembic.Context, producing the final output
string.
Types
@type reason() :: {:undefined_variable, [String.t()]} | {:include_not_found, String.t(), term()} | :include_loader_not_configured | {:include_compile_error, term()} | Alembic.Filters.reason()
Functions
@spec eval([Alembic.AST.ast_node()], Alembic.Context.t()) :: {:ok, String.t()} | {:error, reason()}
Renders an AST against a Alembic.Context, producing the final output
string.
Examples
iex> ast = [{:text, "Hello, "}, {:output, ["name"], []}, {:text, "!"}]
iex> Alembic.Evaluator.eval(ast, Alembic.Context.new(%{"name" => "World"}))
{:ok, "Hello, World!"}
iex> ast = [{:output, ["missing"], []}]
iex> ctx = Alembic.Context.new(%{}) |> Alembic.Context.strict(true)
iex> Alembic.Evaluator.eval(ast, ctx)
{:error, {:undefined_variable, ["missing"]}}