Skip to content

Commit 8ad54d1

Browse files
author
Nicolas Laurent
committed
[GR-27281] Implement beginless range (#2155)
PullRequest: truffleruby/2226
2 parents b7796b9 + 31a9cf7 commit 8ad54d1

File tree

21 files changed

+427
-31
lines changed

21 files changed

+427
-31
lines changed

spec/ruby/core/array/element_set_spec.rb

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,6 @@ def obj.to_ary() [1, 2, 3] end
439439

440440
ruby_version_is "2.6" do
441441
describe "Array#[]= with [m..]" do
442-
443442
it "just sets the section defined by range to nil even if the rhs is nil" do
444443
a = [1, 2, 3, 4, 5]
445444
a[eval("(2..)")] = nil
@@ -476,6 +475,53 @@ def obj.to_ary() [1, 2, 3] end
476475
end
477476
end
478477

478+
ruby_version_is "2.7" do
479+
describe "Array#[]= with [..n] and [...n]" do
480+
it "just sets the section defined by range to nil even if the rhs is nil" do
481+
a = [1, 2, 3, 4, 5]
482+
a[eval("(..2)")] = nil
483+
a.should == [nil, 4, 5]
484+
a[eval("(...2)")] = nil
485+
a.should == [nil, 5]
486+
end
487+
488+
it "just sets the section defined by range to nil if n < 0 and the rhs is nil" do
489+
a = [1, 2, 3, 4, 5]
490+
a[eval("(..-3)")] = nil
491+
a.should == [nil, 4, 5]
492+
a[eval("(...-1)")] = [nil, 5]
493+
end
494+
495+
it "replaces the section defined by range" do
496+
a = [6, 5, 4, 3, 2, 1]
497+
a[eval("(...3)")] = 9
498+
a.should == [9, 3, 2, 1]
499+
a[eval("(..2)")] = [7, 7, 7, 7, 7]
500+
a.should == [7, 7, 7, 7, 7, 1]
501+
end
502+
503+
it "replaces the section if n < 0" do
504+
a = [1, 2, 3, 4, 5]
505+
a[eval("(..-2)")] = [7, 8, 9]
506+
a.should == [7, 8, 9, 5]
507+
end
508+
509+
it "replaces everything if n > the array size" do
510+
a = [1, 2, 3]
511+
a[eval("(...7)")] = [4]
512+
a.should == [4]
513+
end
514+
515+
it "inserts at the beginning if n < negative the array size" do
516+
a = [1, 2, 3]
517+
a[eval("(..-7)")] = [4]
518+
a.should == [4, 1, 2, 3]
519+
a[eval("(...-10)")] = [6]
520+
a.should == [6, 4, 1, 2, 3]
521+
end
522+
end
523+
end
524+
479525
describe "Array#[] after a shift" do
480526
it "works for insertion" do
481527
a = [1,2]

spec/ruby/core/array/fill_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,11 @@ def obj.<=>(rhs); rhs == self ? 0 : nil end
324324
[1, 2, 3, 4].fill(eval("(3...)")) { |x| x + 2 }.should == [1, 2, 3, 5]
325325
end
326326
end
327+
328+
ruby_version_is "2.7" do
329+
it "works with beginless ranges" do
330+
[1, 2, 3, 4].fill('x', eval("(..2)")).should == ["x", "x", "x", 4]
331+
[1, 2, 3, 4].fill(eval("(...2)")) { |x| x + 2 }.should == [2, 3, 3, 4]
332+
end
333+
end
327334
end

spec/ruby/core/array/shared/slice.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,4 +520,41 @@ def to.to_int() -2 end
520520
-> { "hello".send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError)
521521
-> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError)
522522
end
523+
524+
ruby_version_is "2.6" do
525+
it "can accept endless ranges" do
526+
a = [0, 1, 2, 3, 4, 5]
527+
a.send(@method, eval("(2..)")).should == [2, 3, 4, 5]
528+
a.send(@method, eval("(2...)")).should == [2, 3, 4, 5]
529+
a.send(@method, eval("(-2..)")).should == [4, 5]
530+
a.send(@method, eval("(-2...)")).should == [4, 5]
531+
a.send(@method, eval("(9..)")).should == nil
532+
a.send(@method, eval("(9...)")).should == nil
533+
a.send(@method, eval("(-9..)")).should == nil
534+
a.send(@method, eval("(-9...)")).should == nil
535+
end
536+
end
537+
538+
ruby_version_is "2.7" do
539+
it "can accept beginless ranges" do
540+
a = [0, 1, 2, 3, 4, 5]
541+
a.send(@method, eval("(..3)")).should == [0, 1, 2, 3]
542+
a.send(@method, eval("(...3)")).should == [0, 1, 2]
543+
a.send(@method, eval("(..-3)")).should == [0, 1, 2, 3]
544+
a.send(@method, eval("(...-3)")).should == [0, 1, 2]
545+
a.send(@method, eval("(..0)")).should == [0]
546+
a.send(@method, eval("(...0)")).should == []
547+
a.send(@method, eval("(..9)")).should == [0, 1, 2, 3, 4, 5]
548+
a.send(@method, eval("(...9)")).should == [0, 1, 2, 3, 4, 5]
549+
a.send(@method, eval("(..-9)")).should == []
550+
a.send(@method, eval("(...-9)")).should == []
551+
end
552+
553+
it "can accept nil...nil ranges" do
554+
a = [0, 1, 2, 3, 4, 5]
555+
a.send(@method, eval("(nil...nil)")).should == a
556+
a.send(@method, eval("(...nil)")).should == a
557+
a.send(@method, eval("(nil..)")).should == a
558+
end
559+
end
523560
end

