package scotch.compiler.syntax.value; import static java.util.stream.Collectors.toList; import static lombok.AccessLevel.PACKAGE; import static scotch.compiler.error.SymbolNotFoundError.symbolNotFound; import static scotch.compiler.syntax.value.NoBindingError.noBinding; import static scotch.compiler.syntax.value.Values.method; import static scotch.compiler.syntax.value.Values.unboundMethod; import static scotch.compiler.syntax.type.Types.fn; import static scotch.compiler.syntax.type.Types.instance; import java.util.ArrayList; import java.util.Collections; import java.util.List; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.ToString; import scotch.compiler.analyzer.DependencyAccumulator; import scotch.compiler.analyzer.NameAccumulator; import scotch.compiler.analyzer.NameQualifier; import scotch.compiler.analyzer.OperatorAccumulator; import scotch.compiler.analyzer.PrecedenceParser; import scotch.compiler.analyzer.TypeChecker; import scotch.compiler.intermediate.IntermediateGenerator; import scotch.compiler.intermediate.IntermediateValue; import scotch.compiler.syntax.pattern.PatternReducer; import scotch.compiler.syntax.reference.ValueReference; import scotch.compiler.text.SourceLocation; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.InstanceType; import scotch.compiler.syntax.type.Type; @AllArgsConstructor(access = PACKAGE) @EqualsAndHashCode(callSuper = false) @ToString(exclude = "sourceLocation") public class UnboundMethod extends Value { private final SourceLocation sourceLocation; private final ValueReference valueRef; private final Type type; @Override public Value accumulateDependencies(DependencyAccumulator state) { throw new UnsupportedOperationException(); } @Override public Value accumulateNames(NameAccumulator state) { throw new UnsupportedOperationException(); } @Override public IntermediateValue generateIntermediateCode(IntermediateGenerator state) { throw new UnsupportedOperationException(); // TODO } @Override public Value bindMethods(TypeChecker typeChecker) { throw new UnsupportedOperationException(); } @Override public Value bindTypes(TypeChecker typeChecker) { return bind(typeChecker).bindTypes(typeChecker); } @Override public Value checkTypes(TypeChecker typeChecker) { return this; } @Override public Value defineOperators(OperatorAccumulator state) { throw new UnsupportedOperationException(); } @Override public SourceLocation getSourceLocation() { return sourceLocation; } public Symbol getSymbol() { return valueRef.getSymbol(); } @Override public Type getType() { return type; } @Override public Value parsePrecedence(PrecedenceParser state) { throw new UnsupportedOperationException(); } @Override public Value qualifyNames(NameQualifier state) { throw new UnsupportedOperationException(); } @Override public Value reducePatterns(PatternReducer reducer) { throw new UnsupportedOperationException(); // TODO } @Override public Value withType(Type type) { return unboundMethod(sourceLocation, valueRef, type); } private Value bind(TypeChecker state) { return state.getRawValue(valueRef) .map(valueType -> { List<InstanceType> instances = listInstanceTypes(valueType); return state.getRawValue(valueRef) .map(rawValue -> rawValue.zip(type, state) .map(map -> instances.stream() .map(instance -> instance.withBinding(map.get(instance.getBinding()))) .collect(toList())) .map(instanceTypes -> method(sourceLocation, valueRef, instanceTypes, state.generate(getMethodType(instanceTypes)))) .orElseGet(() -> { state.error(noBinding(getSymbol(), sourceLocation)); return this; })) .orElseGet(() -> notFound(state)); }) .orElseGet(() -> notFound(state)); } private UnboundMethod notFound(TypeChecker state) { state.error(symbolNotFound(valueRef.getSymbol(), sourceLocation)); return this; } private Type getMethodType(List<InstanceType> instances) { List<Type> reversedInstances = new ArrayList<>(instances); Collections.reverse(reversedInstances); return reversedInstances.stream().reduce(type, (left, right) -> fn(right, left)); } private List<InstanceType> listInstanceTypes(Type valueType) { return valueType.getContexts().stream() .map(pair -> pair.into((type, symbol) -> instance(symbol, type.simplify()))) .collect(toList()); } }