Appearance
Full Specification
FreshComplete 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@vocabIRI— 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 = "+#" fragmentIntroduces 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 = "." iriRefEmits: currentSubject rdf:type resolvedIRI .
Multiple types may appear in one block: {.prov:Person .foaf:Agent}.
2.4 Predicate Forms
Literal predicate (no prefix):
predicate = iriRefEmits: currentSubject predicateIRI carrierLiteral .
Object predicate (? prefix):
objectPredicate = "?" iriRefEmits: currentSubject predicateIRI objectIRI . (where objectIRI comes from the most recent {+iri} in the same block)
Reverse predicate (! prefix):
reversePredicate = "!" iriRefEmits: objectIRI predicateIRI currentSubject . (triple points from object to subject)
2.5 Literal Modifiers
Datatype:
datatype = "^^" iriRefModifies 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
| Syntax | Carrier 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
| Syntax | Carrier type |
|---|---|
# text | Heading (levels 1-6) |
- text | Unordered list item |
1. text | Ordered list item |
> text | Blockquote |
```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:
parse(D)produces quadsQgenerate(Q)produces textTparse(T)produces quadsQ'QandQ'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:
NamedNodefor all IRI referencesLiteralfor all literal values, with optional datatype or language tagQuadas 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)