mruby/スクリプト解析を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[mrubyを読む]]
#contents
*はじめに [#ha2d1f6f]
スクリプト解析は文字列を解析する関数とファイルを解析する...
*mrb_parse_nstring(src/parse.y) [#k3c722a6]
mrb_parse_string()は渡された文字列をstrlenしてmrb_parse_n...
#code(C){{
parser_state*
mrb_parse_nstring(mrb_state *mrb, const char *s, size_t l...
{
parser_state *p;
p = mrb_parser_new(mrb);
if (!p) return 0;
p->s = s;
p->send = s + len;
mrb_parser_parse(p);
return p;
}
}}
スクリプト解析で鍵となるのはmrb_parser_state構造体のよう...
次にmrb_parser_parse()を見てみます。ちょっと長めですが全...
#code(C){{
void
mrb_parser_parse(parser_state *p)
{
node *tree;
if (setjmp(p->jmp) != 0) {
yyerror(p, "memory allocation error");
p->nerr++;
p->tree = p->begin_tree = 0;
return;
}
p->cmd_start = TRUE;
p->in_def = p->in_single = FALSE;
p->nerr = p->nwarn = 0;
p->sterm = 0;
yyparse(p);
tree = p->tree;
if (!tree) {
if (p->begin_tree) {
tree = p->begin_tree;
}
else {
tree = new_nil(p);
}
}
else {
if ((intptr_t)tree->car == NODE_SCOPE) {
p->locals = cons(tree->cdr->car, 0);
}
if (p->begin_tree) {
tree = new_begin(p, p->begin_tree);
append(tree, p->tree);
}
}
}
}}
関数を眺めると、
yyparse()を実行するとmrb_parser_state.treeに解析結果が格...
ということがわかります。node(mrb_ast_nodeがtypedefされた...
#code(C){{
typedef struct mrb_ast_node {
struct mrb_ast_node *car, *cdr;
} mrb_ast_node;
}}
CRubyのNODE構造体に比べると驚くほどシンプルです。シンプル...
#code(C){{
// (:scope (vars..) (prog...))
static node*
new_scope(parser_state *p, node *body)
{
return cons((node*)NODE_SCOPE, cons(p->locals->car, bod...
}
}}
*解析してみる [#a48d3327]
具体的にスクリプトを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}"
bin/mrubyは--verboseオプションを付けると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ツリーが...
**primary(keyword_class cpath superclass) [#re464262]
まずクラス定義があります。これは、program → top_compstmt ...
**primary(keyword_def fname) [#d234a397]
次のメソッド定義は、bodystmt → compstmt → stmts → stmt → ...
***f_arglist [#k04e2de0]
続いて、引数記述の解析に移ります。Rubyは、通常引数、オプ...
今回は通常引数のみの一番単純なケースなので、f_arglist → f...
**primary(method_call brace_block) [#b924c00e]
代入は飛ばしてブロック付きメソッド実行に移ります。
メソッド呼び出し全体にマッチするルールは「method_call bra...
次に「(1..n).each」の部分はmethod_callの「primary_value '...
最後に(1..n)の部分は、primary_value → primary → tLPAREN c...
「method_call brace_block」の際に呼ばれるcall_with_block(...
method_call brace_block
{
call_with_block(p, $1, $2);
$$ = $1;
}
#code(C){{
static void
call_with_block(parser_state *p, node *a, node *b)
{
node *n = a->cdr->cdr->cdr;
if (!n->car) n->car = cons(0, b);
else {
args_with_block(p, n->car, b);
}
}
}}
慣れないとわかりづらいですが、carはリストの先頭要素、cdr...
:cdr|(b c d)
:cdrのcdr|(c d)
:cdrのcdrのcdr|(d)
で、aとはNODE_CALLなので、
primary_value '.' operation2 opt_paren_args
{
$$ = new_call(p, $1, $3, $4);
}
#code(C){{
// (:call a b c)
static node*
new_call(parser_state *p, node *a, mrb_sym b, node *c)
{
return list4((node*)NODE_CALL, a, (node*)b, c);
}
}}
call_with_block()中のnは引数情報を指すことがわかります。
**CRubyで構築されるノードとの違いについて [#f8ca8e88]
[[Ruby1.9のスクリプト解析>Ruby1.9/スクリプト解析を読む]]...
-CRubyではNODE_ITERがNODE_CALLを持っていたが、mrubyではブ...
-CRubyではブロック内の変数はDVARとなっていたがmrubyでブロ...
なお、CRubyと比べてNODE_SCOPEがルートにしかありませんが、...
*おわりに [#n6f9a233]
というわけでmrubyのスクリプト解析を見てきました。わかった...
-CRubyに比べてノード構造体は非常にスリム化、ただし、各ノ...
-文法ルールは大体CRubyと同じ(当たり前だが)
といったところでしょうか。
ちなみに、初期化のところで張った正規表現機能がオフの場合...
終了行:
[[mrubyを読む]]
#contents
*はじめに [#ha2d1f6f]
スクリプト解析は文字列を解析する関数とファイルを解析する...
*mrb_parse_nstring(src/parse.y) [#k3c722a6]
mrb_parse_string()は渡された文字列をstrlenしてmrb_parse_n...
#code(C){{
parser_state*
mrb_parse_nstring(mrb_state *mrb, const char *s, size_t l...
{
parser_state *p;
p = mrb_parser_new(mrb);
if (!p) return 0;
p->s = s;
p->send = s + len;
mrb_parser_parse(p);
return p;
}
}}
スクリプト解析で鍵となるのはmrb_parser_state構造体のよう...
次にmrb_parser_parse()を見てみます。ちょっと長めですが全...
#code(C){{
void
mrb_parser_parse(parser_state *p)
{
node *tree;
if (setjmp(p->jmp) != 0) {
yyerror(p, "memory allocation error");
p->nerr++;
p->tree = p->begin_tree = 0;
return;
}
p->cmd_start = TRUE;
p->in_def = p->in_single = FALSE;
p->nerr = p->nwarn = 0;
p->sterm = 0;
yyparse(p);
tree = p->tree;
if (!tree) {
if (p->begin_tree) {
tree = p->begin_tree;
}
else {
tree = new_nil(p);
}
}
else {
if ((intptr_t)tree->car == NODE_SCOPE) {
p->locals = cons(tree->cdr->car, 0);
}
if (p->begin_tree) {
tree = new_begin(p, p->begin_tree);
append(tree, p->tree);
}
}
}
}}
関数を眺めると、
yyparse()を実行するとmrb_parser_state.treeに解析結果が格...
ということがわかります。node(mrb_ast_nodeがtypedefされた...
#code(C){{
typedef struct mrb_ast_node {
struct mrb_ast_node *car, *cdr;
} mrb_ast_node;
}}
CRubyのNODE構造体に比べると驚くほどシンプルです。シンプル...
#code(C){{
// (:scope (vars..) (prog...))
static node*
new_scope(parser_state *p, node *body)
{
return cons((node*)NODE_SCOPE, cons(p->locals->car, bod...
}
}}
*解析してみる [#a48d3327]
具体的にスクリプトを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}"
bin/mrubyは--verboseオプションを付けると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ツリーが...
**primary(keyword_class cpath superclass) [#re464262]
まずクラス定義があります。これは、program → top_compstmt ...
**primary(keyword_def fname) [#d234a397]
次のメソッド定義は、bodystmt → compstmt → stmts → stmt → ...
***f_arglist [#k04e2de0]
続いて、引数記述の解析に移ります。Rubyは、通常引数、オプ...
今回は通常引数のみの一番単純なケースなので、f_arglist → f...
**primary(method_call brace_block) [#b924c00e]
代入は飛ばしてブロック付きメソッド実行に移ります。
メソッド呼び出し全体にマッチするルールは「method_call bra...
次に「(1..n).each」の部分はmethod_callの「primary_value '...
最後に(1..n)の部分は、primary_value → primary → tLPAREN c...
「method_call brace_block」の際に呼ばれるcall_with_block(...
method_call brace_block
{
call_with_block(p, $1, $2);
$$ = $1;
}
#code(C){{
static void
call_with_block(parser_state *p, node *a, node *b)
{
node *n = a->cdr->cdr->cdr;
if (!n->car) n->car = cons(0, b);
else {
args_with_block(p, n->car, b);
}
}
}}
慣れないとわかりづらいですが、carはリストの先頭要素、cdr...
:cdr|(b c d)
:cdrのcdr|(c d)
:cdrのcdrのcdr|(d)
で、aとはNODE_CALLなので、
primary_value '.' operation2 opt_paren_args
{
$$ = new_call(p, $1, $3, $4);
}
#code(C){{
// (:call a b c)
static node*
new_call(parser_state *p, node *a, mrb_sym b, node *c)
{
return list4((node*)NODE_CALL, a, (node*)b, c);
}
}}
call_with_block()中のnは引数情報を指すことがわかります。
**CRubyで構築されるノードとの違いについて [#f8ca8e88]
[[Ruby1.9のスクリプト解析>Ruby1.9/スクリプト解析を読む]]...
-CRubyではNODE_ITERがNODE_CALLを持っていたが、mrubyではブ...
-CRubyではブロック内の変数はDVARとなっていたがmrubyでブロ...
なお、CRubyと比べてNODE_SCOPEがルートにしかありませんが、...
*おわりに [#n6f9a233]
というわけでmrubyのスクリプト解析を見てきました。わかった...
-CRubyに比べてノード構造体は非常にスリム化、ただし、各ノ...
-文法ルールは大体CRubyと同じ(当たり前だが)
といったところでしょうか。
ちなみに、初期化のところで張った正規表現機能がオフの場合...
ページ名: