Package scriptingLanguage

Source Code of scriptingLanguage.BitwiseOROperation

package scriptingLanguage;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lexer.abstractLexer.AbstractAction;
import lexer.abstractLexer.AbstractDescender;
import lexer.abstractLexer.AbstractLexer;
import lexer.abstractLexer.AbstractRule;
import lexer.errors.LexerException;
import lipstone.joshua.customStructures.tuples.Pair;
import lipstone.joshua.parser.Parser;
import lipstone.joshua.parser.plugin.PluginController;
import lipstone.joshua.parser.types.BigDec;
import scriptingLanguage.errors.InterpreterException;
import scriptingLanguage.errors.InvalidAccessException;
import scriptingLanguage.errors.InvalidAssignmentException;
import scriptingLanguage.errors.NullAccessException;
import scriptingLanguage.errors.UndefinedResultException;
import scriptingLanguage.errors.UnendedLineException;
import scriptingLanguage.errors.UnexpectedTokenException;
import scriptingLanguage.errors.UnexpectedTypeException;
import scriptingLanguage.frames.AbstractFrame;
import scriptingLanguage.frames.Frame;
import scriptingLanguage.frames.InterpreterRoot;
import scriptingLanguage.frames.RootFrame;
import scriptingLanguage.frames.errors.FrameAccessException;
import scriptingLanguage.variables.AbstractClass;
import scriptingLanguage.variables.AbstractMethod;
import scriptingLanguage.variables.AbstractObject;
import scriptingLanguage.variables.ArrayClass;
import scriptingLanguage.variables.ArrayObject;
import scriptingLanguage.variables.InterpreterClass;
import scriptingLanguage.variables.InterpreterMethod;
import scriptingLanguage.variables.InterpreterMethodClass;
import scriptingLanguage.variables.JavaClass;
import scriptingLanguage.variables.JavaObject;
import scriptingLanguage.variables.PrimitiveClass;
import scriptingLanguage.variables.PrimitiveObject;
import scriptingLanguage.variables.Variable;

public class Interpreter {
  static {
    System.setProperty("java.awt.headless", "true");
  }
 
  private final Lexer lexer = new Lexer();
  private final RootFrame root;
  public static final Type<Number> numberType = new Type<Number>("Number");
  public static final Type<Byte> byteType = new Type<Byte>("Byte");
  public static final Type<Short> shortType = new Type<Short>("Short");
  public static final Type<Integer> integerType = new Type<Integer>("Integer");
  public static final Type<Long> longType = new Type<Long>("Long");
  public static final Type<Float> floatType = new Type<Float>("Float");
  public static final Type<Double> doubleType = new Type<Double>("Double");
  public static final Type<Boolean> booleanType = new Type<Boolean>("Boolean");
  public static final Type<Character> characterType = new Type<Character>("Character") {
    @Override
    public String valueToString(Object value) {
      return super.valueToString("'" + value.toString() + "'");
    }
  };
  public static final Type<BigDec> complexType = new Type<BigDec>("Complex");
  public static final Type<String> stringType = new Type<String>("String") {
    @Override
    public String valueToString(Object value) {
      return value.toString() + " ";
      //return '"' + value.toString() + "\" ";
    }
  };
  public static final Type<Void> voidType = new Type<Void>("Boolean");
 
  public static final JavaClass<Object> ObjectClass = new JavaClass<Object>("Object", Object.class) {
   
    @Override
    public int extend(AbstractClass<?> type) {
      if (!type.equals(this))
        return -1;
      return 0;
    }
  };
  public static final JavaClass<Void> voidClass = new JavaClass<Void>("void", Void.class) {
   
    @Override
    public int extend(AbstractClass<?> type) {
      if (!type.equals(this))
        return -1;
      return 0;
    }
  };
 
  public static final Variable<Void> NULL = new Variable<Void>(Interpreter.voidClass, null) {
   
    @Override
    public Token eval(AbstractClass<?> caller, Token parameters, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
      throw new NullAccessException("Interpreter Equivalent of a NullPointerException");
    }
   
    @Override
    public int extend(AbstractClass<?> type) throws NullAccessException {
      throw new NullAccessException("Interpreter Equivalent of a NullPointerException");
    }
   
    @Override
    public void write(Variable<?> data) throws InvalidAccessException {
      throw new InvalidAccessException("Cannot overwrite the Void type.");
    }
   
    @Override
    public Variable<Void> clone() {
      return this;
    }
  };
 
  public static final JavaClass<Number> numberClass = new JavaClass<Number>("Number", Number.class, numberType);
  public static final PrimitiveClass<Byte> byteClass = new PrimitiveClass<Byte>("Byte", Byte.class, byteType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(shortClass))
        return ((Number) value).shortValue();
      if (otherType.equals(integerClass))
        return ((Number) value).intValue();
      if (otherType.equals(longClass))
        return ((Number) value).longValue();
      if (otherType.equals(floatClass))
        return ((Number) value).floatValue();
      if (otherType.equals(doubleClass))
        return ((Number) value).doubleValue();
      if (otherType.equals(complexClass))
        return new BigDec((Integer) ((Number) value).intValue());
      throw new UnexpectedTypeException("Bytes cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Bytes cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Byte add(Object value1, Object value2) throws UnexpectedTypeException {
      return (byte) ((byte) value1 + (byte) value2);
    }
   
