スクリプト解析は文字列を解析する関数とファイルを解析する関数がありますが、それらの違いは所詮、入力の違いなので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ツリーが構築されるのかを追っていくことにします。
(執筆中)