refactored and reworked functions in scarpet

This commit is contained in:
gnembon committed 2019-11-18 00:56:50 -04:00
1 parent 3feb7bffbb
commit 95b85928f9
17 files changed
+360 -218

No files matched your search

@@ -2,7 +2,7 @@ package carpet.fakes;
import carpet.script.CarpetContext; import carpet.script.CarpetContext;
import carpet.script.CarpetEventServer; import carpet.script.CarpetEventServer;
import carpet.script.ScriptHost; import carpet.script.value.FunctionValue;
import carpet.script.value.Value; import carpet.script.value.Value;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
@@ -13,10 +13,10 @@ public interface EntityInterface
float getMainYaw(float partialTicks); float getMainYaw(float partialTicks);
//scarpet part //scarpet part
void onDeathCallback(String reason); void onDeathCallback(String reason);
void setDeathCallback(CarpetContext cc, String function, List<Value> extraArgs); void setDeathCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs);
void onDamageCallback(float amount, DamageSource source); void onDamageCallback(float amount, DamageSource source);
void setDamageCallback(CarpetContext cc, String function, List<Value> extraArgs); void setDamageCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs);
void setRemovedCallback(CarpetContext cc, String function, List<Value> extraArgs); void setRemovedCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs);
void setTickCallback(CarpetContext cc, String function, List<Value> extraArgs); void setTickCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs);
CarpetEventServer.ScheduledCall getTickCallback(); CarpetEventServer.ScheduledCall getTickCallback();
} }
@@ -4,12 +4,10 @@ import carpet.CarpetServer;
import carpet.fakes.EntityInterface; import carpet.fakes.EntityInterface;
import carpet.script.CarpetContext; import carpet.script.CarpetContext;
import carpet.script.CarpetEventServer; import carpet.script.CarpetEventServer;
import carpet.script.ScriptHost; import carpet.script.value.FunctionValue;
import carpet.script.value.Value; import carpet.script.value.Value;
import carpet.settings.CarpetSettings;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
import org.lwjgl.system.CallbackI;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; 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 tickCall;
private CarpetEventServer.ScheduledCall damageCall; private CarpetEventServer.ScheduledCall damageCall;
@Override @Override
public void setDeathCallback(CarpetContext cc, String function, List<Value> extraArgs) public void setDeathCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
deathCall = CarpetServer.scriptServer.events.makeDeathCall(cc, function, extraArgs); deathCall = CarpetServer.scriptServer.events.makeDeathCall(cc, function, extraArgs);
} }
@Override @Override
public void setRemovedCallback(CarpetContext cc, String function, List<Value> extraArgs) public void setRemovedCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
removeCall = CarpetServer.scriptServer.events.makeRemovedCall(cc, function, extraArgs); removeCall = CarpetServer.scriptServer.events.makeRemovedCall(cc, function, extraArgs);
} }
@Override @Override
public void setTickCallback(CarpetContext cc, String function, List<Value> extraArgs) public void setTickCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
tickCall = CarpetServer.scriptServer.events.makeTickCall(cc, function, extraArgs); tickCall = CarpetServer.scriptServer.events.makeTickCall(cc, function, extraArgs);
} }
@@ -55,7 +50,7 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface
} }
@Override @Override
public void setDamageCallback(CarpetContext cc, String function, List<Value> extraArgs) public void setDamageCallback(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
damageCall = CarpetServer.scriptServer.events.makeDamageCall(cc, function, extraArgs); damageCall = CarpetServer.scriptServer.events.makeDamageCall(cc, function, extraArgs);
} }
@@ -3,6 +3,7 @@ package carpet.script;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.script.value.BlockValue; import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue; import carpet.script.value.EntityValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.ListValue; import carpet.script.value.ListValue;
import carpet.script.value.NumericValue; import carpet.script.value.NumericValue;
import carpet.script.value.StringValue; 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.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.dimension.DimensionType;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -39,9 +41,9 @@ public class CarpetEventServer
public static class Callback public static class Callback
{ {
public String host; 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.host = host;
this.udf = udf; this.udf = udf;
@@ -50,7 +52,7 @@ public class CarpetEventServer
@Override @Override
public String toString() 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 BlockPos context_origin;
public long dueTime; public long dueTime;
public ScheduledCall(CarpetContext context, String udf, List<LazyValue> args, long dueTime) public ScheduledCall(CarpetContext context, FunctionValue udf, List<LazyValue> args, long dueTime)
{ {
super(context.host.getName(), udf); super(context.host.getName(), udf);
this.args = args; this.args = args;
@@ -119,7 +121,7 @@ public class CarpetEventServer
// impossible call to add // impossible call to add
return false; return false;
} }
UserDefinedFunction udf = host.globalFunctions.get(funName); FunctionValue udf = host.globalFunctions.get(funName);
if (udf == null || udf.getArguments().size() != reqArgs) if (udf == null || udf.getArguments().size() != reqArgs)
{ {
// call won't match arguments // call won't match arguments
@@ -127,13 +129,13 @@ public class CarpetEventServer
} }
//all clear //all clear
//remove duplicates //remove duplicates
removeEventCall(hostName, funName); removeEventCall(hostName, udf.getString());
callList.add(new Callback(hostName, funName)); callList.add(new Callback(hostName, udf));
return true; 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) public void removeAllCalls(String hostName)
@@ -445,7 +447,7 @@ public class CarpetEventServer
} }
} }
public void scheduleCall(CarpetContext context, String function, List<LazyValue> args, long due) public void scheduleCall(CarpetContext context, FunctionValue function, List<LazyValue> args, long due)
{ {
scheduledCalls.add(new ScheduledCall(context, function, args, due)); scheduledCalls.add(new ScheduledCall(context, function, args, due));
} }
@@ -464,8 +466,8 @@ public class CarpetEventServer
if (!Event.byName.containsKey(event)) if (!Event.byName.containsKey(event))
return false; return false;
Callback callback= decodeCallback(funName); Pair<String,String> call = decodeCallback(funName);
Event.byName.get(event).handler.removeEventCall(callback.host, callback.udf); Event.byName.get(event).handler.removeEventCall(call.getLeft(), call.getRight());
return true; return true;
} }
@@ -474,21 +476,20 @@ public class CarpetEventServer
Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName)); Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName));
} }
private Callback decodeCallback(String funName) private Pair<String,String> decodeCallback(String funName)
{ {
Pattern find = Pattern.compile("(\\w+)\\(from (\\w+)\\)"); Pattern find = Pattern.compile("(\\w+)\\(from (\\w+)\\)");
Matcher matcher = find.matcher(funName); Matcher matcher = find.matcher(funName);
if(matcher.matches()) 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<Value> extraArgs, int argCount) private ScheduledCall makeEventCall(CarpetContext cc, FunctionValue function, List<Value> extraArgs, int argCount)
{ {
UserDefinedFunction udf = cc.host.globalFunctions.get(function); if (function == null || (function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argCount)
if (udf == null || (udf.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argCount)
{ {
// call won't match arguments // call won't match arguments
return null; return null;
@@ -503,19 +504,19 @@ public class CarpetEventServer
return new ScheduledCall(cc, function, lazyArgs, 0); return new ScheduledCall(cc, function, lazyArgs, 0);
} }
public ScheduledCall makeDeathCall(CarpetContext cc, String function, List<Value> extraArgs) public ScheduledCall makeDeathCall(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
return makeEventCall(cc, function, extraArgs, 2); return makeEventCall(cc, function, extraArgs, 2);
} }
public ScheduledCall makeRemovedCall(CarpetContext cc, String function, List<Value> extraArgs) public ScheduledCall makeRemovedCall(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
return makeEventCall(cc, function, extraArgs, 1); return makeEventCall(cc, function, extraArgs, 1);
} }
public ScheduledCall makeTickCall(CarpetContext cc, String function, List<Value> extraArgs) public ScheduledCall makeTickCall(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
return makeEventCall(cc, function, extraArgs, 1); return makeEventCall(cc, function, extraArgs, 1);
} }
public ScheduledCall makeDamageCall(CarpetContext cc, String function, List<Value> extraArgs) public ScheduledCall makeDamageCall(CarpetContext cc, FunctionValue function, List<Value> extraArgs)
{ {
return makeEventCall(cc, function, extraArgs, 4); return makeEventCall(cc, function, extraArgs, 4);
} }
@@ -5,10 +5,12 @@ import carpet.fakes.MinecraftServerInterface;
import carpet.helpers.FeatureGenerator; import carpet.helpers.FeatureGenerator;
import carpet.script.Fluff.TriFunction; import carpet.script.Fluff.TriFunction;
import carpet.script.exception.CarpetExpressionException; import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.ExpressionException; import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue; import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue; import carpet.script.value.EntityValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.LazyListValue; import carpet.script.value.LazyListValue;
import carpet.script.value.ListValue; import carpet.script.value.ListValue;
import carpet.script.value.NBTSerializableValue; import carpet.script.value.NBTSerializableValue;
@@ -2012,9 +2014,16 @@ public class CarpetExpression
throw new InternalExpressionException("First argument to entity_event should be an entity"); throw new InternalExpressionException("First argument to entity_event should be an entity");
String what = lv.get(1).evalValue(c).getString(); String what = lv.get(1).evalValue(c).getString();
Value functionValue = lv.get(2).evalValue(c); Value functionValue = lv.get(2).evalValue(c);
String function = null; if (functionValue instanceof NullValue)
if (!(functionValue instanceof NullValue)) functionValue = null;
function = functionValue.getString(); 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<Value> args = null; List<Value> args = null;
if (lv.size()==4) if (lv.size()==4)
args = Collections.singletonList(lv.get(3).evalValue(c)); args = Collections.singletonList(lv.get(3).evalValue(c));
@@ -2928,7 +2937,7 @@ public class CarpetExpression
CarpetServer.scriptServer.tickStart = System.nanoTime(); // for the next tick CarpetServer.scriptServer.tickStart = System.nanoTime(); // for the next tick
Thread.yield(); Thread.yield();
if(CarpetServer.scriptServer.stopAll) if(CarpetServer.scriptServer.stopAll)
throw new Expression.ExitStatement(Value.NULL); throw new ExitStatement(Value.NULL);
return (cc, tt) -> Value.TRUE; return (cc, tt) -> Value.TRUE;
}); });
@@ -3002,20 +3011,28 @@ public class CarpetExpression
if (lv.size()<2) if (lv.size()<2)
throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name"); throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name");
Long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong(); 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; CarpetContext cc = (CarpetContext)c;
if (!cc.host.globalFunctions.containsKey(funname))
throw new InternalExpressionException("Function "+funname+" is not defined");
List<LazyValue> args = new ArrayList<>(); List<LazyValue> args = new ArrayList<>();
for (int i=2; i < lv.size(); i++) for (int i=2; i < lv.size(); i++)
{ {
Value arg = lv.get(i).evalValue(cc); Value arg = lv.get(i).evalValue(cc);
args.add( (_c, _t) -> arg); args.add( (_c, _t) -> arg);
} }
if (cc.host.globalFunctions.get(funname).getArguments().size() != args.size()) if (function.getArguments().size() != args.size())
throw new InternalExpressionException("Function "+funname+" takes "+ throw new InternalExpressionException("Function "+function.getString()+" takes "+
cc.host.globalFunctions.get(funname).getArguments().size()+" arguments, "+args.size()+" provided."); function.getArguments().size()+" arguments, "+args.size()+" provided.");
CarpetServer.scriptServer.events.scheduleCall(cc, funname, args, delay); CarpetServer.scriptServer.events.scheduleCall(cc, function, args, delay);
return (c_, t_) -> Value.TRUE; return (c_, t_) -> Value.TRUE;
}); });
@@ -1,6 +1,7 @@
package carpet.script; package carpet.script;
import carpet.script.bundled.BundledModule; import carpet.script.bundled.BundledModule;
import carpet.script.value.FunctionValue;
import carpet.script.value.MapValue; import carpet.script.value.MapValue;
import carpet.script.value.StringValue; import carpet.script.value.StringValue;
import carpet.settings.CarpetSettings; import carpet.settings.CarpetSettings;
@@ -365,19 +366,19 @@ public class CarpetScriptServer
return true; return true;
} }
public boolean runas(ServerCommandSource source, String hostname, String udf_name, List<LazyValue> argv) public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List<LazyValue> 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<LazyValue> argv) public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List<LazyValue> argv)
{ {
ScriptHost host = globalHost; ScriptHost host = globalHost;
try try
{ {
if (hostname != null) if (hostname != null)
host = modules.get(hostname).retrieveForExecution(source); 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) catch (NullPointerException | InvalidCallbackException npe)
{ {
+1 -1
View File
@@ -37,7 +37,7 @@ public class Context
return host.globalVariables.get(name); return host.globalVariables.get(name);
} }
void setVariable(String name, LazyValue lv) public void setVariable(String name, LazyValue lv)
{ {
if (name.startsWith("global_")) if (name.startsWith("global_"))
{ {
+22 -117
View File
@@ -11,11 +11,15 @@ import carpet.script.Fluff.QuadFunction;
import carpet.script.Fluff.QuinnFunction; import carpet.script.Fluff.QuinnFunction;
import carpet.script.Fluff.SexFunction; import carpet.script.Fluff.SexFunction;
import carpet.script.Fluff.TriFunction; import carpet.script.Fluff.TriFunction;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.ExpressionException; import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ReturnStatement;
import carpet.script.exception.ThrowStatement;
import carpet.script.value.AbstractListValue; import carpet.script.value.AbstractListValue;
import carpet.script.value.ContainerValueInterface; import carpet.script.value.ContainerValueInterface;
import carpet.script.value.FunctionSignatureValue; import carpet.script.value.FunctionSignatureValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.GlobalValue; import carpet.script.value.GlobalValue;
import carpet.script.value.LContainerValue; import carpet.script.value.LContainerValue;
import carpet.script.value.LazyListValue; import carpet.script.value.LazyListValue;
@@ -389,31 +393,6 @@ public class Expression implements Cloneable
return copy; 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<String> getExpressionSnippet(Tokenizer.Token token, Expression expr) static List<String> 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<String> arguments, List<String> globals, LazyValue code) private FunctionValue addContextFunction(Context context, String name, Expression expr, Tokenizer.Token token, List<String> arguments, List<String> globals, LazyValue code)
{ {
name = name.toLowerCase(Locale.ROOT); name = name.toLowerCase(Locale.ROOT);
if (functions.containsKey(name)) if (functions.containsKey(name))
throw new ExpressionException(expr, token, "Function "+name+" would mask a built-in function"); 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<String, LazyValue> contextValues = new HashMap<>(); Map<String, LazyValue> contextValues = new HashMap<>();
for (String global : globals) for (String global : globals)
{ {
@@ -708,67 +676,12 @@ public class Expression implements Cloneable
contextValues.put(global, lv); contextValues.put(global, lv);
} }
} }
if (contextValues.isEmpty()) contextValues = null;
context.host.globalFunctions.put(name, new UserDefinedFunction(arguments, function_context, token) FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues);
{ // do not store lambda definitions
@Override if (!name.equals("_")) context.host.globalFunctions.put(name, result);
public LazyValue lazyEval(Context c, Integer type, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams) return result;
{
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<arguments.size(); i++)
{
String arg = arguments.get(i);
Value val = lazyParams.get(i).evalValue(c).reboundedTo(arg);
newFrame.setVariable(arg, (cc, tt) -> 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;
}
});
} }
/** /**
@@ -944,12 +857,15 @@ public class Expression implements Cloneable
addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) -> { // adjust based on c addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) -> { // adjust based on c
if (lv.size() == 0) if (lv.size() == 0)
throw new InternalExpressionException("'call' expects at least function name to call"); 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 //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 (t != Context.SIGNATURE) // just call the function
{ {
if (!c.host.globalFunctions.containsKey(name)) Value functionValue = lv.get(0).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"); throw new InternalExpressionException("Function "+name+" is not defined yet");
} }
List<LazyValue> lvargs = new ArrayList<>(lv.size()-1); List<LazyValue> lvargs = new ArrayList<>(lv.size()-1);
@@ -957,12 +873,12 @@ public class Expression implements Cloneable
{ {
lvargs.add(lv.get(i)); lvargs.add(lv.get(i));
} }
UserDefinedFunction acf = c.host.globalFunctions.get(name); FunctionValue fun = (FunctionValue)functionValue;
Value retval = acf.lazyEval(c, t, acf.expression, acf.token, lvargs).evalValue(c); 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?) return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?)
} }
// gimme signature // gimme signature
String name = lv.get(0).evalValue(c).getString();
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
List<String> globals = new ArrayList<>(); List<String> globals = new ArrayList<>();
for (int i = 1; i < lv.size(); i++) for (int i = 1; i < lv.size(); i++)
@@ -981,8 +897,6 @@ public class Expression implements Cloneable
args.add(v.boundVariable); args.add(v.boundVariable);
} }
} }
if (name.equals("_"))
name = "__lambda_"+tok.lineno+"_"+tok.linepos;
Value retval = new FunctionSignatureValue(name, args, globals); Value retval = new FunctionSignatureValue(name, args, globals);
return (cc, tt) -> retval; return (cc, tt) -> retval;
}); });
@@ -1003,20 +917,11 @@ public class Expression implements Cloneable
addLazyBinaryOperatorWithDelegation("->", precedence.get("def->"), false, (c, type, e, t, lv1, lv2) -> addLazyBinaryOperatorWithDelegation("->", precedence.get("def->"), false, (c, type, e, t, lv1, lv2) ->
{ {
Value v1 = lv1.evalValue(c, Context.SIGNATURE); Value v1 = lv1.evalValue(c, Context.SIGNATURE);
String result; if (!(v1 instanceof FunctionSignatureValue))
if (v1 instanceof FunctionSignatureValue) throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
{
FunctionSignatureValue sign = (FunctionSignatureValue) v1; FunctionSignatureValue sign = (FunctionSignatureValue) v1;
addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getGlobals(), lv2); Value result = addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getGlobals(), lv2);
result = sign.getName(); return (cc, tt) -> result;
}
else
{
v1.assertAssignable();
c.setVariable(v1.getVariable(), lv2);
result = v1.getVariable();
}
return (cc, tt) -> new StringValue(result);
}); });
addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); }); addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
@@ -1,6 +1,6 @@
package carpet.script; package carpet.script;
import carpet.CarpetServer; import carpet.script.exception.ExpressionException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.Collections; import java.util.Collections;
@@ -26,6 +26,20 @@ public class ExpressionInspector
return e.getName(); 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<String> scarpetFunctions; private static TreeSet<String> scarpetFunctions;
private static TreeSet<String> APIFunctions; private static TreeSet<String> APIFunctions;
static static
+9 -11
View File
@@ -2,6 +2,7 @@ package carpet.script;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.script.bundled.ModuleInterface; import carpet.script.bundled.ModuleInterface;
import carpet.script.value.FunctionValue;
import carpet.settings.CarpetSettings; import carpet.settings.CarpetSettings;
import carpet.script.exception.ExpressionException; import carpet.script.exception.ExpressionException;
import carpet.script.exception.InvalidCallbackException; import carpet.script.exception.InvalidCallbackException;
@@ -20,7 +21,6 @@ import net.minecraft.util.math.BlockPos;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@@ -31,7 +31,7 @@ import static java.lang.Math.max;
public class ScriptHost public class ScriptHost
{ {
private final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>(); private final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
public Map<String, UserDefinedFunction> globalFunctions = new Object2ObjectOpenHashMap<>(); public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>(); public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>();
private Tag globalState; private Tag globalState;
@@ -118,8 +118,8 @@ public class ScriptHost
{ {
if (CarpetServer.scriptServer.stopAll) if (CarpetServer.scriptServer.stopAll)
return "SCARPET PAUSED"; return "SCARPET PAUSED";
UserDefinedFunction acf = globalFunctions.get(call); FunctionValue function = globalFunctions.get(call);
if (acf == null) if (function == null)
return "UNDEFINED"; return "UNDEFINED";
List<LazyValue> argv = new ArrayList<>(); List<LazyValue> argv = new ArrayList<>();
if (coords != null) if (coords != null)
@@ -185,7 +185,7 @@ public class ScriptHost
return "Fail: "+tok.surface+" is not allowed in invoke"; return "Fail: "+tok.surface+" is not allowed in invoke";
} }
} }
List<String> args = acf.getArguments(); List<String> args = function.getArguments();
if (argv.size() != args.size()) if (argv.size() != args.size())
{ {
String error = "Fail: stored function "+call+" takes "+args.size()+" arguments, not "+argv.size()+ ":\n"; 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 // TODO: this is just for now - invoke would be able to invoke other hosts scripts
Context context = new CarpetContext(this, source, BlockPos.ORIGIN); Context context = new CarpetContext(this, source, BlockPos.ORIGIN);
return Expression.evalValue( 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,
Context.VOID Context.VOID
).getString(); ).getString();
@@ -211,13 +211,11 @@ public class ScriptHost
} }
} }
public Value callUDF(BlockPos pos, ServerCommandSource source, UserDefinedFunction acf, List<LazyValue> argv) throws InvalidCallbackException public Value callUDF(BlockPos pos, ServerCommandSource source, FunctionValue fun, List<LazyValue> argv) throws InvalidCallbackException
{ {
if (CarpetServer.scriptServer.stopAll) if (CarpetServer.scriptServer.stopAll)
return Value.NULL; return Value.NULL;
if (argv.size() != fun.getArguments().size())
List<String> args = acf.getArguments();
if (argv.size() != args.size())
{ {
throw new InvalidCallbackException(); 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 // TODO: this is just for now - invoke would be able to invoke other hosts scripts
Context context = new CarpetContext(this, source, pos); Context context = new CarpetContext(this, source, pos);
return Expression.evalValue( 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,
Context.VOID); Context.VOID);
} }
@@ -1,29 +0,0 @@
package carpet.script;
import java.util.List;
public abstract class UserDefinedFunction extends Fluff.AbstractLazyFunction implements Fluff.ILazyFunction
{
protected List<String> arguments;
protected Expression expression;
protected Tokenizer.Token token;
UserDefinedFunction(List<String> args, Expression expr, Tokenizer.Token t)
{
super(args.size());
arguments = args;
expression = expr;
token = t;
}
public List<String> getArguments()
{
return arguments;
}
public Expression getExpression()
{
return expression;
}
public Tokenizer.Token getToken()
{
return token;
}
}
@@ -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;
}
}
@@ -0,0 +1,12 @@
package carpet.script.exception;
import carpet.script.value.Value;
public class ReturnStatement extends ExitStatement
{
public ReturnStatement(Value value)
{
super(value);
}
}
@@ -0,0 +1,11 @@
package carpet.script.exception;
import carpet.script.value.Value;
public class ThrowStatement extends ExitStatement
{
public ThrowStatement(Value value)
{
super(value);
}
}
@@ -775,14 +775,14 @@ public class EntityValue extends Value
// "nbt" <-big one, for now use run('data merge entity ... // "nbt" <-big one, for now use run('data merge entity ...
}}; }};
public void setEvent(CarpetContext cc, String event, String fun, List<Value> args) public void setEvent(CarpetContext cc, String event, FunctionValue fun, List<Value> args)
{ {
if (!(events.containsKey(event))) if (!(events.containsKey(event)))
throw new InternalExpressionException("Unknown entity event: " + event); throw new InternalExpressionException("Unknown entity event: " + event);
events.get(event).accept(cc, entity, fun, args); events.get(event).accept(cc, entity, fun, args);
} }
private static Map<String, Fluff.QuadConsumer<CarpetContext, Entity, String, List<Value>>> events = new HashMap<String, Fluff.QuadConsumer<CarpetContext, Entity, String, List<Value>>>() {{ private static Map<String, Fluff.QuadConsumer<CarpetContext, Entity, FunctionValue, List<Value>>> events = new HashMap<String, Fluff.QuadConsumer<CarpetContext, Entity, FunctionValue, List<Value>>>() {{
put("on_death", (c, e, f, l) -> ((EntityInterface)e).setDeathCallback(c, f, l)); 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_removed", (c, e, f, l) -> ((EntityInterface)e).setRemovedCallback(c, f, l));
put("on_tick", (c, e, f, l) -> ((EntityInterface)e).setTickCallback(c, f, l)); put("on_tick", (c, e, f, l) -> ((EntityInterface)e).setTickCallback(c, f, l));
@@ -1,7 +1,5 @@
package carpet.script.value; package carpet.script.value;
import carpet.script.exception.ExpressionException;
import java.util.List; import java.util.List;
public class FunctionSignatureValue extends FrameworkValue public class FunctionSignatureValue extends FrameworkValue
@@ -10,7 +8,6 @@ public class FunctionSignatureValue extends FrameworkValue
private List<String> arguments; private List<String> arguments;
private List<String> globals; private List<String> globals;
public FunctionSignatureValue(String name, List<String> args, List<String> globals) public FunctionSignatureValue(String name, List<String> args, List<String> globals)
{ {
this.identifier = name; this.identifier = name;
@@ -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<String, LazyValue> outerState;
private List<String> args;
private static long variantCounter = 1;
private long variant;
public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List<String> 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<String> args, Map<String, LazyValue> 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<LazyValue> 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<LazyValue> 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<args.size(); i++)
{
String arg = args.get(i);
Value val = lazyParams.get(i).evalValue(c).reboundedTo(arg);
newFrame.setVariable(arg, (cc, tt) -> 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<String> getArguments()
{
return args;
}
}
@@ -3,6 +3,7 @@ package carpet.settings;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.utils.Messenger; import carpet.utils.Messenger;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@@ -264,8 +265,10 @@ public class SettingsManager
String lcSearch = search.toLowerCase(Locale.ROOT); String lcSearch = search.toLowerCase(Locale.ROOT);
return rules.values().stream().filter(rule -> return rules.values().stream().filter(rule ->
{ {
if (rule.name.toLowerCase(Locale.ROOT).contains(lcSearch)) return true; if (rule.name.toLowerCase(Locale.ROOT).contains(lcSearch)) return true; // substring match, case insensitive
for (String c : rule.categories) if (c.toLowerCase(Locale.ROOT).equals(search)) return true; 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; return false;
}).collect(ImmutableList.toImmutableList()); }).collect(ImmutableList.toImmutableList());
} }