diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 2978583c..86d62dc0 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -51,6 +51,7 @@ import net.minecraft.item.BlockItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.particle.ParticleEffect; import net.minecraft.predicate.entity.EntityPredicates; @@ -2789,6 +2790,13 @@ public class CarpetExpression return (cc, tt) -> ret; }); + this.expr.addLazyFunction("escape_nbt", 1, (c, t, lv) -> { + Value v = lv.get(0).evalValue(c); + String string = v.getString(); + Value ret = new StringValue(StringTag.escape(string)); + return (cc, tt) -> ret; + }); + //"overridden" native call that prints to stderr this.expr.addLazyFunction("print", 1, (c, t, lv) -> { diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 06eb0506..bd8e166e 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -45,6 +45,7 @@ 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; @@ -2852,8 +2853,29 @@ public class Expression implements Cloneable addUnaryFunction("title", v -> new StringValue(WordUtils.capitalizeFully(v.getString()))); - // TODO: split, replace, replace_first, escape_special (to make nbt to string) + addFunction("replace", (lv) -> + { + if (lv.size() != 3 && lv.size() !=2) + throw new InternalExpressionException("'replace' expects string to read, pattern regex, and optional replacement string"); + String data = lv.get(0).getString(); + String regex = lv.get(1).getString(); + String replacement = ""; + if (lv.size() == 3) + replacement = lv.get(2).getString(); + return new StringValue(data.replaceAll(regex, replacement)); + }); + addFunction("replace_first", (lv) -> + { + if (lv.size() != 3 && lv.size() !=2) + throw new InternalExpressionException("'replace_first' expects string to read, pattern regex, and optional replacement string"); + String data = lv.get(0).getString(); + String regex = lv.get(1).getString(); + String replacement = ""; + if (lv.size() == 3) + replacement = lv.get(2).getString(); + return new StringValue(data.replaceFirst(regex, replacement)); + }); addUnaryFunction("type", v -> new StringValue(v.getTypeString())); @@ -2977,10 +2999,7 @@ public class Expression implements Cloneable */ public Expression(String expression) { - this.expression = expression.trim(). - replaceAll("\\r\\n?", "\n"). - replaceAll("\\t"," "). - replaceAll(";+$", ""); + this.expression = expression.trim().replaceAll("\\r\\n?", "\n").replaceAll("\\t"," "); VariablesAndConstants(); UserDefinedFunctionsAndControlFlow(); Operators(); @@ -2996,20 +3015,27 @@ public class Expression implements Cloneable Stack stack = new Stack<>(); 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); Tokenizer.Token lastFunction = null; Tokenizer.Token previousToken = null; - while (tokenizer.hasNext()) + for (Tokenizer.Token token : cleanedTokens) { - Tokenizer.Token token; - try - { - token = tokenizer.next(); - } - catch (StringIndexOutOfBoundsException e) - { - throw new ExpressionException("Script ended prematurely"); - } switch (token.type) { case STRINGPARAM: diff --git a/src/main/java/carpet/script/Tokenizer.java b/src/main/java/carpet/script/Tokenizer.java index 0a1f7524..4a4ba257 100644 --- a/src/main/java/carpet/script/Tokenizer.java +++ b/src/main/java/carpet/script/Tokenizer.java @@ -193,14 +193,14 @@ public class Tokenizer implements Iterator token.append(ch); pos++; linepos++; - if (expression != null && previousToken != null && previousToken.type == Token.TokenType.OPERATOR && (ch == ')' || ch == ',') ) - { - if (previousToken.surface.equalsIgnoreCase(";")) - throw new ExpressionException(this.expression, previousToken, - "Cannot have semicolon at the end of the expression"); + + if (expression != null && previousToken != null && + previousToken.type == Token.TokenType.OPERATOR && + (ch == ')' || ch == ',') && + !previousToken.surface.equalsIgnoreCase(";") + ) throw new ExpressionException(this.expression, previousToken, - "Can't have operator "+previousToken.surface+" at the end of a subexpression"); - } + "Can't have operator " + previousToken.surface + " at the end of a subexpression"); } else { diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 26beeea0..f27e1c33 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -722,6 +722,14 @@ public class EntityValue extends Value } }); + put("ai", (e, v) -> + { + if (e instanceof MobEntity) + { + ((MobEntity) e).setAiDisabled(!v.getBoolean()); + } + }); + // gamemode // spectate // "fire"