# `Rupa.Codegen`
[🔗](https://github.com/zero-one-group/rupa/blob/v0.1.0/lib/rupa/codegen.ex#L1)

The module backend: `Rupa.IR` in, the AST of a module out.

Same IR as `Rupa.Closure`, so a feature is written once and both backends get it. What
changes is what the work costs. A closure reads its constants out of an environment and
reaches the next node through an indirect call. Generated code has the constants as
literals, calls the next node directly, and tests a scalar field's type and bounds inline —
so the common case, a field that is present and valid, allocates nothing at all and the
`{:ok, value}` wrapper never gets built.

Three things follow from generating at runtime rather than at build time:

  * a compiled `pattern` regex is embedded as a literal, because the OTP that compiled it is
    the OTP about to run it — the version skew that would make that unsafe in a release
    cannot happen here
  * recursion needs no table: a ref is a direct call to the function generated for that
    definition, so the one lookup the closure backend does goes away
  * the module is not part of your release. `Rupa.compile/2` says what that means.

The generated module is a handle, not an API. Everything reaches it through `Rupa.decode/3`,
and `ctx` here is just the mode — `:halt` or `:collect`.

# `direction`

```elixir
@type direction() :: :decode | :encode | :encode_json
```

Which of the three walks generated a function.

# `attribute`

```elixir
@spec attribute(String.t(), %{required(String.t()) =&gt; direction()}) :: String.t()
```

What a compiler diagnostic about generated code is talking about, in words.

Which name a message is about is not worth a grammar: the generated names are `d0`, `e7`,
`jref_node` and nothing an ordinary sentence would contain, so the first identifier the index
knows is the one being discussed. A message naming none of them is described in general
rather than guessed at.

    iex> {:ok, program} = Rupa.Stage.run(%{name: Rupa.T.string()})
    iex> {_ast, index} = Rupa.Codegen.module(MyApp.Codecs.Named, program, %{})
    iex> Rupa.Codegen.attribute("this clause of defp e1/2 is never used", index)
    "the encoder it generated"

    iex> Rupa.Codegen.attribute("something about nothing in particular", %{})
    "the codec it generated"

# `module`

```elixir
@spec module(module(), Rupa.IR.program(), term()) ::
  {Macro.t(), %{required(String.t()) =&gt; direction()}}
```

Builds the module AST for a staged program, and the index of which walk wrote what.

The schema is embedded so the module can say what it was built from, and hashed so a repeat
compile under the same name is a lookup rather than a recompile. The index is how
`Rupa.compile/2` turns a compiler diagnostic about `d17/2` back into a sentence: it names
every function this module generated, so a message can be told from an ordinary word.

`Rupa.compile/2` with `as:` is the way in; this only builds the AST, and compiling it is the
caller's job:

    schema = %{name: Rupa.T.string()}
    {:ok, program} = Rupa.Stage.run(schema)
    {ast, index} = Rupa.Codegen.module(MyApp.Codecs.User, program, schema)
    index["d0"]
    #=> :decode

---

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