diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 6b4f22b6..fe4fafab 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -13,10 +13,12 @@ import carpet.script.Fluff.TriFunction; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; import carpet.script.value.AbstractListValue; +import carpet.script.value.ContainerValueInterface; import carpet.script.value.FunctionSignatureValue; import carpet.script.value.GlobalValue; import carpet.script.value.LazyListValue; import carpet.script.value.ListValue; +import carpet.script.value.MapValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; @@ -1227,7 +1229,12 @@ public class Expression implements Cloneable */ public void Operators() { - addBinaryOperator(".", precedence.get("attribute~."),true, Value::getElementAt); + addBinaryOperator(".", precedence.get("attribute~."),true, (container, key) -> + { + if (container instanceof ContainerValueInterface) + return ((ContainerValueInterface) container).get(key); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); addBinaryOperator("+", precedence.get("addition+-"), true, Value::add); addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract); addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply); @@ -1322,7 +1329,7 @@ public class Expression implements Cloneable v1.assertAssignable(); String varname = v1.getVariable(); LazyValue boundedLHS; - if (v1 instanceof ListValue) + if (v1 instanceof ListValue || v1 instanceof MapValue) { ((ListValue) v1).append(v2); boundedLHS = (cc, tt)-> v1; @@ -1872,7 +1879,46 @@ public class Expression implements Cloneable return LazyListValue.range(from, to, step); }); - addBinaryFunction("get", Value::getElementAt); + addFunction("m", lv -> + { + if (lv.size() == 1 && lv.get(0) instanceof LazyListValue) + return new MapValue(((LazyListValue) lv.get(0)).unroll()); + return new MapValue(lv); + }); + + addUnaryFunction("keys", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().keySet()); + throw new InternalExpressionException("keys applies only to maps"); + }); + + addUnaryFunction("values", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().values()); + throw new InternalExpressionException("keys applies only to maps"); + }); + + addUnaryFunction("pairs", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().entrySet().stream().map( + (p) -> ListValue.of(p.getKey(), p.getValue()) + ).collect(Collectors.toList())); + throw new InternalExpressionException("pairs applies only to maps"); + }); + + addBinaryFunction("get", (container, key) -> + { + if (container instanceof ContainerValueInterface) + return ((ContainerValueInterface) container).get(key); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); + + addBinaryFunction("has", (container, key) -> + { + if (container instanceof ContainerValueInterface) + return new NumericValue(((ContainerValueInterface) container).has(key)); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); //Deprecated, use "get" instead addBinaryFunction("element", (v1, v2) -> @@ -1892,27 +1938,36 @@ public class Expression implements Cloneable { if(lv.size()<3) { - throw new InternalExpressionException("put takes at least three arguments, a list, index, and values to insert at that index"); + throw new InternalExpressionException("put takes at least three arguments, a container, address, and values to insert at that index"); } - Value list = lv.get(0); - if (!(list instanceof ListValue)) + Value container = lv.get(0); + if (!(container instanceof ContainerValueInterface)) { - throw new InternalExpressionException("First argument of element should be a list"); + throw new InternalExpressionException("First argument of put should be a container: list, map or nbt"); } - int size = lv.size(); - //List items = ((ListValue)lv.get(0)).getItems(); - Value index = lv.get(1); - if (index == Value.NULL) + Value where = lv.get(1); + Value what = lv.get(2); + Value ret; + if (lv.size()>3) { - ((ListValue) list).extend(lv.subList(2,size)); + return ((ContainerValueInterface) container).put(where, what, lv.get(3)); } else { - ((ListValue) list).addAtIndex((int) NumericValue.asNumber(index).getLong(), lv.subList(2,size)); + return ((ContainerValueInterface) container).put(where, what); } - return new NumericValue(size-2); }); + addBinaryFunction("delete", (container, address) -> + { + if (container instanceof ContainerValueInterface) + { + return ((ContainerValueInterface) container).remove(address); + } + throw new InternalExpressionException("First argument of remove should be a container: list, map or nbt"); + }); + + //condition and expression will get a bound 'i' //returns last successful expression or false // while(cond, limit, expr) => ?? diff --git a/src/main/java/carpet/script/value/AbstractListValue.java b/src/main/java/carpet/script/value/AbstractListValue.java index f7e33a02..f72ffd59 100644 --- a/src/main/java/carpet/script/value/AbstractListValue.java +++ b/src/main/java/carpet/script/value/AbstractListValue.java @@ -1,9 +1,15 @@ package carpet.script.value; +import carpet.script.exception.InternalExpressionException; + import java.util.Iterator; public abstract class AbstractListValue extends Value { public abstract Iterator iterator(); public void fatality() { } + public void append(Value v) + { + throw new InternalExpressionException("Cannot append a value to abstract list"); + } } diff --git a/src/main/java/carpet/script/value/ContainerValueInterface.java b/src/main/java/carpet/script/value/ContainerValueInterface.java new file mode 100644 index 00000000..09e54b28 --- /dev/null +++ b/src/main/java/carpet/script/value/ContainerValueInterface.java @@ -0,0 +1,13 @@ +package carpet.script.value; + +public interface ContainerValueInterface +{ + public Value put(Value where, Value value); + default Value put(Value where, Value value, Value conditions) + { + return put(where, value); + } + public Value get(Value where); + public boolean has(Value where); + public Value remove(Value where); +} diff --git a/src/main/java/carpet/script/value/LazyListValue.java b/src/main/java/carpet/script/value/LazyListValue.java index e34a83e7..5035c018 100644 --- a/src/main/java/carpet/script/value/LazyListValue.java +++ b/src/main/java/carpet/script/value/LazyListValue.java @@ -126,11 +126,6 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato return false; } - @Override - public Value getElementAt(Value v2) - { - throw new InternalExpressionException("get element can only be obtained for regular lists"); - } @Override public String getTypeString() { diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index d24cd39c..a875e519 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -9,7 +9,7 @@ import java.util.stream.Collectors; import static java.lang.Math.abs; -public class ListValue extends AbstractListValue +public class ListValue extends AbstractListValue implements ContainerValueInterface { protected List items; @Override @@ -98,6 +98,7 @@ public class ListValue extends AbstractListValue } return output; } + @Override public void append(Value v) { items.add(v); @@ -271,6 +272,8 @@ public class ListValue extends AbstractListValue super(list); } } + + @Override public int length() { return items.size(); @@ -309,7 +312,30 @@ public class ListValue extends AbstractListValue } @Override - public Value getElementAt(Value value) + public Value put(Value ind, Value value) + { + if (ind == Value.NULL) + { + items.add(value); + return items.get(items.size()-1); + } + else + { + int numitems = items.size(); + int index = (int) NumericValue.asNumber(ind).getLong(); + if (index < 0) + { + long range = abs(index) / numitems; + index += (range + 2) * numitems; + index = index % numitems; + } + while (index >= items.size()) items.add(Value.NULL); + return items.set(index, value); + } + } + + @Override + public Value get(Value value) { long index = NumericValue.asNumber(value).getLong(); int numitems = items.size(); @@ -319,6 +345,24 @@ public class ListValue extends AbstractListValue return items.get((int)index); } + @Override + public boolean has(Value where) + { + long index = NumericValue.asNumber(where).getLong(); + return index >= 0 && index < items.size(); + } + + @Override + public Value remove(Value where) + { + long index = NumericValue.asNumber(where).getLong(); + int numitems = items.size(); + long range = abs(index)/numitems; + index += (range+2)*numitems; + index = index % numitems; + return items.remove((int)index); + } + @Override public String getTypeString() { diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java index 57610d09..adc6ffb7 100644 --- a/src/main/java/carpet/script/value/MapValue.java +++ b/src/main/java/carpet/script/value/MapValue.java @@ -1,18 +1,32 @@ package carpet.script.value; -import java.util.Arrays; +import carpet.script.exception.InternalExpressionException; + +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -public class MapValue extends AbstractListValue +public class MapValue extends AbstractListValue implements ContainerValueInterface { private Map map; - public MapValue() + + private MapValue() { map = new HashMap<>(); } + public MapValue(List kvPairs) + { + this(); + for (Value v : kvPairs) + { + put(v); + } + } + @Override public Iterator iterator() { @@ -22,7 +36,20 @@ public class MapValue extends AbstractListValue @Override public String getString() { - return Arrays.toString(map.entrySet().toArray()); + return "{"+map.entrySet().stream().map((p) -> p.getKey().getString()+": "+p.getValue().getString()).collect(Collectors.joining(", "))+"}"; + } + + @Override + public String getPrettyString() + { + if (map.size()<6) + return "{"+map.entrySet().stream().map((p) -> p.getKey().getPrettyString()+": "+p.getValue().getPrettyString()).collect(Collectors.joining(", "))+"}"; + List keys = new ArrayList<>(map.keySet()); + int max = keys.size(); + return "{"+keys.get(0).getPrettyString()+": "+map.get(keys.get(0)).getPrettyString()+", "+ + keys.get(1).getPrettyString()+": "+map.get(keys.get(1)).getPrettyString()+", ..., "+ + keys.get(max-2).getPrettyString()+": "+map.get(keys.get(max-2)).getPrettyString()+", "+ + keys.get(max-1).getPrettyString()+": "+map.get(keys.get(max-1)).getPrettyString()+"}"; } @Override @@ -31,4 +58,150 @@ public class MapValue extends AbstractListValue return !map.isEmpty(); } + @Override + public Value clone() + { + return new MapValue(map); + } + public MapValue(Map other) + { + map = new HashMap<>(other); + } + + @Override + public Value add(Value o) + { + throw new InternalExpressionException("cannot add to a map value"); + } + + @Override + public Value subtract(Value v) + { + throw new InternalExpressionException("cannot subtract to a map value"); + } + + @Override + public Value multiply(Value v) + { + throw new InternalExpressionException("cannot multiply to a map value"); + } + + @Override + public Value divide(Value v) + { + throw new InternalExpressionException("cannot divide to a map value"); + } + + public void put(Value v) + { + if (!(v instanceof ListValue)) + { + map.put(v, Value.NULL); + return; + } + ListValue pair = (ListValue)v; + if (pair.getItems().size() != 2) + { + throw new InternalExpressionException("map constructor requires elements that have two elements"); + } + map.put(pair.getItems().get(0), pair.getItems().get(1)); + } + + @Override + public void append(Value v) + { + put(v); + } + + @Override + public int compareTo(Value o) + { + throw new InternalExpressionException("cannot compare to a map value"); + } + + @Override + public boolean equals(final Value o) + { + if (o instanceof MapValue) + { + MapValue ol = (MapValue)o; + int this_size = this.map.keySet().size(); + int o_size = ol.map.keySet().size(); + if (this_size != o_size) return false; + if (this_size == 0) return true; + return this.map.keySet().equals(ol.map.keySet()); + } + return false; + } + + public Map getMap() + { + return map; + } + + public void extend(List subList) + { + for (Value v: subList) put(v); + } + + @Override + public int length() + { + return map.size(); + } + + @Override + public Value in(Value value1) + { + if (map.containsKey(value1)) return value1; + return Value.NULL; + } + + @Override + public Value slice(long from, long to) + { + throw new InternalExpressionException("cannot slice a map value"); + } + + @Override + public double readNumber() + { + return (double)map.size(); + } + + @Override + public Value get(Value v2) + { + return map.getOrDefault(v2, Value.NULL); + } + + @Override + public boolean has(Value where) + { + return map.containsKey(where); + } + + @Override + public Value remove(Value where) + { + return map.remove(where); + } + + @Override + public Value put(Value key, Value value) + { + return map.put(key, value); + } + + @Override + public String getTypeString() + { + return "map"; + } + + @Override + public int hashCode() + { + return map.hashCode(); + } } diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index efb616ee..65eddd74 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -3,6 +3,7 @@ package carpet.script.value; import carpet.script.CarpetContext; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; +import carpet.settings.CarpetSettings; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.entity.HopperBlockEntity; @@ -20,17 +21,18 @@ import net.minecraft.nbt.StringNbtReader; import net.minecraft.nbt.Tag; import net.minecraft.util.math.BlockPos; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; -public class NBTSerializableValue extends Value +public class NBTSerializableValue extends Value implements ContainerValueInterface { private String nbtString = null; private Tag nbtTag = null; - private Supplier nbtSupplier = null; + private Supplier nbtSupplier = null; public NBTSerializableValue(ItemStack stack) { @@ -43,7 +45,7 @@ public class NBTSerializableValue extends Value { try { - return (new StringNbtReader(new StringReader(nbtString))).parseCompoundTag(); + return (new StringNbtReader(new StringReader(nbtString))).parseTag(); } catch (CommandSyntaxException e) { @@ -207,6 +209,82 @@ public class NBTSerializableValue extends Value } } + @Override + public Value put(Value where, Value value) + { + /// WIP + + NbtPathArgumentType.NbtPath path = cachePath(where.getString()); + Tag tagToInsert = value instanceof NBTSerializableValue ? + ((NBTSerializableValue) value).getTag() : + new NBTSerializableValue(value.getString()).getTag(); + CarpetSettings.LOG.error("Inserting tag of class: "+tagToInsert.getClass().getSimpleName()); + + Tag tag = getTag().copy(); + try + { + Collection collection_1 = path.get(tag); //, CompoundTag::new); + for(Tag t: collection_1) + { + ((CompoundTag)t).copyFrom((CompoundTag) tagToInsert); + } + if (collection_1.size() > 0) + { + nbtSupplier = () -> tag; + nbtTag = tag; + } + return this; + } + catch (CommandSyntaxException e) + { + return Value.NULL; + } + } + + @Override + public Value put(Value where, Value value, Value conditions) + { + throw new InternalExpressionException("not implemented"); + } + + @Override + public Value get(Value value) + { + NbtPathArgumentType.NbtPath path = cachePath(value.getString()); + try + { + List tags = path.get(getTag()); + if (tags.size()==0) + return Value.NULL; + if (tags.size()==1) + return NBTSerializableValue.decodeTag(tags.get(0)); + return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList())); + } + catch (CommandSyntaxException ignored) { } + return Value.NULL; + } + + @Override + public boolean has(Value where) + { + return cachePath(where.getString()).count(getTag()) > 0; + } + + @Override + public Value remove(Value where) + { + NbtPathArgumentType.NbtPath path = cachePath(where.getString()); + Tag tag = getTag().copy(); + int removed = path.remove(tag); + if (removed > 0) + { + nbtSupplier = null; + nbtTag = tag; + return Value.TRUE; + } + return Value.FALSE; + } + public static class InventoryLocator { public Object owner; @@ -242,22 +320,7 @@ public class NBTSerializableValue extends Value return res; } - @Override - public Value getElementAt(Value value) - { - NbtPathArgumentType.NbtPath path = cachePath(value.getString()); - try - { - List tags = path.get(getTag()); - if (tags.size()==0) - return Value.NULL; - if (tags.size()==1) - return NBTSerializableValue.decodeTag(tags.get(0)); - return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList())); - } - catch (CommandSyntaxException ignored) { } - return Value.NULL; - } + @Override public String getTypeString() diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java index c0bffde9..eda60bb7 100644 --- a/src/main/java/carpet/script/value/Value.java +++ b/src/main/java/carpet/script/value/Value.java @@ -173,11 +173,6 @@ public abstract class Value implements Comparable, Cloneable return (long)readNumber(); } - public Value getElementAt(Value v2) - { - throw new InternalExpressionException("get element can only be obtained for lists and nbt values"); - } - public String getTypeString() { throw new InternalExpressionException("How did you get here? Cannot get type of an intenal type.");