From 7ad6de64ffc4af41c915b699ebb547d6191604cd Mon Sep 17 00:00:00 2001 From: gnembon Date: Fri, 25 Dec 2020 04:55:32 -0400 Subject: [PATCH] slice can accept negative indices --- docs/scarpet/language/Containers.md | 11 +++- .../java/carpet/script/value/ListValue.java | 57 +++++++------------ .../java/carpet/script/value/StringValue.java | 2 + src/main/java/carpet/script/value/Value.java | 12 ++-- 4 files changed, 37 insertions(+), 45 deletions(-) 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 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) getItems().subList((int)from, (int) to)); + return new ListValue((Collection) 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() {