package scotch.symbol.descriptor;
import static java.util.Collections.sort;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static scotch.symbol.type.TypeDescriptors.sum;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import lombok.EqualsAndHashCode;
import scotch.symbol.Symbol;
import scotch.symbol.type.TypeDescriptor;
import scotch.symbol.type.VariableTypeDescriptor;
@EqualsAndHashCode(callSuper = false)
public class DataTypeDescriptor {
public static Builder builder(Symbol symbol) {
return new Builder(symbol);
}
private final Symbol symbol;
private final List<TypeDescriptor> parameters;
private final Map<Symbol, DataConstructorDescriptor> constructors;
private DataTypeDescriptor(Symbol symbol, List<TypeDescriptor> parameters, List<DataConstructorDescriptor> constructors) {
List<DataConstructorDescriptor> sortedConstructors = new ArrayList<>(constructors);
sort(sortedConstructors);
this.symbol = symbol;
this.parameters = new ArrayList<>(parameters);
this.constructors = new LinkedHashMap<>();
sortedConstructors.forEach(constructor -> this.constructors.put(constructor.getSymbol(), constructor));
}
public Optional<DataConstructorDescriptor> getConstructor(Symbol symbol) {
return Optional.ofNullable(constructors.get(symbol));
}
public List<TypeDescriptor> getParameters() {
return parameters;
}
public Symbol getSymbol() {
return symbol;
}
public TypeDescriptor getType() {
return sum(symbol, parameters);
}
public TypeDescriptor getType(Supplier<VariableTypeDescriptor> generator) {
return sum(symbol, parameters.stream()
.map(parameter -> generator.get().withContext(parameter.getContext()))
.collect(toList()));
}
public DataTypeDescriptor mapParameters(Map<TypeDescriptor, TypeDescriptor> mappedParameters) {
List<TypeDescriptor> reifiedParameters = parameters.stream()
.map(parameter -> Optional.of(mappedParameters.get(parameter)).<RuntimeException>orElseThrow(UnsupportedOperationException::new /* TODO */))
.collect(toList());
return new DataTypeDescriptor(symbol, reifiedParameters, constructors.values().stream()
.map(constructor -> constructor.mapParameters(mappedParameters))
.collect(toList()));
}
@Override
public String toString() {
return symbol.getSimpleName()
+ (parameters.isEmpty() ? "" : " " + parameters.stream().map(Object::toString).collect(joining(", ")))
+ " = " + constructors.values().stream().map(Object::toString).collect(joining(" | "));
}
public static final class Builder {
private final Symbol symbol;
private Optional<String> className;
private List<TypeDescriptor> parameters;
private List<DataConstructorDescriptor> constructors;
private Builder(Symbol symbol) {
this.symbol = symbol;
this.className = Optional.empty();
this.parameters = new ArrayList<>();
this.constructors = new ArrayList<>();
}
public Builder addConstructor(DataConstructorDescriptor constructor) {
constructors.add(constructor);
return this;
}
public Builder addParameter(TypeDescriptor type) {
parameters.add(type);
return this;
}
public DataTypeDescriptor build() {
return new DataTypeDescriptor(symbol, parameters, constructors);
}
public Builder withClassName(String className) {
this.className = Optional.of(className);
return this;
}
public Builder withConstructors(List<DataConstructorDescriptor> constructors) {
constructors.forEach(this::addConstructor);
return this;
}
public Builder withParameters(List<TypeDescriptor> parameters) {
parameters.forEach(this::addParameter);
return this;
}
}
}