# `Ichor.ABNF`
[🔗](https://github.com/joetjen/ichor/blob/v0.3.0/lib/ichor/abnf.ex#L1)

The ABNF (RFC 5234 + RFC 7405) front-end: `priv/grammar/abnf.aether`
parsed by Aether's own front-end, compiled by the native codegen
backend (`use Ichor`), dispatching to `Ichor.ABNF.Actions`
(`Grammar.IR` as its target category, same as `Regex.Actions`).

The `.aether` source lives under `priv/grammar/`, not `lib/`, since
`use Ichor` only ever reads it once, at compile time, to generate this
module's own functions -- nothing about it is needed once the BEAM
file exists, so it's a build-time asset (`priv/`'s own purpose),
not a runtime one.

    iex> {:ok, ruleset} = Ichor.ABNF.run("ip4-octet = 1*3DIGIT\r\n")
    iex> ruleset[:"ip4-octet"]
    %Grammar.IR.Rep{expr: %Grammar.IR.RuleRef{name: :digit}, min: 1, max: 3}

`run/1` (generated by `use Ichor`) returns `{:ok, %{rule_name_atom =>
Grammar.IR.expr()}}` for a whole `rulelist` -- every rule the source
defines, keyed by its (lowercased, per RFC 5234) name. `parse/1` and
`tokenize/1` (also generated) are available for a bare recognizer or
raw token stream, with no `Ichor.ABNF.Actions` involved.

# `parse`

```elixir
@spec parse(String.t(), term()) ::
  {:ok, non_neg_integer(), Ichor.Capture.raw_captures()}
  | {:error, Ichor.Error.t()}
```

Matches `input` against the grammar's root rule, requiring the entire (tokenized) input to be consumed. A bare recognizer -- no `Ichor.Actions` involved. `context` is read-only and only ever consulted by a `Grammar.IR.Custom` `@native(...)` node, if the grammar has one.

# `run`

```elixir
@spec run(String.t(), Ichor.Actions.context()) ::
  {:ok, term()} | {:error, Ichor.Error.t() | [Ichor.Error.t()]}
```

Like `parse/1`, but runs the match through the compile-time-known Actions module, returning the grammar's actual evaluated result.

# `run_sequence`

```elixir
@spec run_sequence(String.t(), Ichor.Actions.context()) ::
  {:ok, [term()], Ichor.Actions.context()}
  | {:error, Ichor.Error.t() | [Ichor.Error.t()]}
```

Like `run/2`, but evaluates `input` as a sequence of top-level matches against the root rule, one after another, threading context from each into the next (loading a standard-library file one top-level form at a time is the motivating case -- most grammars only ever need `run/2`).

# `tokenize`

```elixir
@spec tokenize(String.t(), term()) ::
  {:ok, [Grammar.VM.Token.t()]} | {:error, Ichor.Error.t()}
```

Tokenizes `input` completely via maximal munch, applies any `@keywords`/`@refine` rules, or reports the first position nothing matches. `context` is read-only and only ever consulted by a `Grammar.IR.CustomLexeme` `@native(...)` token, if the grammar has one.

---

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