Merge from ruby_1_8, including r16205.

Note that passing a block to a Proc is experimental and partly broken.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8_7@16605 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
knu 2008-05-26 03:08:26 +00:00
parent 90ac7da8c8
commit 35ec34fa2f
6 changed files with 199 additions and 29 deletions

View file

@ -1,3 +1,13 @@
Mon May 26 11:53:21 2008 Akinori MUSHA <knu@iDaemons.org>
* eval.c (rb_yield_0, proc_invoke, proc_arity): allow passing a
block to a Proc. [ruby-dev:23533]; by nobu; backported from
1.9. This implementation in current shape is known to be
buggy/broken, especially with nested block invocation. Take
this as an experimental feature.
* parse.y (block_par, block_var): ditto.
Mon May 26 08:00:52 2008 Akinori MUSHA <knu@iDaemons.org> Mon May 26 08:00:52 2008 Akinori MUSHA <knu@iDaemons.org>
* marshal.c (r_object0, Init_marshal): Fix the garbled s_call * marshal.c (r_object0, Init_marshal): Fix the garbled s_call

11
NEWS
View file

@ -190,7 +190,10 @@ with all sufficient information, see the ChangeLog file.
Return an enumerator if no block is given. Return an enumerator if no block is given.
* Object#tap implemented. * Object#instance_exec
* Object#tap
New methods.
* ObjectSpace.each_object * ObjectSpace.each_object
@ -345,6 +348,12 @@ with all sufficient information, see the ChangeLog file.
=== Interpreter Implementation === Interpreter Implementation
* passing a block to a Proc [experimental]
This implementation in current shape is known to be buggy/broken,
especially with nested block invocation. Take this as an
experimental feature.
* stack trace * stack trace
On non-SystemStackError exception, full stack trace is shown. On non-SystemStackError exception, full stack trace is shown.

63
eval.c
View file

