# `mix ichor.gen`

Compiles a grammar file to a plain `.ex` file, once, ahead of time. This
is the recommended way to use Ichor for anything shipping to
production — see the [tutorial](../TUTORIAL.md#9-shipping-a-compiled-parser-mix-ichorgen-and-ichor_runtime)
for the full reasoning, or the [cheatsheet](../CHEATSHEET.md#compile-a-grammar-ahead-of-time-mix-ichorgen)
for a quick-reference version of everything on this page.

```sh
mix ichor.gen PATH_TO_GRAMMAR --module MODULE --actions ACTIONS_MODULE --out PATH [--root NAME]
```

It runs the exact same parse → analyze → codegen pipeline as
`use Ichor, grammar: ..., actions: ...`, but as a one-time command
instead of a macro re-run on every `mix compile`, writing the result to
disk as an ordinary module you check in like any other source file.

## Options

| Option | Required | Meaning |
| --- | --- | --- |
| `--module` | Yes | The generated module's name, e.g. `MyApp.Calculator`. |
| `--actions` | Yes | The `Ichor.Actions` module the generated `run/1,2` dispatches to, e.g. `MyApp.Calculator.Actions`. Baked in as a literal reference — it doesn't need to exist yet, only when the generated file's own callers run. |
| `--out` | Yes | Path to write the generated module to. |
| `--root` | No | The entry rule's name. Only meaningful for non-Aether grammar styles (see below) — Aether always uses its own `@root` pragma. |

## Example: a basic grammar

```sh
mix ichor.gen calculator.aether \
    --module Calculator \
    --actions Calculator.Actions \
    --out lib/calculator.ex
```

Open `lib/calculator.ex` afterward and it reads like ordinary,
hand-written Elixir — `tokenize/1`, `parse/1`, `run/1,2`, plus whatever
private helper functions the codegen backend needed, all emitted as
`def` (not `defp`, unlike `use Ichor`'s macro-expanded output — see
"Why every helper is public" below):

```elixir
# Generated by:
#
#     mix ichor.gen calculator.aether --module Calculator --actions Calculator.Actions --out lib/calculator.ex
#
# Do not edit by hand -- rerun the command above instead.

defmodule Calculator do
  @moduledoc false
  # tokenize/1, parse/1, run/1, run/2, ...
end

Calculator.run("2 + 3 * 4")
#=> {:ok, 14}
```

Rerun the same command whenever `calculator.aether` changes — there's
no automatic staleness check between the checked-in file and its source
grammar, so it's worth wiring into a `mix.exs` alias of your own (a
`gen.grammar` alias calling `"ichor.gen ..."`, say) rather than typed by
hand each time.

## Example: keeping `ichor` out of production

The whole point of generating ahead of time is that the output only
ever calls a small, fixed set of support modules by name — no macro,
no compile-time dependency on `ichor` itself. That fixed set is exactly
what the independently-published
[`ichor_runtime`](https://hex.pm/packages/ichor_runtime) package is, so
a project that only ever consumes pregenerated parsers can mark `ichor`
dev-only:

```elixir
def deps do
  [
    {:ichor_runtime, "~> 0.2"},
    {:ichor, "~> 0.2", only: :dev, runtime: false}
  ]
end
```

Contrast with `use Ichor`: because that macro needs the real `Ichor`
module loaded to expand, `ichor` has to be a genuine dependency in
whatever environment compiles it — Mix won't load an `only: :dev`
dependency under `MIX_ENV=prod`, so a `mix release` build (which
compiles under `:prod` by default) fails outright if any module still
says `use Ichor` while `ichor` is dev-only. `mix ichor.gen`-generated
code has no such constraint.

## Example: importing a non-Aether grammar

`PATH_TO_GRAMMAR` doesn't have to be `.aether` source — ABNF, BNF, ISO
EBNF, and PEG grammars work too, via `Ichor.GrammarImport`. The style is
picked automatically from the file extension:

| Extension | Style |
| --- | --- |
| `.aether` (or anything unrecognized) | Aether (default) |
| `.abnf` | ABNF (RFC 5234 + RFC 7405) |
| `.bnf` | Classical BNF |
| `.ebnf` | ISO EBNF (ISO/IEC 14977) |
| `.peg` | PEG (Ford/pest/PEG.js convention) |

```sh
mix ichor.gen http_request_line.abnf \
    --module HttpRequestLine \
    --actions HttpRequestLine.Actions \
    --out lib/http_request_line.ex \
    --root request_line
```

`--root` overrides the source's own first-declared rule as the entry
point — here `request_line`, since ABNF has no `@root` pragma of its
own to say so. Two other differences from Aether source apply to every
non-Aether style: the grammar is always `@noskip` (whitespace tolerance
has to already be written explicitly into the source, matching how real
ABNF/BNF/EBNF/PEG grammars are written), and an `Ichor.Actions` module
written against the generated parser only ever sees the default
fallback capture shape (`Ichor.Node`/single-capture passthrough) —
never a named capture, since none of these formats have syntax for one.

For ABNF specifically, RFC 5234 Appendix B's own "core rules" (`ALPHA`,
`DIGIT`, `CRLF`, ...) are filled in automatically for whichever of them
the source references but never defines itself — extremely common in
real ABNF, which generally assumes those are always available. See
`Ichor.GrammarImport`'s own moduledoc for the one sharp edge this can't
fully paper over (two genuinely-overlapping core rules both referenced
by the same source).

## Example: overriding the detected style with `@style`

An `@style` line anywhere in the source wins over the extension-based
guess — useful for a `.txt` grammar, or one whose extension doesn't
match its actual format:

```text
@style abnf

request-line = method SP request-target SP http-version CRLF
```

```sh
mix ichor.gen request_line.txt \
    --module RequestLine \
    --actions RequestLine.Actions \
    --out lib/request_line.ex
```

`@style` accepts `abnf`, `bnf`, `ebnf`, `peg`, or `aether`, and is
stripped before the source reaches whichever parser actually runs, so
it doesn't need to be valid syntax in the target format at all.

## Why every helper is public

Every private helper function the codegen backend generates
(tokenizer/rule sub-functions, LR/GLR dispatch clauses, ...) is emitted
as `def`, not `defp`. Through `use Ichor`, these come from macro
expansion, which Elixir's compiler doesn't flag as unused even when a
particular grammar's shape leaves some unreferenced (a predefined token
like `HEX` compiles a lexer function whether or not any rule in *this*
grammar uses it). Written out as ordinary source instead, the same
`defp`s would trip `--warnings-as-errors` on an unrelated grammar-shape
detail — `def` sidesteps that.

## See also

- [Tutorial §9](../TUTORIAL.md#9-shipping-a-compiled-parser-mix-ichorgen-and-ichor_runtime) — the full worked example, alongside `use Ichor` and the raw `Grammar.VM` pipeline for comparison.
- [Cheatsheet](../CHEATSHEET.md#compile-a-grammar-ahead-of-time-mix-ichorgen) — quick reference once you already know which path you want.
- [`mix ichor.tokens`](ICHOR_TOKENS.md) — inspect a grammar's tokens without generating anything.
