Python界隈でよく見かける構造化文書のための記法として、reStructuredText(以降はreSTと書きます)があります。
- reStructuredText
https://docutils.sourceforge.io/rst.html
軽量マークアップ言語などと呼ばれることもありますが、reSTはかなり高度な表現力がある記法です。 その記法をパースするために標準で使われているのはDocutilsという仕組みです。ただ、DocutilsはreST専用ではなく、他の記法のパーサを実装することもできるらしいです。 その意味でDocutilsは、Pandocと同じく、内部の抽象的なデータ構造へと記法を押し込めるツールだといえる気がします。
Docutilsについては『マスタリングDocutils』に詳しいので興味がある方は購入しましょう。
- 『マスタリングDocutils』
マスタリング docutils - マークアップ言語愛好会 - BOOTH
今日はDocutilsのことは忘れて、reSTの記法をPandocで読み込み、Pandocの抽象データ型(Pandoc構造)に押し込める話をします。
reSTのリストテーブル
reSTにはリストテーブル(List Table)という記法があります。 正確に言うとこれはreST本来の記法ではなく、reSTに備わっているディレクティブという仕組みで定義された拡張です。 ちなみにディレクティブとは、思いっきり単純にいうと、「ドキュメントの構造を作り出す局所ローカルな記法」を定義するためにreSTに用意されている仕掛けです。
- reStructuredText Directives - List Table https://docutils.sourceforge.io/docs/ref/rst/directives.html#list-table
リストテーブルは、表(テーブル)を2階層の箇条書きで書けるという便利な記法です。 上記の公式ドキュメントにはこんな例が載っています。
.. list-table:: Frozen Delights! :widths: 15 10 30 :header-rows: 1 * - Treat - Quantity - Description * - Albatross - 2.99 - On a stick! * - Crunchy Frog - 1.49 - If we took the bones out, it wouldn't be crunchy, now would it? * - Gannet Ripple - 1.99 - On a stick!
上記のリストテーブルは、こんな感じのシンプルなテーブルとして最終的にレンダリングされることが想定されています。
Treat | Quantity | Description |
---|---|---|
Albatross | 2.99 | On a stick! |
Crunchy Frog | 1.49 | If we took the bones out, it wouldn't be crunchy, now would it? |
Gannet Ripple | 1.99 | On a stick! |
PandocのreST Readerでリストテーブルは読めるか
さて、PandocにはreSTのReaderもあります。
わりといろいろなディレクティブにも対応しているのですが、リストテーブルについては2017年5月ごろまで長らく未対応でした。
試しに当時のpandoc
コマンド(バージョン1.17.2がたまたま手元で利用可能でした)で上記の例を読み込んでみると、こんな感じに無視されてしまいます。
(pandoc
では、-t native
とすることで、Haskellの代数的データ型(をshow
したもの)の生の姿を見られます)
$ pandoc -f rst -t native temp.rst pandoc: ignoring unknown directive: list-table "source" (line 19, column 1) []
一方、最近のpandoc
コマンドでは、こんなふうにPandoc構造として読み取ってくれます。
$ pandoc -f rst -t native temp.rst [Table [Str "Frozen",Space,Str "Delights!"] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] [[Plain [Str "Treat"]] ,[Plain [Str "Quantity"]] ,[Plain [Str "Description"]]] [[[Plain [Str "Albatross"]] ,[Plain [Str "2.99"]] ,[Plain [Str "On",Space,Str "a",Space,Str "stick!"]]] ,[[Plain [Str "Crunchy",Space,Str "Frog"]] ,[Plain [Str "1.49"]] ,[Plain [Str "If",Space,Str "we",Space,Str "took",Space,Str "the",Space,Str "bones",Space,Str "out,",Space,Str "it",Space,Str "wouldn't",Space,Str "be",SoftBreak,Str "crunchy,",Space,Str "now",Space,Str "would",Space,Str "it?"]]] ,[[Plain [Str "Gannet",Space,Str "Ripple"]] ,[Plain [Str "1.99"]] ,[Plain [Str "On",Space,Str "a",Space,Str "stick!"]]]]]
しかし、これには実は制限があります。
リストテーブルでは、テーブルのヘッダとなる行の数を:header-rows:
で指定できるのですが、これを2
にしても、常に最初の1行だけがヘッダになります。
たとえば次のようなリストテーブルをPandocでMarkdownに変換してみると…、
.. list-table:: :widths: 15 10 30 :header-rows: 2 * - Treat - Quantity - Description * - Albatross - 2.99 - On a stick!
こうにしかなりません。
$ pandoc -f rst -t markdown temp.rst Treat Quantity Description --------------- ---------- ----------------------------------------------------- Albatross 2.99 On a stick!
Markdownにヘッダ行が複数のテーブルがないからだろ、と思うかもしれませんが、HTMLでも同じです。
$ pandoc -f rst -t html temp.rst <table> <thead> <tr class="header"> <th>Treat</th> <th>Quantity</th> <th>Description</th> </tr> </thead> <tbody> <tr class="odd"> <td>Albatross</td> <td>2.99</td> <td>On a stick!</td> </tr> </tbody> </table>
この連載をずっと読んでもらっていればわかると思いますが、これは「複数行ヘッダのテーブル」を表す型がPandoc構造にないからです。 どんなにがんばってReaderを実装しても、このリストテーブルをreSTの意図通りに読むこと、つまりDocutilsと同じように読むことはできないのです。
宣伝
最後に宣伝ですが、このPandocのreSTリストテーブル対応、ぼくがやりました。 何もコメントせずにいきなりPRを出したらjgmから瞬時に怒られが発生して青くなったのは懐かしい思い出です(最初はPRを出すつもりなかったけど操作ミスでこうなった)。
- RST Reader: parse list table directive #3688
https://github.com/jgm/pandoc/pull/3688
そもそもなんで実装したかといったら、『Goならわかるシステムプログラミング』の原稿がreSTで、アスキー.jp連載時はこれをPandocでHTMLにしてアスキーさんに入稿してたんですが、そのときにPandocがリストテーブル未対応で困ったからなのでした(なおSphinxを使わなかったのは、入稿仕様のHTMLにするPandocのテンプレートをすでに作っていて、それを使いまわしたかったからです)。
こんな感じに編集ツールを作るようなお仕事もできるので、そうしたお仕事があったらご連絡ください。