diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 5f5aaa2d..8a6c367f 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -78,6 +78,7 @@ import net.minecraft.world.LightType; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkStatus; import net.minecraft.world.chunk.WorldChunk; import net.minecraft.world.dimension.DimensionType; import net.minecraft.loot.context.LootContext; @@ -805,6 +806,14 @@ public class CarpetExpression return (c_, t_) -> retval; }); + this.expr.addLazyFunction("generation_status", -1, (c, t, lv) -> + { + BlockPos pos = BlockValue.fromParams((CarpetContext)c, lv, 0).block.getPos(); + Chunk chunk = ((CarpetContext)c).s.getWorld().getChunk(pos.getX()>>4, pos.getZ()>>4, ChunkStatus.EMPTY); + Value retval = new StringValue(chunk.getStatus().getName()); + return (c_, t_) -> retval; + }); + this.expr.addLazyFunction("suffocates", -1, (c, t, lv) -> genericStateTest(c, "suffocates", lv, (s, p, w) -> new NumericValue(s.canSuffocate(w, p)))); diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 75124926..1fdbe740 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -46,7 +46,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import static java.lang.Math.abs; import static java.lang.Math.max; @@ -711,6 +710,21 @@ public class Expression implements Cloneable throw new ExpressionException(expr, token, "Problems in allocating global function "+name); } + Map contextValues = new HashMap<>(); + for (String global : globals) + { + LazyValue lv = context.getVariable(global); + if (lv == null) + { + Value zero = Value.ZERO.reboundedTo(global); + contextValues.put(global, (cc, tt) -> zero); + } + else + { + contextValues.put(global, lv); + } + } + context.host.globalFunctions.put(name, new UserDefinedFunction(arguments, function_context, token) { @Override @@ -725,19 +739,7 @@ public class Expression implements Cloneable } Context newFrame = c.recreate(); - for (String global : globals) - { - LazyValue lv = c.getVariable(global); - if (lv == null) - { - Value zero = Value.ZERO.reboundedTo(global); - newFrame.setVariable(global, (cc, tt) -> zero); - } - else - { - newFrame.setVariable(global, lv); - } - } + contextValues.forEach(newFrame::setVariable); for (int i=0; i new NumericValue(v.hashCode())); + addUnaryFunction("copy", Value::deepcopy); + addLazyFunction("bool", 1, (c, t, lv) -> { Value v = lv.get(0).evalValue(c, Context.BOOLEAN); if (v instanceof StringValue) @@ -3044,21 +3040,7 @@ public class Expression implements Cloneable Tokenizer tokenizer = new Tokenizer(this, expression, allowComments, allowNewlineSubstitutions); // stripping lousy but acceptable semicolons - Iterable iterable = () -> tokenizer; - List originalTokens = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); - List cleanedTokens = new ArrayList<>(); - Tokenizer.Token last = null; - while (originalTokens.size() > 0) - { - Tokenizer.Token current = originalTokens.remove(originalTokens.size()-1); - if (current.type != Tokenizer.Token.TokenType.OPERATOR - || !current.surface.equals(";") - || (last != null && last.type != Tokenizer.Token.TokenType.CLOSE_PAREN && last.type != Tokenizer.Token.TokenType.COMMA && !last.surface.equals(";"))) - cleanedTokens.add(current); - if (current.type != Tokenizer.Token.TokenType.MARKER) - last = current; - } - Collections.reverse(cleanedTokens); + List cleanedTokens = tokenizer.fixSemicolons(); Tokenizer.Token lastFunction = null; Tokenizer.Token previousToken = null; diff --git a/src/main/java/carpet/script/Tokenizer.java b/src/main/java/carpet/script/Tokenizer.java index 88880473..ef81a5a4 100644 --- a/src/main/java/carpet/script/Tokenizer.java +++ b/src/main/java/carpet/script/Tokenizer.java @@ -4,8 +4,11 @@ import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; /** * Expression tokenizer that allows to iterate over a {@link String} @@ -38,6 +41,32 @@ public class Tokenizer implements Iterator this.newLinesMarkers = allowNewLineMakers; } + public List fixSemicolons() + { + Iterable iterable = () -> this; + List originalTokens = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList()); + List cleanedTokens = new ArrayList<>(); + Token last = null; + while (originalTokens.size() > 0) + { + Token current = originalTokens.remove(originalTokens.size()-1); + if (!isSemicolon(current) + || (last != null && last.type != Token.TokenType.CLOSE_PAREN && last.type != Token.TokenType.COMMA && !isSemicolon(last))) + { + if (isSemicolon(current)) + { + current.surface = ";"; + current.type = Token.TokenType.OPERATOR; + } + cleanedTokens.add(current); + } + if (current.type != Token.TokenType.MARKER) + last = current; + } + Collections.reverse(cleanedTokens); + return cleanedTokens; + } + @Override public boolean hasNext() { @@ -60,6 +89,12 @@ public class Tokenizer implements Iterator || (ch >= 'A' && ch <= 'F'); } + public static boolean isSemicolon(Token tok) + { + return ( tok.type == Token.TokenType.OPERATOR && tok.surface.equals(";") ) + || (tok.type == Token.TokenType.UNARY_OPERATOR && tok.surface.equals(";u") ); + } + public static List simplepass(String input) { Tokenizer tok = new Tokenizer(null, input, false, false); diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 10d88e4f..c25c546b 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -58,6 +58,7 @@ import java.util.stream.Collectors; import static carpet.script.value.NBTSerializableValue.nameFromRegistryId; +// TODO: decide whether copy(entity) should duplicate entity in the world. public class EntityValue extends Value { private Entity entity; diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index b16a54b1..397a9baf 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -43,6 +43,15 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf { return new ListValue(items); } + + @Override + public Value deepcopy() + { + List copyItems = new ArrayList<>(items.size()); + for (Value entry: items) copyItems.add(entry.deepcopy()); + return new ListValue(copyItems); + } + public ListValue(Collection list) { items = new ArrayList<>(); diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java index 04924f90..f196b628 100644 --- a/src/main/java/carpet/script/value/MapValue.java +++ b/src/main/java/carpet/script/value/MapValue.java @@ -63,6 +63,15 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa { return new MapValue(map); } + + @Override + public Value deepcopy() + { + Map copyMap = new HashMap<>(); + map.forEach((key, value) -> copyMap.put(key.deepcopy(), value.deepcopy())); + return new MapValue(copyMap); + } + public MapValue(Map other) { map = new HashMap<>(other); diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 48dc030e..3c570fa4 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -95,11 +95,26 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return value; } + + @Override public Value clone() { - return new NBTSerializableValue(getTag().copy()); + // sets only nbttag, even if emtpy; + NBTSerializableValue copy = new NBTSerializableValue(nbtTag); + copy.nbtSupplier = this.nbtSupplier; + copy.nbtString = this.nbtString; + return copy; } + @Override + public Value deepcopy() + { + NBTSerializableValue copy = (NBTSerializableValue) clone(); + // same fields except when tag is set - need to copy it. + if (copy.nbtTag != null) + copy.nbtTag = copy.getTag().copy(); + return copy; + } public static InventoryLocator locateInventory(CarpetContext c, List params, int offset) { diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java index 32ee351b..4b529c6a 100644 --- a/src/main/java/carpet/script/value/Value.java +++ b/src/main/java/carpet/script/value/Value.java @@ -189,4 +189,17 @@ public abstract class Value implements Comparable, Cloneable if (stringVal.isEmpty()) return 0; return ("s"+stringVal).hashCode(); } + + public Value deepcopy() + { + try + { + return (Value)this.clone(); + } + catch (CloneNotSupportedException e) + { + // should never happen + throw new InternalExpressionException("Cannot make a copy of value: "+this); + } + }; }