Ruby1.9/スクリプト解析を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*はじめに [#z31c86e5]
ここではRuby1.9のスクリプト解析を読解したいと思います。
*ruby_options(eval.c) [#r4f09aab]
初期化が終わって次はオプションの解析です。Ruby1.8のころと...
また、SAVE_ROOT_JMPBUFマクロでくるまれています。eval_inte...
*ruby_process_options(ruby.c) [#r1cdd1f4]
Ruby1.8ではオプション情報はグローバル変数に格納されていま...
そのほかの変更点としてオプション解析のメイン処理であるpro...
*ruby_init_gems(ruby.c) [#r6ee94b2]
RubyGemsを初期化するコードがなかなかおもしろいので書いて...
ruby_init_gems(struct cmdline_options *opt)
{
VALUE gem;
gem = rb_define_module("Gem");
rb_const_set(gem, rb_intern("Enable"), opt->disable_...
Init_prelude();
}
というわけでGemモジュールのEnable定数を設定後、Init_prelu...
Init_prelude関数がどこにあるかというとprelude.cに書いてあ...
*rb_parser_compile_file(parser.y) [#p8ff30c1]
スクリプトの解析本体についてはあまり変化はないようですが(...
NODE*
rb_parser_compile_file(volatile VALUE vparser, const cha...
{
struct parser_params *parser;
volatile VALUE tmp;
NODE *node;
Data_Get_Struct(vparser, struct parser_params, parse...
lex_gets = lex_io_gets;
lex_input = file;
lex_pbeg = lex_p = lex_pend = 0;
node = yycompile(parser, f, start);
tmp = vparser; /* prohibit tail call optimization */
return node;
}
lex_inputとかは一見グローバルに見えますが実は、
#define lex_input (parser->parser_lex_input)
とdefineされているので引数で渡されたvparserにくるまれたpa...
*解析してみる [#n0c46771]
次ステップ以降で必要となるので具体的なスクリプトをNODE表...
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}"
一応、以下の要素があるものにしました。クラスになってる意...
-クラス定義
-メソッド定義
-ブロック(繰り返し)
-条件分岐
なお、解析の詳細については[[青木さんのRHG>http://i.loveru...
**program [#rfd7de4e]
一番初めの規則はprogramです。初めにlocal_push(はマクロで...
**primary(keyword_class cpath superclass) [#g42377d5]
続いて
class MonteCarlo
という部分がprimary規則のうちkeyword_class cpath supercla...
***cpath [#aa14046d]
cpath規則ではcnameにマッチします。NEW_COLON2マクロによりN...
nd_type = NODE_COLON2
u1.value = 0
u2.value = ID("MonteCarlo")
u3.value = 0
**primary(keyword_def fname) [#q9cb2c18]
次に
def pi(n)
の部分がprimary規則のkeyword_def fnameにマッチします。fna...
***f_args(f_arg opt_f_block_arg) [#ca2994fc]
primary規則のkeyword_defがマッチするためには次にf_arglist...
***f_arg_item [#l9850543]
f_arg規則はf_arg_itemがマッチするので素通りです。
f_arg_item規則ではf_norm_argがマッチします。ローカル変数...
nd_type = NODE_ARGS_AUX
u1.value = ID("n")
u2.value = 1
u3.value = 0
***new_args_gen [#xd2311a4]
さて、f_argが決定したのでnew_args_gen関数が呼ばれてNODEが...
$$ = new_args($1, 0, 0, 0, $2);
で、new_args_genの宣言は
new_args_gen(struct parser_params *parser, NODE *m, NODE...
{
となっています。opt_f_block_argが0なのでmだけに値が入って...
まず、NEW_ARGSマクロによりNODEが作られ、その後に設定が行...
nd_type = NODE_ARGS
u1.value = 0
u2.value = 1
u3.value = NODE_ARGS_AUX(0, 0)
**arg(lhs '=' arg) [#w3867655]
次は
count = 0
の部分です。bodystmt → compstmt → stmts → stmt → expr → a...
***lhs(variable) [#h924fa94]
countはlhs規則のvariableにマッチし、variableはtIDENTIFIER...
nd_type = NODE_LASGN
u1.value = ID("count")
u2.value = 0
u3.value = 0
***numeric(tINTEGER) [#f12e9654]
0はarg規則が再帰してprimary → literal → numeric → tINTEGE...
nd_type = NODE_LIT
u1.value = 0 ← リテラル値。値がたまたま0なので0
u2.value = 0
u3.value = 0
***node_assign_gen [#x2f2aae9]
rhsのNODEがvalue_expr_gen関数に渡されますがNODE_LITなので...
nd_type = NODE_LASGN
u1.value = ID("count")
u2.value = NODE_LIT(0)
u3.value = 0
**arg(arg tDOT2 arg) [#d01ef04a]
次に
(1..n).each do
です。結構めんどくさいです。もっと簡単なのにしとけばよか...
一度に説明するのは不可能なので分割して説明します。
まず、(1..n)の()はprimary規則のtLPAREN compstmt ')'にマッ...
次に、1..nはarg規則のarg tDOT2 argにマッチします。
1は先ほどと同様にNODE_LITになります。
***var_ref [#v0b4f961]
nの方はarg → primary → var_refとなります。gettable_gen関...
nd_type = NODE_LVAR
u1.value = ID("n")
u2.value = 0
u3.value = 0
***arg(arg tDOT2 arg)の続き [#t0f7ef4b]
というわけで各要素が決定されたのでNODE構築です。片方がリ...
nd_type = NODE_DOT2
u1.value = NODE_LIT(1)
u2.value = NODE_LVAR(ID("n"), 0, 0)
u3.value = 0
**primary(method_call brace_block) [#j20e4c6b]
次にeach doの部分です。primary規則のうちmethod_call brace...
***method_call(primary_value '.' operation2 opt_paren_arg...
eachは引数がないのでmethod_call規則のうちprimary_value '....
nd_type = NODE_CALL
u1.value = NODE_DOT2
u2.value = ID("each")
u3.value = 0
***brace_block(keyword_do) [#q7f41052]
ブロックに入ったのでdyna_push_gen関数が呼ばれて新しくロー...
lvtbl→ブロック変数テーブル ← dyna_pushで割り当てられる
|
prev
↓
ローカル変数テーブル−prev→もう一つ外のローカル変...
↑
local_pushで割り当てられる
**arg(lhs '=' arg)その2 [#nda1d24d]
次は
x = rand
です。また、arg規則のlhs '=' argがマッチします。
***NODE_DASGN_CURR [#z8dd5ae9]
lhs規則は同じくvariableにマッチしますがブロック内変数なの...
nd_type = NODE_DASGN_CURR
u1.value = ID("x")
u2.value = 0
u3.value = 0
***NODE_VCALL [#n43e252a]
rhsはvar_ref規則にマッチし、gettable_gen関数が呼び出され...
nd_type = NODE_VCALL
u1.value = 0
u2.value = ID("rand")
u3.value = 0
**primary(keyword_if expr_value then) [#af45ead6]
次の行は同じことなので飛ばしてその次の行、
if x * x + y * y <= 1
これも複雑なので分割して説明します。
***expr_value [#pbd397aa]
x * x + y * y <= 1の部分はarg規則が繰り返し適用されること...
xはブロック内変数なのでNODE_DVARのNODEが作られます。もう...
nd_type = NODE_CALL
u1.value = NODE_DVAR(ID("x"))
u2.value = "*"
u3.value = NODE_ARRAY(NODE_DVAR(ID("x")), 1)
最終的にexpr_valueとして以下のようなNODE構造がセットされ...
NODE_CALL
NODE_CALL
NODE_CALL
NODE_DVAR(x)
*
NODE_ARRAY
NODE_DVAR(x)
1
+
NODE_ARRAY
NODE_CALL
NODE_DVAR(y)
*
NODE_ARRAY
NODE_DVAR(y)
1
1
<=
NODE_ARRAY
NODE_LIT(1)
1
***arg(var_lhs tOP_ASGN arg) [#q0ca504e]
ifを閉じる前に次の行に行きます。
count += 1
自己代入は特殊です。+=というメソッドがあるわけではありま...
NODE_DASGN
ID("count")
NODE_CALL
NODE_DVAR(count)
+
NODE_ARRAY
NODE_LIT(1)
1
***primary(keyword_if expr_value then)の続き [#f025b43c]
arg → expr → stmt → stmtsとなり、上で構築した自己代入NODE...
endに達したのでNODE_IFが構築できます。if_tailはないので0...
nd_type = NODE_IF
u1.value = NODE_CALL
u2.value = NODE_DASGN
u3.value = 0
その後、fixpos関数を呼び出すことで行数を調整するようです。
**brace_block(keyword_do)の続き [#ga45fac7]
次のendでブロック終了です。opt_block_paramは0で、stmtsは3...
NODE_BLOCK
NODE_DASGN_CURR(y = rand)
一番後ろのNODEを参照
NODE_BLOCK
NODE_IF
一番後ろのNODE(自分自身)を参照
次にNODE_DASGN_CURR(x = rand)と今作ったNODE_BLOCKがblock_...
NODE_BLOCK
NODE_DASGN_CURR(x = rand)
一番後ろのNODEを参照
NODE_BLOCK
NODE_DASGN_CURR(y = rand)
一番後ろのNODEを参照
NODE_BLOCK
NODE_IF
一番後ろのNODE(自分自身)を参照
最後にNODE_ITERを作って完成です。
NODE_ITER
NODE_SCOPE
変数テーブル
NODE_BLOCK
0
**primary(method_call brace_block)の続き [#j9b3bc25]
method_call、brace_blockが決定したのでprmaryが構築できま...
先ほど作ったNODE_ITERに情報が追加されます。
nd_type = NODE_ITER
u1.value = 0
u2.value = NODE_SCOPE
u3.value = NODE_CALL((1..n).each)
**primary(keyword_def fname)の続き [#x3f92137]
(count.to_f / n) * 4
の行は今までにわかった知識と努力と根性があればNODE化でき...
endに行き着いたのでメソッド終了です。
bodystmtは以下みたいな感じになります。
NODE_BLOCK
NODE_LASGN(count = 0)
NODE_BLOCK
NODE_ITER
NODE_BLOCK
NODE_CALL((count.to_f / n) * 4)
remove_begin関数、reduce_node_gen関数では特に変化はないは...
NODE_DEFN
ID("pi")
NODE_SCOPE
変数テーブル
NODE_BLOCK
NODE_ARGS
**primary(keyword_class cpath superclass)の続き [#ie86f1e2]
bodystmtはメソッド定義しかないのでそのままNODE_DEFNです。...
NODE_CLASS
NODE_COLON2(0, MonteCarlo)
NODE_SCOPE
変数テーブル
NODE_DEFN
0
0
**strings [#fe059a4a]
途中は飛ばして次に
puts "pi = #{pi}"
という式展開がどういうNODEに変換されるか見ることにしまし...
nd_type = NODE_STRTERM
u1.value = str_dquote
u2.value = '"'
u3.value = 0
次にparser_yylex関数が呼ばれるとparser_parse_string関数が...
nd_type = NODE_STR
u1.value = "pi = "
u2.value = 0
u3.value = 0
次にparser_parse_string関数が呼ばれるとtSTRING_DBEGが返さ...
nd_type = NODE_EVSTR
u1.value = 0
u2.value = NODE_LVAR(pi)
u3.value = 0
最後にliteral_concat_gen関数が呼ばれてNODE_STRとNODE_EVST...
NODE_DSTR
"pi = "
2
NODE_ARRAY
NODE_EVSTR
一番後ろのNODE(自分自身)を参照
**program続き [#cda4299a]
最後まで行ったのでprogram規則が終了です。以下のNODEが作ら...
nd_type = NODE_SCOPE
u1.value = 変数テーブル
u2.value = NODE_BLOCK
u3.value = 0
以上でNODE表現への変換が終了です。最後に変換されたNODEツ...
&ref(montecarlo.node.txt);
*おわりに [#p369c977]
ここではRuby1.9のスクリプト解析を見てきました。Ruby1.9で...
この時点では解析情報はまだ従来の構文木(NODE)表現です。...
終了行:
#contents
*はじめに [#z31c86e5]
ここではRuby1.9のスクリプト解析を読解したいと思います。
*ruby_options(eval.c) [#r4f09aab]
初期化が終わって次はオプションの解析です。Ruby1.8のころと...
また、SAVE_ROOT_JMPBUFマクロでくるまれています。eval_inte...
*ruby_process_options(ruby.c) [#r1cdd1f4]
Ruby1.8ではオプション情報はグローバル変数に格納されていま...
そのほかの変更点としてオプション解析のメイン処理であるpro...
*ruby_init_gems(ruby.c) [#r6ee94b2]
RubyGemsを初期化するコードがなかなかおもしろいので書いて...
ruby_init_gems(struct cmdline_options *opt)
{
VALUE gem;
gem = rb_define_module("Gem");
rb_const_set(gem, rb_intern("Enable"), opt->disable_...
Init_prelude();
}
というわけでGemモジュールのEnable定数を設定後、Init_prelu...
Init_prelude関数がどこにあるかというとprelude.cに書いてあ...
*rb_parser_compile_file(parser.y) [#p8ff30c1]
スクリプトの解析本体についてはあまり変化はないようですが(...
NODE*
rb_parser_compile_file(volatile VALUE vparser, const cha...
{
struct parser_params *parser;
volatile VALUE tmp;
NODE *node;
Data_Get_Struct(vparser, struct parser_params, parse...
lex_gets = lex_io_gets;
lex_input = file;
lex_pbeg = lex_p = lex_pend = 0;
node = yycompile(parser, f, start);
tmp = vparser; /* prohibit tail call optimization */
return node;
}
lex_inputとかは一見グローバルに見えますが実は、
#define lex_input (parser->parser_lex_input)
とdefineされているので引数で渡されたvparserにくるまれたpa...
*解析してみる [#n0c46771]
次ステップ以降で必要となるので具体的なスクリプトをNODE表...
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}"
一応、以下の要素があるものにしました。クラスになってる意...
-クラス定義
-メソッド定義
-ブロック(繰り返し)
-条件分岐
なお、解析の詳細については[[青木さんのRHG>http://i.loveru...
**program [#rfd7de4e]
一番初めの規則はprogramです。初めにlocal_push(はマクロで...
**primary(keyword_class cpath superclass) [#g42377d5]
続いて
class MonteCarlo
という部分がprimary規則のうちkeyword_class cpath supercla...
***cpath [#aa14046d]
cpath規則ではcnameにマッチします。NEW_COLON2マクロによりN...
nd_type = NODE_COLON2
u1.value = 0
u2.value = ID("MonteCarlo")
u3.value = 0
**primary(keyword_def fname) [#q9cb2c18]
次に
def pi(n)
の部分がprimary規則のkeyword_def fnameにマッチします。fna...
***f_args(f_arg opt_f_block_arg) [#ca2994fc]
primary規則のkeyword_defがマッチするためには次にf_arglist...
***f_arg_item [#l9850543]
f_arg規則はf_arg_itemがマッチするので素通りです。
f_arg_item規則ではf_norm_argがマッチします。ローカル変数...
nd_type = NODE_ARGS_AUX
u1.value = ID("n")
u2.value = 1
u3.value = 0
***new_args_gen [#xd2311a4]
さて、f_argが決定したのでnew_args_gen関数が呼ばれてNODEが...
$$ = new_args($1, 0, 0, 0, $2);
で、new_args_genの宣言は
new_args_gen(struct parser_params *parser, NODE *m, NODE...
{
となっています。opt_f_block_argが0なのでmだけに値が入って...
まず、NEW_ARGSマクロによりNODEが作られ、その後に設定が行...
nd_type = NODE_ARGS
u1.value = 0
u2.value = 1
u3.value = NODE_ARGS_AUX(0, 0)
**arg(lhs '=' arg) [#w3867655]
次は
count = 0
の部分です。bodystmt → compstmt → stmts → stmt → expr → a...
***lhs(variable) [#h924fa94]
countはlhs規則のvariableにマッチし、variableはtIDENTIFIER...
nd_type = NODE_LASGN
u1.value = ID("count")
u2.value = 0
u3.value = 0
***numeric(tINTEGER) [#f12e9654]
0はarg規則が再帰してprimary → literal → numeric → tINTEGE...
nd_type = NODE_LIT
u1.value = 0 ← リテラル値。値がたまたま0なので0
u2.value = 0
u3.value = 0
***node_assign_gen [#x2f2aae9]
rhsのNODEがvalue_expr_gen関数に渡されますがNODE_LITなので...
nd_type = NODE_LASGN
u1.value = ID("count")
u2.value = NODE_LIT(0)
u3.value = 0
**arg(arg tDOT2 arg) [#d01ef04a]
次に
(1..n).each do
です。結構めんどくさいです。もっと簡単なのにしとけばよか...
一度に説明するのは不可能なので分割して説明します。
まず、(1..n)の()はprimary規則のtLPAREN compstmt ')'にマッ...
次に、1..nはarg規則のarg tDOT2 argにマッチします。
1は先ほどと同様にNODE_LITになります。
***var_ref [#v0b4f961]
nの方はarg → primary → var_refとなります。gettable_gen関...
nd_type = NODE_LVAR
u1.value = ID("n")
u2.value = 0
u3.value = 0
***arg(arg tDOT2 arg)の続き [#t0f7ef4b]
というわけで各要素が決定されたのでNODE構築です。片方がリ...
nd_type = NODE_DOT2
u1.value = NODE_LIT(1)
u2.value = NODE_LVAR(ID("n"), 0, 0)
u3.value = 0
**primary(method_call brace_block) [#j20e4c6b]
次にeach doの部分です。primary規則のうちmethod_call brace...
***method_call(primary_value '.' operation2 opt_paren_arg...
eachは引数がないのでmethod_call規則のうちprimary_value '....
nd_type = NODE_CALL
u1.value = NODE_DOT2
u2.value = ID("each")
u3.value = 0
***brace_block(keyword_do) [#q7f41052]
ブロックに入ったのでdyna_push_gen関数が呼ばれて新しくロー...
lvtbl→ブロック変数テーブル ← dyna_pushで割り当てられる
|
prev
↓
ローカル変数テーブル−prev→もう一つ外のローカル変...
↑
local_pushで割り当てられる
**arg(lhs '=' arg)その2 [#nda1d24d]
次は
x = rand
です。また、arg規則のlhs '=' argがマッチします。
***NODE_DASGN_CURR [#z8dd5ae9]
lhs規則は同じくvariableにマッチしますがブロック内変数なの...
nd_type = NODE_DASGN_CURR
u1.value = ID("x")
u2.value = 0
u3.value = 0
***NODE_VCALL [#n43e252a]
rhsはvar_ref規則にマッチし、gettable_gen関数が呼び出され...
nd_type = NODE_VCALL
u1.value = 0
u2.value = ID("rand")
u3.value = 0
**primary(keyword_if expr_value then) [#af45ead6]
次の行は同じことなので飛ばしてその次の行、
if x * x + y * y <= 1
これも複雑なので分割して説明します。
***expr_value [#pbd397aa]
x * x + y * y <= 1の部分はarg規則が繰り返し適用されること...
xはブロック内変数なのでNODE_DVARのNODEが作られます。もう...
nd_type = NODE_CALL
u1.value = NODE_DVAR(ID("x"))
u2.value = "*"
u3.value = NODE_ARRAY(NODE_DVAR(ID("x")), 1)
最終的にexpr_valueとして以下のようなNODE構造がセットされ...
NODE_CALL
NODE_CALL
NODE_CALL
NODE_DVAR(x)
*
NODE_ARRAY
NODE_DVAR(x)
1
+
NODE_ARRAY
NODE_CALL
NODE_DVAR(y)
*
NODE_ARRAY
NODE_DVAR(y)
1
1
<=
NODE_ARRAY
NODE_LIT(1)
1
***arg(var_lhs tOP_ASGN arg) [#q0ca504e]
ifを閉じる前に次の行に行きます。
count += 1
自己代入は特殊です。+=というメソッドがあるわけではありま...
NODE_DASGN
ID("count")
NODE_CALL
NODE_DVAR(count)
+
NODE_ARRAY
NODE_LIT(1)
1
***primary(keyword_if expr_value then)の続き [#f025b43c]
arg → expr → stmt → stmtsとなり、上で構築した自己代入NODE...
endに達したのでNODE_IFが構築できます。if_tailはないので0...
nd_type = NODE_IF
u1.value = NODE_CALL
u2.value = NODE_DASGN
u3.value = 0
その後、fixpos関数を呼び出すことで行数を調整するようです。
**brace_block(keyword_do)の続き [#ga45fac7]
次のendでブロック終了です。opt_block_paramは0で、stmtsは3...
NODE_BLOCK
NODE_DASGN_CURR(y = rand)
一番後ろのNODEを参照
NODE_BLOCK
NODE_IF
一番後ろのNODE(自分自身)を参照
次にNODE_DASGN_CURR(x = rand)と今作ったNODE_BLOCKがblock_...
NODE_BLOCK
NODE_DASGN_CURR(x = rand)
一番後ろのNODEを参照
NODE_BLOCK
NODE_DASGN_CURR(y = rand)
一番後ろのNODEを参照
NODE_BLOCK
NODE_IF
一番後ろのNODE(自分自身)を参照
最後にNODE_ITERを作って完成です。
NODE_ITER
NODE_SCOPE
変数テーブル
NODE_BLOCK
0
**primary(method_call brace_block)の続き [#j9b3bc25]
method_call、brace_blockが決定したのでprmaryが構築できま...
先ほど作ったNODE_ITERに情報が追加されます。
nd_type = NODE_ITER
u1.value = 0
u2.value = NODE_SCOPE
u3.value = NODE_CALL((1..n).each)
**primary(keyword_def fname)の続き [#x3f92137]
(count.to_f / n) * 4
の行は今までにわかった知識と努力と根性があればNODE化でき...
endに行き着いたのでメソッド終了です。
bodystmtは以下みたいな感じになります。
NODE_BLOCK
NODE_LASGN(count = 0)
NODE_BLOCK
NODE_ITER
NODE_BLOCK
NODE_CALL((count.to_f / n) * 4)
remove_begin関数、reduce_node_gen関数では特に変化はないは...
NODE_DEFN
ID("pi")
NODE_SCOPE
変数テーブル
NODE_BLOCK
NODE_ARGS
**primary(keyword_class cpath superclass)の続き [#ie86f1e2]
bodystmtはメソッド定義しかないのでそのままNODE_DEFNです。...
NODE_CLASS
NODE_COLON2(0, MonteCarlo)
NODE_SCOPE
変数テーブル
NODE_DEFN
0
0
**strings [#fe059a4a]
途中は飛ばして次に
puts "pi = #{pi}"
という式展開がどういうNODEに変換されるか見ることにしまし...
nd_type = NODE_STRTERM
u1.value = str_dquote
u2.value = '"'
u3.value = 0
次にparser_yylex関数が呼ばれるとparser_parse_string関数が...
nd_type = NODE_STR
u1.value = "pi = "
u2.value = 0
u3.value = 0
次にparser_parse_string関数が呼ばれるとtSTRING_DBEGが返さ...
nd_type = NODE_EVSTR
u1.value = 0
u2.value = NODE_LVAR(pi)
u3.value = 0
最後にliteral_concat_gen関数が呼ばれてNODE_STRとNODE_EVST...
NODE_DSTR
"pi = "
2
NODE_ARRAY
NODE_EVSTR
一番後ろのNODE(自分自身)を参照
**program続き [#cda4299a]
最後まで行ったのでprogram規則が終了です。以下のNODEが作ら...
nd_type = NODE_SCOPE
u1.value = 変数テーブル
u2.value = NODE_BLOCK
u3.value = 0
以上でNODE表現への変換が終了です。最後に変換されたNODEツ...
&ref(montecarlo.node.txt);
*おわりに [#p369c977]
ここではRuby1.9のスクリプト解析を見てきました。Ruby1.9で...
この時点では解析情報はまだ従来の構文木(NODE)表現です。...
ページ名: