Camlp4 で構文拡張 [OCaml]
OCaml 用の COM オートメーションライブラリを作り始めたという記事 [1] の中で Camlp4 を使って構文を書き換えればもっと綺麗な書き方ができるかもしれないと書いた。
その後 Camlp4 が理解できていないのは相変わらずなのだけど、見よう見まねと試行錯誤の結果、部分的にそれらしいことができるようになった。
ちなみにその見よう見まねは Martin Jambon さんの解説 [2] を読んでがんばった。この解説は公式のチュートリアルよりかなり分かりやすいと思う。(というか公式の方は私の頭ではまったく理解できない。何か理解するなと言っているようなオーラを感じる)。なお Campl4 は次リリースで下位互換性がなくなるような変更が予定されていて、今 Camlp4 を新たに勉強しようというのはかなり微妙な状況にあるようだ。
今回書いてみたのは前回の記事で挙げた問題点の (1) と (3) への対策。こういう構文が使えるようにしたい(このコードのファイル名は prog.ml とする)。
let get obj prop =
Printf.printf "get %s\n" prop
let set obj prop value =
Printf.printf "set %s=%s\n" prop value
let _ =
let obj = 0 in (* dummy *)
obj->Hello->World;
(* get (get obj "Hello") "World" *)
obj->member1->property = "somevalue"
(* set (get obj "member1") "property" "somevalue" *)
単純化のために get 関数と set 関数はダミーのものを用意した。obj 変数もダミーで、特に意味はない。
ゴールは「obj-> ~」という部分がそれぞれ下の行のコメントの形に置き換えられるようにすることだ。
そのための Camlp4 のコードを以下のように書いた(ファイル名は pa_odmacro.ml とする)
let propget loc obj prop =
<:expr<get $obj$ $str:prop$>>
let propset loc obj prop value =
<:expr<set $obj$ $str:prop$ $value$>>
EXTEND
Pcaml.expr: LEVEL "expr1" [
[ obj = Pcaml.expr ; "->" ; prop = UIDENT -> propget loc obj prop
| obj = Pcaml.expr ; "->" ; prop = LIDENT -> propget loc obj prop ]
];
END
EXTEND
Pcaml.expr: LEVEL "expr1" [
[ obj = Pcaml.expr ; "->" ; prop = UIDENT; "="; value = Pcaml.expr -> propset loc obj prop value
| obj = Pcaml.expr ; "->" ; prop = LIDENT; "="; value = Pcaml.expr -> propset loc obj prop value ]
];
END
これを以下の Makefile を使ってメイクする。
all:
ocamlc -c -I +camlp4 -pp "camlp4o pa_extend.cmo q_MLast.cmo -loc loc" pa_odmacro.ml
camlp4o -I . pr_o.cmo pa_odmacro.cmo prog.ml -o prog.ppo
ocamlopt -o prog -pp "camlp4o -I . pa_odmacro.cmo" prog.ml
出来上がった prog.exe の実行結果は以下のとおり。
F:\odmacro>prog get Hello get World get member1 set property=somevalue
Makefile の2行目では prog.ml をプリプロセスした結果の OCaml コードを prog.ppo というファイル名で出力している。これを覗いてみると…
let get obj prop = Printf.printf "get %s\n" prop
let set obj prop value = Printf.printf "set %s=%s\n" prop value
let _ =
let obj = 0 in
get (get obj "Hello") "World";
set (get obj "member1") "property" "somevalue"
(* set (get obj "member1") "property" "somevalue" *)
よかった。期待通りの結果が出ている。
今回の pa_odmacro.ml はとりあえず動いたという程度なのでどういうことをしているのかというのを事細かに説明することができない。
特に分かっていないこと:
・LEVEL "expr1" というときの "expr1" というのは本当は他にもあって状況に応じて適切なものを選定すべきなのだろうが、何があってそれぞれどういう意味を持つのか理解していない
・今回「->」という記号を使ったが、既存の OCaml 文法の「->」の使用とぶつかるかどうか検証していない(そういう場合 Camlp4 は警告とか出してくれるのだろうか?)
・(上記のコードとはあまり関係ないが)何故 Camlp4 ではわざわざ revised syntax などというものが使われているのかわからない
[1] http://blog.so-net.ne.jp/rainyday/2006-08-16
[2] http://martin.jambon.free.fr/extend-ocaml-syntax.html
コメント 0