    @Override
    public Byte subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (byte) ((byte) value1 - (byte) value2);
    }
   
    @Override
    public Byte multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (byte) ((byte) value1 * (byte) value2);
    }
   
    @Override
    public Byte divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (byte) ((byte) value1 / (byte) value2);
    }
   
    @Override
    public Byte mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (byte) ((byte) value1 % (byte) value2);
    }
  };
  public static final PrimitiveClass<Short> shortClass = new PrimitiveClass<Short>("Short", Short.class, shortType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(integerClass))
        return ((Number) value).intValue();
      if (otherType.equals(longClass))
        return ((Number) value).longValue();
      if (otherType.equals(floatClass))
        return ((Number) value).floatValue();
      if (otherType.equals(doubleClass))
        return ((Number) value).doubleValue();
      if (otherType.equals(complexClass))
        return new BigDec((Integer) ((Number) value).intValue());
      throw new UnexpectedTypeException("Shorts cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return ((Number) value).byteValue();
      if (otherType.equals(characterClass))
        return (char) ((Number) value).shortValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Shorts cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Short add(Object value1, Object value2) throws UnexpectedTypeException {
      return (short) ((short) value1 + (short) value2);
    }
   
    @Override
    public Short subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (short) ((short) value1 - (short) value2);
    }
   
    @Override
    public Short multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (short) ((short) value1 * (short) value2);
    }
   
    @Override
    public Short divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (short) ((short) value1 / (short) value2);
    }
   
    @Override
    public Short mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (short) ((short) value1 % (short) value2);
    }
  };
  public static final PrimitiveClass<Integer> integerClass = new PrimitiveClass<Integer>("Integer", Integer.class, integerType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(longClass))
        return ((Number) value).longValue();
      if (otherType.equals(floatClass))
        return ((Number) value).floatValue();
      if (otherType.equals(doubleClass))
        return ((Number) value).doubleValue();
      if (otherType.equals(complexClass))
        return new BigDec((Integer) ((Number) value).intValue());
      throw new UnexpectedTypeException("Integers cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return ((Number) value).byteValue();
      if (otherType.equals(characterClass))
        return (char) ((Number) value).shortValue();
      if (otherType.equals(shortClass))
        return ((Number) value).shortValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Integers cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Integer add(Object value1, Object value2) throws UnexpectedTypeException {
      return (int) value1 + (int) value2;
    }
   
    @Override
    public Integer subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (int) value1 - (int) value2;
    }
   
    @Override
    public Integer multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (int) value1 * (int) value2;
    }
   
    @Override
    public Integer divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (int) value1 / (int) value2;
    }
   
    @Override
    public Integer mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (int) value1 % (int) value2;
    }
  };
  public static final PrimitiveClass<Long> longClass = new PrimitiveClass<Long>("Long", Long.class, longType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(floatClass))
        return ((Number) value).floatValue();
      if (otherType.equals(doubleClass))
        return ((Number) value).doubleValue();
      if (otherType.equals(complexClass))
        return new BigDec(((Number) value).longValue());
      throw new UnexpectedTypeException("Longs cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return ((Number) value).byteValue();
      if (otherType.equals(characterClass))
        return (char) ((Number) value).shortValue();
      if (otherType.equals(shortClass))
        return ((Number) value).shortValue();
      if (otherType.equals(integerClass))
        return ((Number) value).intValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Longs cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Long add(Object value1, Object value2) throws UnexpectedTypeException {
      return (long) value1 + (long) value2;
    }
   
    @Override
    public Long subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (long) value1 - (long) value2;
    }
   
    @Override
    public Long multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (long) value1 * (long) value2;
    }
   
    @Override
    public Long divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (long) value1 / (long) value2;
    }
   
    @Override
    public Long mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (long) value1 % (long) value2;
    }
  };
  public static final PrimitiveClass<Float> floatClass = new PrimitiveClass<Float>("Float", Float.class, floatType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(doubleClass))
        return ((Number) value).doubleValue();
      if (otherType.equals(complexClass))
        return new BigDec(((Number) value).floatValue());
      throw new UnexpectedTypeException("Longs cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return ((Number) value).byteValue();
      if (otherType.equals(characterClass))
        return (char) ((Number) value).shortValue();
      if (otherType.equals(shortClass))
        return ((Number) value).shortValue();
      if (otherType.equals(integerClass))
        return ((Number) value).intValue();
      if (otherType.equals(longClass))
        return ((Number) value).longValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Longs cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Float add(Object value1, Object value2) throws UnexpectedTypeException {
      return (float) value1 + (float) value2;
    }
   
    @Override
    public Float subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (float) value1 - (float) value2;
    }
   
    @Override
    public Float multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (float) value1 * (float) value2;
    }
   
    @Override
    public Float divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (float) value1 / (float) value2;
    }
   
    @Override
    public Float mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (float) value1 % (float) value2;
    }
  };
  public static final PrimitiveClass<Double> doubleClass = new PrimitiveClass<Double>("Double", Double.class, doubleType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(complexClass))
        return new BigDec(((Number) value).doubleValue());
      throw new UnexpectedTypeException("Doubles cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return ((Number) value).byteValue();
      if (otherType.equals(characterClass))
        return (char) ((Number) value).shortValue();
      if (otherType.equals(shortClass))
        return ((Number) value).shortValue();
      if (otherType.equals(integerClass))
        return ((Number) value).intValue();
      if (otherType.equals(longClass))
        return ((Number) value).longValue();
      if (otherType.equals(floatClass))
        return ((Number) value).floatValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Doubles cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Double add(Object value1, Object value2) throws UnexpectedTypeException {
      return (double) value1 + (double) value2;
    }
   
    @Override
    public Double subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (double) value1 - (double) value2;
    }
   
    @Override
    public Double multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (double) value1 * (double) value2;
    }
   
    @Override
    public Double divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (double) value1 / (double) value2;
    }
   
    @Override
    public Double mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (double) value1 % (double) value2;
    }
  };
  public static final PrimitiveClass<Boolean> booleanClass = new PrimitiveClass<Boolean>("Boolean", Boolean.class, booleanType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Booleans cannot be widened to" + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Booleans cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Boolean add(Object value1, Object value2) throws UnexpectedTypeException {
      throw new UnexpectedTypeException("The + operation is not valid for the types boolean and boolean.");
    }
   
    @Override
    public Boolean subtract(Object value1, Object value2) throws UnexpectedTypeException {
      throw new UnexpectedTypeException("The - operation is not valid for the types boolean and boolean.");
    }
   
    @Override
    public Boolean multiply(Object value1, Object value2) throws UnexpectedTypeException {
      throw new UnexpectedTypeException("The * operation is not valid for the types boolean and boolean.");
    }
   
    @Override
    public Boolean divide(Object value1, Object value2) throws UnexpectedTypeException {
      throw new UnexpectedTypeException("The / operation is not valid for the types boolean and boolean.");
    }
   
    @Override
    public Boolean mod(Object value1, Object value2) throws UnexpectedTypeException {
      throw new UnexpectedTypeException("The % operation is not valid for the types boolean and boolean.");
    }
   
  };
  public static final PrimitiveClass<Character> characterClass = new PrimitiveClass<Character>("Character", Character.class, characterType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      if (otherType.equals(integerClass))
        return (int) ((Character) value).charValue();
      if (otherType.equals(longClass))
        return (long) ((Character) value).charValue();
      if (otherType.equals(floatClass))
        return (float) ((Character) value).charValue();
      if (otherType.equals(doubleClass))
        return (double) ((Character) value).charValue();
      if (otherType.equals(complexClass))
        return new BigDec((Integer) (int) ((Character) value).charValue());
      throw new UnexpectedTypeException("Characters cannot be widened to " + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return (byte) ((Character) value).charValue();
      if (otherType.equals(shortClass))
        return (short) ((Character) value).charValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Characters cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public Character add(Object value1, Object value2) throws UnexpectedTypeException {
      return (char) ((char) value1 + (char) value2);
    }
   
    @Override
    public Character subtract(Object value1, Object value2) throws UnexpectedTypeException {
      return (char) ((char) value1 - (char) value2);
    }
   
    @Override
    public Character multiply(Object value1, Object value2) throws UnexpectedTypeException {
      return (char) ((char) value1 * (char) value2);
    }
   
    @Override
    public Character divide(Object value1, Object value2) throws UnexpectedTypeException {
      return (char) ((char) value1 / (char) value2);
    }
   
    @Override
    public Character mod(Object value1, Object value2) throws UnexpectedTypeException {
      return (char) ((char) value1 % (char) value2);
    }
  };
  public static final PrimitiveClass<BigDec> complexClass = new PrimitiveClass<BigDec>("Complex", BigDec.class, complexType) {
   
    @Override
    public Object widen(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Complex Numbers cannot be widened to" + otherType.getName() + ".");
    }
   
    @Override
    public Object narrow(Object value, PrimitiveClass<?> otherType) throws UnexpectedTypeException {
      if (otherType.equals(byteClass))
        return ((BigDec) value).getInternal().real().byteValue();
      if (otherType.equals(characterClass))
        return (char) ((BigDec) value).getInternal().real().intValue();
      if (otherType.equals(shortClass))
        return ((BigDec) value).getInternal().real().shortValue();
      if (otherType.equals(integerClass))
        return ((BigDec) value).getInternal().real().intValue();
      if (otherType.equals(longClass))
        return ((BigDec) value).getInternal().real().longValue();
      if (otherType.equals(floatClass))
        return ((BigDec) value).getInternal().real().floatValue();
      if (otherType.equals(doubleClass))
        return ((BigDec) value).getInternal().real().doubleValue();
      if (otherType.equals(this))
        return value;
      throw new UnexpectedTypeException("Complex Numbers cannot be narrowed to" + otherType.getName() + ".");
    }
   
    @Override
    public BigDec add(Object value1, Object value2) throws UnexpectedTypeException, UndefinedResultException {
      try {
        return ((BigDec) value1).add((BigDec) value2);
      }
      catch (lipstone.joshua.parser.exceptions.UndefinedResultException e) {
        throw new UndefinedResultException(e.getMessage());
      }
    }
   
    @Override
    public BigDec subtract(Object value1, Object value2) throws UnexpectedTypeException, UndefinedResultException {
      try {
        return ((BigDec) value1).subtract((BigDec) value2);
      }
      catch (lipstone.joshua.parser.exceptions.UndefinedResultException e) {
        throw new UndefinedResultException(e.getMessage());
      }
    }
   
    @Override
    public BigDec multiply(Object value1, Object value2) throws UnexpectedTypeException, UndefinedResultException {
      try {
        return ((BigDec) value1).multiply((BigDec) value2);
      }
      catch (lipstone.joshua.parser.exceptions.UndefinedResultException e) {
        throw new UndefinedResultException(e.getMessage());
      }
    }
   
    @Override
    public BigDec divide(Object value1, Object value2) throws UnexpectedTypeException, UndefinedResultException {
      try {
        return ((BigDec) value1).divide((BigDec) value2);
      }
      catch (lipstone.joshua.parser.exceptions.UndefinedResultException e) {
        throw new UndefinedResultException(e.getMessage());
      }
    }
   
    @Override
    public BigDec mod(Object value1, Object value2) throws UnexpectedTypeException, UndefinedResultException {
      try {
        return ((BigDec) value1).mod((BigDec) value2);
      }
      catch (lipstone.joshua.parser.exceptions.UndefinedResultException e) {
        throw new UndefinedResultException(e.getMessage());
      }
    }
  };
  public static final JavaClass<String> stringClass = new JavaClass<>("String", String.class, stringType);
 
  public static final Type<String> ifelseType = new Type<String>("ifelse");
  public static final Type<String> loopType = new Type<String>("loop");
  public static final Type<String> switchType = new Type<String>("switch");
 
  public static final Type<String> postfixType = new Operator<String>("postfix", 1, true);
  public static final Type<String> prefixType = new Operator<String>("unary", 2, false);
  public static final Type<String> objectType = new Operator<String>("object", 3, false);
  public static final Type<String> multiplicativeType = new Operator<String>("multiplicative", 4, true);
  public static final Type<String> additiveType = new Operator<String>("additive", 5, true);
  public static final Type<String> shiftType = new Operator<String>("shift", 6, true);
  public static final Type<String> relationalType = new Operator<String>("relational", 7, true);
  public static final Type<String> equalityType = new Operator<String>("equality", 8, true);
  public static final Type<String> bitwiseANDType = new Operator<String>("bitwiseAND", 9, true);
  public static final Type<String> bitwiseXORType = new Operator<String>("bitwiseXOR", 10, true);
  public static final Type<String> bitwiseORType = new Operator<String>("bitwiseOR", 11, true);
  public static final Type<String> logicalANDType = new Operator<String>("logicalAND", 12, true);
  public static final Type<String> logicalORType = new Operator<String>("logicalOR", 13, true);
  public static final Type<Ternary> conditionalType = new Operator<Ternary>("conditional", 14, false);
  public static final Type<String> assignmentType = new Operator<String>("assignment", 15, false);
 
  public static final Type<Token> parenthesesType = new Operator<Token>("parentheses", 1, true);
  public static final Type<Token> squareBracketsType = new Operator<Token>("squareBrackets", 1, true);
  public static final Type<Token> curlyBracesType = new Type<Token>("curlyBraces");
  private static final Type<Token> methodClassType = new Type<Token>("methodClass"); //This is a helper type
 
  public static final Type<String> identifierType = new Type<String>("identifier");
  public static final Type<String> scopeType = new Type<String>("scope");
  public static final Type<String> staticType = new Type<String>("static");
  public static final Type<String> extendsType = new Type<String>("extends");
  public static final Type<String> classType = new Type<String>("class");
  public static final Type<String> superType = new Type<String>("super");
  public static final Type<String> importType = new Type<String>("import");
  public static final Type<String> endLineType = new Type<String>("endLine");
  public static final Type<String> separatorType = new Type<String>("separator");
  public static final Type<String> callType = new Operator<String>("call", 1, true);
  public static final Type<String> breakType = new Type<String>("break");
  private static final Type<String> syntaxType = new Type<String>("syntax"); //This is a helper type
 
  public static final Type<Token> blockType = new Type<Token>("Block");
  public static final Type<Variable<?>> variableType = new Type<Variable<?>>("Variable");
 
  /**
   * Initializes the interpreter without a {@link lipstone.joshua.parser.Parser Parser}
   */
  public Interpreter() {
    this(null);
  }
 
  /**
   * Intializes this Interpreter
   *
   * @param parser
   *            the <tt>Parser</tt> to use for the math capabilities (like Java's Math.{@literal <}function{@literal >}).
   *            This can be <tt>null</tt>
   */
  public Interpreter(Parser parser) {
    root = new InterpreterRoot(parser, this);
    root.addImport("java.lang.Object");
    root.addImport("java.lang.*");
    lexer.addRule("IfElse", new Rule<String>("(if|else if|else)", ifelseType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token output = new Token(match.group(), type);
        if (!match.group().equals("else"))
          output.append(lexer.getNextToken(true));
        Token temp;
        if ((temp = lexer.getNextToken(true)).getCarType().equals(curlyBracesType)) {
          output.getNextToken().append(temp);
          return output;
        }
        Token block = new Token(), head = block;
        do {
          head = head.append(temp);
        } while (!temp.getLastToken().getCarType().equals(endLineType) && lexer.hasNext() && !(temp = lexer.getNextToken(true)).isNull());
        output.getLastToken().append(new Token(block, curlyBracesType));
        return output;
      }
    }));
    lexer.addRule("Loops", new Rule<String>("(for|while|do)", loopType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token output = new Token(match.group(), type), head = output;
        if (match.equals("do")) {
          Token current;
          Token block = new Token();
          while (!(current = lexer.getNextToken(true)).getCar().equals("while"))
            block = block.append(current);
          head = head.append(block.getCarType().equals(curlyBracesType) ? block : new Token(block, curlyBracesType));
          head = head.append(current);
          head = head.append(lexer.getNextToken(true));
        }
        else {
          head = head.append(lexer.getNextToken(true));
          Token current = lexer.getNextToken(true);
          if (current.getCarType().equals(curlyBracesType))
            head.append(current);
          else {
            Token block = new Token();
            do {
              block = block.append(current);
            } while (!current.getCarType().equals(endLineType) && lexer.hasNext() && !(current = lexer.getNextToken(true)).isNull());
            head = head.append(new Token(block, curlyBracesType));
          }
        }
        return output;
      }
    }));
    lexer.addRule("Switch", new Rule<String>("(switch|case|default)", switchType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token condition = (Token) lexer.getNextToken(true).getCar(), current = (Token) lexer.getNextToken(true).getCar(), temp, head = null;
       
        ArrayList<Token> cases = new ArrayList<Token>();
       
        do {
          if (current.getCar().equals("case")) {
            temp = new Token();
            head = temp;
            cases.add(temp);
            head = head.append((current = current.getNextToken()));
            current = current.getNextToken();
          }
          else if (current.getCar().equals("default")) {
            temp = new Token();
            head = temp;
            cases.add(temp);
            current = current.getNextToken();
          }
          else
            head = head.append(current.singular());
        } while (!(current = current.getNextToken()).isNull());
        return new Token(match.group(), type, new Token(condition, Type.TOKEN, new Token(cases, type), Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addRule("Break", new Rule<String>("(break|return|continue)", breakType, null));
   
    lexer.addRule("Unary", new Rule<String>("(\\+\\+|--|\\+|-|~|! *?)(?=(\\(|[a-zA-Z_]+[\\w]*))", prefixType, new Action<String>() {
      @Override
      public Token action(Matcher matcher, Lexer lexer, Type<String> type) throws LexerException {
        String token = matcher.group(1);
        if ((token.equals("+") || token.equals("-")) && !lexer.getPreviousToken().isNull())
          return new Token(new AdditiveOperation(token), additiveType);
        return new Token(new Token(), Type.TOKEN, new Token(new PrefixOperation(token), type), Type.TOKEN);
      }
    }));
    lexer.addRule("Postfix", new Rule<String>("(\\+\\+|--)", postfixType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new PostfixOperation(match.group()), type, new Token(new Token(), Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addRule("Multiplicative", new Rule<String>("(\\*|/|\\%)", multiplicativeType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new MultiplicativeOperation(match.group()), type);
      }
    }));
    lexer.addRule("Additive", new Rule<String>("(\\+|-)", additiveType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new AdditiveOperation(match.group()), type);
      }
    }));
    lexer.addRule("Relational", new Rule<String>("(<|<=|>|>=)", relationalType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new RelationalOperation(match.group()), type);
      }
    }));
    lexer.addRule("Equality", new Rule<String>("(==|!=)", equalityType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new EqualityOperation(match.group()), type);
      }
    }));
    lexer.addRule("BitwiseAND", new Rule<String>("&", bitwiseANDType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new BitwiseANDOperation(), type);
      }
    }));
    lexer.addRule("BitwiseXOR", new Rule<String>("^", bitwiseXORType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new BitwiseXOROperation(), type);
      }
    }));
    lexer.addRule("BitwiseOR", new Rule<String>("|", bitwiseORType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        return new Token(new BitwiseOROperation(), type);
      }
    }));
    lexer.addRule("LogicalAND", new Rule<String>("&&", logicalANDType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token right = new Token(), head = right, current = lexer.getNextToken(true);
        do {
          head = head.append(current);
        } while (lexer.hasNext() && !(current = lexer.getNextToken(true)).getCarType().equals(separatorType) && !current.getCarType().equals(endLineType) && current.getCarType().getPrecedence() <= 12);
        return new Token(new LogicalANDOperation(match.group()), type, new Token(right, blockType, current, Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addRule("LogicalOR", new Rule<String>("||", logicalORType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token right = new Token(), head = right, current = lexer.getNextToken(true);
        do {
          head = head.append(current);
        } while (lexer.hasNext() && !(current = lexer.getNextToken(true)).getCarType().equals(separatorType) && !current.getCarType().equals(endLineType) && current.getCarType().getPrecedence() <= 13);
        return new Token(new LogicalOROperation(match.group()), type, new Token(right, blockType, current, Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addRule("Assignment", new Rule<String>("(\\+|-|\\*|/|%|&|\\^|\\|)?=", assignmentType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        String t = match.group(1) == null ? "" : match.group(1);
        switch (t) {
          case "+":
          case "-":
            return new Token(new AssignmentOperation(new AdditiveOperation(t), (Operator<?>) additiveType), type);
          case "*":
          case "/":
          case "%":
            return new Token(new AssignmentOperation(new MultiplicativeOperation(t), (Operator<?>) multiplicativeType), type);
          case "&":
            return new Token(new AssignmentOperation(new BitwiseANDOperation(), (Operator<?>) bitwiseANDType), type);
          case "^":
            return new Token(new AssignmentOperation(new BitwiseXOROperation(), (Operator<?>) bitwiseXORType), type);
          case "|":
            return new Token(new AssignmentOperation(new BitwiseOROperation(), (Operator<?>) bitwiseORType), type);
            //TODO add bitshift operations
          default:
            return new Token(new AssignmentOperation(null, (Operator<?>) type), type);
        }
      }
    }));
   
    lexer.addRule("Scope", new Rule<String>("(default|public|protected|private)", scopeType, null));
    lexer.addRule("Static", new Rule<String>("static", staticType, null));
    lexer.addRule("Import", new Rule<String>("import(-file)?", importType, null));
    lexer.addRule("Extends", new Rule<String>("extends", extendsType, null));
    lexer.addRule("Super", new Rule<String>("super", superType, null));
    lexer.addRule("Class", new Rule<String>("class", classType, null));
    lexer.addRule("Object", new Rule<String>("new", objectType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token output = new Token(), head = output, current = null;
        head = head.append(lexer.getNextToken(true)); //Type, no need to deal with package-specified types because Identifier handles that
        if (head.getCarType().equals(parenthesesType) || head.getCarType().equals(curlyBracesType))
          return new Token(null, Type.EMPTY, new Token(new NewOperation(), type, new Token(output, blockType), Type.TOKEN), Type.TOKEN);
        while (lexer.hasNext() && (current = lexer.getNextToken(true)).getCarType().equals(squareBracketsType))
          head = head.append(current);
        return new Token(null, Type.EMPTY, new Token(new NewOperation(), type, new Token(output, blockType, current, Type.TOKEN), Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addRule("Endline", new Rule<String>(";", endLineType, null));
    lexer.addRule("Conditional", new Rule<Ternary>("\\?", conditionalType, new Action<Ternary>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<Ternary> type) throws LexerException {
        Token ifTrue = new Token(), ifFalse = new Token(), head = lexer.getNextToken(true), current = ifTrue;
        do
          current = current.append(head);
        while (!(head = lexer.getNextToken(true)).getCarType().equals(syntaxType));
        current = ifFalse;
        while (lexer.hasNext() && !(head = lexer.getNextToken(true)).getCarType().equals(endLineType))
          current = current.append(head);
        return new Token(new TernaryOperation(), conditionalType, new Token(new Ternary(ifTrue, ifFalse), type), Type.TOKEN);
      }
    }));
    lexer.addRule("Separator", new Rule<String>("[\\Q,\\E]", separatorType, null));
    lexer.addRule("Call", new Rule<String>("\\.", callType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token block = lexer.getNextToken(true), next = block.getNextToken();
        if (next.getCarType().equals(parenthesesType)) {
          if (next.getNextToken().getCarType().equals(curlyBracesType)) {
            block.append(next);
            return new Token(".", type, block, Type.TOKEN);
          }
          block.append(new Token(((Operation<Token>) next.getCar()).getData(), parenthesesType));
          return new Token(new CallOperation(), type, new Token(block, blockType), Type.TOKEN);
        }
        return new Token(new CallOperation(), type, new Token(block, blockType, next, Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addRule("Boolean", new Rule<Boolean>("(true|false)", booleanType, new Action<Boolean>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<Boolean> type) throws LexerException {
        return new Token(new PrimitiveObject<>(booleanClass, new Boolean(match.group().equals("true"))), booleanType);
      }
    }));
    lexer.addRule("Number", new Rule<Number>("(([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([fdli])?|-?infinity)", numberType, new Action<Number>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<Number> type) throws LexerException {
        String number = match.group(2), t = match.group(4);
        if (number.contains("n"))
          if (number.startsWith("-"))
            return new Token(new PrimitiveObject<>(complexClass, new BigDec(-1)), complexType);
          else
            return new Token(new PrimitiveObject<>(complexClass, new BigDec(1)), complexType);
        if (t != null) {
          switch (t) {
            case "f":
              return new Token(new PrimitiveObject<>(floatClass, new Float(number)), floatType);
            case "d":
              return new Token(new PrimitiveObject<>(doubleClass, new Double(number)), doubleType);
            case "l":
              return new Token(new PrimitiveObject<>(longClass, new Long(number)), longType);
            case "i":
              return new Token(new PrimitiveObject<>(complexClass, new BigDec(match.group())), complexType);
          }
        }
        if (number.contains("."))
          return new Token(new PrimitiveObject<>(doubleClass, new Double(number)), doubleType);
        return new Token(new PrimitiveObject<>(integerClass, new Integer(number)), integerType);
      }
    }));
    lexer.addRule("String", new Rule<String>("(?<!\\\\)\"([^\"]|\\\\\")*\"", stringType, new Action<String>() {
      @Override
      public Token action(Matcher matcher, Lexer lexer, Type<String> type) {
        String input = matcher.group();
        return new Token(new JavaObject<>(stringClass, input.length() > 2 ? input.substring(1, input.length() - 1) : ""), type);
      }
    }));
    lexer.addRule("Identifier", new Rule<String>("[a-zA-Z_]+[\\w]*", identifierType, new Action<String>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<String> type) throws LexerException {
        Token output = new Token(match.group(), type), head = output, current = null;
        while (lexer.hasNext()) {
          current = lexer.getNextToken(true);
          head = head.append(current);
          if (!current.getCarType().equals(callType))
            break;
        }
        if (head.getPreviousToken().equals(parenthesesType) && head.getCarType().equals(curlyBracesType)) {
          Token rest = head.getPreviousToken().getPreviousToken().split();
          String specificType = "";
          do {
            specificType = specificType + output.getCar().toString();
          } while (!(output = output.getNextToken()).isNull());
          return new Token(specificType, identifierType, rest, Type.TOKEN);
        }
        return output;
      }
    }));
    lexer.addRule("Syntax", new Rule<String>(":", syntaxType, null));
    lexer.addDescender("MethodClass", new Descender(":{", "}", methodClassType, null));
    lexer.addDescender("Parentheses", new Descender("(", ")", parenthesesType, new Action<Token>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<Token> type) throws LexerException {
        Token next = lexer.getNextToken(true);
        if (next.getCarType().equals(curlyBracesType))
          return new Token(new ParenthesesOperation(lexer.lex(match.group())), type, next, Type.TOKEN);
        return new Token(new ParenthesesOperation(lexer.lex(match.group())), type, new Token(new Token(), Type.TOKEN, next, Type.TOKEN), Type.TOKEN);
      }
    }));
    lexer.addDescender("SquareBrackets", new Descender("[", "]", squareBracketsType, new Action<Token>() {
      @Override
      public Token action(Matcher match, Lexer lexer, Type<Token> type) throws LexerException {
        return new Token(new SquareBracketsOperation(lexer.lex(match.group())), type, new Token(), Type.TOKEN);
      }
    }));
    lexer.addDescender("CurlyBraces", new Descender("{", "}", curlyBracesType, null));
    lexer.ignore("/\\*.*?\\*/", Pattern.DOTALL); //Multi-line comments
    lexer.ignore("//[^\n\r]*?(\n|\r)"); //Single-line comments
    lexer.ignore("[\n\r\t]");
  }
 
  /**
   * Splits the given section of a program into its component lines
   *
   * @param program
   *            the program to split into lines
   * @return the lines that make up this program
   */
  public static final ArrayList<Token> splitLines(Token program) {
    ArrayList<Token> lines = new ArrayList<Token>();
    Token head = program;
    do {
      if (program.getCarType().equals(endLineType)) { //Automatically removes the ; - it is not needed after this point.
        Token next = program.split();
        program.remove();
        lines.add(head);
        head = program = next;
      }
      else if (!head.isNull() && ((program.getCarType().equals(ifelseType) || program.getCarType().equals(loopType))) ||
          (head.getCarType().equals(curlyBracesType) && !(program.getCarType().equals(endLineType) ||
              program.getCarType().equals(separatorType) || program.getCarType().equals(parenthesesType) || program.equals(squareBracketsType)))) {
        Token next = program.getPreviousToken().split();
        lines.add(head);
        head = program = next;
      }
    } while (!(program = program.getNextToken()).isNull());
    if (!head.isNull())
      lines.add(head);
    return lines;
  }
 
  public String eval(String program) throws InterpreterException, LexerException {
    Frame frame = new Frame(root, this);
    Token output = eval(ObjectClass, lexer.lex(program), frame);
    return output.getCarType().equals(breakType) ? output.getNextToken().toString() : output.toString();
  }
 
  public static Token eval(AbstractClass<?> caller, Token program, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    ArrayList<Token> lines = splitLines(program);
    return eval(caller, lines, frame);
  }
 
  private static Token eval(AbstractClass<?> caller, ArrayList<Token> lines, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Token line = null;
    while (lines.size() > 0) {
      line = lines.remove(0);
      if (isVariableDeclaration(line, frame)) {
        evalVariableDeclaration(caller, line, frame, frame);
        continue;
      }
      if (line.getCarType().equals(importType)) {
        frame.addImport(line.getNextToken().toString());
        continue;
      }
      if (line.getCarType().equals(ifelseType)) {
        if (!line.getCar().equals("if"))
          throw new UnexpectedTokenException("If-else blocks must be started with an 'if'");
        lines.add(0, line);
        line = evalIfElse(caller, lines, frame);
        if (line == null)
          continue;
      }
      if (line.getCarType().equals(loopType)) {
        lines.add(0, line);
        line = evalLoop(caller, lines, frame);
        if (line == null)
          continue;
      }
      if (line.getCarType().equals(objectType) && line.getCar().equals("class")) {
        makeNewType(line, frame); //This is a declaration of a new type
        continue;
      }
      if (line.getCarType().equals(breakType)) { //TODO efficiency, hopefully remove recursive call
        if (line.getCar().equals("break"))
          return null;
        Token eval = line.split(), ret = new Token(line.getCar(), breakType, evalInner(caller, eval, frame), Type.TOKEN);
        line.append(eval);
        return ret;
      }
      line = evalInner(caller, line, frame);
    }
    return line;
  }
 
  public static Token evalInner(AbstractClass<?> caller, Token line, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    boolean fromLeft = true;
    Stack<Token> previous = new Stack<>(), left = new Stack<>(), right = new Stack<>(), next = new Stack<>();
    if (line.getCarType() instanceof Operator)
      line = line.getNextToken();
    Token current = line;
    left.push(current.getPreviousToken());
    previous.push(left.peek().getPreviousToken());
    right.push(current.getNextToken());
    next.push(right.peek().getNextToken());
    int leftOp = 0, rightOp = 0;
    while (((leftOp = left.peek().getCarType().getPrecedence()) > 0) | ((rightOp = right.peek().getCarType().getPrecedence()) > 0)) {
      if (current.getCar() instanceof NewOperation) {
        current = ((NewOperation) current.getCar()).eval(caller, null, right.pop(), frame);
        right.push(next.pop());
        if (next.size() == 0)
          next.push(right.peek().getNextToken());
      }
      else if (leftOp > 0 && fromLeft && (rightOp == 0 || leftOp < rightOp || (leftOp == rightOp && left.peek().getCarType().isLRAssociative()))) { //<previous> <left> <current>
        Token p = previous.pop(), l = left.pop();
        if (previous.size() == 0) {
          previous.push(p.getPreviousToken().getPreviousToken());
          left.push(l.getPreviousToken().getPreviousToken());
        }
        current = evalReading(caller, p, (Operation<?>) l.getCar(), current, frame);
      }
      else if (rightOp > 0 && !fromLeft && (leftOp == 0 || rightOp < leftOp || (rightOp == leftOp && !right.peek().getCarType().isLRAssociative()))) { //<current> <right> <next>
        Token r = right.pop(), n = next.pop();
        if (next.size() == 0) {
          right.push(r.getNextToken().getNextToken());
          next.push(n.getNextToken().getNextToken());
        }
        current = evalReading(caller, current, (Operation<?>) r.getCar(), n, frame);
      }
      else if (rightOp != 0 && (leftOp == 0 || rightOp < leftOp)) {
        if (right.size() == 1) { //The size can never equal zero, so 1 is the minimum
          Token temp = right.pop();
          left.push(temp);
          right.push(temp.getNextToken().getNextToken());
          temp = next.pop();
          previous.push(current);
          current = temp;
          next.push(temp.getNextToken().getNextToken());
        }
        else { //Move to the right
          previous.push(current);
          left.push(right.pop());
          current = next.pop();
        }
        fromLeft = true;
      }
      else if (leftOp != 0 && (rightOp == 0 || leftOp < rightOp)) {
        if (left.size() == 1) { //The size can never equal zero, so 1 is the minimum
          Token temp = left.pop();
          right.push(temp);
          left.push(temp.getPreviousToken().getPreviousToken());
          next.push(current);
          temp = previous.pop();
          current = temp;
          previous.push(temp.getPreviousToken().getPreviousToken());
        }
        else { //Move to the left
          next.push(current);
          right.push(left.pop());
          current = previous.pop();
        }
        fromLeft = false;
      }
      else
        break;
    }
    return current.getCar() instanceof String && frame.hasVariable((String) current.getCar()) ? frame.readVariable((String) current.getCar()) : current;
  }
 
  private static Token evalReading(AbstractClass<?> caller, Token left, Operation<?> operation, Token right, AbstractFrame frame) throws InterpreterException {
    Token l = left == null ? null : left.getCarType().equals(identifierType) && !(operation instanceof ParenthesesOperation) ? frame.readVariable((String) left.getCar()) : left;
    Token r = right == null ? null : right.getCarType().equals(identifierType) && !(operation instanceof PrefixOperation) ? frame.readVariable((String) right.getCar()) : right;
    return operation.eval(caller, l, r, frame);
  }
 
  private static boolean isVariableDeclaration(Token line, AbstractFrame frame) throws FrameAccessException {
    if (!line.getCarType().equals(identifierType)) //If it isn't an identifier, then it cannot be a declaration
      return false;
    //Determine the type (assuming that it is a declaration)
    String type = (String) line.getCar();
    if (line.getNextToken().getCarType().equals(callType))
      while ((line = line.getNextToken()).getCarType().equals(callType))
        type = type + "." + (line = line.getNextToken()).getCar();
    if (!frame.hasType(type)) //If the type does not exist, then it is not a declaration
      return false;
    while ((line = line.getNextToken()).getCarType().equals(squareBracketsType) || line.getCarType().equals(methodClassType)); //Fast-forward through square brackets
    return line.getCarType().equals(identifierType); //If the token after the type is an identifier, then it is a type declaration
  }
 
  public static ArrayList<Token> evalList(AbstractClass<?> caller, Token values, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    ArrayList<Token> items = new ArrayList<>(), pieces = new ArrayList<>();
    if (!values.isNull()) {
      Token head = values;
      do {
        if (values.getCarType().equals(separatorType)) {
          Token next = values.split();
          pieces.add(values.getPreviousToken());
          pieces.add(next);
          values.remove();
          items.add(eval(caller, head, frame));
          head = values = next;
        }
      } while (!(values = values.getNextToken()).isNull());
      while (pieces.size() > 1)
        pieces.remove(0).append(new Token(",", separatorType)).append(pieces.remove(0));
    }
    return items;
  }
 
  public static ArrayList<Token> evalFunctionArguments(AbstractClass<?> caller, Token values, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    ArrayList<Token> args = new ArrayList<Token>();
    if (values.length() > 0)
      do {
        Token arg = values.singular();
        while (!(values = values.getNextToken()).getCarType().equals(separatorType) && !values.isNull())
          arg = arg.append(values.singular());
        args.add(frame.getInterpreter().eval(caller, arg.getFirstToken(), frame));
      } while (!(values = values.getNextToken()).isNull());
    return args;
  }
 
  public static String evalFunctionName(String name, ArrayList<Token> arguments) {
    name = name + ":{";
    for (Token argument : arguments)
      name = name + argument.getCarType() + ", ";
    return (name.substring(name.length() - 2).equals(", ") ? name.substring(0, name.length() - 2) : name) + "}";
  }
 
  private static void makeNewType(Token line, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    if (!(line = line.getNextToken()).getCarType().equals(identifierType))
      throw new UnexpectedTokenException("Expected an identifier, got a " + line.getCarType() + ".  " + line.toString());
    String type = (String) line.getCar();
    frame.addType(type, new InterpreterClass(type, line.getNextToken(), frame));
  }
 
  public static void evalVariableDeclaration(AbstractClass<?> caller, Token line, AbstractFrame declared, AbstractFrame save) throws InterpreterException {
    String type = (String) line.getCar();
    while ((line = line.getNextToken()).getCarType().equals(Interpreter.callType))
      type = type + "." + (line = line.getNextToken()).getCar();
    int globalDepth = 0;
    AbstractClass<?> baseType = declared.getType(type);
    while ((line.getCarType().equals(methodClassType))) {
      baseType = new InterpreterMethodClass(baseType.getName() + ":{" + line.getCar().toString() + "}", (Token) line.getCar(), baseType,
          declared, new Type<Object>(baseType.getName() + ":{" + line.getCar().toString() + "}"));
      line = line.getNextToken();
    }
    while (line.getCarType().equals(squareBracketsType)) {
      globalDepth++;
      type = "array:" + type;
      line = line.getNextToken();
    }
    do {
      String name = (String) line.getCar(), t = type;
      int depth = globalDepth;
      while ((line = line.getNextToken()).getCarType().equals(squareBracketsType)) {
        depth++;
        t = "array:" + t;
      }
      AbstractClass<?> clazz = depth != 0 ? ArrayClass.makeArrayClass(baseType, depth) : baseType;
      Token value = clazz.makePlaceholder();
      save.writeVariable(name, value, true);
      if (line.isNull()) //End of line, so we are done
        break;
      if (line.getCarType().equals(separatorType)) //Start of next assignment
        continue;
      if (!line.getCar().equals("="))
        throw new UnexpectedTokenException("Expected an = operator, but got a " + line.getCar() + " operator.");
      Token rest = line.split(), head = rest, last = null;
      while (!(head = head.getNextToken()).getCarType().equals(separatorType) && !head.isNull());
      if (head.getNextToken().getCarType().equals(separatorType))
        last = head.split();
      ((Variable<?>) value.getCar()).write(depth != 0 && rest.getCarType().equals(curlyBracesType) ? ((Variable<?>) clazz.eval(caller, rest, declared).getCar()) :
          ((Variable<?>) eval(caller, rest, declared).getCar()).clone());
      if (last != null)
        head.append(last);
      line.append(rest);
      line = head;
    } while (!(line = line.getNextToken()).isNull());
  }
 
  public static boolean isNumber(AbstractClass<?> carType) throws NullAccessException {
    return (carType.extend(numberClass) > -1) || carType.equals(complexClass);
  }
 
  public static boolean isPrimitive(AbstractClass<?> carType) throws NullAccessException {
    return isNumber(carType) || carType.equals(characterClass) || carType.equals(booleanClass);
  }
 
  public RootFrame getRoot() {
    return root;
  }
 
  static PrimitiveObject<?>[] castPrimitives(PrimitiveObject<?>... data) throws UnexpectedTypeException {
    for (int i = 0; i < data.length - 1; i++) {
      if (data[i].getType().equals(data[i + 1].getType()))
        continue;
      try {
        data[i + 1] = data[i].getType().makePrimitiveObject(data[i + 1].getType().widen(data[i + 1].getSource(), data[i].getType()));
      }
      catch (UnexpectedTypeException e) {
        for (int j = 0; j <= i; j++)
          data[j] = data[i + 1].getType().makePrimitiveObject(data[j].getType().widen(data[j].getSource(), data[i + 1].getType()));
      }
    }
    return data;
  }
 
  /**
   * This reads any arrangement of nested switch and loop blocks, and determines which subsection of the block, if any, the
   * interpreter should run. Note that this DOES remove each line that it inspects from the program.
   *
   * @param lines
   *            the lines in the program that the block is a part of
   * @param frame
   *            the frame to evaluate in
   * @param eval
   *            true causes it to evaluate the conditions in the if statements and trim the block such that only the part
   *            that evaluates to true runs
   * @return the result of evaluate the block of code in the if statement, if there was any.
   * @throws IllegalArgumentException
   * @throws ArrayIndexOutOfBoundsException
   * @throws InterpreterException
   */
  private static Token evalIfElse(AbstractClass<?> caller, ArrayList<Token> lines, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    if (!lines.get(0).getCar().equals("if"))
      throw new UnexpectedTokenException("If-else blocks must be started with an 'if'");
    while (lines.size() > 0 && lines.get(0).getCarType().equals(ifelseType)) {
      Token line = lines.remove(0);
      if (!line.getCarType().equals(ifelseType))
        throw new UnexpectedTokenException("Expected a if-else, got a " + line.getCarType() + ".  " + line.toString());
      if (line.getCar().equals("else"))
        return eval(caller, (Token) line.getNextToken().getCar(), new Frame(frame));
      if (!line.getNextToken().getCarType().equals(parenthesesType))
        throw new UnexpectedTokenException("Expected a parenthesis, got a " + line.getNextToken().getCarType() + ".  " + line.toString());
      Token value = eval(caller, (Token) line.getNextToken().getCar(), new Frame(frame));
      if (!((Variable<?>) value.getCar()).getType().equals(booleanClass))
        throw new UnexpectedTokenException("Expected a boolean, got a " + value.getCarType() + ".  " + line.toString());
      if ((boolean) ((PrimitiveObject<Boolean>) value.getCar()).getSource())
        return eval(caller, (Token) line.getNextToken().getNextToken().getCar(), new Frame(frame));
    }
    return null;
  }
 
  private static Token evalLoop(AbstractClass<?> caller, ArrayList<Token> lines, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Token line = lines.remove(0);
    frame = new Frame(frame);
    if (line.getCar().equals("for")) {
      if (!line.getNextToken().getCarType().equals(parenthesesType))
        throw new UnexpectedTokenException("Expected a parenthesis, got a " + line.getNextToken().getCarType() + ".  " + line.toString());
      ArrayList<Token> inner = splitLines((Token) line.getNextToken().getCar());
      Token block = line.getNextToken().getNextToken();
      if (block.getCarType().equals(curlyBracesType))
        block = (Token) block.getCar();
      else
        block = null;
      eval(caller, inner.remove(0), frame);
      if (inner.size() < 2)
        throw new UnendedLineException("A for loop requires three arguments, got " + inner.size() + 1 + ".");
      Token incrementer = inner.remove(1);
      while (!incrementer.isNull()) {
        Token item = new Token();
        do {
          item = item.append(incrementer.singular());
        } while (!(incrementer = incrementer.getNextToken()).getCarType().equals(separatorType) && !incrementer.isNull());
        if (!incrementer.isNull())
          incrementer = incrementer.getNextToken();
        inner.add(item.getFirstToken());
      }
      while (((PrimitiveObject<Boolean>) eval(caller, inner.get(0), frame).getCar()).getSource()) {
        if (block != null) {
          Token result = eval(caller, block.clone(), new Frame(frame));
          if (result.getCarType().equals(breakType))
            if (result.getCar().equals("continue"))
              continue;
            else
              return result.getCar().equals("break") ? null : result;
        }
        for (int i = 0; i < inner.size(); i++)
          eval(caller, inner.get(i).clone(), frame);
      }
    }
    else {
      Token block = null;
      if (line.getCar().equals("do")) {
        if (!line.getNextToken().getCarType().equals(curlyBracesType))
          throw new UnexpectedTokenException("Expected a curly brace, got a " + line.getNextToken().getCarType() + ".  " + line.toString());
        block = (Token) line.getNextToken().getCar();
        Token result = eval(caller, block.clone(), new Frame(frame));
        if (result.getCarType().equals(breakType) && !result.getCar().equals("continue"))
          return result.getCar().equals("break") ? null : result;
        line = lines.remove(0);
      }
      else
        block = (Token) line.getNextToken().getNextToken().getCar();
      if (!line.getNextToken().getCarType().equals(parenthesesType))
        throw new UnexpectedTokenException("Expected a parenthesis, got a " + line.getNextToken().getCarType() + ".  " + line.toString());
      Token condition = (Token) line.getNextToken().getCar();
      while (((PrimitiveObject<Boolean>) eval(caller, condition.clone(), frame).getCar()).getSource()) {
        if (block != null) {
          Token result = eval(caller, block.clone(), new Frame(frame));
          if (result.getCarType().equals(breakType))
            if (result.getCar().equals("continue"))
              continue;
            else
              return result.getCar().equals("break") ? null : result;
        }
      }
    }
    return null;
  }
 
  private Token evalSwitch(Token value, Switch s, Frame frame) {
    return null;
  }
 
  private static void REPL(Interpreter interpreter) {
    Scanner scanner = new Scanner(System.in);
    String line = "", program = "";
    final String indent = "    ";
    Stack<String> endings = new Stack<>();
    while (true) {
      if (endings.size() == 0)
        System.out.print("? ");
      else {
        System.out.print("> ");
        for (int i = 0; i < endings.size(); i++)
          System.out.print(indent);
      }
      if (endings.size() > 0 && endings.peek().equals("("))
        endings.pop();
      if (!scanner.hasNextLine())
        break;
      line = scanner.nextLine();
      if (line.length() == 0)
        continue;
      if (endings.size() == 0 && line.startsWith("return")) {
        try {
          System.out.println(interpreter.eval(program + "\n" + line));
        }
        catch (InterpreterException | LexerException e) {
          e.printStackTrace();
        }
      }
      else if (endings.size() == 0 && line.startsWith(":")) {
        line = line.substring(1);
        if (line.equals("EXIT"))
          break;
        continue;
      }
      else {
        program = program + "\n" + line;
        int oldSize = endings.size();
        for (int i = 0; i < line.length(); i++) {
          if (line.charAt(i) == '{')
            endings.push("}");
          else if (line.charAt(i) == '[')
            endings.push("]");
          else if (line.charAt(i) == '(')
            endings.push(")");
          else if (endings.size() > 0 && endings.peek().charAt(0) == line.charAt(i))
            endings.pop();
        }
        if (line.charAt(line.length() - 1) == ')')
          endings.push("(");
        if (endings.size() == oldSize && line.charAt(line.length() - 1) != ';' && (endings.size() == 0 || !endings.peek().equals(";"))) //This one goes last
          endings.push(";");
        else if (endings.size() > 0 && endings.peek().equals(";") && line.charAt(line.length() - 1) == ';')
          endings.pop();
      }
    }
    scanner.close();
  }
 
  public static void main(String[] args) {
    Parser parser = new Parser();
    PluginController pc = new PluginController(parser);
    pc.setAutomaticStackPrinting(true);
    parser.setDataPersistence(false);
    Interpreter interpreter = new Interpreter(parser);
    if (args.length == 0)
      REPL(interpreter);
    else {
      try {
        File testFile = new File(args[0]);
        Scanner scanner = new Scanner(testFile);
        String program = "";
        while (scanner.hasNextLine())
          program = program + scanner.nextLine().replaceAll("\\\\n", "\n") + "\n";
        System.out.println(interpreter.eval(program));
        scanner.close();
      }
      catch (LexerException | FileNotFoundException | InterpreterException e) {
        e.printStackTrace();
      }
    }
  }
}

class Lexer extends AbstractLexer<Token, Type<?>, Rule<?>, Descender, Lexer> {
 
  @Override
  public Token makeNewToken() {
    return new Token();
  }
 
  @Override
  protected Token descend(Descender d, Matcher m) throws LexerException {
    return d.apply(m, this);
  }
 
  @Override
  protected Token hit(Rule<?> r, Matcher m) throws LexerException {
    return r.apply(m, this);
  }
 
}

interface Action<T> extends AbstractAction<Token, Type<T>, T, Lexer> {
 
  @Override
  public Token action(Matcher match, Lexer lexer, Type<T> type) throws LexerException;
}

class Rule<T> extends AbstractRule<Token, Type<T>, Action<T>, T, Lexer> {
 
  public Rule(String pattern, Type<T> type, Action<T> action) {
    super(pattern, type, action);
  }
 
  public Rule(String pattern, int flags, Type<T> type, Action<T> action) {
    super(pattern, type, action);
  }
 
  @Override
  protected Token apply(Matcher match, Lexer lexer) throws LexerException {
    return action == null ? new Token((T) match.group(), type) : action.action(match, lexer, type);
  }
}

class Descender extends AbstractDescender<Token, Type<Token>, Action<Token>, Lexer> {
 
  public Descender(String open, String close, Type<Token> type, Action<Token> action) {
    super(open, close, type, action);
  }
 
  @Override
  protected Token apply(Matcher match, Lexer lexer) throws LexerException {
    return action == null ? new Token(lexer.lex(match.group()), type) : action.action(match, lexer, type);
  }
}

class Operator<T> extends Type<T> {
  private final int precedence;
  private final boolean isLRAssociative;
 
  public Operator(String name, int precedence, boolean isLRAssociative) {
    super(name);
    this.precedence = precedence;
    this.isLRAssociative = isLRAssociative;
  }
 
  @Override
  public int getPrecedence() {
    return precedence;
  }
 
  @Override
  public boolean isLRAssociative() {
    return isLRAssociative;
  }
 
  @Override
  public String toString() {
    return name.toString();
  }
}

abstract class Operation<T> {
  protected final T data;
 
  public Operation(T data) {
    this.data = data;
  }
 
  public abstract Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException;
 
  @Override
  public boolean equals(Object o) {
    return data.equals(o);
  }
 
  @Override
  public String toString() {
    return data.toString();
  }
 
  public T getData() {
    return data;
  }
}

class CallOperation extends Operation<String> {
  public CallOperation() {
    super(".");
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) (first.getCar() instanceof String ? frame.readVariable((String) first.getCar()).getCar() : first.getCar());
    if (!(f instanceof AbstractObject<?>))
      throw new UnexpectedTokenException("Expected an object, got a " + f.getType() + ".  ");
    return f.eval(caller, (Token) last.getCar(), frame);
  }
}

class PostfixOperation extends Operation<String> {
  public PostfixOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    PrimitiveObject<?> value = (PrimitiveObject<?>) first.getCar(), ret = (PrimitiveObject<?>) value.clone();
    PrimitiveObject<?>[] p = Interpreter.castPrimitives(value, new PrimitiveObject<>(Interpreter.integerClass, 1));
    if (data.equals("++"))
      value.write(value.getType().makePrimitiveObject(p[0].getType().narrow(p[0].getType().add(p[0].getSource(), p[1].getSource()), value.getType())));
    else if (data.equals("--"))
      value.write(value.getType().makePrimitiveObject(p[0].getType().narrow(p[0].getType().subtract(p[0].getSource(), p[1].getSource()), value.getType())));
    return new Token(ret, ret.getType().getTokenType());
  }
}

