From 6a326684c2792a521f0e8c56093a49f6d68bd962 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Thu, 29 Aug 2019 20:05:10 +0900 Subject: [PATCH] Implement Enumerator.produce [Feature #14781] --- enumerator.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/enumerator.c b/enumerator.c index 96daad2b4b..c2fd88062a 100644 --- a/enumerator.c +++ b/enumerator.c @@ -135,7 +135,7 @@ struct enumerator { rb_enumerator_size_func *size_fn; }; -static VALUE rb_cGenerator, rb_cYielder; +static VALUE rb_cGenerator, rb_cYielder, rb_cEnumProducer; struct generator { VALUE proc; @@ -146,6 +146,11 @@ struct yielder { VALUE proc; }; +struct producer { + VALUE init; + VALUE proc; +}; + typedef struct MEMO *lazyenum_proc_func(VALUE, struct MEMO *, VALUE, long); typedef VALUE lazyenum_size_func(VALUE, VALUE); typedef struct { @@ -2701,6 +2706,150 @@ stop_result(VALUE self) return rb_attr_get(self, id_result); } +/* + * Producer + */ + +static void +producer_mark(void *p) +{ + struct producer *ptr = p; + rb_gc_mark_movable(ptr->init); + rb_gc_mark_movable(ptr->proc); +} + +static void +producer_compact(void *p) +{ + struct producer *ptr = p; + ptr->init = rb_gc_location(ptr->init); + ptr->proc = rb_gc_location(ptr->proc); +} + +#define producer_free RUBY_TYPED_DEFAULT_FREE + +static size_t +producer_memsize(const void *p) +{ + return sizeof(struct producer); +} + +static const rb_data_type_t producer_data_type = { + "producer", + { + producer_mark, + producer_free, + producer_memsize, + producer_compact, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY +}; + +static struct producer * +producer_ptr(VALUE obj) +{ + struct producer *ptr; + + TypedData_Get_Struct(obj, struct producer, &producer_data_type, ptr); + if (!ptr || ptr->proc == Qundef) { + rb_raise(rb_eArgError, "uninitialized producer"); + } + return ptr; +} + +/* :nodoc: */ +static VALUE +producer_allocate(VALUE klass) +{ + struct producer *ptr; + VALUE obj; + + obj = TypedData_Make_Struct(klass, struct producer, &producer_data_type, ptr); + ptr->init = Qundef; + ptr->proc = Qundef; + + return obj; +} + +static VALUE +producer_init(VALUE obj, VALUE init, VALUE proc) +{ + struct producer *ptr; + + TypedData_Get_Struct(obj, struct producer, &producer_data_type, ptr); + + if (!ptr) { + rb_raise(rb_eArgError, "unallocated producer"); + } + + ptr->init = init; + ptr->proc = proc; + + return obj; +} + +static VALUE +producer_each_stop(VALUE dummy, VALUE exc) +{ + return rb_attr_get(exc, id_result); +} + +static VALUE +producer_each_i(VALUE obj) +{ + struct producer *ptr; + VALUE init, proc, curr; + + ptr = producer_ptr(obj); + init = ptr->init; + proc = ptr->proc; + + if (init == Qundef) { + curr = Qnil; + } else { + rb_yield(init); + curr = init; + } + + for (;;) { + curr = rb_funcall(proc, id_call, 1, curr); + rb_yield(curr); + } + + return Qnil; +} + +/* :nodoc: */ +static VALUE +producer_each(VALUE obj) +{ + rb_need_block(); + + return rb_rescue2(producer_each_i, obj, producer_each_stop, (VALUE)0, rb_eStopIteration, (VALUE)0); +} + +static VALUE +producer_size(VALUE obj, VALUE args, VALUE eobj) +{ + return DBL2NUM(HUGE_VAL); +} + +static VALUE +enumerator_s_produce(int argc, VALUE *argv, VALUE klass) +{ + VALUE init, producer; + + rb_need_block(); + + if (rb_scan_args(argc, argv, "01", &init) == 0) { + init = Qundef; + } + + producer = producer_init(producer_allocate(rb_cEnumProducer), init, rb_block_proc()); + + return rb_enumeratorize_with_size(producer, sym_each, 0, 0, producer_size); +} + /* * Document-class: Enumerator::Chain * @@ -3684,6 +3833,12 @@ InitVM_Enumerator(void) rb_define_method(rb_cYielder, "<<", yielder_yield_push, 1); rb_define_method(rb_cYielder, "to_proc", yielder_to_proc, 0); + /* Producer */ + rb_cEnumProducer = rb_define_class_under(rb_cEnumerator, "Producer", rb_cObject); + rb_define_alloc_func(rb_cEnumProducer, producer_allocate); + rb_define_method(rb_cEnumProducer, "each", producer_each, 0); + rb_define_singleton_method(rb_cEnumerator, "produce", enumerator_s_produce, -1); + /* Chain */ rb_cEnumChain = rb_define_class_under(rb_cEnumerator, "Chain", rb_cEnumerator); rb_define_alloc_func(rb_cEnumChain, enum_chain_allocate); -- 2.23.0