summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorksss <[email protected]>2014-03-13 21:51:12 +0900
committerksss <[email protected]>2014-03-13 23:47:12 +0900
commit463c5f83c3e5f379d4dd59deb17179915fbaf93b (patch)
tree3685cb8f01670060796a525910bdc969b8c30dd1
parent91f2d47d341b96c7cf1803c332f1e276aee6a8e2 (diff)
downloadmruby-463c5f83c3e5f379d4dd59deb17179915fbaf93b.tar.gz
mruby-463c5f83c3e5f379d4dd59deb17179915fbaf93b.zip
add mruby-enumerator
-rw-r--r--mrbgems/default.gembox3
-rw-r--r--mrbgems/mruby-enumerator/mrbgem.rake4
-rw-r--r--mrbgems/mruby-enumerator/mrblib/core_mod.rb98
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb621
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb398
5 files changed, 1124 insertions, 0 deletions
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 33ee99be0..2f436e5b6 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -50,6 +50,9 @@ MRuby::GemBox.new do |conf|
# Use Fiber class
conf.gem :core => "mruby-fiber"
+ # Use Enumerator class (require mruby-fiber)
+ conf.gem :core => "mruby-enumerator"
+
# Use extended toplevel object (main) methods
conf.gem :core => "mruby-toplevel-ext"
diff --git a/mrbgems/mruby-enumerator/mrbgem.rake b/mrbgems/mruby-enumerator/mrbgem.rake
new file mode 100644
index 000000000..c2e6a4a39
--- /dev/null
+++ b/mrbgems/mruby-enumerator/mrbgem.rake
@@ -0,0 +1,4 @@
+MRuby::Gem::Specification.new('mruby-enumerator') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+end
diff --git a/mrbgems/mruby-enumerator/mrblib/core_mod.rb b/mrbgems/mruby-enumerator/mrblib/core_mod.rb
new file mode 100644
index 000000000..968cc6ac4
--- /dev/null
+++ b/mrbgems/mruby-enumerator/mrblib/core_mod.rb
@@ -0,0 +1,98 @@
+##
+# modifying existing methods
+##
+
+# See /mrblib/kernel.rb
+module Kernel
+ def loop
+ return to_enum :loop unless block_given?
+
+ while(true)
+ yield
+ end
+ rescue => StopIteration
+ nil
+ end
+end
+
+# See /mrblib/numeric.rb
+module Integral
+ def times &block
+ return to_enum :times unless block_given?
+
+ i = 0
+ while i < self
+ block.call i
+ i += 1
+ end
+ self
+ end
+end
+
+# See /mrblib/enum.rb
+module Enumerable
+ def collect(&block)
+ return to_enum :collect unless block_given?
+
+ ary = []
+ self.each{|val|
+ ary.push(block.call(val))
+ }
+ ary
+ end
+ alias map collect
+end
+
+# See /mrblib/array.rb
+class Array
+ def each(&block)
+ return to_enum :each unless block_given?
+
+ idx, length = -1, self.length-1
+ while idx < length and length <= self.length and length = self.length-1
+ elm = self[idx += 1]
+ unless elm
+ if elm == nil and length >= self.length
+ break
+ end
+ end
+ block.call(elm)
+ end
+ self
+ end
+end
+
+# See /mrblib/hash.rb
+class Hash
+ def each(&block)
+ return to_enum :each unless block_given?
+
+ self.keys.each { |k| block.call [k, self[k]] }
+ self
+ end
+end
+
+# See /mrblib/range.rb
+class Range
+ def each &block
+ return to_enum :each unless block_given?
+
+ val = self.first
+ unless val.respond_to? :succ
+ raise TypeError, "can't iterate"
+ end
+
+ last = self.last
+ return self if (val <=> last) > 0
+
+ while((val <=> last) < 0)
+ block.call(val)
+ val = val.succ
+ end
+
+ if not exclude_end? and (val <=> last) == 0
+ block.call(val)
+ end
+ self
+ end
+end
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
new file mode 100644
index 000000000..4f44a2d21
--- /dev/null
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -0,0 +1,621 @@
+##
+# enumerator.rb Enumerator class
+# See Copyright Notice in mruby.h
+
+##
+# A class which allows both internal and external iteration.
+#
+# An Enumerator can be created by the following methods.
+# - Kernel#to_enum
+# - Kernel#enum_for
+# - Enumerator.new
+#
+# Most methods have two forms: a block form where the contents
+# are evaluated for each item in the enumeration, and a non-block form
+# which returns a new Enumerator wrapping the iteration.
+#
+# enumerator = %w(one two three).each
+# puts enumerator.class # => Enumerator
+#
+# enumerator.each_with_object("foo") do |item, obj|
+# puts "#{obj}: #{item}"
+# end
+#
+# # foo: one
+# # foo: two
+# # foo: three
+#
+# enum_with_obj = enumerator.each_with_object("foo")
+# puts enum_with_obj.class # => Enumerator
+#
+# enum_with_obj.each do |item, obj|
+# puts "#{obj}: #{item}"
+# end
+#
+# # foo: one
+# # foo: two
+# # foo: three
+#
+# This allows you to chain Enumerators together. For example, you
+# can map a list's elements to strings containing the index
+# and the element as a string via:
+#
+# puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
+# # => ["0:foo", "1:bar", "2:baz"]
+#
+# An Enumerator can also be used as an external iterator.
+# For example, Enumerator#next returns the next value of the iterator
+# or raises StopIteration if the Enumerator is at the end.
+#
+# e = [1,2,3].each # returns an enumerator object.
+# puts e.next # => 1
+# puts e.next # => 2
+# puts e.next # => 3
+# puts e.next # raises StopIteration
+#
+# You can use this to implement an internal iterator as follows:
+#
+# def ext_each(e)
+# while true
+# begin
+# vs = e.next_values
+# rescue StopIteration
+# return $!.result
+# end
+# y = yield(*vs)
+# e.feed y
+# end
+# end
+#
+# o = Object.new
+#
+# def o.each
+# puts yield
+# puts yield(1)
+# puts yield(1, 2)
+# 3
+# end
+#
+# # use o.each as an internal iterator directly.
+# puts o.each {|*x| puts x; [:b, *x] }
+# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
+#
+# # convert o.each to an external iterator for
+# # implementing an internal iterator.
+# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
+# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
+
+class Enumerator
+ include Enumerable
+
+ ##
+ # call-seq:
+ # Enumerator.new(size = nil) { |yielder| ... }
+ # Enumerator.new(obj, method = :each, *args)
+ #
+ # Creates a new Enumerator object, which can be used as an
+ # Enumerable.
+ #
+ # In the first form, iteration is defined by the given block, in
+ # which a "yielder" object, given as block parameter, can be used to
+ # yield a value by calling the +yield+ method (aliased as +<<+):
+ #
+ # fib = Enumerator.new do |y|
+ # a = b = 1
+ # loop do
+ # y << a
+ # a, b = b, a + b
+ # end
+ # end
+ #
+ # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+ #
+ def initialize obj=nil, meth=:each, *args, &block
+ if block_given?
+ obj = Generator.new &block
+ else
+ raise ArgumentError unless obj
+ end
+
+ @obj = obj
+ @meth = meth
+ @args = args.dup
+ @fib = nil
+ @dst = nil
+ @lookahead = nil
+ @feedvalue = nil
+ @stop_exc = false
+ end
+ attr_accessor :obj, :meth, :args, :fib
+ private :obj, :meth, :args, :fib
+
+ def initialize_copy obj
+ raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
+ raise TypeError, "can't copy execution context" if obj.fib
+ @obj = obj.obj
+ @meth = obj.meth
+ @args = obj.args
+ @fib = nil
+ @lookahead = nil
+ @feedvalue = nil
+ self
+ end
+
+ ##
+ # call-seq:
+ # e.with_index(offset = 0) {|(*args), idx| ... }
+ # e.with_index(offset = 0)
+ #
+ # Iterates the given block for each element with an index, which
+ # starts from +offset+. If no block is given, returns a new Enumerator
+ # that includes the index, starting from +offset+
+ #
+ # +offset+:: the starting index to use
+ #
+ def with_index offset=0
+ return to_enum :with_index, offset unless block_given?
+ raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int)
+
+ n = offset.to_int
+ each do |i|
+ yield [i,n]
+ n += 1
+ end
+ end
+
+ ##
+ # call-seq:
+ # e.each_with_index {|(*args), idx| ... }
+ # e.each_with_index
+ #
+ # Same as Enumerator#with_index(0), i.e. there is no starting offset.
+ #
+ # If no block is given, a new Enumerator is returned that includes the index.
+ #
+ def each_with_index
+ with_index
+ end
+
+ ##
+ # call-seq:
+ # e.each_with_object(obj) {|(*args), obj| ... }
+ # e.each_with_object(obj)
+ # e.with_object(obj) {|(*args), obj| ... }
+ # e.with_object(obj)
+ #
+ # Iterates the given block for each element with an arbitrary object, +obj+,
+ # and returns +obj+
+ #
+ # If no block is given, returns a new Enumerator.
+ #
+ # === Example
+ #
+ # to_three = Enumerator.new do |y|
+ # 3.times do |x|
+ # y << x
+ # end
+ # end
+ #
+ # to_three_with_string = to_three.with_object("foo")
+ # to_three_with_string.each do |x,string|
+ # puts "#{string}: #{x}"
+ # end
+ #
+ # # => foo:0
+ # # => foo:1
+ # # => foo:2
+ #
+ def with_object object
+ return to_enum :with_object, offset unless block_given?
+
+ each do |i|
+ yield [i,object]
+ end
+ object
+ end
+
+ def inspect
+ return "#<#{self.class}: uninitialized>" unless @obj
+ "#<#{self.class}: #{@obj}:#{@meth}>"
+ end
+
+ ##
+ # call-seq:
+ # enum.each { |elm| block } -> obj
+ # enum.each -> enum
+ # enum.each(*appending_args) { |elm| block } -> obj
+ # enum.each(*appending_args) -> an_enumerator
+ #
+ # Iterates over the block according to how this Enumerator was constructed.
+ # If no block and no arguments are given, returns self.
+ #
+ # === Examples
+ #
+ # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"]
+ # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"]
+ # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
+ #
+ # obj = Object.new
+ #
+ # def obj.each_arg(a, b=:b, *rest)
+ # yield a
+ # yield b
+ # yield rest
+ # :method_returned
+ # end
+ #
+ # enum = obj.to_enum :each_arg, :a, :x
+ #
+ # enum.each.to_a #=> [:a, :x, []]
+ # enum.each.equal?(enum) #=> true
+ # enum.each { |elm| elm } #=> :method_returned
+ #
+ # enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]]
+ # enum.each(:y, :z).equal?(enum) #=> false
+ # enum.each(:y, :z) { |elm| elm } #=> :method_returned
+ #
+ def each *argv, &block
+ if 0 < argv.length
+ obj = self.dup
+ args = obj.args
+ if !args.empty?
+ args = args.dup
+ args.concat argv
+ else
+ args = argv.dup
+ end
+ @args = args
+ end
+ return self unless block_given?
+ @obj.__send__ @meth, *@args, &block
+ end
+
+ ##
+ # call-seq:
+ # e.next -> object
+ #
+ # Returns the next object in the enumerator, and move the internal position
+ # forward. When the position reached at the end, StopIteration is raised.
+ #
+ # === Example
+ #
+ # a = [1,2,3]
+ # e = a.to_enum
+ # p e.next #=> 1
+ # p e.next #=> 2
+ # p e.next #=> 3
+ # p e.next #raises StopIteration
+ #
+ # Note that enumeration sequence by +next+ does not affect other non-external
+ # enumeration methods, unless the underlying iteration methods itself has
+ # side-effect
+ #
+ def next
+ ary2sv next_values, false
+ end
+
+ ##
+ # call-seq:
+ # e.next_values -> array
+ #
+ # Returns the next object as an array in the enumerator, and move the
+ # internal position forward. When the position reached at the end,
+ # StopIteration is raised.
+ #
+ # This method can be used to distinguish <code>yield</code> and <code>yield
+ # nil</code>.
+ #
+ # === Example
+ #
+ # o = Object.new
+ # def o.each
+ # yield
+ # yield 1
+ # yield 1, 2
+ # yield nil
+ # yield [1, 2]
+ # end
+ # e = o.to_enum
+ # p e.next_values
+ # p e.next_values
+ # p e.next_values
+ # p e.next_values
+ # p e.next_values
+ # e = o.to_enum
+ # p e.next
+ # p e.next
+ # p e.next
+ # p e.next
+ # p e.next
+ #
+ # ## yield args next_values next
+ # # yield [] nil
+ # # yield 1 [1] 1
+ # # yield 1, 2 [1, 2] [1, 2]
+ # # yield nil [nil] nil
+ # # yield [1, 2] [[1, 2]] [1, 2]
+ #
+ # Note that +next_values+ does not affect other non-external enumeration
+ # methods unless underlying iteration method itself has side-effect
+ #
+ def next_values
+ if @lookahead
+ vs = @lookahead
+ @lookahead = nil
+ return vs
+ end
+ raise @stop_exc if @stop_exc
+
+ raise NotImplementedError, "Enumerator require Fiber" unless Object.const_defined?(:Fiber)
+
+ curr = Fiber.current
+
+ if !@fib || [email protected]?
+ @dst = curr
+ @fib = Fiber.new do
+ result = each do |*args|
+ feedvalue = nil
+ Fiber.yield args
+ if @feedvalue
+ feedvalue = @feedvalue
+ @feedvalue = nil
+ end
+ feedvalue
+ end
+ @stop_exc = StopIteration.new "iteration reached an end"
+ @stop_exc.result = result
+ Fiber.yield nil
+ end
+ @lookahead = nil
+ end
+
+ vs = @fib.resume curr
+ if @stop_exc
+ @fib = nil
+ @dst = nil
+ @lookahead = nil
+ @feedvalue = nil
+ raise @stop_exc
+ end
+ vs
+ end
+
+ ##
+ # call-seq:
+ # e.peek -> object
+ #
+ # Returns the next object in the enumerator, but doesn't move the internal
+ # position forward. If the position is already at the end, StopIteration
+ # is raised.
+ #
+ # === Example
+ #
+ # a = [1,2,3]
+ # e = a.to_enum
+ # p e.next #=> 1
+ # p e.peek #=> 2
+ # p e.peek #=> 2
+ # p e.peek #=> 2
+ # p e.next #=> 2
+ # p e.next #=> 3
+ # p e.next #raises StopIteration
+ #
+ def peek
+ ary2sv peek_values, true
+ end
+
+ ##
+ # call-seq:
+ # e.peek_values -> array
+ #
+ # Returns the next object as an array, similar to Enumerator#next_values, but
+ # doesn't move the internal position forward. If the position is already at
+ # the end, StopIteration is raised.
+ #
+ # === Example
+ #
+ # o = Object.new
+ # def o.each
+ # yield
+ # yield 1
+ # yield 1, 2
+ # end
+ # e = o.to_enum
+ # p e.peek_values #=> []
+ # e.next
+ # p e.peek_values #=> [1]
+ # p e.peek_values #=> [1]
+ # e.next
+ # p e.peek_values #=> [1, 2]
+ # e.next
+ # p e.peek_values # raises StopIteration
+ #
+ def peek_values
+ if @lookahead.nil?
+ @lookahead = next_values
+ end
+ @lookahead.dup
+ end
+
+ ##
+ # call-seq:
+ # e.rewind -> e
+ #
+ # Rewinds the enumeration sequence to the beginning.
+ #
+ # If the enclosed object responds to a "rewind" method, it is called.
+ #
+ def rewind
+ @obj.rewind if @obj.respond_to? :rewind
+ @fib = nil
+ @dst = nil
+ @lookahead = nil
+ @feedvalue = nil
+ @stop_exc = false
+ self
+ end
+
+ ##
+ # call-seq:
+ # e.feed obj -> nil
+ #
+ # Sets the value to be returned by the next yield inside +e+.
+ #
+ # If the value is not set, the yield returns nil.
+ #
+ # This value is cleared after being yielded.
+ #
+ # # Array#map passes the array's elements to "yield" and collects the
+ # # results of "yield" as an array.
+ # # Following example shows that "next" returns the passed elements and
+ # # values passed to "feed" are collected as an array which can be
+ # # obtained by StopIteration#result.
+ # e = [1,2,3].map
+ # p e.next #=> 1
+ # e.feed "a"
+ # p e.next #=> 2
+ # e.feed "b"
+ # p e.next #=> 3
+ # e.feed "c"
+ # begin
+ # e.next
+ # rescue StopIteration
+ # p $!.result #=> ["a", "b", "c"]
+ # end
+ #
+ # o = Object.new
+ # def o.each
+ # x = yield # (2) blocks
+ # p x # (5) => "foo"
+ # x = yield # (6) blocks
+ # p x # (8) => nil
+ # x = yield # (9) blocks
+ # p x # not reached w/o another e.next
+ # end
+ #
+ # e = o.to_enum
+ # e.next # (1)
+ # e.feed "foo" # (3)
+ # e.next # (4)
+ # e.next # (7)
+ # # (10)
+ #
+ def feed value
+ raise TypeError, "feed value already set" if @feedvalue
+ @feedvalue = value
+ nil
+ end
+
+ # just for internal
+ def ary2sv args, dup
+ return args unless args.kind_of? Array
+
+ case args.length
+ when 0
+ nil
+ when 1
+ args[0]
+ else
+ return args.dup if dup
+ args
+ end
+ end
+ private :ary2sv
+
+ # just for internal
+ class Generator
+ def initialize &block
+ raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc
+
+ @proc = block
+ end
+
+ def each *args, &block
+ args.unshift Yielder.new(&block)
+ @proc.call *args
+ end
+ end
+
+ # just for internal
+ class Yielder
+ def initialize &block
+ raise LocalJumpError, "no block given" unless block_given?
+
+ @proc = block
+ end
+
+ def yield *args
+ @proc.call *args
+ end
+
+ def << *args
+ self.yield *args
+ self
+ end
+ end
+end
+
+class StopIteration < IndexError
+ attr_accessor :result
+end
+
+module Kernel
+ ##
+ # call-seq:
+ # obj.to_enum(method = :each, *args) -> enum
+ # obj.enum_for(method = :each, *args) -> enum
+ # obj.to_enum(method = :each, *args) {|*args| block} -> enum
+ # obj.enum_for(method = :each, *args){|*args| block} -> enum
+ #
+ # Creates a new Enumerator which will enumerate by calling +method+ on
+ # +obj+, passing +args+ if any.
+ #
+ # If a block is given, it will be used to calculate the size of
+ # the enumerator without the need to iterate it (see Enumerator#size).
+ #
+ # === Examples
+ #
+ # str = "xyz"
+ #
+ # enum = str.enum_for(:each_byte)
+ # enum.each { |b| puts b }
+ # # => 120
+ # # => 121
+ # # => 122
+ #
+ # # protect an array from being modified by some_method
+ # a = [1, 2, 3]
+ # some_method(a.to_enum)
+ #
+ # It is typical to call to_enum when defining methods for
+ # a generic Enumerable, in case no block is passed.
+ #
+ # Here is such an example, with parameter passing and a sizing block:
+ #
+ # module Enumerable
+ # # a generic method to repeat the values of any enumerable
+ # def repeat(n)
+ # raise ArgumentError, "#{n} is negative!" if n < 0
+ # unless block_given?
+ # return to_enum(__method__, n) do # __method__ is :repeat here
+ # sz = size # Call size and multiply by n...
+ # sz * n if sz # but return nil if size itself is nil
+ # end
+ # end
+ # each do |*val|
+ # n.times { yield *val }
+ # end
+ # end
+ # end
+ #
+ # %i[hello world].repeat(2) { |w| puts w }
+ # # => Prints 'hello', 'hello', 'world', 'world'
+ # enum = (1..14).repeat(3)
+ # # => returns an Enumerator when called without a block
+ # enum.first(4) # => [1, 1, 1, 2]
+ #
+ def to_enum meth=:each, *args
+ Enumerator.new self, meth, *args
+ end
+ alias :enum_for :to_enum
+end
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
new file mode 100644
index 000000000..c790c1367
--- /dev/null
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -0,0 +1,398 @@
+@obj = Object.new
+class << @obj
+ include Enumerable
+ def foo *a
+ a.each { |x| yield x }
+ end
+end
+
+assert 'Enumerator' do
+ assert_equal Class, Enumerator.class
+end
+
+assert 'Enumerator' do
+ assert_equal Object, Enumerator.superclass
+end
+
+assert 'Enumerator.new' do
+ assert_equal [0,1,2], 3.times.map{|i| i}.sort
+ assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort
+ assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort
+ assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a
+ assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a
+ assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3)
+ assert_raise(ArgumentError) { Enumerator.new }
+ enum = @obj.to_enum
+ assert_raise(NoMethodError) { enum.each {} }
+
+ # examples
+ fib = Enumerator.new do |y|
+ a = b = 1
+ loop do
+ y << a
+ a, b = b, a + b
+ end
+ end
+ assert_equal fib.take(10), [1,1,2,3,5,8,13,21,34,55]
+end
+
+assert 'Enumerator#initialize_copy' do
+ assert_equal [1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).dup.to_a
+ e = @obj.to_enum :foo, 1, 2, 3
+ assert_nothing_raised { assert_equal(1, e.next) }
+ assert_raise(TypeError) { e.dup }
+
+ e = Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.dup
+ assert_nothing_raised { assert_equal(1, e.next) }
+ assert_raise(TypeError) { e.dup }
+end
+
+assert 'Enumerator#with_index' do
+ assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a)
+ assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
+end
+
+assert 'Enumerator#with_index nonnum offset' do
+ s = Object.new
+ def s.to_int; 1 end
+ assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
+end
+
+assert 'Enumerator#with_index string offset' do
+ assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
+end
+
+assert 'Enumerator#with_object' do
+ obj = [0, 1]
+ ret = (1..10).each.with_object(obj) {|i, memo|
+ memo[0] += i
+ memo[1] *= i
+ }
+ assert_true(obj.equal?(ret))
+ assert_equal([55, 3628800], ret)
+end
+
+assert 'Enumerator#inspect' do
+ e = (0..10).each
+ assert_equal("#<Enumerator: 0..10:each>", e.inspect)
+end
+
+assert 'Enumerator#each' do
+ o = Object.new
+ def o.each(ary)
+ ary << 1
+ yield
+ end
+ ary = []
+ e = o.to_enum.each(ary)
+ e.next
+ assert_equal([1], ary)
+end
+
+assert 'Enumerator#next' do
+ e = 3.times
+ 3.times { |i|
+ assert_equal i, e.next
+ }
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'Enumerator#next_values' do
+ o = Object.new
+ def o.each
+ yield
+ yield 1
+ yield 1, 2
+ end
+ e = o.to_enum
+ assert_equal nil, e.next
+ assert_equal 1, e.next
+ assert_equal [1,2], e.next
+ e = o.to_enum
+ assert_equal [], e.next_values
+ assert_equal [1], e.next_values
+ assert_equal [1,2], e.next_values
+end
+
+assert 'Enumerator#peek' do
+ a = [1]
+ e = a.each
+ assert_equal 1, e.peek
+ assert_equal 1, e.peek
+ assert_equal 1, e.next
+ assert_raise(StopIteration) { e.peek }
+ assert_raise(StopIteration) { e.peek }
+end
+
+assert 'Enumerator#peek modify' do
+ o = Object.new
+ def o.each
+ yield 1,2
+ end
+ e = o.to_enum
+ a = e.peek
+ a << 3
+ assert_equal([1,2], e.peek)
+end
+
+assert 'Enumerator#peek_values' do
+ o = Object.new
+ def o.each
+ yield
+ yield 1
+ yield 1, 2
+ end
+ e = o.to_enum
+ assert_equal nil, e.peek
+ assert_equal nil, e.next
+ assert_equal 1, e.peek
+ assert_equal 1, e.next
+ assert_equal [1,2], e.peek
+ assert_equal [1,2], e.next
+ e = o.to_enum
+ assert_equal [], e.peek_values
+ assert_equal [], e.next_values
+ assert_equal [1], e.peek_values
+ assert_equal [1], e.next_values
+ assert_equal [1,2], e.peek_values
+ assert_equal [1,2], e.next_values
+ e = o.to_enum
+ assert_equal [], e.peek_values
+ assert_equal nil, e.next
+ assert_equal [1], e.peek_values
+ assert_equal 1, e.next
+ assert_equal [1,2], e.peek_values
+ assert_equal [1,2], e.next
+ e = o.to_enum
+ assert_equal nil, e.peek
+ assert_equal [], e.next_values
+ assert_equal 1, e.peek
+ assert_equal [1], e.next_values
+ assert_equal [1,2], e.peek
+ assert_equal [1,2], e.next_values
+end
+
+assert 'Enumerator#peek_values modify' do
+ o = Object.new
+ def o.each
+ yield 1,2
+ end
+ e = o.to_enum
+ a = e.peek_values
+ a << 3
+ assert_equal [1,2], e.peek
+end
+
+assert 'Enumerator#feed' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.next
+ e.feed 3
+ assert_raise(StopIteration) { e.next }
+ assert_equal [1,2,3], ary
+end
+
+assert 'Enumerator#feed mixed' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.next
+ e.feed 1
+ e.next
+ e.next
+ e.feed 3
+ assert_raise(StopIteration) { e.next }
+ assert_equal [1,nil,3], ary
+end
+
+assert 'Enumerator#feed twice' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.feed 1
+ assert_raise(TypeError) { e.feed 2 }
+end
+
+assert 'Enumerator#feed before first next' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.feed 1
+ e.next
+ e.next
+ assert_equal [1], ary
+end
+
+assert 'Enumerator#feed yielder' do
+ x = nil
+ e = Enumerator.new {|y| x = y.yield; 10 }
+ e.next
+ e.feed 100
+ assert_raise(StopIteration) { e.next }
+ assert_equal 100, x
+end
+
+assert 'Enumerator#rewind' do
+ e = @obj.to_enum(:foo, 1, 2, 3)
+ assert_equal 1, e.next
+ assert_equal 2, e.next
+ e.rewind
+ assert_equal 1, e.next
+ assert_equal 2, e.next
+ assert_equal 3, e.next
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'Enumerator#rewind clear feed' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum(:each, ary)
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.rewind
+ e.next
+ e.next
+ assert_equal([1,nil], ary)
+end
+
+assert 'Enumerator#rewind clear' do
+ o = Object.new
+ def o.each(ary)
+ ary << yield
+ ary << yield
+ ary << yield
+ end
+ ary = []
+ e = o.to_enum :each, ary
+ e.next
+ e.feed 1
+ e.next
+ e.feed 2
+ e.rewind
+ e.next
+ e.next
+ assert_equal [1,nil], ary
+end
+
+assert 'Enumerator::Generator' do
+ # note: Enumerator::Generator is a class just for internal
+ g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo }
+ g2 = g.dup
+ a = []
+ assert_equal(:foo, g.each {|x| a << x })
+ assert_equal([1, 2, 3], a)
+ a = []
+ assert_equal(:foo, g2.each {|x| a << x })
+ assert_equal([1, 2, 3], a)
+end
+
+assert 'Enumerator::Generator args' do
+ g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x }
+ a = []
+ assert_equal(:bar, g.each(:bar) {|x| a << x })
+ assert_equal([1, 2, 3], a)
+end
+
+assert 'Enumerator::Yielder' do
+ # note: Enumerator::Yielder is a class just for internal
+ a = []
+ y = Enumerator::Yielder.new {|x| a << x }
+ assert_equal(y, y << 1 << 2 << 3)
+ assert_equal([1, 2, 3], a)
+
+ a = []
+ y = Enumerator::Yielder.new {|x| a << x }
+ assert_equal([1], y.yield(1))
+ assert_equal([1, 2], y.yield(2))
+ assert_equal([1, 2, 3], y.yield(3))
+
+ assert_raise(LocalJumpError) { Enumerator::Yielder.new }
+end
+
+assert 'next after StopIteration' do
+ a = [1]
+ e = a.each
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.next }
+ assert_raise(StopIteration) { e.next }
+ e.rewind
+ assert_equal(1, e.next)
+ assert_raise(StopIteration) { e.next }
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'gc' do
+ assert_nothing_raised do
+ 1.times do
+ foo = [1,2,3].to_enum
+ GC.start
+ end
+ GC.start
+ end
+end
+
+assert 'nested iteration' do
+ def (o = Object.new).each
+ yield :ok1
+ yield [:ok2, :x].each.next
+ end
+ e = o.to_enum
+ assert_equal :ok1, e.next
+ assert_equal :ok2, e.next
+ assert_raise(StopIteration) { e.next }
+end
+
+assert 'Kernel#to_enum' do
+ assert_equal Enumerator, [].to_enum.class
+ assert_raise(ArgumentError){ nil.to_enum }
+end
+
+
+assert 'modifying existing methods' do
+ e = 3.times
+ i = 0
+ loop_ret = loop {
+ assert_equal i, e.next
+ i += 1
+ }
+ assert_nil loop_ret
+
+ assert_equal Enumerator, loop.class
+ assert_equal Enumerator, 3.times.class
+ assert_equal Enumerator, [].each.class
+ assert_equal Enumerator, [].map.class
+ assert_equal Enumerator, {a:1}.each.class
+ assert_equal Enumerator, (1..5).each.class
+end