class PrefixOperation extends Operation<String> {
  public PrefixOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    if (data.equals("!")) {
      Variable<?> value = (Variable<?>) last.getCar();
      if (!value.getType().equals(Interpreter.booleanClass))
        throw new UnexpectedTypeException("The logical inverse operation, !, is not valid for the type, " + value.getType());
      return new Token(new PrimitiveObject<>(Interpreter.booleanClass, !((PrimitiveObject<Boolean>) value).getSource()), Interpreter.booleanType);
    }
    if (!last.getCarType().equals(Interpreter.identifierType))
      throw new UnexpectedTypeException("The type, " + last.getCarType() + ", is not compatible with the " + data + " operation.");
    AbstractObject<?> val = (AbstractObject<?>) frame.readVariable((String) last.getCar()).getCar();
    if (!Interpreter.isNumber(val.getType()) && !val.getType().equals(Interpreter.characterClass))
      throw new UnexpectedTypeException("The type, " + val.getType() + ", is not compatible with the " + data + " operation.");
    PrimitiveObject<?> value = (PrimitiveObject<?>) val;
    Token result = null;
    if (data.equals("++")) {
      PrimitiveObject<?>[] p = Interpreter.castPrimitives(value, new PrimitiveObject<>(Interpreter.integerClass, 1));
      value.write(value.getType().makePrimitiveObject(p[0].getType().narrow(p[0].getType().add(p[0].getSource(), p[1].getSource()), value.getType())));
      return new Token(value, value.getType().getTokenType());
    }
    else if (data.equals("--")) {
      PrimitiveObject<?>[] p = Interpreter.castPrimitives(value, new PrimitiveObject<>(Interpreter.integerClass, 1));
      value.write(value.getType().makePrimitiveObject(p[0].getType().narrow(p[0].getType().subtract(p[0].getSource(), p[1].getSource()), value.getType())));
      return new Token(value, value.getType().getTokenType());
    }
    else if (data.equals("+")) {
      PrimitiveObject<?>[] p = Interpreter.castPrimitives(value, new PrimitiveObject<>(Interpreter.integerClass, -1), new PrimitiveObject<>(Interpreter.integerClass, 0));
      if (p[0].getType().compareValues(p[0].getSource(), p[2].getSource()) < 1)
        return new Token(value.getType().makePrimitiveObject(p[0].getType().narrow(p[0].getType().multiply(p[0].getSource(), p[1].getSource()),
            value.getType())), value.getType().getTokenType());
    }
    else if (data.equals("-")) {
      PrimitiveObject<?>[] p = Interpreter.castPrimitives(value, new PrimitiveObject<>(Interpreter.integerClass, -1));
      return new Token(value.getType().makePrimitiveObject(p[0].getType().narrow(p[0].getType().multiply(p[0].getSource(), p[1].getSource()),
          value.getType())), value.getType().getTokenType());
    }
    return result;
  }
}

