Turns an ABNF/BNF/ISO-EBNF/W3C-EBNF/PEG ruleset (as Ichor.ABNF/
Ichor.BNF/Ichor.EBNF.ISO/Ichor.EBNF.W3C/Ichor.PEG produce them -- %{rule_name_atom => Grammar.IR.expr()}, explicitly documented as "not a runnable
Aether.Grammar") into one that actually is: picks a root (the
source's own first-declared rule, unless overridden), assembles a
full %Aether.Grammar{}, and (for ABNF specifically) fills in RFC
5234 Appendix B's implicit "core rules" (ALPHA, DIGIT, CRLF,
...) for anything the source references but never defines itself --
extremely common in real ABNF, which generally assumes those are
always available.
Mix.Tasks.Ichor.Gen's own entry point for a grammar whose source
isn't .aether text at all; nothing here is native-Aether-specific,
so it's equally usable standalone by anything else that wants a
runnable grammar out of one of these formats.
None of ABNF, ISO EBNF, or PEG (as specified, and as this project's
own importers implement them) have Aether's own named-capture syntax
(name:expr) or its @skip auto-splicing convenience -- both are
Aether-specific authoring conveniences, not properties of the target
formats -- so every assembled grammar is @noskip (whitespace
tolerance has to already be written explicitly into the imported
rules, matching how real ABNF/EBNF/PEG source is actually written),
and an Ichor.Actions module written against one only ever sees the
default fallback shape (Ichor.Node/passthrough), never a named
capture.
Summary
Functions
Builds a grammar from ruleset, root (an atom naming the entry
rule), and token_names (the subset of ruleset keys that are
lexical/terminal -- everything else becomes a parser rule; [] is a
perfectly good default, since every bare Literal/CharClass/Any
leaf inside a designated rule still gets auto-promoted into its own
synthetic token below, exactly as Aether.Parser already does for
hand-written Aether source -- pre-declaring a name as a token only
matters if you want it to read as one by that name in generated
code, not for correctness). @noskip always -- see this module's own
moduledoc for why.
Imports source (written in style) into a runnable %Aether.Grammar{}.
Which notation source, read from path, is written in, and the
source to hand that notation's reader.
The style names import_grammar/3/Mix.Tasks.Ichor.Gen accept, matched against --style/@style/a file extension.
Types
Functions
@spec assemble(%{required(atom()) => Grammar.IR.expr()}, atom(), [atom()]) :: Aether.Grammar.t()
Builds a grammar from ruleset, root (an atom naming the entry
rule), and token_names (the subset of ruleset keys that are
lexical/terminal -- everything else becomes a parser rule; [] is a
perfectly good default, since every bare Literal/CharClass/Any
leaf inside a designated rule still gets auto-promoted into its own
synthetic token below, exactly as Aether.Parser already does for
hand-written Aether source -- pre-declaring a name as a token only
matters if you want it to read as one by that name in generated
code, not for correctness). @noskip always -- see this module's own
moduledoc for why.
@spec import_grammar(style(), String.t(), keyword()) :: {:ok, Aether.Grammar.t()} | {:error, Ichor.Error.t() | [Ichor.Error.t()]}
Imports source (written in style) into a runnable %Aether.Grammar{}.
opts:
:root-- the entry rule's name (an atom; matched against the ruleset's own key casing -- e.g.:foo, never:"Foo"). Defaults to the first rulesourceitself declares.
Returns {:error, Ichor.Error.t()} if source fails to parse, if it
declares no rules at all and no :root override was given, or if an
explicit :root names a rule the ruleset doesn't actually have.
@spec styled(String.t(), Path.t()) :: {:ok, style() | :aether, String.t()} | {:error, Ichor.Error.t()}
Which notation source, read from path, is written in, and the
source to hand that notation's reader.
An @style line wins over the extension (@style abnf, bnf,
ebnf, peg or aether), and is taken out of the source, since it
is valid syntax in none of them. Without one, the extension says:
.abnf, .bnf, .ebnf or .iso.ebnf (ISO), .w3c.ebnf (W3C),
.peg, and Aether for .aether or anything else.
One definition for every reader of a grammar file -- mix ichor.gen
and the Ichor Conformance Suite's adapter alike -- so the two can
never disagree about what a file is.
iex> Ichor.GrammarImport.styled("a <- 'a'", "g.peg")
{:ok, :peg, "a <- 'a'"}
@spec styles() :: [style()]
The style names import_grammar/3/Mix.Tasks.Ichor.Gen accept, matched against --style/@style/a file extension.