Github Flavored Markdownは矛盾しているのか

>   これは「[Markdown Advent Caleandar 2020](https://adventar.org/calendars/4964)」の12日目の記事です。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">gfm spec のドキュメント、Version 0.28-gfm (2017-08-01) って書いてある割には 0.28 以降の (2018年以降の) CommonMark の修正も取り込んであるし、記述に矛盾があるし、<a href="https://t.co/2xhuSngbJK">https://t.co/2xhuSngbJK</a> とも動作が違うし、本当に信頼できないものだという思いを強めております。</p>&mdash; tk0miya (@tk0miya) <a href="https://twitter.com/tk0miya/status/1110778525689225216?ref_src=twsrc%5Etfw">March 27, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

どうやら、[某Markdownソムリエ氏][sommelier]の情報によると、GitHub Flavored Markdown（GFM）の[仕様][gfm]には**「矛盾」が含まれている**ようです。

### GFMが矛盾しているかもしれない話

GFMに潜む「矛盾」については、以下のQiita記事で詳しく論じられています。それによると、**「自動リンク」に関する規定に問題がある**とのことです。

* [GitHub Flavored Markdown は何であって何でないか][sommelier-qiita]（Qiita/tk0miya）

#### GFMの「自動リンク」が矛盾しているかもしれない

[CommonMark][cm]と[GFM][gfm]の規格では、「自動リンク(autolink)」の規定について次のような差異があります。

* 段落に`http://example.com`のような“単独のURL”だけが書かれている場合、CommonMarkではそれは自動リンクにはならない。
* しかし、GFMでは“単独のURL”の記述も`http://`で始まる場合は自動リンクになる。

この差異は「細かい機能追加」といえるものであり、それ自体は特に問題になるものではありません[^rawhtml]。問題になっているのは、この規定に関する、GFMの規格の**記述**です。

[^rawhtml]: GFM規格の[6.11節][gfm-raw-html-ex]にある「一部の生HTML記法の適用除外」は「機能の削除」に相当するため、「GFMはCommonMarkの厳密な上位互換である([GFM is a strict superset of CommonMark.][gfm-what-is])」と称するのは問題がありそうですが……。

* [6.8節“Autolinks”][gfm-autolinks]はCommonMarkの規格の[6.7節][cm-autolinks]を変更なしにそのまま引き写している。そしてそこでは“単独のURL”は**自動リンクにならないことが明記**されている。
* [6.9節“Autolinks (extension)”][gfm-autolinks-ex]はGFM規格で独自に追加された節で、そこでは`http://`で始まる“単独のURL”が**自動リンクになる**と規定されている。

つまり、独自追加の節がどれかは読者には明示的に示されているものの、6.8節と6.9節は**「同列で並んでいる」**ため、結局のところ、**「単一の規格の中で矛盾した記述が併存する」**事態になってしまうわけです。これを受けて、[某ソムリエ氏][sommelier]は次のように述べて、これが**「矛盾」である**と指摘しています。

> 順序や成り立ちを考えると、前者の仕様は破棄され GFM としては後者の仕様を採用したという理解となるのですが、仕様書の中で矛盾が起きているのは非常に厄介です。

#### GFMの「自動リンク」が矛盾していないかもしれない

この議論は一見して妥当そうです。しかし、私が実際にGFMの規格の[6.9節][gfm-autolinks-ex]を読んでみると、割と印象が異なるように感じました。

> GFM enables the `autolink` extension, where autolinks will be recognised in a greater number of conditions.  
> （GFMでは`autolink`拡張が有効である。そこでは、より広い条件に当てはまるものが自動リンクと見なされる。）

つまり、**本文において**明確に「この節の内容は“autolink拡張”の規定である」と述べています。つまり、GFMにおいて「拡張(extension)」という概念は参考情報(informative)ではなく規定の一部をなす(normative)であると解釈すべきでしょう。そう考えると、6.8節の内容は「GFMの“基礎”の規定」であり、6.9節の内容は「GFMの“autolink拡張”の規定」である、ということになり、両者は**「同列に並んでいる」とはいえず**、従って、必ずしも矛盾しているとはいえないことになります。

もちろん「“基礎”と“拡張”の規定の関係性についての規定が不明瞭」という問題はあるのですが、そもそも**「GFMの規定は『かっちりして』いない」**（これは異論のないところでしょう）わけなので、不明瞭であるからといって「だから同列であると見なされるので、矛盾がある」と結論づけるのはやはり無理があると思いました。

### GFMがやっぱり矛盾している話

というわけで、GFMの「自動リンク」の規定に矛盾があるかどうかは怪しい、ということになってしまいました。GFMは矛盾していないのでしょうか？

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">「GFM specの内部の矛盾」を見つけた。でも、「Markdown アドベントカレンダー」のネタとしてとっておくことにしよう🙃🙃🙃<a href="https://twitter.com/hashtag/markdown?src=hash&amp;ref_src=twsrc%5Etfw">#markdown</a> <a href="https://twitter.com/hashtag/gfm?src=hash&amp;ref_src=twsrc%5Etfw">#gfm</a></p>&mdash; 某ZR（ざんねん🙃） (@zr_tex8r) <a href="https://twitter.com/zr_tex8r/status/1325126067095416837?ref_src=twsrc%5Etfw">November 7, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

どうやら某ZR氏（非Markdownソムリエ）によると、**GFMの「打消しテキスト」の規定に矛盾がある**らしいです。

#### GFMの「打消しテキスト」が矛盾しているはず

GFMの「打消しテキスト(strikethrough text)」の機能は、`strikethrough`というextensionとして規定されています。規定のある個所は[6.5節“Strikethrough (extension)”][gfm-strikethrough]です。

> GFM enables the strikethrough extension, where an additional emphasis type is available.  
> （GFMでは`strikethrough`拡張が有効である。そこでは、新たなタイプの強調記法が使える。）
> 
> Strikethrough text is any text wrapped in two tildes (`~`).  
> （打消しテキストは、2つのチルダで囲われた任意のテキストである。）

本文はこれだけです（この後に例が続く）。ぱっと見て気になる点は、「**優先順位**に関する記述がない」ということです。ここでいう「優先順位」というのは、例えば次の例のように「複数の種類のマークアップが互いに干渉する状態にあるときにどれを有効とするのか」という規定を指します。

```md
Here is a ~~strike *emph through~~ asis* text.
```

もしここで打ち消しテキスト`~~…~~`と強調`*…*`の両方を有効と見なすと、これをHTMLで表したときに次のようになってしまいます。

```html:(不正)
<p>Here is a 
strike <em>emph through
 asis</em> text.
```

しかしHTMLではこのような「2つの要素が部分的に重なっている状態」は認められていません。

例によって、「かっちりしている」CommonMarkにおいては、互いに干渉する可能性のあるインライン要素について、明確に優先順位が規定されています。例えば、CommonMarkにおける強調の規定（[6.4節“Emphasis and strong emphasis”][cm-emphasis]）では次のように書かれていて「リンク記法と干渉する場合には`*…*`は強調にならない」ことが明確に規定されています。

> Inline code spans, links, images, and HTML tags group more tightly than emphasis.
> So, when there is a choice between an interpretation that contains one of these elements and one that does not, the former always wins.
> Thus, for example, `*[foo*](bar)` is parsed as `*<a href="bar">foo*</a>` rather than as `<em>[foo</em>](bar)`.

GFMの「打ち消しテキスト」に優先順位が決められていないのは、ある意味「GFMらしい」わけですが、この件については単に「かっちりしていない」だけでなく、もっと重大な結果を引き起こします。なぜなら、「優先順位がない」ということは即ち「`strikethrough`拡張機能が『打消しテキスト』**以外の**規定に対して変更を加えるための根拠をもたない」ことを意味するからです。

もう一度、先に挙げた「干渉している例」がどう解釈されるべきかを考えてみましょう。

```md
Here is a ~~strike *emph through~~ asis* text.
```

* [6.4節“Emphasis and strong emphasis”][gfm-emphasis]はCommonMarkの規格の[6.4節][cm-emphasis]を**変更なし**にそのまま引き写している。先述の通り、この節の解釈が変更される根拠はこの節の外にも存在しない。ということは、CommonMarkで強調と解釈される記述はGFMでも**必ず**強調と解釈される。
* 従って、先の例の`*emph through~~ asis*`は強調で**ある**。
* 一方で、6.5節では、「2つのチルダで囲われた**任意の**テキスト」が打消しテキストと解釈されると規定している。
* 従って、先の例の`~~strike *emph through~~`は打消しテキストで**ある**。

結局、優先順位がないために、「HTMLとして不正な構造」になってしまうのを避けられません。

一方で、GFMの規格の1.4節（CommonMark規格の1.3節の引き写し）では「この規格文書ではMarkdown文書の構造をHTMLで表現する」としていて、ここでは「Markdownの文書の構造はHTMLで表せる」ことが前提になっています。従って、「HTMLで表せないMarkdownのコードの存在」は**矛盾**であるといっていいでしょう。

## まとめ

＊「なんだ、結局、ソムリエ氏と同じことをやってるじゃん」
ZR「えっ」
＊「だって、『かっちりしていない』GFMに対して厳密な議論を適用するのがまず無理じゃない」
ZR「えっえっ」
＊「実際には何か優先順位があることになってるんじゃないの？」
ZR「えっえっえっ」

**～おしまい～**:upside_down:

[cm-about-this]: https://spec.commonmark.org/0.29/#about-this-document
[cm-autolinks]: https://spec.commonmark.org/0.29/#autolinks
[cm-emphasis]: https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis
[cm]: https://spec.commonmark.org/0.29/
[gfm-about-this]: https://github.github.com/gfm/#about-this-document
[gfm-autolinks-ex]: https://github.github.com/gfm/#autolinks-extension-
[gfm-autolinks]: https://github.github.com/gfm/#autolinks
[gfm-emphasis]: https://github.github.com/gfm/#emphasis-and-strong-emphasis
[gfm-raw-html-ex]: https://github.github.com/gfm/#disallowed-raw-html-extension-
[gfm-strikethrough]: https://github.github.com/gfm/#strikethrough-extension-
[gfm-what-is]: https://github.github.com/gfm/#what-is-github-flavored-markdown-
[gfm]: https://github.github.com/gfm/
[sommelier-qiita]: https://qiita.com/tk0miya/items/6b81e0e4563199037018
[sommelier]: https://qiita.com/tk0miya/items/89c39281a430081f1747