djot.v GitHub

djot.v

A djot parser written and verified in Rocq, and extracted to OCaml.

djot was designed around a few goals: parsing never backtracks, text means the same thing inside a container as outside it, a link is recognized without looking at the rest of the document, and a paragraph can be hard-wrapped anywhere. Here those goals are theorems about the parser.

djot is one setting of the parser. Its inline delimiters can be respelled, its constructs can be switched off, and extensions can be switched on. For each setting, the proofs say which of the properties it keeps. A Markdown-like profile, for example, keeps no backtracking and uniformity, and gives up safe hard-wrapping.

Versions

Version
0.1.0
Syntax reference
jgm/djot at d77f8a0, 2026-06-16
Built from
kernel-0.1.0-133-gfa2f052, 2026-10-15

Properties

The status of each property under the two named profiles. The playground shows the same for any profile.

proved
The theorems hold for the profile.
conditional
They hold under a condition on the document, stated below the status.
broken
The property fails for the profile, for the reason stated.
conjectured
Believed to hold, and not proved for the profile.
unknown
No claim either way.
inapplicable
The property is about a construct that is switched off.
Propertydjotmarkdown-like
A later line never changes a block already parsed.provedproved
Each byte of a paragraph is read once.provedproved
After an edit, reparsing can stop once the parser is back in the state it had before the edit.provedproved
Replacing one block leaves the parse of every other block unchanged.provedproved
Recording source positions does not change the parse.provedproved
Parsing takes linear time.conjecturedconjectured
Text inside a block quote parses as it would at top level.provedproved
Text inside an item of a bullet or ordered list parses as it would at top level, when indented the way the formatter writes it.provedproved
The same, for the items of a definition list.provedproved
Text inside a fenced div parses as it would at top level, unless it contains the div's own closing fence.provedproved
Lines indented under a footnote belong to it, cannot affect anything outside it, and parse as they would at top level.conditionalNot when the footnote's first line starts a list: the indentation of the following items is measured from the footnote marker.conditionalNot when the footnote's first line starts a list: the indentation of the following items is measured from the footnote marker.
A text line that continues a paragraph may leave out the prefixes of the quotes, list items and footnotes around it, and parses as it would with them written.provedconditionalNot for a line that underlines the paragraph: it makes a setext heading.
A list is loose exactly where a blank line separates two of its items, or two blocks inside one item.conditionalA blank line inside a code block does not count.conditionalA blank line inside a code block does not count.
Indenting every line of a document by the same amount does not change its parse.provedproved
Whether [foo][bar] is a link does not depend on whether bar is defined.provedproved
Adding, removing or changing a reference definition changes only link and image attributes in the HTML.provedproved
A line inside a paragraph never starts a new block.provedbrokenA line that begins with a list marker starts a list.
A heading continues on the following lines until a blank line.provedbrokenA heading is one line.
Which blocks a document has, and how they nest, is decided without reading inline syntax.conditionalExcept a table caption, which disappears when its inline content is empty.conditionalExcept a table caption, which disappears when its inline content is empty.
When delimiters overlap, the first opener that gets closed wins, and a closer takes the closest open opener. Exactly one reading follows these rules, and the parser gives it.provedconditionalFor a paragraph without a character an extension claims: `[[` with wikilinks, `$` with dollar math, `:` with tags, `%` with holes, a hyphen when the delete delimiter is wider than one, or a parenthesis used as a delimiter.
The same rules, written as a grammar over a paragraph's delimiters, give every paragraph exactly one parse, and it is the reading the rules allow.provedproved