逆ポーランド記法を解釈する Camlp4 プリプロセッサ [OCaml]
最近 Camlp4 を本腰入れて理解しようとがんばっている。
今日は逆ポーランド記法を OCaml の構文木に変換するプリプロセッサを作ってみた。
Camlp4 は基本的には OCaml コードを構文木に変換する関数を Pcaml.parse_implem に定義しているのだけど、それを書き換えれば任意の言語を入力にすることができる。その関数は char Stream.t を読んで構文木を返すことができればいい。
(* ocamlc -c -I +camlp4 -pp "camlp4o q_MLast.cmo" pa_rpn.ml *)
let _ =
Pcaml.parse_implem := function strm ->
let _loc = Token.dummy_loc in
let stack = Stack.create () in
let rec process stack strm =
match (Stream.peek strm) with
| Some ('0'..'9')
-> let c = String.make 1 (Stream.next strm) in
Stack.push <:expr<$int:c$>> stack;
process stack strm
| Some ('+')
-> Stream.junk strm;
let x = Stack.pop stack and y = Stack.pop stack in
Stack.push <:expr< $y$ + $x$ >> stack;
process stack strm
| Some ('-')
-> Stream.junk strm;
let x = Stack.pop stack and y = Stack.pop stack in
Stack.push <:expr< $y$ - $x$ >> stack;
process stack strm
| Some ('*')
-> Stream.junk strm;
let x = Stack.pop stack and y = Stack.pop stack in
Stack.push <:expr< $y$ * $x$ >> stack;
process stack strm
| Some ('/')
-> Stream.junk strm;
let x = Stack.pop stack and y = Stack.pop stack in
Stack.push <:expr< $y$ / $x$ >> stack;
process stack strm
| Some _
-> raise (Failure "unknown char")
| None
-> let e = Stack.pop stack in
<:str_item< print_int $e$ >>
in
[(process stack strm), _loc], false;
これを以下のようにコンパイルして
ocamlc -c -I +camlp4 -pp "camlp4o q_MLast.cmo" pa_rpn.ml
こんな感じで使う。
F:\>type rpn.ml
12-3*
F:\>ocamlc -pp "camlp4o ./pa_rpn.cmo" rpn.ml
F:\>camlprog
-3
F:\>
OCaml ソースコード上は print_int ((1 - 2) * 3)
に相当する構文木に変換されるのだが pr_o.cmo で見た場合は何故か元のソースがそのまま最後にくっついてしまう。
F:\>camlp4o ./pa_rpn.cmo pr_o.cmo rpn.ml
let _ = print_int ((1 - 2) * 3)12-3*
F:\>
これはどうやら pr_o.cmo のバグで、 Pcaml.parse_implem を直接書き換えるとおかしくなるみたいだ。Matin Jambon 氏のページ [1] でもそのような現象について記載されている。
Camlp4 を勉強しながらその成果をチュートリアルみたいなものとして残そうとしているのだけど、3.09版でやっているので、下方互換性のない3.10がいずれ正式になることと思うと未来のない作業かもしれない。最終的には文章を書く練習だと考えればいいとも思っているが。
[1] http://martin.jambon.free.fr/extend-ocaml-syntax.html#inserting-bof
コメント 0