CamlP4 では OCaml の lexer 拡張はできない。じゃあどうするか。んでもって OCaml で俺の糞 Perl を駆逐したいという話。

CamlP4 は lexer も parser も定義することが出来て、パース結果を元に計算させたり、他の言語のプログラムテキストや Caml の abstract syntax tree に変換することが出来ます。
また、CamlP4 ではパースルールに名前をつけることができ、この名前を使うことで、既存パースルールを取り去って別のものに差し替えたり、新しいルールを作って、既存ルールのある場所に挿入することが出来ます。
CamlP4 ではさらに OCaml の文法を CamlP4 のパースルールとして再実装しています。このルール群をいろいろ改造していくことで OCaml の文法を拡張する事ができる。これがよくあるパーサ拡張 pa_XXX.ml (以下、P4拡張)です。pa_monad.ml とか。昨日も書いたように(http://d.hatena.ne.jp/camlspotter/20090813)ちょいと癖がありすぎますが基本的にはそんなに難しいもんじゃない。P4拡張同士が同じルールを変な風にいじっていなければ、複数のパーサ拡張を CamlP4 に読み込ませて OCaml 文法をどんどん強化していくことも可能です。
で、一昨日昨日とP4拡張でパーサじゃなくて lexer の拡張に挑んでいたのですが、、、

結果として、OCaml lexer の拡張は P4拡張としては実装できない。

ことが判りました。構造上 lexer 部分は固定なのです。
始めにも書きましたが、 CamlP4 では lexer も parser も定義することが出来ますから、 OCaml 文法定義をいろいろ使って、lexer 改造を施したパーサを定義することはできるのですが、、、これは全く新しい OCaml 用パーサルール群を別に定義していることになるだけで、P4拡張のように元のパーサルール群に手を入れていることにはなりません。この改造パーサとP4拡張を同時に使用してもP4拡張は改造パーサのルール群には手を出さない。まあ非常に全うなことではありますけれども。だからP4拡張と同時に使うことが出来ません。
じゃあどうするかって言うと、pre-process を二回やってやればなんとかなる。まあ workaround:

改造 lexer 付きパーサでパース => 普通の lexer で読めるコードに変換 => CamlP4 + P4拡張群 => 普通の OCaml で読めるソース

ただし、ocamlc は preprocess オプション -pp を複数個取れません。なので多段 preprocess が面倒くさい。と思ったら ppcompose と言うものがありました:

"The Whitespace Thing" for OCaml

http://people.csail.mit.edu/mikelin/ocaml+twt/

"The Whitespace Thing" is an alternative syntax for OCaml that uses indentation to group multi-line expressions, like Python and Haskell.

...

Some camlp4 extensions can be used with ocaml+twt, by applying camlp4 to the output of ocaml+twt. There is a utility, ppcompose, included in the distribution to assist with this (see the README file).

ご覧の通りこの "The Whitespace Thing" は OCaml にインデントベースのパースルールを入れるという凶悪な :-) 物です。どうぞ好きな方は使ってみてください。で、この拡張も lexer をいじらないとできませんから、P4拡張としては実装できないわけです。(というか P4 を使って実装してない)そうすると他の P4拡張と同時に使うためには、多段 preprocess が必要になって、そのためのツール ppcompose が同梱されています。

で、私が lexer を拡張して何をやりたいかというと、私は日常のスクリプティングperl でやるのですが、気が付くと数百行の perl スクリプトを書いてしまっていて気が滅入る。なんで OCaml で初めから書かなかったのか、と後悔することが良くあります。それには幾つか理由があるわけでそこんところを lexer や parser を改造することで対応したいと思っています。例えば、

$/.../             ===> Pcre.regexp "..."
$s/.../.../g       ===> Pcre の文字列置換関数
$`....`            ===> shell 呼び出し
$"....${hello}..." ===> sprintf "....%s..." hello
x${y}              ===> Hashtbl.find x y
x${y} <- z         ===> Hashtbl.replace x y z

初めの三つ以外は別に lexer 拡張いらないし、他の人が同じようなものを作っているのですけれど、どうせなら統一感 ($) を出したいと思って。あと、$ ってやっぱ perlish というか、scriptish な感じ、でしょ?上のが出切る様になったら、随分と OCamlスクリプティングが楽になると、思うんです。どうでしょー。