Ichor.ABNF (Ichor v0.2.0)

Copy Markdown View Source

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.

Summary

Functions

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.

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

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).

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.

Functions

parse(input, context \\ nil)

@spec parse(String.t(), term()) ::
  {:ok, non_neg_integer(), map()} | {: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(input, initial_context \\ nil)

@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(input, initial_context)

@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(input, context \\ nil)

@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.