class AdditiveOperation extends Operation<String> {
  public AdditiveOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if ((f.getType().equals(Interpreter.stringClass) || l.getType().equals(Interpreter.stringClass)) && data.equals("+"))
      return new Token(new JavaObject<>(Interpreter.stringClass, f.getSource().toString() + l.getSource().toString()), Interpreter.stringType);
    if ((Interpreter.isNumber(f.getType()) || f.getType().equals(Interpreter.characterClass)) && (Interpreter.isNumber(l.getType()) || l.getType().equals(Interpreter.characterClass))) {
      PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
      if (data.equals("+"))
        return new Token(p[0].getType().makePrimitiveObject(p[0].getType().add(p[0].getSource(), p[1].getSource())), p[0].getType().getTokenType());
      return new Token(p[0].getType().makePrimitiveObject(p[0].getType().subtract(p[0].getSource(), p[1].getSource())), p[0].getType().getTokenType());
    }
    throw new UnexpectedTypeException("The types, " + f.getType().getName() + " and " + l.getType().getName() + ", are not compatible with the operation, " + data + ".");
  }
}

class MultiplicativeOperation extends Operation<String> {
  public MultiplicativeOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if ((f.getType().equals(Interpreter.stringClass) || f.getType().equals(Interpreter.characterClass)) && data.equals("*") && l.getType().equals(Interpreter.integerClass)) {
      int num = (int) ((Variable<Integer>) l).getSource();
      StringBuilder str = new StringBuilder((f.getSource() instanceof String ? ((String) f.getSource()).length() : 1) * num);
      for (int i = 0; i < num; i++)
        str.append(f.getSource());
      return new Token(new JavaObject<String>(Interpreter.stringClass, str.toString()), Interpreter.stringType);
    }
    if ((Interpreter.isNumber(f.getType()) || f.getType().equals(Interpreter.characterClass)) && (Interpreter.isNumber(l.getType()) || l.getType().equals(Interpreter.characterClass))) {
      PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
      if (data.equals("*"))
        return new Token(p[0].getType().makePrimitiveObject(p[0].getType().multiply(p[0].getSource(), p[1].getSource())), p[0].getType().getTokenType());
      else if (data.equals("/"))
        return new Token(p[0].getType().makePrimitiveObject(p[0].getType().divide(p[0].getSource(), p[1].getSource())), p[0].getType().getTokenType());
      // Then this is mod
      return new Token(p[0].getType().makePrimitiveObject(p[0].getType().mod(p[0].getSource(), p[1].getSource())), p[0].getType().getTokenType());
    }
    throw new UnexpectedTypeException("The types, " + f.getType().getName() + " and " + l.getType().getName() + ", are not compatible with the operation, " + data + ".");
  }
}

