新しい構造化Wiki記法パーサーの設計案
プラグインの記述
プラグイン=要素の記述。要素はRubyのクラス。このクラスが持つクラス変数(か定数):
- 開始マーク
- 終了マーク(省略可)
- XMLを生成するプログラム(メソッド)
- 包含可能な要素またはグループ(無ければ省略)
- 所属するグループ(無ければ省略可)
- どこにも含められなかったときに補完する親要素(無ければ省略可)
「見出し」とか基本的な要素は開始マークと包含可能要素だけ書けばいいので簡単。「段落」要素とかその他一切を@contentsグループとかに属させておいて、「見出し」の包含可能な要素には:@contentsと書いておく。
「太字」とかインライン要素は、開始マークと終了マークを書いて、@inlineグループに属しておく。包含可能要素は@inline - [self]としておく。良くあるパターンだから、ちょっと宣言が書いてあったら全自動でやってしまえばいい。
ちょぃと小粋なことがやりたくなったら、ブロック要素で終了マークを使ってみたり、親要素とか使う。
パースと自動構造化
文脈(その文脈で利用できる要素の集合)の中から開始マークを探す。どの要素にもマッチしなかったら:text要素。
マッチしたら、その要素をスタックに積む。そして:
- その要素に終了マークが定義されていたら、文脈を、その要素が包含可能な要素のセットに切り替える
- 文脈はスタック。この要素がpopされたら文脈も元に戻る
- 「太字」の中で「見出し」にマッチしたり、「整形済みテキスト」の中で「太字」にマッチしたりするのを防げる
文脈の中から開始マークか、スタックのトップの要素の終了マークを探す(←コレ文章が終わるまでループ)
開始マークにマッチしたら、その要素=Tをスタックに積む。このとき自動構造化:
- スタックのトップの要素=Aが包含可能な要素のセットの中にTが含まれていたら、TをAの子要素とし、Tをスタックに積む
- 含まれていなかったら、Aをpopしてスタックの次の要素Bについて繰り返す
- popするとき文脈もpopする
- popする要素に終了マークが定義されていたらエラー。終了マークが定義されている要素は終了マークによってのみpopされなければならない
- スタックが空になったら:
- Tに親要素が定義されていたら、スタックをpopを始める前の状態に戻し、Tの親要素を生成して補完する。T_pending.push(T); T = T.parent.new
- Tに親要素が定義されていなかったら、エラー
開始マークではなくスタックのトップの要素の終了マークにマッチしたら、単にスタックをpopする。
開始要素を探すのはStringScannerで。文脈は、開始マークのセットとスタックのトップの要素の終了マークをRegexp#unionしてキャッシュ。一度使った正規表現は使い回す。速度は鬼車に依存!
要注目: