package me.tomassetti.turin.definitions;
import me.tomassetti.turin.compiler.ParamUtils;
import me.tomassetti.jvm.JvmType;
import me.tomassetti.turin.resolvers.SymbolResolver;
import me.tomassetti.turin.parser.ast.expressions.ActualParam;
import me.tomassetti.turin.symbols.FormalParameter;
import me.tomassetti.turin.typesystem.TypeUsage;
import java.util.*;
public abstract class InternalInvokableDefinition {
public boolean match(SymbolResolver resolver, List<ActualParam> actualParams) {
// all named parameters should be after the named ones
if (!ParamUtils.verifyOrder(actualParams)) {
throw new IllegalArgumentException("Named params should all be grouped after the positional ones");
}
if (actualParams.stream().filter((p)->p.isAsterisk()).findFirst().isPresent()) {
if (actualParams.size() > 1) {
throw new IllegalArgumentException("Too many params");
}
return !matchAsterisk(resolver, actualParams.get(0), getFormalParameters()).isPresent();
} else {
return !matchNotAsterisk(resolver, actualParams).isPresent();
}
}
private Optional<String> matchAsterisk(SymbolResolver resolver, ActualParam actualParam, List<? extends FormalParameter> formalParameters) {
TypeUsage paramType = actualParam.getValue().calcType();
// it needs to have all the getters for the non-default parameters
// all the other getters that match default params have to be the right type
if (!paramType.isReference()) {
return Optional.of("An asterisk param should be an object");
}
List<ActualParam> actualParams = new ArrayList<>();
TypeDefinition typeDefinition = paramType.asReferenceTypeUsage().getTypeDefinition();
for (FormalParameter formalParameter : formalParameters) {
String getterName = ParamUtils.getterName(formalParameter, resolver);
if (typeDefinition.hasMethodFor(getterName, Collections.emptyList(), false)) {
TypeUsage res = typeDefinition.returnTypeWhenInvokedWith(getterName, Collections.emptyList(), false);
if (!res.canBeAssignedTo(formalParameter.getType())){
return Optional.of("the given value has a getter '" + getterName + "' with incompatible type");
}
} else {
if (!formalParameter.hasDefaultValue()) {
return Optional.of("the given value has not a getter '" + getterName + "'");
}
}
}
return Optional.empty();
}
private Optional<String> matchNotAsterisk(SymbolResolver resolver, List<ActualParam> actualParams) {
Set<String> paramsAssigned = new HashSet<>();
List<? extends FormalParameter> formalParameters = getFormalParameters();
List<ActualParam> unnamedParams = ParamUtils.unnamedParams(actualParams);
List<ActualParam> namedParams = ParamUtils.namedParams(actualParams);
// use the unnamed params
if (unnamedParams.size() > formalParameters.size()) {
return Optional.of("Too many unnamed params: " + actualParams);
}
int i = 0;
for (ActualParam param : unnamedParams) {
if (!param.getValue().calcType().canBeAssignedTo(formalParameters.get(i).getType())){
return Optional.of("TODO");
}
paramsAssigned.add(formalParameters.get(i).getName());
i++;
}
// use the named params
Map<String, FormalParameter> validNames = new HashMap<>();
formalParameters.forEach((p) -> validNames.put(p.getName(), p));
for (ActualParam param : namedParams) {
if (paramsAssigned.contains(param.getName())) {
return Optional.of("Param " + param.getName() + " assigned several times");
}
if (!validNames.containsKey(param.getName())) {
return Optional.of("Unknown param " + param.getName());
}
if (!param.getValue().calcType().canBeAssignedTo(validNames.get(param.getName()).getType())){
return Optional.of("TODO");
}
paramsAssigned.add(param.getName());
}
// verify that all properties with no default or initial value have been assigned
for (FormalParameter formalParameter : formalParameters) {
if (!paramsAssigned.contains(formalParameter.getName()) && !formalParameter.hasDefaultValue()) {
return Optional.of("Param not assigned: " + formalParameter.getName());
}
}
return Optional.empty();
}
private List<? extends FormalParameter> formalParameters;
public InternalInvokableDefinition(List<? extends FormalParameter> formalParameters) {
this.formalParameters = formalParameters;
}
public List<? extends FormalParameter> getFormalParameters() {
return formalParameters;
}
public boolean matchJvmTypes(SymbolResolver resolver, List<JvmType> jvmTypes) {
List<? extends FormalParameter> formalParameters = getFormalParameters();
if (formalParameters.size() != jvmTypes.size()) {
return false;
}
for (int i=0;i<jvmTypes.size();i++) {
FormalParameter formalParameter = formalParameters.get(i);
JvmType jvmType = jvmTypes.get(i);
if (!formalParameter.getType().jvmType().isAssignableBy(jvmType)) {
return false;
}
}
return true;
}
public boolean hasDefaultParams() {
return getFormalParameters().stream().filter((p)->p.hasDefaultValue()).findFirst().isPresent();
}
public abstract InternalConstructorDefinition asConstructor();
public abstract InternalMethodDefinition asMethod();
public abstract boolean isConstructor();
public abstract boolean isMethod();
public abstract TypeUsage getReturnType();
public abstract InternalInvokableDefinition apply(Map<String, TypeUsage> typeParams);
}