The specification is written so that a second implementation is possible without reading the first. This is the order that works.
A processor (Core profile)
Line classifier and lexer
Split bytes into lines, handling LF, CRLF, CR and a byte-order mark. Classify each line (statement, table row, comment, fence, prose). Keep the lexer’s state between chunks so it only ever scans new bytes. Sections 4 and 5.
Statement parser
A recursive-descent or Pratt parser over the tokens of one statement. Apply the recovery rules as you go and record a diagnostic for each. Sections 6 to 8.
Catalog
Load the manifest (schemas/catalog.schema.json). Build the derived sets: flag names, enum values per component, aliases. Implement the closest-name rule. Section 9.
Binding arguments
Turn a parsed call into a node: resolve bare words, bind positional and named arguments, coerce values, collect children. Section 11. Then tables: section 12.
The tree and edits
Assign node ids, build the tree, apply replace, patch, append and delete. Sections 13 and 14.
End of stream
Close open brackets, drop unresolved references, break cycles, find the root, report unreachable statements. Order the diagnostics as section 22.2 requires. Section 15.
Limits
Apply every processor limit of section 21.1. Test with hostile input: deep nesting, huge tables, a shared-reference graph.
Run the suite
Every case, whole and one character at a time. See Conformance.
A good first milestone is the first twenty cases, which cover the common path. The reference processor is about 16 KB gzip of TypeScript, so this is a project of weeks, not months.
A runtime (Interactive and Tools)
- An evaluator for the expression grammar that interprets, never compiles.
- State with declared defaults and change notification.
- Dependency tracking, so a state change re-evaluates only what read it.
- The built-in functions of section 18.7, exactly.
- A query manager: caching per argument set, refresh timers, cancellation, the call budget.
- An action runner: steps in order, stop at the first failure, report through the action envelope.
- Every runtime limit of section 21.2.
A renderer
- Map each catalog component to a native one. Key on node ids, so an update touches only what changed.
- Draw
#pendingnodes as placeholders and nothing for#unknownbeyond their children. - Apply the URL policy before loading any image, video or background (section 23.4).
- Render Markdown without raw HTML.
- Meet the accessibility requirements of section 24.
- Report every interaction with the action envelope of section 20.3.
Use the reference implementation as an oracle
When the specification seems ambiguous, compare with @gistui/core:
import { parse } from "@gistui/core";
const r = parse(source, catalog);
console.log(JSON.stringify({ tree: r.root, errors: r.errors, valid: r.valid }, null, 2));If the two disagree and the specification does not settle it, that is a defect in the specification. Please report it: Appendix D lists the points already known.
Schemas
| Schema | For |
|---|---|
tree.schema.json |
The abstract tree in JSON form |
diagnostic.schema.json |
One diagnostic |
catalog.schema.json |
A catalog manifest |
action.schema.json |
The action envelope |
capabilities.schema.json |
The capability object |
expected.schema.json |
A conformance expectation file |
They are in spec/schemas/. Their $ids are under https://gistui.com/schemas/1.0/; those are identifiers, and the files are not published there yet.