Perlで良い感じに使えるMarkdownをパースする方法とライブラリについて

この記事では、Perlでマークダウンをパースする方法について解説していく。

PerlではMarkdownをパースするのに良い感じのライブラリがいくつかある。

Text::Markdown::DiscountDiscountというC言語実装のMarkdown parserがもとになったC拡張ライブラリ。 一方で、Text::MultiMarkdownはpure perl実装になっている。

C拡張ライブラリだとWindowsで上手くビルドできない場合が多々あるので、今回はpure perl実装である Text::MultiMarkdownを使っていく。

Text::MultiMarkdownの使い方

まずはライブラリをインストールする。(cpmを使う)

cpm install Text::MultiMarkdown

そしてコードを書いていく。使い方はいたってシンプルでよい。

use v5.38;
use Text::MultiMarkdown;

my $text = <<TEXT;
# こんにちは

これがマークダウンです。

- たろう
- はなこ
- さとし

TEXT

my $m = Text::MultiMarkdown->new;
my $html = $m->markdown($text);

Text::MultiMarkdownを拡張していく。

しかし、素のText::MultiMarkdownでは色々と機能が足りておらず、自分で拡張していくのがよい。

例えば、 ``` で囲んだからコードをかけるcode fence blockがあると便利だが、その機能は用意されていない。

そこでText::MultiMarkdown::Extendedというクラスを自作して拡張していくとよい。

package Text::MultiMarkdown::Extended {
    use strict;
    use warnings;
    use utf8;
    use parent 'Text::MultiMarkdown';

    sub _RunBlockGamut {
        my ( $self, $text ) = @_;
        $text = $self->_DoCodeFenceBlocks($text);      # 先に code fence block を処理
        $text = $self->SUPER::_RunBlockGamut($text);
        return $text;
    }

    sub _DoCodeFenceBlocks {
        my ( $self, $text ) = @_;

        $text =~ s{
        (?:\n|\A)             # 行頭または文書の先頭
        (`{3,})               # $1 = 開始バッククォート(3つ以上)
        ([^\n]*)\n            # $2 = 言語指定(オプション)
        (.*?)                 # $3 = コード本体(非貪欲マッチ)
        \n\1                  # 終了バッククォート(開始と同じ数)
        (?:\n|\Z)             # 行末または文書の終端
    }{
        my $fence = $1;
        my $lang = $2;
        my $code = $3;
        my $class_attr = $lang ? " class=\"language-$lang\"" : "";
        my $encoded_code = $self->_EncodeCode($code);
        "\n\n<pre><code$class_attr>$encoded_code\n</code></pre>\n\n";
    }egsx;

        return $text;
    }
};

これでcode fence blockが使えるようになった。使い方は先ほどと同じようにつかえる。

use Text::MultiMarkdown::Extended;

my $m = Text::MultiMarkdown::Extended->new;
my $html = $m->markdown($text);

参考文献