Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 821 lines (693 sloc) 18.587 kb
9595725 update copyrights to 2011
Laurent Sansonetti authored
1 /*
2 * This file is covered by the Ruby license. See COPYING for more details.
3 *
4 * Copyright (C) 2007-2011, Apple Inc. All rights reserved.
5 * Copyright (C) 2001-2003 Akinori MUSHA
6 */
9c1d230 committing experimental branch content
Laurent Sansonetti authored
7
d0898dd include/ruby/macruby.h -> macruby_internal.h
Laurent Sansonetti authored
8 #include "macruby_internal.h"
301b43b ported to rb_objc_block_call() + misc cleanup
Laurent Sansonetti authored
9 #include "id.h"
81232c2 refactor duplicated code
Laurent Sansonetti authored
10 #include "ruby/node.h"
11 #include "vm.h"
9c1d230 committing experimental branch content
Laurent Sansonetti authored
12
13 /*
76080c7 @Watson1978 update rdoc
Watson1978 authored
14 * Document-class: Enumerator
9c1d230 committing experimental branch content
Laurent Sansonetti authored
15 *
16 * A class which provides a method `each' to be used as an Enumerable
17 * object.
76080c7 @Watson1978 update rdoc
Watson1978 authored
18 *
19 * An enumerator can be created by following methods.
20 * - Kernel#to_enum
21 * - Kernel#enum_for
22 * - Enumerator.new
23 *
24 * Also, most iteration methods without a block returns an enumerator.
25 * For example, Array#map returns an enumerator if a block is not given.
26 * The enumerator has the with_index method.
27 * So ary.map.with_index works as follows.
28 *
29 * p %w[foo bar baz].map.with_index {|w,i| "#{i}:#{w}" }
30 * #=> ["0:foo", "1:bar", "2:baz"]
31 *
32 * An enumerator object can be used as an external iterator.
33 * I.e. Enumerator#next returns the next value of the iterator.
34 * Enumerator#next raises StopIteration at end.
35 *
36 * e = [1,2,3].each # returns an enumerator object.
37 * p e.next #=> 1
38 * p e.next #=> 2
39 * p e.next #=> 3
40 * p e.next #raises StopIteration
41 *
42 * An external iterator can be used to implement an internal iterator as follows.
43 *
44 * def ext_each(e)
45 * while true
46 * begin
47 * vs = e.next_values
48 * rescue StopIteration
49 * return $!.result
50 * end
51 * y = yield(*vs)
52 * e.feed y
53 * end
54 * end
55 *
56 * o = Object.new
57 * def o.each
58 * p yield
59 * p yield(1)
60 * p yield(1, 2)
61 * 3
62 * end
63 *
64 * # use o.each as an internal iterator directly.
65 * p o.each {|*x| p x; [:b, *x] }
66 * #=> [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
67 *
68 * # convert o.each to an external iterator for
69 * # implementing an internal iterator.
70 * p ext_each(o.to_enum) {|*x| p x; [:b, *x] }
71 * #=> [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
72 *
9c1d230 committing experimental branch content
Laurent Sansonetti authored
73 */
74 VALUE rb_cEnumerator;
75 static VALUE sym_each;
76
77 VALUE rb_eStopIteration;
78
79 struct enumerator {
80 VALUE obj;
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
81 SEL sel;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
82 VALUE args;
83 VALUE fib;
84 VALUE dst;
85 VALUE no_next;
86 };
87
dd801db @Watson1978 implement Enumerator::{Generator, Yielder}
Watson1978 authored
88 static VALUE rb_cGenerator, rb_cYielder;
89
90 struct generator {
91 VALUE proc;
92 };
93
94 struct yielder {
95 VALUE proc;
96 };
97
98 static VALUE generator_allocate(VALUE klass, SEL sel);
99 static VALUE generator_init(VALUE obj, VALUE proc);
100
9c1d230 committing experimental branch content
Laurent Sansonetti authored
101 static struct enumerator *
102 enumerator_ptr(VALUE obj)
103 {
104 struct enumerator *ptr;
105
106 Data_Get_Struct(obj, struct enumerator, ptr);
107 #if 0
108 if (RDATA(obj)->dmark != enumerator_mark) {
109 rb_raise(rb_eTypeError,
110 "wrong argument type %s (expected %s)",
111 rb_obj_classname(obj), rb_class2name(rb_cEnumerator));
112 }
113 #endif
de68086 @Watson1978 Properly detect if the object is initialized and raise error when app…
Watson1978 authored
114 if (!ptr || ptr->obj == Qundef) {
9c1d230 committing experimental branch content
Laurent Sansonetti authored
115 rb_raise(rb_eArgError, "uninitialized enumerator");
116 }
117 return ptr;
118 }
119
120 /*
121 * call-seq:
122 * obj.to_enum(method = :each, *args)
123 * obj.enum_for(method = :each, *args)
124 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
125 * Returns Enumerator.new(self, method, *args).
9c1d230 committing experimental branch content
Laurent Sansonetti authored
126 *
127 * e.g.:
128 *
129 * str = "xyz"
130 *
131 * enum = str.enum_for(:each_byte)
132 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
133 *
134 * # protects an array from being modified
135 * a = [1, 2, 3]
136 * some_method(a.to_enum)
137 *
138 */
139 static VALUE
140 obj_to_enum(VALUE obj, SEL sel, int argc, VALUE *argv)
141 {
142 VALUE meth = sym_each;
143
144 if (argc > 0) {
145 --argc;
146 meth = *argv++;
147 }
c6a0341 Kernel#to_enum: implemented
Laurent Sansonetti authored
148
e1960f6 Make sure #to_enum verify param is a symbol
Thibault Martin-Lagardette authored
149 ID meth_id = rb_to_id(meth);
81232c2 refactor duplicated code
Laurent Sansonetti authored
150 SEL enum_sel = rb_vm_id_to_sel(meth_id, argc);
c6a0341 Kernel#to_enum: implemented
Laurent Sansonetti authored
151 return rb_enumeratorize(obj, enum_sel, argc, argv);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
152 }
153
154 static VALUE
5b94e0e @Watson1978 enumerator_allocate() needs sel argument because "alloc" method is de…
Watson1978 authored
155 enumerator_allocate(VALUE klass, SEL sel)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
156 {
157 struct enumerator *ptr;
de68086 @Watson1978 Properly detect if the object is initialized and raise error when app…
Watson1978 authored
158 VALUE enum_obj;
159
160 enum_obj = Data_Make_Struct(klass, struct enumerator,
161 NULL, NULL, ptr);
162 ptr->obj = Qundef;
163
164 return enum_obj;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
165 }
166
167 static VALUE
168 enumerator_each_i(VALUE v, VALUE enum_obj, int argc, VALUE *argv)
169 {
170 return rb_yield_values2(argc, argv);
171 }
172
173 static VALUE
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
174 enumerator_init(VALUE enum_obj, VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
175 {
de68086 @Watson1978 Properly detect if the object is initialized and raise error when app…
Watson1978 authored
176 struct enumerator *ptr;
177
178 Data_Get_Struct(enum_obj, struct enumerator, ptr);
179
180 if (!ptr) {
181 rb_raise(rb_eArgError, "unallocated enumerator");
182 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
183
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
184 GC_WB(&ptr->obj, obj);
185 ptr->sel = sel;
186 if (argc > 0) {
187 GC_WB(&ptr->args, rb_ary_new4(argc, argv));
188 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
189 ptr->fib = 0;
190 ptr->dst = Qnil;
191 ptr->no_next = Qfalse;
192
193 return enum_obj;
194 }
195
196 /*
197 * call-seq:
76080c7 @Watson1978 update rdoc
Watson1978 authored
198 * Enumerator.new(obj, method = :each, *args)
199 * Enumerator.new { |y| ... }
200 *
201 * Creates a new Enumerator object, which is to be used as an
202 * Enumerable object iterating in a given way.
203 *
204 * In the first form, a generated Enumerator iterates over the given
205 * object using the given method with the given arguments passed.
206 * Use of this form is discouraged. Use Kernel#enum_for(), alias
207 * to_enum, instead.
208 *
209 * e = Enumerator.new(ObjectSpace, :each_object)
210 * #-> ObjectSpace.enum_for(:each_object)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
211 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
212 * e.select { |obj| obj.is_a?(Class) } #=> array of all classes
9c1d230 committing experimental branch content
Laurent Sansonetti authored
213 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
214 * In the second form, iteration is defined by the given block, in
215 * which a "yielder" object given as block parameter can be used to
216 * yield a value by calling the +yield+ method, alias +<<+.
217 *
218 * fib = Enumerator.new { |y|
219 * a = b = 1
220 * loop {
221 * y << a
222 * a, b = b, a + b
223 * }
224 * }
225 *
226 * p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
9c1d230 committing experimental branch content
Laurent Sansonetti authored
227 */
228 static VALUE
229 enumerator_initialize(VALUE obj, SEL sel, int argc, VALUE *argv)
230 {
231 VALUE recv, meth = sym_each;
232
dd801db @Watson1978 implement Enumerator::{Generator, Yielder}
Watson1978 authored
233 if (argc == 0) {
234 if (!rb_block_given_p())
235 rb_raise(rb_eArgError, "wrong number of argument (0 for 1+)");
236
237 recv = generator_init(generator_allocate(rb_cGenerator, 0), rb_block_proc());
9c1d230 committing experimental branch content
Laurent Sansonetti authored
238 }
dd801db @Watson1978 implement Enumerator::{Generator, Yielder}
Watson1978 authored
239 else {
240 recv = *argv++;
241 if (--argc) {
242 meth = *argv++;
243 --argc;
244 }
245 }
246
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
247 ID meth_id = rb_to_id(meth);
81232c2 refactor duplicated code
Laurent Sansonetti authored
248 SEL meth_sel = rb_vm_id_to_sel(meth_id, argc);
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
249 return enumerator_init(obj, recv, meth_sel, argc, argv);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
250 }
251
252 /* :nodoc: */
253 static VALUE
254 enumerator_init_copy(VALUE obj, SEL sel, VALUE orig)
255 {
256 struct enumerator *ptr0, *ptr1;
257
258 ptr0 = enumerator_ptr(orig);
259 if (ptr0->fib) {
260 /* Fibers cannot be copied */
261 rb_raise(rb_eTypeError, "can't copy execution context");
262 }
de68086 @Watson1978 Properly detect if the object is initialized and raise error when app…
Watson1978 authored
263
264 Data_Get_Struct(obj, struct enumerator, ptr1);
265
266 if (!ptr1) {
267 rb_raise(rb_eArgError, "unallocated enumerator");
268 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
269
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
270 GC_WB(&ptr1->obj, ptr0->obj);
271 ptr1->sel = ptr0->sel;
272 if (ptr0->args != 0) {
273 GC_WB(&ptr1->args, ptr0->args);
274 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
275 ptr1->fib = 0;
276
277 return obj;
278 }
279
280 VALUE
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
281 rb_enumeratorize(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
282 {
5b94e0e @Watson1978 enumerator_allocate() needs sel argument because "alloc" method is de…
Watson1978 authored
283 return enumerator_init(enumerator_allocate(rb_cEnumerator, 0), obj, sel,
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
284 argc, argv);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
285 }
286
287 static VALUE
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
288 enumerator_block_call(VALUE obj, VALUE (*func)(ANYARGS), VALUE arg)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
289 {
290 struct enumerator *e;
291 int argc = 0;
292 const VALUE *argv = 0;
293
294 e = enumerator_ptr(obj);
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
295 if (e->args != 0) {
9c1d230 committing experimental branch content
Laurent Sansonetti authored
296 argc = RARRAY_LEN(e->args);
297 argv = RARRAY_PTR(e->args);
298 }
70ea0b5 per-vm method cache + misc fixes/improvements
Laurent Sansonetti authored
299 return rb_objc_block_call(e->obj, e->sel, argc, (VALUE *)argv,
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
300 func, arg);
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 if (!rb_block_given_p()) {
315 return obj;
316 }
317 return enumerator_block_call(obj, enumerator_each_i, obj);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
318 }
319
320 static VALUE
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
321 enumerator_with_index_i(VALUE val, VALUE m, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
322 {
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
323 VALUE idx;
324 VALUE *memo = (VALUE *)m;
325
326 idx = INT2FIX(*memo);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
327 ++*memo;
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
328
329 if (argc <= 1)
330 return rb_yield_values(2, val, idx);
331
332 return rb_yield_values(2, rb_ary_new4(argc, argv), idx);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
333 }
334
335 /*
336 * call-seq:
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
337 * e.with_index(offset = 0) {|(*args), idx| ... }
76080c7 @Watson1978 update rdoc
Watson1978 authored
338 * e.with_index(offset = 0)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
339 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
340 * Iterates the given block for each element with an index, which
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
341 * starts from +offset+. If no block is given, returns an enumerator.
9c1d230 committing experimental branch content
Laurent Sansonetti authored
342 *
343 */
344 static VALUE
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
345 enumerator_with_index(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
346 {
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
347 VALUE memo;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
348
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
349 rb_scan_args(argc, argv, "01", &memo);
350 RETURN_ENUMERATOR(obj, argc, argv);
351 memo = NIL_P(memo) ? 0 : (VALUE)NUM2LONG(memo);
352 return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)&memo);
353 }
354
355 /*
356 * call-seq:
357 * e.each_with_index {|(*args), idx| ... }
358 * e.each_with_index
359 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
360 * Same as Enumerator#with_index, except each_with_index does not
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
361 * receive an offset argument.
362 *
363 */
364 static VALUE
365 enumerator_each_with_index(VALUE obj, SEL sel)
366 {
367 return enumerator_with_index(obj, sel, 0, NULL);
368 }
369
370 static VALUE
371 enumerator_with_object_i(VALUE val, VALUE memo, int argc, VALUE *argv)
372 {
373 if (argc <= 1) {
374 return rb_yield_values(2, val, memo);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
375 }
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
376
377 return rb_yield_values(2, rb_ary_new4(argc, argv), memo);
378 }
379
380 /*
381 * call-seq:
382 * e.with_object(obj) {|(*args), memo_obj| ... }
383 * e.with_object(obj)
384 *
385 * Iterates the given block for each element with an arbitrary
386 * object given, and returns the initially given object.
387 *
388 * If no block is given, returns an enumerator.
389 *
390 */
391 static VALUE
392 enumerator_with_object(VALUE obj, SEL sel, VALUE memo)
393 {
394 RETURN_ENUMERATOR(obj, 1, &memo);
395 enumerator_block_call(obj, enumerator_with_object_i, memo);
396 return memo;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
397 }
398
399 #if 0
400 static VALUE
401 next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
402 {
403 rb_fiber_yield(argc, argv);
404 return Qnil;
405 }
406
407 static VALUE
408 next_i(VALUE curr, VALUE obj)
409 {
410 struct enumerator *e = enumerator_ptr(obj);
411 VALUE rnil = Qnil;
412
413 rb_block_call(obj, rb_intern("each"), 0, 0, next_ii, obj);
414 e->no_next = Qtrue;
415 return rb_fiber_yield(1, &rnil);
416 }
417
418 static void
419 next_init(VALUE obj, struct enumerator *e)
420 {
421 VALUE curr = rb_fiber_current();
422 e->dst = curr;
423 e->fib = rb_fiber_new(next_i, obj);
424 }
425 #endif
426
427 /*
428 * call-seq:
76080c7 @Watson1978 update rdoc
Watson1978 authored
429 * e.next -> object
9c1d230 committing experimental branch content
Laurent Sansonetti authored
430 *
431 * Returns the next object in the enumerator, and move the internal
76080c7 @Watson1978 update rdoc
Watson1978 authored
432 * position forward. When the position reached at the end, StopIteration
433 * is raised.
434 *
435 * a = [1,2,3]
436 * e = a.to_enum
437 * p e.next #=> 1
438 * p e.next #=> 2
439 * p e.next #=> 3
440 * p e.next #raises StopIteration
9c1d230 committing experimental branch content
Laurent Sansonetti authored
441 *
442 * Note that enumeration sequence by next method does not affect other
443 * non-external enumeration methods, unless underlying iteration
444 * methods itself has side-effect, e.g. IO#each_line.
445 *
446 */
447
448 static VALUE
449 enumerator_next(VALUE obj, SEL sel)
450 {
451 // TODO
452 #if 0
453 struct enumerator *e = enumerator_ptr(obj);
454 VALUE curr, v;
455 curr = rb_fiber_current();
456
457 if (!e->fib || !rb_fiber_alive_p(e->fib)) {
458 next_init(obj, e);
459 }
460
461 v = rb_fiber_resume(e->fib, 1, &curr);
462 if (e->no_next) {
463 e->fib = 0;
464 e->dst = Qnil;
465 e->no_next = Qfalse;
466 rb_raise(rb_eStopIteration, "iteration reached at end");
467 }
468 return v;
469 #endif
470 return Qnil;
471 }
472
473 /*
474 * call-seq:
76080c7 @Watson1978 update rdoc
Watson1978 authored
475 * e.rewind -> e
9c1d230 committing experimental branch content
Laurent Sansonetti authored
476 *
477 * Rewinds the enumeration sequence by the next method.
76080c7 @Watson1978 update rdoc
Watson1978 authored
478 *
479 * If the enclosed object responds to a "rewind" method, it is called.
9c1d230 committing experimental branch content
Laurent Sansonetti authored
480 */
481
482 static VALUE
483 enumerator_rewind(VALUE obj, SEL sel)
484 {
485 struct enumerator *e = enumerator_ptr(obj);
486
487 e->fib = 0;
488 e->dst = Qnil;
489 e->no_next = Qfalse;
490 return obj;
491 }
492
1f07e4b @Watson1978 implement Enumerator#inspect
Watson1978 authored
493 static VALUE
494 inspect_enumerator(VALUE obj, VALUE dummy, int recur)
495 {
496 struct enumerator *e;
497 const char *cname;
498 VALUE eobj, str;
499 int tainted, untrusted;
500
501 Data_Get_Struct(obj, struct enumerator, e);
502
503 cname = rb_obj_classname(obj);
504
505 if (!e || e->obj == Qundef) {
506 return rb_sprintf("#<%s: uninitialized>", cname);
507 }
508
509 if (recur) {
510 str = rb_sprintf("#<%s: ...>", cname);
511 OBJ_TAINT(str);
512 return str;
513 }
514
515 eobj = e->obj;
516
517 tainted = OBJ_TAINTED(eobj);
518 untrusted = OBJ_UNTRUSTED(eobj);
519
520 /* (1..100).each_cons(2) => "#<Enumerator: 1..100:each_cons(2)>" */
521 str = rb_sprintf("#<%s: ", cname);
522 rb_str_concat(str, rb_inspect(eobj));
523 rb_str_buf_cat2(str, ":");
524 rb_str_buf_cat2(str, sel_getName(e->sel));
525
526 if (e->args) {
527 long argc = RARRAY_LEN(e->args);
528 VALUE *argv = (VALUE*)RARRAY_PTR(e->args);
529
530 rb_str_buf_cat2(str, "(");
531
532 while (argc--) {
533 VALUE arg = *argv++;
534
535 rb_str_concat(str, rb_inspect(arg));
536 rb_str_buf_cat2(str, argc > 0 ? ", " : ")");
537
538 if (OBJ_TAINTED(arg)) tainted = TRUE;
539 if (OBJ_UNTRUSTED(arg)) untrusted = TRUE;
540 }
541 }
542
543 rb_str_buf_cat2(str, ">");
544
545 if (tainted) OBJ_TAINT(str);
546 if (untrusted) OBJ_UNTRUST(str);
547 return str;
548 }
549
550 /*
551 * call-seq:
552 * e.inspect -> string
553 *
554 * Create a printable version of <i>e</i>.
555 */
556
557 static VALUE
558 enumerator_inspect(VALUE obj, SEL sel)
559 {
560 return rb_exec_recursive(inspect_enumerator, obj, 0);
561 }
562
dd801db @Watson1978 implement Enumerator::{Generator, Yielder}
Watson1978 authored
563 /*
564 * Yielder
565 */
566 #if !WITH_OBJC
567 static void
568 yielder_mark(void *p)
569 {
570 struct yielder *ptr = p;
571 rb_gc_mark(ptr->proc);
572 }
573 #endif
574
575 static struct yielder *
576 yielder_ptr(VALUE obj)
577 {
578 struct yielder *ptr;
579
580 Data_Get_Struct(obj, struct yielder, ptr);
581 #if !WITH_OBJC
582 if (RDATA(obj)->dmark != yielder_mark) {
583 rb_raise(rb_eTypeError,
584 "wrong argument type %s (expected %s)",
585 rb_obj_classname(obj), rb_class2name(rb_cYielder));
586 }
587 #endif
588 if (!ptr || ptr->proc == Qundef) {
589 rb_raise(rb_eArgError, "uninitialized yielder");
590 }
591 return ptr;
592 }
593
594 /* :nodoc: */
595 static VALUE
596 yielder_allocate(VALUE klass, SEL sel)
597 {
598 struct yielder *ptr;
599 VALUE obj;
600
601 obj = Data_Make_Struct(klass, struct yielder, NULL, NULL, ptr);
602 ptr->proc = Qundef;
603
604 return obj;
605 }
606
607 static VALUE
608 yielder_init(VALUE obj, VALUE proc)
609 {
610 struct yielder *ptr;
611
612 Data_Get_Struct(obj, struct yielder, ptr);
613
614 if (!ptr) {
615 rb_raise(rb_eArgError, "unallocated yielder");
616 }
617
618 GC_WB(&ptr->proc, proc);
619
620 return obj;
621 }
622
623 /* :nodoc: */
624 static VALUE
625 yielder_initialize(VALUE obj, SEL sel)
626 {
627 rb_need_block();
628
629 return yielder_init(obj, rb_block_proc());
630 }
631
632 /* :nodoc: */
633 static VALUE
634 yielder_yield(VALUE obj, SEL sel, VALUE args)
635 {
636 struct yielder *ptr = yielder_ptr(obj);
637
638 return rb_proc_call(ptr->proc, args);
639 }
640
641 /* :nodoc: */
642 static VALUE yielder_yield_push(VALUE obj, SEL sel, VALUE args)
643 {
644 yielder_yield(obj, 0, args);
645 return obj;
646 }
647
648 static VALUE
649 yielder_yield_i(VALUE obj, VALUE memo, int argc, VALUE *argv)
650 {
651 return rb_yield_values2(argc, argv);
652 }
653
654 static VALUE
655 yielder_new(void)
656 {
657 return yielder_init(yielder_allocate(rb_cYielder, 0), rb_proc_new(yielder_yield_i, 0));
658 }
659
660 /*
661 * Generator
662 */
663 #if !WITH_OBJC
664 static void
665 generator_mark(void *p)
666 {
667 struct generator *ptr = p;
668 rb_gc_mark(ptr->proc);
669 }
670 #endif
671
672 static struct generator *
673 generator_ptr(VALUE obj)
674 {
675 struct generator *ptr;
676
677 Data_Get_Struct(obj, struct generator, ptr);
678 #if !WITH_OBJC
679 if (RDATA(obj)->dmark != generator_mark) {
680 rb_raise(rb_eTypeError,
681 "wrong argument type %s (expected %s)",
682 rb_obj_classname(obj), rb_class2name(rb_cGenerator));
683 }
684 #endif
685 if (!ptr || ptr->proc == Qundef) {
686 rb_raise(rb_eArgError, "uninitialized generator");
687 }
688 return ptr;
689 }
690
691 /* :nodoc: */
692 static VALUE
693 generator_allocate(VALUE klass, SEL sel)
694 {
695 struct generator *ptr;
696 VALUE obj;
697
698 obj = Data_Make_Struct(klass, struct generator, NULL, NULL, ptr);
699 ptr->proc = Qundef;
700
701 return obj;
702 }
703
704 static VALUE
705 generator_init(VALUE obj, VALUE proc)
706 {
707 struct generator *ptr;
708
709 Data_Get_Struct(obj, struct generator, ptr);
710
711 if (!ptr) {
712 rb_raise(rb_eArgError, "unallocated generator");
713 }
714
715 GC_WB(&ptr->proc, proc);
716
717 return obj;
718 }
719
720 VALUE rb_obj_is_proc(VALUE proc);
721
722 /* :nodoc: */
723 static VALUE
724 generator_initialize(VALUE obj, SEL sel, int argc, VALUE *argv)
725 {
726 VALUE proc;
727
728 if (argc == 0) {
729 rb_need_block();
730
731 proc = rb_block_proc();
732 } else {
733 rb_scan_args(argc, argv, "1", &proc);
734
735 if (!rb_obj_is_proc(proc))
736 rb_raise(rb_eTypeError,
737 "wrong argument type %s (expected Proc)",
738 rb_obj_classname(proc));
739
740 if (rb_block_given_p()) {
741 rb_warn("given block not used");
742 }
743 }
744
745 return generator_init(obj, proc);
746 }
747
748 /* :nodoc: */
749 static VALUE
750 generator_init_copy(VALUE obj, SEL sel, VALUE orig)
751 {
752 struct generator *ptr0, *ptr1;
753
754 ptr0 = generator_ptr(orig);
755
756 Data_Get_Struct(obj, struct generator, ptr1);
757
758 if (!ptr1) {
759 rb_raise(rb_eArgError, "unallocated generator");
760 }
761
762 ptr1->proc = ptr0->proc;
763
764 return obj;
765 }
766
767 /* :nodoc: */
768 static VALUE
769 generator_each(VALUE obj, SEL sel)
770 {
771 struct generator *ptr = generator_ptr(obj);
772 VALUE yielder;
773
774 yielder = yielder_new();
775
776 return rb_proc_call(ptr->proc, rb_ary_new3(1, yielder));
777
778 return obj;
779 }
780
9c1d230 committing experimental branch content
Laurent Sansonetti authored
781 void
782 Init_Enumerator(void)
783 {
784 rb_objc_define_method(rb_mKernel, "to_enum", obj_to_enum, -1);
785 rb_objc_define_method(rb_mKernel, "enum_for", obj_to_enum, -1);
786
fabdd03 Enumerator should be a top-level class
Laurent Sansonetti authored
787 rb_cEnumerator = rb_define_class("Enumerator", rb_cObject);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
788 rb_include_module(rb_cEnumerator, rb_mEnumerable);
789
790 rb_objc_define_method(*(VALUE *)rb_cEnumerator, "alloc", enumerator_allocate, 0);
791 rb_objc_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
792 rb_objc_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
793 rb_objc_define_method(rb_cEnumerator, "each", enumerator_each, 0);
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
794 rb_objc_define_method(rb_cEnumerator, "each_with_index", enumerator_each_with_index, 0);
795 rb_objc_define_method(rb_cEnumerator, "each_with_object", enumerator_with_object, 1);
796 rb_objc_define_method(rb_cEnumerator, "with_index", enumerator_with_index, -1);
797 rb_objc_define_method(rb_cEnumerator, "with_object", enumerator_with_object, 1);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
798 rb_objc_define_method(rb_cEnumerator, "next", enumerator_next, 0);
799 rb_objc_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
1f07e4b @Watson1978 implement Enumerator#inspect
Watson1978 authored
800 rb_objc_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
801
802 rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
803
dd801db @Watson1978 implement Enumerator::{Generator, Yielder}
Watson1978 authored
804 /* Generator */
805 rb_cGenerator = rb_define_class_under(rb_cEnumerator, "Generator", rb_cObject);
806 rb_include_module(rb_cGenerator, rb_mEnumerable);
807 rb_objc_define_method(*(VALUE *)rb_cGenerator, "alloc", generator_allocate, 0);
808 rb_objc_define_method(rb_cGenerator, "initialize", generator_initialize, -1);
809 rb_objc_define_method(rb_cGenerator, "initialize_copy", generator_init_copy, 1);
810 rb_objc_define_method(rb_cGenerator, "each", generator_each, 0);
811
812 /* Yielder */
813 rb_cYielder = rb_define_class_under(rb_cEnumerator, "Yielder", rb_cObject);
814 rb_objc_define_method(*(VALUE *)rb_cYielder, "alloc", yielder_allocate, 0);
815 rb_objc_define_method(rb_cYielder, "initialize", yielder_initialize, 0);
816 rb_objc_define_method(rb_cYielder, "yield", yielder_yield, -2);
817 rb_objc_define_method(rb_cYielder, "<<", yielder_yield_push, -2);
818
9c1d230 committing experimental branch content
Laurent Sansonetti authored
819 sym_each = ID2SYM(rb_intern("each"));
820 }
Something went wrong with that request. Please try again.