The W3C-style EBNF front-end -- the notation the XML 1.0 spec's own
section 6 "Notation" uses, also shared by XQuery/XPath's grammars:
priv/grammar/ebnf-w3c.aether parsed by Aether's own front-end,
compiled by the native codegen backend (use Ichor), dispatching to
Ichor.EBNF.W3C.Actions (Grammar.IR as its target category).
The .aether source lives under priv/grammar/, not lib/, for the
same reason as Ichor.ABNF's own: use Ichor only ever reads it
once, at compile time.
iex> {:ok, ruleset} = Ichor.EBNF.W3C.run("nonzero_digit ::= [0-9] - '0'\n")
iex> ruleset[:nonzero_digit]
%Grammar.IR.Seq{exprs: [%Grammar.IR.NotPred{expr: %Grammar.IR.Literal{value: "0"}}, %Grammar.IR.CharClass{ranges: [{48, 57}]}]}run/1 (generated by use Ichor) returns {:ok, %{ident_atom => Grammar.IR.expr()}} for a whole grammar_file -- every rule the
source defines. parse/1 and tokenize/1 (also generated) are
available for a bare recognizer or raw token stream, with no
Ichor.EBNF.W3C.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.
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
@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.
@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.
@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).
@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.