CallableType.ceylon

import ceylon.ast.core {
    CallableType,
    DefaultedType,
    Node,
    PrimaryType,
    SpreadType,
    Type,
    TypeList,
    VariadicType
}
import com.redhat.ceylon.compiler.typechecker.tree {
    JNode=Node,
    Tree {
        JFunctionType=FunctionType,
        JSpreadType=SpreadType,
        JType=Type
    }
}
import ceylon.interop.java {
    CeylonIterable
}

"Converts a RedHat AST [[FunctionType|JFunctionType]] to a `ceylon.ast` [[CallableType]]."
shared CallableType callableTypeToCeylon(JFunctionType callableType, Anything(JNode,Node) update = noop) {
    TypeList|SpreadType argumentTypes;
    if (is JSpreadType spreadType = CeylonIterable(callableType.argumentTypes).first) {
        "Cannot have any other types beside a spread type"
        assert (callableType.argumentTypes.size() == 1);
        argumentTypes = spreadTypeToCeylon(spreadType, update);
    } else {
        variable VariadicType? variadicType = null;
        <Type|DefaultedType>[] argumentTypesSeq = CeylonIterable(callableType.argumentTypes).collect((JType jtype) {
                value typeIsh = typeIshToCeylon(jtype, update);
                if (is VariadicType typeIsh) {
                    "Can’t have multiple variadic types"
                    assert (!(variadicType exists));
                    variadicType = typeIsh;
                    return null; // filtered out
                } else {
                    assert (is Type|DefaultedType typeIsh);
                    "Can’t have non-variadic type after variadic type"
                    assert (!(variadicType exists));
                    return typeIsh;
                }
            }).select((Anything a) => a exists).collect((Type|DefaultedType? element) {
                assert (exists element);
                return element;
            });
        argumentTypes = TypeList(argumentTypesSeq, variadicType);
        update(callableType, argumentTypes);
    }
    assert (is PrimaryType returnType = typeToCeylon(callableType.returnType, update));
    value result = CallableType(returnType, argumentTypes);
    update(callableType, result);
    return result;
}

"Compiles the given [[code]] for a Callable Type
 into a [[CallableType]] using the Ceylon compiler
 (more specifically, the rule for an `primaryType`)."
shared CallableType? compileCallableType(String code, Anything(JNode,Node) update = noop) {
    if (is JFunctionType jFunctionType = createParser(code).primaryType()) {
        return callableTypeToCeylon(jFunctionType, update);
    } else {
        return null;
    }
}