class RelationalOperation extends Operation<String> {
  public RelationalOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if (!Interpreter.isPrimitive(f.getType()) || !Interpreter.isPrimitive(l.getType()))
      throw new UnexpectedTypeException("The types, " + f.getType() + " and " + l.getType() + ", are not compatible with the " + data + " operation.");
    PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
    switch (data) {
      case "<":
        return new Token(new PrimitiveObject<>(Interpreter.booleanClass, p[0].getType().compareValues(p[0].getSource(), p[1].getSource()) < 0), Interpreter.booleanType);
      case "<=":
        return new Token(new PrimitiveObject<>(Interpreter.booleanClass, p[0].getType().compareValues(p[0].getSource(), p[1].getSource()) <= 0), Interpreter.booleanType);
      case ">":
        return new Token(new PrimitiveObject<>(Interpreter.booleanClass, p[0].getType().compareValues(p[0].getSource(), p[1].getSource()) > 0), Interpreter.booleanType);
      case ">=":
        return new Token(new PrimitiveObject<>(Interpreter.booleanClass, p[0].getType().compareValues(p[0].getSource(), p[1].getSource()) >= 0), Interpreter.booleanType);
      default:
        throw new UnexpectedTokenException(data + " is not a valid relational operator.");
    }
  }
}

class EqualityOperation extends Operation<String> {
  public EqualityOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if (!Interpreter.isPrimitive(f.getType()) || !Interpreter.isPrimitive(l.getType()))
      throw new UnexpectedTypeException("The types, " + f.getType() + " and " + l.getType() + ", are not compatible with the " + data + " operation.");
    PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
    if (data.equals("=="))
      return new Token(new PrimitiveObject<>(Interpreter.booleanClass, p[0].getType().compareValues(p[0].getSource(), p[1].getSource()) == 0), Interpreter.booleanType);
    if (data.equals("!="))
      return new Token(new PrimitiveObject<>(Interpreter.booleanClass, p[0].getType().compareValues(p[0].getSource(), p[1].getSource()) != 0), Interpreter.booleanType);
    throw new UnexpectedTokenException(data + " is not a valid equality operator.");
  }
}

