The pure-syntax half of Aether's front-end: turns .aether source (via
Aether.Lexer) into a concrete syntax tree that's a faithful record of
what was written -- every pragma, token, and rule definition, in file
order -- with no desugaring and no cross-definition semantics applied.
This is the same "Reader" role Grammar.Native's generated parse/1
(or any of the ABNF/BNF/EBNF/PEG importers' own parse/1) plays for a
grammar's input: a bare recognizer producing a raw tree, nothing
more. Aether.Eval is the matching "Eval" half -- it's what turns this
CST into actual Grammar.IR (not to be confused with
Ichor.Actions.evaluate/5, which evaluates parsed input through a
grammar's own actions; this evaluates grammar source into IR).
Owns: the token/rule reference split (ALL_CAPS names a token,
snake-case names a rule), the full expression grammar (choice >
sequence > term > postfix > primary), ~ markers, quantifier syntax,
@indent/@samecol, and every check that only depends on local
syntax (a token body referencing a rule, an inline character class
outside a token, a name or pragma declared twice). Everything that
depends on the whole grammar -- predefined-token override/use
tracking, case-insensitivity resolution, char-class/regex desugaring,
inline-literal promotion, @skip splicing -- is Aether.Eval's job,
not this module's.
Summary
Types
A raw expression node, mirroring exactly what was written -- no
desugaring. Composite nodes (:seq/:choice/:capture/:and_pred/
:not_pred/:indent) carry no position of their own (matching
Grammar.IR's own convention of falling back to a descendant's span);
leaf and quantifier nodes do.
Author-supplied facts for a @native(...) node, standing in for what
Grammar.Analysis would otherwise compute structurally -- nil means
"not given, Aether.Eval applies its default." :leading, when given,
is itself a list of declared-dependency rule names (never arbitrary
rules outside that list -- @native(...)'s own argument list is the
only thing this node can reference).
A {line, column} source position.
Functions
Reads source into an Aether.Reader.Grammar CST.
Types
@type cst() :: {:seq, [{cst(), suppress_skip :: boolean()}]} | {:choice, [cst()]} | {:capture, atom(), cst()} | {:and_pred, cst()} | {:not_pred, cst()} | {:indent, cst(), :indent | :samecol} | {:quant, cst(), :star | :plus | :opt | {:bound, non_neg_integer(), non_neg_integer() | :infinity}, pos()} | {:literal, String.t(), Aether.Token.case_flag(), pos()} | {:char_class, boolean(), [Aether.Token.class_item()], pos()} | {:regex, String.t(), pos()} | {:dot, pos()} | {:ref, atom(), pos()} | {:native, String.t(), String.t(), [atom()], hint(), pos()}
A raw expression node, mirroring exactly what was written -- no
desugaring. Composite nodes (:seq/:choice/:capture/:and_pred/
:not_pred/:indent) carry no position of their own (matching
Grammar.IR's own convention of falling back to a descendant's span);
leaf and quantifier nodes do.
Author-supplied facts for a @native(...) node, standing in for what
Grammar.Analysis would otherwise compute structurally -- nil means
"not given, Aether.Eval applies its default." :leading, when given,
is itself a list of declared-dependency rule names (never arbitrary
rules outside that list -- @native(...)'s own argument list is the
only thing this node can reference).
@type pos() :: {pos_integer(), pos_integer()}
A {line, column} source position.
Functions
@spec read(String.t(), String.t() | nil) :: {:ok, Aether.Reader.Grammar.t()} | {:error, Ichor.Error.t()}
Reads source into an Aether.Reader.Grammar CST.