Skip to content
This repository
Newer
Older
100644 474 lines (410 sloc) 10.72 kb
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
1 /************************************************
2
3 enumerator.c - provides Enumerator class
4
5 $Author: knu $
6
7 Copyright (C) 2001-2003 Akinori MUSHA
8
9 $Idaemons: /home/cvs/rb/enumerator/enumerator.c,v 1.1.1.1 2001/07/15 10:12:48 knu Exp $
10 $RoughId: enumerator.c,v 1.6 2003/07/27 11:03:24 nobu Exp $
11 $Id: enumerator.c 16614 2008-05-26 08:49:08Z knu $
12
13 ************************************************/
14
15 #include "ruby/ruby.h"
301b43b1 » Laurent Sansonetti
2009-04-05 ported to rb_objc_block_call() + misc cleanup
16 #include "id.h"
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
17
18 /*
19 * Document-class: Enumerable::Enumerator
20 *
21 * A class which provides a method `each' to be used as an Enumerable
22 * object.
23 */
24 VALUE rb_cEnumerator;
25 static VALUE sym_each;
26
27 VALUE rb_eStopIteration;
28
29 struct enumerator {
30 VALUE obj;
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
31 SEL sel;
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
32 VALUE args;
33 VALUE fib;
34 VALUE dst;
35 VALUE no_next;
36 };
37
38 static struct enumerator *
39 enumerator_ptr(VALUE obj)
40 {
41 struct enumerator *ptr;
42
43 Data_Get_Struct(obj, struct enumerator, ptr);
44 #if 0
45 if (RDATA(obj)->dmark != enumerator_mark) {
46 rb_raise(rb_eTypeError,
47 "wrong argument type %s (expected %s)",
48 rb_obj_classname(obj), rb_class2name(rb_cEnumerator));
49 }
50 #endif
51 if (!ptr) {
52 rb_raise(rb_eArgError, "uninitialized enumerator");
53 }
54 return ptr;
55 }
56
57 /*
58 * call-seq:
59 * obj.to_enum(method = :each, *args)
60 * obj.enum_for(method = :each, *args)
61 *
62 * Returns Enumerable::Enumerator.new(self, method, *args).
63 *
64 * e.g.:
65 *
66 * str = "xyz"
67 *
68 * enum = str.enum_for(:each_byte)
69 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
70 *
71 * # protects an array from being modified
72 * a = [1, 2, 3]
73 * some_method(a.to_enum)
74 *
75 */
76 static VALUE
77 obj_to_enum(VALUE obj, SEL sel, int argc, VALUE *argv)
78 {
79 VALUE meth = sym_each;
80
81 if (argc > 0) {
82 --argc;
83 meth = *argv++;
84 }
c6a03419 » Laurent Sansonetti
2009-08-14 Kernel#to_enum: implemented
85
86 ID meth_id = SYM2ID(meth);
87 SEL enum_sel;
88 if (argc > 0) {
89 char buf[100];
90 snprintf(buf, sizeof buf, "%s:", rb_id2name(meth_id));
91 enum_sel = sel_registerName(buf);
92 }
93 else {
94 enum_sel = sel_registerName(rb_id2name(meth_id));
95 }
96 return rb_enumeratorize(obj, enum_sel, argc, argv);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
97 }
98
99 static VALUE
100 each_slice_i(VALUE val, VALUE *memo)
101 {
102 VALUE ary = memo[0];
103 VALUE v = Qnil;
104 long size = (long)memo[1];
105
106 rb_ary_push(ary, val);
107
108 if (RARRAY_LEN(ary) == size) {
109 v = rb_yield(ary);
110 memo[0] = rb_ary_new2(size);
111 }
112
113 return v;
114 }
115
116 /*
117 * call-seq:
118 * e.each_slice(n) {...}
119 * e.each_slice(n)
120 *
121 * Iterates the given block for each slice of <n> elements. If no
122 * block is given, returns an enumerator.
123 *
124 * e.g.:
125 * (1..10).each_slice(3) {|a| p a}
126 * # outputs below
127 * [1, 2, 3]
128 * [4, 5, 6]
129 * [7, 8, 9]
130 * [10]
131 *
132 */
133 static VALUE
134 enum_each_slice(VALUE obj, SEL sel, VALUE n)
135 {
136 long size = NUM2LONG(n);
137 VALUE args[2], ary;
138
139 if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
140 RETURN_ENUMERATOR(obj, 1, &n);
141 args[0] = rb_ary_new2(size);
142 args[1] = (VALUE)size;
143
301b43b1 » Laurent Sansonetti
2009-04-05 ported to rb_objc_block_call() + misc cleanup
144 rb_objc_block_call(obj, selEach, cacheEach, 0, 0, each_slice_i, (VALUE)args);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
145
146 ary = args[0];
147 if (RARRAY_LEN(ary) > 0) rb_yield(ary);
148
149 return Qnil;
150 }
151
152 static VALUE
153 each_cons_i(VALUE val, VALUE *memo)
154 {
155 VALUE ary = memo[0];
156 VALUE v = Qnil;
157 long size = (long)memo[1];
158
159 if (RARRAY_LEN(ary) == size) {
160 rb_ary_shift(ary);
161 }
162 rb_ary_push(ary, val);
163 if (RARRAY_LEN(ary) == size) {
164 v = rb_yield(rb_ary_dup(ary));
165 }
166 return v;
167 }
168
169 /*
170 * call-seq:
171 * each_cons(n) {...}
172 * each_cons(n)
173 *
174 * Iterates the given block for each array of consecutive <n>
175 * elements. If no block is given, returns an enumerator.a
176 *
177 * e.g.:
178 * (1..10).each_cons(3) {|a| p a}
179 * # outputs below
180 * [1, 2, 3]
181 * [2, 3, 4]
182 * [3, 4, 5]
183 * [4, 5, 6]
184 * [5, 6, 7]
185 * [6, 7, 8]
186 * [7, 8, 9]
187 * [8, 9, 10]
188 *
189 */
190 static VALUE
191 enum_each_cons(VALUE obj, SEL sel, VALUE n)
192 {
193 long size = NUM2LONG(n);
194 VALUE args[2];
195
196 if (size <= 0) rb_raise(rb_eArgError, "invalid size");
197 RETURN_ENUMERATOR(obj, 1, &n);
198 args[0] = rb_ary_new2(size);
199 args[1] = (VALUE)size;
200
301b43b1 » Laurent Sansonetti
2009-04-05 ported to rb_objc_block_call() + misc cleanup
201 rb_objc_block_call(obj, selEach, cacheEach, 0, 0, each_cons_i, (VALUE)args);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
202
203 return Qnil;
204 }
205
206 static VALUE
207 enumerator_allocate(VALUE klass)
208 {
209 struct enumerator *ptr;
210 return Data_Make_Struct(klass, struct enumerator,
211 NULL, -1, ptr);
212 }
213
214 static VALUE
215 enumerator_each_i(VALUE v, VALUE enum_obj, int argc, VALUE *argv)
216 {
217 return rb_yield_values2(argc, argv);
218 }
219
220 static VALUE
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
221 enumerator_init(VALUE enum_obj, VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
222 {
223 struct enumerator *ptr = enumerator_ptr(enum_obj);
224
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
225 GC_WB(&ptr->obj, obj);
226 ptr->sel = sel;
227 if (argc > 0) {
228 GC_WB(&ptr->args, rb_ary_new4(argc, argv));
229 }
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
230 ptr->fib = 0;
231 ptr->dst = Qnil;
232 ptr->no_next = Qfalse;
233
234 return enum_obj;
235 }
236
237 /*
238 * call-seq:
239 * Enumerable::Enumerator.new(obj, method = :each, *args)
240 *
241 * Creates a new Enumerable::Enumerator object, which is to be
242 * used as an Enumerable object using the given object's given
243 * method with the given arguments.
244 *
245 * Use of this method is not discouraged. Use Kernel#enum_for()
246 * instead.
247 */
248 static VALUE
249 enumerator_initialize(VALUE obj, SEL sel, int argc, VALUE *argv)
250 {
251 VALUE recv, meth = sym_each;
252
253 if (argc == 0)
254 rb_raise(rb_eArgError, "wrong number of argument (0 for 1)");
255 recv = *argv++;
256 if (--argc) {
257 meth = *argv++;
258 --argc;
259 }
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
260 ID meth_id = rb_to_id(meth);
261 SEL meth_sel;
262 if (argc == 0) {
263 meth_sel = sel_registerName(rb_id2name(meth_id));
264 }
265 else {
266 char buf[100];
267 snprintf(buf, sizeof buf, "%s:", rb_id2name(meth_id));
268 meth_sel = sel_registerName(buf);
269 }
270 return enumerator_init(obj, recv, meth_sel, argc, argv);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
271 }
272
273 /* :nodoc: */
274 static VALUE
275 enumerator_init_copy(VALUE obj, SEL sel, VALUE orig)
276 {
277 struct enumerator *ptr0, *ptr1;
278
279 ptr0 = enumerator_ptr(orig);
280 if (ptr0->fib) {
281 /* Fibers cannot be copied */
282 rb_raise(rb_eTypeError, "can't copy execution context");
283 }
284 ptr1 = enumerator_ptr(obj);
285
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
286 GC_WB(&ptr1->obj, ptr0->obj);
287 ptr1->sel = ptr0->sel;
288 if (ptr0->args != 0) {
289 GC_WB(&ptr1->args, ptr0->args);
290 }
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
291 ptr1->fib = 0;
292
293 return obj;
294 }
295
296 VALUE
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
297 rb_enumeratorize(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
298 {
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
299 return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, sel,
300 argc, argv);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
301 }
302
303 /*
304 * call-seq:
305 * enum.each {...}
306 *
307 * Iterates the given block using the object and the method specified
308 * in the first place. If no block is given, returns self.
309 *
310 */
311 static VALUE
312 enumerator_each(VALUE obj, SEL sel)
313 {
314 struct enumerator *e;
315 int argc = 0;
316 const VALUE *argv = 0;
317
318 if (!rb_block_given_p()) return obj;
319 e = enumerator_ptr(obj);
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
320 if (e->args != 0) {
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
321 argc = RARRAY_LEN(e->args);
322 argv = RARRAY_PTR(e->args);
323 }
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
324 return rb_objc_block_call(e->obj, e->sel, NULL, argc, (VALUE *)argv,
325 enumerator_each_i, (VALUE)e);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
326 }
327
328 static VALUE
329 enumerator_with_index_i(VALUE val, VALUE *memo)
330 {
331 val = rb_yield_values(2, val, INT2FIX(*memo));
332 ++*memo;
333 return val;
334 }
335
336 /*
337 * call-seq:
338 * e.with_index {|(*args), idx| ... }
339 * e.with_index
340 *
341 * Iterates the given block for each elements with an index, which
342 * start from 0. If no block is given, returns an enumerator.
343 *
344 */
345 static VALUE
346 enumerator_with_index(VALUE obj, SEL sel)
347 {
348 struct enumerator *e;
349 VALUE memo = 0;
350 int argc = 0;
351 const VALUE *argv = 0;
352
353 RETURN_ENUMERATOR(obj, 0, 0);
354 e = enumerator_ptr(obj);
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
355 if (e->args != 0) {
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
356 argc = RARRAY_LEN(e->args);
357 argv = RARRAY_PTR(e->args);
358 }
a6a914f3 » Laurent Sansonetti
2009-04-05 implemented implicit enumeratorization
359 return rb_objc_block_call(e->obj, e->sel, NULL, argc, (VALUE *)argv,
360 enumerator_with_index_i, (VALUE)&memo);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
361 }
362
363 #if 0
364 static VALUE
365 next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
366 {
367 rb_fiber_yield(argc, argv);
368 return Qnil;
369 }
370
371 static VALUE
372 next_i(VALUE curr, VALUE obj)
373 {
374 struct enumerator *e = enumerator_ptr(obj);
375 VALUE rnil = Qnil;
376
377 rb_block_call(obj, rb_intern("each"), 0, 0, next_ii, obj);
378 e->no_next = Qtrue;
379 return rb_fiber_yield(1, &rnil);
380 }
381
382 static void
383 next_init(VALUE obj, struct enumerator *e)
384 {
385 VALUE curr = rb_fiber_current();
386 e->dst = curr;
387 e->fib = rb_fiber_new(next_i, obj);
388 }
389 #endif
390
391 /*
392 * call-seq:
393 * e.next => object
394 *
395 * Returns the next object in the enumerator, and move the internal
396 * position forward. When the position reached at the end, internal
397 * position is rewinded then StopIteration is raised.
398 *
399 * Note that enumeration sequence by next method does not affect other
400 * non-external enumeration methods, unless underlying iteration
401 * methods itself has side-effect, e.g. IO#each_line.
402 *
403 */
404
405 static VALUE
406 enumerator_next(VALUE obj, SEL sel)
407 {
408 // TODO
409 #if 0
410 struct enumerator *e = enumerator_ptr(obj);
411 VALUE curr, v;
412 curr = rb_fiber_current();
413
414 if (!e->fib || !rb_fiber_alive_p(e->fib)) {
415 next_init(obj, e);
416 }
417
418 v = rb_fiber_resume(e->fib, 1, &curr);
419 if (e->no_next) {
420 e->fib = 0;
421 e->dst = Qnil;
422 e->no_next = Qfalse;
423 rb_raise(rb_eStopIteration, "iteration reached at end");
424 }
425 return v;
426 #endif
427 return Qnil;
428 }
429
430 /*
431 * call-seq:
432 * e.rewind => e
433 *
434 * Rewinds the enumeration sequence by the next method.
435 */
436
437 static VALUE
438 enumerator_rewind(VALUE obj, SEL sel)
439 {
440 struct enumerator *e = enumerator_ptr(obj);
441
442 e->fib = 0;
443 e->dst = Qnil;
444 e->no_next = Qfalse;
445 return obj;
446 }
447
448 void
449 Init_Enumerator(void)
450 {
451 rb_objc_define_method(rb_mKernel, "to_enum", obj_to_enum, -1);
452 rb_objc_define_method(rb_mKernel, "enum_for", obj_to_enum, -1);
453
454 rb_objc_define_method(rb_mEnumerable, "each_slice", enum_each_slice, 1);
455 rb_objc_define_method(rb_mEnumerable, "each_cons", enum_each_cons, 1);
456
fabdd03c » Laurent Sansonetti
2009-06-06 Enumerator should be a top-level class
457 rb_cEnumerator = rb_define_class("Enumerator", rb_cObject);
9c1d2307 » Laurent Sansonetti
2009-03-11 committing experimental branch content
458 rb_include_module(rb_cEnumerator, rb_mEnumerable);
459
460 rb_objc_define_method(*(VALUE *)rb_cEnumerator, "alloc", enumerator_allocate, 0);
461 rb_objc_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
462 rb_objc_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
463 rb_objc_define_method(rb_cEnumerator, "each", enumerator_each, 0);
464 rb_objc_define_method(rb_cEnumerator, "each_with_index", enumerator_with_index, 0);
465 rb_objc_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0);
466 rb_objc_define_method(rb_cEnumerator, "next", enumerator_next, 0);
467 rb_objc_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
468
469 rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
470
471 sym_each = ID2SYM(rb_intern("each"));
472
473 rb_provide("enumerator.so"); /* for backward compatibility */
474 }
Something went wrong with that request. Please try again.