markdown のサブセット mini-mark のパーサを実装した話。
パーサを実装といっても、PEGで表記したものを parboiled で実装しただけです。
PEGという文法があり、これを定義しておけばパーサになる(ただしPEGを解釈して実際のパーサに変換してくれる何かしらのツール等が必要ですが)という世界らしい。(よくわかっていません。) ここでは、PEGの Java 実装の一つであるらしい parboiled を使ってパーサを実装してみます。
こんな感じのマークアップされたテキストをパースしたい。 説明の都合上これを mini-mark と呼びます。
# h1 header
This is a paragraph.
## h2 header
This is another paragraph.
厳密な記述ではないですが、だいたいこんな感じでしょうか。
Doc      ← Block*
Block    ← NewLine / HeadLine / ParaLine
HeadLine ← '#'+ Spaces ParaLine
ParaLine ← Inline NewLine
Inline   ← \n以外のすべての文字*
NewLine  ← '\n'
Spaces   ← ' '+
以下を念頭にPEG表記をparboiledに変換します。
記述方法の詳細情報はこちら
https://github.com/sirthias/parboiled/wiki/Rule-Construction-in-Java
完成したコードは以下の通り。 PEG表記と比べて冗長さは増えましたが、ほぼそのまま機械的に置きかえるだけで済むのは素敵です。
MiniMarkParser.groovy
@BuildParseTree 
class MiniMarkParser extends BaseParser<Object> {
    Rule Doc() {
        ZeroOrMore(Block())
    }
    Rule Block(){
        FirstOf( NewLine(), HeadLine(), ParaLine() )
    }
    Rule HeadLine(){
        Sequence( OneOrMore('#'), Spaces(), ParaLine() )
    }
    Rule ParaLine(){
        Sequence( Inline(), NewLine() )
    }
    Rule Inline(){
        ZeroOrMore(NoneOf('\n'))
    }
    Rule NewLine(){
        String('\n')
    }
    Rule Spaces(){
        OneOrMore(' ')
    }
}
パース対象となる文字列の文法だけ定義して、あとは機械的な作業でパーサーの実装がつくることができれば、実装が楽なだけでなく、保守の面から考えてもとても魅力的です。