Step 1 of 5 · Reading · ~3 min
Read
Inline Elements
Links and Images
[text](url) -> <a href="url">text</a>
[text](url "a title") -> <a href="url" title="a title">text</a>
 -> <img src="url" alt="alt" />
<https://example.com> -> an autolink (its own lesson)
An image is a link with an exclamation mark in front of it. That one character is the whole difference in the syntax, and it is also the source of the most common bug in hand-written Markdown parsers. Your program will scan a line from left to right and rewrite links and images into anchors and <img> tags.
Why must the ! be checked first?
A scanner copies ordinary characters to the output one at a time. When it reaches !, it has to look one character ahead. If the next character is [, this may be an image. If you only test for [, the ! has already been copied by the time you get there, and it is left stranded in front of an anchor:
| Input | Link-only scanner | Correct |
|---|---|---|
See  | See !<a href="logo.png">logo</a> | See <img src="logo.png" alt="logo" /> |
If the ! does not start an image (Wow! [ok](/ok)), it is just a character: copy it and carry on.
What can the destination contain?
Everything up to the first space or the ) that closes the link. Parentheses are allowed if they balance, so a Wikipedia URL survives:
| Source | href |
|---|---|
[wiki](https://en.wikipedia.org/wiki/Foo_(bar)) | https://en.wikipedia.org/wiki/Foo_(bar) |
[x](a(b)c) | a(b)c |
[a](b(c) | not a link: the parenthesis never closes |
A scanner that stops at the first ) truncates the first row and leaves a stray ) in the text. CommonMark also lets you wrap the destination in angle brackets to sidestep the question, which this exercise does not need.
The destination may be followed by spaces and a title in double quotes, which becomes a title attribute. The href, src, alt and title are attribute values, so &, <, > and " inside them must be escaped: a query string ?q=a&lang=en turns into ?q=a&lang=en.
What goes in the brackets?
The label of an image is plain text: it becomes the alt attribute, and it may not contain brackets. The label of a link is more interesting:
- it may contain balanced brackets, so you find the matching
]by counting depth, not by stopping at the first one; - it may contain an image. This is the classic README badge,
[](run-url); - it may never contain another link. For
[foo [bar](/uri)](/uri2), the inner link wins and the outer brackets are literal text.
To implement the last rule, scan the label recursively with the same function. If the recursion found a link, the outer candidate is rejected: copy its [ as text and carry on from the next character, which is how the inner link gets its chance.
The four bracket shapes
| Form | Looks like | Resolves how |
|---|---|---|
| Inline | [text](url) | the URL is right there |
| Reference | [text][label] | looked up in a table built earlier |
| Collapsed | [label][] | the label doubles as the text |
| Autolink | <https://example.com> | the URL is also the text |
Only the first is in scope here; the others get their own lessons. Anything that fails the inline shape ([text] (url) with a space, [a](, a bracket with no partner) is plain text and is copied unchanged.
Common mistakes
- Dropping the
!check. Images come out as an exclamation mark plus an anchor. - Stopping the destination at the first
). Parenthesised URLs are truncated. - Not escaping attribute values. A raw
&or"in a URL breaks the tag. - Nesting links. The outer link must give way to the inner one.
Your exercise
Write scan, which converts links and images in one line. The starter reads the lines and prints the results; escape_html is given.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…