package me.tomassetti.turin.parser.ast.invokables;
import me.tomassetti.jvm.JvmMethodDefinition;
import me.tomassetti.jvm.JvmNameUtils;
import me.tomassetti.jvm.JvmType;
import me.tomassetti.turin.definitions.InternalInvokableDefinition;
import me.tomassetti.turin.definitions.InternalMethodDefinition;
import me.tomassetti.turin.resolvers.SymbolResolver;
import me.tomassetti.turin.parser.ast.FormalParameterNode;
import me.tomassetti.turin.parser.ast.Named;
import me.tomassetti.turin.parser.ast.Node;
import me.tomassetti.turin.parser.ast.annotations.AnnotationUsage;
import me.tomassetti.turin.parser.ast.expressions.InvokableExpr;
import me.tomassetti.turin.parser.ast.statements.Statement;
import me.tomassetti.turin.parser.ast.typeusage.TypeUsageNode;
import me.tomassetti.turin.symbols.FormalParameter;
import me.tomassetti.turin.symbols.Symbol;
import me.tomassetti.turin.typesystem.InvokableReferenceTypeUsage;
import me.tomassetti.turin.typesystem.TypeUsage;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class FunctionDefinitionNode extends InvokableDefinitionNode implements Named, Symbol {
public static final String CLASS_PREFIX = "Function_";
public static final String INVOKE_METHOD_NAME = "invoke";
private List<AnnotationUsage> annotations = new ArrayList<>();
public void addAnnotation(AnnotationUsage annotation) {
annotation.setParent(this);
annotations.add(annotation);
}
public List<AnnotationUsage> getAnnotations() {
return annotations;
}
@Override
public Iterable<Node> getChildren() {
List<Node> children = new ArrayList<>();
for (Node n : super.getChildren()) {
children.add(n);
}
children.addAll(annotations);
return children;
}
public FunctionDefinitionNode(String name, TypeUsageNode returnType, List<FormalParameterNode> parameters, Statement body) {
super(parameters, body, name, returnType);
}
@Override
public TypeUsage calcType() {
InvokableReferenceTypeUsage invokableReferenceTypeUsage = new InvokableReferenceTypeUsage(internalInvokableDefinition());
return invokableReferenceTypeUsage;
}
private InternalInvokableDefinition internalInvokableDefinition() {
return new InternalMethodDefinition(INVOKE_METHOD_NAME, parameters, returnType, jvmMethodDefinition(symbolResolver()));
}
@Override
public Optional<List<? extends FormalParameter>> findFormalParametersFor(InvokableExpr invokable) {
return Optional.of(parameters);
}
protected String getGeneratedClassQualifiedName() {
String qName = this.contextName() + "." + CLASS_PREFIX + name;
if (!JvmNameUtils.isValidQualifiedName(qName)) {
throw new IllegalStateException(qName);
}
return qName;
}
public JvmMethodDefinition jvmMethodDefinition(SymbolResolver resolver) {
String qName = getGeneratedClassQualifiedName();
String descriptor = "(" + String.join("", parameters.stream().map((fp)->fp.getType().jvmType().getDescriptor()).collect(Collectors.toList())) + ")" + returnType.jvmType().getDescriptor();
return new JvmMethodDefinition(JvmNameUtils.canonicalToInternal(qName), INVOKE_METHOD_NAME, descriptor, true, false);
}
public boolean match(List<JvmType> argsTypes, SymbolResolver resolver) {
if (argsTypes.size() != parameters.size()) {
return false;
}
int i = 0;
for (FormalParameterNode formalParameter : parameters) {
if (!formalParameter.getType().jvmType().isAssignableBy(argsTypes.get(i))) {
return false;
}
i++;
}
return true;
}
public String getQualifiedName() {
return contextName() + "." + name;
}
}