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);
}
}