modules and imports

This commit is contained in:
gnembon committed 2020-01-08 23:38:31 -04:00
1 parent 34bf877bc7
commit 4f65be946c
17 files changed
+479 -314

No files matched your search

@@ -37,6 +37,7 @@ import java.util.Locale;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal; import static net.minecraft.server.command.CommandManager.literal;
@@ -78,7 +79,7 @@ public class ScriptCommand
executes( (cc) -> { CarpetServer.scriptServer.stopAll = false; return 1;}); executes( (cc) -> { CarpetServer.scriptServer.stopAll = false; return 1;});
LiteralArgumentBuilder<ServerCommandSource> l = literal("run"). LiteralArgumentBuilder<ServerCommandSource> l = literal("run").
requires((player) -> player.hasPermissionLevel(2)). requires((player) -> player.hasPermissionLevel(2)).
then(argument("expr", StringArgumentType.greedyString()).suggests((cc, bb) -> suggestCode(cc, bb)). then(argument("expr", StringArgumentType.greedyString()).suggests(ScriptCommand::suggestCode).
executes((cc) -> compute( executes((cc) -> compute(
cc, cc,
StringArgumentType.getString(cc, "expr")))); StringArgumentType.getString(cc, "expr"))));
@@ -141,7 +142,7 @@ public class ScriptCommand
then(argument("from", BlockPosArgumentType.blockPos()). then(argument("from", BlockPosArgumentType.blockPos()).
then(argument("to", BlockPosArgumentType.blockPos()). then(argument("to", BlockPosArgumentType.blockPos()).
then(argument("expr", StringArgumentType.greedyString()). then(argument("expr", StringArgumentType.greedyString()).
suggests((cc, bb) -> suggestCode(cc, bb)). suggests(ScriptCommand::suggestCode).
executes( (cc) -> scriptScan( executes( (cc) -> scriptScan(
cc, cc,
BlockPosArgumentType.getBlockPos(cc, "origin"), BlockPosArgumentType.getBlockPos(cc, "origin"),
@@ -288,7 +289,7 @@ public class ScriptCommand
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c) private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c)
{ {
CarpetScriptHost host = getHost(c); CarpetScriptHost host = getHost(c);
return host.getPublicFunctions(); return host.globaFunctionNames(host.myCode, s -> !s.startsWith("_")).sorted().collect(Collectors.toList());
} }
private static int listEvents(ServerCommandSource source) private static int listEvents(ServerCommandSource source)
{ {
@@ -314,9 +315,8 @@ public class ScriptCommand
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
Messenger.m(source, "lb Stored functions"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); Messenger.m(source, "lb Stored functions"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":"));
for (String fname : host.getAvailableFunctions(all)) host.globaFunctionNames(host.myCode, (str) -> all || !str.startsWith("__")).sorted().forEach( (s) -> {
{ FunctionValue fun = host.getFunction(s);
FunctionValue fun = host.globalFunctions.get(fname);
Expression expr = fun.getExpression(); Expression expr = fun.getExpression();
Tokenizer.Token tok = fun.getToken(); Tokenizer.Token tok = fun.getToken();
List<String> snippet = ExpressionInspector.Expression_getExpressionSnippet(tok, expr); List<String> snippet = ExpressionInspector.Expression_getExpressionSnippet(tok, expr);
@@ -326,16 +326,13 @@ public class ScriptCommand
Messenger.m(source, "w "+snippetLine); Messenger.m(source, "w "+snippetLine);
} }
Messenger.m(source, "gi ----------------"); Messenger.m(source, "gi ----------------");
} });
//Messenger.m(source, "w "+code); //Messenger.m(source, "w "+code);
Messenger.m(source, "w "); Messenger.m(source, "w ");
Messenger.m(source, "lb Global variables"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); Messenger.m(source, "lb Global variables"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":"));
host.globaVariableNames(host.myCode, (s) -> s.startsWith("global_")).sorted().forEach( (s) -> {
for (String vname : host.globalVariables.keySet()) Messenger.m(source, "wb "+s+": ", "w "+ host.getGlobalVariable(s).evalValue(null).getPrettyString());
{ });
if (!vname.startsWith("global")) continue;
Messenger.m(source, "wb "+vname+": ", "w "+ host.globalVariables.get(vname).evalValue(null).getPrettyString());
}
return 1; return 1;
} }
@@ -402,7 +399,7 @@ public class ScriptCommand
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context); CarpetScriptHost host = getHost(context);
handleCall(source, host, () -> { handleCall(source, host, () -> {
CarpetExpression ex = new CarpetExpression(expr, source, new BlockPos(0, 0, 0)); CarpetExpression ex = new CarpetExpression(host.myCode, expr, source, new BlockPos(0, 0, 0));
return ex.scriptRunCommand(host, new BlockPos(source.getPosition())); return ex.scriptRunCommand(host, new BlockPos(source.getPosition()));
}); });
return 1; return 1;
@@ -413,7 +410,8 @@ public class ScriptCommand
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context); CarpetScriptHost host = getHost(context);
MutableIntBoundingBox area = new MutableIntBoundingBox(a, b); MutableIntBoundingBox area = new MutableIntBoundingBox(a, b);
CarpetExpression cexpr = new CarpetExpression(expr, source, origin); CarpetExpression cexpr = new CarpetExpression(host.myCode, expr, source, origin);
int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ(); int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ();
if (int_1 > CarpetSettings.fillLimit) if (int_1 > CarpetSettings.fillLimit)
{ {
@@ -463,7 +461,7 @@ public class ScriptCommand
ServerCommandSource source = context.getSource(); ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context); CarpetScriptHost host = getHost(context);
MutableIntBoundingBox area = new MutableIntBoundingBox(a, b); MutableIntBoundingBox area = new MutableIntBoundingBox(a, b);
CarpetExpression cexpr = new CarpetExpression(expr, source, origin); CarpetExpression cexpr = new CarpetExpression(host.myCode, expr, source, origin);
int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ(); int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ();
if (int_1 > CarpetSettings.fillLimit) if (int_1 > CarpetSettings.fillLimit)
{ {
+10 -1
View File
@@ -1,5 +1,6 @@
package carpet.script; package carpet.script;
import carpet.script.value.Value;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -15,9 +16,17 @@ public class CarpetContext extends Context
} }
@Override @Override
public Context recreate() public Context duplicate()
{ {
return new CarpetContext(this.host, this.s, this.origin); 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 // impossible call to add
return false; return false;
} }
FunctionValue udf = host.globalFunctions.get(funName); FunctionValue udf = host.getFunction(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
@@ -7,6 +7,7 @@ import carpet.helpers.FeatureGenerator;
import carpet.mixins.ChunkTicketManager_scarpetMixin; import carpet.mixins.ChunkTicketManager_scarpetMixin;
import carpet.mixins.ServerChunkManager_scarpetMixin; import carpet.mixins.ServerChunkManager_scarpetMixin;
import carpet.script.Fluff.TriFunction; import carpet.script.Fluff.TriFunction;
import carpet.script.bundled.Module;
import carpet.script.exception.CarpetExpressionException; import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExitStatement; import carpet.script.exception.ExitStatement;
import carpet.script.exception.ExpressionException; import carpet.script.exception.ExpressionException;
@@ -2135,9 +2136,7 @@ public class CarpetExpression
else if (!(functionValue instanceof FunctionValue)) else if (!(functionValue instanceof FunctionValue))
{ {
String name = functionValue.getString(); String name = functionValue.getString();
functionValue = c.host.globalFunctions.get(name); functionValue = c.host.getAssertFunction(this.expr.module, name);
if (functionValue == null)
throw new InternalExpressionException("Function "+name+" is not defined yet");
} }
FunctionValue function = (FunctionValue)functionValue; FunctionValue function = (FunctionValue)functionValue;
List<Value> args = null; List<Value> args = null;
@@ -3194,9 +3193,7 @@ public class CarpetExpression
if (!(functionValue instanceof FunctionValue)) if (!(functionValue instanceof FunctionValue))
{ {
String name = functionValue.getString(); String name = functionValue.getString();
functionValue = c.host.globalFunctions.get(name); functionValue = c.host.getAssertFunction(this.expr.module, name);
if (functionValue == null)
throw new InternalExpressionException("Function "+name+" is not defined yet");
} }
FunctionValue function = (FunctionValue)functionValue; FunctionValue function = (FunctionValue)functionValue;
@@ -3298,11 +3295,13 @@ public class CarpetExpression
* @param source source * @param source source
* @param origin origin * @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.origin = origin;
this.source = source; this.source = source;
this.expr = new Expression(expression); this.expr = new Expression(expression);
this.expr.asAModule(module);
API_BlockManipulation(); API_BlockManipulation();
API_EntityManipulation(); API_EntityManipulation();
@@ -1,7 +1,7 @@
package carpet.script; package carpet.script;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.script.bundled.ModuleInterface; import carpet.script.bundled.Module;
import carpet.script.exception.CarpetExpressionException; import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExpressionException; import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InternalExpressionException;
@@ -36,7 +36,7 @@ public class CarpetScriptHost extends ScriptHost
private Tag globalState; private Tag globalState;
private int saveTimeout; 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); super(code, perUser, parent);
this.saveTimeout = 0; this.saveTimeout = 0;
@@ -45,12 +45,9 @@ public class CarpetScriptHost extends ScriptHost
globalState = loadState(); 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 ); 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 // parse code and convert to expression
if (module != null) if (module != null)
{ {
@@ -63,8 +60,8 @@ public class CarpetScriptHost extends ScriptHost
return null; return null;
} }
host.setChatErrorSnooper(source); host.setChatErrorSnooper(source);
CarpetExpression ex = new CarpetExpression(code, source, new BlockPos(0, 0, 0)); CarpetExpression ex = new CarpetExpression(host.myCode, code, source, new BlockPos(0, 0, 0));
ex.getExpr().asAModule(module); ex.getExpr().asATextSource();
ex.scriptRunCommand(host, new BlockPos(source.getPosition())); ex.scriptRunCommand(host, new BlockPos(source.getPosition()));
} }
catch (CarpetExpressionException e) catch (CarpetExpressionException e)
@@ -85,11 +82,11 @@ public class CarpetScriptHost extends ScriptHost
} }
@Override @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 // 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 // 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_",""); String event = funName.replaceFirst("__on_","");
@@ -99,35 +96,27 @@ public class CarpetScriptHost extends ScriptHost
} }
@Override @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) if (module == null || module.getCode() == null)
throw new InternalExpressionException("Unable to locate package: "+name); throw new InternalExpressionException("Unable to locate package: "+name);
return module; return module;
} }
@Override @Override
protected boolean runModuleCode(Context c, ModuleInterface module) protected void runModuleCode(Context c, Module module)
{ {
CarpetContext cc = (CarpetContext)c; CarpetContext cc = (CarpetContext)c;
try CarpetExpression ex = new CarpetExpression(module, module.getCode(), cc.s, cc.origin);
{ ex.getExpr().asATextSource();
CarpetExpression ex = new CarpetExpression(module.getCode(), cc.s, cc.origin); ex.scriptRunCommand(this, 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;
} }
@Override @Override
public void delFunction(String funName) public void delFunction(Module module, String funName)
{ {
super.delFunction(funName); super.delFunction(module, funName);
// mcarpet // mcarpet
if (funName.startsWith("__on_")) if (funName.startsWith("__on_"))
{ {
@@ -157,7 +146,7 @@ public class CarpetScriptHost extends ScriptHost
{ {
if (CarpetServer.scriptServer.stopAll) if (CarpetServer.scriptServer.stopAll)
return "SCARPET PAUSED"; return "SCARPET PAUSED";
FunctionValue function = globalFunctions.get(call); FunctionValue function = getFunction(call);
if (function == null) if (function == null)
return "UNDEFINED"; return "UNDEFINED";
List<LazyValue> argv = new ArrayList<>(); List<LazyValue> argv = new ArrayList<>();
@@ -170,9 +159,10 @@ public class CarpetScriptHost extends ScriptHost
switch (tok.type) switch (tok.type)
{ {
case VARIABLE: 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; break;
} }
case STRINGPARAM: 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 // 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 function.getExpression().evalValue(
() -> function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv), () -> function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv),
context, context,
Context.VOID 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 // 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 fun.getExpression().evalValue(
() -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv), () -> fun.lazyEval(context, Context.VOID, fun.getExpression(), fun.getToken(), argv),
context, context,
Context.VOID); Context.VOID);
@@ -281,7 +271,7 @@ public class CarpetScriptHost extends ScriptHost
if (this.saveTimeout > 0) if (this.saveTimeout > 0)
dumpState(); dumpState();
FunctionValue closing = globalFunctions.get("__on_close"); FunctionValue closing = getFunction("__on_close");
if (closing != null) if (closing != null)
{ {
try try
@@ -294,7 +284,7 @@ public class CarpetScriptHost extends ScriptHost
} }
userHosts.forEach((key, value) -> userHosts.forEach((key, value) ->
{ {
FunctionValue userClosing = value.globalFunctions.get("__on_close"); FunctionValue userClosing = value.getFunction("__on_close");
if (userClosing != null) if (userClosing != null)
{ {
ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key); ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
@@ -373,7 +363,7 @@ public class CarpetScriptHost extends ScriptHost
public void setChatErrorSnooper(ServerCommandSource source) public void setChatErrorSnooper(ServerCommandSource source)
{ {
errorSnooper = (expr, token, message) -> errorSnooper = (expr, /*Nullable*/ token, message) ->
{ {
try try
{ {
@@ -383,29 +373,43 @@ public class CarpetScriptHost extends ScriptHost
{ {
return null; return null;
} }
String[] lines = expr.getCodeString().split("\n");
String shebang = message; 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 { else
shebang += " at pos "+(token.pos+1);
}
if (expr.getName() != null)
{ {
shebang += " in "+expr.getName()+""; shebang += " in system chat";
} }
Messenger.m(source, "r "+shebang); if (token != null)
if (lines.length > 1 && token.lineno > 0)
{ {
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 "+ else
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, "r " + shebang);
} }
return new ArrayList<>(); return new ArrayList<>();
}; };
@@ -7,7 +7,7 @@ import carpet.script.value.StringValue;
import carpet.CarpetSettings; import carpet.CarpetSettings;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.script.bundled.FileModule; import carpet.script.bundled.FileModule;
import carpet.script.bundled.ModuleInterface; import carpet.script.bundled.Module;
import carpet.script.exception.InvalidCallbackException; import carpet.script.exception.InvalidCallbackException;
import carpet.script.value.Value; import carpet.script.value.Value;
import carpet.utils.Messenger; import carpet.utils.Messenger;
@@ -40,7 +40,7 @@ public class CarpetScriptServer
private final Set<String> holyMoly; private final Set<String> holyMoly;
public final CarpetEventServer events; 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("camera"));
add(new BundledModule("event_test")); add(new BundledModule("event_test"));
add(new BundledModule("stats_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( File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts"); CarpetServer.minecraft_server.getLevelName(), "scripts");
@@ -82,7 +82,7 @@ public class CarpetScriptServer
return new FileModule(script); return new FileModule(script);
} }
} }
for (ModuleInterface moduleData : bundledModuleData) for (Module moduleData : bundledModuleData)
{ {
if (moduleData.getName().equalsIgnoreCase(name)) if (moduleData.getName().equalsIgnoreCase(name))
{ {
@@ -97,7 +97,7 @@ public class CarpetScriptServer
List<String> moduleNames = new ArrayList<>(); List<String> moduleNames = new ArrayList<>();
if (includeBuiltIns) if (includeBuiltIns)
{ {
for (ModuleInterface mi : bundledModuleData) for (Module mi : bundledModuleData)
{ {
moduleNames.add(mi.getName()); moduleNames.add(mi.getName());
} }
@@ -129,7 +129,7 @@ public class CarpetScriptServer
{ {
//TODO add per player modules to support player actions better on a server //TODO add per player modules to support player actions better on a server
name = name.toLowerCase(Locale.ROOT); name = name.toLowerCase(Locale.ROOT);
ModuleInterface module = getModule(name); Module module = getModule(name);
if (module == null) if (module == null)
{ {
Messenger.m(source, "r Failed to add "+name+" app"); Messenger.m(source, "r Failed to add "+name+" app");
@@ -170,13 +170,13 @@ public class CarpetScriptServer
private boolean addConfig(ServerCommandSource source, String hostName) private boolean addConfig(ServerCommandSource source, String hostName)
{ {
CarpetScriptHost host = modules.get(hostName); CarpetScriptHost host = modules.get(hostName);
if (host == null || !host.globalFunctions.containsKey("__config")) if (host == null || host.getFunction("__config") == null)
{ {
return false; return false;
} }
try 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; if (!(ret instanceof MapValue)) return false;
Map<Value, Value> config = ((MapValue) ret).getMap(); Map<Value, Value> config = ((MapValue) ret).getMap();
host.setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player")); host.setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player"));
@@ -195,7 +195,7 @@ public class CarpetScriptServer
{ {
return; return;
} }
if (!host.globalFunctions.containsKey("__command")) if (host.getFunction("__command") == null)
{ {
Messenger.m(source, "gi "+hostName+" app loaded."); Messenger.m(source, "gi "+hostName+" app loaded.");
return; return;
@@ -217,11 +217,11 @@ public class CarpetScriptServer
return 1; return 1;
}); });
for (String function : host.getPublicFunctions()) for (String function : host.globaFunctionNames(host.myCode, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
{ {
command = command. command = command.
then(literal(function). 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) -> { executes( (c) -> {
String response = modules.get(hostName).retrieveForExecution(c.getSource()). String response = modules.get(hostName).retrieveForExecution(c.getSource()).
call(c.getSource(), function,null,""); call(c.getSource(), function,null,"");
+23 -18
View File
@@ -1,5 +1,7 @@
package carpet.script; package carpet.script;
import carpet.script.value.Value;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -26,38 +28,26 @@ public class Context
this.host = host; this.host = host;
} }
LazyValue getVariable(String name) LazyValue getVariable(String name)
{ {
if (variables.containsKey(name)) return variables.get(name);
{
return variables.get(name);
}
return host.globalVariables.get(name);
} }
public void setVariable(String name, LazyValue lv) public void setVariable(String name, LazyValue lv)
{ {
if (name.startsWith("global_"))
{
host.globalVariables.put(name, lv);
return;
}
variables.put(name, lv); variables.put(name, lv);
} }
boolean isAVariable(String name)
{
return variables.containsKey(name) || host.globalVariables.containsKey(name);
}
void delVariable(String variable) void delVariable(String variable)
{ {
variables.remove(variable); variables.remove(variable);
} }
public void removeVariablesMatching(String varname)
{
variables.entrySet().removeIf(e -> e.getKey().startsWith(varname));
}
public Context with(String variable, LazyValue lv) public Context with(String variable, LazyValue lv)
{ {
variables.put(variable, lv); variables.put(variable, lv);
@@ -70,6 +60,21 @@ public class Context
} }
public Context recreate() 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); return new Context(this.host);
} }
+81 -96
View File
@@ -11,12 +11,13 @@ 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.bundled.ModuleInterface; import carpet.script.bundled.Module;
import carpet.script.exception.BreakStatement; import carpet.script.exception.BreakStatement;
import carpet.script.exception.ContinueStatement; import carpet.script.exception.ContinueStatement;
import carpet.script.exception.ExitStatement; 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.ResolvedException;
import carpet.script.exception.ReturnStatement; import carpet.script.exception.ReturnStatement;
import carpet.script.exception.ThrowStatement; import carpet.script.exception.ThrowStatement;
import carpet.script.value.AbstractListValue; import carpet.script.value.AbstractListValue;
@@ -332,7 +333,7 @@ import static java.lang.Math.min;
* </p> * </p>
*/ */
public class Expression implements Cloneable public class Expression
{ {
private static final Map<String, Integer> precedence = new HashMap<String,Integer>() {{ private static final Map<String, Integer> precedence = new HashMap<String,Integer>() {{
put("attribute~:", 80); put("attribute~:", 80);
@@ -366,21 +367,18 @@ public class Expression implements Cloneable
private boolean allowNewlineSubstitutions = true; private boolean allowNewlineSubstitutions = true;
private boolean allowComments = false; private boolean allowComments = false;
public ModuleInterface module = null; public Module module = null;
void asAModule(ModuleInterface mi) void asATextSource()
{ {
allowNewlineSubstitutions = false; allowNewlineSubstitutions = false;
allowComments = true; allowComments = true;
module = mi;
} }
private String name; void asAModule(Module mi)
String getName() {return name;}
Expression withName(String alias)
{ {
name = alias;
return this; module = mi;
} }
/** Cached AST (Abstract Syntax Tree) (root) of the expression */ /** 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<>(); private final Map<String, ILazyFunction> functions = new Object2ObjectOpenHashMap<>();
Set<String> getFunctionNames() {return functions.keySet();} 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) static List<String> getExpressionSnippet(Tokenizer.Token token, Expression expr)
{ {
String code = expr.getCodeString(); String code = expr.getCodeString();
@@ -561,7 +547,7 @@ public class Expression implements Cloneable
return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack); return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack);
if (exc instanceof ArithmeticException) if (exc instanceof ArithmeticException)
return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage()); return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage());
if (exc instanceof ExpressionException) if (exc instanceof ResolvedException)
return exc; return exc;
// unexpected really - should be caught earlier and converted to InternalExpressionException // unexpected really - should be caught earlier and converted to InternalExpressionException
exc.printStackTrace(); 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); name = name.toLowerCase(Locale.ROOT);
if (functions.containsKey(name)) if (functions.containsKey(name))
throw new ExpressionException(context, expr, token, "Function "+name+" would mask a built-in function"); throw new ExpressionException(context, expr, token, "Function "+name+" would mask a built-in function");
Map<String, LazyValue> contextValues = new HashMap<>(); 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) 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 else
{ {
contextValues.put(global, lv); contextValues.put(outer, lv);
} }
} }
if (contextValues.isEmpty()) contextValues = null; if (contextValues.isEmpty()) contextValues = null;
FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues); FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues);
// do not store lambda definitions // do not store lambda definitions
if (!name.equals("_")) context.host.addUserDefinedFunction(name, result); if (!name.equals("_")) context.host.addUserDefinedFunction(module, name, result);
return result; return result;
} }
@@ -869,11 +855,13 @@ public class Expression implements Cloneable
public void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right public void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right
{ {
// artificial construct to handle user defined functions and function definitions // 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()); if (lv.size() < 2) throw new InternalExpressionException("'import' needs at least a module name to import, and list of values to import");
Value ret = new NumericValue(success); String moduleName = lv.get(0).evalValue(c).getString();
return (cc, tt) -> ret; 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 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)) if (!(functionValue instanceof FunctionValue))
{ {
String name = functionValue.getString(); String name = functionValue.getString();
functionValue = c.host.globalFunctions.get(name); functionValue = c.host.getAssertFunction(module, name);
if (functionValue == null)
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);
for (int i=1; i< lv.size(); i++) for (int i=1; i< lv.size(); i++)
@@ -1255,7 +1241,7 @@ public class Expression implements Cloneable
{ {
String lname = li.next().getVariable(); String lname = li.next().getVariable();
Value vval = ri.next().reboundedTo(lname); Value vval = ri.next().reboundedTo(lname);
c.setVariable(lname, (cc, tt) -> vval); setAnyVariable(c, lname, (cc, tt) -> vval);
} }
return (cc, tt) -> Value.TRUE; return (cc, tt) -> Value.TRUE;
} }
@@ -1272,7 +1258,7 @@ public class Expression implements Cloneable
String varname = v1.getVariable(); String varname = v1.getVariable();
Value copy = v2.reboundedTo(varname); Value copy = v2.reboundedTo(varname);
LazyValue boundedLHS = (cc, tt) -> copy; LazyValue boundedLHS = (cc, tt) -> copy;
c.setVariable(varname, boundedLHS); setAnyVariable(c, varname, boundedLHS);
return boundedLHS; return boundedLHS;
}); });
@@ -1294,7 +1280,7 @@ public class Expression implements Cloneable
Value lval = li.next(); Value lval = li.next();
String lname = lval.getVariable(); String lname = lval.getVariable();
Value result = lval.add(ri.next()).bindTo(lname); 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; return (cc, tt) -> Value.TRUE;
} }
@@ -1323,7 +1309,7 @@ public class Expression implements Cloneable
Value result = v1.add(v2).bindTo(varname); Value result = v1.add(v2).bindTo(varname);
boundedLHS = (cc, tt) -> result; boundedLHS = (cc, tt) -> result;
} }
c.setVariable(varname, boundedLHS); setAnyVariable(c, varname, boundedLHS);
return boundedLHS; return boundedLHS;
}); });
@@ -1349,8 +1335,8 @@ public class Expression implements Cloneable
String rname = rval.getVariable(); String rname = rval.getVariable();
lval.reboundedTo(rname); lval.reboundedTo(rname);
rval.reboundedTo(lname); rval.reboundedTo(lname);
c.setVariable(lname, (cc, tt) -> rval); setAnyVariable(c, lname, (cc, tt) -> rval);
c.setVariable(rname, (cc, tt) -> lval); setAnyVariable(c, rname, (cc, tt) -> lval);
} }
return (cc, tt) -> Value.TRUE; return (cc, tt) -> Value.TRUE;
} }
@@ -1360,8 +1346,8 @@ public class Expression implements Cloneable
String rvalvar = v2.getVariable(); String rvalvar = v2.getVariable();
Value lval = v2.reboundedTo(lvalvar); Value lval = v2.reboundedTo(lvalvar);
Value rval = v1.reboundedTo(rvalvar); Value rval = v1.reboundedTo(rvalvar);
c.setVariable(lvalvar, (cc, tt) -> lval); setAnyVariable(c, lvalvar, (cc, tt) -> lval);
c.setVariable(rvalvar, (cc, tt) -> rval); setAnyVariable(c, rvalvar, (cc, tt) -> rval);
return (cc, tt) -> lval; return (cc, tt) -> lval;
}); });
@@ -2991,9 +2977,7 @@ public class Expression implements Cloneable
addLazyFunction("var", 1, (c, t, lv) -> { addLazyFunction("var", 1, (c, t, lv) -> {
String varname = lv.get(0).evalValue(c).getString(); String varname = lv.get(0).evalValue(c).getString();
if (!c.isAVariable(varname)) return getOrSetAnyVariable(c, varname);
c.setVariable(varname, (_c, _t ) -> Value.ZERO.reboundedTo(varname));
return c.getVariable(varname);
}); });
addLazyFunction("undef", 1, (c, t, lv) -> addLazyFunction("undef", 1, (c, t, lv) ->
@@ -3001,7 +2985,7 @@ public class Expression implements Cloneable
Value remove = lv.get(0).evalValue(c); Value remove = lv.get(0).evalValue(c);
if (remove instanceof FunctionValue) if (remove instanceof FunctionValue)
{ {
c.host.delFunction(remove.getString()); c.host.delFunction(module, remove.getString());
return (cc, tt) -> Value.NULL; return (cc, tt) -> Value.NULL;
} }
String varname = remove.getString(); String varname = remove.getString();
@@ -3010,33 +2994,24 @@ public class Expression implements Cloneable
varname = varname.replaceAll("\\*+$", ""); varname = varname.replaceAll("\\*+$", "");
if (isPrefix) if (isPrefix)
{ {
for (String key: c.host.globalFunctions.keySet()) c.host.delFunctionWithPrefix(module, varname);
{
if (key.startsWith(varname)) c.host.delFunction(key);
}
if (varname.startsWith("global_")) if (varname.startsWith("global_"))
{ {
for (String key : c.host.globalVariables.keySet()) c.host.delGlobalVariableWithPrefix(module, varname);
{
if (key.startsWith(varname)) c.host.delGlobalVariable(key);
}
} }
if (!varname.startsWith("_")) else if (!varname.startsWith("_"))
{ {
for (String key : c.variables.keySet()) c.removeVariablesMatching(varname);
{
if (key.startsWith(varname)) c.delVariable(key);
}
} }
} }
else else
{ {
c.host.delFunction(varname); c.host.delFunction(module, varname);
if (varname.startsWith("global_")) if (varname.startsWith("global_"))
{ {
c.host.delGlobalVariable(varname); c.host.delGlobalVariable(module, varname);
} }
if (!varname.startsWith("_")) else if (!varname.startsWith("_"))
{ {
c.delVariable(varname); c.delVariable(varname);
} }
@@ -3044,25 +3019,17 @@ public class Expression implements Cloneable
return (cc, tt) -> Value.NULL; return (cc, tt) -> Value.NULL;
}); });
//deprecate
addLazyFunction("vars", 1, (c, t, lv) -> { addLazyFunction("vars", 1, (c, t, lv) -> {
String prefix = lv.get(0).evalValue(c).getString(); String prefix = lv.get(0).evalValue(c).getString();
List<Value> values = new ArrayList<>(); List<Value> values = new ArrayList<>();
if (prefix.startsWith("global")) if (prefix.startsWith("global"))
{ {
for (String k: c.host.globalVariables.keySet()) c.host.globaVariableNames(module, (s) -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
{
if (k.startsWith(prefix))
values.add(new StringValue(k));
}
} }
else else
{ {
for (String k: c.getAllVariableNames()) c.getAllVariableNames().stream().filter(s -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
{
if (k.startsWith(prefix))
values.add(new StringValue(k));
}
} }
Value retval = ListValue.wrap(values); Value retval = ListValue.wrap(values);
return (cc, tt) -> retval; return (cc, tt) -> retval;
@@ -3075,9 +3042,7 @@ public class Expression implements Cloneable
if (!(funcDesc instanceof FunctionValue)) if (!(funcDesc instanceof FunctionValue))
{ {
String name = funcDesc.getString(); String name = funcDesc.getString();
funcDesc = c.host.globalFunctions.get(name); funcDesc = c.host.getAssertFunction(module, name);
if (funcDesc == null)
throw new InternalExpressionException("Function "+name+" is not defined yet");
} }
FunctionValue fun = (FunctionValue)funcDesc; FunctionValue fun = (FunctionValue)funcDesc;
int extraargs = lv.size() - fun.getArguments().size(); 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"); static final Expression none = new Expression("null");
/** /**
* @param expression . * @param expression .
@@ -3143,6 +3135,7 @@ public class Expression implements Cloneable
BasicDataStructures(); BasicDataStructures();
} }
private List<Tokenizer.Token> shuntingYard(Context c) private List<Tokenizer.Token> shuntingYard(Context c)
{ {
List<Tokenizer.Token> outputQueue = new ArrayList<>(); List<Tokenizer.Token> outputQueue = new ArrayList<>();
@@ -3267,7 +3260,7 @@ public class Expression implements Cloneable
} }
if (stack.isEmpty()) if (stack.isEmpty())
{ {
throw new ExpressionException(c, "Mismatched parentheses"); throw new ExpressionException(c, this, "Mismatched parentheses");
} }
stack.pop(); stack.pop();
if (!stack.isEmpty() && stack.peek().type == Tokenizer.Token.TokenType.FUNCTION) if (!stack.isEmpty() && stack.peek().type == Tokenizer.Token.TokenType.FUNCTION)
@@ -3325,7 +3318,7 @@ public class Expression implements Cloneable
return evalValue(() -> ast, c, expectedType); 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 try
{ {
@@ -3333,7 +3326,7 @@ public class Expression implements Cloneable
} }
catch (ContinueStatement | BreakStatement | ReturnStatement exc) 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) catch (ExitStatement exit)
{ {
@@ -3341,15 +3334,15 @@ public class Expression implements Cloneable
} }
catch (StackOverflowError ignored) 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) 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) 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); stack.push(result);
break; break;
case VARIABLE: case VARIABLE:
stack.push((c, t) -> stack.push((c, t) -> getOrSetAnyVariable(c, token.surface).evalValue(c));
{
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);
});
break; break;
case FUNCTION: case FUNCTION:
String name = token.surface.toLowerCase(Locale.ROOT); String name = token.surface.toLowerCase(Locale.ROOT);
@@ -3507,15 +3492,15 @@ public class Expression implements Cloneable
if (stack.size() > 1) 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) 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) 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; package carpet.script;
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;
@@ -21,31 +20,17 @@ public class ExpressionInspector
return Expression.getExpressionSnippet(token, expr); return Expression.getExpressionSnippet(token, expr);
} }
public static String Expression_getName(Expression e) public static String Expression_getModuleName(Expression e)
{ {
return e.getName(); return e.module == null?null:e.module.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);
} }
private static TreeSet<String> scarpetFunctions; private static TreeSet<String> scarpetFunctions;
private static TreeSet<String> APIFunctions; private static TreeSet<String> APIFunctions;
static static
{ {
Set<String> allFunctions = (new CarpetExpression("null", null, null)).getExpr().getFunctionNames(); Set<String> allFunctions = (new CarpetExpression(null, "null", null, null)).getExpr().getFunctionNames();
scarpetFunctions = new TreeSet<>(new Expression("null").getFunctionNames()); scarpetFunctions = new TreeSet<>(Expression.none.getFunctionNames());
APIFunctions = allFunctions.stream().filter(s -> !scarpetFunctions.contains(s)).collect(Collectors.toCollection(TreeSet::new)); 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 // not that useful in commandline, more so in external scripts, so skipping here
//scarpetMatches.addAll(CarpetServer.scriptServer.events.eventHandlers.keySet().stream(). //scarpetMatches.addAll(CarpetServer.scriptServer.events.eventHandlers.keySet().stream().
// filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> "__"+s+"(").collect(Collectors.toList())); // filter(s -> s.startsWith(prefix) && s.length() <= maxLen).map(s -> "__"+s+"(").collect(Collectors.toList()));
scarpetMatches.addAll(host.globalFunctions.keySet().stream(). scarpetMatches.addAll(host.globaFunctionNames(host.myCode, s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList()));
filter(s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList())); scarpetMatches.addAll(host.globaVariableNames(host.myCode, s -> s.startsWith(prefix)).collect(Collectors.toList()));
scarpetMatches.addAll(host.globalVariables.keySet().stream().
filter(s -> s.startsWith(prefix)).collect(Collectors.toList()));
return scarpetMatches; return scarpetMatches;
} }
} }
+238 -57
View File
@@ -1,111 +1,292 @@
package carpet.script; 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.FunctionValue;
import carpet.script.value.Value; import carpet.script.value.Value;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.HashSet; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Stream;
public abstract class ScriptHost 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<>(); protected final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>(); private Map<Module,ModuleData> moduleData = new HashMap<>(); // marking imports
public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>(); private Map<String,Module> modules = new HashMap<>();
private Set<String> imports = new HashSet<>();
protected ScriptHost parent; protected ScriptHost parent;
protected boolean perUser; protected boolean perUser;
//private String name;
public String getName() {return myCode==null?null:myCode.getName();} public String getName() {return myCode==null?null:myCode.getName();}
protected final ModuleInterface myCode; public final Module myCode;
public Fluff.TriFunction<Expression, Tokenizer.Token, String, List<String>> errorSnooper = null; 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.parent = parent;
//this.name = name;
this.myCode = code; this.myCode = code;
this.perUser = perUser; this.perUser = perUser;
if (code != null) imports.add(code.getName()); ModuleData moduleData = new ModuleData(code);
globalVariables.put("euler", (c, t) -> Expression.euler); initializeModuleGlobals(moduleData);
globalVariables.put("pi", (c, t) -> Expression.PI); this.moduleData.put(code, moduleData);
globalVariables.put("null", (c, t) -> Value.NULL); this.modules.put(code==null?null:code.getName(), code);
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);
} }
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 if (modules.containsKey(moduleName)) return; // aready imported
ModuleInterface module = getModuleByName(moduleName); Module module = getModuleByName(moduleName);
if (imports.contains(module.getName())) return false; // aready imported, once again, in case some discrepancies if (modules.containsKey(module.getName())) return; // aready imported, once again, in case some discrepancies in names?
imports.add(module.getName()); modules.put(module.getName(), module);
if (runModuleCode(c, 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(myCode, 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(myCode, 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) public ScriptHost retrieveForExecution(String /*Nullable*/ user)
{ {
if (!perUser) if (!perUser) return this;
return this; ScriptHost oldUserHost = userHosts.get(user);
ScriptHost userHost = userHosts.get(user); if (oldUserHost != null) return oldUserHost;
if (userHost != null) ScriptHost userHost = this.duplicate();
return userHost; userHost.modules.putAll(this.modules);
userHost = this.duplicate(); for (Map.Entry<Module, ScriptHost.ModuleData> e : this.moduleData.entrySet())
userHost.globalVariables.putAll(this.globalVariables); {
userHost.globalFunctions.putAll(this.globalFunctions); userHost.moduleData.put(e.getKey(), new ModuleData(e.getKey(), e.getValue()));
userHost.imports.addAll(this.imports); }
// fixing imports
userHost.moduleData.forEach((module, data) ->
{
data.setImportsBasedOn(this, this.moduleData.get(data.parent));
});
userHosts.put(user, userHost); userHosts.put(user, userHost);
return userHost; return userHost;
} }
protected abstract ScriptHost duplicate(); 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 onClose() { }
public void setPerPlayer(boolean isPerUser) public void setPerPlayer(boolean isPerUser)
@@ -6,7 +6,7 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Locale; import java.util.Locale;
public class BundledModule implements ModuleInterface public class BundledModule extends Module
{ {
private String name; private String name;
private String code; private String code;
@@ -14,7 +14,7 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Locale; import java.util.Locale;
public class FileModule implements ModuleInterface public class FileModule extends Module
{ {
private String name; private String name;
private String code; private String code;
@@ -10,13 +10,13 @@ import java.nio.file.Files;
import static carpet.script.bundled.FileModule.read; import static carpet.script.bundled.FileModule.read;
import static carpet.script.bundled.FileModule.write; import static carpet.script.bundled.FileModule.write;
public interface ModuleInterface public abstract class Module
{ {
String getName(); public abstract String getName();
String getCode(); public abstract String getCode();
default boolean isInternal() {return true; } public boolean isInternal() {return true; }
default Tag getData(String file) public Tag getData(String file)
{ {
File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile( File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt"); CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
@@ -24,7 +24,7 @@ public interface ModuleInterface
return read(dataFile); return read(dataFile);
} }
default void saveData(String file, Tag globalState) public void saveData(String file, Tag globalState)
{ {
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile( File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt"); CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
@@ -41,4 +41,10 @@ public interface ModuleInterface
write(globalState, dataFile); write(globalState, dataFile);
} }
@Override
public int hashCode()
{
return getName().hashCode();
}
} }
@@ -6,7 +6,7 @@ import net.minecraft.server.command.ServerCommandSource;
import java.util.List; import java.util.List;
public class CarpetExpressionException extends RuntimeException public class CarpetExpressionException extends RuntimeException implements ResolvedException
{ {
public final List<FunctionValue> stack; public final List<FunctionValue> stack;
@@ -10,16 +10,17 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static carpet.script.ExpressionInspector.Expression_getExpressionSnippet; 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. */ /* The expression evaluators exception class. */
public class ExpressionException extends RuntimeException public class ExpressionException extends RuntimeException implements ResolvedException
{ {
public final Context context; public final Context context;
public final List<FunctionValue> stack = new ArrayList<>(); 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; context = c;
} }
@@ -35,11 +36,12 @@ public class ExpressionException extends RuntimeException
context = c; 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<>(); 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); List<String> snippet = Expression_getExpressionSnippet(token, expr);
errMsg.addAll(snippet); errMsg.addAll(snippet);
@@ -52,11 +54,6 @@ public class ExpressionException extends RuntimeException
{ {
errmessage += " at pos " + (token.pos + 1); errmessage += " at pos " + (token.pos + 1);
} }
if (Expression_getName(expr) != null)
{
errmessage += " (" + Expression_getName(expr) + ")";
}
} }
errMsg.add(errmessage); errMsg.add(errmessage);
return errMsg; 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.Fluff;
import carpet.script.LazyValue; import carpet.script.LazyValue;
import carpet.script.Tokenizer; import carpet.script.Tokenizer;
import carpet.script.bundled.ModuleInterface;
import carpet.script.exception.BreakStatement; import carpet.script.exception.BreakStatement;
import carpet.script.exception.ContinueStatement; import carpet.script.exception.ContinueStatement;
import carpet.script.exception.ExitStatement; import carpet.script.exception.ExitStatement;
@@ -128,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) 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 try
{ {
return lazyEval(c, type, callContext, t, lazyParams); return lazyEval(c, type, e, t, lazyParams);
} }
catch (ExpressionException exc) catch (ExpressionException exc)
{ {
@@ -142,12 +139,12 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
catch (InternalExpressionException exc) catch (InternalExpressionException exc)
{ {
exc.stack.add(this); 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) 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));
} }
} }
@@ -190,14 +187,10 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
retVal = throwStatement.retval; retVal = throwStatement.retval;
rethrow = true; rethrow = true;
} }
catch (ArithmeticException | ExitStatement | ExpressionException | InternalExpressionException exc ) //catch (ArithmeticException | ExitStatement | ExpressionException | InternalExpressionException exc )
{ //{
throw exc; // rethrow so could be contextualized if needed // throw exc; // rethrow so could be contextualized if needed
} //}
catch (Exception exc)
{
throw new ExpressionException(c, e, t, "Error while evaluating expression: "+exc.getMessage());
}
if (rethrow) if (rethrow)
{ {
throw new ThrowStatement(retVal); throw new ThrowStatement(retVal);