Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: opal/opal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7718535c433f
Choose a base ref
...
head repository: opal/opal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5f5db444908c
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Nov 8, 2013

  1. Fix Kernel#to_s

    meh committed Nov 8, 2013

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    c484bf0 View commit details

Commits on Nov 9, 2013

  1. Implement lazy enumerators and remove #next and #rewind

    We always provided a basically useless implementation of next and
    rewind, until we come up with a sane solution that also works for lazy
    and infinite enumerators, it's better to have none than cause hard to
    trace infinite loops.
    meh committed Nov 9, 2013
    Copy the full SHA
    5f5db44 View commit details
Showing with 258 additions and 16 deletions.
  1. +12 −0 opal/core/enumerable.rb
  2. +234 −15 opal/core/enumerator.rb
  3. +1 −1 opal/core/kernel.rb
  4. +11 −0 spec/filters/unsupported/enumerator.rb
12 changes: 12 additions & 0 deletions opal/core/enumerable.rb
Original file line number Diff line number Diff line change
@@ -547,6 +547,18 @@ def inject(object = undefined, sym = undefined, &block)
}
end

def lazy
Enumerator::Lazy.new(self, enumerator_size) {|enum, *args|
enum.yield(*args)
}
end

def enumerator_size
respond_to?(:size) ? size : nil
end

private :enumerator_size

alias map collect

def max(&block)
249 changes: 234 additions & 15 deletions opal/core/enumerator.rb
Original file line number Diff line number Diff line change
@@ -28,22 +28,8 @@ def size
Proc === @size ? @size.call : @size
end

def next
@cache ||= to_a

raise StopIteration, 'end of enumeration' if @cache.empty?

@cache.shift
end

def rewind
@cache = nil

self
end

def inspect
"#<Enumerator: #{@object.inspect}:#{@method}>"
"#<#{self.class.name}: #{@object.inspect}:#{@method}>"
end

class Generator
@@ -97,4 +83,237 @@ def yield(*values)

alias << yield
end

class Lazy < self
class StopLazyError < Exception; end

def initialize(object, size = nil, &block)
unless block_given?
raise ArgumentError, 'tried to call lazy new without a block'
end

@enumerator = object

super size do |yielder, *each_args|
begin
object.each(*each_args) {|*args|
%x{
args.unshift(#{yielder});
if ($opal.$yieldX(block, args) === $breaker) {
return $breaker;
}
}
}
rescue Exception
nil
end
end
end

alias force to_a

def lazy
self
end

def collect(&block)
unless block
raise ArgumentError, 'tried to call lazy map without a block'
end

Lazy.new(self, enumerator_size) {|enum, *args|
%x{
var value = $opal.$yieldX(block, args);
if (value === $breaker) {
return $breaker;
}
#{enum.yield `value`};
}
}
end

def drop(n)
n = Opal.coerce_to n, Integer, :to_int

if n < 0
raise ArgumentError, "attempt to drop negative size"
end

current_size = enumerator_size
set_size = if Integer === current_size
n < current_size ? n : current_size
else
current_size
end

dropped = 0
Lazy.new(self, set_size) {|enum, *args|
if dropped < n
dropped += 1
else
enum.yield(*args)
end
}
end

def drop_while(&block)
unless block
raise ArgumentError, 'tried to call lazy drop_while without a block'
end

succeeding = true
Lazy.new(self, nil) {|enum, *args|
if succeeding
%x{
var value = $opal.$yieldX(block, args);
if (value === $breaker) {
return $breaker;
}
if (#{Opal.falsy?(`value`)}) {
succeeding = false;
#{enum.yield(*args)};
}
}
else
enum.yield(*args)
end
}
end

def find_all(&block)
unless block
raise ArgumentError, 'tried to call lazy select without a block'
end

Lazy.new(self, nil) {|enum, *args|
%x{
var value = $opal.$yieldX(block, args);
if (value === $breaker) {
return $breaker;
}
if (#{Opal.truthy?(`value`)}) {
#{enum.yield(*args)};
}
}
}
end

alias flat_map collect_concat

def grep(pattern, &block)
if block
Lazy.new(self, nil) {|enum, *args|
%x{
var param = #{Opal.destructure(args)},
value = #{pattern === `param`};
if (#{Opal.truthy?(`value`)}) {
value = $opal.$yield1(block, param);
if (value === $breaker) {
return $breaker;
}
#{enum.yield `$opal.$yield1(block, param)`};
}
}
}
else
Lazy.new(self, nil) {|enum, *args|
%x{
var param = #{Opal.destructure(args)},
value = #{pattern === `param`};
if (#{Opal.truthy?(`value`)}) {
#{enum.yield `param`};
}
}
}
end
end

alias map collect

alias select find_all

def reject(&block)
unless block
raise ArgumentError, 'tried to call lazy reject without a block'
end

Lazy.new(self, nil) {|enum, *args|
%x{
var value = $opal.$yieldX(block, args);
if (value === $breaker) {
return $breaker;
}
if (#{Opal.falsy?(`value`)}) {
#{enum.yield(*args)};
}
}
}
end

def take(n)
n = Opal.coerce_to n, Integer, :to_int

if n < 0
raise ArgumentError, "attempt to take negative size"
end

current_size = enumerator_size
set_size = if Integer === current_size
n < current_size ? n : current_size
else
current_size
end

taken = 0
Lazy.new(self, set_size) {|enum, *args|
if taken < n
enum.yield(*args)
taken += 1
else
raise StopLazyError
end
}
end

def take_while(&block)
unless block
raise ArgumentError, 'tried to call lazy take_while without a block'
end

Lazy.new(self, nil) {|enum, *args|
%x{
var value = $opal.$yieldX(block, args);
if (value === $breaker) {
return $breaker;
}
if (#{Opal.truthy?(`value`)}) {
#{enum.yield(*args)};
}
else {
#{raise StopLazyError};
}
}
}
end

def inspect
"#<#{self.class.name}: #{@enumerator.inspect}>"
end
end
end
2 changes: 1 addition & 1 deletion opal/core/kernel.rb
Original file line number Diff line number Diff line change
@@ -538,7 +538,7 @@ def to_proc
end

def to_s
`"#<" + self._klass._name + ":" + self._id + ">"`
`"#<" + #{self.class.name} + ":" + self._id + ">"`
end

def freeze
11 changes: 11 additions & 0 deletions spec/filters/unsupported/enumerator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
opal_filter "Enumerator as generator" do
fails "Enumerator#next returns the next element of the enumeration"
fails "Enumerator#next raises a StopIteration exception at the end of the stream"
fails "Enumerator#next cannot be called again until the enumerator is rewound"

fails "Enumerator#rewind resets the enumerator to its initial state"
fails "Enumerator#rewind returns self"
fails "Enumerator#rewind has no effect on a new enumerator"
fails "Enumerator#rewind has no effect if called multiple, consecutive times"
fails "Enumerator#rewind does nothing if the object doesn't have a #rewind method"
end