Cutdown Ambiguity Matrix

Rules Under Test

  • Escapes resolve before token matching.
  • Brace-family tie-break uses same priority + longest opener ({{ before {) + left-to-right.
  • In [text][...] target slot, leading @ means citation-link classification; mention parser does not run there.
  • Invalid openers are emitted as literal text unless a rule says otherwise.

Canonical Cases

1) Escape precedence

Input:

\{{name}}

Expected AST:

Paragraph
└── Text("{{name}}")

Input:

\@alice

Expected AST:

Paragraph
└── Text("@alice")

Input:

\[a][@c1]

Expected AST:

Paragraph
└── Text("[a][@c1]")

2) Variable vs attributes

Input:

{{customer}}{.v}

Expected AST:

Paragraph
└── Variable(key="customer", attributes={class:["v"]})

Input:

{{}}

Expected AST:

Paragraph
└── Text("{{}}")

Input:

{{name}

Expected AST:

Paragraph
└── Text("{{name}")

Input:

[paper][@smith2025]

Expected AST:

Paragraph
└── Link(kind="cite", text=[Text("paper")], target="@smith2025")

Input:

[][@a.b-c_d]

Expected AST:

Paragraph
└── Link(kind="cite", text=[], target="@a.b-c_d")

Input:

[@smith2025]

Expected AST:

Paragraph
└── Text("[@smith2025]")

4) Inline boundary crossing (CDN-0014)

Greedy left-to-right parse is unchanged. CDN-0014 is emitted at the span of the crossing closer.

Input:

** a __ b ** c __

Expected AST:

Paragraph
├── Emphasis([Text("a __ b")])
└── Text(" c __")

Expected diagnostics:

CDN-0014  warning  span: the closing "**" (col 9)
message: Crossed inline boundaries: "**" closes while "__" (col 5) is still open

Input:

"" q start [link "" q end][/path]

Expected AST:

Paragraph
├── QuoteInline([Text("q start [link")])
└── Text(" q end][/path]")

Expected diagnostics:

CDN-0014  warning  span: the closing '""' (col 16)
message: Crossed inline boundaries: '""' closes while "[" (col 10) is still open

Input (valid nesting — no diagnostic):

** __ text __ **

Expected AST:

Paragraph
└── Emphasis([Strong([Text("text")])])

Expected diagnostics: none

Input (triple crossing — two CDN-0014 emitted):

** __ ~~ text ** __ ~~

Expected AST:

Paragraph
├── Emphasis([Text("__ ~~ text")])
└── Text(" __ ~~")

Expected diagnostics:

CDN-0014  warning  span: closing "**" (col 15)  — closes while "__" (col 4) is open
CDN-0014  warning  span: closing "**" (col 15)  — closes while "~~" (col 7) is open

5) Spoiler ^^ opener

Input:

\^^secret^^

Expected AST:

Paragraph
└── Text("^^secret^^")

Input:

^^{{var}}^^

Expected AST:

Paragraph
└── Spoiler([Variable(key="var")])

Input:

^^ open

Expected AST:

Paragraph
├── Text("^^")
└── Text(" open")

Input:

**^^x^^**

Expected AST:

Paragraph
└── Emphasis([Spoiler([Text("x")])])

Input:

^^^x^^^

Expected AST:

Paragraph
├── Spoiler([Text("^x")])
└── Text("^^")

Input (boundary crossing — CDN-0014):

^^ a ** b ^^ c **

Expected AST:

Paragraph
├── Spoiler([Text("a ** b")])
└── Text(" c **")

Expected diagnostics:

CDN-0014  warning  span: the closing "^^" (col 11)
message: Crossed inline boundaries: "^^" closes while "**" (col 6) is still open

6) CodeInline escape (\` only)

Input:

``\`x``

Expected AST:

Paragraph
└── CodeInline(value="`x")

Input (double backslash stays literal — \\ is NOT an escape inside CodeInline):

``a\\b``

Expected AST:

Paragraph
└── CodeInline(value="a\\b")

Input (unknown \X stays literal):

``\nb``

Expected AST:

Paragraph
└── CodeInline(value="\nb")

Note: the value above contains the two literal characters \ and n, not a newline.

Input (escape lets multi-backtick embed — the motivating case):

``\`\`\`x``

Expected AST:

Paragraph
└── CodeInline(value="```x")

Input (interaction with run-of-3 opener collapse):

```\`x```

Expected AST:

Paragraph
└── CodeInline(value="``x")

The opener ``` collapses via run-of-3 to (double opener) + literal `` (in content). Then `` \ inside contributes another literal ` for a value of x .

Change Checklist

For every new inline token:

  1. Add at least one escape case.
  2. Add at least one opener conflict case.
  3. Add at least one malformed/unclosed case.
  4. Add one interaction case with each neighboring precedence tier.
  5. Add expected AST snapshots.