Merge branch 'master' into 1.15
# Conflicts: # src/main/java/carpet/commands/ScriptCommand.java
This commit is contained in:
commit
01273548b2
17 files changed
+490
-329
No files matched your search
@@ -1,5 +1,6 @@
|
||||
package carpet.script;
|
||||
|
||||
import carpet.script.value.Value;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
@@ -15,9 +16,17 @@ public class CarpetContext extends Context
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context recreate()
|
||||
public Context duplicate()
|
||||
{
|
||||
return new CarpetContext(this.host, this.s, this.origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize()
|
||||
{
|
||||
super.initialize();
|
||||
variables.put("_x", (c, t) -> Value.ZERO);
|
||||
variables.put("_y", (c, t) -> Value.ZERO);
|
||||
variables.put("_z", (c, t) -> Value.ZERO);
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public class CarpetEventServer
|
||||
// impossible call to add
|
||||
return false;
|
||||
}
|
||||
FunctionValue udf = host.globalFunctions.get(funName);
|
||||
FunctionValue udf = host.getFunction(funName);
|
||||
if (udf == null || udf.getArguments().size() != reqArgs)
|
||||
{
|
||||
// call won't match arguments
|
||||
|
||||
@@ -8,6 +8,7 @@ import carpet.helpers.FeatureGenerator;
|
||||
import carpet.mixins.ChunkTicketManager_scarpetMixin;
|
||||
import carpet.mixins.ServerChunkManager_scarpetMixin;
|
||||
import carpet.script.Fluff.TriFunction;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.CarpetExpressionException;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.ExpressionException;
|
||||
@@ -2137,9 +2138,7 @@ public class CarpetExpression
|
||||
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 = c.host.getAssertFunction(this.expr.module, name);
|
||||
}
|
||||
FunctionValue function = (FunctionValue)functionValue;
|
||||
List<Value> args = null;
|
||||
@@ -3196,9 +3195,7 @@ public class CarpetExpression
|
||||
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 = c.host.getAssertFunction(this.expr.module, name);
|
||||
}
|
||||
FunctionValue function = (FunctionValue)functionValue;
|
||||
|
||||
@@ -3300,11 +3297,13 @@ public class CarpetExpression
|
||||
* @param source source
|
||||
* @param origin origin
|
||||
*/
|
||||
public CarpetExpression(String expression, ServerCommandSource source, BlockPos origin)
|
||||
public CarpetExpression(Module module, String expression, ServerCommandSource source, BlockPos origin)
|
||||
{
|
||||
this.origin = origin;
|
||||
this.source = source;
|
||||
this.expr = new Expression(expression);
|
||||
this.expr.asAModule(module);
|
||||
|
||||
|
||||
API_BlockManipulation();
|
||||
API_EntityManipulation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package carpet.script;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.script.bundled.ModuleInterface;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.CarpetExpressionException;
|
||||
import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
@@ -36,7 +36,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
private Tag globalState;
|
||||
private int saveTimeout;
|
||||
|
||||
private CarpetScriptHost(CarpetScriptServer server, ModuleInterface code, boolean perUser, ScriptHost parent)
|
||||
private CarpetScriptHost(CarpetScriptServer server, Module code, boolean perUser, ScriptHost parent)
|
||||
{
|
||||
super(code, perUser, parent);
|
||||
this.saveTimeout = 0;
|
||||
@@ -45,12 +45,9 @@ public class CarpetScriptHost extends ScriptHost
|
||||
globalState = loadState();
|
||||
}
|
||||
|
||||
public static CarpetScriptHost create(CarpetScriptServer scriptServer, ModuleInterface module, boolean perPlayer, ServerCommandSource source)
|
||||
public static CarpetScriptHost create(CarpetScriptServer scriptServer, Module module, boolean perPlayer, ServerCommandSource source)
|
||||
{
|
||||
CarpetScriptHost host = new CarpetScriptHost(scriptServer, module, perPlayer, null );
|
||||
host.globalVariables.put("_x", (c, t) -> Value.ZERO);
|
||||
host.globalVariables.put("_y", (c, t) -> Value.ZERO);
|
||||
host.globalVariables.put("_z", (c, t) -> Value.ZERO);
|
||||
// parse code and convert to expression
|
||||
if (module != null)
|
||||
{
|
||||
@@ -63,8 +60,8 @@ public class CarpetScriptHost extends ScriptHost
|
||||
return null;
|
||||
}
|
||||
host.setChatErrorSnooper(source);
|
||||
CarpetExpression ex = new CarpetExpression(code, source, new BlockPos(0, 0, 0));
|
||||
ex.getExpr().asAModule(module);
|
||||
CarpetExpression ex = new CarpetExpression(host.main, code, source, new BlockPos(0, 0, 0));
|
||||
ex.getExpr().asATextSource();
|
||||
ex.scriptRunCommand(host, new BlockPos(source.getPosition()));
|
||||
}
|
||||
catch (CarpetExpressionException e)
|
||||
@@ -81,15 +78,15 @@ public class CarpetScriptHost extends ScriptHost
|
||||
@Override
|
||||
protected ScriptHost duplicate()
|
||||
{
|
||||
return new CarpetScriptHost(scriptServer, myCode, false, this);
|
||||
return new CarpetScriptHost(scriptServer, main, false, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUserDefinedFunction(String funName, FunctionValue function)
|
||||
public void addUserDefinedFunction(Module module, String funName, FunctionValue function)
|
||||
{
|
||||
super.addUserDefinedFunction(funName, function);
|
||||
super.addUserDefinedFunction(module, funName, function);
|
||||
// mcarpet
|
||||
if (funName.startsWith("__on_"))
|
||||
if (funName.startsWith("__on_")) // here we can make a determination if we want to only accept events from main module.
|
||||
{
|
||||
// this is nasty, we have the host and function, yet we add it via names, but hey - works for now
|
||||
String event = funName.replaceFirst("__on_","");
|
||||
@@ -99,35 +96,27 @@ public class CarpetScriptHost extends ScriptHost
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModuleInterface getModuleByName(String name)
|
||||
protected Module getModuleByName(String name)
|
||||
{
|
||||
ModuleInterface module = scriptServer.getModule(name);
|
||||
Module module = scriptServer.getModule(name);
|
||||
if (module == null || module.getCode() == null)
|
||||
throw new InternalExpressionException("Unable to locate package: "+name);
|
||||
return module;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean runModuleCode(Context c, ModuleInterface module)
|
||||
protected void runModuleCode(Context c, Module module)
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
try
|
||||
{
|
||||
CarpetExpression ex = new CarpetExpression(module.getCode(), cc.s, cc.origin);
|
||||
ex.getExpr().asAModule(module);
|
||||
ex.scriptRunCommand(this, cc.origin);
|
||||
}
|
||||
catch (CarpetExpressionException e) // hopefully that error got reported already
|
||||
{
|
||||
throw new InternalExpressionException(e.getMessage());
|
||||
}
|
||||
return true;
|
||||
CarpetExpression ex = new CarpetExpression(module, module.getCode(), cc.s, cc.origin);
|
||||
ex.getExpr().asATextSource();
|
||||
ex.scriptRunCommand(this, cc.origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delFunction(String funName)
|
||||
public void delFunction(Module module, String funName)
|
||||
{
|
||||
super.delFunction(funName);
|
||||
super.delFunction(module, funName);
|
||||
// mcarpet
|
||||
if (funName.startsWith("__on_"))
|
||||
{
|
||||
@@ -157,7 +146,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
{
|
||||
if (CarpetServer.scriptServer.stopAll)
|
||||
return "SCARPET PAUSED";
|
||||
FunctionValue function = globalFunctions.get(call);
|
||||
FunctionValue function = getFunction(call);
|
||||
if (function == null)
|
||||
return "UNDEFINED";
|
||||
List<LazyValue> argv = new ArrayList<>();
|
||||
@@ -170,9 +159,10 @@ public class CarpetScriptHost extends ScriptHost
|
||||
switch (tok.type)
|
||||
{
|
||||
case VARIABLE:
|
||||
if (globalVariables.containsKey(tok.surface.toLowerCase(Locale.ROOT)))
|
||||
LazyValue var = getGlobalVariable(tok.surface.toLowerCase(Locale.ROOT));
|
||||
if (var != null)
|
||||
{
|
||||
argv.add(globalVariables.get(tok.surface.toLowerCase(Locale.ROOT)));
|
||||
argv.add(var);
|
||||
break;
|
||||
}
|
||||
case STRINGPARAM:
|
||||
@@ -238,7 +228,7 @@ public class CarpetScriptHost extends 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(
|
||||
return function.getExpression().evalValue(
|
||||
() -> function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv),
|
||||
context,
|
||||
Context.VOID
|
||||
@@ -262,7 +252,7 @@ public class CarpetScriptHost extends 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(
|
||||
return fun.getExpression().evalValue(
|
||||
() -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv),
|
||||
context,
|
||||
Context.VOID);
|
||||
@@ -281,7 +271,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
if (this.saveTimeout > 0)
|
||||
dumpState();
|
||||
|
||||
FunctionValue closing = globalFunctions.get("__on_close");
|
||||
FunctionValue closing = getFunction("__on_close");
|
||||
if (closing != null)
|
||||
{
|
||||
try
|
||||
@@ -294,7 +284,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
}
|
||||
userHosts.forEach((key, value) ->
|
||||
{
|
||||
FunctionValue userClosing = value.globalFunctions.get("__on_close");
|
||||
FunctionValue userClosing = value.getFunction("__on_close");
|
||||
if (userClosing != null)
|
||||
{
|
||||
ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
|
||||
@@ -322,19 +312,19 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
private void dumpState()
|
||||
{
|
||||
myCode.saveData(null, globalState);
|
||||
main.saveData(null, globalState);
|
||||
}
|
||||
|
||||
private Tag loadState()
|
||||
{
|
||||
return myCode.getData(null);
|
||||
return main.getData(null);
|
||||
}
|
||||
|
||||
public Tag getGlobalState(String file)
|
||||
{
|
||||
if (getName() == null ) return null;
|
||||
if (file != null)
|
||||
return myCode.getData(file);
|
||||
return main.getData(file);
|
||||
if (parent == null)
|
||||
return globalState;
|
||||
return ((CarpetScriptHost)parent).globalState;
|
||||
@@ -346,7 +336,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
if (file!= null)
|
||||
{
|
||||
myCode.saveData(file, tag);
|
||||
main.saveData(file, tag);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -373,7 +363,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
public void setChatErrorSnooper(ServerCommandSource source)
|
||||
{
|
||||
errorSnooper = (expr, token, message) ->
|
||||
errorSnooper = (expr, /*Nullable*/ token, message) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -383,29 +373,43 @@ public class CarpetScriptHost extends ScriptHost
|
||||
{
|
||||
return null;
|
||||
}
|
||||
String[] lines = expr.getCodeString().split("\n");
|
||||
|
||||
String shebang = message;
|
||||
if (lines.length > 1)
|
||||
if (expr.module != null)
|
||||
{
|
||||
shebang += " at line "+(token.lineno+1)+", pos "+(token.linepos+1);
|
||||
shebang += " in " + expr.module.getName() + "";
|
||||
}
|
||||
else {
|
||||
shebang += " at pos "+(token.pos+1);
|
||||
}
|
||||
if (expr.getName() != null)
|
||||
else
|
||||
{
|
||||
shebang += " in "+expr.getName()+"";
|
||||
shebang += " in system chat";
|
||||
}
|
||||
Messenger.m(source, "r "+shebang);
|
||||
if (lines.length > 1 && token.lineno > 0)
|
||||
if (token != null)
|
||||
{
|
||||
Messenger.m(source, "l "+lines[token.lineno-1]);
|
||||
String[] lines = expr.getCodeString().split("\n");
|
||||
|
||||
if (lines.length > 1)
|
||||
{
|
||||
shebang += " at line " + (token.lineno + 1) + ", pos " + (token.linepos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
shebang += " at pos " + (token.pos + 1);
|
||||
}
|
||||
Messenger.m(source, "r " + shebang);
|
||||
if (lines.length > 1 && token.lineno > 0)
|
||||
{
|
||||
Messenger.m(source, "l " + lines[token.lineno - 1]);
|
||||
}
|
||||
Messenger.m(source, "l " + lines[token.lineno].substring(0, token.linepos), "r HERE>> ", "l " +
|
||||
lines[token.lineno].substring(token.linepos));
|
||||
if (lines.length > 1 && token.lineno < lines.length - 1)
|
||||
{
|
||||
Messenger.m(source, "l " + lines[token.lineno + 1]);
|
||||
}
|
||||
}
|
||||
Messenger.m(source, "l "+lines[token.lineno].substring(0, token.linepos), "r HERE>> ", "l "+
|
||||
lines[token.lineno].substring(token.linepos));
|
||||
if (lines.length > 1 && token.lineno < lines.length-1)
|
||||
else
|
||||
{
|
||||
Messenger.m(source, "l "+lines[token.lineno+1]);
|
||||
Messenger.m(source, "r " + shebang);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import carpet.script.value.StringValue;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.CarpetServer;
|
||||
import carpet.script.bundled.FileModule;
|
||||
import carpet.script.bundled.ModuleInterface;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.InvalidCallbackException;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.utils.Messenger;
|
||||
@@ -40,7 +40,7 @@ public class CarpetScriptServer
|
||||
private final Set<String> holyMoly;
|
||||
public final CarpetEventServer events;
|
||||
|
||||
private static final List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
|
||||
private static final List<Module> bundledModuleData = new ArrayList<Module>(){{
|
||||
add(new BundledModule("camera"));
|
||||
add(new BundledModule("event_test"));
|
||||
add(new BundledModule("stats_test"));
|
||||
@@ -69,7 +69,7 @@ public class CarpetScriptServer
|
||||
|
||||
}
|
||||
|
||||
public ModuleInterface getModule(String name)
|
||||
public Module getModule(String name)
|
||||
{
|
||||
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts");
|
||||
@@ -82,7 +82,7 @@ public class CarpetScriptServer
|
||||
return new FileModule(script);
|
||||
}
|
||||
}
|
||||
for (ModuleInterface moduleData : bundledModuleData)
|
||||
for (Module moduleData : bundledModuleData)
|
||||
{
|
||||
if (moduleData.getName().equalsIgnoreCase(name))
|
||||
{
|
||||
@@ -97,7 +97,7 @@ public class CarpetScriptServer
|
||||
List<String> moduleNames = new ArrayList<>();
|
||||
if (includeBuiltIns)
|
||||
{
|
||||
for (ModuleInterface mi : bundledModuleData)
|
||||
for (Module mi : bundledModuleData)
|
||||
{
|
||||
moduleNames.add(mi.getName());
|
||||
}
|
||||
@@ -129,7 +129,7 @@ public class CarpetScriptServer
|
||||
{
|
||||
//TODO add per player modules to support player actions better on a server
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
ModuleInterface module = getModule(name);
|
||||
Module module = getModule(name);
|
||||
if (module == null)
|
||||
{
|
||||
Messenger.m(source, "r Failed to add "+name+" app");
|
||||
@@ -170,13 +170,13 @@ public class CarpetScriptServer
|
||||
private boolean addConfig(ServerCommandSource source, String hostName)
|
||||
{
|
||||
CarpetScriptHost host = modules.get(hostName);
|
||||
if (host == null || !host.globalFunctions.containsKey("__config"))
|
||||
if (host == null || host.getFunction("__config") == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
Value ret = host.callUDF(BlockPos.ORIGIN, source, host.globalFunctions.get("__config"), Collections.emptyList());
|
||||
Value ret = host.callUDF(BlockPos.ORIGIN, source, host.getFunction("__config"), Collections.emptyList());
|
||||
if (!(ret instanceof MapValue)) return false;
|
||||
Map<Value, Value> config = ((MapValue) ret).getMap();
|
||||
host.setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player"));
|
||||
@@ -195,7 +195,7 @@ public class CarpetScriptServer
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!host.globalFunctions.containsKey("__command"))
|
||||
if (host.getFunction("__command") == null)
|
||||
{
|
||||
Messenger.m(source, "gi "+hostName+" app loaded.");
|
||||
return;
|
||||
@@ -217,11 +217,11 @@ public class CarpetScriptServer
|
||||
return 1;
|
||||
});
|
||||
|
||||
for (String function : host.getPublicFunctions())
|
||||
for (String function : host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
|
||||
{
|
||||
command = command.
|
||||
then(literal(function).
|
||||
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getPublicFunctions().contains(function)).
|
||||
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
|
||||
executes( (c) -> {
|
||||
String response = modules.get(hostName).retrieveForExecution(c.getSource()).
|
||||
call(c.getSource(), function,null,"");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package carpet.script;
|
||||
|
||||
import carpet.script.value.Value;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -26,38 +28,26 @@ public class Context
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
|
||||
LazyValue getVariable(String name)
|
||||
{
|
||||
if (variables.containsKey(name))
|
||||
{
|
||||
return variables.get(name);
|
||||
}
|
||||
return host.globalVariables.get(name);
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
public void setVariable(String name, LazyValue lv)
|
||||
{
|
||||
if (name.startsWith("global_"))
|
||||
{
|
||||
host.globalVariables.put(name, lv);
|
||||
return;
|
||||
}
|
||||
variables.put(name, lv);
|
||||
}
|
||||
|
||||
|
||||
boolean isAVariable(String name)
|
||||
{
|
||||
return variables.containsKey(name) || host.globalVariables.containsKey(name);
|
||||
}
|
||||
|
||||
|
||||
void delVariable(String variable)
|
||||
{
|
||||
variables.remove(variable);
|
||||
}
|
||||
|
||||
public void removeVariablesMatching(String varname)
|
||||
{
|
||||
variables.entrySet().removeIf(e -> e.getKey().startsWith(varname));
|
||||
}
|
||||
|
||||
public Context with(String variable, LazyValue lv)
|
||||
{
|
||||
variables.put(variable, lv);
|
||||
@@ -70,6 +60,21 @@ public class Context
|
||||
}
|
||||
|
||||
public Context recreate()
|
||||
{
|
||||
Context ctx = duplicate();
|
||||
ctx.initialize();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
protected void initialize()
|
||||
{
|
||||
//special variables for second order functions so we don't need to check them all the time
|
||||
variables.put("_", (c, t) -> Value.ZERO);
|
||||
variables.put("_i", (c, t) -> Value.ZERO);
|
||||
variables.put("_a", (c, t) -> Value.ZERO);
|
||||
}
|
||||
|
||||
public Context duplicate()
|
||||
{
|
||||
return new Context(this.host);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ import carpet.script.Fluff.QuadFunction;
|
||||
import carpet.script.Fluff.QuinnFunction;
|
||||
import carpet.script.Fluff.SexFunction;
|
||||
import carpet.script.Fluff.TriFunction;
|
||||
import carpet.script.bundled.ModuleInterface;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.BreakStatement;
|
||||
import carpet.script.exception.ContinueStatement;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ResolvedException;
|
||||
import carpet.script.exception.ReturnStatement;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.value.AbstractListValue;
|
||||
@@ -332,7 +333,7 @@ import static java.lang.Math.min;
|
||||
* </p>
|
||||
*/
|
||||
|
||||
public class Expression implements Cloneable
|
||||
public class Expression
|
||||
{
|
||||
private static final Map<String, Integer> precedence = new HashMap<String,Integer>() {{
|
||||
put("attribute~:", 80);
|
||||
@@ -366,21 +367,18 @@ public class Expression implements Cloneable
|
||||
private boolean allowNewlineSubstitutions = true;
|
||||
private boolean allowComments = false;
|
||||
|
||||
public ModuleInterface module = null;
|
||||
public Module module = null;
|
||||
|
||||
void asAModule(ModuleInterface mi)
|
||||
void asATextSource()
|
||||
{
|
||||
allowNewlineSubstitutions = false;
|
||||
allowComments = true;
|
||||
module = mi;
|
||||
}
|
||||
|
||||
private String name;
|
||||
String getName() {return name;}
|
||||
Expression withName(String alias)
|
||||
void asAModule(Module mi)
|
||||
{
|
||||
name = alias;
|
||||
return this;
|
||||
|
||||
module = mi;
|
||||
}
|
||||
|
||||
/** Cached AST (Abstract Syntax Tree) (root) of the expression */
|
||||
@@ -393,18 +391,6 @@ public class Expression implements Cloneable
|
||||
private final Map<String, ILazyFunction> functions = new Object2ObjectOpenHashMap<>();
|
||||
Set<String> getFunctionNames() {return functions.keySet();}
|
||||
|
||||
@Override
|
||||
protected Expression clone() throws CloneNotSupportedException
|
||||
{
|
||||
// very very shallow copy for global functions to grab the context for error msgs
|
||||
Expression copy = (Expression) super.clone();
|
||||
copy.expression = this.expression;
|
||||
copy.name = this.name;
|
||||
copy.module = this.module;
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
static List<String> getExpressionSnippet(Tokenizer.Token token, Expression expr)
|
||||
{
|
||||
String code = expr.getCodeString();
|
||||
@@ -561,7 +547,7 @@ public class Expression implements Cloneable
|
||||
return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack);
|
||||
if (exc instanceof ArithmeticException)
|
||||
return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage());
|
||||
if (exc instanceof ExpressionException)
|
||||
if (exc instanceof ResolvedException)
|
||||
return exc;
|
||||
// unexpected really - should be caught earlier and converted to InternalExpressionException
|
||||
exc.printStackTrace();
|
||||
@@ -669,29 +655,29 @@ public class Expression implements Cloneable
|
||||
}
|
||||
});
|
||||
}
|
||||
private FunctionValue 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> outers, LazyValue code)
|
||||
{
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
if (functions.containsKey(name))
|
||||
throw new ExpressionException(context, expr, token, "Function "+name+" would mask a built-in function");
|
||||
Map<String, LazyValue> contextValues = new HashMap<>();
|
||||
for (String global : globals)
|
||||
for (String outer : outers)
|
||||
{
|
||||
LazyValue lv = context.getVariable(global);
|
||||
LazyValue lv = context.getVariable(outer);
|
||||
if (lv == null)
|
||||
{
|
||||
throw new InternalExpressionException("Variable "+global+" needs to be defined in outer scope to be used as outer parameter");
|
||||
throw new InternalExpressionException("Variable "+outer+" needs to be defined in outer scope to be used as outer parameter, and cannot be global");
|
||||
}
|
||||
else
|
||||
{
|
||||
contextValues.put(global, lv);
|
||||
contextValues.put(outer, lv);
|
||||
}
|
||||
}
|
||||
if (contextValues.isEmpty()) contextValues = null;
|
||||
|
||||
FunctionValue result = new FunctionValue(expr, token, module, name, code, arguments, contextValues);
|
||||
FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues);
|
||||
// do not store lambda definitions
|
||||
if (!name.equals("_")) context.host.addUserDefinedFunction(name, result);
|
||||
if (!name.equals("_")) context.host.addUserDefinedFunction(module, name, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -869,11 +855,13 @@ public class Expression implements Cloneable
|
||||
public void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right
|
||||
{
|
||||
// artificial construct to handle user defined functions and function definitions
|
||||
addLazyFunction("import", 1, (c, t, lv) ->
|
||||
addLazyFunction("import", -1, (c, t, lv) ->
|
||||
{
|
||||
boolean success = c.host.importModule(c, lv.get(0).evalValue(c).getString());
|
||||
Value ret = new NumericValue(success);
|
||||
return (cc, tt) -> ret;
|
||||
if (lv.size() < 2) throw new InternalExpressionException("'import' needs at least a module name to import, and list of values to import");
|
||||
String moduleName = lv.get(0).evalValue(c).getString();
|
||||
c.host.importModule(c, moduleName);
|
||||
c.host.importNames(c, module, moduleName, lv.subList(1, lv.size()).stream().map((l) -> l.evalValue(c).getString()).collect(Collectors.toList()));
|
||||
return (cc, tt) -> Value.NULL;
|
||||
});
|
||||
|
||||
addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) -> { // adjust based on c
|
||||
@@ -886,9 +874,7 @@ public class Expression implements Cloneable
|
||||
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 = c.host.getAssertFunction(module, name);
|
||||
}
|
||||
List<LazyValue> lvargs = new ArrayList<>(lv.size()-1);
|
||||
for (int i=1; i< lv.size(); i++)
|
||||
@@ -1255,7 +1241,7 @@ public class Expression implements Cloneable
|
||||
{
|
||||
String lname = li.next().getVariable();
|
||||
Value vval = ri.next().reboundedTo(lname);
|
||||
c.setVariable(lname, (cc, tt) -> vval);
|
||||
setAnyVariable(c, lname, (cc, tt) -> vval);
|
||||
}
|
||||
return (cc, tt) -> Value.TRUE;
|
||||
}
|
||||
@@ -1272,7 +1258,7 @@ public class Expression implements Cloneable
|
||||
String varname = v1.getVariable();
|
||||
Value copy = v2.reboundedTo(varname);
|
||||
LazyValue boundedLHS = (cc, tt) -> copy;
|
||||
c.setVariable(varname, boundedLHS);
|
||||
setAnyVariable(c, varname, boundedLHS);
|
||||
return boundedLHS;
|
||||
});
|
||||
|
||||
@@ -1294,7 +1280,7 @@ public class Expression implements Cloneable
|
||||
Value lval = li.next();
|
||||
String lname = lval.getVariable();
|
||||
Value result = lval.add(ri.next()).bindTo(lname);
|
||||
c.setVariable(lname, (cc, tt) -> result);
|
||||
setAnyVariable(c, lname, (cc, tt) -> result);
|
||||
}
|
||||
return (cc, tt) -> Value.TRUE;
|
||||
}
|
||||
@@ -1323,7 +1309,7 @@ public class Expression implements Cloneable
|
||||
Value result = v1.add(v2).bindTo(varname);
|
||||
boundedLHS = (cc, tt) -> result;
|
||||
}
|
||||
c.setVariable(varname, boundedLHS);
|
||||
setAnyVariable(c, varname, boundedLHS);
|
||||
return boundedLHS;
|
||||
});
|
||||
|
||||
@@ -1349,8 +1335,8 @@ public class Expression implements Cloneable
|
||||
String rname = rval.getVariable();
|
||||
lval.reboundedTo(rname);
|
||||
rval.reboundedTo(lname);
|
||||
c.setVariable(lname, (cc, tt) -> rval);
|
||||
c.setVariable(rname, (cc, tt) -> lval);
|
||||
setAnyVariable(c, lname, (cc, tt) -> rval);
|
||||
setAnyVariable(c, rname, (cc, tt) -> lval);
|
||||
}
|
||||
return (cc, tt) -> Value.TRUE;
|
||||
}
|
||||
@@ -1360,8 +1346,8 @@ public class Expression implements Cloneable
|
||||
String rvalvar = v2.getVariable();
|
||||
Value lval = v2.reboundedTo(lvalvar);
|
||||
Value rval = v1.reboundedTo(rvalvar);
|
||||
c.setVariable(lvalvar, (cc, tt) -> lval);
|
||||
c.setVariable(rvalvar, (cc, tt) -> rval);
|
||||
setAnyVariable(c, lvalvar, (cc, tt) -> lval);
|
||||
setAnyVariable(c, rvalvar, (cc, tt) -> rval);
|
||||
return (cc, tt) -> lval;
|
||||
});
|
||||
|
||||
@@ -2991,9 +2977,7 @@ public class Expression implements Cloneable
|
||||
|
||||
addLazyFunction("var", 1, (c, t, lv) -> {
|
||||
String varname = lv.get(0).evalValue(c).getString();
|
||||
if (!c.isAVariable(varname))
|
||||
c.setVariable(varname, (_c, _t ) -> Value.ZERO.reboundedTo(varname));
|
||||
return c.getVariable(varname);
|
||||
return getOrSetAnyVariable(c, varname);
|
||||
});
|
||||
|
||||
addLazyFunction("undef", 1, (c, t, lv) ->
|
||||
@@ -3001,7 +2985,7 @@ public class Expression implements Cloneable
|
||||
Value remove = lv.get(0).evalValue(c);
|
||||
if (remove instanceof FunctionValue)
|
||||
{
|
||||
c.host.delFunction(remove.getString());
|
||||
c.host.delFunction(module, remove.getString());
|
||||
return (cc, tt) -> Value.NULL;
|
||||
}
|
||||
String varname = remove.getString();
|
||||
@@ -3010,33 +2994,24 @@ public class Expression implements Cloneable
|
||||
varname = varname.replaceAll("\\*+$", "");
|
||||
if (isPrefix)
|
||||
{
|
||||
for (String key: c.host.globalFunctions.keySet())
|
||||
{
|
||||
if (key.startsWith(varname)) c.host.delFunction(key);
|
||||
}
|
||||
c.host.delFunctionWithPrefix(module, varname);
|
||||
if (varname.startsWith("global_"))
|
||||
{
|
||||
for (String key : c.host.globalVariables.keySet())
|
||||
{
|
||||
if (key.startsWith(varname)) c.host.delGlobalVariable(key);
|
||||
}
|
||||
c.host.delGlobalVariableWithPrefix(module, varname);
|
||||
}
|
||||
if (!varname.startsWith("_"))
|
||||
else if (!varname.startsWith("_"))
|
||||
{
|
||||
for (String key : c.variables.keySet())
|
||||
{
|
||||
if (key.startsWith(varname)) c.delVariable(key);
|
||||
}
|
||||
c.removeVariablesMatching(varname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
c.host.delFunction(varname);
|
||||
c.host.delFunction(module, varname);
|
||||
if (varname.startsWith("global_"))
|
||||
{
|
||||
c.host.delGlobalVariable(varname);
|
||||
c.host.delGlobalVariable(module, varname);
|
||||
}
|
||||
if (!varname.startsWith("_"))
|
||||
else if (!varname.startsWith("_"))
|
||||
{
|
||||
c.delVariable(varname);
|
||||
}
|
||||
@@ -3044,25 +3019,17 @@ public class Expression implements Cloneable
|
||||
return (cc, tt) -> Value.NULL;
|
||||
});
|
||||
|
||||
|
||||
//deprecate
|
||||
addLazyFunction("vars", 1, (c, t, lv) -> {
|
||||
String prefix = lv.get(0).evalValue(c).getString();
|
||||
List<Value> values = new ArrayList<>();
|
||||
if (prefix.startsWith("global"))
|
||||
{
|
||||
for (String k: c.host.globalVariables.keySet())
|
||||
{
|
||||
if (k.startsWith(prefix))
|
||||
values.add(new StringValue(k));
|
||||
}
|
||||
c.host.globaVariableNames(module, (s) -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (String k: c.getAllVariableNames())
|
||||
{
|
||||
if (k.startsWith(prefix))
|
||||
values.add(new StringValue(k));
|
||||
}
|
||||
c.getAllVariableNames().stream().filter(s -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
|
||||
}
|
||||
Value retval = ListValue.wrap(values);
|
||||
return (cc, tt) -> retval;
|
||||
@@ -3075,9 +3042,7 @@ public class Expression implements Cloneable
|
||||
if (!(funcDesc instanceof FunctionValue))
|
||||
{
|
||||
String name = funcDesc.getString();
|
||||
funcDesc = c.host.globalFunctions.get(name);
|
||||
if (funcDesc == null)
|
||||
throw new InternalExpressionException("Function "+name+" is not defined yet");
|
||||
funcDesc = c.host.getAssertFunction(module, name);
|
||||
}
|
||||
FunctionValue fun = (FunctionValue)funcDesc;
|
||||
int extraargs = lv.size() - fun.getArguments().size();
|
||||
@@ -3127,6 +3092,33 @@ public class Expression implements Cloneable
|
||||
|
||||
}
|
||||
|
||||
private void setAnyVariable(Context c, String name, LazyValue lv)
|
||||
{
|
||||
if (name.startsWith("global_"))
|
||||
{
|
||||
c.host.setGlobalVariable(module, name, lv);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.setVariable(name, lv);
|
||||
}
|
||||
}
|
||||
|
||||
private LazyValue getOrSetAnyVariable(Context c, String name)
|
||||
{
|
||||
LazyValue var = null;
|
||||
if (!name.startsWith("global_"))
|
||||
{
|
||||
var = c.getVariable(name);
|
||||
if (var != null) return var;
|
||||
}
|
||||
var = c.host.getGlobalVariable(module, name);
|
||||
if (var != null) return var;
|
||||
var = (_c, _t ) -> Value.ZERO.reboundedTo(name);
|
||||
setAnyVariable(c, name, var);
|
||||
return var;
|
||||
}
|
||||
|
||||
static final Expression none = new Expression("null");
|
||||
/**
|
||||
* @param expression .
|
||||
@@ -3143,6 +3135,7 @@ public class Expression implements Cloneable
|
||||
BasicDataStructures();
|
||||
}
|
||||
|
||||
|
||||
private List<Tokenizer.Token> shuntingYard(Context c)
|
||||
{
|
||||
List<Tokenizer.Token> outputQueue = new ArrayList<>();
|
||||
@@ -3267,7 +3260,7 @@ public class Expression implements Cloneable
|
||||
}
|
||||
if (stack.isEmpty())
|
||||
{
|
||||
throw new ExpressionException(c, "Mismatched parentheses");
|
||||
throw new ExpressionException(c, this, "Mismatched parentheses");
|
||||
}
|
||||
stack.pop();
|
||||
if (!stack.isEmpty() && stack.peek().type == Tokenizer.Token.TokenType.FUNCTION)
|
||||
@@ -3325,7 +3318,7 @@ public class Expression implements Cloneable
|
||||
return evalValue(() -> ast, c, expectedType);
|
||||
}
|
||||
|
||||
static Value evalValue(Supplier<LazyValue> exprProvider, Context c, Integer expectedType)
|
||||
Value evalValue(Supplier<LazyValue> exprProvider, Context c, Integer expectedType)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -3333,7 +3326,7 @@ public class Expression implements Cloneable
|
||||
}
|
||||
catch (ContinueStatement | BreakStatement | ReturnStatement exc)
|
||||
{
|
||||
throw new ExpressionException(c, "Control flow functions, like continue, break, or return, should only be used in loops and functions respectively.");
|
||||
throw new ExpressionException(c, this, "Control flow functions, like continue, break, or return, should only be used in loops and functions respectively.");
|
||||
}
|
||||
catch (ExitStatement exit)
|
||||
{
|
||||
@@ -3341,15 +3334,15 @@ public class Expression implements Cloneable
|
||||
}
|
||||
catch (StackOverflowError ignored)
|
||||
{
|
||||
throw new ExpressionException(c, "Your thoughts are too deep");
|
||||
throw new ExpressionException(c, this, "Your thoughts are too deep");
|
||||
}
|
||||
catch (InternalExpressionException exc)
|
||||
{
|
||||
throw new ExpressionException(c, "Your expression result is incorrect:"+exc.getMessage());
|
||||
throw new ExpressionException(c, this, "Your expression result is incorrect:"+exc.getMessage());
|
||||
}
|
||||
catch (ArithmeticException exc)
|
||||
{
|
||||
throw new ExpressionException(c, "The final result is incorrect, "+exc.getMessage());
|
||||
throw new ExpressionException(c, this, "The final result is incorrect, "+exc.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3376,15 +3369,7 @@ public class Expression implements Cloneable
|
||||
stack.push(result);
|
||||
break;
|
||||
case VARIABLE:
|
||||
stack.push((c, t) ->
|
||||
{
|
||||
if (!c.isAVariable(token.surface)) // new variable
|
||||
{
|
||||
c.setVariable(token.surface, (cc, tt ) -> Value.ZERO.reboundedTo(token.surface));
|
||||
}
|
||||
LazyValue lazyVariable = c.getVariable(token.surface);
|
||||
return lazyVariable.evalValue(c);
|
||||
});
|
||||
stack.push((c, t) -> getOrSetAnyVariable(c, token.surface).evalValue(c));
|
||||
break;
|
||||
case FUNCTION:
|
||||
String name = token.surface.toLowerCase(Locale.ROOT);
|
||||
@@ -3507,15 +3492,15 @@ public class Expression implements Cloneable
|
||||
|
||||
if (stack.size() > 1)
|
||||
{
|
||||
throw new ExpressionException(c, "Too many unhandled function parameter lists");
|
||||
throw new ExpressionException(c, this, "Too many unhandled function parameter lists");
|
||||
}
|
||||
else if (stack.peek() > 1)
|
||||
{
|
||||
throw new ExpressionException(c, "Too many numbers or variables");
|
||||
throw new ExpressionException(c, this, "Too many numbers or variables");
|
||||
}
|
||||
else if (stack.peek() < 1)
|
||||
{
|
||||
throw new ExpressionException(c, "Empty expression");
|
||||
throw new ExpressionException(c, this, "Empty expression");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package carpet.script;
|
||||
|
||||
import carpet.script.exception.ExpressionException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -21,31 +20,17 @@ public class ExpressionInspector
|
||||
return Expression.getExpressionSnippet(token, expr);
|
||||
}
|
||||
|
||||
public static String Expression_getName(Expression e)
|
||||
public static String Expression_getModuleName(Expression e)
|
||||
{
|
||||
return e.getName();
|
||||
}
|
||||
|
||||
public static Expression Expression_cloneWithName(Expression e, Context c, String name, Tokenizer.Token token)
|
||||
{
|
||||
Expression copy;
|
||||
try
|
||||
{
|
||||
copy = e.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException ex)
|
||||
{
|
||||
throw new ExpressionException(c, e, token, "Problems in allocating global function "+name);
|
||||
}
|
||||
return copy.withName(name);
|
||||
return e.module == null?null:e.module.getName();
|
||||
}
|
||||
|
||||
private static TreeSet<String> scarpetFunctions;
|
||||
private static TreeSet<String> APIFunctions;
|
||||
static
|
||||
{
|
||||
Set<String> allFunctions = (new CarpetExpression("null", null, null)).getExpr().getFunctionNames();
|
||||
scarpetFunctions = new TreeSet<>(new Expression("null").getFunctionNames());
|
||||
Set<String> allFunctions = (new CarpetExpression(null, "null", null, null)).getExpr().getFunctionNames();
|
||||
scarpetFunctions = new TreeSet<>(Expression.none.getFunctionNames());
|
||||
APIFunctions = allFunctions.stream().filter(s -> !scarpetFunctions.contains(s)).collect(Collectors.toCollection(TreeSet::new));
|
||||
}
|
||||
|
||||
@@ -63,10 +48,8 @@ public class ExpressionInspector
|
||||
// not that useful in commandline, more so in external scripts, so skipping here
|
||||
//scarpetMatches.addAll(CarpetServer.scriptServer.events.eventHandlers.keySet().stream().
|
||||
// filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> "__"+s+"(").collect(Collectors.toList()));
|
||||
scarpetMatches.addAll(host.globalFunctions.keySet().stream().
|
||||
filter(s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList()));
|
||||
scarpetMatches.addAll(host.globalVariables.keySet().stream().
|
||||
filter(s -> s.startsWith(prefix)).collect(Collectors.toList()));
|
||||
scarpetMatches.addAll(host.globaFunctionNames(host.main, s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList()));
|
||||
scarpetMatches.addAll(host.globaVariableNames(host.main, s -> s.startsWith(prefix)).collect(Collectors.toList()));
|
||||
return scarpetMatches;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +1,292 @@
|
||||
package carpet.script;
|
||||
|
||||
import carpet.script.bundled.ModuleInterface;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.Value;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class ScriptHost
|
||||
{
|
||||
public class ModuleData
|
||||
{
|
||||
Module parent;
|
||||
public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, ModuleData> functionImports = new Object2ObjectOpenHashMap<>(); // imported functions string to module
|
||||
public Map<String, ModuleData> globalsImports = new Object2ObjectOpenHashMap<>(); // imported global variables string to module
|
||||
public Map<String, ModuleData> futureImports = new Object2ObjectOpenHashMap<>(); // imports not known before used
|
||||
|
||||
public ModuleData(Module parent, ModuleData other)
|
||||
{
|
||||
// imports are just pointers, but they still point to the wrong modules (point to the parent)
|
||||
this.parent = parent;
|
||||
globalFunctions.putAll(other.globalFunctions);
|
||||
other.globalVariables.forEach((key, value) ->
|
||||
{
|
||||
Value var = value.evalValue(null);
|
||||
Value copy = var.deepcopy();
|
||||
copy.boundVariable = var.boundVariable;
|
||||
globalVariables.put(key, (c, t) -> copy);
|
||||
});
|
||||
}
|
||||
|
||||
public void setImportsBasedOn(ScriptHost host, ModuleData other)
|
||||
{
|
||||
// fixing imports
|
||||
other.functionImports.forEach((name, targetData) -> {
|
||||
functionImports.put(name, host.moduleData.get(targetData.parent));
|
||||
});
|
||||
other.globalsImports.forEach((name, targetData) -> {
|
||||
globalsImports.put(name, host.moduleData.get(targetData.parent));
|
||||
});
|
||||
other.futureImports.forEach((name, targetData) -> {
|
||||
futureImports.put(name, host.moduleData.get(targetData.parent));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public ModuleData(Module parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
protected final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>();
|
||||
private Set<String> imports = new HashSet<>();
|
||||
private Map<Module,ModuleData> moduleData = new HashMap<>(); // marking imports
|
||||
private Map<String,Module> modules = new HashMap<>();
|
||||
|
||||
protected ScriptHost parent;
|
||||
protected boolean perUser;
|
||||
|
||||
//private String name;
|
||||
public String getName() {return myCode==null?null:myCode.getName();}
|
||||
public String getName() {return main ==null?null: main.getName();}
|
||||
|
||||
protected final ModuleInterface myCode;
|
||||
public final Module main;
|
||||
|
||||
public Fluff.TriFunction<Expression, Tokenizer.Token, String, List<String>> errorSnooper = null;
|
||||
|
||||
ScriptHost(ModuleInterface code, boolean perUser, ScriptHost parent)
|
||||
protected ScriptHost(Module code, boolean perUser, ScriptHost parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
//this.name = name;
|
||||
this.myCode = code;
|
||||
this.main = code;
|
||||
this.perUser = perUser;
|
||||
if (code != null) imports.add(code.getName());
|
||||
globalVariables.put("euler", (c, t) -> Expression.euler);
|
||||
globalVariables.put("pi", (c, t) -> Expression.PI);
|
||||
globalVariables.put("null", (c, t) -> Value.NULL);
|
||||
globalVariables.put("true", (c, t) -> Value.TRUE);
|
||||
globalVariables.put("false", (c, t) -> Value.FALSE);
|
||||
|
||||
//special variables for second order functions so we don't need to check them all the time
|
||||
globalVariables.put("_", (c, t) -> Value.ZERO);
|
||||
globalVariables.put("_i", (c, t) -> Value.ZERO);
|
||||
globalVariables.put("_a", (c, t) -> Value.ZERO);
|
||||
ModuleData moduleData = new ModuleData(code);
|
||||
initializeModuleGlobals(moduleData);
|
||||
this.moduleData.put(code, moduleData);
|
||||
this.modules.put(code==null?null:code.getName(), code);
|
||||
}
|
||||
|
||||
public void addUserDefinedFunction(String funName, FunctionValue function)
|
||||
void initializeModuleGlobals(ModuleData md)
|
||||
{
|
||||
globalFunctions.put(funName, function);
|
||||
md.globalVariables.put("euler", (c, t) -> Expression.euler);
|
||||
md.globalVariables.put("pi", (c, t) -> Expression.PI);
|
||||
md.globalVariables.put("null", (c, t) -> Value.NULL);
|
||||
md.globalVariables.put("true", (c, t) -> Value.TRUE);
|
||||
md.globalVariables.put("false", (c, t) -> Value.FALSE);
|
||||
}
|
||||
|
||||
public boolean importModule(Context c, String moduleName)
|
||||
public void importModule(Context c, String moduleName)
|
||||
{
|
||||
if (imports.contains(moduleName)) return false; // aready imported
|
||||
ModuleInterface module = getModuleByName(moduleName);
|
||||
if (imports.contains(module.getName())) return false; // aready imported, once again, in case some discrepancies
|
||||
imports.add(module.getName());
|
||||
if (runModuleCode(c, module))
|
||||
if (modules.containsKey(moduleName)) return; // aready imported
|
||||
Module module = getModuleByName(moduleName);
|
||||
if (modules.containsKey(module.getName())) return; // aready imported, once again, in case some discrepancies in names?
|
||||
modules.put(module.getName(), module);
|
||||
ModuleData data = new ModuleData(module);
|
||||
initializeModuleGlobals(data);
|
||||
moduleData.put(module, data);
|
||||
runModuleCode(c, module);
|
||||
//moduleData.remove(module); // we are pooped already, but doesn't hurt to clean that up.
|
||||
//modules.remove(module.getName());
|
||||
//throw new InternalExpressionException("Failed to import a module "+moduleName);
|
||||
}
|
||||
public void importNames(Context c, Module targetModule, String sourceModuleName, List<String> identifiers )
|
||||
{
|
||||
if (!moduleData.containsKey(targetModule))
|
||||
{
|
||||
return true;
|
||||
throw new InternalExpressionException("Cannot import to module that doesn't exist");
|
||||
}
|
||||
Module source = modules.get(sourceModuleName);
|
||||
ModuleData sourceData = moduleData.get(source);
|
||||
ModuleData targetData = moduleData.get(targetModule);
|
||||
if (sourceData == null || targetData == null)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot import from module that is not imported");
|
||||
}
|
||||
for (String identifier: identifiers)
|
||||
{
|
||||
if (sourceData.globalFunctions.containsKey(identifier))
|
||||
{
|
||||
targetData.functionImports.put(identifier, sourceData);
|
||||
}
|
||||
else if (sourceData.globalVariables.containsKey(identifier))
|
||||
{
|
||||
targetData.globalsImports.put(identifier, sourceData);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetData.functionImports.put(identifier, sourceData);
|
||||
}
|
||||
}
|
||||
imports.remove(module.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract ModuleInterface getModuleByName(String name); // this should be shell out in the executuor
|
||||
protected abstract Module getModuleByName(String name); // this should be shell out in the executor
|
||||
|
||||
protected abstract boolean runModuleCode(Context c, ModuleInterface module); // this should be shell out in the executuor
|
||||
protected abstract void runModuleCode(Context c, Module module); // this should be shell out in the executor
|
||||
|
||||
public void delFunction(String funName)
|
||||
public FunctionValue getFunction(String name) { return getFunction(main, name); }
|
||||
public FunctionValue getAssertFunction(Module module, String name)
|
||||
{
|
||||
globalFunctions.remove(funName);
|
||||
FunctionValue ret = getFunction(module, name);
|
||||
if (ret == null) throw new InternalExpressionException("Function "+name+" is not defined yet");
|
||||
return ret;
|
||||
}
|
||||
private FunctionValue getFunction(Module module, String name)
|
||||
{
|
||||
ModuleData local = moduleData.get(module);
|
||||
FunctionValue ret = local.globalFunctions.get(name); // most uses would be from local scope anyways
|
||||
if (ret != null) return ret;
|
||||
ModuleData target = local.functionImports.get(name);
|
||||
if (target != null)
|
||||
{
|
||||
ret = target.globalFunctions.get(name);
|
||||
if (ret != null) return ret;
|
||||
}
|
||||
// not in local scope - will need to travel over import links
|
||||
target = local.futureImports.get(name);
|
||||
if (target == null) return null;
|
||||
target = findModuleDataFromFunctionImports(name, target, 0);
|
||||
if (target == null) return null;
|
||||
local.futureImports.remove(name);
|
||||
local.functionImports.put(name, target);
|
||||
return target.globalFunctions.get(name);
|
||||
}
|
||||
|
||||
public void delGlobalVariable(String varName)
|
||||
private ModuleData findModuleDataFromFunctionImports(String name, ModuleData source, int ttl)
|
||||
{
|
||||
globalVariables.remove(varName);
|
||||
if (ttl > 10) throw new InternalExpressionException("Cannot import "+name+", either your imports are too deep or too loopy");
|
||||
if (source.globalFunctions.containsKey(name))
|
||||
return source;
|
||||
if (source.functionImports.containsKey(name))
|
||||
return findModuleDataFromFunctionImports(name, source.functionImports.get(name), ttl+1);
|
||||
if (source.futureImports.containsKey(name))
|
||||
return findModuleDataFromFunctionImports(name, source.futureImports.get(name), ttl+1);
|
||||
return null;
|
||||
}
|
||||
|
||||
public LazyValue getGlobalVariable(String name) { return getGlobalVariable(main, name); }
|
||||
public LazyValue getGlobalVariable(Module module, String name)
|
||||
{
|
||||
ModuleData local = moduleData.get(module);
|
||||
LazyValue ret = local.globalVariables.get(name); // most uses would be from local scope anyways
|
||||
if (ret != null) return ret;
|
||||
ModuleData target = local.globalsImports.get(name);
|
||||
if (target != null)
|
||||
{
|
||||
ret = target.globalVariables.get(name);
|
||||
if (ret != null) return ret;
|
||||
}
|
||||
// not in local scope - will need to travel over import links
|
||||
target = local.futureImports.get(name);
|
||||
if (target == null) return null;
|
||||
target = findModuleDataFromGlobalImports(name, target, 0);
|
||||
if (target == null) return null;
|
||||
local.futureImports.remove(name);
|
||||
local.globalsImports.put(name, target);
|
||||
return target.globalVariables.get(name);
|
||||
}
|
||||
|
||||
private ModuleData findModuleDataFromGlobalImports(String name, ModuleData source, int ttl)
|
||||
{
|
||||
if (ttl > 10) throw new InternalExpressionException("Cannot import "+name+", either your imports are too deep or too loopy");
|
||||
if (source.globalVariables.containsKey(name))
|
||||
return source;
|
||||
if (source.globalsImports.containsKey(name))
|
||||
return findModuleDataFromGlobalImports(name, source.globalsImports.get(name), ttl+1);
|
||||
if (source.futureImports.containsKey(name))
|
||||
return findModuleDataFromGlobalImports(name, source.futureImports.get(name), ttl+1);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void delFunctionWithPrefix(Module module, String prefix)
|
||||
{
|
||||
ModuleData data = moduleData.get(module);
|
||||
data.globalFunctions.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
data.functionImports.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
}
|
||||
public void delFunction(Module module, String funName)
|
||||
{
|
||||
ModuleData data = moduleData.get(module);
|
||||
data.globalFunctions.remove(funName);
|
||||
data.functionImports.remove(funName);
|
||||
}
|
||||
|
||||
public void delGlobalVariableWithPrefix(Module module, String prefix)
|
||||
{
|
||||
ModuleData data = moduleData.get(module);
|
||||
data.globalVariables.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
data.globalsImports.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
}
|
||||
|
||||
public void delGlobalVariable(Module module, String varName)
|
||||
{
|
||||
ModuleData data = moduleData.get(module);
|
||||
data.globalFunctions.remove(varName);
|
||||
data.functionImports.remove(varName);
|
||||
}
|
||||
|
||||
public void addUserDefinedFunction(Module module, String name, FunctionValue fun)
|
||||
{
|
||||
moduleData.get(module).globalFunctions.put(name, fun);
|
||||
}
|
||||
|
||||
public void setGlobalVariable(Module module, String name, LazyValue lv)
|
||||
{
|
||||
moduleData.get(module).globalVariables.put(name, lv);
|
||||
}
|
||||
|
||||
public Stream<String> globaVariableNames(Module module, Predicate<String> predicate)
|
||||
{
|
||||
return Stream.concat(
|
||||
moduleData.get(module).globalVariables.keySet().stream(),
|
||||
moduleData.get(module).globalsImports.keySet().stream()
|
||||
).filter(predicate);
|
||||
}
|
||||
|
||||
public Stream<String> globaFunctionNames(Module module, Predicate<String> predicate)
|
||||
{
|
||||
return Stream.concat(
|
||||
moduleData.get(module).globalFunctions.keySet().stream(),
|
||||
moduleData.get(module).functionImports.keySet().stream()
|
||||
).filter(predicate);
|
||||
}
|
||||
|
||||
public ScriptHost retrieveForExecution(String /*Nullable*/ user)
|
||||
{
|
||||
if (!perUser)
|
||||
return this;
|
||||
ScriptHost userHost = userHosts.get(user);
|
||||
if (userHost != null)
|
||||
return userHost;
|
||||
userHost = this.duplicate();
|
||||
userHost.globalVariables.putAll(this.globalVariables);
|
||||
userHost.globalFunctions.putAll(this.globalFunctions);
|
||||
userHost.imports.addAll(this.imports);
|
||||
if (!perUser) return this;
|
||||
ScriptHost oldUserHost = userHosts.get(user);
|
||||
if (oldUserHost != null) return oldUserHost;
|
||||
ScriptHost userHost = this.duplicate();
|
||||
userHost.modules.putAll(this.modules);
|
||||
for (Map.Entry<Module, ScriptHost.ModuleData> e : this.moduleData.entrySet())
|
||||
{
|
||||
userHost.moduleData.put(e.getKey(), new ModuleData(e.getKey(), e.getValue()));
|
||||
}
|
||||
// fixing imports
|
||||
userHost.moduleData.forEach((module, data) ->
|
||||
{
|
||||
data.setImportsBasedOn(this, this.moduleData.get(data.parent));
|
||||
});
|
||||
userHosts.put(user, userHost);
|
||||
return userHost;
|
||||
}
|
||||
|
||||
protected abstract ScriptHost duplicate();
|
||||
|
||||
public List<String> getPublicFunctions()
|
||||
{
|
||||
return globalFunctions.keySet().stream().filter((str) -> !str.startsWith("_")).sorted().collect(Collectors.toList());
|
||||
}
|
||||
public List<String> getAvailableFunctions(boolean all)
|
||||
{
|
||||
return globalFunctions.keySet().stream().filter((str) -> all || !str.startsWith("__")).sorted().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void onClose() { }
|
||||
|
||||
public void setPerPlayer(boolean isPerUser)
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
public class BundledModule implements ModuleInterface
|
||||
public class BundledModule extends Module
|
||||
{
|
||||
private String name;
|
||||
private String code;
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FileModule implements ModuleInterface
|
||||
public class FileModule extends Module
|
||||
{
|
||||
private String name;
|
||||
private String code;
|
||||
|
||||
+12
-6
@@ -10,13 +10,13 @@ import java.nio.file.Files;
|
||||
import static carpet.script.bundled.FileModule.read;
|
||||
import static carpet.script.bundled.FileModule.write;
|
||||
|
||||
public interface ModuleInterface
|
||||
public abstract class Module
|
||||
{
|
||||
String getName();
|
||||
String getCode();
|
||||
default boolean isInternal() {return true; }
|
||||
public abstract String getName();
|
||||
public abstract String getCode();
|
||||
public boolean isInternal() {return true; }
|
||||
|
||||
default Tag getData(String file)
|
||||
public Tag getData(String file)
|
||||
{
|
||||
File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
|
||||
@@ -24,7 +24,7 @@ public interface ModuleInterface
|
||||
return read(dataFile);
|
||||
}
|
||||
|
||||
default void saveData(String file, Tag globalState)
|
||||
public void saveData(String file, Tag globalState)
|
||||
{
|
||||
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
|
||||
@@ -41,4 +41,10 @@ public interface ModuleInterface
|
||||
write(globalState, dataFile);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return getName().hashCode();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CarpetExpressionException extends RuntimeException
|
||||
public class CarpetExpressionException extends RuntimeException implements ResolvedException
|
||||
{
|
||||
public final List<FunctionValue> stack;
|
||||
|
||||
|
||||
@@ -10,16 +10,17 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static carpet.script.ExpressionInspector.Expression_getExpressionSnippet;
|
||||
import static carpet.script.ExpressionInspector.Expression_getName;
|
||||
import static carpet.script.ExpressionInspector.Expression_getModuleName;
|
||||
|
||||
/* The expression evaluators exception class. */
|
||||
public class ExpressionException extends RuntimeException
|
||||
public class ExpressionException extends RuntimeException implements ResolvedException
|
||||
{
|
||||
public final Context context;
|
||||
public final List<FunctionValue> stack = new ArrayList<>();
|
||||
public ExpressionException(Context c, String message)
|
||||
|
||||
public ExpressionException(Context c, Expression e, String message)
|
||||
{
|
||||
super(makeMessage(c, null, null, message));
|
||||
super(makeMessage(c, e, null, message));
|
||||
context = c;
|
||||
}
|
||||
|
||||
@@ -35,11 +36,12 @@ public class ExpressionException extends RuntimeException
|
||||
context = c;
|
||||
}
|
||||
|
||||
private static final Fluff.TriFunction<Expression, Tokenizer.Token, String, List<String>> errorMaker = (expr, token, errmessage) ->
|
||||
private static final Fluff.TriFunction<Expression, Tokenizer.Token, String, List<String>> errorMaker = (expr, /*Nullable*/ token, errmessage) ->
|
||||
{
|
||||
|
||||
List<String> errMsg = new ArrayList<>();
|
||||
if (expr != null && token != null)
|
||||
errmessage += Expression_getModuleName(expr) == null?"":(" in "+Expression_getModuleName(expr));
|
||||
if (token != null)
|
||||
{
|
||||
List<String> snippet = Expression_getExpressionSnippet(token, expr);
|
||||
errMsg.addAll(snippet);
|
||||
@@ -52,11 +54,6 @@ public class ExpressionException extends RuntimeException
|
||||
{
|
||||
errmessage += " at pos " + (token.pos + 1);
|
||||
}
|
||||
if (Expression_getName(expr) != null)
|
||||
{
|
||||
errmessage += " (" + Expression_getName(expr) + ")";
|
||||
}
|
||||
|
||||
}
|
||||
errMsg.add(errmessage);
|
||||
return errMsg;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
public interface ResolvedException
|
||||
{
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import carpet.script.ExpressionInspector;
|
||||
import carpet.script.Fluff;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.Tokenizer;
|
||||
import carpet.script.bundled.ModuleInterface;
|
||||
import carpet.script.exception.BreakStatement;
|
||||
import carpet.script.exception.ContinueStatement;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
@@ -24,17 +23,15 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
private Expression expression;
|
||||
private Tokenizer.Token token;
|
||||
private String name;
|
||||
private String containedPackage;
|
||||
private LazyValue body;
|
||||
private Map<String, LazyValue> outerState;
|
||||
private List<String> args;
|
||||
private static long variantCounter = 1;
|
||||
private long variant;
|
||||
|
||||
private FunctionValue(Expression expression, Tokenizer.Token token, String cpackage, String name, LazyValue body, List<String> args)
|
||||
private FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List<String> args)
|
||||
{
|
||||
this.expression = expression;
|
||||
this.containedPackage = cpackage;
|
||||
this.token = token;
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
@@ -43,10 +40,9 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
variant = 0L;
|
||||
}
|
||||
|
||||
public FunctionValue(Expression expression, Tokenizer.Token token, ModuleInterface code, String name, LazyValue body, List<String> args, Map<String, LazyValue> outerState)
|
||||
public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List<String> args, Map<String, LazyValue> outerState)
|
||||
{
|
||||
this.expression = expression;
|
||||
this.containedPackage = code==null?null:code.getName();
|
||||
this.token = token;
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
@@ -61,7 +57,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
return name;
|
||||
}
|
||||
|
||||
public String fullName() {return containedPackage == null?name:name+"["+containedPackage+"]";}
|
||||
public String fullName() {return expression.module == null?name:name+"["+expression.module.getName()+"]";}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean()
|
||||
@@ -72,7 +68,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
@Override
|
||||
protected Value clone()
|
||||
{
|
||||
FunctionValue ret = new FunctionValue(expression, token, containedPackage, name, body, args);
|
||||
FunctionValue ret = new FunctionValue(expression, token, name, body, args);
|
||||
ret.outerState = this.outerState;
|
||||
ret.variant = this.variant;
|
||||
return ret;
|
||||
@@ -131,11 +127,9 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
|
||||
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 (errata: needed to know the package to report with exceptions
|
||||
Expression callContext = ExpressionInspector.Expression_cloneWithName(e, c, fullName(), t);
|
||||
try
|
||||
{
|
||||
return lazyEval(c, type, callContext, t, lazyParams);
|
||||
return lazyEval(c, type, e, t, lazyParams);
|
||||
}
|
||||
catch (ExpressionException exc)
|
||||
{
|
||||
@@ -145,12 +139,12 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
catch (InternalExpressionException exc)
|
||||
{
|
||||
exc.stack.add(this);
|
||||
throw new ExpressionException(c, callContext, t, exc.getMessage(), exc.stack);
|
||||
throw new ExpressionException(c, e, t, exc.getMessage(), exc.stack);
|
||||
}
|
||||
|
||||
catch (ArithmeticException exc)
|
||||
{
|
||||
throw new ExpressionException(c, callContext, t, "Your math is wrong, "+exc.getMessage(), Collections.singletonList(this));
|
||||
throw new ExpressionException(c, e, t, "Your math is wrong, "+exc.getMessage(), Collections.singletonList(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,14 +187,10 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
retVal = throwStatement.retval;
|
||||
rethrow = true;
|
||||
}
|
||||
catch (ArithmeticException | ExitStatement | ExpressionException | InternalExpressionException exc )
|
||||
{
|
||||
throw exc; // rethrow so could be contextualized if needed
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
throw new ExpressionException(c, e, t, "Error while evaluating expression: "+exc.getMessage());
|
||||
}
|
||||
//catch (ArithmeticException | ExitStatement | ExpressionException | InternalExpressionException exc )
|
||||
//{
|
||||
// throw exc; // rethrow so could be contextualized if needed
|
||||
//}
|
||||
if (rethrow)
|
||||
{
|
||||
throw new ThrowStatement(retVal);
|
||||
|
||||
Reference in new issue
Block a user