Feature #6183 » 16.05.2013.diff
enumerator.c | ||
---|---|---|
VALUE stop_exc;
|
||
VALUE size;
|
||
VALUE (*size_fn)(ANYARGS);
|
||
VALUE procs;
|
||
};
|
||
static VALUE rb_cGenerator, rb_cYielder;
|
||
struct generator {
|
||
VALUE proc;
|
||
VALUE obj;
|
||
VALUE hybrid;
|
||
};
|
||
struct yielder {
|
||
VALUE proc;
|
||
};
|
||
struct proc_entry {
|
||
VALUE proc;
|
||
VALUE memo;
|
||
NODE * (*proc_fn)(ANYARGS);
|
||
VALUE (*size_fn)(ANYARGS);
|
||
};
|
||
static VALUE generator_allocate(VALUE klass);
|
||
static VALUE generator_init(VALUE obj, VALUE proc);
|
||
... | ... | |
rb_gc_mark(ptr->feedvalue);
|
||
rb_gc_mark(ptr->stop_exc);
|
||
rb_gc_mark(ptr->size);
|
||
rb_gc_mark(ptr->procs);
|
||
}
|
||
#define enumerator_free RUBY_TYPED_DEFAULT_FREE
|
||
... | ... | |
return ptr;
|
||
}
|
||
static void
|
||
proc_entry_mark(void *p)
|
||
{
|
||
struct proc_entry *ptr = p;
|
||
rb_gc_mark(ptr->proc);
|
||
rb_gc_mark(ptr->memo);
|
||
}
|
||
#define proc_entry_free RUBY_TYPED_DEFAULT_FREE
|
||
static size_t
|
||
proc_entry_memsize(const void *p)
|
||
{
|
||
return p ? sizeof(struct proc_entry) : 0;
|
||
}
|
||
static const rb_data_type_t proc_entry_data_type = {
|
||
"proc_entry",
|
||
{
|
||
proc_entry_mark,
|
||
proc_entry_free,
|
||
proc_entry_memsize,
|
||
},
|
||
};
|
||
static struct proc_entry *
|
||
proc_entry_ptr(VALUE proc_entry) {
|
||
struct proc_entry *ptr;
|
||
TypedData_Get_Struct(proc_entry, struct proc_entry, &proc_entry_data_type, ptr);
|
||
return ptr;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* obj.to_enum(method = :each, *args) -> enum
|
||
... | ... | |
return obj;
|
||
}
|
||
static struct generator * generator_ptr(VALUE obj);
|
||
static VALUE
|
||
append_method(VALUE obj, VALUE str, ID default_method)
|
||
{
|
||
VALUE method;
|
||
method = rb_attr_get(obj, id_method);
|
||
if (NIL_P(method)) {
|
||
rb_str_buf_cat2(str, ":");
|
||
rb_str_buf_cat2(str, rb_id2name(default_method));
|
||
}
|
||
else if (method != Qfalse) {
|
||
Check_Type(method, T_SYMBOL);
|
||
rb_str_buf_cat2(str, ":");
|
||
rb_str_buf_cat2(str, rb_id2name(SYM2ID(method)));
|
||
}
|
||
return str;
|
||
}
|
||
static VALUE
|
||
append_args(VALUE obj, VALUE str, VALUE default_args)
|
||
{
|
||
VALUE eargs;
|
||
int tainted, untrusted;
|
||
eargs = rb_attr_get(obj, id_arguments);
|
||
if (NIL_P(eargs)) {
|
||
eargs = default_args;
|
||
}
|
||
if (eargs != Qfalse) {
|
||
long argc = RARRAY_LEN(eargs);
|
||
VALUE *argv = RARRAY_PTR(eargs);
|
||
if (argc > 0) {
|
||
rb_str_buf_cat2(str, "(");
|
||
while (argc--) {
|
||
VALUE arg = *argv++;
|
||
rb_str_concat(str, rb_inspect(arg));
|
||
rb_str_buf_cat2(str, argc > 0 ? ", " : ")");
|
||
if (OBJ_TAINTED(arg)) tainted = TRUE;
|
||
if (OBJ_UNTRUSTED(arg)) untrusted = TRUE;
|
||
}
|
||
}
|
||
}
|
||
if (tainted) { OBJ_TAINT(str); }
|
||
if (untrusted) { OBJ_UNTRUST(str); }
|
||
return str;
|
||
}
|
||
static VALUE
|
||
inspect_enumerator(VALUE obj, VALUE dummy, int recur)
|
||
{
|
||
struct enumerator *e;
|
||
struct generator *g;
|
||
const char *cname;
|
||
VALUE eobj, eargs, str, method;
|
||
VALUE eobj, str;
|
||
int tainted, untrusted;
|
||
int i;
|
||
TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, e);
|
||
... | ... | |
return str;
|
||
}
|
||
eobj = rb_attr_get(obj, id_receiver);
|
||
if (NIL_P(eobj)) {
|
||
eobj = e->obj;
|
||
if (e->procs) {
|
||
g = generator_ptr(e->obj);
|
||
eobj = g->obj;
|
||
} else {
|
||
eobj = rb_attr_get(obj, id_receiver);
|
||
if (NIL_P(eobj)) {
|
||
eobj = e->obj;
|
||
}
|
||
}
|
||
tainted = OBJ_TAINTED(eobj);
|
||
untrusted = OBJ_UNTRUSTED(eobj);
|
||
/* (1..100).each_cons(2) => "#<Enumerator: 1..100:each_cons(2)>" */
|
||
str = rb_sprintf("#<%s: ", cname);
|
||
rb_str_concat(str, rb_inspect(eobj));
|
||
method = rb_attr_get(obj, id_method);
|
||
if (NIL_P(method)) {
|
||
rb_str_buf_cat2(str, ":");
|
||
rb_str_buf_cat2(str, rb_id2name(e->meth));
|
||
}
|
||
else if (method != Qfalse) {
|
||
Check_Type(method, T_SYMBOL);
|
||
rb_str_buf_cat2(str, ":");
|
||
rb_str_buf_cat2(str, rb_id2name(SYM2ID(method)));
|
||
}
|
||
eargs = rb_attr_get(obj, id_arguments);
|
||
if (NIL_P(eargs)) {
|
||
eargs = e->args;
|
||
}
|
||
if (eargs != Qfalse) {
|
||
long argc = RARRAY_LEN(eargs);
|
||
VALUE *argv = RARRAY_PTR(eargs);
|
||
if (argc > 0) {
|
||
rb_str_buf_cat2(str, "(");
|
||
while (argc--) {
|
||
VALUE arg = *argv++;
|
||
rb_str_concat(str, rb_inspect(arg));
|
||
rb_str_buf_cat2(str, argc > 0 ? ", " : ")");
|
||
/* (1..100).each_cons(2) => "#<Enumerator: 1..100:each_cons(2)>"
|
||
* In case procs chained enumerator traversing all proc entries manually
|
||
*/
|
||
if (e->procs) {
|
||
if (strcmp(rb_obj_classname(eobj), cname) == 0) {
|
||
str = rb_inspect(eobj);
|
||
} else {
|
||
str = rb_sprintf("#<%s: ", cname);
|
||
rb_str_concat(str, rb_inspect(eobj));
|
||
rb_str_buf_cat2(str, ">");
|
||
}
|
||
for (i = 0; i < RARRAY_LEN(e->procs); i++) {
|
||
str = rb_str_concat(rb_sprintf("#<%s: ", cname), str);
|
||
append_method(RARRAY_AREF(e->procs, i), str, e->meth);
|
||
append_args(RARRAY_AREF(e->procs, i), str, e->args);
|
||
rb_str_buf_cat2(str, ">");
|
||
}
|
||
} else {
|
||
str = rb_sprintf("#<%s: ", cname);
|
||
rb_str_concat(str, rb_inspect(eobj));
|
||
append_method(obj, str, e->meth);
|
||
append_args(obj, str, e->args);
|
||
if (OBJ_TAINTED(arg)) tainted = TRUE;
|
||
if (OBJ_UNTRUSTED(arg)) untrusted = TRUE;
|
||
}
|
||
}
|
||
rb_str_buf_cat2(str, ">");
|
||
}
|
||
rb_str_buf_cat2(str, ">");
|
||
if (tainted) OBJ_TAINT(str);
|
||
if (untrusted) OBJ_UNTRUST(str);
|
||
return str;
|
||
... | ... | |
* (1..100).drop_while.size # => nil
|
||
*/
|
||
static struct generator * generator_ptr(VALUE obj);
|
||
static VALUE
|
||
enumerator_size(VALUE obj)
|
||
{
|
||
struct enumerator *e = enumerator_ptr(obj);
|
||
if (e->size_fn) {
|
||
return (*e->size_fn)(e->obj, e->args, obj);
|
||
}
|
||
if (rb_obj_is_proc(e->size)) {
|
||
if (e->args)
|
||
return rb_proc_call(e->size, e->args);
|
||
else
|
||
return rb_proc_call_with_block(e->size, 0, 0, Qnil);
|
||
long i = 0;
|
||
struct generator *g;
|
||
struct proc_entry *entry;
|
||
VALUE receiver;
|
||
if (e->procs) {
|
||
g = generator_ptr(e->obj);
|
||
receiver = rb_check_funcall(g->obj, id_size, 0, 0);
|
||
for(i = 0; i < RARRAY_LEN(e->procs); i++) {
|
||
entry = proc_entry_ptr(RARRAY_AREF(e->procs, i));
|
||
if (entry->size_fn) {
|
||
receiver = (*entry->size_fn)(RARRAY_AREF(e->procs, i), receiver);
|
||
} else {
|
||
return Qnil;
|
||
}
|
||
}
|
||
return receiver;
|
||
} else {
|
||
if (e->size_fn) {
|
||
return (*e->size_fn)(e->obj, e->args, obj);
|
||
}
|
||
if (rb_obj_is_proc(e->size)) {
|
||
if (e->args)
|
||
return rb_proc_call(e->size, e->args);
|
||
else
|
||
return rb_proc_call_with_block(e->size, 0, 0, Qnil);
|
||
}
|
||
return e->size;
|
||
}
|
||
return e->size;
|
||
}
|
||
/*
|
||
... | ... | |
{
|
||
struct generator *ptr = p;
|
||
rb_gc_mark(ptr->proc);
|
||
rb_gc_mark(ptr->obj);
|
||
rb_gc_mark(ptr->hybrid);
|
||
}
|
||
#define generator_free RUBY_TYPED_DEFAULT_FREE
|
||
... | ... | |
}
|
||
/* Lazy Enumerator methods */
|
||
static VALUE
|
||
enum_size(VALUE self)
|
||
{
|
||
VALUE r = rb_check_funcall(self, id_size, 0, 0);
|
||
VALUE r;
|
||
r = rb_check_funcall(self, id_size, 0, 0);
|
||
return (r == Qundef) ? Qnil : r;
|
||
}
|
||
... | ... | |
return Qnil;
|
||
}
|
||
static VALUE
|
||
lazy_init_yielder(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
{
|
||
VALUE yielder = RARRAY_AREF(m, 0);
|
||
VALUE procs_array = RARRAY_AREF(m, 1);
|
||
struct proc_entry *entry;
|
||
VALUE memos = rb_attr_get(yielder, id_memo);
|
||
long i = 0;
|
||
NODE *result;
|
||
result = NEW_MEMO(Qtrue, rb_enum_values_pack(argc, argv), Qfalse);
|
||
for (i = 0; i < RARRAY_LEN(procs_array); i++) {
|
||
entry = proc_entry_ptr(RARRAY_AREF(procs_array, i));
|
||
if (RTEST(result->u1.value)) {
|
||
(*entry->proc_fn)(RARRAY_AREF(procs_array, i), result, memos, i);
|
||
}
|
||
}
|
||
if (RTEST(result->u1.value)) {
|
||
rb_funcall2(yielder, id_yield, 1, &(result->u2.value));
|
||
}
|
||
if (RTEST(result->u3.value)) {
|
||
rb_iter_break();
|
||
}
|
||
return result->u2.value;
|
||
return Qnil;
|
||
}
|
||
static VALUE
|
||
lazy_init_block(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
{
|
||
VALUE procs = RARRAY_AREF(m, 1);
|
||
rb_ivar_set(val, id_memo, rb_ary_new2(RARRAY_LEN(procs)));
|
||
rb_block_call(RARRAY_AREF(m, 0), id_each, 0, 0,
|
||
lazy_init_yielder, rb_ary_new3(2, val, procs));
|
||
return Qnil;
|
||
}
|
||
static VALUE
|
||
lazy_generator_init(VALUE enumerator, VALUE procs)
|
||
{
|
||
VALUE generator;
|
||
VALUE obj;
|
||
struct generator *gen_ptr;
|
||
struct generator *old_gen_ptr;
|
||
struct enumerator *e = enumerator_ptr(enumerator);
|
||
if (RARRAY_LEN(procs) > 0) {
|
||
old_gen_ptr = generator_ptr(e->obj);
|
||
if (old_gen_ptr->hybrid) {
|
||
obj = enumerator;
|
||
} else {
|
||
obj = old_gen_ptr->obj;
|
||
}
|
||
} else {
|
||
obj = enumerator;
|
||
}
|
||
generator = generator_allocate(rb_cGenerator);
|
||
rb_block_call(generator, id_initialize, 0, 0,
|
||
lazy_init_block, rb_ary_new3(2, obj, procs));
|
||
gen_ptr = generator_ptr(generator);
|
||
gen_ptr->obj = obj;
|
||
return generator;
|
||
}
|
||
static VALUE
|
||
create_proc_entry(VALUE memo, NODE * (*proc_fn)(ANYARGS))
|
||
{
|
||
struct proc_entry *entry;
|
||
VALUE entry_obj;
|
||
entry_obj = TypedData_Make_Struct(rb_cObject, struct proc_entry,
|
||
&proc_entry_data_type, entry);
|
||
if (rb_block_given_p()) {
|
||
entry->proc = rb_block_proc();
|
||
}
|
||
entry->proc_fn = proc_fn;
|
||
entry->memo = memo;
|
||
return entry_obj;
|
||
}
|
||
static VALUE
|
||
lazy_add_proc(VALUE enum_obj, VALUE memo, NODE * (*proc_fn)(ANYARGS))
|
||
{
|
||
struct enumerator *ptr;
|
||
VALUE entry;
|
||
entry = create_proc_entry(memo, proc_fn);
|
||
ptr = enumerator_ptr(enum_obj);
|
||
rb_ary_push(ptr->procs, entry);
|
||
return entry;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* Lazy.new(obj, size=nil) { |yielder, *values| ... }
|
||
... | ... | |
}
|
||
static VALUE
|
||
lazy_proc_entry_set_method(VALUE proc_entry, VALUE args, VALUE (*size_fn)(ANYARGS)) {
|
||
ID id = rb_frame_this_func();
|
||
struct proc_entry *e = proc_entry_ptr(proc_entry);
|
||
rb_ivar_set(proc_entry, id_method, ID2SYM(id));
|
||
if (NIL_P(args)) {
|
||
/* Qfalse indicates that the arguments are empty */
|
||
rb_ivar_set(proc_entry, id_arguments, Qfalse);
|
||
}
|
||
else {
|
||
rb_ivar_set(proc_entry, id_arguments, args);
|
||
}
|
||
e->size_fn = size_fn;
|
||
return proc_entry;
|
||
}
|
||
static VALUE
|
||
lazy_set_method(VALUE lazy, VALUE args, VALUE (*size_fn)(ANYARGS))
|
||
{
|
||
ID id = rb_frame_this_func();
|
||
... | ... | |
return lazy;
|
||
}
|
||
static VALUE
|
||
lazy_copy(int argc, VALUE *argv, VALUE obj)
|
||
{
|
||
struct enumerator *new_e;
|
||
struct generator *g;
|
||
VALUE new_obj;
|
||
VALUE new_generator;
|
||
VALUE new_procs;
|
||
struct enumerator *e = enumerator_ptr(obj);
|
||
if (RTEST(e->procs)) {
|
||
new_procs = rb_ary_new4(RARRAY_LEN(e->procs), RARRAY_PTR(e->procs));
|
||
} else {
|
||
new_procs = rb_ary_new();
|
||
}
|
||
new_generator = lazy_generator_init(obj, new_procs);
|
||
g = generator_ptr(new_generator);
|
||
g->hybrid = Qfalse;
|
||
new_obj = enumerator_init_copy(enumerator_allocate(rb_cLazy), obj);
|
||
new_e = enumerator_ptr(new_obj);
|
||
new_e->obj = new_generator;
|
||
new_e->procs = new_procs;
|
||
new_e->meth = rb_to_id(sym_each);
|
||
if (argc > 0) {
|
||
new_e->meth = rb_to_id(*argv++);
|
||
new_e->args = rb_ary_new4(argc - 1, argv);
|
||
} else {
|
||
new_e->meth = id_each;
|
||
new_e->args = rb_ary_new4(argc, argv);
|
||
}
|
||
return new_obj;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* e.lazy -> lazy_enumerator
|
||
... | ... | |
}
|
||
static VALUE
|
||
lazy_map_func(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
lazy_map_size(VALUE entry, VALUE receiver)
|
||
{
|
||
VALUE result = rb_yield_values2(argc - 1, &argv[1]);
|
||
return receiver;
|
||
}
|
||
rb_funcall(argv[0], id_yield, 1, result);
|
||
return Qnil;
|
||
static NODE *
|
||
lazy_map_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index)
|
||
{
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
result->u2.value = rb_proc_call_with_block(entry->proc, 1, &(result->u2.value), Qnil);
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_map(VALUE obj)
|
||
{
|
||
VALUE new_enum;
|
||
VALUE entry;
|
||
if (!rb_block_given_p()) {
|
||
rb_raise(rb_eArgError, "tried to call lazy map without a block");
|
||
}
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_map_func, 0),
|
||
Qnil, lazy_receiver_size);
|
||
new_enum = lazy_copy(0, 0, obj);
|
||
entry = lazy_add_proc(new_enum, Qnil, lazy_map_func);
|
||
lazy_proc_entry_set_method(entry, Qnil, lazy_map_size);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
... | ... | |
Qnil, 0);
|
||
}
|
||
static VALUE
|
||
lazy_select_func(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
static NODE *
|
||
lazy_select_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index)
|
||
{
|
||
VALUE element = rb_enum_values_pack(argc - 1, argv + 1);
|
||
if (RTEST(rb_yield(element))) {
|
||
return rb_funcall(argv[0], id_yield, 1, element);
|
||
}
|
||
return Qnil;
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
result->u1.value = rb_proc_call_with_block(entry->proc, 1, &(result->u2.value), Qnil);
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_select(VALUE obj)
|
||
{
|
||
VALUE new_enum;
|
||
VALUE entry;
|
||
if (!rb_block_given_p()) {
|
||
rb_raise(rb_eArgError, "tried to call lazy select without a block");
|
||
}
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_select_func, 0),
|
||
Qnil, 0);
|
||
new_enum = lazy_copy(0, 0, obj);
|
||
entry = lazy_add_proc(new_enum, Qnil, lazy_select_func);
|
||
lazy_proc_entry_set_method(entry, Qnil, 0);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
lazy_reject_func(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
{
|
||
VALUE element = rb_enum_values_pack(argc - 1, argv + 1);
|
||
if (!RTEST(rb_yield(element))) {
|
||
return rb_funcall(argv[0], id_yield, 1, element);
|
||
}
|
||
return Qnil;
|
||
static NODE *
|
||
lazy_reject_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index) {
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
result->u1.value = !RTEST(rb_proc_call_with_block(entry->proc, 1, &(result->u2.value), Qnil));
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_reject(VALUE obj)
|
||
{
|
||
VALUE new_enum, entry;
|
||
if (!rb_block_given_p()) {
|
||
rb_raise(rb_eArgError, "tried to call lazy reject without a block");
|
||
}
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_reject_func, 0),
|
||
Qnil, 0);
|
||
new_enum = lazy_copy(0, 0, obj);
|
||
entry = lazy_add_proc(new_enum, Qnil, lazy_reject_func);
|
||
lazy_proc_entry_set_method(entry, Qnil, 0);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
lazy_grep_func(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
{
|
||
VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
|
||
VALUE result = rb_funcall(m, id_eqq, 1, i);
|
||
if (RTEST(result)) {
|
||
rb_funcall(argv[0], id_yield, 1, i);
|
||
}
|
||
return Qnil;
|
||
static NODE *
|
||
lazy_grep_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index) {
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
result->u1.value = rb_funcall(entry->memo, id_eqq, 1, result->u2.value);
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_grep_iter(VALUE val, VALUE m, int argc, VALUE *argv)
|
||
{
|
||
VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
|
||
VALUE result = rb_funcall(m, id_eqq, 1, i);
|
||
static NODE *
|
||
lazy_grep_iter(VALUE proc_entry, NODE *result, VALUE memos, int memo_index) {
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
result->u1.value = rb_funcall(entry->memo, id_eqq, 1, result->u2.value);
|
||
if (RTEST(result)) {
|
||
rb_funcall(argv[0], id_yield, 1, rb_yield(i));
|
||
if (RTEST(result->u1.value)) {
|
||
result->u2.value = rb_proc_call_with_block(entry->proc, 1, &(result->u2.value), Qnil);
|
||
}
|
||
return Qnil;
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_grep(VALUE obj, VALUE pattern)
|
||
{
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
rb_block_given_p() ?
|
||
lazy_grep_iter : lazy_grep_func,
|
||
pattern),
|
||
rb_ary_new3(1, pattern), 0);
|
||
VALUE new_enum, entry;
|
||
new_enum = lazy_copy(0, 0, obj);
|
||
entry = lazy_add_proc(new_enum, pattern, rb_block_given_p() ?
|
||
lazy_grep_iter : lazy_grep_func);
|
||
lazy_proc_entry_set_method(entry, rb_ary_new3(1, pattern), 0);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
... | ... | |
}
|
||
static VALUE
|
||
lazy_take_func(VALUE val, VALUE args, int argc, VALUE *argv)
|
||
lazy_take_size(VALUE entry, VALUE receiver)
|
||
{
|
||
long len = NUM2LONG(RARRAY_AREF(rb_ivar_get(entry, id_arguments), 0));
|
||
if (NIL_P(receiver) || (FIXNUM_P(receiver) && FIX2LONG(receiver) < len))
|
||
return receiver;
|
||
return LONG2NUM(len);
|
||
}
|
||
static NODE *
|
||
lazy_take_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index)
|
||
{
|
||
long remain;
|
||
VALUE memo = rb_attr_get(argv[0], id_memo);
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
VALUE memo = rb_ary_entry(memos, memo_index);
|
||
if (NIL_P(memo)) {
|
||
memo = args;
|
||
memo = entry->memo;
|
||
}
|
||
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
|
||
if ((remain = NUM2LONG(memo)-1) == 0) {
|
||
return Qundef;
|
||
}
|
||
else {
|
||
rb_ivar_set(argv[0], id_memo, LONG2NUM(remain));
|
||
return Qnil;
|
||
remain = NUM2LONG(memo);
|
||
if (remain == 0) {
|
||
result->u1.value = Qfalse;
|
||
result->u3.value = Qtrue;
|
||
} else if(--remain == 0) {
|
||
result->u3.value = Qtrue;
|
||
}
|
||
}
|
||
static VALUE
|
||
lazy_take_size(VALUE generator, VALUE args, VALUE lazy)
|
||
{
|
||
VALUE receiver = lazy_size(lazy);
|
||
long len = NUM2LONG(RARRAY_AREF(rb_ivar_get(lazy, id_arguments), 0));
|
||
if (NIL_P(receiver) || (FIXNUM_P(receiver) && FIX2LONG(receiver) < len))
|
||
return receiver;
|
||
return LONG2NUM(len);
|
||
rb_ary_store(memos, memo_index, LONG2NUM(remain));
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_take(VALUE obj, VALUE n)
|
||
{
|
||
long len = NUM2LONG(n);
|
||
VALUE lazy;
|
||
VALUE new_enum;
|
||
int argc = 0;
|
||
VALUE argv[2];
|
||
VALUE entry;
|
||
if (len < 0) {
|
||
rb_raise(rb_eArgError, "attempt to take negative size");
|
||
}
|
||
if (len == 0) {
|
||
VALUE len = INT2FIX(0);
|
||
lazy = lazy_to_enum_i(obj, sym_cycle, 1, &len, 0);
|
||
}
|
||
else {
|
||
lazy = rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_take_func, n);
|
||
argv[0] = sym_cycle;
|
||
argv[1] = INT2NUM(0);
|
||
argc = 2;
|
||
}
|
||
return lazy_set_method(lazy, rb_ary_new3(1, n), lazy_take_size);
|
||
new_enum = lazy_copy(argc, argv, obj);
|
||
entry = lazy_add_proc(new_enum, n, lazy_take_func);
|
||
lazy_proc_entry_set_method(entry, rb_ary_new3(1, n), lazy_take_size);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
lazy_take_while_func(VALUE val, VALUE args, int argc, VALUE *argv)
|
||
{
|
||
VALUE result = rb_yield_values2(argc - 1, &argv[1]);
|
||
if (!RTEST(result)) return Qundef;
|
||
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
|
||
return Qnil;
|
||
static NODE *
|
||
lazy_take_while_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index) {
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
result->u1.value = rb_proc_call_with_block(entry->proc, 1, &(result->u2.value), Qnil);
|
||
if (!RTEST(result->u1.value)) {
|
||
result->u1.value = Qfalse;
|
||
result->u2.value = Qundef;
|
||
result->u3.value = Qtrue;
|
||
}
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_take_while(VALUE obj)
|
||
{
|
||
VALUE new_enum, entry;
|
||
if (!rb_block_given_p()) {
|
||
rb_raise(rb_eArgError, "tried to call lazy take_while without a block");
|
||
}
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_take_while_func, 0),
|
||
Qnil, 0);
|
||
new_enum = lazy_copy(0, 0, obj);
|
||
entry = lazy_add_proc(new_enum, Qnil, lazy_take_while_func);
|
||
lazy_proc_entry_set_method(entry, Qnil, 0);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
lazy_drop_size(VALUE generator, VALUE args, VALUE lazy)
|
||
lazy_drop_size(VALUE proc_entry, VALUE receiver)
|
||
{
|
||
long len = NUM2LONG(RARRAY_AREF(rb_ivar_get(lazy, id_arguments), 0));
|
||
VALUE receiver = lazy_size(lazy);
|
||
long len = NUM2LONG(RARRAY_AREF(rb_ivar_get(proc_entry, id_arguments), 0));
|
||
if (NIL_P(receiver))
|
||
return receiver;
|
||
if (FIXNUM_P(receiver)) {
|
||
... | ... | |
return rb_funcall(receiver, '-', 1, LONG2NUM(len));
|
||
}
|
||
static VALUE
|
||
lazy_drop_func(VALUE val, VALUE args, int argc, VALUE *argv)
|
||
static NODE *
|
||
lazy_drop_func(VALUE proc_entry, NODE *result, VALUE memos, int memo_index)
|
||
{
|
||
long remain;
|
||
VALUE memo = rb_attr_get(argv[0], id_memo);
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
VALUE memo = rb_ary_entry(memos, memo_index);
|
||
if (NIL_P(memo)) {
|
||
memo = args;
|
||
}
|
||
if ((remain = NUM2LONG(memo)) == 0) {
|
||
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
|
||
memo = entry->memo;
|
||
}
|
||
else {
|
||
rb_ivar_set(argv[0], id_memo, LONG2NUM(--remain));
|
||
remain = NUM2LONG(memo);
|
||
if (remain-- > 0) {
|
||
result->u1.value = Qfalse;
|
||
rb_ary_store(memos, memo_index, LONG2NUM(remain));
|
||
}
|
||
return Qnil;
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_drop(VALUE obj, VALUE n)
|
||
{
|
||
long len = NUM2LONG(n);
|
||
VALUE new_enum, entry;
|
||
VALUE argv[2];
|
||
argv[0] = sym_each;
|
||
argv[1] = n;
|
||
if (len < 0) {
|
||
rb_raise(rb_eArgError, "attempt to drop negative size");
|
||
}
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_drop_func, n),
|
||
rb_ary_new3(1, n), lazy_drop_size);
|
||
new_enum = lazy_copy(2, argv, obj);
|
||
entry = lazy_add_proc(new_enum, n, lazy_drop_func);
|
||
lazy_proc_entry_set_method(entry, rb_ary_new3(1, n), lazy_drop_size);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|
||
lazy_drop_while_func(VALUE val, VALUE args, int argc, VALUE *argv)
|
||
{
|
||
VALUE memo = rb_attr_get(argv[0], id_memo);
|
||
if (NIL_P(memo) && !RTEST(rb_yield_values2(argc - 1, &argv[1]))) {
|
||
rb_ivar_set(argv[0], id_memo, memo = Qtrue);
|
||
static NODE *
|
||
lazy_drop_while_func(VALUE proc_entry, NODE* result, VALUE memos, int memo_index) {
|
||
struct proc_entry *entry = proc_entry_ptr(proc_entry);
|
||
VALUE memo = rb_ary_entry(memos, memo_index);
|
||
if(NIL_P(memo)) {
|
||
memo = entry->memo;
|
||
}
|
||
if (memo == Qtrue) {
|
||
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
|
||
if (!RTEST(memo)) {
|
||
result->u1.value = !RTEST(rb_proc_call_with_block(entry->proc, 1, &(result->u2.value), Qnil));
|
||
if (result->u1.value) {
|
||
rb_ary_store(memos, memo_index, Qtrue);
|
||
}
|
||
}
|
||
return Qnil;
|
||
return result;
|
||
}
|
||
static VALUE
|
||
lazy_drop_while(VALUE obj)
|
||
{
|
||
VALUE new_enum, entry;
|
||
if (!rb_block_given_p()) {
|
||
rb_raise(rb_eArgError, "tried to call lazy drop_while without a block");
|
||
}
|
||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||
lazy_drop_while_func, 0),
|
||
Qnil, 0);
|
||
new_enum = lazy_copy(0, 0, obj);
|
||
entry = lazy_add_proc(new_enum, Qfalse, lazy_drop_while_func);
|
||
lazy_proc_entry_set_method(entry, Qnil, 0);
|
||
return new_enum;
|
||
}
|
||
static VALUE
|