2012/6/19 SASADA Koichi <ko1 / atdot.net>:
>  ささだです.
>
> (2012/06/15 22:02), Yugui wrote:
>> コンパイルコストが本当に必要になるケースがはっきりしてくるまでは、まずはそれで良いかもしれません。
>> じゃ、ruby_compile_main_from_string と ruby_eval_main は削除しましょう。
>>
>> ここで案が2つあります。
>> 1. toplevel_binding と rb_f_evalを公開する
>> 2. さらに両者を合わせてruby_eval_in_main みたいなのを作る。
>>
>> どっちが良いと思います?
>> ちなみに、いずれにしてもさらにファイル版は必要だと思います。というのはマジックコメントとか読んで
>> 適切なエンコーディングを付けたりってのは結構面倒なので。
>> で、そのエンコーディングを付ける話を始めるとまたencdet話に跳んでしまうので、
>> * rb_eval_string_in_main
>> * rb_eval_file_in_main
>> を作るのが現実的かなと思ってます。
>
>  返事が遅くなってすみません.長いメールになりましたが,ちゃんと議論しよ
> うとすると,こういうふうにまとめないといかんと思って,それで面倒がって遅
> れておりました.すみません.
>
>  中田さんに協力してもらって,現在,Ruby を組み込んだアプリから Ruby プ
> ログラムを実行する方法をまとめました.下記に示すように意外とある,という
> か,あまり一貫性がなく追加しているので,あまり良くないなぁと,思っている
> というのがありまして,なので慎重になっている次第です.
>
>
>  プログラムを組み込んだアプリから指定して実行する,という時,気にしない
> といけない点がいくつかあるかと思います.次の 5 項目としてまとめました.
>
> (a) プログラムの文字列はどうやって渡すか?
> (b) トップレベルの self はどうなるか?
> (c) 実行バインディングはどうなるか?
> (d) __FILE__ はどうなるか?
> (e) エンコーディングはどうなるか?
> (f) エラーが起きたらどうなるか?(構文エラー,実行時エラー)
>
>
> (1) ruby_options() を利用(-r,-e,ファイル名を指定,標準入力から入力)
>
>  ruby(1) と同じ利用感が得られます.
>
>  -e は,エンコーディングは下記のようになるようです.
>
> http://jp.rubyist.net/magazine/?0025-Ruby19_m17n
>> なお、標準入力から読み込んだスクリプトや、コマンドラインオプション -e
> で与えたスクリプトの場合は、magic comment がなかった場合、ロケールが
> script encoding として用いられます。このため、1 行スクリプトを書く場合に
> までいちいち magic comment を書く必要はありません。
>
>  プログラムの __FILE__ を好き勝手に指定する方法はありません(勝手に決ま
> ります).-r,ファイル名指定では,それぞれ読み込んだファイルの名前,-e
> では,"-e" になります.
>
> まとめますと,
>
> (a) プログラムの文字列はどうやって渡すか?
>
>  -r:ファイル名でファイルを指定(ファイルの中身がプログラム)
>  -e:実行する文字列を C 文字列で指定
>  ファイル名:ファイル名でファイルを指定(ファイルの中身がプログラム)
>  標準入力から:標準入力から読む
>
> (b) トップレベルの self はどうなるか?
>
>  main になる(ruby -e self で得られるオブジェクトを,ここでは main と呼
> ぶことにします).
>
> (c) 実行バインディングはどうなるか?
>
> -r:新しいバインディングを作って実行します.
>  例えば,ruby -rfoo -e 'p a' とあって,foo.rb に a=1 とあったとして
> も,-r と -e は別々の環境を指しているので -e で a が見つからないというエ
> ラーになります.
>
> それ以外:
>
>  TOPLEVEL_BINDING になります.
>
>  例えば,ruby -e 'a=1' -e 'p a' とすると,プログラムが 2 回実行されます
> が,同じバインディングを共有しているため,問題無く2回目の実行で 1 が出力
> されます.
>
> (d) __FILE__ はどうなるか?
>
>  下記に固定されます.
>
>  -r:指定されたファイル名
>  -e:"-e"
>  ファイル名:ファイル名
>  標準入力から:"-"
>
> (e) エンコーディングはどうなるか?
>
>  マジックコメントを見て判断.ただし,下記の例外があり.
>
> http://jp.rubyist.net/magazine/?0025-Ruby19_m17n
>> なお、標準入力から読み込んだスクリプトや、コマンドラインオプション -e
> で与えたスクリプトの場合は、magic comment がなかった場合、ロケールが
> script encoding として用いられます。このため、1 行スクリプトを書く場合に
> までいちいち magic comment を書く必要はありません。
>
> (f) エラーが起きたらどうなるか?(構文エラー,実行時エラー)
>
>  構文エラー,実行時エラー,ともにエラーが起きたらバックトレースを標準エ
> ラー出力に表示して終了します.
>
>
> (2) VALUE rb_eval_string(const char *str)
>    VALUE rb_eval_string_protect(const char *str, int *state)
>    VALUE rb_eval_string_wrap(const char *str, int *state)
>
>  eval という名前ですが,意に反して,実は Kernel.eval ではありません.判
> りづらいですね.これがまさに,Ruby を組み込んだアプリから使うことを意識
> して作ったインターフェースです.多分.
>
>  str で指定された C 文字列を実行します.-e に似ています.ただし,実行コ
> ンテキストは TOPLEVEL_BINDING ではありません.self は main  です.これ
> は,require された時のコンテキストと同じです.
>
>  __FILE__ は "(eval)" 固定です.
>
>  protect は,例外が起きたとき,外に例外が伝搬しません.state にどういう
> 実行状態で止まったか,ということが入ります.
>
>  wrap は,protect の機能に加えて,load で第2引数を true にしたときに
> toplevel に匿名 module が指定されますが,それと同じ挙動を行います.
>
>  実は,yugui さんが欲しいのは,__FILE__ が指定出来ない,という点以外は
> これなんじゃないかと思うのですが,どうでしょうか.TOPLEVEL_BINDING で実
> 行したい,というのは,本当に必要な要件なんでしょうか.

