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

Ichor reads grammar definitions -- its own language Aether, plus ABNF,
BNF, EBNF, and PEG importers -- and turns each one into a working
Lexer + Parser + Executor for whatever language the grammar describes,
via either an interpreted VM backend (`Grammar.VM`) or compile-time
native codegen (`__using__/1` below).

There are three genuinely different ways to go from grammar text to a
running parser: `__using__/1` below (compile-time codegen, rerun on
every `mix compile`), `Mix.Tasks.Ichor.Gen` (the same codegen, run
once ahead of time to a checked-in `.ex` file -- **the recommended
default for anything shipping to production**, since it's the only
one of the three where `ichor` itself never needs to be present at
runtime, `mix release` builds included), and `Grammar.VM` called
directly on an `Aether.Parser.parse/2` + `Grammar.Analysis.run/1`
result (no codegen at all, for a grammar not known until your own
program is already running). See the tutorial's "Which path is right
for you?" section for a full worked comparison of all three.

This top-level module holds `__using__/1`, the native codegen
backend's own entry point:

    defmodule Calculator do
      use Ichor, grammar: "calculator.aether", actions: Calculator.Actions
    end

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

`grammar:` is a path to a `.aether` file, resolved relative to the
`use`-ing module's own source file (so a grammar file can live
alongside the module that compiles it); `grammar_source:` takes
literal grammar text directly instead, for callers that don't want a
separate file on disk. Exactly one of the two is required. `actions:`
bakes in a compile-time-known module reference -- `parse/1`,
`tokenize/1`, and `run/1,2` all get added directly to the `use`-ing
module, via `generate/3` below (dispatching further to
`Grammar.Native`/`Grammar.Native.LR`/`Grammar.Native.GLR` by
`@engine`).

`generate/3` is also `Mix.Tasks.Ichor.Gen`'s entry point: the same
parse-analyze-codegen pipeline, just called from a Mix task instead of
from macro expansion, so a grammar can be compiled to a plain,
ordinary `.ex` file once and checked in, instead of every `use Ichor`
caller re-parsing and re-analyzing the same grammar on every compile.

A grammar-with-macros implementation (anything needing to evaluate a
raw capture node that didn't come from the original parse -- LISP's
own `defmacro`/expansion, most notably) wants `Ichor.Actions.evaluate_node/3`,
not anything here: unlike `generate/3`/`__using__/1`, which are
genuinely compile-time-only, that's a runtime entry point, and lives
in `ichor_runtime` alongside the rest of `Ichor.Actions`.

# `generate`

```elixir
@spec generate(String.t(), String.t() | nil, module()) :: Macro.t()
```

Parses and analyzes `source` (a full `.aether` grammar), then
generates the quoted module body for `actions_module` -- whichever of
`Grammar.Native`, `Grammar.Native.LR`, or `Grammar.Native.GLR` the
grammar's own `@engine` pragma selects. `file` is used only for
error messages (line/column context); pass `nil` if `source` didn't
come from a file.

Shared by `__using__/1` (which splices the result straight into the
caller's module) and `Mix.Tasks.Ichor.Gen` (which wraps it in its own
`defmodule` and writes it to disk as ordinary source).

Raises `CompileError` if `source` fails to parse or fails analysis
(unresolved rule references, a non-`peg` engine picked for a grammar
Analysis flags, an LR/GLR grammar with unresolvable conflicts, etc.).

# `generate_from_grammar`

```elixir
@spec generate_from_grammar(Aether.Grammar.t(), module()) :: Macro.t()
```

Like `generate/3`, but for a grammar that's already an `%Aether.Grammar{}`
-- not yet run through `Grammar.Analysis` -- rather than raw `.aether`
text. `Ichor.GrammarImport` is the caller: a grammar assembled from an
imported ABNF/BNF/EBNF/PEG ruleset never goes through `Aether.Parser`
at all, so `generate/3`'s own parse step doesn't apply to it, but
everything after parsing (analysis, engine dispatch) is identical.

Raises `CompileError` on the same conditions `generate/3` does, minus
the parse step.

---

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