OCamlでevalして結果をstringで得る
こんにちは、何者でもなくなりました。
さて、今はOCamlとLaTeXしか書いてないという状況に陥りました。人生にありがちな時期ですね。
1. OCamlでもeval
がしたい!
ではこちらをどうぞ。
以下引用(upop上で動くよ)。
#require "compiler-libs" (* Assuming you're using utop, if compiling then this is the package you need *)
let eval code =
let as_buf = Lexing.from_string code in
let parsed = !Toploop.parse_toplevel_phrase as_buf in
ignore (Toploop.execute_phrase true Format.std_formatter parsed)
compiler-libs
というOCamlに同梱されているライブラリーを使いますと、このようにevaれるわけですね。
2. eval
の結果を文字列で取りたい!
evaってるのはToploop.execute_phrase
で、戻り値はランタイムエラーしたかそうでないかのbool
になります。
上記ではignore
で捨ててunit
にしてますね。
例えば、eval "int_of_char 'a';;"
なんてしたときに戻り値が"97"
というstring
だと嬉しい人がいるかもしれない。
俺なら実装やってくだけだな
3. 出力先
format
型の話をしよう。
OCaml 標準ライブラリ探訪 #3.0: Printf: 便利だけどいろいろ謎のある奴 - Oh, you `re no (fun _ → more)
OCamlのformat (型安全なprintf/scanf) の仕組み - 簡潔なQ
話おわり
4. 解
evalのToploop.execute_phrase
に渡している引数を観察すると、
true
とFormat.std_formatter
を渡していることが分かる。
true
は後述。第2引数は件のformat
じゃねえかい。
このformatの出力先をstring ref
みたいなところに出してその中身を返せば行けそうだ。
でもどうやって? まずはFormat.std_formatter
がどうなってるかを見てみよう。
stdlib/format.ml#L1038で定義されている。
formatter_of_out_channel
が何者か辿ってみると、
let formatter_of_out_channel oc =
make_formatter (output_substring oc) (fun () -> flush oc)
了解!
このmake_formatter
って使えそうだなと思って定義を見てみるが全くわからない。
幸運にも、formatter_of_out_channel
の真下にわかりやすい例がある。
let formatter_of_buffer b =
make_formatter (Buffer.add_substring b) ignore
Buffer.add_substring
とignore
という、わかりやすい関数で構成されている。
これなら型がたちどころに分かるな。
Module Buffer - OCaml.jp
を見るとsubstringしてバッファーにくっつける感じですね。
そしてバッファーb
を部分適用しているので、なるほどmake_formatter
に渡しているのはstring -> int -> int -> unit
とunit -> unit
か。
もう見えてきましたね。substringをstring ref
などに書いて参照すればOK。
あとはやるだけ。
let records = ref "" (* 書き出す string ref *)
let ref_b rs = fun s i j ->
let subs = String.sub s i j in
rs := !rs ^ subs
let fmt = Format.make_formatter (ref_b records) ignore (* 新しいフォーマッター *)
let eval code =
let as_buf = Lexing.from_string code in
let parsed = !Toploop.parse_toplevel_phrase as_buf in
let () = Toploop.execute_phrase true fmt parsed |> ignore in
let ret = !records in
records := ""; ret
はい実行
utop # eval "int_of_char 'a';;";;
- : string = "- : int = 97\n"
ありがたい! 型名まで付いている!!! いらない!!!!!
おわりだよ〜
その前にToploop.execute_phrase
に渡しているbool
は、実行結果をフォーマッターに渡すか否かですはい解散
追記20171118
こんなことしなくてもFormat.str_formatter
とFormat.flush_str_formatter
という便利なものがあってじゃな…。
『ブレードランナー 2049』観に行きましたが全部最高でした。
『ブレードランナー』の続編としての立ち位置、ストーリー、BGM、ハリソン・フォード…全て…。