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"]}}