diff --git a/src/main/java/carpet/script/exception/ExpressionException.java b/src/main/java/carpet/script/exception/ExpressionException.java index ec72fccb..ab9ec2e9 100644 --- a/src/main/java/carpet/script/exception/ExpressionException.java +++ b/src/main/java/carpet/script/exception/ExpressionException.java @@ -37,6 +37,14 @@ public class ExpressionException extends RuntimeException implements ResolvedExc context = c; } + public ExpressionException(Context c, Expression e, Tokenizer.Token t, Supplier messageSupplier, List stack) + { + super("Error"); + this.stack.addAll(stack); + lazyStacktrace = messageSupplier; + context = c; + } + private static final Fluff.TriFunction> errorMaker = (expr, /*Nullable*/ token, errmessage) -> { diff --git a/src/main/java/carpet/script/exception/ProcessedThrowStatement.java b/src/main/java/carpet/script/exception/ProcessedThrowStatement.java index 003b71b0..977cf51f 100644 --- a/src/main/java/carpet/script/exception/ProcessedThrowStatement.java +++ b/src/main/java/carpet/script/exception/ProcessedThrowStatement.java @@ -6,14 +6,15 @@ import carpet.script.Context; import carpet.script.Expression; import carpet.script.Tokenizer.Token; import carpet.script.value.FunctionValue; +import carpet.script.value.Value; public class ProcessedThrowStatement extends ExpressionException { - public final Throwables.Exception exception; - public final String message; + public final Throwables thrownExceptionType; + public final Value data; - public ProcessedThrowStatement(Context c, Expression e, Token token, String message, List stack, Throwables.Exception exception) { - super(c, e, token, "Unhandled exception: "+message, stack); - this.exception = exception; - this.message = message; + public ProcessedThrowStatement(Context c, Expression e, Token token, List stack, Throwables thrownExceptionType, Value data) { + super(c, e, token, () -> "Unhandled "+thrownExceptionType.getId()+" exception: "+data.getString(), stack); + this.thrownExceptionType = thrownExceptionType; + this.data = data; } } diff --git a/src/main/java/carpet/script/exception/ThrowStatement.java b/src/main/java/carpet/script/exception/ThrowStatement.java index 527befe1..01bf9ccf 100644 --- a/src/main/java/carpet/script/exception/ThrowStatement.java +++ b/src/main/java/carpet/script/exception/ThrowStatement.java @@ -3,35 +3,25 @@ package carpet.script.exception; import carpet.script.Context; import carpet.script.Expression; import carpet.script.Tokenizer.Token; +import carpet.script.value.StringValue; import carpet.script.value.Value; -import static carpet.script.exception.Throwables.Exception; - public class ThrowStatement extends InternalExpressionException { - private final Exception exception; - /** - * Creates a throw exception from a value. - * That value will also be used as the message.
- * To be used when throwing from Scarpet's {@code throw} function with a single argument - * @param value The value to pass - */ - public ThrowStatement(Value value) - { - this(value, value, Value.NULL); - } + private final Throwables thrownExceptionType; + private final Value exceptionData; /** * Creates a throw exception from a value, and assigns it a specified message. *

To be used when throwing from Scarpet's {@code throw} function - * @param message The message to display if uncaught - * @param value The value to pass - * @param parent A parent's name + * @param data The value to pass + * @param type Exception type */ - public ThrowStatement(Value message, Value value, Value parent) + public ThrowStatement(Value data, Throwables type) { - super(message.getString()); - exception = new Exception(value, parent); + super(type.getId()); + exceptionData = data; + thrownExceptionType = type; } /** @@ -39,20 +29,19 @@ public class ThrowStatement extends InternalExpressionException * Conveniently creates a value from the {@code value} String * to be used easily in Java code * @param message The message to display when not handled - * @param exception An {@link Exception} containing the inheritance data + * @param thrownExceptionType An {@link Throwables} containing the inheritance data * for this exception. When throwing from Java, - * those exceptions should be pre-registered and used - * instead of creating new ones each time. - * @param parent This exception's data + * those exceptions should be pre-registered. */ - public ThrowStatement(String message, Exception exception) + public ThrowStatement(String message, Throwables thrownExceptionType) { super(message); - this.exception = exception; + this.exceptionData = StringValue.of(message); + this.thrownExceptionType = thrownExceptionType; } @Override public ExpressionException promote(Context c, Expression e, Token token) { - return new ProcessedThrowStatement(c, e, token, getMessage(), stack, exception); + return new ProcessedThrowStatement(c, e, token, stack, thrownExceptionType, exceptionData); } } diff --git a/src/main/java/carpet/script/exception/Throwables.java b/src/main/java/carpet/script/exception/Throwables.java index ba42f993..136aa09e 100644 --- a/src/main/java/carpet/script/exception/Throwables.java +++ b/src/main/java/carpet/script/exception/Throwables.java @@ -1,129 +1,98 @@ package carpet.script.exception; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; - -import carpet.script.value.NullValue; -import carpet.script.value.StringValue; -import carpet.script.value.Value; +import java.util.Set; /** - * This class contains default Scarpet catchable exception's ids, as well as their inheritance and + * This class contains default Scarpet catchable types, as well as their inheritance and * methods to check whether filters are compatible with those. - * - * Exceptions here cannot be thrown as an actual {@link Throwable}, they are just part of the metadata for - * {@link ThrowStatement} and {@link ProcessedThrowStatement} */ public class Throwables { - protected static final Map byValue = new HashMap<>(); - public static final Exception EXCEPTION = register("exception", null); - public static final Exception VALUE_EXCEPTION = register("value_exception", EXCEPTION); - public static final Exception UNKNOWN_ITEM = register("unknown_item", VALUE_EXCEPTION); - public static final Exception UNKNOWN_BLOCK = register("unknown_block", VALUE_EXCEPTION); - public static final Exception UNKNOWN_BIOME = register("unknown_biome", VALUE_EXCEPTION); - public static final Exception UNKNOWN_SOUND = register("unknown_sound", VALUE_EXCEPTION); - public static final Exception UNKNOWN_PARTICLE = register("unknown_particle", VALUE_EXCEPTION); - public static final Exception UNKNOWN_POI_TYPE = register("unknown_poi_type", VALUE_EXCEPTION); - public static final Exception UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION); - public static final Exception UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION); - public static final Exception UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION); - public static final Exception FILE_READ_EXCEPTION = register("file_read_exception", EXCEPTION); - public static final Exception NBT_READ = register("nbt_read_exception", FILE_READ_EXCEPTION); - public static final Exception JSON_READ = register("json_read_exception", FILE_READ_EXCEPTION); - public static final Exception USER_DEFINED = register("user_defined_exception", EXCEPTION); - protected static final Exception ERROR = register("error", null); - - private Throwables() {} - + private final String id; + private final Throwables parent; + + private static final Map byValue = new HashMap<>(); + private static final Map> handles = new HashMap<>(); + + public static final Throwables THROWN_EXCEPTION_TYPE = register("exception", null); + public static final Throwables VALUE_EXCEPTION = register("value_exception", THROWN_EXCEPTION_TYPE); + public static final Throwables UNKNOWN_ITEM = register("unknown_item", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_BLOCK = register("unknown_block", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_BIOME = register("unknown_biome", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_SOUND = register("unknown_sound", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_PARTICLE = register("unknown_particle", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_POI_TYPE = register("unknown_poi_type", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION); + public static final Throwables UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION); + public static final Throwables FILE_READ_EXCEPTION = register("file_read_exception", THROWN_EXCEPTION_TYPE); + public static final Throwables NBT_READ = register("nbt_read_exception", FILE_READ_EXCEPTION); + public static final Throwables JSON_READ = register("json_read_exception", FILE_READ_EXCEPTION); + public static final Throwables USER_DEFINED = register("user_defined_exception", THROWN_EXCEPTION_TYPE); + /** * Creates an exception and registers it to be used as parent for * user defined exceptions in Scarpet's throw function. - *

Scarpet exceptions should, in general, have a top-level parent being {@link Throwables#EXCEPTION} - * @param value The value for the exception as a {@link String}. Will be converted into a {@link StringValue} + *

Scarpet exceptions should have a top-level parent being {@link Throwables#THROWN_EXCEPTION_TYPE} + * @param id The value for the exception as a {@link String}. * @param parent The parent of the exception being created, or null if top-level * @return The created exception */ - public static Exception register(String value, Exception parent) + public static Throwables register(String id, Throwables parent) { - Exception exc = new Exception(value, parent); - byValue.put(exc.getValue(), exc); + Throwables exc = new Throwables(id, parent); + byValue.put(id, exc); + Set itHandles = new HashSet<>(); + itHandles.add(exc); + while (parent!=null) + { + handles.get(parent.id).add(exc); + parent = parent.parent; + } + handles.put(id, itHandles); return exc; } - + /** - * The data of a {@link ThrowStatement} exception. Supports creating with inheritance and - * checking whether a filter is compatible with them. + * Creates a new exception. + *

Not suitable for creating exceptions that can't be caught. + * Use an {@link InternalExpressionException} for that + * @param id The exception's value as a {@link String} */ - public static class Exception { - private final Value value; - private final Exception parent; - private final boolean isError; - - /** - * Creates a new exception, with an optional parent. - *

To be used in Scarpet's throw command - *

Parent will default to {@link Throwables#USER_DEFINED} if provided - * parentValue meets Value#isNull, else it will create a new {@link Exception}, - * with its parent being {@link Throwables#USER_DEFINED}. - *

If passed parent matches {@link Throwables#ERROR}, it will create an error - * exception, which cannot be caught. - * @param value The {@link Value} of the exception - * @param parentValue An optional {@link Value} matching the name of a parent exception. - *
Accepts a {@link NullValue}, but not a {@code null} - */ - public Exception(Value value, Value parentValue) - { - if (byValue.containsKey(value)) - throw new InternalExpressionException("Exception value can't be the same as existing exception. Use it as parent instead"); - this.value = value; - if (!parentValue.isNull()) - { - Exception possibleParent = byValue.get(parentValue); - parent = possibleParent == null ? new Exception(parentValue, Value.NULL) : possibleParent; - } else - parent = USER_DEFINED; - isError = this.parent == ERROR; - } - - /** - * Creates a new exception. - *

Not suitable for creating exceptions that can't be caught. - * Use an {@link InternalExpressionException} for that - * @param value The exception's value as a {@link String} - * @param parent The parent exception, or null if no parent is available - */ - protected Exception(String value, Exception parent) - { - this.value = StringValue.of(value); - this.parent = parent; - this.isError = false; - } - - /** - * Checks whether the given filter matches an instance of this exception, by checking equality - * with itself and possible parents. - * @param filter The {@link Value} to check against - * @return Whether or not the given value matches this exception's hierarchy - */ - public boolean isInstance(Value filter) { - return getValue().equals(filter) || (parent != null && parent.isInstance(filter)); - } - - /** - * Returns the {@link Value} provided when creating this exception - * @return The {@link Value} of this exception - */ - public Value getValue() { - return value; - } - - /** - * Returns whether this exception has been defined as an error. - *

Error exceptions cannot be caught in Scarpet - * @return Whether this exception has been defined as an error - */ - public boolean isError() { - return isError; - } + protected Throwables(String id, Throwables parent) + { + this.id = id; + this.parent = parent; + } + + public static Throwables getTypeForException(String type) + { + Throwables properType = byValue.get(type); + if (properType == null) throw new InternalExpressionException("Unknown exception type: "+type); + return properType; + } + + + /** + * Checks whether the given filter matches an instance of this exception, by checking equality + * with itself and possible parents. + * @param filter The type to check against + * @return Whether or not the given value matches this exception's hierarchy + */ + public boolean isRelevantFor(String filter) + { + Set relevantSet = handles.get(filter); + if (relevantSet == null) throw new InternalExpressionException("Unknown exception type: "+filter); + return relevantSet.contains(this); + } + + /** + * Returns the throwable type + * @return The type of this exception + */ + public String getId() { + return id; } } diff --git a/src/main/java/carpet/script/language/ControlFlow.java b/src/main/java/carpet/script/language/ControlFlow.java index b704a908..f5173f26 100644 --- a/src/main/java/carpet/script/language/ControlFlow.java +++ b/src/main/java/carpet/script/language/ControlFlow.java @@ -8,7 +8,6 @@ import carpet.script.exception.InternalExpressionException; import carpet.script.exception.ProcessedThrowStatement; import carpet.script.exception.ThrowStatement; import carpet.script.exception.Throwables; -import carpet.script.value.StringValue; import carpet.script.value.Value; public class ControlFlow { @@ -50,15 +49,13 @@ public class ControlFlow { switch (lv.size()) { case 0: - throw new ThrowStatement(new StringValue("No further information"), Value.NULL, Value.NULL); + throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED); case 1: - throw new ThrowStatement(lv.get(0).evalValue(c)); + throw new ThrowStatement(lv.get(0).evalValue(c), Throwables.USER_DEFINED ); case 2: - throw new ThrowStatement(lv.get(1).evalValue(c), lv.get(0).evalValue(c), Value.NULL); - case 3: - throw new ThrowStatement(lv.get(1).evalValue(c), lv.get(0).evalValue(c), lv.get(2).evalValue(c)); + throw new ThrowStatement(lv.get(1).evalValue(c), Throwables.getTypeForException(lv.get(0).evalValue(c).getString())); default: - throw new InternalExpressionException("throw() can't accept more than 3 parameters"); + throw new InternalExpressionException("throw() can't accept more than 2 parameters"); } }); @@ -73,55 +70,43 @@ public class ControlFlow { } catch (ProcessedThrowStatement ret) { - if (ret.exception.isError()) - throw ret; if (lv.size() == 1) { - if (!ret.exception.isInstance(Throwables.USER_DEFINED.getValue())) + if (ret.thrownExceptionType != Throwables.USER_DEFINED) throw ret; return (c_, t_) -> Value.NULL; } - if (lv.size() == 0 || lv.size() % 2 == 0) + if (lv.size() > 3 && lv.size() % 2 == 0) { - throw new InternalExpressionException("Try-catch block needs the code to run, and either a catch_expr or a number of pairs of filters and catch_expr"); + throw new InternalExpressionException("Try-catch block needs the code to run, and either a catch expression for user thrown exceptions, or a number of pairs of filters and catch expressions"); } Value val = null; // This is always assigned at some point, just the compiler doesn't know LazyValue __ = c.getVariable("_"); - c.setVariable("_", (__c, __t) -> ret.exception.getValue().reboundedTo("_")); - LazyValue __msg = c.getVariable("_msg"); - c.setVariable("_msg", (__c, __t) -> StringValue.of(ret.message).reboundedTo("_msg")); - - boolean handled; + c.setVariable("_", (__c, __t) -> ret.data.reboundedTo("_")); + if (lv.size() == 2) { - if (!ret.exception.isInstance(Throwables.USER_DEFINED.getValue())) - handled = false; - else { + if (ret.thrownExceptionType == Throwables.USER_DEFINED) val = lv.get(1).evalValue(c, t); - handled = true; - } } else { int pointer = 1; - handled = false; while (pointer < lv.size() -1) { - if (ret.exception.isInstance(lv.get(pointer).evalValue(c))) + if (ret.thrownExceptionType.isRelevantFor(lv.get(pointer).evalValue(c).getString())) { val = lv.get(pointer + 1).evalValue(c, t); - handled = true; break; } pointer += 2; } } c.setVariable("_", __); - c.setVariable("_msg", __msg); - if (!handled) + if (val == null) // not handled { throw ret; }