Thanks for considering a contribution. This document covers what you need to know before opening an issue or a pull request.
Getting started
ichor and ichor_runtime
are two separate repositories, independently published and maintained
-- ichor_runtime is the small runtime support library a generated
parser actually calls at runtime (capture dispatch, error formatting,
the compiled Tokenizer/Parser combinators, the LR/GLR shift-reduce/GSS
runtime, plus the standalone Ichor.Toolkit.Pratt/TermWalk/
Ichor.Backtrack); this repository (ichor) depends on it via a
normal ~> Hex version constraint, like any other dependency. If your
change touches something a generated parser calls at match/evaluation
time, you very likely want
ichor_runtime's own repo
and its own CONTRIBUTION.md, not this one.
git clone <this repository>
cd ichor
mix deps.get
mix test
That should complete with no failures on a clean checkout. If it doesn't, please open an issue before doing anything else — that's a bug in its own right.
Project layout
lib/aether/— the Aether lexer and parser (.aethersource ->Grammar.IR).lib/grammar/—Grammar.IRitself, the analysis pass, and both backends (Grammar.VM,Grammar.Native), plus the LR/GLR table builder (Grammar.LRTable.Builder,Automaton/Desugar/Sets) — the dev-time-only half of LR/GLR support; the runtime half lives inichor_runtime.lib/ichor/— the ABNF/BNF/EBNF/PEG format importers.Ichor.Actionsitself, along with everything else a generated parser calls at runtime, lives inichor_runtime's own repo instead — see its README for the full list and the reasoning behind the split.lib/mix/tasks/—mix ichor.tokensandmix ichor.gen.priv/grammar/— the.aethersources the format importers compile from (read once, at compile time, viause Ichor).test/— one directory per worked-example grammar (test/lisp/,test/yaml/, ...), plustest/support/for shared test fixtures and Actions implementations.guides/— the documentation underguides/, published via ExDoc alongside the generated module docs.
Making a change
Tests first, or at least alongside. A grammar-level change should come with a test exercising the actual input/output behavior, not just "does this parse." Several bugs found during this project's own development only showed up once a worked example was actually run end-to-end, not merely checked for successful parsing.
Both backends, where it applies.
Grammar.VMandGrammar.Nativeare required to agree on every grammar's behavior. A change to shared semantics (an IR node's meaning, a compiler pass) needs verification against both, not just whichever one you happened to be testing against.Which repo does this belong in? If you're adding something a generated parser calls at match/evaluation time (not just once at codegen time), it almost certainly belongs in
ichor_runtime, not here — that was the exact gapIchor.Actions.evaluate_node/3andIchor.Toolkit.Pratt/TermWalk/Ichor.Backtrackwere originally filed under before being moved (see CHANGELOG.md's[Unreleased]entry for the reasoning). If in doubt: does a project depending onichor_runtimealone (withichoronly: :dev, runtime: false) still need it at runtime? If yes, it belongs inichor_runtime, which means opening the PR against that repo instead.Run
mix precommitbefore opening a PR (chainsformat->compile --warnings-as-errors->credo --strict->sobelow->test->dialyzer):mix precommitAlso run
mix docsand skim the output for warnings — a broken moduledoc cross-reference or extras link won't fail the build but will show up there.Match the existing documentation style. Default to no comments; when one is warranted, explain a non-obvious why (a hidden constraint, a subtle invariant, the specific bug class it prevents), not what the code already makes obvious by being well-named. Moduledocs should be self-contained — don't cite an external design document or a numbered "phase," since neither exists in this repository; document the library as it actually is.
Adding a new grammar-format importer
If you're adding support for reading another external grammar notation
(along the lines of Ichor.ABNF/Ichor.BNF/Ichor.EBNF.ISO/
Ichor.EBNF.W3C/Ichor.PEG), the existing five are the template to
follow:
- Write an
.aethergrammar describing the target format's own syntax, underpriv/grammar/. - Write an
Ichor.Actionsimplementation whose target category isGrammar.IR— turning a parsed rule of the external format into the equivalentGrammar.IRexpression, the same wayIchor.ABNF.ActionsorIchor.PEG.Actionsdo. - Compile the pair via
use Ichor, nested underIchor.*(not a bare top-level module name), matching the existing five. - Verify the importer fails informatively (a normal
Ichor.Error, not a crash) on syntax that isn't valid in your target format, not just that it succeeds on syntax that is.
Reporting bugs
Please include: the grammar (or a minimal excerpt reproducing the
issue), the input, what you expected, and what actually happened
(including the full Ichor.Error, if one was raised). "Doesn't parse"
and "doesn't work" are much harder to act on than a specific
input/expected/actual triple.
License
By contributing, you agree that your contributions will be licensed under the project's MIT license.