class BitwiseANDOperation extends Operation<String> {
  public BitwiseANDOperation() {
    super("&");
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if ((f.getType().equals(Interpreter.byteClass) || f.getType().equals(Interpreter.shortClass) || f.getType().equals(Interpreter.characterClass) || f.getType().equals(Interpreter.integerClass) ||
        f.getType().equals(Interpreter.longClass)) && (l.getType().equals(Interpreter.byteClass) || l.getType().equals(Interpreter.shortClass) || l.getType().equals(Interpreter.characterClass) ||
            l.getType().equals(Interpreter.integerClass) || l.getType().equals(Interpreter.longClass))) {
      if (f.getType().equals(Interpreter.booleanType) && l.getType().equals(Interpreter.booleanType))
        return new Token(((PrimitiveObject<Boolean>) f).getType().makePrimitiveObject((boolean) ((PrimitiveObject<Boolean>) f).getSource() &
            (boolean) ((PrimitiveObject<Boolean>) l).getSource()), Interpreter.booleanType);
      PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
      return new Token(p[0].getType().makePrimitiveObject(p[0].getType().equals(Interpreter.characterClass) ? (((Character) p[0].getSource()).charValue() & ((Character) p[1].getSource()).charValue()) :
          (((Number) p[0].getSource()).longValue() & ((Number) p[1].getSource()).longValue())), p[0].getType().getTokenType());
    }
    throw new UnexpectedTypeException("The operation, " + data + ", is not defined for the types, " + f.getType() + " and " + l.getType() + ".");
  }
}

