diff --git a/docs/scarpet/language/Containers.md b/docs/scarpet/language/Containers.md
index 8b07497c..f017adaf 100644
--- a/docs/scarpet/language/Containers.md
+++ b/docs/scarpet/language/Containers.md
@@ -170,14 +170,21 @@ split('\\.','foo.bar') => [foo, bar]
### `slice(expr, from, to?)`
extracts a substring, or sublist (based on the type of the result of the expression under expr with
-starting index of `from`, and ending at `to` if provided, or the end, if omitted
+starting index of `from`, and ending at `to` if provided, or the end, if omitted. Can use negative indices to
+indicate counting form the back of the list, so `-1 <=> length(_)`.
+
+Special case is made for iterators (`range`, `rect` etc), which does require non-negative indices (negative `from` is treated as
+`0`, and negative `to` as `inf`), but allows retrieving parts of the sequence and ignore
+other parts. In that case consecutive calls to `slice` will refer to index `0` the current iteration position since iterators
+cannot go back nor track where they are in the sequence (see examples).
-slice(l(0,1,2,3,4,5), 1, 3) => [1, 2, 3]
+slice([0,1,2,3,4,5], 1, 3) => [1, 2, 3]
slice('foobar', 0, 1) => 'f'
slice('foobar', 3) => 'bar'
slice(range(10), 3, 5) => [3, 4, 5]
slice(range(10), 5) => [5, 6, 7, 8, 9]
+r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
### `sort(list), sort(values ...)`
diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
index c7a7a409..f6a3d8eb 100644
--- a/src/main/java/carpet/script/value/ListValue.java
+++ b/src/main/java/carpet/script/value/ListValue.java
@@ -289,27 +289,22 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
items.add(v);
}
- public void addAtIndex(int index, List subList)
+ /**
+ * Finds a proper list index >=0 and < len that correspont to the rolling index value of idx
+ *
+ * @param idx
+ * @param len
+ * @return
+ */
+ public static int normalizeIndex(long idx, int len)
{
- int numitems = items.size();
- long range = abs(index)/numitems;
- index += (range+2)*numitems;
- index = index % numitems;
- for (Value v: subList)
- {
- if (index < numitems)
- {
- items.set(index, v);
- }
- else
- {
- items.add(v);
- }
- index++;
- }
+ if (idx >=0 && idx < len) return (int)idx;
+ long range = abs(idx)/len;
+ idx += (range+2)*len;
+ idx = idx % len;
+ return (int)idx;
}
-
public static class ListConstructorValue extends ListValue
{
public ListConstructorValue(Collection extends Value> list)
@@ -339,15 +334,15 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
}
@Override
- public Value slice(long from, long to)
+ public Value slice(long fromDesc, long toDesc)
{
List items = getItems();
int size = items.size();
- if (to < 0 || to > size) to = size;
- if (from < 0 || from > size) from = size;
+ int from = normalizeIndex(fromDesc, size);
+ int to = normalizeIndex(toDesc, size);
if (from > to)
return ListValue.of();
- return new ListValue((Collection extends Value>) getItems().subList((int)from, (int) to));
+ return new ListValue((Collection extends Value>) getItems().subList(from, to));
}
@Override
@@ -395,10 +390,8 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
return false;
int index = (int)((NumericValue) ind).getLong();
if (index < 0)
- {
- long range = abs(index) / numitems;
- index += (range + 2) * numitems;
- index = index % numitems;
+ {// only for values < 0
+ index = normalizeIndex(index, numitems);
}
if (replace)
{
@@ -424,11 +417,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
public Value get(Value value)
{
long index = NumericValue.asNumber(value, "'address' to a list index").getLong();
- int numitems = items.size();
- long range = abs(index)/numitems;
- index += (range+2)*numitems;
- index = index % numitems;
- return items.get((int)index);
+ return items.get(normalizeIndex(index, items.size()));
}
@Override
@@ -443,11 +432,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
{
if (!(where instanceof NumericValue) || items.isEmpty()) return false;
long index = ((NumericValue) where).getLong();
- int numitems = items.size();
- long range = abs(index)/numitems;
- index += (range+2)*numitems;
- index = index % numitems;
- items.remove((int)index);
+ items.remove(normalizeIndex(index, items.size()));
return true;
}
diff --git a/src/main/java/carpet/script/value/StringValue.java b/src/main/java/carpet/script/value/StringValue.java
index 61868c60..48fea7c1 100644
--- a/src/main/java/carpet/script/value/StringValue.java
+++ b/src/main/java/carpet/script/value/StringValue.java
@@ -5,6 +5,8 @@ import net.minecraft.nbt.Tag;
public class StringValue extends Value
{
+ public static Value EMPTY = StringValue.of("");
+
private String str;
@Override
diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java
index c2f188e7..aea956a6 100644
--- a/src/main/java/carpet/script/value/Value.java
+++ b/src/main/java/carpet/script/value/Value.java
@@ -178,16 +178,14 @@ public abstract class Value implements Comparable, Cloneable
return getString().length();
}
- public Value slice(long from, long to)
+ public Value slice(long fromDesc, long toDesc)
{
String value = this.getString();
int size = value.length();
- if (to > size) to = -1;
- if (from < 0) from = 0;
- if (from > size) from = size;
- if (to>=0)
- return new StringValue(value.substring((int)from, (int)to));
- return new StringValue(value.substring((int)from));
+ int from = ListValue.normalizeIndex(fromDesc, size);
+ int to = ListValue.normalizeIndex(toDesc, size);
+ if (from > to) return StringValue.EMPTY;
+ return new StringValue(value.substring(from, to));
}
public double readDoubleNumber()
{