mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* enumerator.c (enumerator_initialize): Add an ability to generate
an enumerator from a block. [ruby-dev:35903] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@20631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									8707c67dbf
								
							
						
					
					
						commit
						f6895becb9
					
				
					 3 changed files with 317 additions and 13 deletions
				
			
		
							
								
								
									
										315
									
								
								enumerator.c
									
										
									
									
									
								
							
							
						
						
									
										315
									
								
								enumerator.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -33,7 +33,25 @@ struct enumerator {
 | 
			
		|||
    rb_block_call_func *iter;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static VALUE rb_cGenerator, rb_cYielder;
 | 
			
		||||
 | 
			
		||||
struct generator {
 | 
			
		||||
    VALUE proc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct yielder {
 | 
			
		||||
    VALUE proc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static VALUE generator_allocate _((VALUE klass));
 | 
			
		||||
static VALUE generator_init _((VALUE obj, VALUE proc));
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Enumerator
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void enumerator_mark _((void *));
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
enumerator_mark(p)
 | 
			
		||||
    void *p;
 | 
			
		||||
| 
						 | 
				
			
			@ -292,12 +310,34 @@ enumerator_init(enum_obj, obj, meth, argc, argv)
 | 
			
		|||
/*
 | 
			
		||||
 *  call-seq:
 | 
			
		||||
 *    Enumerator.new(obj, method = :each, *args)
 | 
			
		||||
 *    Enumerator.new { |y| ... }
 | 
			
		||||
 *
 | 
			
		||||
 *  Creates a new Enumerator object, which is to be used as an
 | 
			
		||||
 *  Enumerable object using the given object's given method with the
 | 
			
		||||
 *  given arguments.
 | 
			
		||||
 *  Enumerable object iterating in a given way.
 | 
			
		||||
 *
 | 
			
		||||
 *  Use of this method is discouraged.  Use Kernel#enum_for() instead.
 | 
			
		||||
 *  In the first form, a generated Enumerator iterates over the given
 | 
			
		||||
 *  object using the given method with the given arguments passed.
 | 
			
		||||
 *  Use of this form is discouraged.  Use Kernel#enum_for(), alias
 | 
			
		||||
 *  to_enum, instead.
 | 
			
		||||
 *
 | 
			
		||||
 *    e = Enumerator.new(ObjectSpace, :each_object)
 | 
			
		||||
 *        #-> ObjectSpace.enum_for(:each_object)
 | 
			
		||||
 *
 | 
			
		||||
 *    e.select { |obj| obj.is_a?(Class) }  #=> array of all classes
 | 
			
		||||
 *
 | 
			
		||||
 *  In the second form, iteration is defined by the given block, in
 | 
			
		||||
 *  which a "yielder" object given as block parameter can be used to
 | 
			
		||||
 *  yield a value by calling the +yield+ method, alias +<<+.
 | 
			
		||||
 *
 | 
			
		||||
 *    fib = Enumerator.new { |y|
 | 
			
		||||
 *      a = b = 1
 | 
			
		||||
 *      loop {
 | 
			
		||||
 *        y << a
 | 
			
		||||
 *        a, b = b, a + b
 | 
			
		||||
 *      }
 | 
			
		||||
 *    }
 | 
			
		||||
 *
 | 
			
		||||
 *    p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
 | 
			
		||||
 */
 | 
			
		||||
static VALUE
 | 
			
		||||
enumerator_initialize(argc, argv, obj)
 | 
			
		||||
| 
						 | 
				
			
			@ -307,13 +347,19 @@ enumerator_initialize(argc, argv, obj)
 | 
			
		|||
{
 | 
			
		||||
    VALUE recv, meth = sym_each;
 | 
			
		||||
 | 
			
		||||
    if (argc == 0)
 | 
			
		||||
	rb_raise(rb_eArgError, "wrong number of argument (0 for 1)");
 | 
			
		||||
    recv = *argv++;
 | 
			
		||||
    if (--argc) {
 | 
			
		||||
	meth = *argv++;
 | 
			
		||||
	--argc;
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
	if (!rb_block_given_p())
 | 
			
		||||
	    rb_raise(rb_eArgError, "wrong number of argument (0 for 1+)");
 | 
			
		||||
 | 
			
		||||
	recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc());
 | 
			
		||||
    } else {
 | 
			
		||||
	recv = *argv++;
 | 
			
		||||
	if (--argc) {
 | 
			
		||||
	    meth = *argv++;
 | 
			
		||||
	    --argc;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return enumerator_init(obj, recv, meth, argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -490,6 +536,242 @@ enumerator_rewind(obj)
 | 
			
		|||
    return rb_funcall(obj, rb_intern("rewind"), 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Yielder
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void yielder_mark _((void *));
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
yielder_mark(void *p)
 | 
			
		||||
{
 | 
			
		||||
    struct yielder *ptr = p;
 | 
			
		||||
    rb_gc_mark(ptr->proc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct yielder *
 | 
			
		||||
yielder_ptr(VALUE obj)
 | 
			
		||||
{
 | 
			
		||||
    struct yielder *ptr;
 | 
			
		||||
 | 
			
		||||
    Data_Get_Struct(obj, struct yielder, ptr);
 | 
			
		||||
    if (RDATA(obj)->dmark != yielder_mark) {
 | 
			
		||||
	rb_raise(rb_eTypeError,
 | 
			
		||||
		 "wrong argument type %s (expected %s)",
 | 
			
		||||
		 rb_obj_classname(obj), rb_class2name(rb_cYielder));
 | 
			
		||||
    }
 | 
			
		||||
    if (!ptr || ptr->proc == Qundef) {
 | 
			
		||||
	rb_raise(rb_eArgError, "uninitialized yielder");
 | 
			
		||||
    }
 | 
			
		||||
    return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE yielder_allocate _((VALUE));
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_allocate(klass)
 | 
			
		||||
    VALUE klass;
 | 
			
		||||
{
 | 
			
		||||
    struct yielder *ptr;
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
 | 
			
		||||
    obj = Data_Make_Struct(klass, struct yielder, yielder_mark, -1, ptr);
 | 
			
		||||
    ptr->proc = Qundef;
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_init(obj, proc)
 | 
			
		||||
    VALUE obj, proc;
 | 
			
		||||
{
 | 
			
		||||
    struct yielder *ptr;
 | 
			
		||||
 | 
			
		||||
    Data_Get_Struct(obj, struct yielder, ptr);
 | 
			
		||||
 | 
			
		||||
    if (!ptr) {
 | 
			
		||||
	rb_raise(rb_eArgError, "unallocated yielder");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ptr->proc = proc;
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_initialize(obj)
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
{
 | 
			
		||||
    rb_need_block();
 | 
			
		||||
 | 
			
		||||
    return yielder_init(obj, rb_block_proc());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_yield(obj, args)
 | 
			
		||||
    VALUE obj, args;
 | 
			
		||||
{
 | 
			
		||||
    struct yielder *ptr = yielder_ptr(obj);
 | 
			
		||||
 | 
			
		||||
    rb_proc_call(ptr->proc, args);
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_new_i(dummy)
 | 
			
		||||
    VALUE dummy;
 | 
			
		||||
{
 | 
			
		||||
    return yielder_init(yielder_allocate(rb_cYielder), rb_block_proc());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_yield_i(obj, memo)
 | 
			
		||||
    VALUE obj, memo;
 | 
			
		||||
{
 | 
			
		||||
    return rb_yield(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
yielder_new()
 | 
			
		||||
{
 | 
			
		||||
    return rb_iterate(yielder_new_i, (VALUE)0, yielder_yield_i, (VALUE)0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Generator
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void generator_mark _((void *));
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
generator_mark(p)
 | 
			
		||||
    void *p;
 | 
			
		||||
{
 | 
			
		||||
    struct generator *ptr = p;
 | 
			
		||||
    rb_gc_mark(ptr->proc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct generator *
 | 
			
		||||
generator_ptr(obj)
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
{
 | 
			
		||||
    struct generator *ptr;
 | 
			
		||||
 | 
			
		||||
    Data_Get_Struct(obj, struct generator, ptr);
 | 
			
		||||
    if (RDATA(obj)->dmark != generator_mark) {
 | 
			
		||||
	rb_raise(rb_eTypeError,
 | 
			
		||||
		 "wrong argument type %s (expected %s)",
 | 
			
		||||
		 rb_obj_classname(obj), rb_class2name(rb_cGenerator));
 | 
			
		||||
    }
 | 
			
		||||
    if (!ptr || ptr->proc == Qundef) {
 | 
			
		||||
	rb_raise(rb_eArgError, "uninitialized generator");
 | 
			
		||||
    }
 | 
			
		||||
    return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE generator_allocate _((VALUE));
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
generator_allocate(klass)
 | 
			
		||||
    VALUE klass;
 | 
			
		||||
{
 | 
			
		||||
    struct generator *ptr;
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
 | 
			
		||||
    obj = Data_Make_Struct(klass, struct generator, generator_mark, -1, ptr);
 | 
			
		||||
    ptr->proc = Qundef;
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
generator_init(obj, proc)
 | 
			
		||||
    VALUE obj, proc;
 | 
			
		||||
{
 | 
			
		||||
    struct generator *ptr;
 | 
			
		||||
 | 
			
		||||
    Data_Get_Struct(obj, struct generator, ptr);
 | 
			
		||||
 | 
			
		||||
    if (!ptr) {
 | 
			
		||||
	rb_raise(rb_eArgError, "unallocated generator");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ptr->proc = proc;
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE rb_obj_is_proc _((VALUE));
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
generator_initialize(argc, argv, obj)
 | 
			
		||||
    int argc;
 | 
			
		||||
    VALUE *argv;
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
{
 | 
			
		||||
    VALUE proc;
 | 
			
		||||
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
	rb_need_block();
 | 
			
		||||
 | 
			
		||||
	proc = rb_block_proc();
 | 
			
		||||
    } else {
 | 
			
		||||
	rb_scan_args(argc, argv, "1", &proc);
 | 
			
		||||
 | 
			
		||||
	if (!rb_obj_is_proc(proc))
 | 
			
		||||
	    rb_raise(rb_eTypeError,
 | 
			
		||||
		     "wrong argument type %s (expected Proc)",
 | 
			
		||||
		     rb_obj_classname(proc));
 | 
			
		||||
 | 
			
		||||
	if (rb_block_given_p()) {
 | 
			
		||||
	    rb_warn("given block not used");
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return generator_init(obj, proc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
generator_init_copy(obj, orig)
 | 
			
		||||
    VALUE obj, orig;
 | 
			
		||||
{
 | 
			
		||||
    struct generator *ptr0, *ptr1;
 | 
			
		||||
 | 
			
		||||
    ptr0 = generator_ptr(orig);
 | 
			
		||||
 | 
			
		||||
    Data_Get_Struct(obj, struct generator, ptr1);
 | 
			
		||||
 | 
			
		||||
    if (!ptr1) {
 | 
			
		||||
	rb_raise(rb_eArgError, "unallocated generator");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ptr1->proc = ptr0->proc;
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
static VALUE
 | 
			
		||||
generator_each(obj)
 | 
			
		||||
    VALUE obj;
 | 
			
		||||
{
 | 
			
		||||
    struct generator *ptr = generator_ptr(obj);
 | 
			
		||||
    VALUE yielder;
 | 
			
		||||
 | 
			
		||||
    yielder = yielder_new();
 | 
			
		||||
 | 
			
		||||
    rb_proc_call(ptr->proc, rb_ary_new3(1, yielder));
 | 
			
		||||
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Init_Enumerator()
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -518,6 +800,21 @@ Init_Enumerator()
 | 
			
		|||
 | 
			
		||||
    rb_eStopIteration   = rb_define_class("StopIteration", rb_eIndexError);
 | 
			
		||||
 | 
			
		||||
    /* Generator */
 | 
			
		||||
    rb_cGenerator = rb_define_class_under(rb_cEnumerator, "Generator", rb_cObject);
 | 
			
		||||
    rb_include_module(rb_cGenerator, rb_mEnumerable);
 | 
			
		||||
    rb_define_alloc_func(rb_cGenerator, generator_allocate);
 | 
			
		||||
    rb_define_method(rb_cGenerator, "initialize", generator_initialize, -1);
 | 
			
		||||
    rb_define_method(rb_cGenerator, "initialize_copy", generator_init_copy, 1);
 | 
			
		||||
    rb_define_method(rb_cGenerator, "each", generator_each, 0);
 | 
			
		||||
 | 
			
		||||
    /* Yielder */
 | 
			
		||||
    rb_cYielder = rb_define_class_under(rb_cEnumerator, "Yielder", rb_cObject);
 | 
			
		||||
    rb_define_alloc_func(rb_cYielder, yielder_allocate);
 | 
			
		||||
    rb_define_method(rb_cYielder, "initialize", yielder_initialize, 0);
 | 
			
		||||
    rb_define_method(rb_cYielder, "yield", yielder_yield, -2);
 | 
			
		||||
    rb_define_method(rb_cYielder, "<<", yielder_yield, -2);
 | 
			
		||||
 | 
			
		||||
    sym_each	 	= ID2SYM(rb_intern("each"));
 | 
			
		||||
 | 
			
		||||
    /* backward compatibility */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue