Skip to content
Lesson 18 of 20

Step 1 of 5 · Reading · ~3 min

Read

Production Concerns

Putting It All Together

Every piece you have written so far handled one construct in isolation. This is where they become a program: one function that takes a document and returns HTML, with each handler you built now living as a branch of a single dispatcher.

What the finished converter contains

PhaseHandlers
Block, in dispatch orderfenced code, ATX heading, setext lookahead, thematic break, blockquote, list, paragraph
Inline, in application orderbackslash escape, code span, image, link, strong, em, strikethrough, HTML escape
Renderjoin block HTML with newlines

Notice that the block table is ordered and the inline table is ordered. Neither order is arbitrary — each one exists because an earlier rule would otherwise steal a line, or a later rule would otherwise walk into HTML that an earlier rule produced.

Rendering diagram…

The branch on the right is the point of the whole architecture: code content leaves the block phase and goes straight to the renderer, never passing through inline parsing. That is what keeps *not italic* literal inside a fence.

Why does dispatch order decide correctness?

Take the two rules that both claim a row of dashes:

python

Same input, same rules, different order — a heading became a paragraph plus a horizontal rule. Fenced code has to be checked before everything, because a fence can contain lines that look like any other block; the setext lookahead has to beat the thematic break; and the paragraph handler always goes last, because a paragraph is defined as "whatever nothing else claimed".

What your parser still does not do

Being honest about scope is part of engineering:

  • Link reference definitions need a pre-pass over the whole document.
  • Tight versus loose lists need the list collected before any item renders.
  • Lazy continuation lets a blockquote paragraph run past unprefixed lines.
  • Nested containers — a list inside a quote inside a list — need recursion.
  • Raw HTML blocks, backslash escapes and entity references have their own rules.

CommonMark ships 652 conformance examples in version 0.31.2 precisely because this list is long. cmark is the reference implementation in C; markdown-it, goldmark, pulldown-cmark and markdig are the widely used ports.

Use one of those in production. Write your own to understand parsing — which is what you just did — and then reach for the fuzz-tested library when the input comes from strangers.

Your exercise

Build the end-to-end converter.

The mistake the grader catches is skipping setext headings. Two hidden tests feed Setext H1 over a row of = and Title over a row of -, and expect <h1> and <h2>. Handle only ATX and those two come back as a paragraph plus an <hr /> — the same failure the snippet above demonstrates. The lookahead belongs before your thematic-break branch.

The second trap is inline parsing inside fences. A hidden test puts *em*, **strong** and ~~strike~~ in a paragraph and a plain word in a code block; if the code path shares the inline function, the block content gets rewritten and the diff lands inside <pre>.

Up nextGFM TablesProduction Concerns

Discussion

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

Sign in to post a comment or reply.

Loading…