&ND Core v1 — Normative Specification (Draft)
1. Core Philosophy
&ND describes document structure. It does not interpret meaning. It does not execute code. It fails on ambiguity.
1.1 Locked Vocabulary
The following terms are used normatively throughout this specification and SHOULD be interpreted consistently by implementations and companion documents.
strong: an inline structural node that marks stronger importance or prominence. It does not prescribe bold rendering.emphasis: an inline structural node that marks emphasis. It does not prescribe italic rendering.inline code: an inline raw-like node whose payload suppresses nested inline-node parsing, while still honoring delimiter-safe escapes required by the grammar.raw island: a region whose interior is not recursively parsed as ordinary block or inline structure. In Core v1, code blocks and extension blocks are raw islands.canonical: the single normalized representation required for semantically equivalent documents under the canonical rules.strict: the default normative parse mode. Unknown syntax and malformed Core v1 syntax cause failure.recovery: a non-normative editor-oriented parse mode for partial document recovery from invalid current-version input.forward_compat: a non-normative opt-in parse mode for compatibility with unknown future syntax. It is non-conformant and MUST NOT be used where strict semantic agreement is required.
Where editor tooling or theme systems use presentation-oriented terms such as bold or italic,
those are implementation-layer mappings only and do not change the language semantics above.
2. Design Principles
2.1 Single Representation
Each feature has exactly one syntax.
2.2 Deterministic Parsing
Same input → same output.
2.3 Fail-Closed
Invalid input MUST produce an error and no document.
2.4 Structural Integrity
Proper nesting required
No overlapping inline spans
2.5 Non-Executable
No HTML, no scripts, no runtime behavior.
2.6 Opaque Extensions
Unknown extension block names MUST be preserved as opaque blocks but not interpreted.
This rule applies to extension blocks only. Unknown inline tags and invalid core syntax MUST error.
3. Parsing Modes
3.1 Strict Mode (Normative)
{
"ok": false,
"errors": [...]
}
No AST produced
No partial output
3.2 Recovery Mode (Non-Normative)
{
"ok": false,
"partial": true,
"document": {...},
"errors": [...]
}
For editors only
MUST NOT be used in pipelines
3.3 Forward-Compatibility Mode (Non-Normative)
forward_compat is an explicitly opt-in compatibility mode for shared-version environments where a
consumer may encounter future syntax that a current parser does not implement.
Example result shape:
{
"ok": true,
"mode": "forward_compat",
"conformant": false,
"downgradedUnknowns": [...]
}
Rules:
forward_compatMUST NOT be the default modeforward_compatMUST be explicitly selected by the consumerforward_compatMUST NOT be treated as a conformant strict parseforward_compatMUST NOT be used for canonical emission, signing, validation pipelines, or any workflow that requires strict semantic agreementforward_compatMAY downgrade unknown future constructs into inert text or opaque compatibility nodesforward_compatMUST NOT silently repair malformed known Core v1 syntax
The purpose of forward_compat is compatibility with future syntax, not recovery from invalid
current-version documents.
3.4 Embedding and Profile Convention
The &ND v1 header is a document-type declaration for standalone &ND files.
It is not required in every embedding context.
Profile and embedding rules:
A standalone &ND file SHOULD begin with
&ND v1on its first line.An AEON annotation or doc-comment embedding MAY omit the header; the enclosing profile or tool convention declares the content type.
A profile that embeds &ND content MUST document whether the header is required, optional, or forbidden for that context.
Parsers receiving &ND input through a typed channel (e.g.,
fmt.and.aeonannotation payload) MUST NOT require the header.
fmt.and.aeon comment payloads are therefore headerless &ND by convention.
The &ND v1 header is a file-level signal, not a per-comment payload requirement.
4. Block Model
4.1 Paragraph Boundary Rule (Critical)
Blocks MUST NOT interrupt paragraphs.
My number is
1. not a list
This is a paragraph.
Blocks only start:
at document start
after a blank line
A line that would otherwise look like a block opener is paragraph text when it appears inside an already-open paragraph.
A line that is not block-open eligible MUST NOT be reinterpreted as a block opener. If such a line cannot be consumed as valid paragraph continuation text under the Core v1 paragraph rules, parsing MUST fail rather than silently reclassifying it.
That means block recognition is context-gated:
block-open eligible at document start or immediately after a blank line
not block-open eligible on continuation lines of an open paragraph
The block-open column for a block-open-eligible position is the current block margin:
at top level, the current block margin is column 0
inside a nested block context, the current block margin is the structural indentation column of that nested context
4.2 Block Types
Headings
# Heading
## Heading
...
###### Heading
Rules:
MUST start at the current block margin
MUST have space after
#Single line only
Paragraphs
One or more lines
Separated by blank line
Inline parsing allowed
A paragraph continuation line may begin with characters that would open a block in a block-open-eligible position; those characters are treated as paragraph text instead
Lists
Unordered
- Item
Ordered
1. Item
2. Item
Rules:
MUST have space after marker
MUST be contiguous
One or more consecutive non-blank lines immediately after a list item head that begin at the exact two-space item content margin continue the item head paragraph
An unindented line immediately after a list item head ends the list and is parsed by the enclosing block context
Nested blocks MUST be indented by exactly two spaces
Tabs are invalid
Nested lists require a blank line before them
Ordered lists MAY start at any non-negative decimal integer, but each following item in the same source list MUST increase by exactly one
A nested block intended for a list item's inner block context MUST begin exactly at that inner context's current block margin
Additional structural indentation beyond that exact nested block margin is invalid in Core v1
Blockquotes
> Quote
> Continued
Rules:
>MUST be followed by space or end-of-lineBlank
>line separates paragraphsA blockquote establishes an inner block context
Within that inner context, the current block margin is the column immediately after the
>marker and optional following space on each quote lineA nested block inside a blockquote is valid only when the enclosing quoted position is block-open eligible
A blank
>line preserves the blockquote context while separating quoted paragraphsA nested block intended for a blockquote's inner context MUST begin exactly at that inner current block margin
Additional structural indentation beyond that exact nested block margin is invalid in Core v1
Code Blocks
```lang
raw content
```
Rules:
Open/close must be at the current block margin
Content is opaque
No parsing inside
MUST be closed
Tables
| A | B |
| --- | --- |
| 1 | 2 |
Rules:
MUST start at the current block margin after a blank line
Header row REQUIRED
Separator row REQUIRED
Separator cells MUST be exactly
---All rows MUST have same column count
Once a table is recognized, every subsequent body row MUST conform to the recognized table shape
A non-conforming body row MUST cause parse failure rather than fallback to paragraph text
Table continuation is container-local: rows from a different enclosing block context or block margin MUST NOT continue a recognized table
Inline parsing allowed inside cells
Block content forbidden inside cells
Pipes MUST be escaped:
\|
Horizontal Rule
---
Rules:
MUST be standalone line
Unambiguous with table separator rows: a
TableSepRowalways starts with|, so a line beginning with---(no leading|) is always aHorizontalRule
Extension Blocks
+++type[/subtype]
raw content
+++
Extension blocks MAY be followed by an adjacent fallback block:
+++unsupported/extension
opaque extension payload
+++
+++fallback
fallback &ND content
+++
Rules:
MUST start at the current block margin
MUST be closed
MUST NOT be nested (v1)
Extension content between opener and closer is opaque
No parsing inside extension content
fallbackis a reserved extension nameA
+++fallbackblock is valid only when it immediately follows an extension block with no blank line or intervening blockOnly one fallback block may apply to an extension block
Fallback block content is parsed as ordinary
&NDcontentA fallback block cannot have its own fallback
A
+++fallbackblock found anywhere else MUST fail withorphan_fallback_blockExtension names MUST match the lowercase grammar exactly in strict mode; uppercase characters MUST cause parse failure
Extension opener recognition is local to the current block margin and validated extension-name grammar; a line that does not satisfy that opener grammar is parsed by the ordinary block and paragraph rules instead
Extension name grammar:
[a-z][a-z0-9-]*(/[a-z][a-z0-9-]*)*(\.v[0-9]+)?
5. Inline Model
The inline syntax is intentionally explicit and context-stable.
[* ...] is always strong and [$ ...] is always inline code.
The same character sequence MUST NOT change meaning based on surrounding prose or parser mode.
5.1 Syntax
[<type> <content>]
5.2 Supported Types
[* ...] strong
[/ ...] emphasis
[@ URL | text] link
[$ ...] inline code
5.3 Rules
Nesting
[* strong [/ nested]] valid
[* strong [/ broken] invalid
Must form a proper tree
Overlapping spans are invalid
Inline Code
[$ render()]
Rules:
Content is delimiter-opaque rather than tree-opaque
No nested inline-node parsing occurs inside inline code
Inside inline code, only
\],\\,\[, and\|are recognized escapesEscape resolution inside inline code MUST occur before closing-delimiter detection
Any unescaped
]terminates the inline code constructMUST be closed
Links
[@ https://x | label]
Rules:
URL required
Label required
|is delimiter\|escapes pipeThe label MUST contain at least one inline node or text character
Unknown Inline Types
[x something]
MUST error.
5.4 Symbol Taxonomy
Block symbols
# heading
> blockquote
- unordered list item
1. ordered list item
--- horizontal rule
``` code block
```` ordered code block
+++ extension block
Inline symbols
[* ...] strong
[/ ...] emphasis
[$ ...] inline code
[@ url | text] link
Reserved — do not use in Core v1
[# ...] reserved for anchors/IDs if ever needed
[~ ...] reserved for references/mentions if ever needed
[! ...] reserved for warnings/admonitions if ever needed
[? ...] reserved for hints/questions if ever needed
[+ ...] reserved for consumer defined tags if ever needed
[- ...] reserved for strikethrough if ever needed
[" ...] reserved for inline quoted text if ever needed
[' ...] reserved for comments if ever needed
[: ...] reserved for typed values like datetime if ever needed
[= ...] reserved for highlighting text if ever needed
[_ ...] reserved for underline text if ever needed
[ ] reserved for todo/unchecked if ever needed
[x] reserved for todo/checked if ever needed
[,] reserved for todo/in progress if ever needed
[;] reserved for todo/cancelled if ever needed
[.] reserved for inline line break if ever needed
[>] reserved for forward arrow if ever needed
[<] reserved for backward arrow if ever needed
[%] reserved for auto numbered list items if ever needed
[n] reserved for auto-number marker in headers if ever needed
=== reserved for header text if ever needed
~~~ reserved for alternative formating if ever needed
*** reserved for disclaimer text
In strict Core v1, a reserved block opener at a block-open-eligible position MUST fail with
unknown_block_type. It MUST NOT be accepted as paragraph text. A reserved-looking line on a
paragraph continuation remains paragraph text because block recognition is not eligible there.
Design rule
A symbol should mean one thing across &ND.
# means heading at block level. [# ...] therefore MUST NOT be repurposed for an unrelated
inline meaning. Reserved symbols remain unassigned so that any future assignment can stay aligned
with their natural meaning.
Lock
Core v1 recognizes exactly four inline symbols:
[* strong]
[/ emphasis]
[$ inline code]
[@ url | label]
Everything else is reserved until there is a strong need and a consistent assignment.
6. Escaping
Escape character:
\
Supported escapes:
\[ [
\] ]
\| |
\\ \
Rules:
Escape applies to next character only
Escaping invalid characters MUST error
Escape resolution is lexical and local: it happens during inline scanning, before any escaped character can be interpreted as syntax
There is no later global "escape resolution" rewrite pass
7. Scanner And Parser Model
The normative parser model is:
Line normalization Normalize line endings to logical lines for parsing.
Top-level block scan Scan lines left-to-right and recognize block openers only when the parser is block-open eligible.
Raw islands first Code blocks and extension blocks are detected before inline parsing and consumed as opaque raw regions.
Inline scan Inline parsing is a local left-to-right scan inside inline-capable regions. Escapes are resolved lexically during that scan.
Normative constraints:
Implementations MUST NOT rely on unbounded backtracking.
Implementations MUST use bounded lookahead only.
Block recognition MUST depend only on local parser state plus the current line, except for table recognition which may inspect the immediately following separator line.
Inline parsing MUST be local to the current inline-capable region and MUST NOT depend on global document state.
This model is intentionally stricter than forgiving markup parsers. The goal is deterministic, auditable parsing rather than permissive recovery.
7.1 Normative Scanner Rules
The following scanner rules are normative.
7.1.1 Line classes
Before block parsing, an implementation MUST classify each physical line using only:
the normalized line text
whether the parser is block-open eligible
bounded local lookahead where explicitly allowed
There is no global reinterpretation pass.
7.1.2 Block-open eligible positions
A line is block-open eligible only:
at document start
immediately after a blank line
inside a list item or blockquote only where that enclosing block explicitly permits a nested block
A paragraph continuation line is never block-open eligible.
7.1.3 Raw-island scanning
When a code block opener or extension block opener is recognized:
the opener line is consumed immediately
subsequent lines are treated as opaque raw payload
parsing resumes only at the first valid closing fence at the same block margin as the opener
if no closer exists before EOF, parsing MUST fail
if a closing fence token appears at a different block margin, parsing MUST fail with the corresponding bad-closing-margin error
No inline parsing, block parsing, or escape reinterpretation occurs inside raw payload.
7.1.4 Table recognition
Table recognition is the only block form that may inspect the immediately following line to confirm the current line's role.
A table starts only if:
the current line is block-open eligible
the line begins at the current block margin
the current line matches
TableRowthe immediately following line matches
TableSepRow
Otherwise the current line is parsed according to the normal block/paragraph rules.
Once a table has been recognized, subsequent candidate body rows are part of that table context. A row that does not conform to the established table shape MUST fail the parse; implementations MUST NOT terminate the table early and reinterpret that row as ordinary paragraph text.
7.1.5 Inline scanning
Inline-capable regions are scanned left-to-right.
At each position:
if the next bytes form a valid escape, emit the escaped literal character
else if the next bytes form a valid inline opener, parse that inline form
else emit plain text until the next candidate escape or inline opener
Unknown inline openers MUST fail immediately.
Inside inline code, escape recognition occurs before closing-delimiter detection, and any unescaped
] closes the inline code region.
7.1.6 Failure points
Parsing MUST fail immediately on:
invalid escape
unknown inline tag
unclosed inline construct
unclosed raw block
invalid indentation where indentation is structural
table-shape mismatch
budget exhaustion
Strict mode produces no partial document after any such failure. Escaping a non-escapable character is a syntax error, not a literal fallback.
7.1.7 Worked edge cases
Case A: paragraph vs ordered list
My number is
1. not a list
Result:
one paragraph with two lines
the second line is paragraph text because the parser is not block-open eligible there
Case B: ordered list after blank line
My number is
1. a list item
Result:
one paragraph
one ordered list
Case C: horizontal rule vs paragraph text
Text
---
Result:
invalid as a single paragraph continuation
if
---is intended as a horizontal rule, a blank line is required before it
Canonical valid form:
Text
---
Case D: table requires separator confirmation
| A | B |
plain text
Result:
not a table
if the parser is block-open eligible there, the two lines form one paragraph
if the enclosing context does not allow paragraph text at that position, parsing fails according to that enclosing context
Case E: escaped inline opener
\[* not strong]
Result:
literal text
[* not strong]not a strong node
Case F: unknown inline tag
[x something]
Result:
fail with
unknown_inline_type
Case G: raw block swallowing apparent syntax
```txt
[* not strong]
+++chart/pie
---
```
Result:
one code block
all interior lines are opaque payload
Case H: unterminated extension block
+++chart/pie
apples: 3
Result:
fail with
unclosed_extension_block
Case I: oversized link target
[@ https://example.invalid/...very-long... | label]
Result:
if the configured link-target budget is exceeded, fail with
nd_budget_exceededimplementations MUST stop scanning once the budget boundary is crossed
7.1.8 Conformance Test Seeds
The following seeds are normative conformance fixtures.
An implementation MAY add more tests, but it SHOULD include at least these cases in its conformance suite.
seed-paragraph-vs-ordered-list
Input:
My number is
1. not a list
Expected result:
parse success
one paragraph block
paragraph contains two lines of text
seed-reserved-block-marker-on-paragraph-continuation
Input:
Lead text
~~~=
Expected result:
parse success
one paragraph block
~~~=remains ordinary text because the line is not at a block-open-eligible position
seed-header-standalone
Input:
&ND v1
# Title
Expected result:
parse success
header is recognized as a document declaration
header does not appear as an AST block
one heading block
seed-invalid-header-version
Input:
&ND v2
# Title
Expected result:
parse failure
reason: unsupported or malformed document header
seed-ordered-list-after-blank-line
Input:
My number is
1. a list item
Expected result:
parse success
one paragraph block
one ordered list block with one item
seed-ordered-list-renumber-canonical
Input:
3. Alpha
4. Beta
Expected result:
parse success
one ordered list block
source numbers are a valid monotonic sequence
canonical emission renumbers ordered list items from
1
seed-ordered-list-skipped-number
Input:
1. Alpha
3. Beta
Expected result:
parse failure
reason: ordered list item numbers must increase by exactly one within the source list
seed-horizontal-rule-needs-boundary
Input:
Text
---
Expected result:
parse failure
reason: block opener attempted on a paragraph continuation line
seed-inline-line-break
Input:
First[.]Second
Expected result:
parse failure
error code
unknown_inline_type[.]remains reserved in Core v1
seed-table-requires-separator
Input:
| A | B |
plain text
Expected result:
parse success when evaluated at top level
one paragraph block with two lines
no table block
seed-table-valid-top-level
Input:
| A | B |
| --- | --- |
| 1 | 2 |
Expected result:
parse success
one table block
table has two columns and one body row
seed-table-inline-cells
Input:
| Name | Note |
| --- | --- |
| [* Ada] | escaped \| pipe |
Expected result:
parse success
one table block
first body cell contains one strong inline node
second body cell contains a literal pipe character
seed-table-mismatched-body-row
Input:
| A | B |
| --- | --- |
| 1 | 2 | 3 |
Expected result:
parse failure
reason: table body row has a different cell count from the header row
seed-table-missing-body-row
Input:
| A | B |
| --- | --- |
Expected result:
parse failure
reason: table has no body rows
seed-table-unescaped-pipe-extra-cell
Input:
| A | B |
| --- | --- |
| escaped | pipe | ok |
Expected result:
parse failure
reason: unescaped pipe creates an extra cell and violates table shape
seed-table-heading-instead-of-body-row
Input:
| A | B |
| --- | --- |
# Heading
Expected result:
parse failure
error code
invalid_table_shapereason: once a table is recognized, a following heading line cannot terminate the table early
seed-escaped-inline-opener
Input:
\[* not strong]
Expected result:
parse success
plain text only
no strong inline node
seed-unknown-inline-tag
Input:
[x something]
Expected result:
parse failure
error code
unknown_inline_type
seed-raw-block-opaque
Input:
```txt
[* not strong]
+++chart/pie
---
```
Expected result:
parse success
one code block
payload contains the interior lines exactly, subject only to line-ending normalization
seed-ordered-code-block
Input:
````aeon
title = "Hello World"
mode = "ordered"
````
Expected result:
parse success
one code block
code block is ordered
payload contains the interior lines exactly, subject only to line-ending normalization
seed-ordered-code-block-wrong-closing-fence
Input:
````aeon
title = "Hello World"
```
Expected result:
parse failure
error code
unclosed_code_blockreason: ordered code blocks require a matching quadruple-fence closer
seed-blockquote-ordered-code-block-wrong-closing-fence
Input:
> Quote intro
>
> ````aeon
> title = "Hello World"
> ```
Expected result:
parse failure
error code
unclosed_code_blockreason: a quoted ordered code block still requires a matching quadruple-fence closer
seed-list-item-ordered-code-block-wrong-closing-fence
Input:
- Parent
````aeon
title = "Hello World"
```
Expected result:
parse failure
error code
unclosed_code_blockreason: a list-item-contained ordered code block still requires a matching quadruple-fence closer
seed-list-item-blockquote-ordered-code-block-wrong-closing-fence
Input:
- Parent
> ````aeon
> title = "Hello World"
> ```
Expected result:
parse failure
error code
unclosed_code_blockreason: a doubly nested ordered code block still requires a matching quadruple-fence closer
seed-list-item-unclosed-extension-block
Input:
- Parent
+++chart/pie
apples: 1
Expected result:
parse failure
error code
unclosed_extension_blockreason: a list-item-contained extension block still requires a matching closer at the same nested margin
seed-blockquote-unclosed-extension-block
Input:
> Quote
>
> +++chart/pie
> apples: 1
Expected result:
parse failure
error code
unclosed_extension_blockreason: a blockquote-contained extension block still requires a matching closer at the same nested margin
seed-list-item-blockquote-unclosed-extension-block
Input:
- Parent
> +++chart/pie
> apples: 1
Expected result:
parse failure
error code
unclosed_extension_blockreason: a doubly nested extension block still requires a matching closer at the innermost container margin
seed-extension-block-opaque
Input:
+++chart/pie
[* not strong]
| not | table |
+++
Expected result:
parse success
one extension block
extension name is
chart/piepayload is opaque and not parsed as inline or block content
seed-extension-block-fallback
Input:
+++unsupported/extension
opaque [* extension] payload
+++
+++fallback
Show [* this] instead
+++
Expected result:
parse success
one extension block
extension opener attaches the extension name directly to the
+++fenceprimary extension payload is opaque
immediately adjacent
+++fallbackcontent is parsed as ordinary&NDblocks
seed-orphan-fallback-block
Input:
+++chart/pie
apples: 30
+++
+++fallback
Chart unavailable.
+++
Expected result:
parse failure
error code
orphan_fallback_blockreason: fallback is only valid when directly adjacent to the immediately preceding extension block
seed-fallback-not-immediately-after-extension
Input:
+++chart/pie
apples: 30
+++
Paragraph.
+++fallback
Chart unavailable.
+++
Expected result:
parse failure
error code
orphan_fallback_blockreason: any intervening block makes the fallback orphaned
seed-fallback-crosses-blockquote-boundary
Input:
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
+++fallback
Outside fallback
+++
Expected result:
parse failure
error code
orphan_fallback_blockreason: fallback cannot attach across a blockquote container boundary
seed-fallback-crosses-into-blockquote-boundary
Input:
+++unsupported/extension
opaque payload
+++
> +++fallback
> Quoted fallback
> +++
Expected result:
parse failure
error code
orphan_fallback_blockreason: quoted fallback cannot attach to an extension block outside the quoted container
seed-fallback-crosses-list-item-boundary
Input:
- Parent
+++unsupported/extension
opaque payload
+++
+++fallback
Outside fallback
+++
Expected result:
parse failure
error code
orphan_fallback_blockreason: fallback cannot attach across a list-item container boundary
seed-fallback-crosses-into-list-item-boundary
Input:
+++unsupported/extension
opaque payload
+++
- Parent
+++fallback
Nested fallback
+++
Expected result:
parse failure
error code
orphan_fallback_blockreason: nested list-item fallback cannot attach to an extension block outside the list item
seed-list-item-blockquote-fallback-not-immediately-after-extension
Input:
- Parent
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
> Intervening paragraph
>
> +++fallback
> Show fallback
> +++
Expected result:
parse failure
error code
orphan_fallback_blockreason: fallback adjacency is local to the immediately preceding extension block even inside nested blockquote/list-item containers
seed-blockquote-fallback-not-immediately-after-extension
Input:
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
> Intervening paragraph
>
> +++fallback
> Show fallback
> +++
Expected result:
parse failure
error code
orphan_fallback_blockreason: fallback adjacency remains local to the immediately preceding extension block inside the same blockquote container
seed-nested-fallback-block
Input:
+++chart/pie
apples: 30
+++
+++fallback
+++media/image
src: ./chart.png
+++
+++fallback
Image fallback.
+++
+++
Expected result:
parse failure
error code
nested_fallback_blockreason: fallback content is parsed as ordinary
&ND, but extension blocks parsed inside fallback content cannot themselves receive fallback blocks
seed-unclosed-extension-block
Input:
+++chart/pie
apples: 3
Expected result:
parse failure
error code
unclosed_extension_block
seed-extension-uppercase-name
Input:
+++Chart/pie
payload
+++
Expected result:
parse failure
error code
invalid_extension_namereason: extension names are lowercase in strict mode
seed-invalid-escape
Input:
\q
Expected result:
parse failure
error code
invalid_escape
seed-budget-block-count-at-limit
Input:
One
Two
Expected result:
parse success when whole-document block-count equals the configured budget
budget limit is inclusive
seed-budget-block-count
Input:
One
Two
Expected result:
parse failure if configured whole-document block-count budget is exceeded
error code
nd_budget_exceededbudget failure occurs as blocks are created
seed-budget-code-block-size-at-limit
Input:
```
12345678
```
Expected result:
parse success when raw code block payload size equals the configured budget
budget limit is inclusive
seed-budget-code-block-size
Input:
```
123456789
```
Expected result:
parse failure if configured raw block payload budget is exceeded
error code
nd_budget_exceededbudget failure occurs while scanning code block payload
seed-budget-document-size-at-limit
Input:
Hello
Expected result:
parse success when normalized document size equals the configured budget
budget limit is inclusive
seed-budget-document-size
Input:
This document is too long.
Expected result:
parse failure if configured document-size budget is exceeded
error code
nd_budget_exceededscanner rejects before unrestricted block parsing
seed-budget-extension-block-size-at-limit
Input:
+++diagram/opaque
12345678
+++
Expected result:
parse success when extension block payload size equals the configured budget
budget limit is inclusive
seed-budget-extension-block-size
Input:
+++diagram/opaque
123456789
+++
Expected result:
parse failure if configured extension block payload budget is exceeded
error code
nd_budget_exceededbudget failure occurs while scanning opaque extension payload
seed-budget-inline-depth-at-limit
Input:
[* outer [* inner]]
Expected result:
parse success when inline nesting depth equals the configured budget
budget limit is inclusive
seed-budget-inline-depth
Input:
[* outer [* inner]]
Expected result:
parse failure if configured inline nesting-depth budget is exceeded
error code
nd_budget_exceededbudget failure occurs when entering nested inline content
seed-budget-line-length-at-limit
Input:
Hello
Expected result:
parse success when physical line length equals the configured budget
budget limit is inclusive
seed-budget-line-length
Input:
This line is too long.
Expected result:
parse failure if configured physical-line-length budget is exceeded
error code
nd_budget_exceededscanner rejects during line validation
seed-budget-link-target-at-limit
Input:
[@ abc| label]
Expected result:
parse success when link target length equals the configured budget
budget limit is inclusive
seed-budget-link-target
Input:
[@ https://example.invalid/...very-long... | label]
Expected result:
parse failure if configured link-target budget is exceeded
error code
nd_budget_exceededscanner stops at the budget boundary rather than consuming unbounded input
seed-budget-list-item-count-at-limit
Input:
- One
- Two
Expected result:
parse success when whole-document list-item count equals the configured budget
budget limit is inclusive
seed-budget-list-item-count
Input:
- One
- Two
- Three
Expected result:
parse failure if configured list-item budget is exceeded
error code
nd_budget_exceededbudget failure occurs as list items are recognized
seed-budget-list-item-count-whole-document
Input:
- One
- Two
- Three
Expected result:
parse failure if configured list-item budget is exceeded across separate lists
error code
nd_budget_exceededlist-item budget is a whole-document budget
seed-budget-nesting-depth-at-limit
Input:
> Quote
Expected result:
parse success when block nesting depth equals the configured budget
budget limit is inclusive
seed-budget-nesting-depth
Input:
> Quote
Expected result:
parse failure if configured block nesting-depth budget is exceeded
error code
nd_budget_exceededbudget failure occurs when entering a nested block context
seed-budget-table-columns-at-limit
Input:
| A | B |
| --- | --- |
| 1 | 2 |
Expected result:
parse success when table column count equals the configured budget
budget limit is inclusive
seed-budget-table-columns
Input:
| A | B | C |
| --- | --- | --- |
| 1 | 2 | 3 |
Expected result:
parse failure if configured table-column budget is exceeded
error code
nd_budget_exceededbudget failure occurs before unrestricted table cell parsing
seed-nested-list-two-space-indent
Input:
- Parent
- Child
- Child
- Next parent
Expected result:
parse success
one unordered list block
first parent item contains a nested unordered list with two child items
second parent item is a sibling of the first parent item
seed-nested-list-deep
Input:
- One
- Two
- Three
- Four
Expected result:
parse success
one unordered list block
nested lists recurse through all four levels
each nested level still depends on a blank line plus the exact two-space content margin
seed-list-item-paragraph-continuation-exact-margin
Input:
- Parse strict documents
world
Expected result:
parse success
one unordered list block
one list item
item contains one paragraph with two source lines
continuation line starts at the exact two-space list-item content margin
seed-list-item-unindented-line-ends-list
Input:
- Parse strict documents
world
Expected result:
parse success
one unordered list block
one following top-level paragraph
unindented text immediately after a list item ends the list
seed-list-item-nested-paragraph-exact-margin
Input:
1. Hello
2. Happy
World
Expected result:
parse success
one ordered list block
second item contains a nested paragraph
nested paragraph starts at the exact two-space list-item inner margin
seed-list-item-unindented-paragraph-after-blank
Input:
1. Hello
2. Happy
World
Expected result:
parse success
one ordered list block
one following top-level paragraph
unindented text after a blank line is not captured into the previous list item
seed-nested-list-invalid-indent-one-space
Input:
- Parent
- Child
Expected result:
parse failure
reason: structural indentation is exactly two spaces in Core v1
seed-nested-list-invalid-indent-tab
Input:
- Parent
\t- Child
Expected result:
parse failure
reason: tabs are invalid where indentation is structural
seed-nested-list-needs-blank-line
Input:
- Parent
- Child
Expected result:
parse failure
reason: nested blocks require a blank line before the nested block in Core v1
seed-blockquote-two-paragraphs
Input:
> First paragraph
>
> Second paragraph
Expected result:
parse success
one blockquote block
blockquote contains two paragraph children
seed-blockquote-extension-fallback
Input:
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
> +++fallback
> Show [* this] instead
> +++
Expected result:
parse success
one blockquote block
blockquote contains one paragraph child followed by one nested extension block child
fallback belongs to that immediately preceding extension block within the quoted container
seed-blockquote-escaped-inline
Input:
> \[* not strong]
Expected result:
parse success
one blockquote block
quoted paragraph contains plain text
[* not strong]no strong inline node
seed-list-item-raw-block
Input:
- Parent
```txt
raw
```
Expected result:
parse success
one unordered list block
first item contains one nested code block
code payload is
raw
seed-list-item-extension-block
Input:
- Parent
+++chart/pie
apples: 3
+++
Expected result:
parse success
one unordered list block
first item contains one nested extension block
extension payload is
apples: 3
seed-list-item-heading
Input:
- Parent
## Child heading
Expected result:
parse success
one unordered list block
first item contains one nested heading block
heading level is
2
seed-list-item-horizontal-rule
Input:
- Parent
---
Expected result:
parse success
one unordered list block
first item contains one nested horizontal rule block
seed-list-item-table
Input:
- Parent
| A | B |
| --- | --- |
| 1 | 2 |
Expected result:
parse success
one unordered list block
first item contains one nested table block
table has two columns and one body row
seed-list-item-table-missing-separator
Input:
- Parent
| A | B |
plain text
Expected result:
parse success
one unordered list block
first item contains one paragraph block with two lines
no nested table block
seed-list-item-table-mismatched-body-row
Input:
- Parent
| A | B |
| --- | --- |
| 1 | 2 | 3 |
Expected result:
parse failure
error code
invalid_table_shapereason: nested table body row has a different cell count from the header row and MUST fail rather than falling back to paragraph text
seed-list-item-table-crosses-boundary
Input:
- Parent
| A | B |
| --- | --- |
| 1 | 2 |
Expected result:
parse failure
error code
invalid_table_shapereason: a nested table inside a list item cannot continue with a body row outside the list-item container
seed-list-item-table-horizontal-rule-instead-of-body-row
Input:
- Parent
| A | B |
| --- | --- |
---
Expected result:
parse failure
error code
invalid_table_shapereason: a nested table inside a list item cannot terminate early and reinterpret a following horizontal rule as a sibling nested block
seed-list-item-raw-block-bad-closing-margin
Input:
- Parent
```txt
raw
```
Expected result:
parse failure
reason: raw block closer must appear at the same block margin as the opener
seed-list-item-extension-block-bad-closing-margin
Input:
- Parent
+++chart/pie
apples: 3
+++
Expected result:
parse failure
error code
extension_block_bad_closing_marginreason: extension block closer must appear at the same block margin as the opener
seed-list-item-blockquote-raw-block-bad-closing-margin
Input:
- Parent
> ```txt
> raw
```
Expected result:
parse failure
error code
raw_block_bad_closing_marginreason: a raw block closer inside a list-item-contained blockquote must appear at the same inner block margin as the opener
seed-list-item-blockquote-extension-block-bad-closing-margin
Input:
- Parent
> +++chart/pie
> apples: 1
+++
Expected result:
parse failure
error code
extension_block_bad_closing_marginreason: an extension block closer inside a list-item-contained blockquote must appear at the same inner block margin as the opener
seed-list-item-blockquote
Input:
- Parent
> Quote line
Expected result:
parse success
one unordered list block
first item contains one nested blockquote block
blockquote contains one paragraph child
seed-list-item-blockquote-two-paragraphs
Input:
- Parent
> First paragraph
>
> Second paragraph
Expected result:
parse success
one unordered list block
first item contains one nested blockquote block
blockquote contains two paragraph children
seed-list-item-blockquote-extension-fallback
Input:
- Parent
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
> +++fallback
> Show [* this] instead
> +++
Expected result:
parse success
one unordered list block
first item contains one nested blockquote block
blockquote contains one paragraph child followed by one nested extension block child
nested extension fallback content is parsed as ordinary
&NDinside the same blockquote context
seed-blockquote-nested-list
Input:
> Quote intro
>
> - Child item
Expected result:
parse success
one blockquote block
blockquote contains one paragraph child followed by one nested unordered list child
seed-blockquote-nested-list-needs-margin
Input:
> Quote intro
>
- Child item
Expected result:
parse success
one blockquote block containing one paragraph child
one top-level unordered list block
the list is not nested in the blockquote because it does not begin at the blockquote's current block margin
seed-blockquote-table-mismatched-body-row
Input:
> Quote intro
>
> | A | B |
> | --- | --- |
> | 1 | 2 | 3 |
Expected result:
parse failure
error code
invalid_table_shapereason: a nested table inside a blockquote uses the same recognized table shape rules and MUST fail rather than falling back to quoted paragraph text
seed-blockquote-table-crosses-boundary
Input:
> | A | B |
> | --- | --- |
| 1 | 2 |
Expected result:
parse failure
error code
invalid_table_shapereason: a nested table inside a blockquote cannot continue with a body row outside the quoted container
seed-blockquote-table-horizontal-rule-instead-of-body-row
Input:
> Quote intro
>
> | A | B |
> | --- | --- |
> ---
Expected result:
parse failure
error code
invalid_table_shapereason: a nested table inside a blockquote cannot terminate early and reinterpret a following quoted horizontal rule as a sibling block
seed-blockquote-nested-code-block
Input:
> Quote intro
>
> ```txt
> raw
> ```
Expected result:
parse success
one blockquote block
blockquote contains one paragraph child followed by one nested code block child
code payload is
raw
seed-blockquote-nested-extension-block
Input:
> Quote intro
>
> +++chart/pie
> apples: 3
> +++
Expected result:
parse success
one blockquote block
blockquote contains one paragraph child followed by one nested extension block child
extension payload is
apples: 3
seed-blockquote-raw-block-bad-closing-margin
Input:
> Quote intro
>
> ```txt
> raw
```
Expected result:
parse failure
reason: raw block closer must appear at the same block margin as the opener inside the enclosing blockquote
seed-blockquote-extension-block-bad-closing-margin
Input:
> Quote intro
>
> +++chart/pie
> apples: 3
+++
Expected result:
parse failure
error code
extension_block_bad_closing_marginreason: extension block closer must appear at the same block margin as the opener inside the enclosing blockquote
seed-list-item-blockquote-then-sibling
Input:
- Parent
> Quote line
- Next parent
Expected result:
parse success
one unordered list block
first item contains one nested blockquote child
second parent item is a sibling at the outer list level
seed-list-item-blockquote-table-crosses-boundary
Input:
- Parent
> | A | B |
> | --- | --- |
| 1 | 2 |
Expected result:
parse failure
error code
invalid_table_shapereason: a doubly nested table inside a list-item-contained blockquote cannot continue at the outer list-item margin
seed-list-item-blockquote-table-horizontal-rule-instead-of-body-row
Input:
- Parent
> | A | B |
> | --- | --- |
> ---
Expected result:
parse failure
error code
invalid_table_shapereason: a doubly nested table inside a list-item-contained blockquote cannot terminate early and reinterpret a quoted horizontal rule as a sibling block
seed-blockquote-escaped-inline-opener
Input:
> \[@ https://example.com | label]
Expected result:
parse success
one blockquote block
quoted paragraph contains plain text
[@ https://example.com | label]no link inline node
seed-inline-nested-strong-emphasis
Input:
[* strong [/ emphasis]]
Expected result:
parse success
one paragraph block
paragraph contains one strong node
the strong node contains text plus one nested emphasis node
seed-inline-unclosed-strong
Input:
[* strong
Expected result:
parse failure
error code
unclosed_inline
seed-inline-strong-newline
Input:
[* strong
continued]
Expected result:
parse failure
error code
unclosed_inlinereason: inline strong node crosses a line boundary before closing
seed-inline-unclosed-nested
Input:
[* strong [/ emphasis]
Expected result:
parse failure
error code
unclosed_inline
seed-inline-unexpected-closing
Input:
text ]
Expected result:
parse failure
error code
unexpected_closing
seed-inline-overlap-attempt
Input:
[* strong [/ emphasis] text]
Expected result:
parse success
one strong node containing one nested emphasis node and trailing text
nesting is strictly tree-structured, not overlapping
seed-inline-code-opaque-brackets
Input:
[$ \[* not strong\] \[@ x \| y\]]
Expected result:
parse success
one inline code node
code payload is
[* not strong] [@ x | y]no nested strong or link nodes inside code
seed-inline-link-label-nesting
Input:
[@ https://example.com | [* label]]
Expected result:
parse success
one link node with the given target
link label contains one nested strong node
seed-inline-link-missing-label
Input:
[@ https://example.com | ]
Expected result:
parse failure
reason: link label is required in Core v1
seed-inline-link-missing-target
Input:
[@ | label]
Expected result:
parse failure
reason: link target is required in Core v1
seed-inline-escaped-pipe-in-link-target
Input:
[@ https://example.com/a\|b | label]
Expected result:
parse success
one link node
target contains the literal pipe character
|
seed-inline-nbsp
Input:
A[_]B
Expected result:
parse failure
error code
unknown_inline_type[_]is not assigned in Core v1
seed-inline-reserved-anchor-tag
Input:
[# section]
Expected result:
parse failure
error code
unknown_inline_type[# ...]remains reserved in Core v1
seed-inline-reserved-reference-tag
Input:
[~ mention]
Expected result:
parse failure
error code
unknown_inline_type[~ ...]remains reserved in Core v1
seed-inline-reserved-admonition-tag
Input:
[! warning]
Expected result:
parse failure
error code
unknown_inline_type[! ...]remains reserved in Core v1
seed-inline-reserved-footnote-tag
Input:
[^ note]
Expected result:
parse failure
error code
unknown_inline_type[^ ...]remains reserved in Core v1
seed-inline-reserved-todo-marker
Input:
[x]
Expected result:
parse failure
error code
unknown_inline_type[x]remains reserved in Core v1
seed-inline-reserved-todo-unchecked-marker
Input:
[ ]
Expected result:
parse failure
error code
unknown_inline_type[ ]remains reserved in Core v1
seed-inline-reserved-todo-in-progress-marker
Input:
[=]
Expected result:
parse failure
error code
unknown_inline_type[=]remains reserved in Core v1
seed-inline-reserved-todo-cancelled-marker
Input:
[;]
Expected result:
parse failure
error code
unknown_inline_type[;]remains reserved in Core v1
seed-block-reserved-highlight-paragraph
Input:
~~~=
Highlighted text
~~~=
Expected result:
parse failure
error code
unknown_block_type~~~=remains reserved in Core v1
seed-block-reserved-header-text
Input:
===header
Header text
===
Expected result:
parse failure
error code
unknown_block_type===remains reserved in Core v1
seed-block-reserved-disclaimer
Input:
***disclaimer
Use with care
***
Expected result:
parse failure
error code
unknown_block_type***remains reserved in Core v1
seed-inline-invalid-escape-in-code
Input:
[$ \q]
Expected result:
parse failure
error code
invalid_escape
seed-source-spans-table-inline
Input:
&ND v1
| Name | Link\|Text |
| --- | --- |
| Alpha | [* Strong] |
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
table block span covers the full table source range
inline spans inside table cells point at trimmed cell content
escaped pipes inside table cells remain part of the cell source span
nested inline spans inside table cells point at their exact source ranges
seed-source-spans-extension-fallback
Input:
&ND v1
+++unsupported/extension
opaque [* extension] payload
+++
+++fallback
Show [* this] instead
+++
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
extension block span covers only the primary opaque extension block, not the attached fallback
fallback paragraph spans point at parsed fallback content after the reserved
+++fallbackopenernested inline spans inside fallback content point at their exact source ranges
seed-source-spans-list-item-blockquote
Input:
&ND v1
- Parent
> First paragraph
>
> Second paragraph
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
list item spans cover the full nested block range belonging to that item
nested blockquote spans include both quoted paragraphs but not the blank line before the nested block
quoted paragraph spans point at the trimmed inner content after the list and blockquote margins
seed-source-spans-list-item-table
Input:
&ND v1
- Parent
| Name | [* Strong] |
| --- | --- |
| Alpha | Link\|Text |
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested table span begins at the trimmed inner block margin inside the list item
inline spans inside nested table cells point at trimmed cell content after both list and table margin stripping
nested inline spans inside list-contained table cells point at their exact source ranges
seed-source-spans-blockquote-table
Input:
&ND v1
> Quote intro
>
> | Name | [* Strong] |
> | --- | --- |
> | Alpha | Link\|Text |
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested table span begins at the trimmed inner block margin inside the blockquote
inline spans inside nested quoted table cells point at trimmed cell content after both blockquote and table margin stripping
nested inline spans inside blockquote-contained table cells point at their exact source ranges
seed-source-spans-list-item-extension-fallback
Input:
&ND v1
- Parent
+++unsupported/extension
opaque payload
+++
+++fallback
Show [* this] instead
+++
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested extension block span covers only the primary opaque block inside the list item, not the attached fallback
fallback paragraph spans begin at the trimmed inner block margin after the list item and reserved fallback opener
nested inline spans inside list-contained fallback content point at their exact source ranges
seed-source-spans-blockquote-extension-fallback
Input:
&ND v1
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
> +++fallback
> Show [* this] instead
> +++
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested extension block span covers only the primary opaque block inside the blockquote, not the attached fallback
fallback paragraph spans begin at the trimmed inner block margin after the blockquote and reserved fallback opener
nested inline spans inside blockquote-contained fallback content point at their exact source ranges
seed-source-spans-list-item-blockquote-table
Input:
&ND v1
- Parent
> Quote intro
>
> | Name | [* Strong] |
> | --- | --- |
> | Alpha | Link\|Text |
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested blockquote span begins at the trimmed inner block margin inside the list item
nested table span begins at the trimmed inner block margin after both list-item and blockquote stripping
inline spans inside doubly nested table cells point at their exact source ranges
seed-source-spans-list-item-blockquote-extension-fallback
Input:
&ND v1
- Parent
> Quote intro
>
> +++unsupported/extension
> opaque payload
> +++
> +++fallback
> Show [* this] instead
> +++
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested extension block span covers only the primary opaque block after both list-item and blockquote stripping
fallback paragraph spans begin at the trimmed inner block margin after the list item, blockquote, and reserved fallback opener
nested inline spans inside doubly nested fallback content point at their exact source ranges
seed-source-spans-list-item-blockquote-paragraph-then-table
Input:
&ND v1
- Parent
> First paragraph
>
> | Name | [* Strong] |
> | --- | --- |
> | Alpha | Link\|Text |
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested blockquote span covers both its leading paragraph and following structured child
the paragraph span ends before the blank quoted separator line while the nested table span begins after it
inline spans inside the following nested table remain exact after both list-item and blockquote margin stripping
seed-source-spans-list-item-blockquote-code-block
Input:
&ND v1
- Parent
> Quote intro
>
> ```txt
> raw
> lines
> ```
Expected result:
parse success
optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
nested blockquote spans begin at the trimmed inner block margin inside the list item
nested code block spans begin at the trimmed inner block margin after both list-item and blockquote stripping
raw block spans cover the full fenced source region rather than only the payload lines
seed-source-spans-ordered-code-block
Input:
&ND v1
````txt
a
b
Expected result:
* parse success * optional source-span CTS metadata checks may be evaluated separately from semantic AST equality * ordered code block spans cover the full quadruple-fence source region * ordered code block spans begin at the opening fence rather than the first payload line
8. Structural Constraints
Parser MUST enforce:
* Proper nesting of inline nodes * Balanced delimiters * No overlapping spans * No unclosed blocks * No mixed block contexts * No hidden reinterpretation of a line from paragraph text into a block opener without a blank-line boundary
9. Output Model
Abstract tree:
{
"type": "document",
"children": [
{ "type": "heading", "level": 1 },
{ "type": "paragraph" },
{ "type": "list", "ordered": false },
{ "type": "code_block" },
{ "type": "horizontal_rule" },
{
"type": "extension_block",
"name": "chart/pie",
"content": "...",
"fallback": {
"type": "document_fragment",
"children": [{ "type": "paragraph" }]
}
}
]
}
Inline:
{ "type": "strong", "children": [...] }
{ "type": "emphasis", "children": [...] }
{ "type": "code", "text": "..." }
{ "type": "link", "href": "...", "children": [...] }
10. Error Model
10.1 Failure Behavior
* Parsing MUST fail-closed * No partial AST in strict mode
10.2 Error Output
{
"ok": false,
"errors": [
{
"code": "unclosed_inline",
"line": 4,
"col": 12
}
]
}
10.3 Error Categories
The categories below describe the stable strict-mode diagnostics currently exercised by the conformance suite. Implementations MAY expose additional internal diagnostics, but they MUST NOT change the meaning of these stable codes.
Structural
* `unclosed_inline` * `unexpected_closing` * `unknown_inline_type` * `missing_link_label` * `missing_blank_line_before_nested_block` * `invalid_ordered_list_sequence`
Block
* `unclosed_code_block` * `unclosed_extension_block` * `raw_block_bad_closing_margin` * `extension_block_bad_closing_margin` * `orphan_fallback_block` * `nested_fallback_block` * `invalid_table_shape` * `invalid_extension_name` * `block_opener_on_paragraph_continuation`
Lexical
* `invalid_escape` * `invalid_indentation` * `invalid_link_target`
Resource Limits
* `nd_budget_exceeded`
11. Resource Limits (Security)
Implementations MUST support limits:
* max document size * max line length * max nesting depth * max inline depth * max table columns * max block size (code/extension) * max block count * max list item count * max link target length
Implementations MUST enforce these limits during parsing, not after building an unrestricted intermediate structure.
`max block count` and `max list item count` are whole-document limits. `max nesting depth` counts entry into nested block contexts from the top-level document context. `max inline depth` counts entry into recursively parsed inline content such as span bodies and link labels.
Strict-mode budget failure MUST produce no document.
Complexity requirements:
* Parsing a document of size `n` MUST be implementable in linear time with respect to `n`, subject to configured budgets. * Implementations MUST avoid parser strategies whose worst-case behavior is superlinear on valid or invalid input. * Implementations MUST avoid regular-expression or parser-combinator patterns that can introduce catastrophic backtracking on attacker-controlled input.
On exceed:
{
"code": "nd_budget_exceeded"
}
12. Non-Goals
Excluded from v1:
* images * footnotes * attributes * HTML embedding * macros * includes * task lists * table alignment * math/diagrams (extensions only)
13. Relationship to AEON
* &ND is not AEON * &ND may be embedded in AEON annotations * &ND produces a document tree * Interpretation belongs to consumers/tonics
14. Guarantees
Language Guarantees
* Single canonical grammar * No dialects * No implicit behavior * No global references * No HTML passthrough * No executable constructs
Structural Guarantees
* Fully typed AST output * Inline constructs are local * Blocks cannot interrupt paragraphs * Proper nesting required
Extension Guarantees
* Opaque * Non-executable * Do not alter parsing * Consumer-defined semantics
15. Final Lock
&ND is not forgiving markup. It is deterministic structured prose.
16. Core Grammar
The following EBNF summarizes the core grammar. Margin-sensitive and state-sensitive behavior remains governed by the normative prose above.
Grammar productions: Document ::= Header? BlankLine? Block* EOF; Header ::= "&ND" SP "v1" LineEnd; Block ::= Heading | HorizontalRule | CodeBlock | ExtensionBlock | Table | Blockquote | List | Paragraph; BlankLine ::= WS? LineEnd; LineEnd ::= "\n" | "\r\n"; SP ::= " "; WS ::= " " | "\t"; EOF ::= ? end of input ?.
Blocks
Grammar productions: Heading ::= HeadingMarker WS InlineContent LineEnd; HeadingMarker ::= "#" | "##" | "###" | "####" | "#####" | "######"; HorizontalRule ::= "---" WS? LineEnd; Paragraph ::= ParagraphTextLine+; ParagraphTextLine ::= InlineContent LineEnd.
Normative note:
ParagraphTextLine is selected only after the parser has determined that the current position is not
block-open eligible for another block. In other words, block-open eligibility is a parser-state
rule, not a textual negative lookahead on every paragraph line. Blank lines remain structural
separators and are never paragraph content.
Lists
Grammar productions: List ::= UnorderedList | OrderedList; UnorderedList ::= UnorderedItem+; UnorderedItem ::= Indent? "- " InlineContent LineEnd NestedBlock*; OrderedList ::= OrderedItem+; OrderedItem ::= Indent? OrderedMarker WS InlineContent LineEnd NestedBlock*; OrderedMarker ::= Digit+ "."; NestedBlock ::= Indent Block; Indent ::= SP SP; Digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
List note:
The EBNF shows structural containment only. The required blank-line boundary before a nested block
is a normative prose rule and is not fully expressed in this summary grammar.
Blockquotes
Grammar productions: Blockquote ::= QuoteLine+; QuoteLine ::= ">" WS? InlineContent? LineEnd.
Blockquote note:
The EBNF shows the quoted line prefix only. Nested block behavior inside a blockquote is governed
by the normative prose and scanner rules: a blockquote establishes an inner block context, blank
quoted lines preserve that context, and nested blocks use the blockquote's inner current block
margin.
Code blocks
Grammar productions: CodeBlock ::= PlainCodeBlock | OrderedCodeBlock | DollarCodeBlock; PlainCodeBlock ::= CodeOpen RawLines CodeClose; OrderedCodeBlock ::= OrderedCodeOpen RawLines OrderedCodeClose; DollarCodeBlock ::= DollarCodeOpen RawLines DollarCodeClose; CodeOpen ::= "```" CodeLang? LineEnd; CodeClose ::= "```" WS? LineEnd; OrderedCodeOpen ::= "````" CodeLang? LineEnd; OrderedCodeClose ::= "````" WS? LineEnd; DollarCodeOpen ::= "~~~$" ( " " CodeLang )? LineEnd; DollarCodeClose ::= "~~~$" LineEnd; CodeLang ::= Ident.
RawLines is implementation-defined scanning: consume all text until the first line matching the
corresponding closing fence at the same block margin. Triple backticks produce a plain code block.
Quadruple backticks produce an ordered code block whose payload lines retain their raw text while
explicitly requesting line ordering in downstream projections. ~~~$ optionally accepts one
space and a language and produces a plain code block. Its closer is exactly bare ~~~$. V1 does
not accept [n] in a dollar opener; ordered code remains available through quadruple backticks.
Canonical v1 output prefers backticks, falling back to ~~~$ when a plain-code payload contains an
exact triple-backtick line.
The briefly introduced language-qualified triple/quadruple tilde alternatives were removed before
publication. ~~~language and ~~~~language reject with deprecated_code_fence; bare ~~~
remains ordinary paragraph text. V2 extends the shared ~~~$ family with [n] numbered-line intent
without changing these v1 forms.
seed-dollar-code-fences
~~~$and~~~$ languageparse as unnumbered code blockscanonical v1 output uses triple backticks when safe and otherwise retains a dollar fence
seed-dollar-code-fence-numbered-v1
~~~$ [n]and~~~$ [n] languageare v2-only and reject in v1
seed-tilde-code-fences
language-qualified triple and quadruple tilde fences reject with
deprecated_code_fencebackticks and the unnumbered dollar family remain supported v1 code-block spellings
seed-plain-tilde-paragraph
bare
~~~without a language suffix remains ordinary paragraph text
seed-tilde-code-block-unclosed
the removed tilde opener rejects before closer matching
seed-ordered-tilde-code-block-wrong-closing-fence
the removed quadruple-tilde opener rejects before closer matching
Extension blocks
Grammar productions: ExtensionBlock ::= ExtensionOpen RawLines ExtensionClose FallbackBlock?; ExtensionOpen ::= "+++" WS? ExtensionName LineEnd; ExtensionClose ::= "+++" WS? LineEnd; FallbackBlock ::= "+++fallback" LineEnd Block* ExtensionClose; ExtensionName ::= LowIdent ( "/" LowIdent )* ( ".v" Digit+ )?; LowIdent ::= LowIdentStart LowIdentChar*; LowIdentStart ::= "a".."z"; LowIdentChar ::= LowIdentStart | Digit | "-".
RawLines is opaque: no inline parsing, no block parsing, no nesting in v1.
If present, FallbackBlock MUST be directly adjacent to the preceding extension close and is parsed
as ordinary &ND block content.
fallback is a reserved extension name and MUST NOT be interpreted as a general extension block.
Tables
Grammar productions: Table ::= TableHeader TableSeparator TableBody; TableHeader ::= TableRow; TableSeparator ::= TableSepRow; TableBody ::= TableRow+; TableRow ::= "|" TableCell ( "|" TableCell )* "|" WS? LineEnd; TableSepRow ::= "|" TableSepCell ( "|" TableSepCell )* "|" WS? LineEnd; TableCell ::= WS? InlineContent? WS?; TableSepCell ::= WS? "---" WS?.
Semantic table rule:
header column count == separator column count == every body row column count
V1 reserves the v2 aligned separator tokens <--, -=-, and --> and rejects them with
invalid_table_alignment. A cell whose content begins immediately after its delimiter with > is
reserved for v2 horizontal spanning and rejects with invalid_table_span. Ordinary literal content
may still begin with > when separated from the delimiter by padding, as in | > literal |.
seed-table-v2-alignment-rejected
v1 rejects v2 alignment separator tokens rather than treating the source as paragraph text
seed-table-v2-span-rejected
v1 rejects the adjacent
|>v2 horizontal-span marker
seed-card-block-v2-rejected
v1 rejects the v2
~~~|card-block fence rather than treating it as paragraph text
Inline content
Grammar productions: InlineContent ::= InlineNode*; InlineNode ::= Text | Strong | Emphasis | Link | Code; Strong ::= "[*" WS InlineContent "]"; Emphasis ::= "[/" WS InlineContent "]"; Link ::= "[@" WS LinkTarget WS? "|" WS? LinkLabel "]"; Code ::= "[$" WS InlineRaw "]"; LinkTarget ::= LinkChar+; LinkLabel ::= InlineNode+; InlineRaw ::= RawChar*.
Inline code rule:
Inline code suppresses inline-node parsing inside [$ ...].
It still uses the inline-code escape rules for delimiter safety.
Only \], \\, \[, and \| are recognized escapes inside inline code, and any unescaped ] closes the
construct.
Block margin note
The EBNF below omits indentation-sensitive margin parameters for readability. Where the prose says
"current block margin", implementations must apply that margin-sensitive rule even if the raw EBNF
shows an unindented opener form.
Text and escaping
Grammar productions: Text ::= TextChar+; TextChar ::= EscapedChar | NormalChar; EscapedChar ::= "\\" Escapable; Escapable ::= "[" | "]" | "|" | "\\"; NormalChar ::= ? any character except [, ], \, line ending ?; RawChar ::= EscapedChar | RawNormalChar; RawNormalChar ::= ? any character except ], \, line ending ?; LinkChar ::= EscapedChar | LinkNormalChar; LinkNormalChar ::= ? any character except |, ], \, line ending ?.
Identifiers
Grammar productions: Ident ::= IdentStart IdentChar*; IdentStart ::= "A".."Z" | "a".."z"; IdentChar ::= IdentStart | Digit | "_" | "-" | ".".
Normative parser rules not fully captured by EBNF
1. Parser MUST fail-closed on unclosed inline spans.
2. Parser MUST fail-closed on unclosed code or extension blocks.
3. Inline spans MUST form a proper tree.
4. Code blocks and extension blocks are raw islands.
5. Extension blocks MUST NOT nest in v1.
6. Tables MUST have equal cell counts per row.
7. Escapes apply only to [, ], |, and \.
8. Unknown inline tags are invalid in Core v1.
9. Unknown extension names are valid but opaque.
10. A line beginning with --- (no leading |) is always a HorizontalRule; table separator
rows always start with |.
11. Structural indentation (list nesting) uses spaces only; tabs are invalid in Indent.
12. The &ND v1 header is optional; its presence or absence is governed by the embedding
profile or tool convention.
13. Blocks may open only at document start or immediately after a blank line.
14. A paragraph continuation line is never reinterpreted as a block opener.
15. Escape handling is lexical during inline scanning; there is no post-parse escape pass.
16. Parser behavior MUST be bounded-lookahead and non-backtracking.
17. A nested block inside a list item requires a blank line before that nested block.
18. A blockquote establishes an inner block context for nested block recognition.
19. Blank quoted lines preserve the enclosing blockquote context.
20. Nested blocks inside a blockquote use the blockquote's inner current block margin.