/* --------------------------------------------------------- * * __________ D E L T A S C R I P T * * (_________() * * / === / - A fast, dynamic scripting language * * | == | - Version 4.13.11.0 * * / === / - Developed by Adam R. Nelson * * | = = | - 2011-2013 * * / === / - Distributed under GNU LGPL v3 * * (________() - http://github.com/ar-nelson/deltascript * * * * --------------------------------------------------------- */ package com.sector91.delta.script; import static com.sector91.delta.script.objects.DS_Tag.tag; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.sector91.delta.script.instrs.DSInstr; import com.sector91.delta.script.objects.DS_Object; import com.sector91.delta.script.objects.DS_Tag; import com.sector91.util.A; import com.sector91.util.StringTemplate; import com.sector91.util.StringUtil; import com.sector91.util._; /** * <p>Any kind of exception that occurs during the execution of DeltaScript * code, complete with message and tags.</p> * * <p>DeltaScript supports <em>tagged errors</em>: any DeltaScript error can * have a set of {@link DS_Tag}s attached to it, and the {@code error} function * will receive these tags as its second argument.</p> * * <p>As a general rule, code intended to be used from DeltaScript should never * throw normal Java exceptions. All exceptions should be wrapped in {@code * DScriptErr} objects, which can contain the instruction and context in which * the error occurred. * </p> * * @author Adam R. Nelson * @version 4.13.11.0 */ public class DScriptErr extends RuntimeException { private static final long serialVersionUID = DeltaScript.VERSION.hashCode(); // Static Tags // ---------------------------------------------------- public static final DS_Tag T_JAVA_EXCEPTION = tag("JavaException"), T_ASSERTION = tag("Assertion"), T_UNDEFINED = tag("Undefined"), T_IMMUTABLE = tag("Immutable"), T_INVALID_TAG = tag("InvalidTag"), T_INVALID_OP = tag("InvalidOperation"), T_INVALID_TYPE = tag("InvalidType"), T_INVALID_ARGS = tag("InvalidArguments"), T_NOT_CALLABLE = tag("NotCallable"), T_NOT_ITERABLE = tag("NotIterable"), T_NOT_INDEXABLE = tag("NotIndexable"), T_NOT_INTEGER = tag("NotInteger"), T_NOT_NUMBER = tag("NotNumber"), T_OUT_OF_BOUNDS = tag("OutOfBounds"), T_WRONG_VEC_SIZE = tag("WrongVectorDimensions"), T_BLANK = tag("Blank"), T_INTERNAL = tag("Internal"), T_PERM_DENIED = tag("PermissionDenied"), T_ADD = tag("Add"), T_SUBTRACT = tag("Subtract"), T_MULTIPLY = tag("Muliply"), T_DIVIDE = tag("Divide"), T_INDEX = tag("Index"), T_INDEXSET = tag("IndexSet"); // Fields // ---------------------------------------------------- final DScriptContext context; final List<DSInstr> instrStackTrace; final Set<DS_Tag> tags = new HashSet<DS_Tag>(); // Constructors // ---------------------------------------------------- public DScriptErr(String message, DS_Tag... tags) {this(message, (DSInstr)null, tags);} public DScriptErr(String message, Throwable cause, DS_Tag... tags) {this(message, cause, null, tags);} public DScriptErr(String message, DSInstr sourceInstr, DS_Tag... tags) { super(message); context = DScriptContext.get(); for (DS_Tag tag : tags) addTag(tag); instrStackTrace = new ArrayList<DSInstr>(); if (sourceInstr != null) pushStackInstr(sourceInstr); } public DScriptErr(String message, Throwable cause, DSInstr sourceInstr, DS_Tag... tags) { super(message, cause); context = DScriptContext.get(); for (DS_Tag tag : tags) addTag(tag); instrStackTrace = new ArrayList<DSInstr>(); if (sourceInstr != null) pushStackInstr(sourceInstr); if (cause instanceof DScriptErr) this.tags.addAll(((DScriptErr)cause).tags); else if (cause != null) { addTag(T_JAVA_EXCEPTION); Class<?> exClass = cause.getClass(); while (exClass != Exception.class && exClass != Throwable.class) { String name = exClass.getSimpleName(); if (name.endsWith("Exception")) addTag(tag(name.substring(0, name.length()-9))); else addTag(tag(name)); exClass = exClass.getSuperclass(); } } } public DScriptContext getContext() {return context;} public DSInstr getSourceInstr() { if (instrStackTrace.isEmpty()) return null; return instrStackTrace.get(0); } public DSInstr[] getInstrStackTrace() {return instrStackTrace.toArray(new DSInstr[instrStackTrace.size()]);} public void pushStackInstr(DSInstr instr) {instrStackTrace.add(instr);} public DScriptErr addTag(DS_Tag tag) { tags.add(tag); return this; } public boolean hasTag(DS_Tag tag) { return tags.contains(tag); } // Console Output // ---------------------------------------------------- @Override public String toString() { return StringTemplate.$("DeltaScript Error: {} [{}]", getMessage(), StringUtil.commaJoin(tags)); } public static _<String, _<Integer, Integer>> locateInSource(String source, int pos) { int lineNum = 1; int lineStart = -1; int lineEnd = source.length(); for (int i=0; i<pos; i++) if (source.charAt(i) == '\n') { lineStart = i; lineNum++; } for (int i=lineStart+1; i<lineEnd; i++) if (source.charAt(i) == '\n') { lineEnd = i; break; } return A._(source.substring(lineStart+1, lineEnd), A._(lineNum, pos-(lineStart+1))); } public static String locatorText(String name, String source, int start, int len) { final _<String, _<Integer, Integer>> res = locateInSource(source,start); final int line = res._2._1, pos = res._2._2; final StringBuilder sb = new StringBuilder(name); sb.append(", line "); sb.append(line); sb.append(":\n"); sb.append(res._1); sb.append('\n'); for (int i=0; i<pos; i++) sb.append(' '); for (int i=0; i<len && i+pos<res._1.length(); i++) sb.append('^'); return sb.toString(); } public String getLocatorText(String name, String source) { return locatorText(name, source, getSourceInstr().sourceStart(), getSourceInstr().sourceLength()); } // Exception Generators // ---------------------------------------------------- public static final DScriptErr invalidOperation(String op, DS_Object obj) { return new DScriptErr("Invalid operation \"" + op + "\" for " + obj.getTypeName() + ".", T_INVALID_OP); } public static final DScriptErr invalidOperation(String op, DS_Object obj1, DS_Object obj2) { return new DScriptErr("Invalid operation \"" + op + "\" for " + obj1.getTypeName() + " and " + obj2.getTypeName() + ".", T_INVALID_OP); } public static final DScriptErr invalidCompare(DS_Object v1, DS_Object v2) { return new DScriptErr("Cannot compare " + v1.getTypeName() + " and " + v2.getTypeName() + ".", T_INVALID_OP); } public static final DScriptErr undefined(String name) { return new DScriptErr(name + " is undefined in this scope.", T_UNDEFINED); } public static final DScriptErr undefined(String name, DS_Object context) { return new DScriptErr(name + " is undefined for type \"" + context.getTypeName() + "\".", T_UNDEFINED); } public static final DScriptErr immutable(String name) { return new DScriptErr(name + " is read-only in this scope.", T_IMMUTABLE); } public static final DScriptErr immutable(String name, DS_Object context) { return new DScriptErr(name + " is read-only for type \"" + context.getTypeName() + "\".", T_IMMUTABLE); } }