diff --git a/spec/ruby/core/array/element_set_spec.rb b/spec/ruby/core/array/element_set_spec.rb index 2e01e838e911..1c143f3e37f7 100644 --- a/spec/ruby/core/array/element_set_spec.rb +++ b/spec/ruby/core/array/element_set_spec.rb @@ -439,7 +439,6 @@ def obj.to_ary() [1, 2, 3] end ruby_version_is "2.6" do describe "Array#[]= with [m..]" do - it "just sets the section defined by range to nil even if the rhs is nil" do a = [1, 2, 3, 4, 5] a[eval("(2..)")] = nil @@ -476,6 +475,53 @@ def obj.to_ary() [1, 2, 3] end end end +ruby_version_is "2.7" do + describe "Array#[]= with [..n] and [...n]" do + it "just sets the section defined by range to nil even if the rhs is nil" do + a = [1, 2, 3, 4, 5] + a[eval("(..2)")] = nil + a.should == [nil, 4, 5] + a[eval("(...2)")] = nil + a.should == [nil, 5] + end + + it "just sets the section defined by range to nil if n < 0 and the rhs is nil" do + a = [1, 2, 3, 4, 5] + a[eval("(..-3)")] = nil + a.should == [nil, 4, 5] + a[eval("(...-1)")] = [nil, 5] + end + + it "replaces the section defined by range" do + a = [6, 5, 4, 3, 2, 1] + a[eval("(...3)")] = 9 + a.should == [9, 3, 2, 1] + a[eval("(..2)")] = [7, 7, 7, 7, 7] + a.should == [7, 7, 7, 7, 7, 1] + end + + it "replaces the section if n < 0" do + a = [1, 2, 3, 4, 5] + a[eval("(..-2)")] = [7, 8, 9] + a.should == [7, 8, 9, 5] + end + + it "replaces everything if n > the array size" do + a = [1, 2, 3] + a[eval("(...7)")] = [4] + a.should == [4] + end + + it "inserts at the beginning if n < negative the array size" do + a = [1, 2, 3] + a[eval("(..-7)")] = [4] + a.should == [4, 1, 2, 3] + a[eval("(...-10)")] = [6] + a.should == [6, 4, 1, 2, 3] + end + end +end + describe "Array#[] after a shift" do it "works for insertion" do a = [1,2] diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb index 6745bc8d090b..8f0814a5da8b 100644 --- a/spec/ruby/core/array/fill_spec.rb +++ b/spec/ruby/core/array/fill_spec.rb @@ -324,4 +324,11 @@ def obj.<=>(rhs); rhs == self ? 0 : nil end [1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5] end end + + ruby_version_is "2.7" do + it "works with beginless ranges" do + [1, 2, 3, 4].fill('x', eval("(..2)")).should == ["x", "x", "x", 4] + [1, 2, 3, 4].fill(eval("(...2)")) { |x| x + 2 }.should == [2, 3, 3, 4] + end + end end diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb index 845be768c671..6818badeda7e 100644 --- a/spec/ruby/core/array/shared/slice.rb +++ b/spec/ruby/core/array/shared/slice.rb @@ -520,4 +520,41 @@ def to.to_int() -2 end -> { "hello".send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError) -> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError) end + + ruby_version_is "2.6" do + it "can accept endless ranges" do + a = [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(2..)")).should == [2, 3, 4, 5] + a.send(@method, eval("(2...)")).should == [2, 3, 4, 5] + a.send(@method, eval("(-2..)")).should == [4, 5] + a.send(@method, eval("(-2...)")).should == [4, 5] + a.send(@method, eval("(9..)")).should == nil + a.send(@method, eval("(9...)")).should == nil + a.send(@method, eval("(-9..)")).should == nil + a.send(@method, eval("(-9...)")).should == nil + end + end + + ruby_version_is "2.7" do + it "can accept beginless ranges" do + a = [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(..3)")).should == [0, 1, 2, 3] + a.send(@method, eval("(...3)")).should == [0, 1, 2] + a.send(@method, eval("(..-3)")).should == [0, 1, 2, 3] + a.send(@method, eval("(...-3)")).should == [0, 1, 2] + a.send(@method, eval("(..0)")).should == [0] + a.send(@method, eval("(...0)")).should == [] + a.send(@method, eval("(..9)")).should == [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(...9)")).should == [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(..-9)")).should == [] + a.send(@method, eval("(...-9)")).should == [] + end + + it "can accept nil...nil ranges" do + a = [0, 1, 2, 3, 4, 5] + a.send(@method, eval("(nil...nil)")).should == a + a.send(@method, eval("(...nil)")).should == a + a.send(@method, eval("(nil..)")).should == a + end + end end diff --git a/spec/ruby/core/array/slice_spec.rb b/spec/ruby/core/array/slice_spec.rb index 382979493873..33a25921aaa4 100644 --- a/spec/ruby/core/array/slice_spec.rb +++ b/spec/ruby/core/array/slice_spec.rb @@ -165,6 +165,18 @@ def to.to_int() -2 end a.should == [1, 2] end end + + ruby_version_is "2.7" do + it "works with beginless ranges" do + a = [0,1,2,3,4] + a.slice!(eval("(..3)")).should == [0, 1, 2, 3] + a.should == [4] + + a = [0,1,2,3,4] + a.slice!(eval("(...-2)")).should == [0, 1, 2] + a.should == [3, 4] + end + end end describe "Array#slice" do diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb index f6801335f892..019152c51ca4 100644 --- a/spec/ruby/core/array/values_at_spec.rb +++ b/spec/ruby/core/array/values_at_spec.rb @@ -67,4 +67,11 @@ def to.to_int() -2 end [1, 2, 3, 4].values_at(eval("(3...)")).should == [4] end end + + ruby_version_is "2.7" do + it "works when given beginless ranges" do + [1, 2, 3, 4].values_at(eval("(..2)")).should == [1, 2, 3] + [1, 2, 3, 4].values_at(eval("(...2)")).should == [1, 2] + end + end end diff --git a/spec/ruby/core/range/bsearch_spec.rb b/spec/ruby/core/range/bsearch_spec.rb index 38ccd3517059..d31c5d5d5162 100644 --- a/spec/ruby/core/range/bsearch_spec.rb +++ b/spec/ruby/core/range/bsearch_spec.rb @@ -126,8 +126,8 @@ inf = Float::INFINITY (0..inf).bsearch { |x| x == inf }.should == inf (0...inf).bsearch { |x| x == inf }.should == nil - (-inf..0).bsearch { |x| x == -inf }.should == nil - (-inf...0).bsearch { |x| x == -inf }.should == nil + (-inf..0).bsearch { |x| x != -inf }.should == -Float::MAX + (-inf...0).bsearch { |x| x != -inf }.should == -Float::MAX (inf..inf).bsearch { |x| true }.should == inf (inf...inf).bsearch { |x| true }.should == nil (-inf..-inf).bsearch { |x| true }.should == -inf @@ -194,10 +194,10 @@ it "works with infinity bounds" do inf = Float::INFINITY - (0..inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil - (0...inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil - (-inf...0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil - (-inf..0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil + (0..inf).bsearch { |x| x == inf ? 0 : 1 }.should == inf + (0...inf).bsearch { |x| x == inf ? 0 : 1 }.should == nil + (-inf...0).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf + (-inf..0).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf (inf..inf).bsearch { 0 }.should == inf (inf...inf).bsearch { 0 }.should == nil (-inf..-inf).bsearch { 0 }.should == -inf @@ -248,7 +248,7 @@ it "returns an element at an index for which block returns 0" do result = eval("(0..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } - [1, 2].should include(result) + [1, 2, 3].should include(result) end end end @@ -330,4 +330,111 @@ end end end + + + ruby_version_is "2.7" do + context "with beginless ranges and Integer values" do + context "with a block returning true or false" do + it "returns the smallest element for which block returns true" do + eval("(..10)").bsearch { |x| x >= 2 }.should == 2 + eval("(...-1)").bsearch { |x| x >= -10 }.should == -10 + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns greater than zero for every element" do + eval("(..0)").bsearch { |x| 1 }.should be_nil + end + + it "returns nil if the block never returns zero" do + eval("(..0)").bsearch { |x| x > 5 ? -1 : 1 }.should be_nil + end + + it "accepts Float::INFINITY from the block" do + eval("(..0)").bsearch { |x| Float::INFINITY }.should be_nil + end + + it "returns an element at an index for which block returns 0.0" do + result = eval("(..10)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = eval("(...10)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + [1, 2, 3].should include(result) + end + end + end + + context "with beginless ranges and Float values" do + context "with a block returning true or false" do + it "returns nil if the block returns true for every element" do + eval("(..-0.1)").bsearch { |x| x > 0.0 }.should be_nil + eval("(...-0.1)").bsearch { |x| x > 0.0 }.should be_nil + end + + it "returns nil if the block returns nil for every element" do + eval("(..-0.1)").bsearch { |x| nil }.should be_nil + eval("(...-0.1)").bsearch { |x| nil }.should be_nil + end + + it "returns the smallest element for which block returns true" do + eval("(..10)").bsearch { |x| x >= 2 }.should == 2 + eval("(..10)").bsearch { |x| x >= 1 }.should == 1 + end + + it "works with infinity bounds" do + inf = Float::INFINITY + eval("(..inf)").bsearch { |x| true }.should == -inf + eval("(...inf)").bsearch { |x| true }.should == -inf + eval("(..-inf)").bsearch { |x| true }.should == -inf + eval("(...-inf)").bsearch { |x| true }.should == nil + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + eval("(..5.0)").bsearch { |x| -1 }.should be_nil + eval("(...5.0)").bsearch { |x| -1 }.should be_nil + end + + it "returns nil if the block returns greater than zero for every element" do + eval("(..1.1)").bsearch { |x| 1 }.should be_nil + eval("(...1.1)").bsearch { |x| 1 }.should be_nil + end + + it "returns nil if the block never returns zero" do + eval("(..6.3)").bsearch { |x| x < 2 ? 1 : -1 }.should be_nil + end + + it "accepts (+/-)Float::INFINITY from the block" do + eval("(..5.0)").bsearch { |x| Float::INFINITY }.should be_nil + eval("(..7.0)").bsearch { |x| -Float::INFINITY }.should be_nil + end + + it "returns an element at an index for which block returns 0.0" do + result = eval("(..8.0)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = eval("(..8.0)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + result.should >= 1 + result.should <= 3 + end + + it "works with infinity bounds" do + inf = Float::INFINITY + eval("(..-inf)").bsearch { |x| 1 }.should == nil + eval("(...-inf)").bsearch { |x| 1 }.should == nil + eval("(..inf)").bsearch { |x| x == inf ? 0 : 1 }.should == inf + eval("(...inf)").bsearch { |x| x == inf ? 0 : 1 }.should == nil + eval("(..-inf)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf + eval("(...-inf)").bsearch { |x| x == -inf ? 0 : -1 }.should == nil + eval("(..inf)").bsearch { |x| 3 - x }.should == 3 + eval("(...inf)").bsearch { |x| 3 - x }.should == 3 + end + end + end + end end diff --git a/spec/ruby/core/range/count_spec.rb b/spec/ruby/core/range/count_spec.rb new file mode 100644 index 000000000000..f6f60fa0546f --- /dev/null +++ b/spec/ruby/core/range/count_spec.rb @@ -0,0 +1,14 @@ +require_relative '../../spec_helper' + +describe "Range#count" do + ruby_version_is "2.7" do + it "returns Infinity for beginless ranges without arguments or blocks" do + inf = Float::INFINITY + eval("('a'...)").count.should == inf + eval("(7..)").count.should == inf + eval("(...'a')").count.should == inf + eval("(...nil)").count.should == inf + eval("(..10.0)").count.should == inf + end + end +end diff --git a/spec/ruby/core/range/each_spec.rb b/spec/ruby/core/range/each_spec.rb index 8935d8682903..f48a13ead377 100644 --- a/spec/ruby/core/range/each_spec.rb +++ b/spec/ruby/core/range/each_spec.rb @@ -54,6 +54,12 @@ end end + ruby_version_is "2.7" do + it "raises a TypeError beginless ranges" do + -> { eval("(..2)").each { |x| x } }.should raise_error(TypeError) + end + end + it "raises a TypeError if the first element does not respond to #succ" do -> { (0.5..2.4).each { |i| i } }.should raise_error(TypeError) diff --git a/spec/ruby/core/range/equal_value_spec.rb b/spec/ruby/core/range/equal_value_spec.rb index 43d5e0e38a95..f88d3029bbb5 100644 --- a/spec/ruby/core/range/equal_value_spec.rb +++ b/spec/ruby/core/range/equal_value_spec.rb @@ -13,4 +13,10 @@ eval("(1.0..)").should == eval("(1.0..)") end end + + ruby_version_is "2.7" do + it "returns true if the endpoints are == for beginless ranges" do + eval("(...10)").should == eval("(...10)") + end + end end diff --git a/spec/ruby/core/range/first_spec.rb b/spec/ruby/core/range/first_spec.rb index 5f073b48eea1..c5c90800acd5 100644 --- a/spec/ruby/core/range/first_spec.rb +++ b/spec/ruby/core/range/first_spec.rb @@ -46,4 +46,10 @@ it "raises a TypeError when passed a String" do -> { (2..3).first("1") }.should raise_error(TypeError) end + + ruby_version_is "2.7" do + it "raises a RangeError when called on an beginless range" do + -> { eval("(..1)").first }.should raise_error(RangeError) + end + end end diff --git a/spec/ruby/core/range/inspect_spec.rb b/spec/ruby/core/range/inspect_spec.rb index d1072531e930..6a206a035511 100644 --- a/spec/ruby/core/range/inspect_spec.rb +++ b/spec/ruby/core/range/inspect_spec.rb @@ -19,6 +19,18 @@ end end + ruby_version_is '2.7' do + it "works for beginless ranges" do + eval("(..1)").inspect.should == "..1" + eval("(...0.1)").inspect.should == "...0.1" + end + + it "works for nil ... nil ranges" do + eval("(..nil)").inspect.should == "nil..nil" + eval("(nil...)").inspect.should == "nil...nil" + end + end + ruby_version_is ''...'2.7' do it "returns a tainted string if either end is tainted" do (("a".taint)..."c").inspect.tainted?.should be_true diff --git a/spec/ruby/core/range/max_spec.rb b/spec/ruby/core/range/max_spec.rb index 728053cefdca..496a34b28200 100644 --- a/spec/ruby/core/range/max_spec.rb +++ b/spec/ruby/core/range/max_spec.rb @@ -51,6 +51,14 @@ -> { eval("(1..)").max }.should raise_error(RangeError) end end + + ruby_version_is "3.0" do + it "returns the end point (or one less) for beginless ranges" do + eval("(...1)").max.should == 0 + eval("(..1)").max.should == 1 + eval("(..1.0)").max.should == 1.0 + end + end end describe "Range#max given a block" do @@ -85,4 +93,10 @@ ('z'..'l').max {|x,y| x <=> y}.should be_nil (5...5).max {|x,y| x <=> y}.should be_nil end + + ruby_version_is "2.7" do + it "raises RangeError when called with custom comparison method on an beginless range" do + -> { eval("(..1)").max {|a, b| a} }.should raise_error(RangeError) + end + end end diff --git a/spec/ruby/core/range/min_spec.rb b/spec/ruby/core/range/min_spec.rb index f1dff73e6d6b..ffd40f3a07e4 100644 --- a/spec/ruby/core/range/min_spec.rb +++ b/spec/ruby/core/range/min_spec.rb @@ -38,6 +38,19 @@ time_end = Time.now + 1.0 (time_start...time_end).min.should equal(time_start) end + + ruby_version_is "2.6" do + it "returns the start point for endless ranges" do + eval("(1..)").min.should == 1 + eval("(1.0...)").min.should == 1.0 + end + end + + ruby_version_is "2.7" do + it "raises RangeError when called on an beginless range" do + -> { eval("(..1)").min }.should raise_error(RangeError) + end + end end describe "Range#min given a block" do @@ -74,9 +87,8 @@ end ruby_version_is "2.6" do - it "returns the start point for endless ranges" do - eval("(1..)").min.should == 1 - eval("(1.0...)").min.should == 1.0 + it "raises RangeError when called with custom comparison method on an endless range" do + -> { eval("(1..)").min {|a, b| a} }.should raise_error(RangeError) end end end diff --git a/spec/ruby/core/range/size_spec.rb b/spec/ruby/core/range/size_spec.rb index b687342b7259..4bf525e541c6 100644 --- a/spec/ruby/core/range/size_spec.rb +++ b/spec/ruby/core/range/size_spec.rb @@ -25,10 +25,24 @@ end ruby_version_is "2.6" do - it 'returns Float::INFINITY for endless ranges' do + it 'returns Float::INFINITY for endless ranges if the start is numeric' do eval("(1..)").size.should == Float::INFINITY eval("(0.5...)").size.should == Float::INFINITY end + + it 'returns nil for endless ranges if the start is not numeric' do + eval("('z'..)").size.should == nil + eval("([]...)").size.should == nil + end + end + + ruby_version_is "2.7" do + it 'returns Float::INFINITY for all beginless ranges' do + eval("(..1)").size.should == Float::INFINITY + eval("(...0.5)").size.should == Float::INFINITY + eval("(..nil)").size.should == Float::INFINITY + eval("(...'o')").size.should == Float::INFINITY + end end it "returns nil if first and last are not Numeric" do diff --git a/spec/ruby/core/range/to_a_spec.rb b/spec/ruby/core/range/to_a_spec.rb index b0067f0e07e9..768dd35eb03b 100644 --- a/spec/ruby/core/range/to_a_spec.rb +++ b/spec/ruby/core/range/to_a_spec.rb @@ -25,4 +25,10 @@ -> { eval("(1..)").to_a }.should raise_error(RangeError) end end + + ruby_version_is "2.7" do + it "throws an exception for beginless ranges" do + -> { eval("(..1)").to_a }.should raise_error(TypeError) + end + end end diff --git a/spec/tags/core/range/new_tags.txt b/spec/tags/core/range/new_tags.txt deleted file mode 100644 index 95eea7390c17..000000000000 --- a/spec/tags/core/range/new_tags.txt +++ /dev/null @@ -1,2 +0,0 @@ -fails:Range.new beginless/endless range allows beginless left boundary -fails:Range.new beginless/endless range distinguishes ranges with included and excluded right boundary diff --git a/spec/tags/language/range_tags.txt b/spec/tags/language/range_tags.txt deleted file mode 100644 index 84753b36b51e..000000000000 --- a/spec/tags/language/range_tags.txt +++ /dev/null @@ -1 +0,0 @@ -fails:Literal Ranges creates beginless ranges diff --git a/src/main/java/org/truffleruby/core/range/RangeNodes.java b/src/main/java/org/truffleruby/core/range/RangeNodes.java index a854c4874950..6e97970d519f 100644 --- a/src/main/java/org/truffleruby/core/range/RangeNodes.java +++ b/src/main/java/org/truffleruby/core/range/RangeNodes.java @@ -364,12 +364,19 @@ protected Object boundedToA(RubyObjectRange range) { return toAInternalCall.call(range, "to_a_internal"); } - @Specialization(guards = "range.isEndless()") + @Specialization(guards = "range.isEndless() || range.isNilNil()") protected Object endlessToA(RubyObjectRange range) { throw new RaiseException(getContext(), coreExceptions().rangeError( "cannot convert endless range to an array", this)); } + + @Specialization(guards = "range.isBeginless()") + protected Object beginlessToA(RubyObjectRange range) { + throw new RaiseException(getContext(), coreExceptions().typeError( + "can't iterate from NilClass", + this)); + } } /** Returns a conversion of the range into an int range, with regard to the supplied array (the array is necessary @@ -419,6 +426,30 @@ protected RubyIntRange endlessObjectRange(RubyObjectRange range, RubyArray array end); } + @Specialization(guards = "range.isBeginless()") + protected RubyIntRange beginlessObjectRange(RubyObjectRange range, RubyArray array) { + int begin = 0; + int end = toInt(range.end); + return new RubyIntRange( + coreLibrary().rangeClass, + getLanguage().intRangeShape, + range.excludedEnd, + begin, + end); + } + + @Specialization(guards = "range.isNilNil()") + protected RubyIntRange nilNilObjectRange(RubyObjectRange range, RubyArray array) { + int begin = 0; + int end = array.size; + return new RubyIntRange( + coreLibrary().rangeClass, + getLanguage().intRangeShape, + false, + begin, + end); + } + private int toInt(Object indexObject) { if (toIntNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -497,7 +528,7 @@ protected RubyObjectRange objectRange(RubyClass rubyClass, Object begin, Object @Cached AllocateHelperNode allocateHelperNode, @Cached DispatchNode compare) { - if (compare.call(begin, "<=>", end) == nil && end != nil) { + if (compare.call(begin, "<=>", end) == nil && end != nil && begin != nil) { throw new RaiseException(getContext(), coreExceptions().argumentError("bad value for range", this)); } @@ -587,6 +618,18 @@ protected int[] normalizeObjectRange(RubyObjectRange range, int size, return normalize(toInt.execute(range.begin), toInt.execute(range.end), range.excludedEnd, size); } + @Specialization(guards = "range.isBeginless()") + protected int[] normalizeBeginlessRange(RubyObjectRange range, int size, + @Cached ToIntNode toInt) { + return normalize(0, toInt.execute(range.end), range.excludedEnd, size); + } + + @Specialization(guards = "range.isNilNil()") + protected int[] normalizeNilNilRange(RubyObjectRange range, int size, + @Cached ToIntNode toInt) { + return new int[]{ 0, size }; + } + private int[] normalize(int begin, int end, boolean excludedEnd, int size) { if (negativeBegin.profile(begin < 0)) { diff --git a/src/main/java/org/truffleruby/core/range/RubyObjectRange.java b/src/main/java/org/truffleruby/core/range/RubyObjectRange.java index 5e66f47494c7..794b1a64cd75 100644 --- a/src/main/java/org/truffleruby/core/range/RubyObjectRange.java +++ b/src/main/java/org/truffleruby/core/range/RubyObjectRange.java @@ -29,12 +29,20 @@ public RubyObjectRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, Ob this.end = end; } + public boolean isNilNil() { + return begin == Nil.INSTANCE && end == Nil.INSTANCE; + } + public boolean isEndless() { - return end == Nil.INSTANCE; + return begin != Nil.INSTANCE && end == Nil.INSTANCE; + } + + public boolean isBeginless() { + return begin == Nil.INSTANCE && end != Nil.INSTANCE; } public boolean isBounded() { - return end != Nil.INSTANCE; + return begin != Nil.INSTANCE && end != Nil.INSTANCE; } @Override diff --git a/src/main/ruby/truffleruby/core/range.rb b/src/main/ruby/truffleruby/core/range.rb index 11b24eee3910..dd7c4bb43b4a 100644 --- a/src/main/ruby/truffleruby/core/range.rb +++ b/src/main/ruby/truffleruby/core/range.rb @@ -81,6 +81,8 @@ def bsearch(&block) else raise TypeError, "bsearch is not available for #{stop.class}" end + elsif Primitive.nil?(start) && Integer === stop + bsearch_beginless(&block) else raise TypeError, "bsearch is not available for #{start.class}" end @@ -97,8 +99,8 @@ def bsearch(&block) end private def bsearch_float(&block) - normalized_begin = self.begin.to_f - normalized_end = Truffle::RangeOperations.endless?(self) ? Float::INFINITY : self.end.to_f + normalized_begin = Primitive.nil?(self.begin) ? -Float::INFINITY : self.begin.to_f + normalized_end = Primitive.nil?(self.end) ? Float::INFINITY : self.end.to_f normalized_end = normalized_end.prev_float if self.exclude_end? min = normalized_begin max = normalized_end @@ -204,6 +206,26 @@ def bsearch(&block) end end + private def bsearch_beginless(&block) + max = self.end + cur = max + diff = 1 + + while true + result = yield cur + case result + when 0 + return cur + when Numeric + return (cur..max).bsearch(&block) if result > 0 + when false + return (cur..max).bsearch(&block) + end + cur -= diff + diff *= 2 + end + end + private def bsearch_integer(&block) min = self.begin max = self.end @@ -240,6 +262,14 @@ def bsearch(&block) last_admissible end + def count(item = undefined) + if Primitive.nil?(self.begin) || Primitive.nil?(self.end) + return Float::INFINITY unless block_given? || !Primitive.undefined?(item) + end + + super + end + private def each_internal(&block) return to_enum { size } unless block_given? first, last = self.begin, self.end @@ -303,6 +333,7 @@ def bsearch(&block) end def first(n=undefined) + raise RangeError, 'cannot get the first element of beginless range' if Primitive.nil?(self.begin) return self.begin if Primitive.undefined? n super @@ -342,18 +373,27 @@ def ===(value) end def inspect - "#{self.begin.inspect}#{exclude_end? ? "..." : ".."}#{Truffle::RangeOperations.endless?(self) ? "" : self.end.inspect}" + sep = exclude_end? ? '...' : '..' + if (Primitive.nil?(self.begin) && Primitive.nil?(self.end)) + "nil#{sep}nil" + else + (Primitive.nil?(self.begin) ? '' : self.begin.inspect) + sep + (Primitive.nil?(self.end) ? '' : self.end.inspect) + end end def last(n=undefined) - raise RangeError, 'cannot get the last element of endless range' if Truffle::RangeOperations.endless?(self) + raise RangeError, 'cannot get the last element of endless range' if Primitive.nil? self.end return self.end if Primitive.undefined? n to_a.last(n) end def max - raise RangeError, 'cannot get the maximum of endless range' if Truffle::RangeOperations.endless?(self) + raise RangeError, 'cannot get the maximum of endless range' if Primitive.nil? self.end + if Primitive.nil? self.begin + raise RangeError, 'cannot get the maximum of beginless range with custom comparison method' if block_given? + return exclude_end? ? self.end - 1 : self.end + end return super if block_given? || (exclude_end? && !self.end.kind_of?(Numeric)) return nil if Comparable.compare_int(self.end <=> self.begin) < 0 return nil if exclude_end? && Comparable.compare_int(self.end <=> self.begin) == 0 @@ -371,8 +411,13 @@ def max end def min + raise RangeError, 'cannot get the minimum of beginless range' if Primitive.nil? self.begin + if Primitive.nil? self.end + raise RangeError, 'cannot get the minimum of endless range with custom comparison method' if block_given? + return self.begin + end return super if block_given? - if !Truffle::RangeOperations.endless?(self) && Comparable.compare_int(self.end <=> self.begin) < 0 + if Comparable.compare_int(self.end <=> self.begin) < 0 return nil elsif exclude_end? && self.end == self.begin return nil @@ -470,8 +515,9 @@ def cover?(value) end def size + return Float::INFINITY if Primitive.nil? self.begin return nil unless self.begin.kind_of?(Numeric) - return Float::INFINITY if Truffle::RangeOperations.endless?(self) + return Float::INFINITY if Primitive.nil? self.end delta = self.end - self.begin return 0 if delta < 0 diff --git a/src/main/ruby/truffleruby/core/truffle/range_operations.rb b/src/main/ruby/truffleruby/core/truffle/range_operations.rb index a466fa99366f..deb90c204da4 100644 --- a/src/main/ruby/truffleruby/core/truffle/range_operations.rb +++ b/src/main/ruby/truffleruby/core/truffle/range_operations.rb @@ -113,7 +113,7 @@ def self.cover?(range, value) return false unless beg_compare if Comparable.compare_int(beg_compare) <= 0 - return true if endless?(range) + return true if Primitive.nil? range.end end_compare = (value <=> range.end) if range.exclude_end? @@ -126,10 +126,6 @@ def self.cover?(range, value) false end - def self.endless?(range) - Primitive.nil? range.end - end - # MRI: r_less def self.range_less(a, b) compare = a <=> b