package scriptingLanguage.variables;
import java.util.ArrayList;
import lipstone.joshua.customStructures.tuples.Pair;
import scriptingLanguage.Interpreter;
import scriptingLanguage.Token;
import scriptingLanguage.Type;
import scriptingLanguage.errors.InterpreterException;
import scriptingLanguage.errors.InvalidAccessException;
import scriptingLanguage.errors.UnexpectedTokenException;
import scriptingLanguage.errors.UnexpectedTypeException;
import scriptingLanguage.frames.AbstractFrame;
import scriptingLanguage.frames.Frame;
public class InterpreterMethod extends AbstractMethod<Token> {
private AbstractClass<?> container;
private Parameter[] parameters;
public InterpreterMethod(InterpreterMethodClass type) {
super(type, null, null);
container = null;
parameters = null;
}
public InterpreterMethod(InterpreterMethod base, AbstractFrame newFrame) {
this((InterpreterMethodClass) base.getType(), base.container, base.getSource(), newFrame, base.getParameters());
}
public InterpreterMethod(InterpreterMethodClass type, AbstractClass<?> container, Token source, AbstractFrame declared, Parameter... parameters) {
super(type, source, declared);
this.container = container;
this.parameters = parameters;
}
public static Pair<String, InterpreterMethod> initMethod(AbstractClass<?> caller, Token type, Token parameters, Token body, AbstractFrame declared) throws InterpreterException {
return initMethod(caller, "", (AbstractClass<?>) (type.getCar() instanceof String ? declared.getType((String) type.getCar()) : type.getCar()), parameters, body, declared);
}
public static Pair<String, InterpreterMethod> initMethod(AbstractClass<?> caller, Token source, AbstractFrame declared) throws InterpreterException {
//source s/b [type] [name](<args>) { body } or [type] (<args>) { body }
if (!source.getCarType().equals(Interpreter.identifierType))
throw new UnexpectedTokenException("Expected an identifier, got a " + source.getCarType().getName() + ". " + source.toString());
String t = (String) source.getCar();
while ((source = source.getNextToken()).equals(Interpreter.callType))
t = t + ((String) source.getCar()) + (source = source.getNextToken()).getCar();
AbstractClass<?> type = declared.getType(declared.getImport(t));
if (source.getCarType().equals(Interpreter.parenthesesType))
return initMethod(caller, "", type, (Token) (source = source.getNextToken()).getCar(), source.getNextToken(), declared);
return initMethod(caller, (String) source.getCar(), type, (Token) (source = source.getNextToken()).getCar(), source.getNextToken(), declared);
}
public static Pair<String, InterpreterMethod> initMethod(AbstractClass<?> caller, String name, AbstractClass<?> type, Token params, Token body, AbstractFrame declared) throws InterpreterException {
name = name + ":{";
ArrayList<Parameter> parameters = new ArrayList<>();
String typeName = type.getName() + ":{" + params.toString() + "}";
Token par = params;
while (!params.isNull()) {
String p = (String) params.getCar();
while ((params = params.getNextToken()).equals(Interpreter.callType))
p = p + ((String) ((Variable<?>) params.getCar()).getSource()) + ((Variable<?>) (params = params.getNextToken()).getCar()).getSource();
name = name + p + ", ";
parameters.add(new Parameter(declared.getType(p), (String) params.getCar()));
if ((params = params.getNextToken()).getCarType().equals(Interpreter.separatorType)) {
params = params.getNextToken();
continue;
}
}
if (name.endsWith(", "))
name = name.substring(0, name.length() - 2);
name = name + "}";
InterpreterMethodClass finalType = new InterpreterMethodClass(typeName, par, type, declared, new Type<Object>(typeName));
if (parameters.size() > 0)
return new Pair<String, InterpreterMethod>(name, new InterpreterMethod(finalType, caller, (Token) body.getCar(), declared, parameters.toArray(new Parameter[parameters.size()])));
else
return new Pair<String, InterpreterMethod>(name, new InterpreterMethod(finalType, caller, (Token) body.getCar(), declared));
}
/**
* @param caller
* the object that called this method
*/
@Override
public Token eval(AbstractClass<?> caller, Token parameters, AbstractFrame frame) throws InterpreterException {
ArrayList<Token> args = new ArrayList<>();
for (int i = 0; i < this.getParameters().length && !parameters.isNull(); i++) {
Token arg = parameters.singular(), head = arg;
while (!(parameters = parameters.getNextToken()).getCarType().equals(Interpreter.separatorType) && !parameters.isNull())
head = head.append(parameters.singular());
Token value = frame.getInterpreter().eval(caller, arg, frame);
//TODO add type length checking, var args
if (((AbstractObject<?>) value.getCar()).getType().extend(this.getParameters()[i].getType()) == -1)
throw new UnexpectedTypeException(((AbstractObject<?>) value.getCar()).getType().getName() + " cannot be cast to " + this.getParameters()[i].getType().getName());
args.add(arg);
parameters = parameters.getNextToken();
}
return eval(args);
}
@Override
public Token eval(ArrayList<Token> args) throws ArrayIndexOutOfBoundsException, IllegalArgumentException, InterpreterException {
Frame base = new Frame(getDeclared(), getDeclared().getInterpreter());
for (int i = 0; i < args.size(); i++)
base.writeVariable(this.getParameters()[i].getName(), args.get(i), true);
Token result = getDeclared().getInterpreter().eval(container, getSource(), base);
AbstractClass<?> returnType = ((AbstractMethodClass) getType()).getReturnType();
if (returnType.equals(Interpreter.voidClass) && !result.isNull())
throw new UnexpectedTypeException("Cannot return a value from a void function.");
if (((Variable<?>) (result = result.getNextToken()).getCar()).getType().extend(returnType) == -1)
if (Interpreter.isPrimitive(getType()) && Interpreter.isPrimitive(((Variable<?>) result.getCar()).getType()))
return ((PrimitiveClass<?>) returnType).eval(((PrimitiveClass<?>) ((Variable<?>) result.getCar()).getType()).cast(((Variable<?>) result.getCar()).getSource(), (PrimitiveClass<?>) returnType));
else
throw new UnexpectedTypeException("Cannot return a " + ((Variable<?>) result.getCar()).getType() + " from a function that should return a " + getType());
return result;
}
/**
* @return the parameters
*/
public Parameter[] getParameters() {
return parameters;
}
@Override
public void write(Variable<?> data) throws InvalidAccessException {
super.write(data);
InterpreterMethod newData = (InterpreterMethod) data;
container = newData.container;
parameters = newData.parameters;
}
@Override
public Variable<Token> clone() {
return new InterpreterMethod((InterpreterMethodClass) getType(), container, getSource(), getDeclared(), parameters);
}
}