Appearance
Grammar
FreshThe official MD-LD grammar is published in two forms: a TextMate grammar for syntax highlighting, and an EBNF for formal specification.
Files
mdld.tmLanguage.json— The TextMate grammar (published standard)mdld.ebnf— ISO 14977 EBNF grammar (reference specification)
Philosophy
The TextMate grammar is the core published artifact for MD-LD syntax, replacing the need for formal parser generators (Nearley, BNF, etc.). This approach:
- Portability — Works with VS Code, Sublime Text, GitHub, Shiki, and any TextMate-compatible tool
- Practicality — Syntax highlighting is the primary use case for grammar definitions
- Simplicity — Single, well-maintained grammar instead of multiple format variants
- Verification — The handmade parser can validate against this grammar
Grammar Structure
1. Context Declarations
json
{
"match": "^\\s*(\\[)(@vocab|[A-Za-z][A-Za-z0-9_-]*)(\\])\\s+(<)([^>]+)(>)",
"captures": {
"2": { "name": "entity.name.tag.prefix.mdld" },
"5": { "name": "string.unquoted.iri.mdld" }
}
}2. Attribute Blocks
The heart of MD-LD — attribute blocks {...} contain semantic annotations:
| Token | Pattern | Example |
|---|---|---|
| Subject | = | {=ex:subject} |
| Fragment | =# | {=#fragment} |
| Object | + | {+ex:object} |
| Type | . | {.foaf:Person} |
| Predicate | (bare) | {predicate} |
| Object Predicate | ? | {?predicate} |
| Reverse Predicate | ! | {!predicate} |
| Datatype | ^^ | {^^xsd:integer} |
| Language | @ | {@en} |
| Remove | - prefix | {-predicate} |
3. Value Carriers
Annotations attach to Markdown constructs:
- Headings:
# Title {=subject .Type} - List items:
- Item {predicate} - Blockquotes:
> Quote {=subject} - Inline spans:
[text] {+object} - Code fences:
```code``` {=subject} - Standalone:
{=subject}
Usage with Shiki
javascript
import { getHighlighter } from 'shiki';
import mdldGrammar from './mdld.tmLanguage.json';
const highlighter = await getHighlighter({
themes: ['github-light'],
langs: [
{
id: 'mdld',
...mdldGrammar
}
]
});
const html = highlighter.codeToHtml(mdldSource, {
lang: 'mdld',
theme: 'github-light'
});Scope Names
| Construct | Scope |
|---|---|
| Prefix | entity.name.tag.prefix.mdld |
| IRI | string.unquoted.iri.mdld |
| Subject | entity.name.subject.mdld |
| Object | entity.name.object.mdld |
| Type | entity.name.type.mdld |
| Predicate | variable.other.predicate.mdld |
| Datatype | entity.name.type.datatype.mdld |
| Language | entity.name.language.mdld |
| Remove mod | keyword.operator.remove.mdld |
| Attribute block | meta.attrsBlock.mdld |
EBNF Grammar
ISO 14977 EBNF formal grammar (version 1.1, 2026-05-04):
ebnf
(* MD-LD EBNF Grammar *)
(* Defines syntax of MD-LD annotations embedded in CommonMark Markdown *)
(* 1. Lexical elements *)
letter = "A".."Z" | "a".."z" ;
digit = "0".."9" ;
whitespace = " " | "\t" ;
newline = "\n" | "\r\n" ;
vchar = ? any visible non-whitespace character ? ;
text = { vchar | whitespace } ;
(* 2. Context declarations *)
contextDecl = "[" , contextKey , "]" , whitespace , "<" , contextIri , ">" ;
contextKey = "@vocab" | prefixName ;
prefixName = letter , { letter | digit | "-" | "_" } ;
contextIri = iri | curie ;
(* 3. Attribute block *)
attrsBlock = "{" , whitespace* , attrsTokens? , whitespace* , "}" ;
attrsTokens = attrsToken , { whitespace+ , attrsToken } ;
(* 4. Attribute tokens with optional remove polarity *)
removeMod = "-" ;
modifiedSubjectDecl = [ removeMod ] , subjectDecl ;
modifiedObjectDecl = [ removeMod ] , objectDecl ;
modifiedTypeDecl = [ removeMod ] , typeDecl ;
modifiedPredicate = [ removeMod ] , predicate ;
modifiedObjectPredicate = [ removeMod ] , objectPredicate ;
modifiedReversePredicate = [ removeMod ] , reversePredicate ;
modifiedDatatype = [ removeMod ] , datatype ;
modifiedLanguage = [ removeMod ] , language ;
(* 5. Subject and object declarations *)
subjectDecl = "=" , [ iriRef | "#" , fragment ] ;
fragmentDecl = "=#" , fragment ;
objectDecl = "+" , iriRef ;
objectFragmentDecl = "+#" , fragment ;
(* 6. Predicates *)
predicate = iriRef ; (* literal predicate *)
objectPredicate = "?" , iriRef ; (* object predicate *)
reversePredicate = "!" , iriRef ; (* reverse predicate *)
(* 7. Type declaration *)
typeDecl = "." , iriRef ;
(* 8. Literal modifiers *)
datatype = "^^" , iriRef ;
language = "@" , langTag ;
langTag = letter , { letter | digit | "-" } ;
(* 9. CURIEs and IRIs *)
iriRef = curie | iri | fragment ;
curie = prefixName , ":" , reference ;
reference = ( letter | digit ) , { letter | digit | "-" | "_" | "." } ;
fragment = reference ;
(* 10. Value carriers *)
inlineSpan = emphasisSpan | strongSpan | codeSpan | linkSpan ;
heading = "#" , { "#" } , whitespace , text ;
blockquote = ">" , whitespace , text ;
(* 11. Grammar guarantees (normative) *)
(* Attribute token order is semantically unordered *)
(* Lists are pure Markdown structure with no semantic scope *)
(* No blank nodes *)
(* No implicit inheritance across list levels *)
(* Deterministic, streaming-friendly parsing *)
(* Round-trip safe *)
(* No semantic inference at syntax level *)
(* 12. Forbidden constructs *)
(* - Blank nodes *)
(* - Key-value pairs *)
(* - Nested attribute blocks *)
(* - Implicit subjects or predicates *)
(* - Structural inference *)
(* - Semantic list propagation *)Origin Integration
The "flat" quad-based architecture works seamlessly with TextMate highlighting:
- Parser emits Quads + Origin
{start, end, type} - Shiki generates HTML spans based on TextMate grammar
- Glue layer uses Origin data to overlay interactive behavior
Because MD-LD is flat (not tree-based), the mapping between text ranges and semantic data is straightforward. Each character range maps to exactly zero or one quads.