This implementation's answers to the Ichor Conformance Suite, through either backend.
The suite runs every Ichor implementation against the same
grammars and inputs, reaching each one through an adapter
(mix ichor.conformance here) with a JSON request, and reading a JSON
answer back:
%{"cases" => [%{"id" => "trees: one number", "operation" => "parse",
"grammar" => "/abs/calculator.aether", "text" => "2"}]}
#=> %{"results" => [%{"id" => "trees: one number", "tree" => ...}]}A grammar may be written in any notation Ichor reads -- Aether,
ABNF, BNF, ISO EBNF, PEG -- told by its extension or an @style line,
exactly as mix ichor.gen tells it (Ichor.GrammarImport.styled/2).
What the suite compares is the parse, so the same language written
twice must come to the same tree.
It lives here, not in ichor_runtime, because the runtime is only
ever called by generated code: to answer a case, a parser has to be
made from the case's grammar first, and making one is Ichor's.
The backend is the implementation. :interpreted runs the
grammar through Grammar.VM, Grammar.LR or Grammar.GLR, by its
engine; :compiled generates a module with Ichor.generate_from_grammar/2
and runs that. The suite configures each as an implementation of its
own, which is what checks that the two agree.
A "render" answers "rendered", the first error exactly as
Ichor.Error.format/1 writes it for a person -- location, message,
the source line and a caret -- or "tree" when the text parses.
Word for word: what a person reading an error is given is output, and
every implementation gives the same.
"pratt", "unify", "solve" and "walk" hold the toolkits to
their cases, through the same standard callbacks every adapter
supplies (Ichor.Conformance.Toolkits).
A "tokens" answers "tokens", the grammar's token stream for the
text -- every token in order, trivia included, @keywords applied, as
{name, text, line, column}, an inline literal's token named nil --
or "error", the lexer error's stage, line and column.
A "lossless" answers "tree", the lossless tree of the text
(Ichor.Syntax) -- a node as {:rule, name, offset, size, line, column, children}, a token as {:token, name, text, trivia, offset, line, column}, an inline literal's token named nil -- or "error", as
a "parse" does. Every field is one an implementation computes, so
the offsets are held to the cases as the structure is.
A "parse" answers one of:
"tree"-- the default tree, what the parse comes to with no actions of its own: a token is its text, a rule with exactly one capture matched once is that capture's value, and any other rule is{:node, rule, captures}, its captures a map from name to value;"error"--%{stage: ..., line: ..., column: ...}of the first error, from reading the grammar, analysing it, or parsing the text.
Both travel as the suite's typed JSON ({"keyword": "node"},
{"integer": "1"}, {"tuple": [...]}, {"map": [[k, v], ...]}), so
nothing tells two values apart differently here than in Praxis.
Summary
Functions
The answer to request through backend, one result per case, in order.