@ -4918,7 +4918,7 @@ rb_yield_0(val, self, klass, flags, avalue)
VALUE val, self, klass; /* OK */ VALUE val, self, klass; /* OK */
int flags, avalue; int flags, avalue;
{ {
NODE *node; NODE *node, *var;
volatile VALUE result = Qnil; volatile VALUE result = Qnil;
volatile VALUE old_cref; volatile VALUE old_cref;
volatile VALUE old_wrapper; volatile VALUE old_wrapper;
@ -4960,27 +4960,35 @@ rb_yield_0(val, self, klass, flags, avalue)
self = block->self; self = block->self;
} }
node = block->body; node = block->body;
var = block->var;
if (block->var) { if (var) {
PUSH_TAG(PROT_NONE); PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) { if ((state = EXEC_TAG()) == 0) {
if (block->var == (NODE*)1) { /* no parameter || */ NODE *bvar = NULL;
block_var:
if (var == (NODE*)1) { /* no parameter || */
if (lambda && RARRAY(val)->len != 0) { if (lambda && RARRAY(val)->len != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)", rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
RARRAY(val)->len); RARRAY(val)->len);
} }
} }
else if (block->var == (NODE*)2) { else if (var == (NODE*)2) {
if (TYPE(val) == T_ARRAY && RARRAY(val)->len != 0) { if (TYPE(val) == T_ARRAY && RARRAY(val)->len != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)", rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
RARRAY(val)->len); RARRAY(val)->len);
} }
} }
else if (nd_type(block->var) == NODE_MASGN) { else if (!bvar && nd_type(var) == NODE_BLOCK_PASS) {
bvar = var->nd_body;
var = var->nd_args;
goto block_var;
}
else if (nd_type(var) == NODE_MASGN) {
if (!avalue) { if (!avalue) {
val = svalue_to_mrhs(val, block->var->nd_head); val = svalue_to_mrhs(val, var->nd_head);
} }
massign(self, block->var, val, lambda); massign(self, var, val, lambda);
} }
else { else {
int len = 0; int len = 0;
@ -5001,13 +5009,21 @@ rb_yield_0(val, self, klass, flags, avalue)
val = Qnil; val = Qnil;
multi_values: multi_values:
{ {
ruby_current_node = block->var; ruby_current_node = var;
rb_warn("multiple values for a block parameter (%d for 1)\n\tfrom %s:%d", rb_warn("multiple values for a block parameter (%d for 1)\n\tfrom %s:%d",
len, cnode->nd_file, nd_line(cnode)); len, cnode->nd_file, nd_line(cnode));
ruby_current_node = cnode; ruby_current_node = cnode;
} }
} }
assign(self, block->var, val, lambda); assign(self, var, val, lambda);
}
if (bvar) {
VALUE blk;
if (flags & YIELD_PROC_CALL)
blk = block->block_obj;
else
blk = rb_block_proc();
assign(self, bvar, blk, 0);
} }
} }
POP_TAG(); POP_TAG();
@ -8691,13 +8707,12 @@ proc_invoke(proc, args, self, klass)
volatile VALUE old_wrapper = ruby_wrapper; volatile VALUE old_wrapper = ruby_wrapper;
volatile int pcall, avalue = Qtrue; volatile int pcall, avalue = Qtrue;
volatile VALUE tmp = args; volatile VALUE tmp = args;
VALUE bvar = Qnil;
if (rb_block_given_p() && ruby_frame->last_func) { if (rb_block_given_p() && ruby_frame->last_func) {
if (klass != ruby_frame->last_class) if (klass != ruby_frame->last_class)
klass = rb_obj_class(proc); klass = rb_obj_class(proc);
rb_warning("block for %s#%s is useless", bvar = rb_block_proc();
rb_class2name(klass),
rb_id2name(ruby_frame->last_func));
} }
Data_Get_Struct(proc, struct BLOCK, data); Data_Get_Struct(proc, struct BLOCK, data);
@ -8713,6 +8728,7 @@ proc_invoke(proc, args, self, klass)
/* PUSH BLOCK from data */ /* PUSH BLOCK from data */
old_block = ruby_block; old_block = ruby_block;
_block = *data; _block = *data;
_block.block_obj = bvar;
if (self != Qundef) _block.frame.self = self; if (self != Qundef) _block.frame.self = self;
if (klass) _block.frame.last_class = klass; if (klass) _block.frame.last_class = klass;
_block.frame.argc = RARRAY(tmp)->len; _block.frame.argc = RARRAY(tmp)->len;
@ -8733,7 +8749,8 @@ proc_invoke(proc, args, self, klass)
state = EXEC_TAG(); state = EXEC_TAG();
if (state == 0) { if (state == 0) {
proc_set_safe_level(proc); proc_set_safe_level(proc);
result = rb_yield_0(args, self, (self!=Qundef)?CLASS_OF(self):0, pcall, avalue); result = rb_yield_0(args, self, (self!=Qundef)?CLASS_OF(self):0,
pcall | YIELD_PROC_CALL, avalue);
} }
else if (TAG_DST()) { else if (TAG_DST()) {
result = prot_tag->retval; result = prot_tag->retval;
@ -8837,30 +8854,36 @@ proc_arity(proc)
VALUE proc; VALUE proc;
{ {
struct BLOCK *data; struct BLOCK *data;
NODE *list; NODE *var, *list;
int n; int n;
Data_Get_Struct(proc, struct BLOCK, data); Data_Get_Struct(proc, struct BLOCK, data);
if (data->var == 0) { var = data->var;
if (var == 0) {
if (data->body && nd_type(data->body) == NODE_IFUNC && if (data->body && nd_type(data->body) == NODE_IFUNC &&
data->body->nd_cfnc == bmcall) { data->body->nd_cfnc == bmcall) {
return method_arity(data->body->nd_tval); return method_arity(data->body->nd_tval);
} }
return INT2FIX(-1); return INT2FIX(-1);
} }
if (data->var == (NODE*)1) return INT2FIX(0); if (var == (NODE*)1) return INT2FIX(0);
if (data->var == (NODE*)2) return INT2FIX(0); if (var == (NODE*)2) return INT2FIX(0);
switch (nd_type(data->var)) { if (nd_type(var) == NODE_BLOCK_ARG) {
var = var->nd_args;
if (var == (NODE*)1) return INT2FIX(0);
if (var == (NODE*)2) return INT2FIX(0);
}
switch (nd_type(var)) {
default: default:
return INT2FIX(1); return INT2FIX(1);
case NODE_MASGN: case NODE_MASGN:
list = data->var->nd_head; list = var->nd_head;
n = 0; n = 0;
while (list) { while (list) {
n++; n++;
list = list->nd_next; list = list->nd_next;
} }
if (data->var->nd_args) return INT2FIX(-n-1); if (var->nd_args) return INT2FIX(-n-1);
return INT2FIX(n); return INT2FIX(n);
} }
} }

75
parse.y
View file

@ -195,6 +195,8 @@ static void top_local_setup();
#define nd_paren(node) (char)((node)->u2.id >> CHAR_BIT*2) #define nd_paren(node) (char)((node)->u2.id >> CHAR_BIT*2)
#define nd_nest u3.id #define nd_nest u3.id
#define NEW_BLOCK_VAR(b, v) NEW_NODE(NODE_BLOCK_PASS, 0, b, v)
/* Older versions of Yacc set YYMAXDEPTH to a very low value by default (150, /* Older versions of Yacc set YYMAXDEPTH to a very low value by default (150,
for instance). This is too low for Ruby to parse some files, such as for instance). This is too low for Ruby to parse some files, such as
date/format.rb, therefore bump the value up to at least Bison's default. */ date/format.rb, therefore bump the value up to at least Bison's default. */
@ -278,7 +280,8 @@ static void top_local_setup();
%type <node> mrhs superclass block_call block_command %type <node> mrhs superclass block_call block_command
%type <node> f_arglist f_args f_optarg f_opt f_rest_arg f_block_arg opt_f_block_arg %type <node> f_arglist f_args f_optarg f_opt f_rest_arg f_block_arg opt_f_block_arg
%type <node> assoc_list assocs assoc undef_list backref string_dvar %type <node> assoc_list assocs assoc undef_list backref string_dvar
%type <node> block_var opt_block_var brace_block cmd_brace_block do_block lhs none fitem %type <node> for_var block_var opt_block_var block_par
%type <node> brace_block cmd_brace_block do_block lhs none fitem
%type <node> mlhs mlhs_head mlhs_basic mlhs_entry mlhs_item mlhs_node %type <node> mlhs mlhs_head mlhs_basic mlhs_entry mlhs_item mlhs_node
%type <id> fsym variable sym symbol operation operation2 operation3 %type <id> fsym variable sym symbol operation operation2 operation3
%type <id> cname fname op %type <id> cname fname op
@ -1610,7 +1613,7 @@ primary : literal
{ {
$$ = $4; $$ = $4;
} }
| kFOR block_var kIN {COND_PUSH(1);} expr_value do {COND_POP();} | kFOR for_var kIN {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt compstmt
kEND kEND
{ {
@ -1757,10 +1760,76 @@ opt_else : none
} }
; ;
block_var : lhs for_var : lhs
| mlhs | mlhs
; ;
block_par : mlhs_item
{
$$ = NEW_LIST($1);
}
| block_par ',' mlhs_item
{
$$ = list_append($1, $3);
}
;
block_var : block_par
{
if ($1->nd_alen == 1) {
$$ = $1->nd_head;
rb_gc_force_recycle((VALUE)$1);
}
else {
$$ = NEW_MASGN($1, 0);
}
}
| block_par ','
{
$$ = NEW_MASGN($1, 0);
}
| block_par ',' tAMPER lhs
{
$$ = NEW_BLOCK_VAR($4, NEW_MASGN($1, 0));
}
| block_par ',' tSTAR lhs ',' tAMPER lhs
{
$$ = NEW_BLOCK_VAR($7, NEW_MASGN($1, $4));
}
| block_par ',' tSTAR ',' tAMPER lhs
{
$$ = NEW_BLOCK_VAR($6, NEW_MASGN($1, -1));
}
| block_par ',' tSTAR lhs
{
$$ = NEW_MASGN($1, $4);
}
| block_par ',' tSTAR
{
$$ = NEW_MASGN($1, -1);
}
| tSTAR lhs ',' tAMPER lhs
{
$$ = NEW_BLOCK_VAR($5, NEW_MASGN(0, $2));
}
| tSTAR ',' tAMPER lhs
{
$$ = NEW_BLOCK_VAR($4, NEW_MASGN(0, -1));
}
| tSTAR lhs
{
$$ = NEW_MASGN(0, $2);
}
| tSTAR
{
$$ = NEW_MASGN(0, -1);
}
| tAMPER lhs
{
$$ = NEW_BLOCK_VAR($2, (NODE*)1);
}
;
opt_block_var : none opt_block_var : none
| '|' /* none */ '|' | '|' /* none */ '|'
{ {

View file

@ -65,6 +65,30 @@ a = *[*[]]; test_ok(a == nil)
a = *[*[1]]; test_ok(a == 1) a = *[*[1]]; test_ok(a == 1)
a = *[*[1,2]]; test_ok(a == [1,2]) a = *[*[1,2]]; test_ok(a == [1,2])
a, = nil; test_ok(a == nil)
a, = 1; test_ok(a == 1)
a, = []; test_ok(a == nil)
a, = [1]; test_ok(a == 1)
a, = [nil]; test_ok(a == nil)
a, = [[]]; test_ok(a == [])
a, = 1,2; test_ok(a == 1)
a, = [1,2]; test_ok(a == 1)
a, = [*[]]; test_ok(a == nil)
a, = [*[1]]; test_ok(a == 1)
a, = *[1,2]; test_ok(a == 1)
a, = [*[1,2]]; test_ok(a == 1)
a, = *nil; test_ok(a == nil)
a, = *1; test_ok(a == 1)
a, = *[]; test_ok(a == nil)
a, = *[1]; test_ok(a == 1)
a, = *[nil]; test_ok(a == nil)
a, = *[[]]; test_ok(a == [])
a, = *[1,2]; test_ok(a == 1)
a, = *[*[]]; test_ok(a == nil)
a, = *[*[1]]; test_ok(a == 1)
a, = *[*[1,2]]; test_ok(a == 1)
*a = nil; test_ok(a == [nil]) *a = nil; test_ok(a == [nil])
*a = 1; test_ok(a == [1]) *a = 1; test_ok(a == [1])
*a = []; test_ok(a == [[]]) *a = []; test_ok(a == [[]])
@ -126,6 +150,27 @@ def f; yield *[nil]; end; f {|a| test_ok(a == nil)}
def f; yield *[[]]; end; f {|a| test_ok(a == [])} def f; yield *[[]]; end; f {|a| test_ok(a == [])}
def f; yield *[*[1]]; end; f {|a| test_ok(a == 1)} def f; yield *[*[1]]; end; f {|a| test_ok(a == 1)}
def f; yield; end; f {|a,| test_ok(a == nil)}
def f; yield nil; end; f {|a,| test_ok(a == nil)}
def f; yield 1; end; f {|a,| test_ok(a == 1)}
def f; yield []; end; f {|a,| test_ok(a == nil)}
def f; yield [1]; end; f {|a,| test_ok(a == 1)}
def f; yield [nil]; end; f {|a,| test_ok(a == nil)}
def f; yield [[]]; end; f {|a,| test_ok(a == [])}
def f; yield [*[]]; end; f {|a,| test_ok(a == nil)}
def f; yield [*[1]]; end; f {|a,| test_ok(a == 1)}
def f; yield [*[1,2]]; end; f {|a,| test_ok(a == 1)}
def f; yield *nil; end; f {|a,| test_ok(a == nil)}
def f; yield *1; end; f {|a,| test_ok(a == 1)}
def f; yield *[]; end; f {|a,| test_ok(a == nil)}
def f; yield *[1]; end; f {|a,| test_ok(a == 1)}
def f; yield *[nil]; end; f {|a,| test_ok(a == nil)}
def f; yield *[[]]; end; f {|a,| test_ok(a == [])}
def f; yield *[*[]]; end; f {|a,| test_ok(a == nil)}
def f; yield *[*[1]]; end; f {|a,| test_ok(a == 1)}
def f; yield *[*[1,2]]; end; f {|a,| test_ok(a == 1)}
def f; yield; end; f {|*a| test_ok(a == [])} def f; yield; end; f {|*a| test_ok(a == [])}
def f; yield nil; end; f {|*a| test_ok(a == [nil])} def f; yield nil; end; f {|*a| test_ok(a == [nil])}
def f; yield 1; end; f {|*a| test_ok(a == [1])} def f; yield 1; end; f {|*a| test_ok(a == [1])}
@ -1000,15 +1045,21 @@ IterTest.new([[8]]).each8 {|x| test_ok(x == [8])}
IterTest.new([[0,0]]).each0 {|x| test_ok(x == [0,0])} IterTest.new([[0,0]]).each0 {|x| test_ok(x == [0,0])}
IterTest.new([[8,8]]).each8 {|x| test_ok(x == [8,8])} IterTest.new([[8,8]]).each8 {|x| test_ok(x == [8,8])}
def m def m0(v)
test_ok(block_given?) v
end end
m{p 'test'}
def m1
m0(block_given?)
end
test_ok(m1{p 'test'})
test_ok(!m1)
def m def m
test_ok(block_given?,&proc{}) m0(block_given?,&proc{})
end end
m{p 'test'} test_ok(m1{p 'test'})
test_ok(!m1)
class C class C
include Enumerable include Enumerable
@ -1079,6 +1130,9 @@ test_ok(get_block(&lambda).class == Proc)
test_ok(Proc.new{|a,| a}.call(1,2,3) == 1) test_ok(Proc.new{|a,| a}.call(1,2,3) == 1)
argument_test(true, Proc.new{|a,|}, 1,2) argument_test(true, Proc.new{|a,|}, 1,2)
test_ok(Proc.new{|&b| b.call(10)}.call {|x| x} == 10)
test_ok(Proc.new{|a,&b| b.call(a)}.call(12) {|x| x} == 12)
def test_return1 def test_return1
Proc.new { Proc.new {
return 55 return 55

View file

@ -86,4 +86,9 @@ class TestProc < Test::Unit::TestCase
b = lambda {} b = lambda {}
assert_not_equal(a, b) assert_not_equal(a, b)
end end
def test_block_par
assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x})
assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x})
end
end end