スクリプト解析は文字列を解析する関数とファイルを解析する関数がありますが、それらの違いは所詮、入力の違いなので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したものです。
次にmrb_parser_parse()を見てみます。ちょっと長めですが全部貼り付け。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| - | | - | | | | ! | | | | | | | | - - | ! - | ! ! - - | ! - | | ! ! ! |
|
関数を眺めると、
yyparse()を実行するとmrb_parser_state.treeに解析結果が格納される
ということがわかります。node(mrb_ast_nodeがtypedefされたもの)は上記のmrb_parser_parse()を見てもわかるとおり、LISPのリスト構造と同じになっています。*1
1
2
3
| - | ! |
|
CRubyのNODE構造体に比べると驚くほどシンプルです。シンプルな分は運用で回避。parse.yの前半部分にはリスト操作、およびそれを使って解析ツリーを作るための関数群が定義されています。例えばこんな感じ、
具体的にスクリプトをNODEに変換してみましょう。対象とするスクリプトはRuby1.9のスクリプト解析で変換してみたものと同じスクリプトを使います*2。
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ツリーがダンプされます*3。上記のスクリプトを食わせると以下の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ツリーが構築されるのかを追っていくことにします。
(執筆中)