# `Linx.NFT.RuntimeCompiler`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/nft/runtime_compiler.ex#L1)

AST → Elixir AST (quoted code) translation for `~NFT` sigil
bodies that contain `#{...}` interpolations.

This is the runtime-emit sibling of `Linx.NFT.Compiler`. Where
the static compiler walks the AST and calls the validator-setter
functions on `Linx.Netfilter.Ruleset` directly to produce a
ready-made `%Ruleset{}` value at *compile time*, the runtime
compiler walks the same AST and produces an Elixir AST that,
when evaluated at *runtime*, calls the same validator-setter
functions to produce the `%Ruleset{}`. The two paths are
semantically identical for static sigils; the runtime emit path
exists so the sigil can splice the values of interpolated
Elixir expressions into the right positions.

The macro picks which path to use based on whether any
`:elixir_expr` tokens were produced by the tokenizer.

## Static portions vs runtime portions

AST nodes outside the value-position of a match (i.e. table
family, chain name, hook, priority, …) **must** be static —
the parser produces the same AST for them, and the runtime
compiler emits them as literal values via `Macro.escape/1`.

AST nodes at value positions inside matches CAN be `:elixir_expr`.
When they are, the runtime compiler:

  1. Parses the raw Elixir source of the interpolation with
     `Code.string_to_quoted!/1` to recover the original AST.
  2. Emits a call to `Linx.NFT.Runtime.cmp!/3` (or one of the
     other encoders) passing the parsed Elixir AST + the field
     kind the surrounding nft syntax expects.

Result: at runtime, the Elixir expression is evaluated in the
caller's scope, encoded per the typed field, and spliced into
the rule's expression list.

## Scope

Mirrors `Linx.NFT.Compiler`'s scope. Anything the static
compiler rejects (limit / meta-set / named objects / flowtables
/ concat keys / includes) the runtime compiler also rejects,
with the same error message. Interpolation is supported at:

  * Match RHS where the field kind is `{:int, _}` / `:ipv4` /
    `:ipv6` / `:ifname`.

Interpolations in keyword positions (table name, chain name,
family, hook, etc.) raise a clear `ParseError` — they'd require
case-by-case wiring through each validator-setter.

# `emit`

```elixir
@spec emit(
  [tuple()],
  keyword()
) :: {:ok, Macro.t()} | {:error, Linx.NFT.ParseError.t()}
```

Emits Elixir AST that, when evaluated, returns a
`%Linx.Netfilter.Ruleset{}`. Returns `{:ok, quoted}` or
`{:error, %ParseError{}}`.

---

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