Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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