@@ -30,118 +30,130 @@ var RE_END_DIVIDE = require( './re_end_divide.js' );
30
30
/**
31
31
* Resolves an "end" index.
32
32
*
33
+ * ## Notes
34
+ *
35
+ * - The function returns `-1` if provided an invalid character sequence.
36
+ * - The function returns `-2` if the resolved end index is out-of-bounds.
37
+ *
33
38
* @private
34
39
* @param {string } v - character sequence containing the "end" keyword
35
40
* @param {NonNegativeInteger } len - maximum number of elements allowed in the slice
36
41
* @param {boolean } decrement - boolean indicating whether a subsequence has a negative decrement
42
+ * @param {boolean } strict - boolean indicating whether to enforce strict bounds checking
37
43
* @returns {NonNegativeInteger } end index (exclusive)
38
44
*
39
45
* @example
40
- * var idx = resolveEnd( 'end', 10, false );
46
+ * var idx = resolveEnd( 'end', 10, false, false );
41
47
* // returns 10
42
48
*
43
- * idx = resolveEnd( 'end', 10, true );
49
+ * idx = resolveEnd( 'end', 10, true, false );
44
50
* // returns 10
45
51
*
46
52
* @example
47
- * var idx = resolveEnd( 'end-1', 10, false );
53
+ * var idx = resolveEnd( 'end-1', 10, false, false );
48
54
* // returns 9
49
55
*
50
- * idx = resolveEnd( 'end-1', 10, true );
56
+ * idx = resolveEnd( 'end-1', 10, true, false );
51
57
* // returns 9
52
58
*
53
59
* @example
54
- * var idx = resolveEnd( 'end-2', 10, false );
60
+ * var idx = resolveEnd( 'end-2', 10, false, false );
55
61
* // returns 8
56
62
*
57
- * idx = resolveEnd( 'end-2', 10, true );
63
+ * idx = resolveEnd( 'end-2', 10, true, false );
58
64
* // returns 8
59
65
*
60
66
* @example
61
- * var idx = resolveEnd( 'end/2', 10, false );
67
+ * var idx = resolveEnd( 'end/2', 10, false, false );
62
68
* // returns 5
63
69
*
64
- * idx = resolveEnd( 'end/2', 10, true );
70
+ * idx = resolveEnd( 'end/2', 10, true, false );
65
71
* // returns 4
66
72
*
67
73
* @example
68
- * var idx = resolveEnd( 'end/2', 11, false );
74
+ * var idx = resolveEnd( 'end/2', 11, false, false );
69
75
* // returns 5
70
76
*
71
- * idx = resolveEnd( 'end/2', 11, true );
77
+ * idx = resolveEnd( 'end/2', 11, true, false );
72
78
* // returns 5
73
79
*
74
80
* @example
75
- * var idx = resolveEnd( 'end/3', 10, false );
81
+ * var idx = resolveEnd( 'end/3', 10, false, false );
76
82
* // returns 3
77
83
*
78
- * idx = resolveEnd( 'end/3', 10, true );
84
+ * idx = resolveEnd( 'end/3', 10, true, false );
79
85
* // returns 3
80
86
*
81
87
* @example
82
- * var idx = resolveEnd( 'end/3', 11, false );
88
+ * var idx = resolveEnd( 'end/3', 11, false, false );
83
89
* // returns 3
84
90
*
85
- * idx = resolveEnd( 'end/3', 11, true );
91
+ * idx = resolveEnd( 'end/3', 11, true, false );
86
92
* // returns 3
87
93
*
88
94
* @example
89
- * var idx = resolveEnd( 'end/4', 10, false );
95
+ * var idx = resolveEnd( 'end/4', 10, false, false );
90
96
* // returns 2
91
97
*
92
- * idx = resolveEnd( 'end/4', 10, true );
98
+ * idx = resolveEnd( 'end/4', 10, true, false );
93
99
* // returns 2
94
100
*
95
101
* @example
96
- * var idx = resolveEnd( 'end/4', 11, false );
102
+ * var idx = resolveEnd( 'end/4', 11, false, false );
97
103
* // returns 2
98
104
*
99
- * idx = resolveEnd( 'end/4', 11, true );
105
+ * idx = resolveEnd( 'end/4', 11, true, false );
100
106
* // returns 2
101
107
*
102
108
* @example
103
- * var idx = resolveEnd( 'end/5', 10, false );
109
+ * var idx = resolveEnd( 'end/5', 10, false, false );
104
110
* // returns 2
105
111
*
106
- * idx = resolveEnd( 'end/5', 10, true );
112
+ * idx = resolveEnd( 'end/5', 10, true, false );
107
113
* // returns 1
108
114
*
109
115
* @example
110
- * var idx = resolveEnd( 'end/5', 11, false );
116
+ * var idx = resolveEnd( 'end/5', 11, false, false );
111
117
* // returns 2
112
118
*
113
- * idx = resolveEnd( 'end/5', 11, true );
119
+ * idx = resolveEnd( 'end/5', 11, true, false );
114
120
* // returns 2
115
121
*
116
122
* @example
117
- * var idx = resolveEnd( 'end-20', 10, false );
123
+ * var idx = resolveEnd( 'end-20', 10, false, false );
118
124
* // returns 0
119
125
*
120
- * idx = resolveEnd( 'end-20', 10, true );
126
+ * idx = resolveEnd( 'end-20', 10, true, false );
121
127
* // returns 0
122
128
*
129
+ * idx = resolveEnd( 'end-20', 10, true, true );
130
+ * // returns -2
131
+ *
123
132
* @example
124
- * var idx = resolveEnd( 'end*2', 10, false );
133
+ * var idx = resolveEnd( 'end*2', 10, false, false );
125
134
* // returns -1
126
135
*
127
- * idx = resolveEnd( 'end*2', 10, true );
136
+ * idx = resolveEnd( 'end*2', 10, true, false );
128
137
* // returns -1
129
138
*
130
139
* @example
131
- * var idx = resolveEnd( 'end+1', 10, false );
140
+ * var idx = resolveEnd( 'end+1', 10, false, false );
132
141
* // returns -1
133
142
*
134
- * idx = resolveEnd( 'end+1', 10, true );
143
+ * idx = resolveEnd( 'end+1', 10, true, false );
135
144
* // returns -1
136
145
*
137
146
* @example
138
- * var idx = resolveEnd( 'end/0.5', 10, false );
147
+ * var idx = resolveEnd( 'end/0.5', 10, false, false );
139
148
* // returns -1
140
149
*
141
- * idx = resolveEnd( 'end/0.5', 10, true );
150
+ * idx = resolveEnd( 'end/0.5', 10, true, false );
142
151
* // returns -1
152
+ *
153
+ * idx = resolveEnd( 'end/0.5', 10, true, true );
154
+ * // returns -2
143
155
*/
144
- function resolveEnd ( v , len , decrement ) {
156
+ function resolveEnd ( v , len , decrement , strict ) {
145
157
var tmp ;
146
158
147
159
// Check for the simple case where "end" refers to the last index (exclusive)...
@@ -155,7 +167,10 @@ function resolveEnd( v, len, decrement ) {
155
167
156
168
// If the computed index exceeds the index bounds, clamp to the first index...
157
169
if ( v < 0 ) {
158
- v = 0 ; // TODO: if we ever support throwing an exception for out-of-bounds slices, we'd need to generate an exception here.
170
+ if ( strict ) {
171
+ return - 2 ; // return out-of-bounds error code
172
+ }
173
+ v = 0 ;
159
174
}
160
175
return v ;
161
176
}
@@ -164,7 +179,7 @@ function resolveEnd( v, len, decrement ) {
164
179
if ( tmp ) {
165
180
v = parseFloat ( tmp [ 1 ] ) ;
166
181
if ( v < 1.0 ) {
167
- return - 1 ;
182
+ return ( strict ) ? - 2 : - 1 ; // if `end/x > end`, then need to return out-of-bounds error code
168
183
}
169
184
// Handle division differently for increasing and decreasing increments in order to preserve the `:n + n: = :` identity and to satisfy user expectation that symmetry be maintained. The main issue being that, e.g., given a length 10 subsequence, `end/2` will yield `5`. Hence, `:end/2 + end/2: = :5 + :5 = :` (i.e., the first five elements (0,1,2,3,4) and the second five elements (5,6,7,8,9)); however, if increasing and decreasing are treated the same, `:end/2:-1 + end/2::-1 = :5:-1 + 5::-1 = :`, but the elements are not the same (i.e., the first elements (9,8,7,6) and the second elements (5,4,3,2,1)), due to the non-inclusive aspect of `j` in `i:j:k`. The slight adjustment (`len-1`) yields `:4:-1 + 4::-1 = :` and the slices (9,8,7,6,5) and (4,3,2,1,0), which better matches user expectation. For a length 11 subsequence, we get (0,1,2,3,4) and (5,6,7,8,9,10) for an increasing increment and (10,9,8,7,6) and (5,4,3,2,1,0), which seems fine given asymmetry in both cases.
170
185
if ( decrement && len > 0 && v !== 1.0 ) { // note: avoid violating `end/1 = end` identity
0 commit comments