spec/ruby/core/array/slice_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,18 @@ def to.to_int() -2 end
165165
a.should == [1, 2]
166166
end
167167
end
168+
169+
ruby_version_is "2.7" do
170+
it "works with beginless ranges" do
171+
a = [0,1,2,3,4]
172+
a.slice!(eval("(..3)")).should == [0, 1, 2, 3]
173+
a.should == [4]
174+
175+
a = [0,1,2,3,4]
176+
a.slice!(eval("(...-2)")).should == [0, 1, 2]
177+
a.should == [3, 4]
178+
end
179+
end
168180
end
169181

170182
describe "Array#slice" do

spec/ruby/core/array/values_at_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,11 @@ def to.to_int() -2 end
6767
[1, 2, 3, 4].values_at(eval("(3...)")).should == [4]
6868
end
6969
end
70+
71+
ruby_version_is "2.7" do
72+
it "works when given beginless ranges" do
73+
[1, 2, 3, 4].values_at(eval("(..2)")).should == [1, 2, 3]
74+
[1, 2, 3, 4].values_at(eval("(...2)")).should == [1, 2]
75+
end
76+
end
7077
end

spec/ruby/core/range/bsearch_spec.rb

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@
126126
inf = Float::INFINITY
127127
(0..inf).bsearch { |x| x == inf }.should == inf
128128
(0...inf).bsearch { |x| x == inf }.should == nil
129-
(-inf..0).bsearch { |x| x == -inf }.should == nil
130-
(-inf...0).bsearch { |x| x == -inf }.should == nil
129+
(-inf..0).bsearch { |x| x != -inf }.should == -Float::MAX
130+
(-inf...0).bsearch { |x| x != -inf }.should == -Float::MAX
131131
(inf..inf).bsearch { |x| true }.should == inf
132132
(inf...inf).bsearch { |x| true }.should == nil
133133
(-inf..-inf).bsearch { |x| true }.should == -inf
@@ -194,10 +194,10 @@
194194

