# `Feel.Backend`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/backend.ex#L1)

What turns a resolved expression into something that runs.

`Feel.prepare/2` parses an expression, resolves its names, and checks it. The
result is the resolved form, the IR. A backend takes the IR and produces an
executable, and `Feel.run/3` later runs that executable on a set of bindings.
Splitting the two lets an expression be prepared once and run many times.

There are two backends, selected with the `:backend` option of `Feel.prepare/2`:

  * `Feel.Backend.Interpreter` (`:interpreter`, the default) walks the IR. It
    generates no code, so it loads nothing into the VM and mints no atoms, which
    suits expressions from sources that are not trusted. It is the reference: the
    other backend must give the same results.
  * `Feel.Backend.Beam` (`:beam`) emits Erlang abstract format, compiles it, and
    loads the module. It can reduce repeated execution cost, depending on the workload and fallback rate;
    preparation and release have separate costs. Generated code calls shared
    `Feel.Runtime` and built-in operations (`Feel.Builtin`). Host compilation can be expensive,
    especially for nested control flow, so an expression that would make more than `:max_generated_statements` of
    `Feel.Limits` is an error of the kind `:limit` (D-154); the modules and atoms it leaves are not bounded,
    and public preparation/execution require `sandbox: false` for trusted expressions.

Both run on `Feel.Runtime`, so they share one implementation of the semantics.

Both backends implement every node the analyzer produces (listed in `Feel.IR`). Their contract
is the same value and ordered diagnostics (D-38); finite regression tests compare both modes.
A new kind of node needs a clause in each. Direct backend calls do not create a sandbox;
use the `Feel` facade to contain untrusted interpreter work.

# `compile_option`

```elixir
@type compile_option() ::
  {:profile, :fast | :explain}
  | {:limits, Feel.Limits.t()}
  | {:dialect, Feel.Dialect.t()}
  | {:extensions, Feel.Extensions.t() | [atom()]}
```

Options for `c:compile/2`.

  * `:profile` - `:fast` (default) or `:explain`. The explain profile is to report which rules and
    branches were taken, for audit trails. **It is not implemented yet:** the option is here so that it has a
    place, and `:explain` raises `ArgumentError` in both backends (`validate_profile!/1`).
  * `:limits` - a `t:Feel.Limits.t/0`. The BEAM backend refuses an expression that makes more code than
    `:max_generated_statements` allows (D-154); the interpreter makes none.
  * `:dialect` - the `t:Feel.Dialect.t/0` the expression was prepared in, `:feel` by default.
    The executable keeps it, and every run of it has it in its environment (D-49).
  * `:extensions` - the extensions it was prepared with (`Feel.Extensions`), none by default. Kept and
    handed to every run in the same way (D-81).

# `executable`

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

What `c:compile/2` returns and `c:run/3` takes. Opaque to everything but the backend.

# `ir`

```elixir
@type ir() :: Feel.IR.t()
```

The resolved form of an expression, as analysis produces it.

# `compile`

```elixir
@callback compile(ir(), [compile_option()]) ::
  {:ok, executable()} | {:error, Feel.Error.t()}
```

Turns the resolved form of an expression into an executable.

# `release`
*optional* 

```elixir
@callback release(executable()) :: :ok | {:error, :in_use}
```

Frees whatever an executable holds. A backend that loads code into the VM needs
this to unload it; the interpreter holds nothing and need not implement it.

Returns `{:error, :in_use}` when the executable is still running; the caller may
try again later.

# `run`

```elixir
@callback run(executable(), Feel.bindings(), [Feel.run_option()]) ::
  Feel.result(Feel.Value.t())
```

Runs an executable on a set of bindings, with the run options of `Feel.run/3`.

A semantic error in the expression is not an `{:error, _}`: in lenient mode it is
a diagnostic in the result (see `Feel.Error`).

# `fetch`

```elixir
@spec fetch(atom()) :: {:ok, module()} | :error
```

Looks up the module of a backend by the name used in the `:backend` option.

    iex> Feel.Backend.fetch(:interpreter)
    {:ok, Feel.Backend.Interpreter}
    iex> Feel.Backend.fetch(:beam)
    {:ok, Feel.Backend.Beam}
    iex> Feel.Backend.fetch(:other)
    :error

# `validate_profile!`

```elixir
@spec validate_profile!(term()) :: :fast
```

Checks the `:profile` option of `c:compile/2`, which both backends and the emitter of the BEAM backend take.

`:fast` is the only profile there is. `:explain` is reserved for a report of the rules and branches that a run took,
for audit trails, and is **not implemented yet**: it raises `ArgumentError` rather than being accepted and ignored, so
a caller who asks for it is not given a run without the report. Anything else raises `ArgumentError` too.

    iex> Feel.Backend.validate_profile!(:fast)
    :fast

    iex> Feel.Backend.validate_profile!(:explain)
    ** (ArgumentError) the :explain profile is not implemented yet: it is reserved for a report of the rules and branches that a run took, and only :fast works

    iex> Feel.Backend.validate_profile!(:slow)
    ** (ArgumentError) expected :profile to be :fast or :explain, got: :slow

---

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