Skip to content
The Delimiter Stack
step 1/5

Reading — step 1 of 5

Learn

~4 min readInline Elements

The Delimiter Stack

The emphasis lesson built a guarded regex and then admitted where it still parts company with the spec: a*"quoted"* gets emphasised when it should not. That is not a bug you can patch with another lookaround. Emphasis is not a regular language, and the appendix to the spec gives an algorithm instead. This is it.

Where does a regex actually break?

Not on the easy cases. It breaks when two candidate spans overlap.

python

A regex matches the first opener with a later closer, so it swallows the second ** as content. The spec resolves the same input the other way — when two spans share a closing delimiter, the one that opens later wins — giving **foo <strong>bar baz</strong>, with the leading pair left as literal text. Nesting has the same shape: *a **b** c* needs the inner pair matched first and the outer pair to survive across it, and no single substitution does both.

What goes on the stack?

Not characters — delimiter runs. A run is a maximal sequence of * (or of _), and it is pushed with its length plus two booleans: can this run open, and can it close. Those come from the spec's flanking rules, which look at exactly one character on each side, treating the line ends as whitespace.

python

Row three is the case the regex could not reach: a run preceded by a letter and followed by punctuation is not left-flanking, so it cannot open, so the line has a closer and no opener and stays literal. Two booleans, no lookarounds.

Matching openers to closers

The scan is asymmetric, and that asymmetry is the whole algorithm.

Rendering diagram…

Forward to find a closer, then backwards to the nearest opener — nearest, not first, which is what makes the later-opening span win. Everything between the two becomes the new node's content, and any delimiter runs caught in the middle are rendered as plain asterisks: an unmatched * is text, always.

Spending is per delimiter, not per run. A run of two that matches a run of one gives up a single asterisk and stays on the stack with one left, which is how a closer can serve twice.

What this leaves out

Runs of three or more, where strong and em combine — the emphasis lesson covers those and tells you which order this course's tests expect. The _ variants add two word-boundary clauses to the same booleans. And a spec rule blocks a match when the two run lengths sum to a multiple of three; none of the inputs here reach it.

Your exercise

Build the stack, match the runs, emit the tags.

The mistake the grader catches is scanning back to the first opener instead of the nearest. A hidden test feeds **foo **bar baz**; the nearest opener gives **foo <strong>bar baz</strong>, and the first one gives <strong>foo **bar baz</strong> — the same wrong answer the regex produced above. Walk backwards from the closer and stop at the first run you can use.

The second is resuming in the wrong place. After a match the closer may still have delimiters left, so restart the scan at it rather than after it; **a *b* c** needs the outer pair matched on a second visit.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

The Delimiter Stack — Build a Markdown to HTML Converter