Category: MarkupD. Bott
Version: 1.0Adrift Labs
April 2026
Mog Syntax Specification

Introduction

Mog is a markup syntax designed for rich, interconnected documents: knowledge bases, wikis, literate programs, and publishing on the web. It draws from Markdown, Org, and Neorg, but diverges with two-character delimiters, explicit nesting, and inline attributes. Together these create a unified grammar for expression . This document specifies the structure and rules for markup in a Mog document.

Table of Contents

1. Document Structure

1.1. Text Encoding

Documents are UTF-8 encoded files. A leading byte-order mark (U+FEFF) will be stripped by the parser and not considered document content. Line endings can be LF (U+000A) or CRLF (U+000D U+000A), parsing will normalize to LF.

1.2. Whitespace

The following Unicode characters are considered whitespace:

  • Tab (0x09)
  • Space (0x20).

Whitespace is not used to structure Mog. Leading and trailing whitespace will be trimmed when parsing a document. The following headings parse identically:

# My heading
  # My heading

1.3. Paragraphs

A paragraph is a continuous sequence of text lines. Paragraphs are terminated by a blank line, structural marker, or the end of the document. Line breaks within a paragraph are parsed as soft wraps.

1.4. Metadata

Metadata may be optionally included at the beginning of a document, delimited by two hyphens. Data is formatted in KDL syntax:

--
title "My Document"
authors "John" "Jane"
date "2026-04-15"
version 1
--

1.5. Escape Sequences

A leading backslash can be used to make any delimiter or marker literal. An isolated backslash does not need self-escaping because all delimiters are two characters.

\**this is not bold\**

2. Structural Markers

Structural markers are used to give documents structure. These markers open the line they are used on, ignoring leading whitespace. Structural markers are repeated to explicitly indicate nesting. Using a heading # marker once is a top-level heading, while ## two markers represents a subheading and so on. These rules apply to all structural markers.

MarkerAST NodeSemantic Meaning
#headingSection heading
-unordered-listUnordered list item
.ordered-listOrdered list item
>blockquoteBlockquote
=thematic-breakThematic Break

2.1. Nesting

All structural markers can nest. Nesting is expressed through marker repetition. A repeated marker indicates a sub-entity:

# Heading 1
## Heading 2
### Heading 3
 ## Heading 2 with leading whitespace
  # Heading 1 with leading whitespace

2.2. Blocks

Structured text may also span multiple lines. All structural blocks must have a matching set of closing markers.

- Reading notes
--
Chapter 3 was particularly relevant:
 >
  ...quote from chapter 3...
 >
--

2.3. Tasks

A task marker can be placed after any structural marker. Tasks are marked using square brackets: [ ]. The status of a task is determined by the character between brackets.

MarkerStatus
[ ]Undone
[x]Done
[~]In progress
[?]Uncertain
[!]Urgent
[-]Cancelled
- [~] Grocery shopping
-- [x] Eggs
-- [~] Milk
-- [?] Oat or almond?
- [ ] Clean kitchen
- [!] Call dentist
- [-] Return sweater

3. Semantic Delimiters

Semantic delimiters are used both inline and as blocks. All delimiters open and close with two-characters. None can nest within themselves.

When semantic delimiters are parsed leading and trailing whitespace will be removed.

DelimiterAST NodeSemantic Meaning
**strongBold
__italicItalic
``verbatimLiteral text
~~strikethroughStruck-through text
$$mathMath expression
||table-cellTable cell
-|table-rowTable row
[[ ]]linkLink target
(( ))link-nameLink name
{{ }}link-footnoteFootnote content
The quick brown fox jumped over the **lazy dog.**

**
The quick brown fox jumped over the lazy dog.
**

A paragraph **with
soft wrapping** in
it.

3.1. Math

Text contained within math delimiters is treated as verbatim, but evaluated as Typst math when rendered.

Einstein's famous equation $$E = m c^2$$ changed physics.

$$
E^2 = (m c^2)^2 + (p c)^2
$$

3.2. Tables

Tables are declared in two parts: header and row. The header line of a table is delimited using pipes ||. Following the header line, each entry row is delimited by a hyphen and pipe -|. Table cells can include semantic delimiters for presentation of tabulated data.

|| Name       || Type      || Color    ||
-| Apple      || Fruit     || Red      ||
-| **Carrot** || Vegetable || Orange   ||
-| Blueberry  || Fruit     || Blue     ||

|| Name || Type || Color || -| Banana || Fruit || Yellow ||

Links [[ ]] are formatted with square brackets. A link can be bare, or it may have a name (( )) and footnote {{ }}.

- [[https://kdl.dev]]
- [[https://kdl.dev]]((KDL))
- [[https://kdl.dev]]((KDL)){{ A node-based document language }}

Link resolution is driven by attributes. A bare link is a document reference and is resolved against filenames and metadata title fields. Headings can be referenced using the # attribute. External targets will be identified by their protocol attribute.

[[recipe]]               Document reference
[[#:Ingredients]]        Heading in current document
[[https://kdl.dev]]      External via protocol attribute

Footnotes

When footnote contents do not fit inline, they may be linked from elsewhere in the document:

A good marinara starts with San Marzano
[[tomato]]((tomatoes)) and a generous amount
of olive oil.

[[tomato]]{{ A fruit not a vegetable }}

Footnotes can appear without a link. If unattached, a footnote will be indexed by its position in the document.

A good marinara starts with San Marzano
tomato{{ A fruit not a vegetable }} and a generous amount
of olive oil.

4. Attributes

Attributes are generic data attached to a marker or delimiter, terminated using a colon. Attribute names may not contain whitespace. Following attribute declaration, an optional whitespace character may be inserted. If present, it will be trimmed when parsed.

##heading1:My Heading

Structural marker with no whitespace after attribute

##heading2: My Heading

Structural marker with whitespace after attribute

-list-attribute: list item with **red: BOLD** content

Additiona attributes can be added as a chain, beginning from the terminating colon of the prior attribute. Attribute chains must stay on a singe line, newline or whitespace declares non attribute content.

##attribute1:attribute2: My Heading

For complex data attribtes may be structed as Janet forms. The following Janet forms are permitted:

-{:key "value"}: Table attribute
-["item 1" "item 2"]: List attribute

Here you can see attributes added to a heading, list item, and bold delimiter. If you are reading this document as text you may also see the above code blocks language attribute: mog.

Chains

These attributes enable much functionality, but we often want more than one value. This is why you must use a closing colon:

##upcase:red: My Heading