CamlP4 をちょっと改造してこんなものを作ったよ

CamlP4 をほんの少し改造して、pa_*.cmo モジュールから OCaml の lexer を取り替えられるようにしました。
http://jun.furuse.info/hacks/camlp4-lexer-plugin-patch
このパッチを当てた CamlP4 を使うと、Gram.set_from_lexbuf という関数で lexer の from_lexbuf 関数を差し替えられるようになります。ってことはどういうことかというと、今まで pa_*.cmo では不可能だった lexer 変更によるオレオ特殊記法の導入が可能になった、ということです。オリジナルの CamlP4 に対し完全上位互換ですし、どうせ P4 はプリプロセッサなので、コンパイラの改造でもないから、id:camlspotter が書いた適当な改造コードだけど、安心です。

連休中はこの改造 CamlP4 を使っていろいろと遊んでいました。こんなことが出来るようになりました。

$/foo\(\)/   (* == Pcre.regexp "foo\\(\\)" : Perl の regexp がそのまま書けます。面倒な \ のエスケープがいりません。 *)

$case string
| $/regexp/ -> ... 
| $/(hello|bye) (world|japan)\n/ as x -> x#_1, x#_2 (* matched group をメソッドで取り出せます *)
| $/...(?P<name>...).../ as x -> x#_name (* named group でも引けます *)
| _ -> ... (* no match *)
;;

$"File $filename is not found"  (* == Printf.sprintf "File %s is not found" filename *)
$"The size is %${get_size ()}d" (* == Printf.sprintf "The size is %d" (get_size ()) *)

$`mkdir hogehoge`;;   (* mkdir hogehoge を subshell で実行。出力を受け取る *)

tbl${x}       (* == Hashtbl.find tbl x *)
tbl${x} <- y  (* == Hashtbl.replace tbl x y *)
tbl$?{x}      (* == Hashtbl.mem tbl x *)

こんな感じ。ついに今までの Perl 資産を OCaml に気軽に移植できそうです。ただ、OCaml のコードの見た目がすごく $$(ダラダラ)して Perlish になりますが、、、

もうちょっと実際に使ってみてから公開したいと思います。もし、Perl のこんな機能というか糞記法が OCaml でも書ければ糞でいいのに、というアイデアがあったら教えてください。

追記

Regexp match に関しては、ずいぶん前に大岩さんが Regexp/OCaml (http://www.oiwa.jp/~yutaka/caml/index-en.html) という同じような CamlP4 extension を出しておられます。Twitter では一応つぶやいていたんですがここでは忘れてました。ごめんなさい。Regexp/OCaml との違いは、

  • \-metacharacter の \ をエスケープしなくていいところ
  • object type を使って group の内容が型で見えるところ
  • 型変換とかは細かい所はやってない

とか、そんな所かなあ。

関連

なぜ CamlP4 にパッチが必要かについてと、このハックの動機についてはこちらを。
http://d.hatena.ne.jp/camlspotter/20090815/1250303784