CommonMark i Parsedown – specyfikacja, architektura oraz porównanie implementacji

Wprowadzenie
Markdown przez lata funkcjonował jako „nieformalny standard”. Oryginalny opis Johna Grubera pozostawiał wiele niejednoznaczności, co skutkowało różnicami między parserami. Odpowiedzią na ten problem stał się projekt CommonMark – formalna, jednoznaczna specyfikacja Markdown wraz z zestawem testów zgodności.
W ekosystemie PHP jedną z najpopularniejszych bibliotek jest Parsedown – szybki i prosty parser Markdown, który jednak nie implementuje pełnej specyfikacji CommonMark.
Poniżej znajduje się techniczne opracowanie łączące:
- architekturę i założenia CommonMark,
- szczegóły składni i algorytmu parsowania,
- porównanie z Parsedown,
- implikacje praktyczne.
1. CommonMark – formalna specyfikacja Markdown
1.1 Cel projektu
CommonMark definiuje:
- jednoznaczną składnię,
- precyzyjne reguły parsowania,
- dwufazowy model przetwarzania,
- zestaw testów referencyjnych (setki przypadków).
Oficjalna specyfikacja:
https://spec.commonmark.org
2. Architektura składni
CommonMark rozdziela przetwarzanie na dwie warstwy:
2.1 Elementy blokowe (Block-level)
Parser najpierw buduje strukturę dokumentu:
- Nagłówki (ATX, Setext)
- Listy (UL/OL)
- Cytaty blokowe
- Bloki kodu (indented i fenced)
- Reguły poziome
- Akapity
- Definicje linków referencyjnych
2.2 Elementy inline (Inline-level)
Po zbudowaniu drzewa bloków analizowane są elementy wewnętrzne:
- Emfaza (
*,_) - Silna emfaza
- Linki i obrazy
- Autolinki
- Kod w linii
- Surowy HTML
- Hard/soft line breaks
3. Algorytm parsowania CommonMark
CommonMark stosuje dwufazowy algorytm:
Faza 1 – Konstrukcja drzewa bloków
Parser czyta dokument linia po linii i buduje strukturę blokową.
Faza 2 – Parsowanie inline
Dopiero po ustaleniu struktury blokowej analizowana jest zawartość tekstowa.
To eliminuje wiele konfliktów, np.:
- Czy linia jest częścią listy czy akapitu?
- Czy
_oznacza kursywę czy znak w słowie? - Czy blok HTML powinien być interpretowany jako Markdown?
4. Kluczowe reguły składni – przykłady
4.1 Nagłówki
# Nagłówek 1
## Nagłówek 2 ##
Reguły:
- 1–6 znaków
# - Wymagana spacja po znaku
# - Opcjonalne zamykające
#
4.2 Emfaza – reguły precyzyjne
foo_bar_baz
W CommonMark nie powstaje kursywa. Podkreślenie wewnątrz słowa nie otwiera znacznika emfazy.
To przykład miejsca, gdzie starsze parsery często działały inaczej.
4.3 Listy
- A
- B
- C
CommonMark dokładnie definiuje:
- minimalne wcięcia,
- kiedy lista jest kontynuowana,
- jak działa zagnieżdżenie,
- kiedy lista się kończy.
4.4 Bloki kodu
Indent (4 spacje)
kod
Fenced
```php
echo "Hello";
````
Specyfikacja precyzuje:
- minimalną liczbę backticków,
- możliwość użycia tyld (
~~~), - zasady zamykania bloku.
4.5 Hard line break
Dwie spacje na końcu linii:
Linia 1
Linia 2
Renderowane jako <br />.
5. Testy zgodności
CommonMark zawiera setki przykładów:
- Markdown wejściowy
- Oczekiwany HTML
- Jednoznaczny wynik
Parser zgodny z CommonMark musi przejść test suite.
To kluczowa różnica wobec starszych implementacji Markdown.
6. Parsedown – popularna implementacja w PHP
Parsedown to:
- Lekki parser w PHP
- Prosta integracja
- Bardzo dobra wydajność
- Wiele praktycznych rozszerzeń
Nie jest jednak formalnie zgodny z CommonMark.
7. Porównanie CommonMark vs Parsedown
7.1 Zgodność ze standardem
| Cecha | CommonMark | Parsedown |
|---|---|---|
| Formalna specyfikacja | ||
| Testy referencyjne | ||
| Jednoznaczność | Wysoka | Średnia |
7.2 Zachowanie w przypadkach granicznych
Emfaza w środku słowa
foo_bar_baz
- CommonMark → brak kursywy
- Parsedown → w niektórych wersjach możliwa interpretacja jako emfaza
Listy złożone
CommonMark precyzyjnie definiuje wcięcia. Parsedown bywa bardziej liberalny.
7.3 Architektura parsera
| Cecha | CommonMark | Parsedown |
|---|---|---|
| Dwufazowy parser | Nieformalny | |
| Drzewo AST | Tak (w wielu implementacjach) | Nie zawsze |
| Determinizm | Wysoki | Zależny od wersji |
7.4 Wydajność
Parsedown:
- Bardzo szybki
- Minimalistyczna implementacja
Implementacje CommonMark:
- Często bardziej rozbudowane
- Niekiedy wolniejsze (więcej walidacji)
7.5 Rozszerzenia
Parsedown:
- Autolinki
- Ułatwienia składni
- Elastyczne zachowanie
CommonMark:
- Trzyma się specyfikacji
- Rozszerzenia muszą być jawnie dodane
8. Bezpieczeństwo
Ani CommonMark, ani Parsedown:
- Nie sanitizują HTML domyślnie
- Nie chronią przed XSS bez dodatkowego filtra
W systemach produkcyjnych konieczne jest użycie sanitizatora HTML.
9. Kiedy wybrać które rozwiązanie?
Wybierz CommonMark jeśli:
- Tworzysz parser
- Wymagana jest interoperacyjność
- Dokumenty muszą być przewidywalne
- Zgodność ze standardem jest kluczowa
Wybierz Parsedown jeśli:
- Tworzysz aplikację PHP
- Liczy się szybkość i prostota
- Nie potrzebujesz ścisłej zgodności
- Akceptujesz drobne różnice interpretacyjne
10. Wnioski techniczne
CommonMark to:
- Formalny model składni Markdown
- Dwufazowy algorytm parsowania
- Jednoznaczne reguły
- Zestaw testów referencyjnych
- Deterministyczne przetwarzanie
Parsedown to:
- Praktyczna, szybka implementacja
- Wygodne API
- Elastyczne podejście
- Brak pełnej zgodności ze specyfikacją
11. Podsumowanie całości
Markdown bez specyfikacji prowadzi do rozbieżności. CommonMark rozwiązuje ten problem przez formalizację składni i testy.
Parsedown reprezentuje pragmatyczne podejście – szybkość i użyteczność ponad formalną zgodność.
Wybór zależy od kontekstu:
- System dokumentacji, edytor WYSIWYG, narzędzia interoperacyjne → CommonMark
- Lekki CMS, blog PHP, aplikacja webowa → Parsedown