読者です 読者をやめる 読者になる 読者になる

WikiForme - 自分用Wiki記法パーサ

はてな記法PukiWiki記法、tDiary記法などなど、世の中「なんとか記法」が溢れているわけですが、往々にして「自分にぴったり合う記法なんてどこにも無い!」という結論に達する場合が多く、結果として「なんとか記法」の乱立を生んでいるのではないでしょうか。


というわけで、自分専用のWiki記法を簡単に作れるカスタマイザブルパーサ WikiForme を作ってみました。乱立乱立!
記法を統一しようなんてムリですよね。もはや宗教論争です。自分専用の記法があればいいんです。


※2007/09/23: バージョン 0.3をリリースしました > WikiForme 0.3 リリース! - 構造化Wiki記法パーサ
※2007/09/13: バージョン 0.2をリリースしました > WikiForme 0.2 - 構造化Wiki記法パーサ
※2007/08/08: バージョン 0.1をリリースしました > WikiForme - 自分用Wiki記法パーサ バージョン0.1!
wikiforme-0.0.1.tar.gz
まだβバージョンですが、いじり倒してください。test.rbを実行してみると、test.txtに書かれたWiki風記法がHTMLに変換されます。



誰しも一度は自分専用のWiki風記法を作ってみようと思うわけですが、やれインライン要素だ、ブロック要素だ、表組みはどうする?などなど、なかなかややこしいものです。結局HTMLを手で打ったり、微妙に気に入らない記法で我慢する日々を過ごしているはず。WikiFormeを使えば、自分専用のWiki風記法を作れます。

特にWikiにこだわってHTMLを出力する必要はなく、XMLを出力したり、他のWikiの記法やTeXなどを出力しても良いでしょう。(…TeXは便利かもしれませんね) XMLに出力できると言うことは、プログラムの設定ファイルをWiki風記法で記述してもらう→XMLに変換してからロードなどなど、様々な使い方ができます。

WikiFormeの特徴は、入れ子構造を作れる点です。一般的な記法では、「章」や「節」と言った入れ子構造を作ることができませんでした(HTMLを出力する分には問題ないのですが)。WIkiFormeでは、章の中に節を入れたり、節の中に段落を入れたりできます。

たとえば以下のようなWiki風記法を、

*WikiForme!
**Rubyです
-Rubyで書かれています
-Rubyで記法を定義します
**入れ子にできます
閉じタグは要りません。
*外に出ます
[インライン要素>http://viver.sourceforge.jp/]も''定義''できます。

以下のような形式に変換できます。

<section title="WikiForme!">

  <subsection title="Rubyです">
    <list>
      <item>Rubyで書かれています</item>
      <item>Rubyで記法を定義します</item>
    </list>
  </subsection>

  <subsection name="入れ子にできます">
    <paragraph>閉じタグは要りません。</paragraph>
  </subsection>

</section>

<section name="外に出ます">
<a href="http://viver.sourceforge.jp/">インライン要素</a><strong>定義</strong>できます。
</section>

記法はRubyで定義します。一つの要素が一つのクラスに対応します。たとえば上のような記法を実現するためには、以下のようなモジュールを作ります。

module MyFormat
  class Section
    block_element "section"
    def containable?(instance)
      [Subsection, BlankLine]    # 包含可能要素
    end
    def process
      body = "<section title=\"#{@arg}\">"
      body << @children.process
      body << "</section>"
      return body
    end
  end

  class List1 < BlockTemplate::RecursiveListClass("list", "item", "")
    def block_element "list"
    def containable?(instance)
      [List2]    # 包含可能要素
    end
    def combinable?(instance)
      [List1]    # 結合可能要素
    end
  end

  # …似たような感じでSubsection、Listを作る

  class MyFormat
    block_element "myformat"
    def process
      @children.process
    end
  end

  Sugar = {    # シンタックスシュガー
    "*" => Section,
    "**" => Subsection,
    "-" => List1,
    "--" => List2,
  }
  Document = MyFormat

  # 詳しくはソースコードと同梱のサンプルを
end

本当はすべての要素にprocessメソッドを定義しないといけないのですが、ListやTableはよく使う(そして複雑)ので、テンプレートを用意してあります。継承するだけで使えます。


WikiFormeは、「包含可能要素」(containable?メソッド)と「結合可能要素」(combinable?メソッド)で文章構造を作ります。そして楽をするために、「シンタックスシュガー」を投入します。


パースの仕組みは単純で、包含可能であれば前の要素の子にし、包含可能でなければ前の要素を閉じます。同様に結合可能であれば前の要素と結合し、結合可能でなければ前の要素を閉じます。先の例では、SubsectionがSectionの子になっており、List1とList1が結合しています。

文章構造が完成すると、最も親(ドキュメント自体)のprocessメソッド(ここではMyFormat::process)が呼ばれます。あとは、その通りです。子の要素が変数@childrenに、結合した要素が@combinationに、テキストが@argに入っています。



ここまででは、実は↓このように書かないといけません。

\section WikiForme!
\subsection Rubyです
\list Rubyで書かれています
\list Rubyで記法を定義します
\subsection 入れ子にできます
閉じタグは要りません。

これでは面倒なので、シンタックスシュガーを割り当てて、簡単に書けるようにします。これで先の例のようになるわけです。


インライン要素もほぼ同様です。基本的な記法は、

&key{argarg}

で、シンタックスシュガーには「開始マーク」と「終了マーク」を指定します。たとえば開始マークを「[ [」、終了マークを「] ]」にしておけば、

[[WikiForme!]]

と書けます。


他にも気分次第でいろいろ。パースはWikiFormeに任せて、自分は間の文字列をどう料理するかを考えるだけでOKです。

[id: ←開始マーク id記法っぽい! 終了マーク→ ]
''開始マークと終了マークが同じでも可''
<em>←開始マーク HTMLっぽい 終了マーク→</em>


続きはソースコードで。
wikiforme-0.0.1.tar.gz