Step 1 of 5 · Reading · ~3 min
Read
Inline Elements
Reference-Style Links
Inline link syntax, [text](url), gets unwieldy when you reuse the same URL many times or when URLs are long. The reference style separates the link text from the destination:
See the [Python docs][py] and the [Python source][py].
[py]: https://docs.python.org/3/
Both uses resolve to <a href="https://docs.python.org/3/">...</a>. This is the first construct in the course where a use can depend on something that appears later in the document, so it forces a design decision: your program makes two passes. The first collects every definition; the second resolves every use. Your program will do both and print the paragraphs with their links resolved.
Three ways to use a definition
| Form | Source | Lookup key | Link text |
|---|---|---|---|
| Full | [Python docs][py] | py | Python docs |
| Collapsed | [py][] | py | py |
| Shortcut | [py] | py | py |
The shortcut is the dangerous one: plain text in brackets, like a citation [1], is a link if and only if a definition with that label exists, and plain text otherwise. A label with no definition is never an error; the brackets are simply text.
What a definition looks like
[label]: destination "optional title"
- It sits on its own line, indented by at most three spaces.
- The label is matched after normalising: lowercase it, trim it, and collapse every run of whitespace to one space. So
[Two Words]and[two words]are the same label. - The destination is a run of characters with no spaces. The title is optional and double-quoted.
- If the same label is defined twice, the first definition wins.
- A definition produces no output of its own: it is removed from the document.
Where may a definition appear?
Anywhere in the document, before or after its uses, with one exception that surprises people: a definition cannot interrupt a paragraph. Compare:
| Source | What the second line is |
|---|---|
Some text, blank line, [x]: /url | a definition |
Some text then immediately [x]: /url | more paragraph text |
So the first pass has to track whether it is inside a paragraph: a definition is recognised only at the start of a block, which means at the top of the input, after a blank line, or after another definition.
A worked trace
Input: See [docs][d] and [home]., blank, [d]: /docs "Docs", [home]: /.
| Pass | Step | Result |
|---|---|---|
| 1 | line 1 starts a paragraph | kept |
| 1 | blank line | kept, next line starts a block |
| 1 | [d]: /docs "Docs" | stored as d -> (/docs, Docs), removed |
| 1 | [home]: / | stored as home -> (/, no title), removed |
| 2 | [docs][d] | key d is defined: <a href="/docs" title="Docs">docs</a> |
| 2 | [home] | no second bracket pair: shortcut, key home is defined: <a href="/">home</a> |
Resolving a use
Look at the first [...] (its label has no [ inside). If a second [...] follows directly, it is the lookup label, and an empty one means "use the first label". Otherwise the first label is the key. If the key is defined, emit the link and skip everything you used. If it is not, copy the [ as text and keep scanning from the next character, so [unknown][nope] comes out exactly as typed.
Destinations and titles are attribute values: escape &, <, > and " in them. The link text is copied as written.
Common mistakes
- Resolving in one pass. A link near the top of the file whose definition is on the last line stays unresolved.
- Case-sensitive or whitespace-sensitive labels.
[Foo]must find[foo]:. - Letting a definition interrupt a paragraph. It is paragraph text, and it must stay in the output.
- Treating an undefined label as an error. It is plain text.
Your exercise
Write the first pass (extract_refs) and the second (resolve). The starter gives you the definition pattern, label normalisation, escaping, and the paragraph joiner from the paragraphs lesson.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…