スクリプト解析は文字列を解析する関数とファイルを解析する関数がありますが、それらの違いは所詮、入力の違いなのでmrb_parse_string()をスクリプト解析のエントリポイントとして読み進めていきたいと思います。
mrb_parse_string()は渡された文字列をstrlenしてmrb_parse_nstring()に渡しているだけなのでmrb_parse_nstring()を見てみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
| - | | | | | | | | | ! |
|
スクリプト解析で鍵となるのはmrb_parser_state構造体のようです。mrb_parser_stateはinclude/compile.hに書かれています。なお、上記ではparser_stateになっていますが、parser_stateはmrb_parser_stateをtypedefしたものです。
(もうちょい説明を書く)
具体的にスクリプトをNODEに変換してみましょう。対象とするスクリプトはRuby1.9のスクリプト解析で変換してみたものと同じスクリプトを使います*1。
class MonteCarlo def pi(n) count = 0 (1..n).each do x = rand y = rand if x * x + y * y <= 1 count += 1 end end (count.to_f / n) * 4 end end n = 10000 * 10000 pi = MonteCarlo.new.pi(n) puts "pi = #{pi}"
bin/mrubyは--verboseオプションを付けるとNODEツリーがダンプされます*2。上記のスクリプトを食わせると以下のNODEツリーが出力されました。
$ ./mruby.exe -c --verbose ../../montecarlo.rb NODE_SCOPE: local variables: n pi NODE_BEGIN: NODE_CLASS: :MonteCarlo body: NODE_BEGIN: NODE_DEF: pi local variables: n count mandatory args: NODE_ARG n NODE_BEGIN: NODE_ASGN: lhs: NODE_LVAR count rhs: NODE_INT 0 base 10 NODE_CALL: NODE_BEGIN: NODE_DOT2: NODE_INT 1 base 10 NODE_LVAR n method='each' (170) args: block: NODE_BLOCK: body: NODE_BEGIN: NODE_ASGN: lhs: NODE_LVAR x rhs: NODE_CALL: NODE_SELF method='rand' (330) NODE_ASGN: lhs: NODE_LVAR y rhs: NODE_CALL: NODE_SELF method='rand' (330) NODE_IF: cond: NODE_CALL: NODE_CALL: NODE_CALL: NODE_LVAR x method='*' (80) args: NODE_LVAR x method='+' (76) args: NODE_CALL: NODE_LVAR y method='*' (80) args: NODE_LVAR y method='<=' (300) args: NODE_INT 1 base 10 then: NODE_BEGIN: NODE_OP_ASGN: lhs: NODE_LVAR count op='+' (76) NODE_INT 1 base 10 NODE_CALL: NODE_BEGIN: NODE_CALL: NODE_CALL: NODE_LVAR count method='to_f' (109) method='/' (148) args: NODE_LVAR n method='*' (80) args: NODE_INT 4 base 10 NODE_ASGN: lhs: NODE_LVAR n rhs: NODE_CALL: NODE_INT 10000 base 10 method='*' (80) args: NODE_INT 10000 base 10 NODE_ASGN: lhs: NODE_LVAR pi rhs: NODE_CALL: NODE_CALL: NODE_CONST MonteCarlo method='new' (6) method='pi' (326) args: NODE_LVAR n NODE_CALL: NODE_SELF method='puts' (286) args: NODE_DSTR NODE_STR "pi = " len 5 NODE_BEGIN: NODE_LVAR pi NODE_STR "" len 0
それではどういうルールを通ることでこのようなNODEツリーが構築されるのかを追っていくことにします。