package scriptingLanguage.frames;
import java.util.LinkedHashMap;
import java.util.Set;
import lipstone.joshua.customStructures.tuples.Pair;
import scriptingLanguage.Interpreter;
import scriptingLanguage.Token;
import scriptingLanguage.errors.InterpreterException;
import scriptingLanguage.errors.UnexpectedTypeException;
import scriptingLanguage.errors.VariableNotFoundException;
import scriptingLanguage.frames.errors.FrameAccessException;
import scriptingLanguage.variables.AbstractClass;
import scriptingLanguage.variables.Variable;
public abstract class AbstractFrame {
protected final AbstractFrame previous;
protected LinkedHashMap<String, Pair<AbstractClass<?>, Token>> variables;
protected final Interpreter interpreter;
public AbstractFrame(AbstractFrame base, AbstractFrame previous) {
this.previous = previous == null ? base.previous : previous;
interpreter = base.interpreter;
}
public AbstractFrame(AbstractFrame previous, Interpreter interpreter) {
this.previous = previous;
this.interpreter = interpreter;
}
public abstract boolean writeVariable(String variable, Token value, boolean isNew) throws InterpreterException;
public Token readVariable(String variable) throws InterpreterException {
AbstractFrame frame = this;
do {
if (frame.variables.containsKey(variable))
return frame.variables.get(variable).getY();
} while ((frame = frame.previous) != null);
if (variable.contains(":{")) { //Check for functions with inheritance
frame = this;
String[] temp = variable.substring(variable.indexOf(":{") + 2, variable.length() - 1).split(", ");
Variable<?>[] varParams = new Variable<?>[temp.length];
for (int i = 0; i < temp.length; i++)
varParams[i] = getType(temp[i]); //Convert these to types for efficiency
int closest = -1;
String name = variable.substring(0, variable.indexOf(":{")), closestKey = "";
do {
for (String key : frame.variables.keySet()) {
if (key.indexOf(":{") < 1 || !key.substring(0, key.indexOf(":{")).equals(name))
continue;
String[] keyParams = key.substring(key.indexOf(":{") + 2, key.length() - 1).split(", ");
if (keyParams.length != varParams.length)
continue;
int close = 0, dist = 0;
for (int i = 0; i < keyParams.length; i++) {
dist = varParams[i].extend(getType(keyParams[i]));
if (dist == -1)
break;
close += dist;
}
if (dist == -1)
continue;
if (closest == -1 || close < closest) {
closest = close;
closestKey = key;
}
}
if (closest > -1)
return frame.variables.get(closestKey).getY();
} while ((frame = frame.previous) != null);
}
throw new VariableNotFoundException(variable + " is not defined in the current frame.");
}
public abstract void addImport(String name) throws FrameAccessException;
public abstract String getImport(String name) throws FrameAccessException;
public abstract Token makeNew(AbstractClass<?> caller, String type, Token parameters, AbstractFrame call) throws InterpreterException, ArrayIndexOutOfBoundsException;
public abstract void addType(String name, AbstractClass<?> type) throws FrameAccessException;
public final AbstractClass<?> getType(String type) throws UnexpectedTypeException, FrameAccessException {
switch (type) {
case "Object":
return Interpreter.ObjectClass;
case "Integer":
return Interpreter.integerClass;
case "Double":
return Interpreter.doubleClass;
case "Boolean":
return Interpreter.booleanClass;
case "Character":
return Interpreter.characterClass;
case "String":
return Interpreter.stringClass;
case "Complex":
return Interpreter.complexClass;
case "void":
case "Void":
return Interpreter.voidClass;
case "Long":
return Interpreter.longClass;
case "Float":
return Interpreter.floatClass;
case "Byte":
return Interpreter.byteClass;
case "Short":
return Interpreter.shortClass;
default:
return getType0(type);
}
}
protected abstract AbstractClass<?> getType0(String type) throws UnexpectedTypeException, FrameAccessException;
public final boolean hasType(String type) throws FrameAccessException {
return type.equals("Integer") || type.equals("String") || type.equals("Character") || type.equals("Double") || type.equals("Boolean") || type.equals("Complex") || type.equals("Long") ||
type.equals("Short") || type.equals("Byte") || type.equalsIgnoreCase("void") || hasType0(type);
}
protected abstract boolean hasType0(String type) throws FrameAccessException;
public boolean hasVariable(String variable) {
return variables.containsKey(variable) || (previous != null && previous.hasVariable(variable));
}
public AbstractFrame getPrevious() {
return previous;
}
public Interpreter getInterpreter() {
return interpreter;
}
public Set<String> getVariables() {
return variables.keySet();
}
}