package scriptingLanguage.frames;
import java.util.LinkedHashMap;
import lipstone.joshua.customStructures.tuples.Pair;
import scriptingLanguage.Interpreter;
import scriptingLanguage.Token;
import scriptingLanguage.errors.InterpreterException;
import scriptingLanguage.errors.InvalidAssignmentException;
import scriptingLanguage.errors.UnexpectedTypeException;
import scriptingLanguage.errors.VariableNotFoundException;
import scriptingLanguage.frames.errors.FrameAccessException;
import scriptingLanguage.variables.AbstractClass;
import scriptingLanguage.variables.Variable;
public class Frame extends AbstractFrame {
/**
* Used for in-method nested frames
*
* @param base
*/
public Frame(AbstractFrame base) {
super(base.previous, base.interpreter);
variables = new LinkedHashMap<>(base.variables);
}
/**
* Creates a shallow copy of the given frame such that only the maps are guarenteed to be separate.</br> If staticFrame
* is null, it uses the types from base.
*
* @param base
* the frame to copy
* @param staticFrame
* the static frame or null
*/
public Frame(AbstractFrame base, AbstractFrame previous) {
super(base, previous);
variables = new LinkedHashMap<>(base.variables);
}
public Frame(AbstractFrame previous, Interpreter interpreter) {
super(previous, interpreter);
this.variables = new LinkedHashMap<>();
}
public boolean writeVariable(String variable, AbstractClass<?> type) throws InvalidAssignmentException {
if (variables.containsKey(variable))
throw new InvalidAssignmentException("The a variable called " + variable + " already exists in this frame.");
variables.put(variable, new Pair<AbstractClass<?>, Token>(type, null));
return true;
}
@Override
public boolean writeVariable(String variable, Token value, boolean isNew) throws InterpreterException {
if (isNew) {
if (variables.containsKey(variable))
throw new InvalidAssignmentException("The a variable called " + variable + " already exists in this frame.");
variables.put(variable, new Pair<AbstractClass<?>, Token>(((Variable<?>) value.getCar()).getType(), value));
return true;
}
if (variables.containsKey(variable)) {
Pair<AbstractClass<?>, Token> pair = variables.get(variable);
if (((Variable<?>) value.getCar()).getType().extend(pair.getX()) != -1) {
pair.setY(value);
return true;
}
throw new InvalidAssignmentException("Cannot assign a value of the type, " + ((Variable<?>) value.getCar()).getType() + ", to the type " + pair.getX());
}
else if (previous != null && !(previous instanceof RootFrame))
return previous.writeVariable(variable, value, false); //Cannot be a first assignment
throw new VariableNotFoundException(variable + " has not been declared in this frame or scope.");
}
@Override
public Token readVariable(String variable) throws InterpreterException {
try {
return super.readVariable(variable);
}
catch (VariableNotFoundException e) {
return getRoot().readVariable(variable);
}
}
@Override
public AbstractClass<?> getType0(String type) throws UnexpectedTypeException, FrameAccessException {
return getRoot().getType(type);
}
protected RootFrame getRoot() throws FrameAccessException {
AbstractFrame frame = this;
do
if (frame instanceof RootFrame)
return (RootFrame) frame;
while ((frame = frame.previous) != null);
throw new FrameAccessException("Could not access the root frame.");
}
@Override
public void addImport(String name) throws FrameAccessException {
getRoot().addImport(name);
}
@Override
public String getImport(String name) throws FrameAccessException {
return getRoot().getImport(name);
}
@Override
public boolean hasType0(String type) throws FrameAccessException {
return getRoot().hasType0(type);
}
@Override
public void addType(String name, AbstractClass<?> type) throws FrameAccessException {
getRoot().addType(name, type);
}
@Override
public Token makeNew(AbstractClass<?> caller, String type, Token parameters, AbstractFrame call) throws InterpreterException, ArrayIndexOutOfBoundsException {
return getRoot().makeNew(caller, type, parameters, call);
}
}