From 5e276c61150ef1e820937b85d01301d9db430639 Mon Sep 17 00:00:00 2001 From: gnembon Date: Thu, 14 Nov 2019 23:18:07 -0400 Subject: [PATCH 1/3] tighten up profiler client support --- src/main/java/carpet/utils/CarpetProfiler.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/carpet/utils/CarpetProfiler.java b/src/main/java/carpet/utils/CarpetProfiler.java index f9facb78..1548fa06 100644 --- a/src/main/java/carpet/utils/CarpetProfiler.java +++ b/src/main/java/carpet/utils/CarpetProfiler.java @@ -53,11 +53,6 @@ public class CarpetProfiler } } - private static String getWorldString(World world) - { - return world.getDimension().getType().toString().replaceFirst("minecraft:", "") + (world.isClient ? "(Client)" : ""); - } - private static String getSectionString(World world, String section) { return String.format("%s.%s%s", @@ -136,7 +131,7 @@ public class CarpetProfiler public static void end_current_section(ProfilerToken tok) { - if (tick_health_requested == 0L || test_type != TYPE.GENERAL || current_tick_start == 0) + if (tick_health_requested == 0L || test_type != TYPE.GENERAL || current_tick_start == 0 || tok == null) return; long end_time = System.nanoTime(); if (tok.type == TYPE.GENERAL) @@ -154,7 +149,7 @@ public class CarpetProfiler public static void end_current_entity_section(ProfilerToken tok) { - if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0) + if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0 || tok == null) return; long end_time = System.nanoTime(); String section; From 3feb7bffbbcc3bc39dd3f159d97167ebccea879c Mon Sep 17 00:00:00 2001 From: gnembon Date: Thu, 14 Nov 2019 23:19:21 -0400 Subject: [PATCH 2/3] midversion to support fixed profiler --- gradle.properties | 2 +- src/main/java/carpet/settings/CarpetSettings.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 34268a64..d187f3f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ org.gradle.jvmargs=-Xmx1G loader_version=0.6.4+build.169 # Mod Properties - mod_version = 1.2.3+v191113 + mod_version = 1.2.3+v191114 maven_group = carpet archives_base_name = fabric-carpet diff --git a/src/main/java/carpet/settings/CarpetSettings.java b/src/main/java/carpet/settings/CarpetSettings.java index b10d363c..f5fca89e 100644 --- a/src/main/java/carpet/settings/CarpetSettings.java +++ b/src/main/java/carpet/settings/CarpetSettings.java @@ -28,7 +28,7 @@ import static carpet.settings.RuleCategory.TNT; public class CarpetSettings { - public static final String carpetVersion = "1.2.3+v191113"; + public static final String carpetVersion = "1.2.3+v191114"; public static final Logger LOG = LogManager.getLogger(); public static boolean skipGenerationChecks = false; public static boolean impendingFillSkipUpdates = false; From 95b85928f93c300e6ce207d9fd0c17961ab15be1 Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 18 Nov 2019 00:56:50 -0400 Subject: [PATCH 3/3] refactored and reworked functions in scarpet --- .../java/carpet/fakes/EntityInterface.java | 10 +- .../mixins/Entity_scarpetEventsMixin.java | 15 +- .../java/carpet/script/CarpetEventServer.java | 45 ++-- .../java/carpet/script/CarpetExpression.java | 39 +++- .../carpet/script/CarpetScriptServer.java | 9 +- src/main/java/carpet/script/Context.java | 2 +- src/main/java/carpet/script/Expression.java | 143 +++--------- .../carpet/script/ExpressionInspector.java | 16 +- src/main/java/carpet/script/ScriptHost.java | 20 +- .../carpet/script/UserDefinedFunction.java | 29 --- .../script/exception/ExitStatement.java | 13 ++ .../script/exception/ReturnStatement.java | 12 ++ .../script/exception/ThrowStatement.java | 11 + .../java/carpet/script/value/EntityValue.java | 4 +- .../script/value/FunctionSignatureValue.java | 3 - .../carpet/script/value/FunctionValue.java | 204 ++++++++++++++++++ .../java/carpet/settings/SettingsManager.java | 7 +- 17 files changed, 362 insertions(+), 220 deletions(-) delete mode 100644 src/main/java/carpet/script/UserDefinedFunction.java create mode 100644 src/main/java/carpet/script/exception/ExitStatement.java create mode 100644 src/main/java/carpet/script/exception/ReturnStatement.java create mode 100644 src/main/java/carpet/script/exception/ThrowStatement.java create mode 100644 src/main/java/carpet/script/value/FunctionValue.java diff --git a/src/main/java/carpet/fakes/EntityInterface.java b/src/main/java/carpet/fakes/EntityInterface.java index e75084e7..7a192b30 100644 --- a/src/main/java/carpet/fakes/EntityInterface.java +++ b/src/main/java/carpet/fakes/EntityInterface.java @@ -2,7 +2,7 @@ package carpet.fakes; import carpet.script.CarpetContext; import carpet.script.CarpetEventServer; -import carpet.script.ScriptHost; +import carpet.script.value.FunctionValue; import carpet.script.value.Value; import net.minecraft.entity.damage.DamageSource; @@ -13,10 +13,10 @@ public interface EntityInterface float getMainYaw(float partialTicks); //scarpet part void onDeathCallback(String reason); - void setDeathCallback(CarpetContext cc, String function, List extraArgs); + void setDeathCallback(CarpetContext cc, FunctionValue function, List extraArgs); void onDamageCallback(float amount, DamageSource source); - void setDamageCallback(CarpetContext cc, String function, List extraArgs); - void setRemovedCallback(CarpetContext cc, String function, List extraArgs); - void setTickCallback(CarpetContext cc, String function, List extraArgs); + void setDamageCallback(CarpetContext cc, FunctionValue function, List extraArgs); + void setRemovedCallback(CarpetContext cc, FunctionValue function, List extraArgs); + void setTickCallback(CarpetContext cc, FunctionValue function, List extraArgs); CarpetEventServer.ScheduledCall getTickCallback(); } diff --git a/src/main/java/carpet/mixins/Entity_scarpetEventsMixin.java b/src/main/java/carpet/mixins/Entity_scarpetEventsMixin.java index 0359fb61..d6abc1e1 100644 --- a/src/main/java/carpet/mixins/Entity_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/Entity_scarpetEventsMixin.java @@ -4,12 +4,10 @@ import carpet.CarpetServer; import carpet.fakes.EntityInterface; import carpet.script.CarpetContext; import carpet.script.CarpetEventServer; -import carpet.script.ScriptHost; +import carpet.script.value.FunctionValue; import carpet.script.value.Value; -import carpet.settings.CarpetSettings; import net.minecraft.entity.Entity; import net.minecraft.entity.damage.DamageSource; -import org.lwjgl.system.CallbackI; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -27,23 +25,20 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface private CarpetEventServer.ScheduledCall tickCall; private CarpetEventServer.ScheduledCall damageCall; - - - @Override - public void setDeathCallback(CarpetContext cc, String function, List extraArgs) + public void setDeathCallback(CarpetContext cc, FunctionValue function, List extraArgs) { deathCall = CarpetServer.scriptServer.events.makeDeathCall(cc, function, extraArgs); } @Override - public void setRemovedCallback(CarpetContext cc, String function, List extraArgs) + public void setRemovedCallback(CarpetContext cc, FunctionValue function, List extraArgs) { removeCall = CarpetServer.scriptServer.events.makeRemovedCall(cc, function, extraArgs); } @Override - public void setTickCallback(CarpetContext cc, String function, List extraArgs) + public void setTickCallback(CarpetContext cc, FunctionValue function, List extraArgs) { tickCall = CarpetServer.scriptServer.events.makeTickCall(cc, function, extraArgs); } @@ -55,7 +50,7 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface } @Override - public void setDamageCallback(CarpetContext cc, String function, List extraArgs) + public void setDamageCallback(CarpetContext cc, FunctionValue function, List extraArgs) { damageCall = CarpetServer.scriptServer.events.makeDamageCall(cc, function, extraArgs); } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index ff74838e..8515f259 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -3,6 +3,7 @@ package carpet.script; import carpet.CarpetServer; import carpet.script.value.BlockValue; import carpet.script.value.EntityValue; +import carpet.script.value.FunctionValue; import carpet.script.value.ListValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; @@ -19,6 +20,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import net.minecraft.world.dimension.DimensionType; +import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.Arrays; @@ -39,9 +41,9 @@ public class CarpetEventServer public static class Callback { public String host; - public String udf; + public FunctionValue udf; - public Callback(String host, String udf) + public Callback(String host, FunctionValue udf) { this.host = host; this.udf = udf; @@ -50,7 +52,7 @@ public class CarpetEventServer @Override public String toString() { - return udf+((host==null)?"":"(from "+host+")"); + return udf.getString()+((host==null)?"":"(from "+host+")"); } } @@ -61,7 +63,7 @@ public class CarpetEventServer public BlockPos context_origin; public long dueTime; - public ScheduledCall(CarpetContext context, String udf, List args, long dueTime) + public ScheduledCall(CarpetContext context, FunctionValue udf, List args, long dueTime) { super(context.host.getName(), udf); this.args = args; @@ -119,7 +121,7 @@ public class CarpetEventServer // impossible call to add return false; } - UserDefinedFunction udf = host.globalFunctions.get(funName); + FunctionValue udf = host.globalFunctions.get(funName); if (udf == null || udf.getArguments().size() != reqArgs) { // call won't match arguments @@ -127,13 +129,13 @@ public class CarpetEventServer } //all clear //remove duplicates - removeEventCall(hostName, funName); - callList.add(new Callback(hostName, funName)); + removeEventCall(hostName, udf.getString()); + callList.add(new Callback(hostName, udf)); return true; } - public void removeEventCall(String hostName, String callName) + public void removeEventCall(String hostName, String funName) { - callList.removeIf((c)-> c.udf.equalsIgnoreCase(callName) && ( hostName == null || c.host.equalsIgnoreCase(hostName) ) ); + callList.removeIf((c)-> c.udf.getString().equals(funName) && ( hostName == null || c.host.equalsIgnoreCase(hostName) ) ); } public void removeAllCalls(String hostName) @@ -445,7 +447,7 @@ public class CarpetEventServer } } - public void scheduleCall(CarpetContext context, String function, List args, long due) + public void scheduleCall(CarpetContext context, FunctionValue function, List args, long due) { scheduledCalls.add(new ScheduledCall(context, function, args, due)); } @@ -464,8 +466,8 @@ public class CarpetEventServer if (!Event.byName.containsKey(event)) return false; - Callback callback= decodeCallback(funName); - Event.byName.get(event).handler.removeEventCall(callback.host, callback.udf); + Pair call = decodeCallback(funName); + Event.byName.get(event).handler.removeEventCall(call.getLeft(), call.getRight()); return true; } @@ -474,21 +476,20 @@ public class CarpetEventServer Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName)); } - private Callback decodeCallback(String funName) + private Pair decodeCallback(String funName) { Pattern find = Pattern.compile("(\\w+)\\(from (\\w+)\\)"); Matcher matcher = find.matcher(funName); if(matcher.matches()) { - return new Callback(matcher.group(2), matcher.group(1)); + return Pair.of(matcher.group(2), matcher.group(1)); } - return new Callback(null, funName); + return Pair.of(null, funName); } - private ScheduledCall makeEventCall(CarpetContext cc, String function, List extraArgs, int argCount) + private ScheduledCall makeEventCall(CarpetContext cc, FunctionValue function, List extraArgs, int argCount) { - UserDefinedFunction udf = cc.host.globalFunctions.get(function); - if (udf == null || (udf.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argCount) + if (function == null || (function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argCount) { // call won't match arguments return null; @@ -503,19 +504,19 @@ public class CarpetEventServer return new ScheduledCall(cc, function, lazyArgs, 0); } - public ScheduledCall makeDeathCall(CarpetContext cc, String function, List extraArgs) + public ScheduledCall makeDeathCall(CarpetContext cc, FunctionValue function, List extraArgs) { return makeEventCall(cc, function, extraArgs, 2); } - public ScheduledCall makeRemovedCall(CarpetContext cc, String function, List extraArgs) + public ScheduledCall makeRemovedCall(CarpetContext cc, FunctionValue function, List extraArgs) { return makeEventCall(cc, function, extraArgs, 1); } - public ScheduledCall makeTickCall(CarpetContext cc, String function, List extraArgs) + public ScheduledCall makeTickCall(CarpetContext cc, FunctionValue function, List extraArgs) { return makeEventCall(cc, function, extraArgs, 1); } - public ScheduledCall makeDamageCall(CarpetContext cc, String function, List extraArgs) + public ScheduledCall makeDamageCall(CarpetContext cc, FunctionValue function, List extraArgs) { return makeEventCall(cc, function, extraArgs, 4); } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 8d5c9cc7..f0b72463 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -5,10 +5,12 @@ import carpet.fakes.MinecraftServerInterface; import carpet.helpers.FeatureGenerator; import carpet.script.Fluff.TriFunction; import carpet.script.exception.CarpetExpressionException; +import carpet.script.exception.ExitStatement; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; import carpet.script.value.BlockValue; import carpet.script.value.EntityValue; +import carpet.script.value.FunctionValue; import carpet.script.value.LazyListValue; import carpet.script.value.ListValue; import carpet.script.value.NBTSerializableValue; @@ -2012,9 +2014,16 @@ public class CarpetExpression throw new InternalExpressionException("First argument to entity_event should be an entity"); String what = lv.get(1).evalValue(c).getString(); Value functionValue = lv.get(2).evalValue(c); - String function = null; - if (!(functionValue instanceof NullValue)) - function = functionValue.getString(); + if (functionValue instanceof NullValue) + functionValue = null; + else if (!(functionValue instanceof FunctionValue)) + { + String name = functionValue.getString(); + functionValue = c.host.globalFunctions.get(name); + if (functionValue == null) + throw new InternalExpressionException("Function "+name+" is not defined yet"); + } + FunctionValue function = (FunctionValue)functionValue; List args = null; if (lv.size()==4) args = Collections.singletonList(lv.get(3).evalValue(c)); @@ -2928,7 +2937,7 @@ public class CarpetExpression CarpetServer.scriptServer.tickStart = System.nanoTime(); // for the next tick Thread.yield(); if(CarpetServer.scriptServer.stopAll) - throw new Expression.ExitStatement(Value.NULL); + throw new ExitStatement(Value.NULL); return (cc, tt) -> Value.TRUE; }); @@ -3002,20 +3011,28 @@ public class CarpetExpression if (lv.size()<2) throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name"); Long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong(); - String funname = lv.get(1).evalValue(c).getString(); + + Value functionValue = lv.get(1).evalValue(c); + if (!(functionValue instanceof FunctionValue)) + { + String name = functionValue.getString(); + functionValue = c.host.globalFunctions.get(name); + if (functionValue == null) + throw new InternalExpressionException("Function "+name+" is not defined yet"); + } + FunctionValue function = (FunctionValue)functionValue; + CarpetContext cc = (CarpetContext)c; - if (!cc.host.globalFunctions.containsKey(funname)) - throw new InternalExpressionException("Function "+funname+" is not defined"); List args = new ArrayList<>(); for (int i=2; i < lv.size(); i++) { Value arg = lv.get(i).evalValue(cc); args.add( (_c, _t) -> arg); } - if (cc.host.globalFunctions.get(funname).getArguments().size() != args.size()) - throw new InternalExpressionException("Function "+funname+" takes "+ - cc.host.globalFunctions.get(funname).getArguments().size()+" arguments, "+args.size()+" provided."); - CarpetServer.scriptServer.events.scheduleCall(cc, funname, args, delay); + if (function.getArguments().size() != args.size()) + throw new InternalExpressionException("Function "+function.getString()+" takes "+ + function.getArguments().size()+" arguments, "+args.size()+" provided."); + CarpetServer.scriptServer.events.scheduleCall(cc, function, args, delay); return (c_, t_) -> Value.TRUE; }); diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index c879cbb6..c6cef679 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -1,6 +1,7 @@ package carpet.script; import carpet.script.bundled.BundledModule; +import carpet.script.value.FunctionValue; import carpet.script.value.MapValue; import carpet.script.value.StringValue; import carpet.settings.CarpetSettings; @@ -365,19 +366,19 @@ public class CarpetScriptServer return true; } - public boolean runas(ServerCommandSource source, String hostname, String udf_name, List argv) + public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List argv) { - return runas(BlockPos.ORIGIN, source, hostname, udf_name, argv); + return runas(BlockPos.ORIGIN, source, hostname, udf, argv); } - public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, String udf_name, List argv) + public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List argv) { ScriptHost host = globalHost; try { if (hostname != null) host = modules.get(hostname).retrieveForExecution(source); - host.callUDF(origin, source, host.globalFunctions.get(udf_name), argv); + host.callUDF(origin, source, udf, argv); } catch (NullPointerException | InvalidCallbackException npe) { diff --git a/src/main/java/carpet/script/Context.java b/src/main/java/carpet/script/Context.java index 5aaa1399..80f85056 100644 --- a/src/main/java/carpet/script/Context.java +++ b/src/main/java/carpet/script/Context.java @@ -37,7 +37,7 @@ public class Context return host.globalVariables.get(name); } - void setVariable(String name, LazyValue lv) + public void setVariable(String name, LazyValue lv) { if (name.startsWith("global_")) { diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 14608707..910d84ca 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -11,11 +11,15 @@ import carpet.script.Fluff.QuadFunction; import carpet.script.Fluff.QuinnFunction; import carpet.script.Fluff.SexFunction; import carpet.script.Fluff.TriFunction; +import carpet.script.exception.ExitStatement; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; +import carpet.script.exception.ReturnStatement; +import carpet.script.exception.ThrowStatement; import carpet.script.value.AbstractListValue; import carpet.script.value.ContainerValueInterface; import carpet.script.value.FunctionSignatureValue; +import carpet.script.value.FunctionValue; import carpet.script.value.GlobalValue; import carpet.script.value.LContainerValue; import carpet.script.value.LazyListValue; @@ -389,31 +393,6 @@ public class Expression implements Cloneable return copy; } - /* Exception thrown to terminate execution mid expression (aka return statement) */ - static class ExitStatement extends RuntimeException - { - Value retval; - ExitStatement(Value value) - { - retval = value; - } - } - static class ReturnStatement extends ExitStatement - { - - ReturnStatement(Value value) - { - super(value); - } - } - static class ThrowStatement extends ExitStatement - { - ThrowStatement(Value value) - { - super(value); - } - } - static List getExpressionSnippet(Tokenizer.Token token, Expression expr) { @@ -679,22 +658,11 @@ public class Expression implements Cloneable } }); } - private void addContextFunction(Context context, String name, Expression expr, Tokenizer.Token token, List arguments, List globals, LazyValue code) + private FunctionValue addContextFunction(Context context, String name, Expression expr, Tokenizer.Token token, List arguments, List globals, LazyValue code) { name = name.toLowerCase(Locale.ROOT); if (functions.containsKey(name)) throw new ExpressionException(expr, token, "Function "+name+" would mask a built-in function"); - Expression function_context; - try - { - function_context = expr.clone(); - function_context.name = name; - } - catch (CloneNotSupportedException e) - { - throw new ExpressionException(expr, token, "Problems in allocating global function "+name); - } - Map contextValues = new HashMap<>(); for (String global : globals) { @@ -708,67 +676,12 @@ public class Expression implements Cloneable contextValues.put(global, lv); } } + if (contextValues.isEmpty()) contextValues = null; - context.host.globalFunctions.put(name, new UserDefinedFunction(arguments, function_context, token) - { - @Override - public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List lazyParams) - { - if (arguments.size() != lazyParams.size()) // something that might be subject to change in the future - { - throw new ExpressionException(e, t, - "Incorrect number of arguments for function "+name+ - ". Should be "+arguments.size()+", not "+lazyParams.size()+" like "+arguments - ); - } - Context newFrame = c.recreate(); - - contextValues.forEach(newFrame::setVariable); - for (int i=0; i val); - } - Value retVal; - boolean rethrow = false; - try - { - retVal = code.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye - } - catch (ReturnStatement returnStatement) - { - retVal = returnStatement.retval; - } - catch (ThrowStatement throwStatement) // might not be really necessary - { - retVal = throwStatement.retval; - rethrow = true; - } - catch (InternalExpressionException exc) - { - throw new ExpressionException(function_context, t, exc.getMessage()); - } - catch (ArithmeticException exc) - { - throw new ExpressionException(function_context, t, "Your math is wrong, "+exc.getMessage()); - } - catch (ExitStatement | ExpressionException exit) - { - throw exit; - } - catch (Exception exc) - { - throw new ExpressionException(e, t, "Error while evaluating expression: "+exc.getMessage()); - } - if (rethrow) - { - throw new ThrowStatement(retVal); - } - Value otherRetVal = retVal; - return (cc, tt) -> otherRetVal; - } - }); + FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues); + // do not store lambda definitions + if (!name.equals("_")) context.host.globalFunctions.put(name, result); + return result; } /** @@ -944,25 +857,28 @@ public class Expression implements Cloneable addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) -> { // adjust based on c if (lv.size() == 0) throw new InternalExpressionException("'call' expects at least function name to call"); - String name = lv.get(0).evalValue(c).getString(); //lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes if (t != Context.SIGNATURE) // just call the function { - if (!c.host.globalFunctions.containsKey(name)) + Value functionValue = lv.get(0).evalValue(c); + if (!(functionValue instanceof FunctionValue)) { - throw new InternalExpressionException("Function "+name+" is not defined yet"); + String name = functionValue.getString(); + functionValue = c.host.globalFunctions.get(name); + if (functionValue == null) + throw new InternalExpressionException("Function "+name+" is not defined yet"); } List lvargs = new ArrayList<>(lv.size()-1); for (int i=1; i< lv.size(); i++) { lvargs.add(lv.get(i)); } - UserDefinedFunction acf = c.host.globalFunctions.get(name); - Value retval = acf.lazyEval(c, t, acf.expression, acf.token, lvargs).evalValue(c); + FunctionValue fun = (FunctionValue)functionValue; + Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), lvargs).evalValue(c); return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?) } - // gimme signature + String name = lv.get(0).evalValue(c).getString(); List args = new ArrayList<>(); List globals = new ArrayList<>(); for (int i = 1; i < lv.size(); i++) @@ -981,8 +897,6 @@ public class Expression implements Cloneable args.add(v.boundVariable); } } - if (name.equals("_")) - name = "__lambda_"+tok.lineno+"_"+tok.linepos; Value retval = new FunctionSignatureValue(name, args, globals); return (cc, tt) -> retval; }); @@ -1003,20 +917,11 @@ public class Expression implements Cloneable addLazyBinaryOperatorWithDelegation("->", precedence.get("def->"), false, (c, type, e, t, lv1, lv2) -> { Value v1 = lv1.evalValue(c, Context.SIGNATURE); - String result; - if (v1 instanceof FunctionSignatureValue) - { - FunctionSignatureValue sign = (FunctionSignatureValue) v1; - addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getGlobals(), lv2); - result = sign.getName(); - } - else - { - v1.assertAssignable(); - c.setVariable(v1.getVariable(), lv2); - result = v1.getVariable(); - } - return (cc, tt) -> new StringValue(result); + if (!(v1 instanceof FunctionSignatureValue)) + throw new InternalExpressionException("'->' operator requires a function signature on the LHS"); + FunctionSignatureValue sign = (FunctionSignatureValue) v1; + Value result = addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getGlobals(), lv2); + return (cc, tt) -> result; }); addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); }); diff --git a/src/main/java/carpet/script/ExpressionInspector.java b/src/main/java/carpet/script/ExpressionInspector.java index dd762c32..627c0bd2 100644 --- a/src/main/java/carpet/script/ExpressionInspector.java +++ b/src/main/java/carpet/script/ExpressionInspector.java @@ -1,6 +1,6 @@ package carpet.script; -import carpet.CarpetServer; +import carpet.script.exception.ExpressionException; import org.apache.commons.lang3.StringUtils; import java.util.Collections; @@ -26,6 +26,20 @@ public class ExpressionInspector return e.getName(); } + public static Expression Expression_cloneWithName(Expression e, String name, Tokenizer.Token token) + { + Expression copy = null; + try + { + copy = e.clone(); + } + catch (CloneNotSupportedException ex) + { + throw new ExpressionException(e, token, "Problems in allocating global function "+name); + } + return copy.withName(name); + } + private static TreeSet scarpetFunctions; private static TreeSet APIFunctions; static diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 5bffe563..6875cf35 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -2,6 +2,7 @@ package carpet.script; import carpet.CarpetServer; import carpet.script.bundled.ModuleInterface; +import carpet.script.value.FunctionValue; import carpet.settings.CarpetSettings; import carpet.script.exception.ExpressionException; import carpet.script.exception.InvalidCallbackException; @@ -20,7 +21,6 @@ import net.minecraft.util.math.BlockPos; import java.math.BigInteger; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -31,7 +31,7 @@ import static java.lang.Math.max; public class ScriptHost { private final Map userHosts = new Object2ObjectOpenHashMap<>(); - public Map globalFunctions = new Object2ObjectOpenHashMap<>(); + public Map globalFunctions = new Object2ObjectOpenHashMap<>(); public Map globalVariables = new Object2ObjectOpenHashMap<>(); private Tag globalState; @@ -118,8 +118,8 @@ public class ScriptHost { if (CarpetServer.scriptServer.stopAll) return "SCARPET PAUSED"; - UserDefinedFunction acf = globalFunctions.get(call); - if (acf == null) + FunctionValue function = globalFunctions.get(call); + if (function == null) return "UNDEFINED"; List argv = new ArrayList<>(); if (coords != null) @@ -185,7 +185,7 @@ public class ScriptHost return "Fail: "+tok.surface+" is not allowed in invoke"; } } - List args = acf.getArguments(); + List args = function.getArguments(); if (argv.size() != args.size()) { String error = "Fail: stored function "+call+" takes "+args.size()+" arguments, not "+argv.size()+ ":\n"; @@ -200,7 +200,7 @@ public class ScriptHost // TODO: this is just for now - invoke would be able to invoke other hosts scripts Context context = new CarpetContext(this, source, BlockPos.ORIGIN); return Expression.evalValue( - () -> acf.lazyEval(context, Context.VOID, acf.expression, acf.token, argv), + () -> function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv), context, Context.VOID ).getString(); @@ -211,13 +211,11 @@ public class ScriptHost } } - public Value callUDF(BlockPos pos, ServerCommandSource source, UserDefinedFunction acf, List argv) throws InvalidCallbackException + public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List argv) throws InvalidCallbackException { if (CarpetServer.scriptServer.stopAll) return Value.NULL; - - List args = acf.getArguments(); - if (argv.size() != args.size()) + if (argv.size() != fun.getArguments().size()) { throw new InvalidCallbackException(); } @@ -226,7 +224,7 @@ public class ScriptHost // TODO: this is just for now - invoke would be able to invoke other hosts scripts Context context = new CarpetContext(this, source, pos); return Expression.evalValue( - () -> acf.lazyEval(context, Context.VOID, acf.expression, acf.token, argv), + () -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv), context, Context.VOID); } diff --git a/src/main/java/carpet/script/UserDefinedFunction.java b/src/main/java/carpet/script/UserDefinedFunction.java deleted file mode 100644 index c2780903..00000000 --- a/src/main/java/carpet/script/UserDefinedFunction.java +++ /dev/null @@ -1,29 +0,0 @@ -package carpet.script; - -import java.util.List; - -public abstract class UserDefinedFunction extends Fluff.AbstractLazyFunction implements Fluff.ILazyFunction -{ - protected List arguments; - protected Expression expression; - protected Tokenizer.Token token; - UserDefinedFunction(List args, Expression expr, Tokenizer.Token t) - { - super(args.size()); - arguments = args; - expression = expr; - token = t; - } - public List getArguments() - { - return arguments; - } - public Expression getExpression() - { - return expression; - } - public Tokenizer.Token getToken() - { - return token; - } -} diff --git a/src/main/java/carpet/script/exception/ExitStatement.java b/src/main/java/carpet/script/exception/ExitStatement.java new file mode 100644 index 00000000..9b772923 --- /dev/null +++ b/src/main/java/carpet/script/exception/ExitStatement.java @@ -0,0 +1,13 @@ +package carpet.script.exception; + +import carpet.script.value.Value; + +/* Exception thrown to terminate execution mid expression (aka return statement) */ +public class ExitStatement extends RuntimeException +{ + public Value retval; + public ExitStatement(Value value) + { + retval = value; + } +} diff --git a/src/main/java/carpet/script/exception/ReturnStatement.java b/src/main/java/carpet/script/exception/ReturnStatement.java new file mode 100644 index 00000000..b2124d56 --- /dev/null +++ b/src/main/java/carpet/script/exception/ReturnStatement.java @@ -0,0 +1,12 @@ +package carpet.script.exception; + +import carpet.script.value.Value; + +public class ReturnStatement extends ExitStatement +{ + + public ReturnStatement(Value value) + { + super(value); + } +} diff --git a/src/main/java/carpet/script/exception/ThrowStatement.java b/src/main/java/carpet/script/exception/ThrowStatement.java new file mode 100644 index 00000000..46c3fe2b --- /dev/null +++ b/src/main/java/carpet/script/exception/ThrowStatement.java @@ -0,0 +1,11 @@ +package carpet.script.exception; + +import carpet.script.value.Value; + +public class ThrowStatement extends ExitStatement +{ + public ThrowStatement(Value value) + { + super(value); + } +} diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 8ec116ca..8537b1f7 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -775,14 +775,14 @@ public class EntityValue extends Value // "nbt" <-big one, for now use run('data merge entity ... }}; - public void setEvent(CarpetContext cc, String event, String fun, List args) + public void setEvent(CarpetContext cc, String event, FunctionValue fun, List args) { if (!(events.containsKey(event))) throw new InternalExpressionException("Unknown entity event: " + event); events.get(event).accept(cc, entity, fun, args); } - private static Map>> events = new HashMap>>() {{ + private static Map>> events = new HashMap>>() {{ put("on_death", (c, e, f, l) -> ((EntityInterface)e).setDeathCallback(c, f, l)); put("on_removed", (c, e, f, l) -> ((EntityInterface)e).setRemovedCallback(c, f, l)); put("on_tick", (c, e, f, l) -> ((EntityInterface)e).setTickCallback(c, f, l)); diff --git a/src/main/java/carpet/script/value/FunctionSignatureValue.java b/src/main/java/carpet/script/value/FunctionSignatureValue.java index 49903a00..c5a4aa8b 100644 --- a/src/main/java/carpet/script/value/FunctionSignatureValue.java +++ b/src/main/java/carpet/script/value/FunctionSignatureValue.java @@ -1,7 +1,5 @@ package carpet.script.value; -import carpet.script.exception.ExpressionException; - import java.util.List; public class FunctionSignatureValue extends FrameworkValue @@ -10,7 +8,6 @@ public class FunctionSignatureValue extends FrameworkValue private List arguments; private List globals; - public FunctionSignatureValue(String name, List args, List globals) { this.identifier = name; diff --git a/src/main/java/carpet/script/value/FunctionValue.java b/src/main/java/carpet/script/value/FunctionValue.java new file mode 100644 index 00000000..492c2728 --- /dev/null +++ b/src/main/java/carpet/script/value/FunctionValue.java @@ -0,0 +1,204 @@ +package carpet.script.value; + +import carpet.script.Context; +import carpet.script.Expression; +import carpet.script.ExpressionInspector; +import carpet.script.Fluff; +import carpet.script.LazyValue; +import carpet.script.Tokenizer; +import carpet.script.exception.ExitStatement; +import carpet.script.exception.ExpressionException; +import carpet.script.exception.InternalExpressionException; +import carpet.script.exception.ReturnStatement; +import carpet.script.exception.ThrowStatement; + +import java.util.List; +import java.util.Map; + +public class FunctionValue extends Value implements Fluff.ILazyFunction +{ + private Expression expression; + private Tokenizer.Token token; + private String name; + private LazyValue body; + private Map outerState; + private List args; + private static long variantCounter = 1; + private long variant; + + public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args) + { + this.expression = expression; + this.token = token; + this.name = name; + this.body = body; + this.args = args; + this.outerState = null; + variant = 0L; + } + + public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args, Map outerState) + { + this.expression = expression; + this.token = token; + this.name = name; + this.body = body; + this.args = args; + this.outerState = outerState; + variant = variantCounter++; + } + + @Override + public String getString() + { + return name; + } + + @Override + public boolean getBoolean() + { + return true; + } + + @Override + protected Value clone() + { + FunctionValue ret = new FunctionValue(expression, token, name, body, args); + ret.outerState = this.outerState; + ret.variant = this.variant; + return ret; + } + + @Override + public int hashCode() + { + return name.hashCode()+(int)variant; + } + + @Override + public boolean equals(Object o) + { + if (o instanceof FunctionValue) + return name.equals(((FunctionValue) o).name) && variant == ((FunctionValue) o).variant; + return false; + } + + @Override + public int compareTo(final Value o) + { + if (o instanceof FunctionValue) + { + int nameSame = this.name.compareTo(((FunctionValue) o).name); + if (nameSame != 0) + return nameSame; + return (int) (variant-((FunctionValue) o).variant); + } + return getString().compareTo(o.getString()); + } + + @Override + public double readNumber() + { + throw new InternalExpressionException("Function value has no numeric value"); + } + + @Override + public Value slice(long from, long to) + { + throw new InternalExpressionException("Cannot slice a function"); + } + + @Override + public int getNumParams() + { + return args.size(); + } + + @Override + public boolean numParamsVaries() + { + return false; + } + + public LazyValue callInContext(Expression callingExpression, Context c, Integer type, Expression e, Tokenizer.Token t, List lazyParams) + { + // this hole thing might not be really needed + Expression callContext = ExpressionInspector.Expression_cloneWithName(callingExpression, name, t); + try + { + return lazyEval(c, type, e, t, lazyParams); + } + catch (InternalExpressionException exc) + { + throw new ExpressionException(callContext, t, exc.getMessage()); + } + catch (ArithmeticException exc) + { + throw new ExpressionException(callContext, t, "Your math is wrong, "+exc.getMessage()); + } + } + + @Override + public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List lazyParams) + { + if (args.size() != lazyParams.size()) // something that might be subject to change in the future + // in case we add variable arguments + { + throw new ExpressionException(e, t, + "Incorrect number of arguments for function "+name+ + ". Should be "+args.size()+", not "+lazyParams.size()+" like "+args + ); + } + Context newFrame = c.recreate(); + + if (outerState != null) outerState.forEach(newFrame::setVariable); + for (int i=0; i val); + } + Value retVal; + boolean rethrow = false; + try + { + retVal = body.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye + } + catch (ReturnStatement returnStatement) + { + retVal = returnStatement.retval; + } + catch (ThrowStatement throwStatement) // might not be really necessary + { + retVal = throwStatement.retval; + rethrow = true; + } + catch (ArithmeticException | ExitStatement | ExpressionException exc) + { + throw exc; // rethrow so could be contextualized if needed + } + catch (Exception exc) + { + throw new ExpressionException(e, t, "Error while evaluating expression: "+exc.getMessage()); + } + if (rethrow) + { + throw new ThrowStatement(retVal); + } + Value otherRetVal = retVal; + return (cc, tt) -> otherRetVal; + } + + public Expression getExpression() + { + return expression; + } + public Tokenizer.Token getToken() + { + return token; + } + public List getArguments() + { + return args; + } +} diff --git a/src/main/java/carpet/settings/SettingsManager.java b/src/main/java/carpet/settings/SettingsManager.java index 96467342..cb21506e 100644 --- a/src/main/java/carpet/settings/SettingsManager.java +++ b/src/main/java/carpet/settings/SettingsManager.java @@ -3,6 +3,7 @@ package carpet.settings; import carpet.CarpetServer; import carpet.utils.Messenger; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -264,8 +265,10 @@ public class SettingsManager String lcSearch = search.toLowerCase(Locale.ROOT); return rules.values().stream().filter(rule -> { - if (rule.name.toLowerCase(Locale.ROOT).contains(lcSearch)) return true; - for (String c : rule.categories) if (c.toLowerCase(Locale.ROOT).equals(search)) return true; + if (rule.name.toLowerCase(Locale.ROOT).contains(lcSearch)) return true; // substring match, case insensitive + for (String c : rule.categories) if (c.equals(search)) return true; // category exactly, case sensitive + if (Sets.newHashSet(rule.description.toLowerCase(Locale.ROOT).split("\\W+")).contains(lcSearch)) + return true; // contains full term in description, but case insensitive return false; }).collect(ImmutableList.toImmutableList()); }