diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index 3544e06a..a5a54e40 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -7,9 +7,9 @@ import carpet.script.CarpetEventServer; import carpet.script.CarpetExpression; import carpet.script.Expression; import carpet.script.ExpressionInspector; -import carpet.script.ScriptHost; import carpet.script.Tokenizer; import carpet.script.exception.CarpetExpressionException; +import carpet.script.value.FunctionValue; import carpet.utils.Messenger; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; @@ -50,7 +50,7 @@ public class ScriptCommand SuggestionsBuilder suggestionsBuilder ) { - ScriptHost currentHost = getHost(context); + CarpetScriptHost currentHost = getHost(context); String previous = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT); int strlen = previous.length(); StringBuilder lastToken = new StringBuilder(); @@ -287,7 +287,7 @@ public class ScriptCommand } private static Collection suggestFunctionCalls(CommandContext c) { - ScriptHost host = getHost(c); + CarpetScriptHost host = getHost(c); return host.getPublicFunctions(); } private static int listEvents(ServerCommandSource source) @@ -310,16 +310,17 @@ public class ScriptCommand } private static int listGlobals(CommandContext context, boolean all) { - ScriptHost host = getHost(context); + CarpetScriptHost host = getHost(context); ServerCommandSource source = context.getSource(); Messenger.m(source, "lb Stored functions"+((host == CarpetServer.scriptServer.globalHost)?":":" in "+host.getName()+":")); for (String fname : host.getAvailableFunctions(all)) { - Expression expr = host.getExpressionForFunction(fname); - Tokenizer.Token tok = host.getTokenForFunction(fname); + FunctionValue fun = host.globalFunctions.get(fname); + Expression expr = fun.getExpression(); + Tokenizer.Token tok = fun.getToken(); List snippet = ExpressionInspector.Expression_getExpressionSnippet(tok, expr); - Messenger.m(source, "wb "+fname,"t defined at: line "+(tok.lineno+1)+" pos "+(tok.linepos+1)); + Messenger.m(source, "wb "+fun.fullName(),"t defined at: line "+(tok.lineno+1)+" pos "+(tok.linepos+1)); for (String snippetLine: snippet) { Messenger.m(source, "w "+snippetLine); @@ -338,11 +339,11 @@ public class ScriptCommand return 1; } - public static void handleCall(ServerCommandSource source, Supplier call) + public static void handleCall(ServerCommandSource source, CarpetScriptHost host, Supplier call) { try { - CarpetServer.scriptServer.setChatErrorSnooper(source); + host.setChatErrorSnooper(source); long start = System.nanoTime(); String result = call.get(); long time = ((System.nanoTime()-start)/1000); @@ -361,9 +362,10 @@ public class ScriptCommand } catch (CarpetExpressionException e) { - Messenger.m(source, "r Exception white evaluating expression at "+new BlockPos(source.getPosition())+": "+e.getMessage()); + e.printStack(source); + Messenger.m(source, "r Exception while evaluating expression at "+new BlockPos(source.getPosition())+": "+e.getMessage()); } - CarpetServer.scriptServer.resetErrorSnooper(); + host.resetErrorSnooper(); } private static int invoke(CommandContext context, String call, BlockPos pos1, BlockPos pos2, String args) @@ -390,7 +392,7 @@ public class ScriptCommand } //if (!(args.trim().isEmpty())) // arguments.addAll(Arrays.asList(args.trim().split("\\s+"))); - handleCall(source, () -> host.call(source, call, positions, args)); + handleCall(source, host, () -> host.call(source, call, positions, args)); return 1; } @@ -398,8 +400,8 @@ public class ScriptCommand private static int compute(CommandContext context, String expr) { ServerCommandSource source = context.getSource(); - ScriptHost host = getHost(context); - handleCall(source, () -> { + CarpetScriptHost host = getHost(context); + handleCall(source, host, () -> { CarpetExpression ex = new CarpetExpression(expr, source, new BlockPos(0, 0, 0)); return ex.scriptRunCommand(host, new BlockPos(source.getPosition())); }); @@ -409,7 +411,7 @@ public class ScriptCommand private static int scriptScan(CommandContext context, BlockPos origin, BlockPos a, BlockPos b, String expr) { ServerCommandSource source = context.getSource(); - ScriptHost host = getHost(context); + CarpetScriptHost host = getHost(context); MutableIntBoundingBox area = new MutableIntBoundingBox(a, b); CarpetExpression cexpr = new CarpetExpression(expr, source, origin); int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ(); @@ -441,6 +443,7 @@ public class ScriptCommand } catch (CarpetExpressionException exc) { + exc.printStack(source); Messenger.m(source, "r Error while processing command: "+exc); return 0; } @@ -458,7 +461,7 @@ public class ScriptCommand BlockStateArgument block, Predicate replacement, String mode) { ServerCommandSource source = context.getSource(); - ScriptHost host = getHost(context); + CarpetScriptHost host = getHost(context); MutableIntBoundingBox area = new MutableIntBoundingBox(a, b); CarpetExpression cexpr = new CarpetExpression(expr, source, origin); int int_1 = area.getBlockCountX() * area.getBlockCountY() * area.getBlockCountZ(); @@ -488,6 +491,7 @@ public class ScriptCommand } catch (CarpetExpressionException e) { + e.printStack(source); Messenger.m(source, "r Exception while filling the area:\n","l "+e.getMessage()); return 0; } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 0090417f..225f84b7 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -3376,11 +3376,11 @@ public class CarpetExpression } catch (ExpressionException e) { - throw new CarpetExpressionException(e.getMessage()); + throw new CarpetExpressionException(e.getMessage(), e.stack); } catch (ArithmeticException ae) { - throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage()); + throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage(), null); } } @@ -3423,11 +3423,11 @@ public class CarpetExpression } catch (ExpressionException e) { - throw new CarpetExpressionException(e.getMessage()); + throw new CarpetExpressionException(e.getMessage(), e.stack); } catch (ArithmeticException ae) { - throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage()); + throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage(), null); } } diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index c7e890f8..418681b3 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -2,13 +2,16 @@ package carpet.script; import carpet.CarpetServer; import carpet.script.bundled.ModuleInterface; +import carpet.script.exception.CarpetExpressionException; import carpet.script.exception.ExpressionException; +import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InvalidCallbackException; import carpet.script.value.FunctionValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; import carpet.CarpetSettings; +import carpet.utils.Messenger; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; @@ -33,19 +36,52 @@ public class CarpetScriptHost extends ScriptHost private Tag globalState; private int saveTimeout; - CarpetScriptHost(CarpetScriptServer server, String name, ModuleInterface code, boolean perUser, ScriptHost parent) + private CarpetScriptHost(CarpetScriptServer server, ModuleInterface code, boolean perUser, ScriptHost parent) { - super(name, code, perUser, parent); + super(code, perUser, parent); this.saveTimeout = 0; this.scriptServer = server; - if (parent == null && name != null) + if (parent == null && code != null) globalState = loadState(); } + public static CarpetScriptHost create(CarpetScriptServer scriptServer, ModuleInterface 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) + { + try + { + String code = module.getCode(); + if (code == null) + { + Messenger.m(source, "r Unable to load "+module.getName()+" app - code not found"); + return null; + } + host.setChatErrorSnooper(source); + CarpetExpression ex = new CarpetExpression(code, source, new BlockPos(0, 0, 0)); + ex.getExpr().asAModule(module); + ex.scriptRunCommand(host, new BlockPos(source.getPosition())); + } + catch (CarpetExpressionException e) + { + e.printStack(source); + Messenger.m(source, "r Exception while evaluating expression at " + new BlockPos(source.getPosition()) + ": " + e.getMessage()); + host.resetErrorSnooper(); + return null; + } + } + return host; + } + @Override protected ScriptHost duplicate() { - return new CarpetScriptHost(scriptServer, this.getName(), myCode, false, this); + return new CarpetScriptHost(scriptServer, myCode, false, this); } @Override @@ -62,6 +98,32 @@ public class CarpetScriptHost extends ScriptHost } } + @Override + protected ModuleInterface getModuleByName(String name) + { + ModuleInterface 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) + { + 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; + } + @Override public void delFunction(String funName) { @@ -309,4 +371,43 @@ public class CarpetScriptHost extends ScriptHost } } + public void setChatErrorSnooper(ServerCommandSource source) + { + errorSnooper = (expr, token, message) -> + { + try + { + source.getPlayer(); + } + catch (CommandSyntaxException e) + { + return null; + } + String[] lines = expr.getCodeString().split("\n"); + String shebang = message; + if (lines.length > 1) + { + shebang += " at line "+(token.lineno+1)+", pos "+(token.linepos+1); + } + else { + shebang += " at pos "+(token.pos+1); + } + if (expr.getName() != null) + { + shebang += " in "+expr.getName()+""; + } + 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]); + } + return new ArrayList<>(); + }; + } } diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 0f8639a8..27b8f091 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -8,14 +8,11 @@ import carpet.CarpetSettings; import carpet.CarpetServer; import carpet.script.bundled.FileModule; import carpet.script.bundled.ModuleInterface; -import carpet.script.exception.CarpetExpressionException; -import carpet.script.exception.ExpressionException; import carpet.script.exception.InvalidCallbackException; import carpet.script.value.Value; import carpet.utils.Messenger; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.BlockPos; @@ -51,13 +48,12 @@ public class CarpetScriptServer public CarpetScriptServer() { - globalHost = createMinecraftScriptHost(null, null, false, null); events = new CarpetEventServer(); modules = new HashMap<>(); tickStart = 0L; stopAll = false; - resetErrorSnooper(); holyMoly = CarpetServer.minecraft_server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet()); + globalHost = CarpetScriptHost.create(this, null, false, null); } public void loadAllWorldScripts() @@ -73,7 +69,7 @@ public class CarpetScriptServer } - private ModuleInterface getModule(String name) + public ModuleInterface getModule(String name) { File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile( CarpetServer.minecraft_server.getLevelName(), "scripts"); @@ -129,39 +125,6 @@ public class CarpetScriptServer return modules.get(name); } - - private CarpetScriptHost createMinecraftScriptHost(String name, ModuleInterface module, boolean perPlayer, ServerCommandSource source) - { - CarpetScriptHost host = new CarpetScriptHost(this, name, 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) - { - try - { - String code = module.getCode(); - if (code == null) - { - Messenger.m(source, "r Unable to load "+name+" app - code not found"); - return null; - } - setChatErrorSnooper(source); - CarpetExpression ex = new CarpetExpression(code, source, new BlockPos(0, 0, 0)); - ex.getExpr().asAModule(); - ex.scriptRunCommand(host, new BlockPos(source.getPosition())); - } - catch (CarpetExpressionException e) - { - Messenger.m(source, "r Exception white evaluating expression at " + new BlockPos(source.getPosition()) + ": " + e.getMessage()); - resetErrorSnooper(); - return null; - } - } - return host; - } - public boolean addScriptHost(ServerCommandSource source, String name, boolean perPlayer, boolean autoload) { //TODO add per player modules to support player actions better on a server @@ -172,7 +135,7 @@ public class CarpetScriptServer Messenger.m(source, "r Failed to add "+name+" app"); return false; } - CarpetScriptHost newHost = createMinecraftScriptHost(name, module, perPlayer, source); + CarpetScriptHost newHost = CarpetScriptHost.create(this, module, perPlayer, source); if (newHost == null) { Messenger.m(source, "r Failed to add "+name+" app"); @@ -278,55 +241,6 @@ public class CarpetScriptServer CarpetServer.settingsManager.notifyPlayersCommandsChanged(); } - public void setChatErrorSnooper(ServerCommandSource source) - { - ExpressionException.errorSnooper = (expr, token, message) -> - { - try - { - source.getPlayer(); - } - catch (CommandSyntaxException e) - { - return null; - } - String[] lines = expr.getCodeString().split("\n"); - - String shebang = message; - - if (lines.length > 1) - { - shebang += " at line "+(token.lineno+1)+", pos "+(token.linepos+1); - } - else - { - shebang += " at pos "+(token.pos+1); - } - if (expr.getName() != null) - { - shebang += " in "+expr.getName()+""; - } - 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]); - } - return new ArrayList<>(); - }; - } - public void resetErrorSnooper() - { - ExpressionException.errorSnooper=null; - } - public boolean removeScriptHost(ServerCommandSource source, String name) { name = name.toLowerCase(Locale.ROOT); diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 1aecd2a9..a11b142b 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -11,6 +11,7 @@ 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.exception.BreakStatement; import carpet.script.exception.ContinueStatement; import carpet.script.exception.ExitStatement; @@ -365,10 +366,13 @@ public class Expression implements Cloneable private boolean allowNewlineSubstitutions = true; private boolean allowComments = false; - void asAModule() + public ModuleInterface module = null; + + void asAModule(ModuleInterface mi) { allowNewlineSubstitutions = false; allowComments = true; + module = mi; } private String name; @@ -396,6 +400,7 @@ public class Expression implements Cloneable Expression copy = (Expression) super.clone(); copy.expression = this.expression; copy.name = this.name; + copy.module = this.module; return copy; } @@ -472,14 +477,14 @@ public class Expression implements Cloneable { if (v2 != null) { - throw new ExpressionException(e, token, "Did not expect a second parameter for unary operator"); + throw new ExpressionException(c, e, token, "Did not expect a second parameter for unary operator"); } Value.assertNotNull(v); return lazyfun.apply(c, t, v); } catch (RuntimeException exc) { - throw handleCodeException(exc, e, token); + throw handleCodeException(c, exc, e, token); } } }); @@ -501,7 +506,7 @@ public class Expression implements Cloneable } catch (RuntimeException exc) { - throw handleCodeException(exc, e, t); + throw handleCodeException(c, exc, e, t); } } }); @@ -521,7 +526,7 @@ public class Expression implements Cloneable } catch (RuntimeException exc) { - throw handleCodeException(exc, e, t); + throw handleCodeException(c, exc, e, t); } } }); @@ -542,25 +547,25 @@ public class Expression implements Cloneable } catch (RuntimeException exc) { - throw handleCodeException(exc, e, token); + throw handleCodeException(c, exc, e, token); } } }); } - static RuntimeException handleCodeException(RuntimeException exc, Expression e, Tokenizer.Token token) + static RuntimeException handleCodeException(Context c, RuntimeException exc, Expression e, Tokenizer.Token token) { if (exc instanceof ExitStatement) return exc; if (exc instanceof InternalExpressionException) - return new ExpressionException(e, token, exc.getMessage()); + return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack); if (exc instanceof ArithmeticException) - return new ExpressionException(e, token, "Your math is wrong, "+exc.getMessage()); + return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage()); if (exc instanceof ExpressionException) return exc; // unexpected really - should be caught earlier and converted to InternalExpressionException exc.printStackTrace(); - return new ExpressionException(e, token, "Error while evaluating expression: "+exc); + return new ExpressionException(c, e, token, "Error while evaluating expression: "+exc); } private void addUnaryOperator(String surface, boolean leftAssoc, Function fun) @@ -659,7 +664,7 @@ public class Expression implements Cloneable } catch (RuntimeException exc) { - throw handleCodeException(exc, e, t); + throw handleCodeException(c, exc, e, t); } } }); @@ -668,7 +673,7 @@ public class Expression implements Cloneable { name = name.toLowerCase(Locale.ROOT); if (functions.containsKey(name)) - throw new ExpressionException(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 contextValues = new HashMap<>(); for (String global : globals) { @@ -684,7 +689,7 @@ public class Expression implements Cloneable } if (contextValues.isEmpty()) contextValues = null; - FunctionValue result = new FunctionValue(expr, token, name, code, arguments, contextValues); + FunctionValue result = new FunctionValue(expr, token, module, name, code, arguments, contextValues); // do not store lambda definitions if (!name.equals("_")) context.host.addUserDefinedFunction(name, result); return result; @@ -861,6 +866,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) -> + { + boolean success = c.host.importModule(c, lv.get(0).evalValue(c).getString()); + Value ret = new NumericValue(success); + return (cc, tt) -> ret; + }); + addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) -> { // adjust based on c if (lv.size() == 0) throw new InternalExpressionException("'call' expects at least function name to call"); @@ -3129,12 +3141,12 @@ public class Expression implements Cloneable BasicDataStructures(); } - private List shuntingYard() + private List shuntingYard(Context c) { List outputQueue = new ArrayList<>(); Stack stack = new Stack<>(); - Tokenizer tokenizer = new Tokenizer(this, expression, allowComments, allowNewlineSubstitutions); + Tokenizer tokenizer = new Tokenizer(c, this, expression, allowComments, allowNewlineSubstitutions); // stripping lousy but acceptable semicolons List cleanedTokens = tokenizer.fixSemicolons(); @@ -3154,7 +3166,7 @@ public class Expression implements Cloneable previousToken.type == Tokenizer.Token.TokenType.HEX_LITERAL || previousToken.type == Tokenizer.Token.TokenType.STRINGPARAM)) { - throw new ExpressionException(this, token, "Missing operator"); + throw new ExpressionException(c, this, token, "Missing operator"); } outputQueue.add(token); break; @@ -3168,7 +3180,7 @@ public class Expression implements Cloneable case COMMA: if (previousToken != null && previousToken.type == Tokenizer.Token.TokenType.OPERATOR) { - throw new ExpressionException(this, previousToken, "Missing parameter(s) for operator "); + throw new ExpressionException(c, this, previousToken, "Missing parameter(s) for operator "); } while (!stack.isEmpty() && stack.peek().type != Tokenizer.Token.TokenType.OPEN_PAREN) { @@ -3178,11 +3190,11 @@ public class Expression implements Cloneable { if (lastFunction == null) { - throw new ExpressionException(this, token, "Unexpected comma"); + throw new ExpressionException(c, this, token, "Unexpected comma"); } else { - throw new ExpressionException(this, lastFunction, "Parse error for function"); + throw new ExpressionException(c, this, lastFunction, "Parse error for function"); } } break; @@ -3191,12 +3203,12 @@ public class Expression implements Cloneable if (previousToken != null && (previousToken.type == Tokenizer.Token.TokenType.COMMA || previousToken.type == Tokenizer.Token.TokenType.OPEN_PAREN)) { - throw new ExpressionException(this, token, "Missing parameter(s) for operator '" + token+"'"); + throw new ExpressionException(c, this, token, "Missing parameter(s) for operator '" + token+"'"); } ILazyOperator o1 = operators.get(token.surface); if (o1 == null) { - throw new ExpressionException(this, token, "Unknown operator '" + token + "'"); + throw new ExpressionException(c, this, token, "Unknown operator '" + token + "'"); } shuntOperators(outputQueue, stack, o1); @@ -3208,12 +3220,12 @@ public class Expression implements Cloneable if (previousToken != null && previousToken.type != Tokenizer.Token.TokenType.OPERATOR && previousToken.type != Tokenizer.Token.TokenType.COMMA && previousToken.type != Tokenizer.Token.TokenType.OPEN_PAREN) { - throw new ExpressionException(this, token, "Invalid position for unary operator " + token ); + throw new ExpressionException(c, this, token, "Invalid position for unary operator " + token ); } ILazyOperator o1 = operators.get(token.surface); if (o1 == null) { - throw new ExpressionException(this, token, "Unknown unary operator '" + token.surface.substring(0, token.surface.length() - 1) + "'"); + throw new ExpressionException(c, this, token, "Unknown unary operator '" + token.surface.substring(0, token.surface.length() - 1) + "'"); } shuntOperators(outputQueue, stack, o1); @@ -3245,7 +3257,7 @@ public class Expression implements Cloneable case CLOSE_PAREN: if (previousToken != null && previousToken.type == Tokenizer.Token.TokenType.OPERATOR) { - throw new ExpressionException(this, previousToken, "Missing parameter(s) for operator " + previousToken); + throw new ExpressionException(c, this, previousToken, "Missing parameter(s) for operator " + previousToken); } while (!stack.isEmpty() && stack.peek().type != Tokenizer.Token.TokenType.OPEN_PAREN) { @@ -3253,7 +3265,7 @@ public class Expression implements Cloneable } if (stack.isEmpty()) { - throw new ExpressionException("Mismatched parentheses"); + throw new ExpressionException(c, "Mismatched parentheses"); } stack.pop(); if (!stack.isEmpty() && stack.peek().type == Tokenizer.Token.TokenType.FUNCTION) @@ -3277,7 +3289,7 @@ public class Expression implements Cloneable Tokenizer.Token element = stack.pop(); if (element.type == Tokenizer.Token.TokenType.OPEN_PAREN || element.type == Tokenizer.Token.TokenType.CLOSE_PAREN) { - throw new ExpressionException(this, element, "Mismatched parentheses"); + throw new ExpressionException(c, this, element, "Mismatched parentheses"); } outputQueue.add(element); } @@ -3306,7 +3318,7 @@ public class Expression implements Cloneable { if (ast == null) { - ast = getAST(); + ast = getAST(c); } return evalValue(() -> ast, c, expectedType); } @@ -3319,7 +3331,7 @@ public class Expression implements Cloneable } catch (ContinueStatement | BreakStatement | ReturnStatement exc) { - throw new ExpressionException("Control flow functions, like continue, break, or return, should only be used in loops and functions respectively."); + throw new ExpressionException(c, "Control flow functions, like continue, break, or return, should only be used in loops and functions respectively."); } catch (ExitStatement exit) { @@ -3327,23 +3339,23 @@ public class Expression implements Cloneable } catch (StackOverflowError ignored) { - throw new ExpressionException("Your thoughts are too deep"); + throw new ExpressionException(c, "Your thoughts are too deep"); } catch (InternalExpressionException exc) { - throw new ExpressionException("Your expression result is incorrect:"+exc.getMessage()); + throw new ExpressionException(c, "Your expression result is incorrect:"+exc.getMessage()); } catch (ArithmeticException exc) { - throw new ExpressionException("The final result is incorrect, "+exc.getMessage()); + throw new ExpressionException(c, "The final result is incorrect, "+exc.getMessage()); } } - private LazyValue getAST() + private LazyValue getAST(Context context) { Stack stack = new Stack<>(); - List rpn = shuntingYard(); - validate(rpn); + List rpn = shuntingYard(context); + validate(context, rpn); for (final Tokenizer.Token token : rpn) { switch (token.type) @@ -3415,7 +3427,7 @@ public class Expression implements Cloneable } catch (NumberFormatException exception) { - throw new ExpressionException(this, token, "Not a number"); + throw new ExpressionException(c, this, token, "Not a number"); } }); break; @@ -3426,13 +3438,13 @@ public class Expression implements Cloneable stack.push((c, t) -> new NumericValue(new BigInteger(token.surface.substring(2), 16).doubleValue())); break; default: - throw new ExpressionException(this, token, "Unexpected token '" + token.surface + "'"); + throw new ExpressionException(context, this, token, "Unexpected token '" + token.surface + "'"); } } return stack.pop(); } - private void validate(List rpn) + private void validate(Context c, List rpn) { /*- * Thanks to Norman Ramsey: @@ -3454,7 +3466,7 @@ public class Expression implements Cloneable case UNARY_OPERATOR: if (stack.peek() < 1) { - throw new ExpressionException(this, token, "Missing parameter(s) for operator " + token); + throw new ExpressionException(c, this, token, "Missing parameter(s) for operator " + token); } break; case OPERATOR: @@ -3462,9 +3474,9 @@ public class Expression implements Cloneable { if (token.surface.equalsIgnoreCase(";")) { - throw new ExpressionException(this, token, "Unnecessary semicolon"); + throw new ExpressionException(c, this, token, "Unnecessary semicolon"); } - throw new ExpressionException(this, token, "Missing parameter(s) for operator " + token); + throw new ExpressionException(c, this, token, "Missing parameter(s) for operator " + token); } // pop the operator's 2 parameters and add the result stack.set(stack.size() - 1, stack.peek() - 2 + 1); @@ -3474,11 +3486,11 @@ public class Expression implements Cloneable int numParams = stack.pop(); if (f != null && !f.numParamsVaries() && numParams != f.getNumParams()) { - throw new ExpressionException(this, token, "Function " + token + " expected " + f.getNumParams() + " parameters, got " + numParams); + throw new ExpressionException(c, this, token, "Function " + token + " expected " + f.getNumParams() + " parameters, got " + numParams); } if (stack.size() <= 0) { - throw new ExpressionException(this, token, "Too many function calls, maximum scope exceeded"); + throw new ExpressionException(c, this, token, "Too many function calls, maximum scope exceeded"); } // push the result of the function stack.set(stack.size() - 1, stack.peek() + 1); @@ -3493,15 +3505,15 @@ public class Expression implements Cloneable if (stack.size() > 1) { - throw new ExpressionException("Too many unhandled function parameter lists"); + throw new ExpressionException(c, "Too many unhandled function parameter lists"); } else if (stack.peek() > 1) { - throw new ExpressionException("Too many numbers or variables"); + throw new ExpressionException(c, "Too many numbers or variables"); } else if (stack.peek() < 1) { - throw new ExpressionException("Empty expression"); + throw new ExpressionException(c, "Empty expression"); } } diff --git a/src/main/java/carpet/script/ExpressionInspector.java b/src/main/java/carpet/script/ExpressionInspector.java index 627c0bd2..3eddc45c 100644 --- a/src/main/java/carpet/script/ExpressionInspector.java +++ b/src/main/java/carpet/script/ExpressionInspector.java @@ -26,16 +26,16 @@ public class ExpressionInspector return e.getName(); } - public static Expression Expression_cloneWithName(Expression e, String name, Tokenizer.Token token) + public static Expression Expression_cloneWithName(Expression e, Context c, String name, Tokenizer.Token token) { - Expression copy = null; + Expression copy; try { copy = e.clone(); } catch (CloneNotSupportedException ex) { - throw new ExpressionException(e, token, "Problems in allocating global function "+name); + throw new ExpressionException(c, e, token, "Problems in allocating global function "+name); } return copy.withName(name); } diff --git a/src/main/java/carpet/script/Fluff.java b/src/main/java/carpet/script/Fluff.java index 77dc72b1..0546d2aa 100644 --- a/src/main/java/carpet/script/Fluff.java +++ b/src/main/java/carpet/script/Fluff.java @@ -112,7 +112,7 @@ public abstract class Fluff } catch (RuntimeException exc) { - throw Expression.handleCodeException(exc, e, t); + throw Expression.handleCodeException(cc, exc, e, t); } } } @@ -146,7 +146,7 @@ public abstract class Fluff } @Override - public LazyValue lazyEval(Context c_ignored, Integer type, Expression e, Tokenizer.Token t, final LazyValue v1, final LazyValue v2) + public LazyValue lazyEval(Context cc, Integer type, Expression e, Tokenizer.Token t, final LazyValue v1, final LazyValue v2) { try { @@ -154,7 +154,7 @@ public abstract class Fluff } catch (RuntimeException exc) { - throw Expression.handleCodeException(exc, e, t); + throw Expression.handleCodeException(cc, exc, e, t); } } } @@ -172,20 +172,20 @@ public abstract class Fluff { if (v2 != null) { - throw new ExpressionException(e, t, "Did not expect a second parameter for unary operator"); + throw new ExpressionException(cc, e, t, "Did not expect a second parameter for unary operator"); } return (c, ignored_type) -> AbstractUnaryOperator.this.evalUnary(v1.evalValue(c)); } catch (RuntimeException exc) { - throw Expression.handleCodeException(exc, e, t); + throw Expression.handleCodeException(cc, exc, e, t); } } @Override public Value eval(Value v1, Value v2) { - throw new ExpressionException("Shouldn't end up here"); + throw new RuntimeException("Shouldn't end up here"); } public abstract Value evalUnary(Value v1); diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 1a5c58d6..619e82ac 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -5,30 +5,36 @@ import carpet.script.value.FunctionValue; import carpet.script.value.Value; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; -public class ScriptHost +public abstract class ScriptHost { protected final Map userHosts = new Object2ObjectOpenHashMap<>(); public Map globalFunctions = new Object2ObjectOpenHashMap<>(); public Map globalVariables = new Object2ObjectOpenHashMap<>(); + private Set imports = new HashSet<>(); protected ScriptHost parent; protected boolean perUser; - private String name; - public String getName() {return name;} + //private String name; + public String getName() {return myCode==null?null:myCode.getName();} protected final ModuleInterface myCode; - ScriptHost(String name, ModuleInterface code, boolean perUser, ScriptHost parent) + public Fluff.TriFunction> errorSnooper = null; + + ScriptHost(ModuleInterface code, boolean perUser, ScriptHost parent) { this.parent = parent; - this.name = name; + //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); @@ -46,6 +52,24 @@ public class ScriptHost globalFunctions.put(funName, function); } + public boolean 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)) + { + return true; + } + imports.remove(module.getName()); + return false; + } + + protected abstract ModuleInterface getModuleByName(String name); // this should be shell out in the executuor + + protected abstract boolean runModuleCode(Context c, ModuleInterface module); // this should be shell out in the executuor + public void delFunction(String funName) { globalFunctions.remove(funName); @@ -66,31 +90,20 @@ public class ScriptHost userHost = this.duplicate(); userHost.globalVariables.putAll(this.globalVariables); userHost.globalFunctions.putAll(this.globalFunctions); + userHost.imports.addAll(this.imports); userHosts.put(user, userHost); return userHost; } - protected ScriptHost duplicate() - { - return new ScriptHost(this.name, myCode, false, this); - } - - public Expression getExpressionForFunction(String name) - { - return globalFunctions.get(name).getExpression(); - } - public Tokenizer.Token getTokenForFunction(String name) - { - return globalFunctions.get(name).getToken(); - } + protected abstract ScriptHost duplicate(); public List getPublicFunctions() { - return globalFunctions.keySet().stream().filter((str) -> !str.startsWith("_")).collect(Collectors.toList()); + 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("__")).collect(Collectors.toList()); + return globalFunctions.keySet().stream().filter((str) -> all || !str.startsWith("__")).sorted().collect(Collectors.toList()); } public void onClose() { } @@ -99,4 +112,9 @@ public class ScriptHost { perUser = isPerUser; } + + public void resetErrorSnooper() + { + errorSnooper=null; + } } diff --git a/src/main/java/carpet/script/Tokenizer.java b/src/main/java/carpet/script/Tokenizer.java index ef81a5a4..779e6743 100644 --- a/src/main/java/carpet/script/Tokenizer.java +++ b/src/main/java/carpet/script/Tokenizer.java @@ -32,11 +32,13 @@ public class Tokenizer implements Iterator private Token previousToken; private Expression expression; + private Context context; - Tokenizer(Expression expr, String input, boolean allowComments, boolean allowNewLineMakers) + Tokenizer(Context c, Expression expr, String input, boolean allowComments, boolean allowNewLineMakers) { this.input = input; this.expression = expr; + this.context = c; this.comments = allowComments; this.newLinesMarkers = allowNewLineMakers; } @@ -89,7 +91,7 @@ public class Tokenizer implements Iterator || (ch >= 'A' && ch <= 'F'); } - public static boolean isSemicolon(Token tok) + private static boolean isSemicolon(Token tok) { return ( tok.type == Token.TokenType.OPERATOR && tok.surface.equals(";") ) || (tok.type == Token.TokenType.UNARY_OPERATOR && tok.surface.equals(";u") ); @@ -97,7 +99,7 @@ public class Tokenizer implements Iterator public static List simplepass(String input) { - Tokenizer tok = new Tokenizer(null, input, false, false); + Tokenizer tok = new Tokenizer(null, null, input, false, false); List res = new ArrayList<>(); while (tok.hasNext()) res.add(tok.next()); return res; @@ -157,8 +159,8 @@ public class Tokenizer implements Iterator pos++; linepos++; token.type = Token.TokenType.STRINGPARAM; - if (pos == input.length() && expression != null) - throw new ExpressionException(this.expression, token, "Program truncated"); + if (pos == input.length() && expression != null && context != null) + throw new ExpressionException(context, this.expression, token, "Program truncated"); ch = input.charAt(pos); while (ch != '\'') { @@ -166,13 +168,13 @@ public class Tokenizer implements Iterator { pos++; linepos++; - if (pos == input.length() && expression != null) - throw new ExpressionException(this.expression, token, "Program truncated"); + if (pos == input.length() && expression != null && context != null) + throw new ExpressionException(context, this.expression, token, "Program truncated"); } token.append(input.charAt(pos++)); linepos++; - if (pos == input.length() && expression != null) - throw new ExpressionException(this.expression, token, "Program truncated"); + if (pos == input.length() && expression != null && context != null) + throw new ExpressionException(context, this.expression, token, "Program truncated"); ch = input.charAt(pos); } pos++; @@ -224,12 +226,12 @@ public class Tokenizer implements Iterator pos++; linepos++; - if (expression != null && previousToken != null && + if (expression != null && context != null && previousToken != null && previousToken.type == Token.TokenType.OPERATOR && (ch == ')' || ch == ',') && !previousToken.surface.equalsIgnoreCase(";") ) - throw new ExpressionException(this.expression, previousToken, + throw new ExpressionException(context, this.expression, previousToken, "Can't have operator " + previousToken.surface + " at the end of a subexpression"); } else @@ -300,7 +302,7 @@ public class Tokenizer implements Iterator token.type = Token.TokenType.OPERATOR; } } - if (expression != null && previousToken != null && + if (expression != null && context != null && previousToken != null && ( token.type == Token.TokenType.LITERAL || token.type == Token.TokenType.HEX_LITERAL || @@ -317,7 +319,7 @@ public class Tokenizer implements Iterator ) ) { - throw new ExpressionException(this.expression, previousToken, "'"+token.surface +"' is not allowed after '"+previousToken.surface+"'"); + throw new ExpressionException(context, this.expression, previousToken, "'"+token.surface +"' is not allowed after '"+previousToken.surface+"'"); } return previousToken = token; } diff --git a/src/main/java/carpet/script/bundled/BundledModule.java b/src/main/java/carpet/script/bundled/BundledModule.java index a21890db..880157c9 100644 --- a/src/main/java/carpet/script/bundled/BundledModule.java +++ b/src/main/java/carpet/script/bundled/BundledModule.java @@ -4,6 +4,7 @@ import org.apache.commons.io.IOUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Locale; public class BundledModule implements ModuleInterface { @@ -13,9 +14,9 @@ public class BundledModule implements ModuleInterface { try { - name = scriptName; + name = scriptName.toLowerCase(Locale.ROOT); code = IOUtils.toString( - getClass().getClassLoader().getResourceAsStream("assets/carpet/scripts/"+scriptName+".sc"), + getClass().getClassLoader().getResourceAsStream("assets/carpet/scripts/"+name+".sc"), StandardCharsets.UTF_8 ); } diff --git a/src/main/java/carpet/script/exception/CarpetExpressionException.java b/src/main/java/carpet/script/exception/CarpetExpressionException.java index 0bd2df69..2c84bfa4 100644 --- a/src/main/java/carpet/script/exception/CarpetExpressionException.java +++ b/src/main/java/carpet/script/exception/CarpetExpressionException.java @@ -1,9 +1,28 @@ package carpet.script.exception; -public class CarpetExpressionException extends ExpressionException +import carpet.script.value.FunctionValue; +import carpet.utils.Messenger; +import net.minecraft.server.command.ServerCommandSource; + +import java.util.List; + +public class CarpetExpressionException extends RuntimeException { - public CarpetExpressionException(String message) + public List stack; + + public CarpetExpressionException(String message, List stack) { super(message); + this.stack = stack; + } + public void printStack(ServerCommandSource source) + { + if (stack != null && !stack.isEmpty()) + { + for (FunctionValue fun : stack) + { + Messenger.m(source, "e ... in "+fun.fullName(), "e /"+(fun.getToken().lineno+1)+":"+(fun.getToken().linepos+1)); + } + } } } diff --git a/src/main/java/carpet/script/exception/ExpressionException.java b/src/main/java/carpet/script/exception/ExpressionException.java index bb392052..fc475481 100644 --- a/src/main/java/carpet/script/exception/ExpressionException.java +++ b/src/main/java/carpet/script/exception/ExpressionException.java @@ -1,8 +1,10 @@ package carpet.script.exception; +import carpet.script.Context; import carpet.script.Expression; import carpet.script.Fluff; import carpet.script.Tokenizer; +import carpet.script.value.FunctionValue; import java.util.ArrayList; import java.util.List; @@ -13,38 +15,58 @@ import static carpet.script.ExpressionInspector.Expression_getName; /* The expression evaluators exception class. */ public class ExpressionException extends RuntimeException { - public ExpressionException(String message) + public Context context; + public List stack = new ArrayList<>(); + public ExpressionException(Context c, String message) { - super(message); + super(makeMessage(c, null, null, message)); + context = c; + } + + public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message) + { + super(makeMessage(c, e, t, message)); + context = c; + } + public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message, List stack) + { + super(makeMessage(c, e, t, message)); + this.stack.addAll(stack); + context = c; } private static Fluff.TriFunction> errorMaker = (expr, token, errmessage) -> { - List snippet = Expression_getExpressionSnippet(token, expr); - List errMsg = new ArrayList<>(snippet); - if (snippet.size() != 1) + List errMsg = new ArrayList<>(); + if (expr != null && token != null) { - errmessage+= " at line "+(token.lineno+1)+", pos "+(token.linepos+1); - } - else - { - errmessage += " at pos "+(token.pos+1); - } - if (Expression_getName(expr) != null) - { - errmessage += " ("+Expression_getName(expr)+")"; + List snippet = Expression_getExpressionSnippet(token, expr); + errMsg.addAll(snippet); + + if (snippet.size() != 1) + { + errmessage += " at line " + (token.lineno + 1) + ", pos " + (token.linepos + 1); + } + else + { + errmessage += " at pos " + (token.pos + 1); + } + if (Expression_getName(expr) != null) + { + errmessage += " (" + Expression_getName(expr) + ")"; + } + } errMsg.add(errmessage); return errMsg; }; - public static Fluff.TriFunction> errorSnooper = null; - static String makeMessage(Expression e, Tokenizer.Token t, String message) throws ExpressionException + static String makeMessage(Context c, Expression e, Tokenizer.Token t, String message) throws ExpressionException { - if (errorSnooper != null) + if (c.host.errorSnooper != null) { - List alternative = errorSnooper.apply(e, t, message); + List alternative = c.host.errorSnooper.apply(e, t, message); if (alternative!= null) { return String.join("\n", alternative); @@ -52,9 +74,4 @@ public class ExpressionException extends RuntimeException } return String.join("\n", errorMaker.apply(e, t, message)); } - - public ExpressionException(Expression e, Tokenizer.Token t, String message) - { - super(makeMessage(e, t, message)); - } } diff --git a/src/main/java/carpet/script/exception/InternalExpressionException.java b/src/main/java/carpet/script/exception/InternalExpressionException.java index eabe7b05..6efb96c6 100644 --- a/src/main/java/carpet/script/exception/InternalExpressionException.java +++ b/src/main/java/carpet/script/exception/InternalExpressionException.java @@ -1,8 +1,14 @@ package carpet.script.exception; +import carpet.script.value.FunctionValue; + +import java.util.ArrayList; +import java.util.List; + /* The internal expression evaluators exception class. */ -public class InternalExpressionException extends ExpressionException +public class InternalExpressionException extends RuntimeException { + public List stack = new ArrayList<>(); public InternalExpressionException(String message) { super(message); diff --git a/src/main/java/carpet/script/value/FrameworkValue.java b/src/main/java/carpet/script/value/FrameworkValue.java index cae758ab..882c79ec 100644 --- a/src/main/java/carpet/script/value/FrameworkValue.java +++ b/src/main/java/carpet/script/value/FrameworkValue.java @@ -1,25 +1,23 @@ package carpet.script.value; -import carpet.script.exception.ExpressionException; - public abstract class FrameworkValue extends Value { @Override public String getString() { - throw new ExpressionException("Scarpet language component cannot be used"); + throw new UnsupportedOperationException("Scarpet language component cannot be used"); } @Override public boolean getBoolean() { - throw new ExpressionException("Scarpet language component cannot be used"); + throw new UnsupportedOperationException("Scarpet language component cannot be used"); } @Override public Value clone() { - throw new ExpressionException("Scarpet language component cannot be used"); + throw new UnsupportedOperationException("Scarpet language component cannot be used"); } @Override diff --git a/src/main/java/carpet/script/value/FunctionValue.java b/src/main/java/carpet/script/value/FunctionValue.java index 0c1e59a7..13b2c7dc 100644 --- a/src/main/java/carpet/script/value/FunctionValue.java +++ b/src/main/java/carpet/script/value/FunctionValue.java @@ -6,6 +6,7 @@ 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; @@ -14,6 +15,7 @@ import carpet.script.exception.InternalExpressionException; import carpet.script.exception.ReturnStatement; import carpet.script.exception.ThrowStatement; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -22,15 +24,17 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction private Expression expression; private Tokenizer.Token token; private String name; + private String containedPackage; private LazyValue body; private Map outerState; private List args; private static long variantCounter = 1; private long variant; - public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args) + private FunctionValue(Expression expression, Tokenizer.Token token, String cpackage, String name, LazyValue body, List args) { this.expression = expression; + this.containedPackage = cpackage; this.token = token; this.name = name; this.body = body; @@ -39,9 +43,10 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction variant = 0L; } - public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List args, Map outerState) + public FunctionValue(Expression expression, Tokenizer.Token token, ModuleInterface code, String name, LazyValue body, List args, Map outerState) { this.expression = expression; + this.containedPackage = code==null?null:code.getName(); this.token = token; this.name = name; this.body = body; @@ -56,6 +61,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction return name; } + public String fullName() {return containedPackage == null?name:name+"["+containedPackage+"]";} + @Override public boolean getBoolean() { @@ -65,7 +72,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction @Override protected Value clone() { - FunctionValue ret = new FunctionValue(expression, token, name, body, args); + FunctionValue ret = new FunctionValue(expression, token, containedPackage, name, body, args); ret.outerState = this.outerState; ret.variant = this.variant; return ret; @@ -124,19 +131,26 @@ 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 - Expression callContext = ExpressionInspector.Expression_cloneWithName(callingExpression, name, t); + // 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, e, t, lazyParams); + return lazyEval(c, type, callContext, t, lazyParams); + } + catch (ExpressionException exc) + { + exc.stack.add(this); + throw exc; } catch (InternalExpressionException exc) { - throw new ExpressionException(callContext, t, exc.getMessage()); + exc.stack.add(this); + throw new ExpressionException(c, callContext, t, exc.getMessage(), exc.stack); } + catch (ArithmeticException exc) { - throw new ExpressionException(callContext, t, "Your math is wrong, "+exc.getMessage()); + throw new ExpressionException(c, callContext, t, "Your math is wrong, "+exc.getMessage(), Collections.singletonList(this)); } } @@ -146,7 +160,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction if (args.size() != lazyParams.size()) // something that might be subject to change in the future // in case we add variable arguments { - throw new ExpressionException(e, t, + throw new ExpressionException(c, e, t, "Incorrect number of arguments for function "+name+ ". Should be "+args.size()+", not "+lazyParams.size()+" like "+args ); @@ -168,7 +182,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction } catch (BreakStatement | ContinueStatement exc) { - throw new ExpressionException(e, t, "'continue' and 'break' can only be called inside loop function bodies"); + throw new ExpressionException(c, e, t, "'continue' and 'break' can only be called inside loop function bodies"); } catch (ReturnStatement returnStatement) { @@ -179,13 +193,13 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction retVal = throwStatement.retval; rethrow = true; } - catch (ArithmeticException | ExitStatement | ExpressionException exc) + catch (ArithmeticException | ExitStatement | ExpressionException | InternalExpressionException exc ) { throw exc; // rethrow so could be contextualized if needed } catch (Exception exc) { - throw new ExpressionException(e, t, "Error while evaluating expression: "+exc.getMessage()); + throw new ExpressionException(c, e, t, "Error while evaluating expression: "+exc.getMessage()); } if (rethrow) {