Skip to content
Newer
Older
100644 481 lines (430 sloc) 11.4 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
88 static struct enumerator *
89 enumerator_ptr(VALUE obj)
90 {
91 struct enumerator *ptr;
92
93 Data_Get_Struct(obj, struct enumerator, ptr);
94 #if 0
95 if (RDATA(obj)->dmark != enumerator_mark) {
96 rb_raise(rb_eTypeError,
97 "wrong argument type %s (expected %s)",
98 rb_obj_classname(obj), rb_class2name(rb_cEnumerator));
99 }
100 #endif
101 if (!ptr) {
102 rb_raise(rb_eArgError, "uninitialized enumerator");
103 }
104 return ptr;
105 }
106
107 /*
108 * call-seq:
109 * obj.to_enum(method = :each, *args)
110 * obj.enum_for(method = :each, *args)
111 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
112 * Returns Enumerator.new(self, method, *args).
9c1d230 committing experimental branch content
Laurent Sansonetti authored
113 *
114 * e.g.:
115 *
116 * str = "xyz"
117 *
118 * enum = str.enum_for(:each_byte)
119 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
120 *
121 * # protects an array from being modified
122 * a = [1, 2, 3]
123 * some_method(a.to_enum)
124 *
125 */
126 static VALUE
127 obj_to_enum(VALUE obj, SEL sel, int argc, VALUE *argv)
128 {
129 VALUE meth = sym_each;
130
131 if (argc > 0) {
132 --argc;
133 meth = *argv++;
134 }
c6a0341 Kernel#to_enum: implemented
Laurent Sansonetti authored
135
e1960f6 Make sure #to_enum verify param is a symbol
Thibault Martin-Lagardette authored
136 ID meth_id = rb_to_id(meth);
81232c2 refactor duplicated code
Laurent Sansonetti authored
137 SEL enum_sel = rb_vm_id_to_sel(meth_id, argc);
c6a0341 Kernel#to_enum: implemented
Laurent Sansonetti authored
138 return rb_enumeratorize(obj, enum_sel, argc, argv);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
139 }
140
141 static VALUE
5b94e0e @Watson1978 enumerator_allocate() needs sel argument because "alloc" method is de…
Watson1978 authored
142 enumerator_allocate(VALUE klass, SEL sel)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
143 {
144 struct enumerator *ptr;
145 return Data_Make_Struct(klass, struct enumerator,
2ba4bd4 honor RData dfree() callback
Laurent Sansonetti authored
146 NULL, NULL, ptr);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
147 }
148
149 static VALUE
150 enumerator_each_i(VALUE v, VALUE enum_obj, int argc, VALUE *argv)
151 {
152 return rb_yield_values2(argc, argv);
153 }
154
155 static VALUE
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
156 enumerator_init(VALUE enum_obj, VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
157 {
158 struct enumerator *ptr = enumerator_ptr(enum_obj);
159
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
160 GC_WB(&ptr->obj, obj);
161 ptr->sel = sel;
162 if (argc > 0) {
163 GC_WB(&ptr->args, rb_ary_new4(argc, argv));
164 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
165 ptr->fib = 0;
166 ptr->dst = Qnil;
167 ptr->no_next = Qfalse;
168
169 return enum_obj;
170 }
171
172 /*
173 * call-seq:
76080c7 @Watson1978 update rdoc
Watson1978 authored
174 * Enumerator.new(obj, method = :each, *args)
175 * Enumerator.new { |y| ... }
176 *
177 * Creates a new Enumerator object, which is to be used as an
178 * Enumerable object iterating in a given way.
179 *
180 * In the first form, a generated Enumerator iterates over the given
181 * object using the given method with the given arguments passed.
182 * Use of this form is discouraged. Use Kernel#enum_for(), alias
183 * to_enum, instead.
184 *
185 * e = Enumerator.new(ObjectSpace, :each_object)
186 * #-> ObjectSpace.enum_for(:each_object)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
187 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
188 * e.select { |obj| obj.is_a?(Class) } #=> array of all classes
9c1d230 committing experimental branch content
Laurent Sansonetti authored
189 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
190 * In the second form, iteration is defined by the given block, in
191 * which a "yielder" object given as block parameter can be used to
192 * yield a value by calling the +yield+ method, alias +<<+.
193 *
194 * fib = Enumerator.new { |y|
195 * a = b = 1
196 * loop {
197 * y << a
198 * a, b = b, a + b
199 * }
200 * }
201 *
202 * p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
9c1d230 committing experimental branch content
Laurent Sansonetti authored
203 */
204 static VALUE
205 enumerator_initialize(VALUE obj, SEL sel, int argc, VALUE *argv)
206 {
207 VALUE recv, meth = sym_each;
208
209 if (argc == 0)
210 rb_raise(rb_eArgError, "wrong number of argument (0 for 1)");
211 recv = *argv++;
212 if (--argc) {
213 meth = *argv++;
214 --argc;
215 }
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
216 ID meth_id = rb_to_id(meth);
81232c2 refactor duplicated code
Laurent Sansonetti authored
217 SEL meth_sel = rb_vm_id_to_sel(meth_id, argc);
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
218 return enumerator_init(obj, recv, meth_sel, argc, argv);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
219 }
220
221 /* :nodoc: */
222 static VALUE
223 enumerator_init_copy(VALUE obj, SEL sel, VALUE orig)
224 {
225 struct enumerator *ptr0, *ptr1;
226
227 ptr0 = enumerator_ptr(orig);
228 if (ptr0->fib) {
229 /* Fibers cannot be copied */
230 rb_raise(rb_eTypeError, "can't copy execution context");
231 }
232 ptr1 = enumerator_ptr(obj);
233
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
234 GC_WB(&ptr1->obj, ptr0->obj);
235 ptr1->sel = ptr0->sel;
236 if (ptr0->args != 0) {
237 GC_WB(&ptr1->args, ptr0->args);
238 }
9c1d230 committing experimental branch content
Laurent Sansonetti authored
239 ptr1->fib = 0;
240
241 return obj;
242 }
243
244 VALUE
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
245 rb_enumeratorize(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
246 {
5b94e0e @Watson1978 enumerator_allocate() needs sel argument because "alloc" method is de…
Watson1978 authored
247 return enumerator_init(enumerator_allocate(rb_cEnumerator, 0), obj, sel,
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
248 argc, argv);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
249 }
250
251 static VALUE
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
252 enumerator_block_call(VALUE obj, VALUE (*func)(ANYARGS), VALUE arg)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
253 {
254 struct enumerator *e;
255 int argc = 0;
256 const VALUE *argv = 0;
257
258 e = enumerator_ptr(obj);
a6a914f implemented implicit enumeratorization
Laurent Sansonetti authored
259 if (e->args != 0) {
9c1d230 committing experimental branch content
Laurent Sansonetti authored
260 argc = RARRAY_LEN(e->args);
261 argv = RARRAY_PTR(e->args);
262 }
70ea0b5 per-vm method cache + misc fixes/improvements
Laurent Sansonetti authored
263 return rb_objc_block_call(e->obj, e->sel, argc, (VALUE *)argv,
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
264 func, arg);
265 }
266
267 /*
268 * call-seq:
269 * enum.each {...}
270 *
271 * Iterates the given block using the object and the method specified
272 * in the first place. If no block is given, returns self.
273 *
274 */
275 static VALUE
276 enumerator_each(VALUE obj, SEL sel)
277 {
278 if (!rb_block_given_p()) {
279 return obj;
280 }
281 return enumerator_block_call(obj, enumerator_each_i, obj);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
282 }
283
284 static VALUE
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
285 enumerator_with_index_i(VALUE val, VALUE m, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
286 {
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
287 VALUE idx;
288 VALUE *memo = (VALUE *)m;
289
290 idx = INT2FIX(*memo);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
291 ++*memo;
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
292
293 if (argc <= 1)
294 return rb_yield_values(2, val, idx);
295
296 return rb_yield_values(2, rb_ary_new4(argc, argv), idx);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
297 }
298
299 /*
300 * call-seq:
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
301 * e.with_index(offset = 0) {|(*args), idx| ... }
76080c7 @Watson1978 update rdoc
Watson1978 authored
302 * e.with_index(offset = 0)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
303 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
304 * Iterates the given block for each element with an index, which
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
305 * starts from +offset+. If no block is given, returns an enumerator.
9c1d230 committing experimental branch content
Laurent Sansonetti authored
306 *
307 */
308 static VALUE
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
309 enumerator_with_index(VALUE obj, SEL sel, int argc, VALUE *argv)
9c1d230 committing experimental branch content
Laurent Sansonetti authored
310 {
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
311 VALUE memo;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
312
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
313 rb_scan_args(argc, argv, "01", &memo);
314 RETURN_ENUMERATOR(obj, argc, argv);
315 memo = NIL_P(memo) ? 0 : (VALUE)NUM2LONG(memo);
316 return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)&memo);
317 }
318
319 /*
320 * call-seq:
321 * e.each_with_index {|(*args), idx| ... }
322 * e.each_with_index
323 *
76080c7 @Watson1978 update rdoc
Watson1978 authored
324 * Same as Enumerator#with_index, except each_with_index does not
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
325 * receive an offset argument.
326 *
327 */
328 static VALUE
329 enumerator_each_with_index(VALUE obj, SEL sel)
330 {
331 return enumerator_with_index(obj, sel, 0, NULL);
332 }
333
334 static VALUE
335 enumerator_with_object_i(VALUE val, VALUE memo, int argc, VALUE *argv)
336 {
337 if (argc <= 1) {
338 return rb_yield_values(2, val, memo);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
339 }
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
340
341 return rb_yield_values(2, rb_ary_new4(argc, argv), memo);
342 }
343
344 /*
345 * call-seq:
346 * e.with_object(obj) {|(*args), memo_obj| ... }
347 * e.with_object(obj)
348 *
349 * Iterates the given block for each element with an arbitrary
350 * object given, and returns the initially given object.
351 *
352 * If no block is given, returns an enumerator.
353 *
354 */
355 static VALUE
356 enumerator_with_object(VALUE obj, SEL sel, VALUE memo)
357 {
358 RETURN_ENUMERATOR(obj, 1, &memo);
359 enumerator_block_call(obj, enumerator_with_object_i, memo);
360 return memo;
9c1d230 committing experimental branch content
Laurent Sansonetti authored
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:
76080c7 @Watson1978 update rdoc
Watson1978 authored
393 * e.next -> object
9c1d230 committing experimental branch content
Laurent Sansonetti authored
394 *
395 * Returns the next object in the enumerator, and move the internal
76080c7 @Watson1978 update rdoc
Watson1978 authored
396 * position forward. When the position reached at the end, StopIteration
397 * is raised.
398 *
399 * a = [1,2,3]
400 * e = a.to_enum
401 * p e.next #=> 1
402 * p e.next #=> 2
403 * p e.next #=> 3
404 * p e.next #raises StopIteration
9c1d230 committing experimental branch content
Laurent Sansonetti authored
405 *
406 * Note that enumeration sequence by next method does not affect other
407 * non-external enumeration methods, unless underlying iteration
408 * methods itself has side-effect, e.g. IO#each_line.
409 *
410 */
411
412 static VALUE
413 enumerator_next(VALUE obj, SEL sel)
414 {
415 // TODO
416 #if 0
417 struct enumerator *e = enumerator_ptr(obj);
418 VALUE curr, v;
419 curr = rb_fiber_current();
420
421 if (!e->fib || !rb_fiber_alive_p(e->fib)) {
422 next_init(obj, e);
423 }
424
425 v = rb_fiber_resume(e->fib, 1, &curr);
426 if (e->no_next) {
427 e->fib = 0;
428 e->dst = Qnil;
429 e->no_next = Qfalse;
430 rb_raise(rb_eStopIteration, "iteration reached at end");
431 }
432 return v;
433 #endif
434 return Qnil;
435 }
436
437 /*
438 * call-seq:
76080c7 @Watson1978 update rdoc
Watson1978 authored
439 * e.rewind -> e
9c1d230 committing experimental branch content
Laurent Sansonetti authored
440 *
441 * Rewinds the enumeration sequence by the next method.
76080c7 @Watson1978 update rdoc
Watson1978 authored
442 *
443 * If the enclosed object responds to a "rewind" method, it is called.
9c1d230 committing experimental branch content
Laurent Sansonetti authored
444 */
445
446 static VALUE
447 enumerator_rewind(VALUE obj, SEL sel)
448 {
449 struct enumerator *e = enumerator_ptr(obj);
450
451 e->fib = 0;
452 e->dst = Qnil;
453 e->no_next = Qfalse;
454 return obj;
455 }
456
457 void
458 Init_Enumerator(void)
459 {
460 rb_objc_define_method(rb_mKernel, "to_enum", obj_to_enum, -1);
461 rb_objc_define_method(rb_mKernel, "enum_for", obj_to_enum, -1);
462
fabdd03 Enumerator should be a top-level class
Laurent Sansonetti authored
463 rb_cEnumerator = rb_define_class("Enumerator", rb_cObject);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
464 rb_include_module(rb_cEnumerator, rb_mEnumerable);
465
466 rb_objc_define_method(*(VALUE *)rb_cEnumerator, "alloc", enumerator_allocate, 0);
467 rb_objc_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
468 rb_objc_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
469 rb_objc_define_method(rb_cEnumerator, "each", enumerator_each, 0);
6f5ed12 Improve core/enumerator pass rate
Thibault Martin-Lagardette authored
470 rb_objc_define_method(rb_cEnumerator, "each_with_index", enumerator_each_with_index, 0);
471 rb_objc_define_method(rb_cEnumerator, "each_with_object", enumerator_with_object, 1);
472 rb_objc_define_method(rb_cEnumerator, "with_index", enumerator_with_index, -1);
473 rb_objc_define_method(rb_cEnumerator, "with_object", enumerator_with_object, 1);
9c1d230 committing experimental branch content
Laurent Sansonetti authored
474 rb_objc_define_method(rb_cEnumerator, "next", enumerator_next, 0);
475 rb_objc_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
476
477 rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
478
479 sym_each = ID2SYM(rb_intern("each"));
480 }
Something went wrong with that request. Please try again.