The highest-impact thing you can do for GEML: implement it from the spec in another language. Two independent parsers that agree are the proof the spec is unambiguous — and what makes GEML a standard, not one repo.
It’s a weekend project, and you can self-certify: reproduce a set of JSON conformance cases, then parse the spec’s own .geml file cleanly. Building one? Open an implementation issue — we’ll help and link it. No need to finish it all at once.
Your parser turns GEML source into a document model (blocks and inline nodes). It’s a conforming parser (§8.2) when:
GEML-spec.geml with zero error diagnostics — that exercises fences, attributes, references, tables, charts, and metadata.#id is unique, and every [[#id]], [text](#id), [^id], chart data=#id, and output of=#id points at something real.U+0000 → U+FFFD. Four lines of code, and skipping them is the most common way a second implementation silently disagrees with the reference on real-world files.The suite pins the ambiguous parts (inline emphasis, list nesting); the dogfood covers the rest.
Two things you also owe an untrusted document, per §9: bound your recursion depth (block, list, and inline nesting — emit the *-nesting-too-deep error and keep going, never blow the stack), and neutralize non-http/https/mailto/tel URL schemes when you build the model, not at the rendering sink.
Plain JSON — copy it in and run it with your own harness. In geml-parser/test/conformance/:
| File | Covers |
|---|---|
inline.json |
emphasis / strong / strikethrough, the rule of three, escapes, nesting |
precedence.json |
atom vs. emphasis order: code, math, links, images, footnotes, breaks |
lists.json |
ordered/unordered, start, indentation nesting, tight vs. loose, task markers |
Each case is { name, geml, want }:
{ "name": "em inside strong", "geml": "**a *b* c**", "want": "strong(\"a \" em(\"b\") \" c\")" }
want is a projection of the parsed model — a compact string. Project your model the same way and assert it equals want.
text "abc" (JSON-quoted)
emphasis em( … )
strong strong( … )
strikethrough s( … )
code span code("…")
inline math math("…")
hard break br
image img("src")
link link("target" children…) target = href | #anchor | doc#anchor
auto-reference ref("target")
footnote ref fn("id")
paragraph children, space-separated
heading level N hN( children… )
list ul[…] | ol[…] suffix "*" if loose, "@N" if ordered start ≠ 1
list item li(…) | li[ ](…) | li[x](…) nested lists appended inside
Children join with one space. _project.mjs is the reference projection — the tiebreaker if anything is unclear. impl2.mjs is a full parser + projection written only from the spec (a few hundred lines) — a worked example of what you’re building.
Each step maps to a spec section and what tests it. Do them incrementally.
U+0000. Do this first and everything downstream gets simpler; every step preserves the line count, so you can still index the original bytes by line. → preliminaries=-run opens a block, an equal-length run closes it, a longer fence nests; ATX headings, lists, paragraphs, %% lines. → dogfood{#id .class key=val} (§4) — value typing; a bare word with no = is a boolean flag. → dogfoodmeta + `` interpolation (§3–§4) — substitute in flow source text, skipping the verbatim atoms (code spans, inline math) and escaped \. → interp.jsoninline.json, precedence.jsonstart, nesting, tight/loose, [ ]/[x]. → lists.jsonformat=csv/tsv parse to one model; compute=, summary=. → dogfoodgeml-chart data=#id charts a table by reference. → dogfoodStep 0 plus 1–5 give a useful parser. 6 is what makes GEML GEML. 7–8 are the payoff.
for file in [inline.json, precedence.json, lists.json, interp.json]:
for case in load(file):
assert project(parse(case.geml)) == case.want
doc = parse(read("spec/GEML-spec.geml"))
assert no "error" diagnostic in doc.diagnostics
# §0.5 — the same document, four ways, must give the same model
base = "# T\n\n- a\n- b\n"
assert parse(base) == parse("" + base) == parse(base.replace("\n", "\r\n"))
# Appendix A — every code you emit is in the catalogue, at its declared severity
for d in parse(read("spec/GEML-spec.geml")).diagnostics + your_error_fixtures():
assert d.code in APPENDIX_A and d.severity == APPENDIX_A[d.code]
Suite green + dogfood clean + §0.5 + Appendix A = an independent, conformant GEML parser. Open an issue or PR (CONTRIBUTING.md) and we’ll add it to the README.
GEML-spec.md (§1–§8) + GEML-history-spec.md.GEML-spec.geml — the spec in GEML; your end-to-end test.geml-parser/ — the reference implementation (a guide; the spec is the definition).