# `Grammar.VM.Compiler`
[🔗](https://github.com/joetjen/ichor/blob/v0.3.0/lib/grammar/vm/compiler.ex#L1)

The combinator half of Grammar.VM's bytecode compilation, shared
between the character-level compiler (token bodies, run by the lexer)
and the token-stream-level compiler (rule bodies, run by the parser).
`Seq`/`Choice`/`Star`/`Plus`/`Opt`/`Rep`/`AndPred`/`NotPred` compile
identically either way -- only the leaf nodes differ
(`Literal`/`CharClass`/`Any`/`RuleRef` for tokens; `RuleRef`/`Indent`/
`Capture` for rules -- tokens can never contain a `Capture`), so each
caller supplies its own `leaf_fun` for those.

Every `compile/3` clause returns `{ops, next_counter}`, where `ops` is a
flat list of instructions possibly containing `{:label, n}` markers
(resolved later by `Grammar.VM.Linker`) and `counter` is the next
unused label number -- threaded through so nested compilation never
reuses a label.

The instruction set is the standard PEG/LPeg one (Ierusalimschy's
"A Text Pattern-Matching Tool based on Parsing Expression Grammars"):
`choice`/`commit` for ordered choice and `*`, `back_commit`/`fail_twice`
for the two lookahead predicates. `Grammar.Analysis` already guarantees
no `Star`/`Plus` here wraps an unconditionally-empty match at the point
a grammar author wrote it directly, but `test_progress` below is still
a hard runtime guarantee rather than relying on that alone -- see the
comment on the `Star` clause.

# `leaf_fun`

```elixir
@type leaf_fun() :: (Grammar.IR.expr(), non_neg_integer() -&gt;
                 {ops(), non_neg_integer()})
```

# `ops`

```elixir
@type ops() :: [term()]
```

# `compile`

```elixir
@spec compile(Grammar.IR.expr(), non_neg_integer(), leaf_fun()) ::
  {ops(), non_neg_integer()}
```

# `fresh`

```elixir
@spec fresh(non_neg_integer()) :: {non_neg_integer(), non_neg_integer()}
```

---

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