PEG.jsのすゝめ

木内智史之介(シャッチョー)
ミンカさんけっこんしてくださいおねがいします(ズザー SEGAさん、DIVAの筐体ください(ズザー

掲示板アプリでのコメントレンダリング問題

掲示板をアプリを作成していると、避けて通れない道の一つに、「コメントの描画」というものがあります。

単に、ユーザー入力をそのまま表示すればいいだけであれば、そんな簡単な事はないんですが、 当たり前ですが、色々要件はついてくるものです。

  • 改行したポイントで改行されるように
  • URLは自動的にリンクに
  • レス記法(»1)は自動的にリンクにして、クリックでそのコメントにジャンプ
  • 引用記法(> hogehoge)は、引用っぽく表示。あ、入れ子可能ね!

レス記法までは、単純な正規表現と要素の置き換えで実現可能ですが、 引用の入れ子まで来ると、文章を構造化して管理していく必要性が出てきて、 急激に難易度が上昇します。

markdownやwikiなどに完全に準拠できるんであれば markdown-itや、 showdownや、 markedなどを検討できるのですが、 レス記法という掲示板独特な記法の追加や、引用と段落のみの文法解釈でよかったりなどの小回りは、 どのライブラリもサポートされていません。

文章を構造的に解析するパーサーを自作しろってか?

と声を荒げたくなる気持ち、よくわかります。

そこで登場するのが「PEG」のアニキでやんす!

「アニキー!助けてー!」

What is PEG(ペグってなに?)

PEG (Parsing Expression Grammar) というのは、分かりやすく言うと「文章を解析するための言語」とでも言うのでしょうか? そんな僕の拙い解説よりも、大先輩の作成されたスライドを見るのが早いですね!

こちらのスライドは、非常にわかりやすくPEGを解説してくれております。 今回僕が作成したPEGも、こちらのスライド中に記載されている構文を基礎に、調整や拡張を行ったものです。

さあ、これが、そのPEGだ!(PEG処女作)

PEG Online という便利なサイトがあって、下のコードを張ると、その場で構造解析を試すことができます。

start = doc
doc = block:block+ {
return { type: "doc", contents: block};
}
block = blockquote / paragraph / blankline
blockquote = prefix:">"+ " " textline:textline {
return { type: "blockquote", contents: textline, level: prefix.length };
}
paragraph = textline:textline+ blankline? {
return { type: "paragraph", contents: textline };
}
textline = !blockquote inline:inline+ blankline? {
return inline;
}
reply = ">>" numbers:numbers {
return { type: "reply", to: numbers, contents: ">>"+numbers };
}
chars = char:char+ {
return { type: "chars" , contents: char.join("") };
}
inline = reply / chars
char = [^\n]
numbers = number:number+ { return number.join(""); }
number = [0-9]
blankline = [\n] {
return { type: "break" };
}

こんな文章を食わすと

kaaaah
>>1111 hohoho
> >>1234
>> ほげほげ

こんな結果を返却してくれる

{
"type": "doc",
"contents": [
{
"type": "paragraph",
"contents": [
[
{
"type": "chars",
"contents": "kaaaah"
}
]
]
},
{
"type": "paragraph",
"contents": [
[
{
"type": "reply",
"to": "1111",
"contents": ">>1111"
},
{
"type": "chars",
"contents": " hohoho"
}
]
]
},
{
"type": "blockquote",
"contents": [
{
"type": "reply",
"to": "1234",
"contents": ">>1234"
}
],
"level": 1
},
{
"type": "blockquote",
"contents": [
{
"type": "chars",
"contents": "ほげほげ"
}
],
"level": 2
}
]
}

オートリンクが未対応ですが、inline要素を増やして、リンクに関する解析を加えてあげるだけで対応できます。

github リポジトリも作っておきました

今回のpegファイルと、parser.jsを、リポジトリ上に配置しておきました。 単純な内容ですので、拡張は容易かと思います。

煮るなり焼くなり、お好きにどうぞ!

8823-scholar/topic-comment-peg