Clojure の パーサコンビネータライブラリ Kern
Clojure でパースするとき、みんなどうしてるんだろう。 Parsec のようなものがあればいいのに〜、と思って探してみたところ、この Kern が一番よさげだった。
特徴
- 状態モナドベースのコンビネータ群
- C, Java, Haskell, Shell の構文をサポート
- パースと式の評価をサポート
- 正確で詳しいエラーメッセージ
- エラーメッセージは簡単に多言語化可能
- パーサの内部状態にクライアントコードからアクセス可能
- サンプルあり
セットアップ
leiningen がインストールされているなら、 project.clj の :dependencies に
[org.blancas/kern "0.6.1"]
で、コード中に
(use 'blancas.kern.core)
Haskell の Parsec っぽい
HaskellのParsecですねという感じ。というか記号とかもそのまんま。 ただちょいちょい違うところがあるかなあという印象。
例えば数字のパース。
doc.core> (run digit "123") \1 nil doc.core> (run (many digit) "123") [\1 \2 \3] nil
run はパース結果を印字する。パースしてない残りの入力はそのまま。パーサの状態は run* で見れる。
doc.core> (run* digit "123") {:input (\2 \3), :pos {:src "", :line 1, :col 2}, :value \1, :ok true, :empty false, :user nil, :error nil} nil
文字列以外にも入力としてファイルとかも受け付ける。 runf は パーサとファイル名、文字コードを受け取ってパースする。
コンビネータ <*> は与えられたパーサを順に実行し結果をベクタにして返す。
doc.core> (run (<*> letter letter digit) "os8") [\o \s \8] nil
parse は run みたいな動作をするけれどパーサの状態を返すという点で異なる。 :ok フィールドをチェックすればパースに成功したかどうか分かる。 :value セレクタを用いればパーサの結果が得られる。
doc.core> (let [st (parse (token* "abc") "abc")] (when (:ok st) (str "got " (:value st)))) "got abc"