From 4f65be946c4b80814f30d169294360816809c0a5 Mon Sep 17 00:00:00 2001 From: gnembon Date: Wed, 8 Jan 2020 23:38:31 -0400 Subject: [PATCH 1/2] modules and imports --- .../java/carpet/commands/ScriptCommand.java | 30 +- .../java/carpet/script/CarpetContext.java | 11 +- .../java/carpet/script/CarpetEventServer.java | 2 +- .../java/carpet/script/CarpetExpression.java | 13 +- .../java/carpet/script/CarpetScriptHost.java | 104 +++--- .../carpet/script/CarpetScriptServer.java | 22 +- src/main/java/carpet/script/Context.java | 41 +-- src/main/java/carpet/script/Expression.java | 177 +++++------ .../carpet/script/ExpressionInspector.java | 29 +- src/main/java/carpet/script/ScriptHost.java | 295 ++++++++++++++---- .../carpet/script/bundled/BundledModule.java | 2 +- .../carpet/script/bundled/FileModule.java | 2 +- .../{ModuleInterface.java => Module.java} | 18 +- .../exception/CarpetExpressionException.java | 2 +- .../script/exception/ExpressionException.java | 19 +- .../script/exception/ResolvedException.java | 5 + .../carpet/script/value/FunctionValue.java | 21 +- 17 files changed, 479 insertions(+), 314 deletions(-) rename src/main/java/carpet/script/bundled/{ModuleInterface.java => Module.java} (77%) create mode 100644 src/main/java/carpet/script/exception/ResolvedException.java diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index a5a54e40..1f460f0f 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -37,6 +37,7 @@ import java.util.Locale; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; 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.literal; @@ -78,7 +79,7 @@ public class ScriptCommand executes( (cc) -> { CarpetServer.scriptServer.stopAll = false; return 1;}); LiteralArgumentBuilder l = literal("run"). 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( cc, StringArgumentType.getString(cc, "expr")))); @@ -141,7 +142,7 @@ public class ScriptCommand then(argument("from", BlockPosArgumentType.blockPos()). then(argument("to", BlockPosArgumentType.blockPos()). then(argument("expr", StringArgumentType.greedyString()). - suggests((cc, bb) -> suggestCode(cc, bb)). + suggests(ScriptCommand::suggestCode). executes( (cc) -> scriptScan( cc, BlockPosArgumentType.getBlockPos(cc, "origin"), @@ -288,7 +289,7 @@ public class ScriptCommand private static Collection suggestFunctionCalls(CommandContext 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) { @@ -314,9 +315,8 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); Messenger.m(source, "lb Stored functions"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); - for (String fname : host.getAvailableFunctions(all)) - { - FunctionValue fun = host.globalFunctions.get(fname); + host.globaFunctionNames(host.myCode, (str) -> all || !str.startsWith("__")).sorted().forEach( (s) -> { + FunctionValue fun = host.getFunction(s); Expression expr = fun.getExpression(); Tokenizer.Token tok = fun.getToken(); List snippet = ExpressionInspector.Expression_getExpressionSnippet(tok, expr); @@ -326,16 +326,13 @@ public class ScriptCommand Messenger.m(source, "w "+snippetLine); } Messenger.m(source, "gi ----------------"); - } + }); //Messenger.m(source, "w "+code); Messenger.m(source, "w "); Messenger.m(source, "lb Global variables"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); - - for (String vname : host.globalVariables.keySet()) - { - if (!vname.startsWith("global")) continue; - Messenger.m(source, "wb "+vname+": ", "w "+ host.globalVariables.get(vname).evalValue(null).getPrettyString()); - } + host.globaVariableNames(host.myCode, (s) -> s.startsWith("global_")).sorted().forEach( (s) -> { + Messenger.m(source, "wb "+s+": ", "w "+ host.getGlobalVariable(s).evalValue(null).getPrettyString()); + }); return 1; } @@ -402,7 +399,7 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); CarpetScriptHost host = getHost(context); 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 1; @@ -413,7 +410,8 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); CarpetScriptHost host = getHost(context); 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(); if (int_1 > CarpetSettings.fillLimit) { @@ -463,7 +461,7 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); CarpetScriptHost host = getHost(context); 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(); if (int_1 > CarpetSettings.fillLimit) { diff --git a/src/main/java/carpet/script/CarpetContext.java b/src/main/java/carpet/script/CarpetContext.java index 5add0cd6..7ea1cc98 100644 --- a/src/main/java/carpet/script/CarpetContext.java +++ b/src/main/java/carpet/script/CarpetContext.java @@ -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); + } } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 20d0db87..0e822279 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -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 diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 6e6054ba..ea2e78e4 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -7,6 +7,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; @@ -2135,9 +2136,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 args = null; @@ -3194,9 +3193,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; @@ -3298,11 +3295,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(); diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 012cba27..c6f8ba71 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -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.myCode, code, source, new BlockPos(0, 0, 0)); + ex.getExpr().asATextSource(); ex.scriptRunCommand(host, new BlockPos(source.getPosition())); } catch (CarpetExpressionException e) @@ -85,11 +82,11 @@ public class CarpetScriptHost extends ScriptHost } @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 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); @@ -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<>(); }; diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 777d60de..08d7308b 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -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 holyMoly; public final CarpetEventServer events; - private static final List bundledModuleData = new ArrayList(){{ + private static final List bundledModuleData = new ArrayList(){{ 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 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 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.myCode, 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,""); diff --git a/src/main/java/carpet/script/Context.java b/src/main/java/carpet/script/Context.java index 31af496f..bd020493 100644 --- a/src/main/java/carpet/script/Context.java +++ b/src/main/java/carpet/script/Context.java @@ -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); } diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 64e4b30f..943901b4 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -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; *

*/ -public class Expression implements Cloneable +public class Expression { private static final Map precedence = new HashMap() {{ 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 functions = new Object2ObjectOpenHashMap<>(); Set 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 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 arguments, List globals, LazyValue code) + private FunctionValue addContextFunction(Context context, String name, Expression expr, Tokenizer.Token token, List arguments, List 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 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, 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 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 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 shuntingYard(Context c) { List 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 exprProvider, Context c, Integer expectedType) + Value evalValue(Supplier 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"); } } diff --git a/src/main/java/carpet/script/ExpressionInspector.java b/src/main/java/carpet/script/ExpressionInspector.java index e4eaa892..922c84cf 100644 --- a/src/main/java/carpet/script/ExpressionInspector.java +++ b/src/main/java/carpet/script/ExpressionInspector.java @@ -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 scarpetFunctions; private static TreeSet APIFunctions; static { - Set allFunctions = (new CarpetExpression("null", null, null)).getExpr().getFunctionNames(); - scarpetFunctions = new TreeSet<>(new Expression("null").getFunctionNames()); + Set 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.myCode, s -> s.startsWith(prefix)).map(s -> s+"(").collect(Collectors.toList())); + scarpetMatches.addAll(host.globaVariableNames(host.myCode, s -> s.startsWith(prefix)).collect(Collectors.toList())); return scarpetMatches; } } diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 619e82ac..63a8b064 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -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 globalFunctions = new Object2ObjectOpenHashMap<>(); + public Map globalVariables = new Object2ObjectOpenHashMap<>(); + public Map functionImports = new Object2ObjectOpenHashMap<>(); // imported functions string to module + public Map globalsImports = new Object2ObjectOpenHashMap<>(); // imported global variables string to module + public Map 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 userHosts = new Object2ObjectOpenHashMap<>(); - public Map globalFunctions = new Object2ObjectOpenHashMap<>(); - public Map globalVariables = new Object2ObjectOpenHashMap<>(); - private Set imports = new HashSet<>(); + private Map moduleData = new HashMap<>(); // marking imports + private Map modules = new HashMap<>(); protected ScriptHost parent; protected boolean perUser; - //private String name; public String getName() {return myCode==null?null:myCode.getName();} - protected final ModuleInterface myCode; + public final Module myCode; public Fluff.TriFunction> 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.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 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 globaVariableNames(Module module, Predicate predicate) + { + return Stream.concat( + moduleData.get(module).globalVariables.keySet().stream(), + moduleData.get(module).globalsImports.keySet().stream() + ).filter(predicate); + } + + public Stream globaFunctionNames(Module module, Predicate 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 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 getPublicFunctions() - { - return globalFunctions.keySet().stream().filter((str) -> !str.startsWith("_")).sorted().collect(Collectors.toList()); - } - public List getAvailableFunctions(boolean all) - { - return globalFunctions.keySet().stream().filter((str) -> all || !str.startsWith("__")).sorted().collect(Collectors.toList()); - } - public void onClose() { } public void setPerPlayer(boolean isPerUser) diff --git a/src/main/java/carpet/script/bundled/BundledModule.java b/src/main/java/carpet/script/bundled/BundledModule.java index 880157c9..7b68d8d8 100644 --- a/src/main/java/carpet/script/bundled/BundledModule.java +++ b/src/main/java/carpet/script/bundled/BundledModule.java @@ -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; diff --git a/src/main/java/carpet/script/bundled/FileModule.java b/src/main/java/carpet/script/bundled/FileModule.java index facded41..301fae7a 100644 --- a/src/main/java/carpet/script/bundled/FileModule.java +++ b/src/main/java/carpet/script/bundled/FileModule.java @@ -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; diff --git a/src/main/java/carpet/script/bundled/ModuleInterface.java b/src/main/java/carpet/script/bundled/Module.java similarity index 77% rename from src/main/java/carpet/script/bundled/ModuleInterface.java rename to src/main/java/carpet/script/bundled/Module.java index dc7aa2ad..adf85549 100644 --- a/src/main/java/carpet/script/bundled/ModuleInterface.java +++ b/src/main/java/carpet/script/bundled/Module.java @@ -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(); + } } diff --git a/src/main/java/carpet/script/exception/CarpetExpressionException.java b/src/main/java/carpet/script/exception/CarpetExpressionException.java index f19fcd80..969b1ea5 100644 --- a/src/main/java/carpet/script/exception/CarpetExpressionException.java +++ b/src/main/java/carpet/script/exception/CarpetExpressionException.java @@ -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 stack; diff --git a/src/main/java/carpet/script/exception/ExpressionException.java b/src/main/java/carpet/script/exception/ExpressionException.java index 3d07f8ee..ec6486b6 100644 --- a/src/main/java/carpet/script/exception/ExpressionException.java +++ b/src/main/java/carpet/script/exception/ExpressionException.java @@ -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 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> errorMaker = (expr, token, errmessage) -> + private static final Fluff.TriFunction> errorMaker = (expr, /*Nullable*/ token, errmessage) -> { List errMsg = new ArrayList<>(); - if (expr != null && token != null) + errmessage += Expression_getModuleName(expr) == null?"":(" in "+Expression_getModuleName(expr)); + if (token != null) { List 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; diff --git a/src/main/java/carpet/script/exception/ResolvedException.java b/src/main/java/carpet/script/exception/ResolvedException.java new file mode 100644 index 00000000..dc75c317 --- /dev/null +++ b/src/main/java/carpet/script/exception/ResolvedException.java @@ -0,0 +1,5 @@ +package carpet.script.exception; + +public interface ResolvedException +{ +} diff --git a/src/main/java/carpet/script/value/FunctionValue.java b/src/main/java/carpet/script/value/FunctionValue.java index 3ccd6b4f..ecbf7dd0 100644 --- a/src/main/java/carpet/script/value/FunctionValue.java +++ b/src/main/java/carpet/script/value/FunctionValue.java @@ -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; @@ -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 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) { @@ -142,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)); } } @@ -190,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); From f67493619968e17b01a4684160e077502e85efb6 Mon Sep 17 00:00:00 2001 From: gnembon Date: Wed, 8 Jan 2020 23:47:54 -0400 Subject: [PATCH 2/2] renamed main --- src/main/java/carpet/commands/ScriptCommand.java | 12 ++++++------ src/main/java/carpet/script/CarpetScriptHost.java | 12 ++++++------ src/main/java/carpet/script/CarpetScriptServer.java | 2 +- src/main/java/carpet/script/ExpressionInspector.java | 4 ++-- src/main/java/carpet/script/ScriptHost.java | 10 +++++----- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index 1f460f0f..dad4cb1d 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -289,7 +289,7 @@ public class ScriptCommand private static Collection suggestFunctionCalls(CommandContext c) { CarpetScriptHost host = getHost(c); - return host.globaFunctionNames(host.myCode, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()); + return host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()); } private static int listEvents(ServerCommandSource source) { @@ -315,7 +315,7 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); Messenger.m(source, "lb Stored functions"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); - host.globaFunctionNames(host.myCode, (str) -> all || !str.startsWith("__")).sorted().forEach( (s) -> { + host.globaFunctionNames(host.main, (str) -> all || !str.startsWith("__")).sorted().forEach( (s) -> { FunctionValue fun = host.getFunction(s); Expression expr = fun.getExpression(); Tokenizer.Token tok = fun.getToken(); @@ -330,7 +330,7 @@ public class ScriptCommand //Messenger.m(source, "w "+code); Messenger.m(source, "w "); Messenger.m(source, "lb Global variables"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); - host.globaVariableNames(host.myCode, (s) -> s.startsWith("global_")).sorted().forEach( (s) -> { + host.globaVariableNames(host.main, (s) -> s.startsWith("global_")).sorted().forEach( (s) -> { Messenger.m(source, "wb "+s+": ", "w "+ host.getGlobalVariable(s).evalValue(null).getPrettyString()); }); return 1; @@ -399,7 +399,7 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); CarpetScriptHost host = getHost(context); handleCall(source, host, () -> { - CarpetExpression ex = new CarpetExpression(host.myCode, expr, source, new BlockPos(0, 0, 0)); + CarpetExpression ex = new CarpetExpression(host.main, expr, source, new BlockPos(0, 0, 0)); return ex.scriptRunCommand(host, new BlockPos(source.getPosition())); }); return 1; @@ -410,7 +410,7 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); CarpetScriptHost host = getHost(context); MutableIntBoundingBox area = new MutableIntBoundingBox(a, b); - CarpetExpression cexpr = new CarpetExpression(host.myCode, expr, source, origin); + CarpetExpression cexpr = new CarpetExpression(host.main, expr, source, origin); int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ(); if (int_1 > CarpetSettings.fillLimit) @@ -461,7 +461,7 @@ public class ScriptCommand ServerCommandSource source = context.getSource(); CarpetScriptHost host = getHost(context); MutableIntBoundingBox area = new MutableIntBoundingBox(a, b); - CarpetExpression cexpr = new CarpetExpression(host.myCode, expr, source, origin); + CarpetExpression cexpr = new CarpetExpression(host.main, expr, source, origin); int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ(); if (int_1 > CarpetSettings.fillLimit) { diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index c6f8ba71..1f595615 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -60,7 +60,7 @@ public class CarpetScriptHost extends ScriptHost return null; } host.setChatErrorSnooper(source); - CarpetExpression ex = new CarpetExpression(host.myCode, code, source, new BlockPos(0, 0, 0)); + CarpetExpression ex = new CarpetExpression(host.main, code, source, new BlockPos(0, 0, 0)); ex.getExpr().asATextSource(); ex.scriptRunCommand(host, new BlockPos(source.getPosition())); } @@ -78,7 +78,7 @@ public class CarpetScriptHost extends ScriptHost @Override protected ScriptHost duplicate() { - return new CarpetScriptHost(scriptServer, myCode, false, this); + return new CarpetScriptHost(scriptServer, main, false, this); } @Override @@ -312,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; @@ -336,7 +336,7 @@ public class CarpetScriptHost extends ScriptHost if (file!= null) { - myCode.saveData(file, tag); + main.saveData(file, tag); return; } diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 08d7308b..135980cd 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -217,7 +217,7 @@ public class CarpetScriptServer return 1; }); - for (String function : host.globaFunctionNames(host.myCode, s -> !s.startsWith("_")).sorted().collect(Collectors.toList())) + for (String function : host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList())) { command = command. then(literal(function). diff --git a/src/main/java/carpet/script/ExpressionInspector.java b/src/main/java/carpet/script/ExpressionInspector.java index 922c84cf..e6d2a1f6 100644 --- a/src/main/java/carpet/script/ExpressionInspector.java +++ b/src/main/java/carpet/script/ExpressionInspector.java @@ -48,8 +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.globaFunctionNames(host.myCode, 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.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; } } diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 63a8b064..d3d609d4 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -64,16 +64,16 @@ public abstract class ScriptHost protected ScriptHost parent; protected boolean perUser; - public String getName() {return myCode==null?null:myCode.getName();} + public String getName() {return main ==null?null: main.getName();} - public final Module myCode; + public final Module main; public Fluff.TriFunction> errorSnooper = null; protected ScriptHost(Module code, boolean perUser, ScriptHost parent) { this.parent = parent; - this.myCode = code; + this.main = code; this.perUser = perUser; ModuleData moduleData = new ModuleData(code); initializeModuleGlobals(moduleData); @@ -138,7 +138,7 @@ public abstract class ScriptHost protected abstract void runModuleCode(Context c, Module module); // this should be shell out in the executor - public FunctionValue getFunction(String name) { return getFunction(myCode, name); } + public FunctionValue getFunction(String name) { return getFunction(main, name); } public FunctionValue getAssertFunction(Module module, String name) { FunctionValue ret = getFunction(module, name); @@ -178,7 +178,7 @@ public abstract class ScriptHost return null; } - public LazyValue getGlobalVariable(String name) { return getGlobalVariable(myCode, name); } + public LazyValue getGlobalVariable(String name) { return getGlobalVariable(main, name); } public LazyValue getGlobalVariable(Module module, String name) { ModuleData local = moduleData.get(module);