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