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 という便利なサイトがあって、下のコードを張ると、その場で構造解析を試すことができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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" };
}

こんな文章を食わすと

1
2
3
4
5
kaaaah

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
  "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