Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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