Skip to content

Carve extensions contract (NORMATIVE)

This document is normative. The conformance corpus (tests/corpus) remains the authority for Tier-1 output; the optional Tier-2 corpus (tests/corpus-optional) pins configuration-dependent outputs per feature id. This document defines the feature taxonomy and the extension mechanism every implementation realizes.

1. Feature taxonomy

TierDefinitionDefaultConformance
1 · CoreNormative syntax in resources/grammar.ebnf + the corpus; identical output everywhere.Always onMandatory (corpus)
2 · Standard-recommendedSpec-listed behaviors every impl SHOULD offer but ship off/passthrough.Off / passthroughOptional corpus when enabled
3 · App extensionNot in the spec, not conformance-tested, may exist in one impl only.OffNever

Invariant: a feature's tier is identical in every language; a Tier-1 feature is core-and-default-on everywhere and its default output is corpus-pinned.

  • Tier 1: corpus categories 01–88 (admonitions, footnotes, cross-references, list-item attributes, ::: | verse, <…> autolinks, the :name[…] / ::: name extension syntax). Recognized ::: type words (the eight admonitions + line-block) are catalogued in examples.md. Smart typography and @mention / #tag / :emoji: parsing are also default-on and corpus-pinned, but per grammar PART 19 a processor MAY disable them.
  • Tier 2: configuration over Tier-1 syntax — mention/tag→URL, emoji glyph map, locale smart-quote sets, bare-URL autolinking.
  • Tier 3: Mermaid, Tabs, CodeGroup, TableOfContents, HeadingPermalinks, HeadingLevelShift, ExternalLinks, DefaultAttributes, Wikilinks, SemanticSpan.

Inline and sidenote footnotes are not Tier 3. They are deferred core reserved syntax ([^…] inline, [>…] sidenote; resources/grammar.ebnf PART 9 §16), not an app extension. The djot-php […]{.fn} form maps onto carve's [^…]; see native-features-analysis.md.

2. Extension system

An extension is a named unit contributing any subset of four things, run as:

parse (core + extension MATCHERS)
  → afterParse TRANSFORM → beforeRender TRANSFORM
  → render (core + extension RENDERERS)

2.1 Matchers (parse stage) — scanner-function contract

  • inline: (text, pos, ctx) -> { node, end } | null
  • block: (lines, ctx) -> { node, linesConsumed } | null
  • ctx exposes: definition tables (link/footnote/abbr); recursive parseInlines(text) / parseBlocks(lines); the extension's config.
  • Precedence: core matchers run first at each position; extension matchers are tried only where core does not consume (extensions add syntax, never hijack core). Extension matchers run in registration order; optional integer priority is the escape hatch.

2.2 Transforms

  • afterParse (Document) -> Document (collection/inspection)
  • beforeRender (Document) -> Document (mutation)
  • Every extension's afterParse runs before any extension's beforeRender; within a phase, registration order.

2.3 Renderers

  • Registered per node type / extension name; receive the node, emit implementation-specific output. Renderers are the impl-idiomatic half; matchers and transforms are the portable contract.

2.4 Registration & config

  • Impl-idiomatic (PHP addExtension/ctor; JS extensions: [...] option), declaring the same lifecycle contributions.

3. Home, conformance, per-impl

  • This document is the normative home for the taxonomy + contract.
  • syntax.md §4.20 is the non-normative narrative.
  • Conformance: Tier-1 = existing corpus (mandatory); Tier-2 = tests/corpus-optional + manifest.json, run per enabled feature; Tier-3 = never in any corpus.

Released under the MIT License.