Step 1 of 5 · Reading · ~3 min
Read
Inline Elements
Bold and Italic
*italic* or _italic_ -> <em>italic</em>
**bold** or __bold__ -> <strong>bold</strong>
**bold *and italic*** -> <strong>bold <em>and italic</em></strong>
Emphasis is the part of Markdown with the most edge cases per character of syntax. The reason is simple: an asterisk is also multiplication, a footnote marker and a decorative bullet, so the parser has to decide, for every asterisk, whether it is markup or text. Your program will turn *-emphasis into <em>, <strong> and both, while leaving code spans alone.
When is an asterisk markup?
The spec's answer is flanking. This lesson uses the half of it that does most of the work:
- an asterisk run can open only if the character after it exists and is not whitespace;
- a run can close only if the character before it is not whitespace.
| Input | Result | Why |
|---|---|---|
*shipped* | <em>shipped</em> | opener touches text, closer touches text |
2 * 3 * 4 | unchanged | both stars have a space on one side |
5 *6* 7 | 5 <em>6</em> 7 | the stars hug the 6 |
un*bel*ievable | un<em>bel</em>ievable | mid-word emphasis with * is intended |
* spaced * | unchanged | the opener is followed by a space |
The full CommonMark rule also looks at punctuation. a*"quoted"* is left alone by the spec and emphasised by this simpler rule; the next lesson replaces the shortcut with the real algorithm.
Why do underscores behave differently?
Because snake_case_names are everywhere. In CommonMark an underscore may only open or close at a word boundary, while an asterisk may work mid-word. To keep this exercise about one idea, underscores are not markers here: _text_ comes out as typed.
Which delimiter do you match first?
Longest first, always: the same maximal-munch rule a lexer uses. Make one pass per length, 3 then 2 then 1.
| Order | **bold** becomes |
|---|---|
try ** before * | <strong>bold</strong> |
try * before ** | *<em>bold</em>* |
One spec detail worth knowing: the reference renderer emits ***x*** as <em><strong>x</strong></em>. This course's tests use the opposite order, <strong><em>x</em></strong>, which is what most hand-written parsers produce. Match the tests here.
When a run is longer than the pass needs, the opener spends its first n stars and the closer its last n. That is how **bold *and italic*** works: the final run holds three stars; the strong pass takes the last two and leaves the first one inside the content, where the em pass later finds it.
Why stash the finished HTML?
Each pass rewrites the string the next pass still has to scan, so a later pass can walk straight into a tag the previous one produced. Parking the finished tag behind a placeholder makes later passes unable to see it, while the text between the tags stays visible:
| Step | Line |
|---|---|
| start | **bold *and italic*** |
| strong pass | [S]bold *and italic*[/S] |
| em pass | [S]bold [E]and italic[/E][/S] |
| restore | <strong>bold <em>and italic</em></strong> |
Code spans get the same treatment, and they go first so nothing inside backticks is ever touched. The starter provides that step (and the stash itself); the code blocks lesson builds it from scratch.
Common mistakes
- An unguarded
*. Treating every pair of stars as emphasis turns2 * 3 * 4into2 <em> 3 </em> 4. Check the whitespace on the inside. - Matching
*before**.**bold**comes out as two italics around bold text. - Substituting in place. Without the stash, a later pass re-reads tags and can wrap
</strong>inside an<em>. - Forgetting that code wins.
`a *b* c`must stay code; run the code step first.
Your exercise
Write emph_pass (one pass for a given star count) and render_line (the order of the steps). Everything else is in the starter. Output is one line per input line.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…