195195
it "works with infinity bounds" do
196196
inf = Float::INFINITY
197-
(0..inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil
198-
(0...inf).bsearch { |x| x == inf ? 0 : -1 }.should == nil
199-
(-inf...0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil
200-
(-inf..0).bsearch { |x| x == -inf ? 0 : 1 }.should == nil
197+
(0..inf).bsearch { |x| x == inf ? 0 : 1 }.should == inf
198+
(0...inf).bsearch { |x| x == inf ? 0 : 1 }.should == nil
199+
(-inf...0).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
200+
(-inf..0).bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
201201
(inf..inf).bsearch { 0 }.should == inf
202202
(inf...inf).bsearch { 0 }.should == nil
203203
(-inf..-inf).bsearch { 0 }.should == -inf
@@ -248,7 +248,7 @@
248248

249249
it "returns an element at an index for which block returns 0" do
250250
result = eval("(0..)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
251-
[1, 2].should include(result)
251+
[1, 2, 3].should include(result)
252252
end
253253
end
254254
end
@@ -330,4 +330,111 @@
330330
end
331331
end
332332
end
333+
334+
335+
ruby_version_is "2.7" do
336+
context "with beginless ranges and Integer values" do
337+
context "with a block returning true or false" do
338+
it "returns the smallest element for which block returns true" do
339+
eval("(..10)").bsearch { |x| x >= 2 }.should == 2
340+
eval("(...-1)").bsearch { |x| x >= -10 }.should == -10
341+
end
342+
end
343+
344+
context "with a block returning negative, zero, positive numbers" do
345+
it "returns nil if the block returns greater than zero for every element" do
346+
eval("(..0)").bsearch { |x| 1 }.should be_nil
347+
end
348+
349+
it "returns nil if the block never returns zero" do
350+
eval("(..0)").bsearch { |x| x > 5 ? -1 : 1 }.should be_nil
351+
end
352+
353+
it "accepts Float::INFINITY from the block" do
354+
eval("(..0)").bsearch { |x| Float::INFINITY }.should be_nil
355+
end
356+
357+
it "returns an element at an index for which block returns 0.0" do
358+
result = eval("(..10)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
359+
result.should == 2
360+
end
361+
362+
it "returns an element at an index for which block returns 0" do
363+
result = eval("(...10)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
364+
[1, 2, 3].should include(result)
365+
end
366+
end
367+
end
368+
369+
context "with beginless ranges and Float values" do
370+
context "with a block returning true or false" do
371+
it "returns nil if the block returns true for every element" do
372+
eval("(..-0.1)").bsearch { |x| x > 0.0 }.should be_nil
373+
eval("(...-0.1)").bsearch { |x| x > 0.0 }.should be_nil
374+
end
375+
376+
it "returns nil if the block returns nil for every element" do
377+
eval("(..-0.1)").bsearch { |x| nil }.should be_nil
378+
eval("(...-0.1)").bsearch { |x| nil }.should be_nil
379+
end
380+
381+
it "returns the smallest element for which block returns true" do
382+
eval("(..10)").bsearch { |x| x >= 2 }.should == 2
383+
eval("(..10)").bsearch { |x| x >= 1 }.should == 1
384+
end
385+
386+
it "works with infinity bounds" do
387+
inf = Float::INFINITY
388+
eval("(..inf)").bsearch { |x| true }.should == -inf
389+
eval("(...inf)").bsearch { |x| true }.should == -inf
390+
eval("(..-inf)").bsearch { |x| true }.should == -inf
391+
eval("(...-inf)").bsearch { |x| true }.should == nil
392+
end
393+
end
394+
395+
context "with a block returning negative, zero, positive numbers" do
396+
it "returns nil if the block returns less than zero for every element" do
397+
eval("(..5.0)").bsearch { |x| -1 }.should be_nil
398+
eval("(...5.0)").bsearch { |x| -1 }.should be_nil
399+
end
400+
401+
it "returns nil if the block returns greater than zero for every element" do
402+
eval("(..1.1)").bsearch { |x| 1 }.should be_nil
403+
eval("(...1.1)").bsearch { |x| 1 }.should be_nil
404+
end
405+
406+
it "returns nil if the block never returns zero" do
407+
eval("(..6.3)").bsearch { |x| x < 2 ? 1 : -1 }.should be_nil
408+
end
409+
410+
it "accepts (+/-)Float::INFINITY from the block" do
411+
eval("(..5.0)").bsearch { |x| Float::INFINITY }.should be_nil
412+
eval("(..7.0)").bsearch { |x| -Float::INFINITY }.should be_nil
413+
end
414+
415+
it "returns an element at an index for which block returns 0.0" do
416+
result = eval("(..8.0)").bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 }
417+
result.should == 2
418+
end
419+
420+
it "returns an element at an index for which block returns 0" do
421+
result = eval("(..8.0)").bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 }
422+
result.should >= 1
423+
result.should <= 3
424+
end
425+
426+
it "works with infinity bounds" do
427+
inf = Float::INFINITY
428+
eval("(..-inf)").bsearch { |x| 1 }.should == nil
429+
eval("(...-inf)").bsearch { |x| 1 }.should == nil
430+
eval("(..inf)").bsearch { |x| x == inf ? 0 : 1 }.should == inf
431+
eval("(...inf)").bsearch { |x| x == inf ? 0 : 1 }.should == nil
432+
eval("(..-inf)").bsearch { |x| x == -inf ? 0 : -1 }.should == -inf
433+
eval("(...-inf)").bsearch { |x| x == -inf ? 0 : -1 }.should == nil
434+
eval("(..inf)").bsearch { |x| 3 - x }.should == 3
435+
eval("(...inf)").bsearch { |x| 3 - x }.should == 3
436+
end
437+
end
438+
end
439+
end
333440
end

spec/ruby/core/range/count_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require_relative '../../spec_helper'
2+
3+
describe "Range#count" do
4+
ruby_version_is "2.7" do
5+
it "returns Infinity for beginless ranges without arguments or blocks" do
6+
inf = Float::INFINITY
7+
eval("('a'...)").count.should == inf
8+
eval("(7..)").count.should == inf
9+
eval("(...'a')").count.should == inf
10+
eval("(...nil)").count.should == inf
11+
eval("(..10.0)").count.should == inf
12+
end
13+
end
14+
end

spec/ruby/core/range/each_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@
5454
end
5555
end
5656

57+
ruby_version_is "2.7" do
58+
it "raises a TypeError beginless ranges" do
59+
-> { eval("(..2)").each { |x| x } }.should raise_error(TypeError)
60+
end
61+
end
62+
5763
it "raises a TypeError if the first element does not respond to #succ" do
5864
-> { (0.5..2.4).each { |i| i } }.should raise_error(TypeError)
5965

spec/ruby/core/range/equal_value_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@
1313
eval("(1.0..)").should == eval("(1.0..)")
1414
end
1515
end
16+
17+
ruby_version_is "2.7" do
18+
it "returns true if the endpoints are == for beginless ranges" do
19+
eval("(...10)").should == eval("(...10)")
20+
end
21+
end
1622
end

spec/ruby/core/range/first_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,10 @@
4646
it "raises a TypeError when passed a String" do
4747
-> { (2..3).first("1") }.should raise_error(TypeError)
4848
end
49+
50+
ruby_version_is "2.7" do
51+
it "raises a RangeError when called on an beginless range" do
52+
-> { eval("(..1)").first }.should raise_error(RangeError)
53+
end
54+
end
4955
end

0 commit comments

Comments
 (0)