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、ハリソン・フォード…全て…。
投稿されたコメントはCC BY 4.0ライセンスの下で公開されます。