なるほど。当面__FILE__の問題だけなんとかなれば要件としては十分そうです。

IRCでも話したように、これまでの議論と笹田さんの案から、
* ruby_opaque_t を void*に戻す

* ruby_compile_main_from_file
* ruby_compile_main_from_string
* ruby_eval_main
を削除

* rb_eval_string_with_filename(VALUE string, VALUE fname);
* rb_eval_string_with_filename_protect(VALUE string, VALUE fname, int* state);
を追加、

という提案をします。異論が無ければあとでコミットしますが、特に追加する関数の名前について何かご意見(もっと良い名前)は無いでしょうか。

>
>
>  まとめます.
>
> (a) プログラムの文字列はどうやって渡すか?
>
>  C 文字列で渡します.
>
> (b) トップレベルの self はどうなるか?
>
>  main になります.
>
> (c) 実行バインディングはどうなるか?
>
>  新しい binding を作って実行します.require で新しく作られるバインディ
> ングと同じです.
>
>  rb_eval_string_wrap() の場合,定数のトップレベルが匿名モジュールになり
> ます.
>
> (d) __FILE__ はどうなるか?
>
>  "(eval)" になります.
>
> (e) エンコーディングはどうなるか?
>
>  マジックコメントを見ます.
> (無いとどうなんだろう...)
>
> (f) エラーが起きたらどうなるか?(構文エラー,実行時エラー)
>
> VALUE rb_eval_string(const char *str)
> 外側に伝搬します.アプリ側でトラップ出来るようにしておかないと死にます.
>
> VALUE rb_eval_string_protect(const char *str, int *state)
> VALUE rb_eval_string_wrap(const char *str, int *state)
> エラーが起きたら state に 0 以外の値が入っています.
>
>
> (3) void rb_load(VALUE fname, int wrap)
>    void rb_load_protect(VALUE fname, int wrap, int *state)
>
>  ファイル名を String で渡す,ということ以外は (2) と変わりません.
>
> (a) プログラムの文字列はどうやって渡すか?
>
>  ファイル名でファイルを指定(ファイルの中身がプログラム).
>
> (b) トップレベルの self はどうなるか?
>
>  main になります.wrap が !0 の場合,定数のトップレベルが匿名モジュール
> になります.
>
> (c) 実行バインディングはどうなるか?
>
>  新しい binding を作って実行します.require で新しく作られるバインディ
> ングと同じです.
>
> (d) __FILE__ はどうなるか?
>
>  渡されたファイル名と同じになります(多分).
>
> (e) エンコーディングはどうなるか?
>
>  マジックコメントを見ます.
>
> (f) エラーが起きたらどうなるか?(構文エラー,実行時エラー)
>
> void rb_load(VALUE fname, int wrap)
>  外側に伝搬します.設定していないと死にます(多分).
>
> void rb_load_protect(VALUE fname, int wrap, int *state)
>  ここで食い止めます.state を見るとエラーの有無をチェック出来ます.
>
>
> (4) その他
>
>  その他,Kernel.eval,Kernel.instance_eval,Kernel.class_eval などを用
> いる,という手段もありますが,どちらかというと拡張ライブラリから呼び出す
> ような手段であり,設定も面倒なので省略します.いや,それらで済むのなら,
> それでいいのですが.
>
>  ruby.h などで非公開の関数ですが rb_iseq_compile() + rb_iseq_eval() と
> いうのもあるかと思ったんですが,よくよく調べてみると,これらをきちんと使
> うのは準備が大変だということがわかりました.
>
>  なお,上記で「準備しないと死ぬ」とあるのは,例外の伝搬先が設定されてい
> ないと死ぬ,という感じです.普通に組込み用途でカジュアルに使わない方がい
> いですね.
>
>
>  とりあえず,現状認識はこの通りです.
>
>
>  では,yugui さんご提案のインターフェースを見てます.
>
>> * rb_eval_string_in_main
>> * rb_eval_file_in_main
>
> (a) プログラムの文字列はどうやって渡すか?
>
>  不明.C 文字列だろうか.
>
> (b) トップレベルの self はどうなるか?
>
>  main です.
>
> (c) 実行バインディングはどうなるか?
>
>  TOPLEVEL_BINDING です.
>
> (d) __FILE__ はどうなるか?
>
>  不明.引数で渡すんだろうか.
>
> (e) エンコーディングはどうなるか?
>
>  多分,プログラム文字列に書いてあるのを読むんじゃないかと思う.
>
> (f) エラーが起きたらどうなるか?(構文エラー,実行時エラー)
>
>  不明.
>
>
>
>  ちなみに,組込みで有名な mruby さんのインターフェースを調べてみます.
>
> int mrb_compile_file(mrb_state*,FILE*);
> int mrb_compile_string(mrb_state*,char*);
> int mrb_compile_nstring(mrb_state*,char*,size_t);
>
>  ぱっと見た感じ,ファイルや C 文字列を渡すと,これらの関数は返値 n を返
> し,コンパイル結果が mrb_state->ipre[n] に格納されます.
>
> mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value);
>
>  そして,mrb_run で,格納されたコンパイル結果を実行しています(多分).
> 第二引数に,コンパイル結果 mrb_state->irep[n] を mrb_proc_new() に渡して
> Proc オブジェクトを作って(多分),第三引数にその Proc の初期値を渡して
> 実行,という感じでしょうか.多分.違ってたらすみません.
>
>  まとめます.
>
> (a) プログラムの文字列はどうやって渡すか?
>
>  C 文字列や FILE*.
>
> (b) トップレベルの self はどうなるか?
>
>  どうなんだろう,これ.サンプル見ても判らなかったんだけど,main が勝手
> に入るのかなぁ.
>
> (c) 実行バインディングはどうなるか?
>
>  そもそも binding が存在しません.
>
> (d) __FILE__ はどうなるか?
>
>  どうなるんでしょうか? そもそもあるの?
>
> $ bin/mruby -e 'p __FILE__'
> "(null)"
> $ bin/mruby t.rb # t.rb には p __FILE__ と書いてある
> "t.rb"
>
> あるようでした.いつの間に設定されてたんだろう.mrb_compile_* だと,何が
> 入るんだろう.
>
> (e) エンコーディングはどうなるか?
>
>  エンコーディングが存在しません(多分.あったらすみません)
>
> (f) エラーが起きたらどうなるか?(構文エラー,実行時エラー)
>
>  どうなるんでしょう? mrb_run() の外まで伝搬するのかなぁ.
>
>  これでサーベイを終わります.JRuby とかも調べ出すといいんでしょうが,
> ちょっとしんどくなってきました.
>
>
>
>  こう見ると,VALUE rb_eval_string_protect(const char *str, int *state)
> が,__FILE__ を指定出来ない(そして名前が Kernel.eval を想起させる悪い名
> 前である),という点以外はいけてるんじゃないかと思うのですが,どうでしょ
> うか.
>
>  アプリケーション組込みアプリが,TOPLEVEL_BINDING 上で動作する,という
> のは,実は想定していませんでした(なので無かった).require 相当が動けば
> いいと思っていました.
>
>  やりたいときは,
>
>    $exec_string にプログラムをセットしてから,
>    rb_eval_string_protected("eval($exec_string, TOPLEVEL_BINDING)"...);
>
> なんて手もあるかもしれません,がそれはあんまりかな.
>
> --
> // SASADA Koichi at atdot dot net
>



-- 
Yuki Sonoda (Yugui)
yugui / yugui.jp
http://yugui.jp