Fix range expansion with negative ends

If just one of the range ends is negative, this now forces direction away from it.

I.e. if the beginning is negative, we go in reverse.
If the end is negative, we go forwards.

This fixes cases like

    $var[2..-1]

if $var only has one element.
This commit is contained in:
Fabian Homborg 2018-04-26 22:42:48 +02:00
parent 5692adbdf6
commit 81a987c39c
4 changed files with 18 additions and 3 deletions

View file

@ -631,6 +631,8 @@ Examples:
Both command substitution and shell variable expansion support accessing only specific items by providing a set of indices in square brackets. It's often needed to access a sequence of elements. To do this, use the range operator '`..`' for this. A range '`a..b`', where range limits 'a' and 'b' are integer numbers, is expanded into a sequence of indices '`a a+1 a+2 ... b`' or '`a a-1 a-2 ... b`' depending on which of 'a' or 'b' is higher. The negative range limits are calculated from the end of the array or command substitution. Note that invalid indexes for either end are silently clamped to one or the size of the array as appropriate.
Range expansion will go in reverse if the end element is earlier in the list than the start, unless exactly one of the given indices is negative. This is to enable clamping without changing direction if the list has fewer elements than expected.
Some examples:
\fish
@ -649,6 +651,11 @@ echo (seq 10)[-1..1]
# Uses elements from the last output line to
# the first one in reverse direction
# Output is: 10 9 8 7 6 5 4 3 2 1
# The command substitution has only one line,
# so these will result in empty output:
echo (echo one)[2..-1]
echo (echo one)[-3..1]
\endfish
The same works when setting or expanding variables:

View file

@ -228,6 +228,12 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector<long
i2 = i2 < size ? i2 : size;
// debug( 0, L"Push range idx %d %d", i1, i2 );
short direction = i2 < i1 ? -1 : 1;
// If only the beginning is negative, always go reverse.
// If only the end, always go forward.
// Prevents `[x..-1]` from going reverse if less than x elements are there.
if (tmp1 > -1 != tmp > -1) {
direction = tmp1 > -1 ? -1 : 1;
}
for (long jjj = i1; jjj * direction <= i2 * direction; jjj += direction) {
// debug(0, L"Expand range [subst]: %i\n", jjj);
idx.push_back(jjj);

View file

@ -65,7 +65,7 @@ expansion "$$foo"
expansion $$foo
expansion "prefix$$foo"
expansion prefix$$foo
expansion $foo[-5..2]
expansion $foo[-5..2] # No result, because the starting index is invalid and we force-reverse.
expansion $foo[-2..-1]
expansion $foo[-10..-5]
expansion (printf '%s\n' $foo)[-5..2]
@ -87,6 +87,7 @@ set -l foo a b c
expansion $foo[17]
expansion $foo[-17]
expansion $foo[17..18]
expansion $foo[4..-2]
echo "$foo[d]"
echo $foo[d]

View file

@ -36,10 +36,10 @@
2 baz quux
1 prefixbaz quux fooest
2 prefixbaz prefixquux
2 bar
0
2 fooest
0
2 bar
0
2 fooest
0
1
@ -55,6 +55,7 @@
0
0
0
0
####################