* eval.c (rb_yield_0, proc_invoke, proc_arity): allow passing a block

to a Proc.  [ruby-dev:23533]

* parse.y (block_par, block_var): ditto.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@6402 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2004-05-25 02:54:22 +00:00
parent 1efe963c7a
commit 82572952ec
5 changed files with 186 additions and 28 deletions

View File

@ -1,3 +1,10 @@
Tue May 25 11:54:13 2004 Nobuyoshi Nakada <nobu@ruby-lang.org>
* eval.c (rb_yield_0, proc_invoke, proc_arity): allow passing a block
to a Proc. [ruby-dev:23533]
* parse.y (block_par, block_var): ditto.
Tue May 25 01:50:17 2004 GOTOU Yuuzou <gotoyuzo@notwork.org> Tue May 25 01:50:17 2004 GOTOU Yuuzou <gotoyuzo@notwork.org>
* ext/openssl/ossl_asn1.c (ossl_i2d_ASN1_TYPE, ossl_ASN1_TYPE_free): * ext/openssl/ossl_asn1.c (ossl_i2d_ASN1_TYPE, ossl_ASN1_TYPE_free):

65
eval.c
View File

@ -4569,7 +4569,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;
@ -4612,27 +4612,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) {
if (!avalue) { bvar = var->nd_body;
val = svalue_to_mrhs(val, block->var->nd_head); var = var->nd_args;
goto block_var;
} }
massign(self, block->var, val, lambda); else if (nd_type(var) == NODE_MASGN) {
if (!avalue) {
val = svalue_to_mrhs(val, var->nd_head);
}
massign(self, var, val, lambda);
} }
else { else {
int len = 0; int len = 0;
@ -4653,13 +4661,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();
@ -8021,13 +8037,12 @@ proc_invoke(proc, args, self, klass)
volatile VALUE old_wrapper = ruby_wrapper; volatile VALUE old_wrapper = ruby_wrapper;
struct RVarmap * volatile old_dvars = ruby_dyna_vars; struct RVarmap * volatile old_dvars = ruby_dyna_vars;
volatile int pcall, avalue = Qtrue; volatile int pcall, avalue = Qtrue;
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);
@ -8043,6 +8058,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;
ruby_block = &_block; ruby_block = &_block;
@ -8053,7 +8069,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;
@ -8157,30 +8174,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(0); return INT2FIX(0);
} }
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

@ -179,6 +179,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. */
@ -262,7 +264,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_block_arg opt_f_block_arg %type <node> f_arglist f_args f_optarg f_opt f_block_arg opt_f_block_arg
%type <node> assoc_list assocs assoc kwargs undef_list backref string_dvar %type <node> assoc_list assocs assoc kwargs undef_list backref string_dvar
%type <node> block_var opt_block_var brace_block cmd_brace_block do_block lhs none %type <node> for_var block_var opt_block_var block_par
%type <node> brace_block cmd_brace_block do_block lhs none
%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> fitem variable sym symbol operation operation2 operation3 %type <id> fitem variable sym symbol operation operation2 operation3
%type <id> cname fname op f_rest_arg %type <id> cname fname op f_rest_arg
@ -1589,7 +1592,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
{ {
@ -1739,10 +1742,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