Skip to content

Full Specification

Fresh

Complete canonical MD-LD specification with grammar and formal definitions.

1. Document Structure

An MD-LD document is a CommonMark Markdown document with optional semantic annotations. Removing all {...} annotation blocks from an MD-LD document produces a valid Markdown document.

1.1 Context Declarations

Context declarations appear at the start of a document (or at any point before use) and define prefix bindings:

[prefix] <IRI>
  • prefix — a valid prefix name ([A-Za-z][A-Za-z0-9_-]*) or @vocab
  • IRI — any IRI or a CURIE referencing a previously declared prefix

Prefix folding: the IRI may reference a previously declared prefix by CURIE. The CURIE is resolved using the accumulated context at the point of declaration. Forward references are not permitted.

Built-in context (always present, cannot be overridden):

javascript
{
  '@vocab': 'http://www.w3.org/2000/01/rdf-schema#',
  'rdf':    'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  'rdfs':   'http://www.w3.org/2000/01/rdf-schema#',
  'xsd':    'http://www.w3.org/2001/XMLSchema#',
  'sh':     'http://www.w3.org/ns/shacl#',
  'prov':   'http://www.w3.org/ns/prov#'
}

The @vocab prefix means that bare predicates (e.g., label, comment) expand to the RDFS namespace.

1.2 Annotation Blocks

An annotation block is a sequence of tokens enclosed in { and }:

attrsBlock = "{" whitespace* attrsTokens? whitespace* "}"
attrsTokens = attrsToken ( whitespace+ attrsToken )*

Annotation blocks attach to the preceding carrier (inline span, heading, blockquote, list item, or code fence). They may also appear standalone (subject declarations only).

2. Annotation Tokens

All tokens support an optional remove polarity prefix (-).

2.1 Subject Declaration

subjectDecl = "=" ( iriRef | "#" fragment )?
  • {=my:node} — set current subject to full IRI
  • {=#fragment} — set current subject to base#fragment
  • {=} — clear current subject

Subject declarations emit no triples. They only update parser state.

2.2 Object Declaration

objectDecl = "+" iriRef
objectFragmentDecl = "+#" fragment

Introduces an object IRI for use with ?p and !p predicates in the same annotation block. Does not change the current subject.

2.3 Type Declaration

typeDecl = "." iriRef

Emits: currentSubject rdf:type resolvedIRI .

Multiple types may appear in one block: {.prov:Person .foaf:Agent}.

2.4 Predicate Forms

Literal predicate (no prefix):

predicate = iriRef

Emits: currentSubject predicateIRI carrierLiteral .

Object predicate (? prefix):

objectPredicate = "?" iriRef

Emits: currentSubject predicateIRI objectIRI . (where objectIRI comes from the most recent {+iri} in the same block)

Reverse predicate (! prefix):

reversePredicate = "!" iriRef

Emits: objectIRI predicateIRI currentSubject . (triple points from object to subject)

2.5 Literal Modifiers

Datatype:

datatype = "^^" iriRef

Modifies the carrier literal to have the specified XSD datatype.

Language tag:

language = "@" langTag
langTag = letter ( letter | digit | "-" )*

Modifies the carrier literal to have the specified language tag.

2.6 Polarity

Any token may be prefixed with - to negate it:

removeMod = "-"
modifiedToken = removeMod? token
  • -p — retract the literal triple
  • -?p — retract the object property triple
  • -!p — retract the reverse property triple
  • -.Class — retract the type triple

2.7 Token Combinations

Multiple tokens may appear in one annotation block. Order is semantically unordered:

markdown
{=my:alice .prov:Person label}
{+my:bob ?my:knows .prov:Person label}
{=my:doc -.ex:Draft .ex:Published -my:version my:status}

3. Value Carriers

A carrier provides the literal value for annotations with literal predicates.

3.1 Inline Carriers

SyntaxCarrier type
[text]Bracketed span
[text](url)Link with display text
**text**Bold emphasis
*text*Italic emphasis
`text`Code span
<URL>Angle bracket link

3.2 Block Carriers

SyntaxCarrier type
# textHeading (levels 1-6)
- textUnordered list item
1. textOrdered list item
> textBlockquote
```code```Fenced code block

3.3 Carrier Value Extraction

The carrier value is the text content of the carrier:

  • For headings: the heading text
  • For list items: the item text
  • For blockquotes: the quoted text
  • For inline spans: the span text
  • For code spans/blocks: the code content

4. Context and Scope

4.1 Subject Scope

The current subject is established by a subject declaration ({=IRI}) and persists until:

  • A new subject declaration is encountered
  • The subject is explicitly cleared ({=})
  • End of document

A heading with a subject declaration sets the subject for all content in that section.

4.2 Prefix Scope

Prefixes persist for the entire document once declared. Later declarations override earlier ones with the same prefix name.

4.3 Fragment Resolution

A fragment declaration {=#fragment} sets the subject to currentBaseIRI#fragment, where currentBaseIRI is the current subject with any existing fragment stripped.

5. Quad Emission

5.1 Literal Triples

For an annotation [value] {predicate} with current subject S:

emit: S predicate "value" .

With datatype: S predicate "value"^^datatype .

With language: S predicate "value"@lang .

5.2 Object Triples

For [text] {+O ?predicate} with current subject S:

emit: S predicate O .

5.3 Reverse Triples

For [text] {+O !predicate} with current subject S:

emit: O predicate S .

5.4 Type Triples

For {.Class} with current subject S:

emit: S rdf:type Class .

5.5 Polarity Resolution

When a triple is asserted with polarity + (or no polarity marker):

  • The triple is added to the quad buffer and the quads array

When a triple is asserted with polarity -:

  • If a matching triple exists in the quad buffer (intra-document): both are removed from the buffer and the quads array
  • If no matching triple exists in the quad buffer (external): the triple is added to the remove set

5.6 Hard Invariant

At document completion, the hard invariant is enforced:

quads ∩ remove = ∅

Any triple that appears in both is removed from the remove set. This invariant guarantees that no triple is simultaneously present and retracted.

6. Forbidden Constructs

The following are explicitly not expressible in MD-LD:

  • Blank nodes (every resource must have a named IRI)
  • Implicit subjects (a subject must be explicitly declared)
  • Structural inference (the parser never infers triples from document structure)
  • Nested annotation blocks ({...{...}...})
  • Key-value pairs inside annotation blocks

7. Normative Properties

7.1 Determinism

The same input always produces the same output. Given identical text and context, parse() always produces the same quads, remove, statements, origin, and primary.

7.2 Round-Trip Safety

For any MD-LD document D:

  1. parse(D) produces quads Q
  2. generate(Q) produces text T
  3. parse(T) produces quads Q'
  4. Q and Q' are semantically equivalent (same triples, possibly different ordering)

7.3 Single-Pass Processing

The parser processes the document in a single forward pass. No backtracking, look-ahead beyond the current line, or second-pass processing is performed.

7.4 No Inference

The parser emits only the triples explicitly present in annotations. It does not add triples through RDFS entailment, OWL reasoning, SHACL inference, or any other reasoning process.

8. Compatibility

8.1 RDF/JS Data Model

Generated quads conform to the RDF/JS Data Model specification:

  • NamedNode for all IRI references
  • Literal for all literal values, with optional datatype or language tag
  • Quad as the container

8.2 W3C Standards

MD-LD is compatible with:

  • RDF 1.1 (core data model)
  • W3C CURIE 1.0 (compact URI syntax)
  • RFC 4151 (tag: URI scheme)
  • ISO 8601 (date/time literals)