class BitwiseXOROperation extends Operation<String> {
  public BitwiseXOROperation() {
    super("^");
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if ((f.getType().equals(Interpreter.byteClass) || f.getType().equals(Interpreter.shortClass) || f.getType().equals(Interpreter.characterClass) || f.getType().equals(Interpreter.integerClass) ||
        f.getType().equals(Interpreter.longClass)) && (l.getType().equals(Interpreter.byteClass) || l.getType().equals(Interpreter.shortClass) || l.getType().equals(Interpreter.characterClass) ||
            l.getType().equals(Interpreter.integerClass) || l.getType().equals(Interpreter.longClass))) {
      if (f.getType().equals(Interpreter.booleanType) && l.getType().equals(Interpreter.booleanType))
        return new Token(((PrimitiveObject<Boolean>) f).getType().makePrimitiveObject((boolean) ((PrimitiveObject<Boolean>) f).getSource() ^
            (boolean) ((PrimitiveObject<Boolean>) l).getSource()), Interpreter.booleanType);
      PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
      return new Token(p[0].getType().makePrimitiveObject(p[0].getType().equals(Interpreter.characterClass) ? (((Character) p[0].getSource()).charValue() ^ ((Character) p[1].getSource()).charValue()) :
          (((Number) p[0].getSource()).longValue() ^ ((Number) p[1].getSource()).longValue())), p[0].getType().getTokenType());
    }
    throw new UnexpectedTypeException("The operation, " + data + ", is not defined for the types, " + f.getType() + " and " + l.getType() + ".");
  }
}

