- Notifications
You must be signed in to change notification settings - Fork 135
/
Copy pathrange.t
368 lines (309 loc) · 16 KB
/
range.t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
useTest;
uselib$?FILE.IO.parent(2).add("packages/Test-Helpers");
useTest::Util;
plan181;
# L<S03/Nonchaining binary precedence/Range object constructor>
# 3..2 must *not* produce "3 2". Use reverse to get a reversed range. -lwall
is~(3..6), "3 4 5 6", "(..) works on numbers (1)";
is~(3..3), "3", "(..) works on numbers (2)";
is~(3..2), "", "(..) works on auto-rev numbers (3)";
is~(8..11), "8 9 10 11", "(..) works on carried numbers (3)";
is~("a".."c"), "a b c", "(..) works on chars (1)";
is~("a".."a"), "a", "(..) works on chars (2)";
is~("b".."a"), "", "(..) works on chars (3)";
is~("a".."z"), "a b c d e f g h i j k l m n o p q r s t u v w x y z", "(..) works on char range ending in z";
is~("A".."Z"), "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z", "(..) works on char range ending in Z";
is~("Y".."AB"), "", "(..) works on carried chars (3)";
is~('Y'..'d'), 'Y Z [ \ ] ^ _ ` a b c d', '(..) works on uppercase letter .. lowercase letter (1)';
is~('z'..'Y'), '', '(..) works on auto-rev uppercase letter .. lowercase letter (2)';
is~('Y'..'_'), 'Y Z [ \ ] ^ _', '(..) works on letter .. non-letter (1)';
is~('_'..'Y'), '', '(..) works on auto-rev letter .. non-letter (2)';
is~(''..''), '', 'all-whitespace range works';
is~(3..9-3), "3 4 5 6", "(..) has correct precedence (1)";
is~(5..9-5), "", "(..) has correct precedence (2)";
is~(2+1..6), "3 4 5 6", "(..) has correct precedence (3)";
is~(2+5..6), "", "(..) has correct precedence (4)";
# Test the three exclusive range operators:
# L<S03/Range and RangeIter semantics/range operator has variants>
is [1^..9], [2..9], "bottom-exclusive range (^..) works (1)";
is [2^..2], [], "bottom-exclusive range (^..) works (2)";
is [3^..2], [], "bottom-exclusive auto-rev range (^..) works (3)";
is [1..^9], [1..8], "top-exclusive range (..^) works (1)";
is [2..^2], [], "top-exclusive range (..^) works (2)";
is [3..^2], [], "top-exclusive auto-rev range (..^) works (3)";
is [1^..^9], [2..8], "double-exclusive range (^..^) works (1)";
is [9^..^1], [], "double-exclusive auto-rev range (^..^) works (2)";
is [1^..^2], [], "double-exclusive range (^..^) can produce null range (1)";
# tests of (x ^..^ x) here and below ensure that our implementation
# of double-exclusive range does not blindly remove an element
# from the head and tail of a list
is [1^..^1], [], "double-exclusive range (x ^..^ x) where x is an int";
is ["a"^.."z"], ["b".."z"], "bottom-exclusive string range (^..) works";
is ["z"^.."a"], [], "bottom-exclusive string auto-rev range (^..) works";
is ["a"..^"z"], ["a".."y"], "top-exclusive string range (..^) works";
is ["z"..^"a"], [], "top-exclusive string auto-rev range (..^) works";
is ["a"^..^"z"], ["b".."y"], "double-exclusive string range (^..^) works";
is ["z"^..^"a"], [], "double-exclusive string auto-rev range (^..^) works";
is ['a'^..^'b'], [], "double-exclusive string range (^..^) can produce null range";
is ['b'^..^'a'], [], "double-exclusive string auto-rev range (^..^) can produce null range";
is ['a'^..^'a'], [], "double-exclusive range (x ^..^ x) where x is a char";
is ('a'..'z').list.join(''), 'a b c d e f g h i j k l m n o p q r s t u v w x y z', '"a".."z"';
# https://github.com/Raku/old-issue-tracker/issues/6001
is ['!'^..'&'], ['"'..'&'], "bottom-exclusive non-alphanumeric string range (^..) works";
is ['!'..^'&'], ['!'..'%'], "top-exclusive non-alphanumeric string range (..^) works";
is ['!'^..^'&'], ['"'..'%'], "double-exclusive non-alphanumeric string range (^..^) works";
is ['%'^..^'&'], [], "double-exclusive non-alphanumeric string range (^..^) can produce null range";
is1.5~~1^..^2, Bool::True, "lazy evaluation of the range operator";
# Test the unary ^ operator
is~(^5), "0 1 2 3 4", "unary ^num produces the range 0..^num";
is [^1], [0], "unary ^ on the boundary ^1 works";
is [^0], [], "unary ^0 produces null range";
is [^-1], [], "unary ^-1 produces null range";
is [^0.1], [0], "unary ^0.1 produces the range 0..^x where 0 < x < 1";
is~(^"5"), "0 1 2 3 4", 'unary ^"num" produces the range 0..^num';
{
my@a=3, 5, 3;
is (^@a).raku, (0..^3).raku, 'unary ^@a produces 0..^+@a';
}
# test iterating on infinite ranges
is (1..*).[^5].join('|'), '1|2|3|4|5', '1..*';
is ('a'..*).[^5].join('|'), 'a|b|c|d|e', '"a"..*';
# test that the zip operator works with ranges
is (1..5Z <a b c>).flat.join('|'), '1|a|2|b|3|c', 'Ranges and infix:<Z>';
is (1..2Z <a b c>).flat.join('|'), '1|a|2|b', 'Ranges and infix:<Z>';
is (<c b a> Z1..5).flat.join('|'), 'c|1|b|2|a|3', 'Ranges and infix:<Z>';
# two ranges
is (1..6Z'a'..'c').flat.join, '1a2b3c', 'Ranges and infix:<Z>';
{
# Test with floats
# 2006-12-05:
# 16:16 <TimToady> ~(1.9 ^..^ 4.9) should produce 2.9, 3.9
# 16:17 <pmichaud> and ~(1.9 ^..^ 4.5) would produce the same?
# 16:17 <TimToady> yes
is~(1.1..4) , "1.1 2.1 3.1", "range with float .min";
is~(1.9..4) , "1.9 2.9 3.9", "range with float .min";
is~(1.1^..4), "2.1 3.1" , "bottom exclusive range of float";
is~(1.9^..4), "2.9 3.9" , "bottom exclusive range of float";
is~(1..4.1) , "1 2 3 4", "range with float .max";
is~(1..4.9) , "1 2 3 4", "range with float .max";
is~(1..^4.1), "1 2 3 4", "top exclusive range of float";
is~(1..^4.9), "1 2 3 4", "top exclusive range of float";
is~(1.1..4.1), "1.1 2.1 3.1 4.1", "range with float .min/.max";
is~(1.9..4.1), "1.9 2.9 3.9" , "range with float .min/.max";
is~(1.1..4.9), "1.1 2.1 3.1 4.1", "range with float .min/.max";
is~(1.9..4.9), "1.9 2.9 3.9 4.9", "range with float .min/.max";
is~(1.1^..^4.1), "2.1 3.1" , "both exclusive float range";
is~(1.9^..^4.1), "2.9 3.9" , "both exclusive float range";
is~(1.1^..^4.9), "2.1 3.1 4.1", "both exclusive float range";
is~(1.9^..^4.9), "2.9 3.9" , "both exclusive float range";
is [1.1^..^1.1], [], "double-exclusive range (x ^..^ x) where x is a float";
}
# Test that the operands are forced to scalar context
# Range.new coerces its arguments to numeric context if needed
# https://github.com/Raku/old-issue-tracker/issues/262
# https://github.com/Raku/old-issue-tracker/issues/2005
{
my@three= (1, 1, 1);
my@one=1;
is~(@one..3) , "1 2 3", "lower inclusive limit is in scalar context";
is~(@one^..3) , "2 3" , "lower exclusive limit is in scalar context";
is~(3^..@one) , "" , "lower exclusive limit is in scalar context";
is~(1..@three) , "1 2 3", "upper inclusive limit is in scalar context";
is~(4..@three) , "" , "upper inclusive limit is in scalar context";
is~(1..^@three) , "1 2" , "upper exclusive limit is in scalar context";
is~(4..^@three) , "" , "upper exclusive limit is in scalar context";
is~(@one..@three), "1 2 3", "both limits is in scalar context";
is~(@one^..@three), "2 3" , "lower exclusive limit scalar context";
is~(@one..^@three), "1 2" , "upper exclusive limit scalar context";
is~(@one^..^@three), "2" , "both exclusive limit scalar context";
}
# test that .map and .grep work on ranges
{
is (0..3).map({$_*2}).join('|'), '0|2|4|6', '.map works on ranges';
is (0..3).grep({$_==1|3}).join('|'), '1|3', '.grep works on ranges';
is (1..3).first({ $_%2==0}), 2, '.first works on ranges';
is (1..3).reduce({ $^a+$^b}), 6, '.reduce works on ranges';
}
# test that range operands are handled in string context if strings
{
my$range;
my$start="100.B";
my$end="102.B";
lives-ok { $range=$start..$end },
'can make range from numeric string vars';
is$range.min, $start, 'range starts at start';
is$range.min.WHAT.gist, Str.gist, 'range start is a string';
is$range.max, $end, 'range ends at end';
is$range.max.WHAT.gist, Str.gist, 'range end is a string';
lives-ok { "$range" }, 'can stringify range';
is~$range, "100.B 101.B 102.B", 'range is correct';
}
# https://github.com/Raku/old-issue-tracker/issues/1171
{
my$range;
lives-ok { '1 3'~~ /(\d+) \s (\d+)/; $range=$0..$1 },
'can make range from match vars';
is$range.min, 1, 'range starts at one';
is$range.max, 3, 'range ends at three';
lives-ok { "$range" }, 'can stringify range';
is~$range, "1 2 3", 'range is correct';
}
# and another set, just for the lulz
# https://github.com/Raku/old-issue-tracker/issues/1171
{
ok'1 3'~~ /(\d) . (\d)/, 'regex sanity';
isa-ok$0..$1, Range, '$0..$1 constructs a Range';
is ($0..$1).join('|'), '1|2|3', 'range from $0..$1';
}
{
my$range;
lives-ok { '1 3'~~ /(\d+) \s (\d+)/; $range=+$0..+$1 },
'can make range from match vars with numeric context forced';
is$range.min, 1, 'range starts at one';
is$range.max, 3, 'range ends at three';
lives-ok { "$range" }, 'can stringify range';
is~$range, "1 2 3", 'range is correct';
}
{
my$range;
lives-ok { '1 3'~~ /(\d+) \s (\d+)/; $range= (~$0)..(~$1) },
'can make range from match vars with string context forced';
is$range.min, 1, 'range starts at one';
is$range.min.WHAT.gist, Str.gist, 'range start is a string';
is$range.max, 3, 'range ends at three';
is$range.max.WHAT.gist, Str.gist, 'range end is a string';
lives-ok { "$range" }, 'can stringify range';
is~$range, "1 2 3", 'range is correct';
}
# L<S03/Nonchaining binary precedence/it is illegal to use a Range as
# implicitly numeric>
{
ok!defined(try { 0..^10 }), '0 .. ^10 is illegal';
}
# Lists are allowed on the rhs if the lhs is numeric (Real):
is~(2.. [<a b c d e>]), "2 3 4 5", '2 .. @list is legal';
# https://github.com/Raku/old-issue-tracker/issues/1254
{
$_=Any; # unsetting $_ to reproduce bug literally
lives-ok {(1..$_)}, '(1..$_) lives';
isa-ok (1..$_), Range, '(..) works on Int .. Any';
}
{
my$range=1..'10';
is+$range, 10, "1 .. '10' has ten elements in it";
is+$range.grep(Numeric), 10, "and they are all numbers";
}
{
my@array=1..10;
my$range=1..@array;
is+$range, 10, "1 .. @array has ten elements in it";
is+$range.grep(Numeric), 10, "and they are all numbers";
}
# https://github.com/Raku/old-issue-tracker/issues/1889
{
lives-ok {("a".."b").map({.trans(""=>"")}).raku},
"range doesn't leak Parrot types";
}
{
my$big=2**130;
my$count=0;
++$countfor$big..$big+2;
is$count, 3, 'can iterate over big Int range';
}
# https://github.com/Raku/old-issue-tracker/issues/2645
{
for1e0..1e0 {
isa-ok$_, Num, 'Range of nums produces a Num';
}
}
# https://github.com/Raku/old-issue-tracker/issues/2119
throws-like'1..2..3', X::Syntax::NonAssociative, '.. is not associative';
{
## once this block died at compile time
## with q[P6opaque: no such attribute '$!phasers']
## cmp. https://github.com/rakudo/rakudo/commit/c5e7a7783d
isa-ok { *.raku for^2 }, Block,
'range optimizer is protected from cases with no block';
}
# https://github.com/Raku/old-issue-tracker/issues/5051
my@opvariants= «..^....^^..^' R..'' R^..'' R..^'' R^..^'»;
for@opvariants {
throws-like"\{ use fatal; |4$_ 5 }", X::Worry::Precedence::Range, "$_ warns on common flattening mistake";
throws-like"\{ use fatal; |4$_ 5 }", X::Worry::Precedence::Range, "$_ warns on common stringification mistake";
eval-lives-ok"\{ use fatal; |(4$_ 5) }", "$_ doesn't warn on parenthesized flattening (range)";
eval-lives-ok"\{ use fatal; (|4)$_ 5 }", "$_ doesn't warn on parenthesized flattening (endpoint)";
eval-lives-ok"\{ use fatal; ~(4$_ 5) }", "$_ doesn't warn on parenthesized stringification (range)";
eval-lives-ok"\{ use fatal; (~4)$_ 5 }", "$_ doesn't warn on parenthesized stringification (endpoint)";
}
# https://github.com/rakudo/rakudo/issues/1582
subtest 'Range operators work with subclasses of Range'=> {
plan10;
# While `(42 but role Foo {}) + 42` produces a plain `Int`, Range ops will produce a subclass
# rather than plain `Range`, if one was used originally, since there's only one `Range` object
# involved, so we know exactly what we have to become.
myroleMeows {}
my$r:= (2..^5) but Meows;
is-deeply ($r+5), ((7..^10) but Meows), 'Range + Real';
is-deeply (5+$r), ((7..^10) but Meows), 'Real + Range';
is-deeply ($r-5), ((-3..^0) but Meows), 'Range - Real'; # No `Real - Range` meaning
is-deeply ($r*5), ((10..^25) but Meows), 'Range * Real';
is-deeply (5*$r), ((10..^25) but Meows), 'Real Range';
is-deeply ($r/5), (((2/5)..^1.0) but Meows), 'Range / Real'; # No `Real / Range` meaning
is-deeply ($r − 5), ((-3..^0) but Meows), "Range U+2212 minus Real";
is-deeply ($r × 5), ((10..^25) but Meows), 'Range U+00D7 Real';
is-deeply (5 × $r), ((10..^25) but Meows), 'Real U+00D7 Range';
is-deeply ($r ÷ 5), (((2/5)..^1.0) but Meows), 'Range U+00F7 Real';
}
# ranges can have an offset applied
subtest 'Ranges can have an offset applied'=> {
plan11;
my@a=do$_for^4+2;
is-deeply [2, 3, 4, 5], @a, 'adding an integer literal offset to a ^ range';
@a=do$_for^4-2;
is-deeply [-2, -1, 0, 1], @a, 'subracting an integer literal offset from a ^ range';
@a=do$_for^4+-2;
is-deeply [-2, -1, 0, 1], @a, 'adding an integer negative literal offset to a ^ range';
@a=do$_for (2..4) +2;
is-deeply [4, 5, 6], @a, 'adding an integer literal offset to a .. range';
myconstant \b =2;
@a=do$_for (2..4) + b;
is-deeply [4, 5, 6], @a, 'adding an integer offset in a constant variable to a .. range';
myint$c=2;
@a=do$_for (2..4) +$c;
is-deeply [4, 5, 6], @a, 'adding an ineger offset in a native int variable to a .. range';
myInt$d=2**65;
@a=do$_for (2..4) +$d;
is-deeply [36893488147419103234, 36893488147419103235, 36893488147419103236], @a, 'adding an offset in a bigint variable to a .. range';
@a=do$_for (2..4) +5e-1;
is-deeply [25e-1, 35e-1, 45e-1], @a, 'adding a Num literal offset to a .. range';
@a=do$_for (2..4) *2;
is-deeply [4, 5, 6, 7, 8], @a, 'multiplying an integer literal offset by a .. range';
@a=do$_for (^4) *2;
is-deeply [0, 1, 2, 3, 4, 5, 6, 7], @a, 'multiplying a ^ range by an integer literal offset';
@a=do$_for (^4) /2;
is-deeply [0.0, 1.0], @a, 'dividing a ^ range by an integer literal offset';
}
# https://github.com/rakudo/rakudo/issues/1809
subtest "Smartmatch Coercions"=> {
plan14;
ok ("42"~~20..50), "stringified number in a context of numeric range, inside the range";
nok ("13"~~20..50), "stringified number in a context of numeric range, less than the min";
nok ("666"~~20..50), "stringified number in a context of numeric range, more than the max";
my$succeed;
lives-ok { $succeed="abc"~~1..10 }, "a string in non-numeric against numeric range doesn't throw";
cmp-ok$succeed, '===', False, "previous smartmatch resulted in False";
ok42~~"3".."9", "number in a context of stringy range, inside the range";
ok"42"~~"3".."9", "same number, stringified, in a context of stringy range, inside the range";
nok42~~"5".."9", "number in a context of stringy range, before the min";
nok"42"~~"5".."9", "same number, stringified, in a context of stringy range, before the min";
nok42~~"1".."3", "number in a context of stringy range, after the max";
nok"42"~~"1".."3", "same number, stringified, in a context of stringy range, after the max";
ok42~~*.."5", "number in a context of whatever-string range, inside the range";
nok42~~*..5, "number in a context of whatever-number range, outside the range";
nok42~~*.."3", "number in a context of whatever-string range, outside the range";
}
# https://github.com/rakudo/rakudo/issues/2676
{
is-deeply (so (3,0).one~~ (1..9)), True, 'Junction ~~ Range ok';
}
# vim: expandtab shiftwidth=4