class BitwiseOROperation extends Operation<String> {
  public BitwiseOROperation() {
    super("|");
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar(), l = (Variable<?>) last.getCar();
    if ((f.getType().equals(Interpreter.byteClass) || f.getType().equals(Interpreter.shortClass) || f.getType().equals(Interpreter.characterClass) || f.getType().equals(Interpreter.integerClass) ||
        f.getType().equals(Interpreter.longClass)) && (l.getType().equals(Interpreter.byteClass) || l.getType().equals(Interpreter.shortClass) || l.getType().equals(Interpreter.characterClass) ||
            l.getType().equals(Interpreter.integerClass) || l.getType().equals(Interpreter.longClass))) {
      if (f.getType().equals(Interpreter.booleanType) && l.getType().equals(Interpreter.booleanType))
        return new Token(((PrimitiveObject<Boolean>) f).getType().makePrimitiveObject((boolean) ((PrimitiveObject<Boolean>) f).getSource() |
            (boolean) ((PrimitiveObject<Boolean>) l).getSource()), Interpreter.booleanType);
      PrimitiveObject<?>[] p = Interpreter.castPrimitives((PrimitiveObject<?>) f, (PrimitiveObject<?>) l);
      return new Token(p[0].getType().makePrimitiveObject(p[0].getType().equals(Interpreter.characterClass) ? (((Character) p[0].getSource()).charValue() | ((Character) p[1].getSource()).charValue()) :
          (((Number) p[0].getSource()).longValue() | ((Number) p[1].getSource()).longValue())), p[0].getType().getTokenType());
    }
    throw new UnexpectedTypeException("The operation, " + data + ", is not defined for the types, " + f.getType() + " and " + l.getType() + ".");
  }
}

class LogicalANDOperation extends Operation<String> {
  public LogicalANDOperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar();
    if (!f.getType().equals(Interpreter.booleanClass))
      throw new UnexpectedTypeException("The type, " + f.getType() + ", is not compatible with the attempted operation.");
    if (!((Variable<Boolean>) f).getSource())
      return new Token(new PrimitiveObject<>(Interpreter.booleanClass, false), Interpreter.booleanType);
    if (last.getCarType().equals(Interpreter.blockType))
      last = (Token) last.getCar();
    last = Interpreter.eval(caller, last, frame);
    if (!((Variable<?>) last.getCar()).getType().equals(Interpreter.booleanClass))
      throw new UnexpectedTypeException("The type, " + last.getCarType() + ", is not compatible with the attempted operation.");
    return new Token(new PrimitiveObject<>(Interpreter.booleanClass, ((Variable<Boolean>) last.getCar()).getSource()), Interpreter.booleanType);
  }
}

class LogicalOROperation extends Operation<String> {
  public LogicalOROperation(String data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> f = (Variable<?>) first.getCar();
    if (!f.getType().equals(Interpreter.booleanClass))
      throw new UnexpectedTypeException("The type, " + f.getType() + ", is not compatible with the attempted operation.");
    if (((Variable<Boolean>) f).getSource())
      return new Token(new PrimitiveObject<>(Interpreter.booleanClass, true), Interpreter.booleanType);
    if (last.getCarType().equals(Interpreter.blockType))
      last = (Token) last.getCar();
    last = Interpreter.eval(caller, last, frame);
    if (!((Variable<?>) last.getCar()).getType().equals(Interpreter.booleanClass))
      throw new UnexpectedTypeException("The type, " + last.getCarType() + ", is not compatible with the attempted operation.");
    return new Token(new PrimitiveObject<>(Interpreter.booleanClass, ((Variable<Boolean>) last.getCar()).getSource()), Interpreter.booleanType);
  }
}

class TernaryOperation extends Operation<String> {
 
  public TernaryOperation() {
    super("?");
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    if (!first.getCarType().equals(Interpreter.booleanType))
      throw new UnexpectedTypeException("The first argument to the ternary operator must be a boolean, but it got a " + ((Variable<?>) first.getCar()).getType());
    return Interpreter.eval(caller, ((PrimitiveObject<Boolean>) first.getCar()).getSource() ? ((Ternary) last.getCar()).getTrue() : ((Ternary) last.getCar()).getFalse(), frame);
  }
}

class AssignmentOperation extends Operation<Operation<?>> {
  private final Operator<?> operator;
  private static final Operation<?> placeholder = new Operation<String>("=") {
    @Override
    public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
      return null;
    }
  };
 
  public AssignmentOperation(Operation<?> data, Operator<?> operator) {
    super(data == null ? placeholder : data);
    this.operator = operator;
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    Variable<?> assign = (Variable<?>) (first.getCar() instanceof String ? frame.readVariable((String) first.getCar()) : first.getCar());
    if (data != placeholder)
      last = Interpreter.eval(caller, new Token(assign, Interpreter.variableType, new Token(data, operator, last, Type.TOKEN), Type.TOKEN), frame);
    assign.write(((Variable<?>) last.getCar()).clone());
    return last;
  }
}

class NewOperation extends Operation<String> {
  public NewOperation() {
    super("new");
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    if (last.getCarType().equals(Interpreter.blockType))
      last = (Token) last.getCar();
    String type = (String) last.getCar();
    while ((last = last.getNextToken()).equals(Interpreter.callType))
      type = type + "." + (last = last.getNextToken()).getCar();
    int depth = 0;
    Token indecies = new Token(), head = indecies;
    while (last.getCarType().equals(Interpreter.squareBracketsType)) {
      depth++;
      type = "array:" + type;
      if (last.getCar() instanceof Token)
        head = head.append(Interpreter.eval(caller, (Token) last.getCar(), frame));
      last = last.getNextToken();
    }
    AbstractClass<?> clazz = depth == 0 ? frame.getType(type) : new ArrayClass(type, frame, new Type<Token>(type));
    if (depth != 0) {
      int len = indecies.length();
      if (len > 0) {
        if (len != depth)
          throw new InvalidAssignmentException("Either none of the dimensions specified for an array can have a size or all of them must have a size.");
        if (last.getCarType().equals(Interpreter.curlyBracesType))
          throw new UnexpectedTokenException("If the dimensions of an array have a specified length, then the contents of the array cannot be specified.");
        return clazz.eval(caller, indecies, frame);
      }
      if (len == 0 && !last.getCarType().equals(Interpreter.curlyBracesType))
        throw new UnexpectedTokenException("If none of the dimensions have a specified length, then the contents of the array must be specified.");
      return clazz.eval(caller, last, frame);
    }
    if (!last.getCarType().equals(Interpreter.parenthesesType))
      throw new UnexpectedTokenException("Expected parentheses to complete the constructor call, but got " + last.getCarType());
    return clazz.eval(caller, (Token) last.getCar(), frame);
  }
}

class ParenthesesOperation extends Operation<Token> {
  public ParenthesesOperation(Token data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    if (last != null && last.getCarType().equals(Interpreter.curlyBracesType)) {
      Pair<String, InterpreterMethod> p = InterpreterMethod.initMethod(caller, first, data, last, frame);
      return new Token(p.getY(), p.getY().getType().getTokenType());
    }
    if (first.getCar() instanceof String)
      first = frame.readVariable((String) first.getCar());
    if (first.getCar() instanceof AbstractMethod)
      return ((AbstractMethod<?>) first.getCar()).eval(Interpreter.evalFunctionArguments(caller, data, frame));
    return Interpreter.eval(caller, data, frame);
  }
}

class SquareBracketsOperation extends Operation<Token> {
  public SquareBracketsOperation(Token data) {
    super(data);
  }
 
  @Override
  public Token eval(AbstractClass<?> caller, Token first, Token last, AbstractFrame frame) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
    return ((ArrayObject) (first.getCar() instanceof ArrayObject ? first.getCar() : frame.readVariable((String) first.getCar()).getCar())).eval(caller, Interpreter.eval(caller, data, frame), frame);
  }
}
TOP

Related Classes of scriptingLanguage.BitwiseOROperation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.