package scotch.compiler.syntax.scope;
import static java.util.stream.Collectors.toList;
import static scotch.compiler.syntax.type.Types.toType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import scotch.compiler.syntax.pattern.PatternCase;
import scotch.compiler.syntax.reference.ClassReference;
import scotch.symbol.MethodSignature;
import scotch.symbol.Operator;
import scotch.symbol.Symbol;
import scotch.symbol.SymbolEntry;
import scotch.symbol.SymbolResolver;
import scotch.symbol.descriptor.DataConstructorDescriptor;
import scotch.symbol.descriptor.DataTypeDescriptor;
import scotch.symbol.descriptor.TypeClassDescriptor;
import scotch.symbol.descriptor.TypeInstanceDescriptor;
import scotch.compiler.syntax.type.SumType;
import scotch.compiler.syntax.type.Type;
import scotch.compiler.syntax.type.TypeScope;
import scotch.compiler.syntax.type.Unification;
import scotch.compiler.syntax.type.VariableType;
import scotch.compiler.syntax.util.SymbolGenerator;
public abstract class BlockScope extends Scope {
protected final TypeScope types;
protected final String moduleName;
protected final Map<Symbol, SymbolEntry> entries;
protected final Set<Symbol> dependencies;
protected final Map<Symbol, List<PatternCase>> patternCases;
protected final SymbolResolver resolver;
protected final SymbolGenerator symbolGenerator;
protected Scope parent;
public BlockScope(Scope parent, TypeScope types, String moduleName, SymbolResolver resolver, SymbolGenerator symbolGenerator) {
this.symbolGenerator = symbolGenerator;
this.dependencies = new HashSet<>();
this.entries = new HashMap<>();
this.types = types;
this.moduleName = moduleName;
this.parent = parent;
this.patternCases = new LinkedHashMap<>();
this.resolver = resolver;
}
@Override
public void addDependency(Symbol symbol) {
if (!isExternal(symbol)) {
dependencies.add(symbol);
}
}
@Override
public void addPattern(Symbol symbol, PatternCase pattern) {
patternCases.computeIfAbsent(symbol, k -> new ArrayList<>()).add(pattern);
}
@Override
public Unification bind(VariableType variableType, Type target) {
return types.bind(variableType, target);
}
@Override
public void defineDataConstructor(Symbol symbol, DataConstructorDescriptor descriptor) {
define(symbol).defineDataConstructor(descriptor);
}
@Override
public void defineDataType(Symbol symbol, DataTypeDescriptor descriptor) {
define(symbol).defineDataType(descriptor);
}
@Override
public void defineOperator(Symbol symbol, Operator operator) {
define(symbol).defineOperator(operator);
}
@Override
public void defineSignature(Symbol symbol, Type type) {
define(symbol).defineSignature(type.toDescriptor());
}
@Override
public void defineValue(Symbol symbol, Type type) {
define(symbol).defineValue(type.toDescriptor(), computeValueMethod(symbol, type));
}
@Override
public void extendContext(Type type, Set<Symbol> additionalContext) {
types.extendContext(type, additionalContext);
}
@Override
public void generalize(Type type) {
types.generalize(type);
}
@Override
public Type generate(Type type) {
return types.generate(type);
}
@Override
public Set<Symbol> getDependencies() {
return new HashSet<>(dependencies);
}
@Override
public Scope getParent() {
return parent;
}
@Override
public Map<Symbol, List<PatternCase>> getPatternCases() {
return patternCases;
}
@Override
public Optional<Type> getSignature(Symbol symbol) {
return getEntry(symbol).flatMap(SymbolEntry::getSignature).map(signature -> toType(signature).genericCopy(types));
}
@Override
public Type getTarget(Type type) {
return types.getTarget(type);
}
@Override
public Optional<TypeClassDescriptor> getTypeClass(ClassReference classRef) {
return resolver.getEntry(classRef.getSymbol()).flatMap(SymbolEntry::getTypeClass);
}
@Override
public Set<TypeInstanceDescriptor> getTypeInstances(Symbol typeClass, List<? extends Type> parameters) {
return resolver.getTypeInstances(typeClass, parameters.stream().map(Type::toDescriptor).collect(toList()));
}
@Override
public Optional<MethodSignature> getValueSignature(Symbol symbol) {
return Optional.ofNullable(entries.get(symbol))
.map(SymbolEntry::getValueMethod)
.orElseGet(() -> parent.getValueSignature(symbol));
}
@Override
public void implement(Symbol typeClass, SumType type) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public boolean isBound(VariableType type) {
return types.isBound(type);
}
@Override
public boolean isGeneric(VariableType variableType) {
return types.isGeneric(variableType);
}
@Override
public boolean isImplemented(Symbol typeClass, SumType type) {
return types.isImplemented(typeClass, type);
}
@Override
public boolean isOperator_(Symbol symbol) {
return getEntry(symbol).map(SymbolEntry::isOperator).orElse(false);
}
@Override
public Scope leaveScope() {
return parent;
}
@Override
public Symbol qualifyCurrent(Symbol symbol) {
return symbol.qualifyWith(moduleName);
}
@Override
public Symbol reserveSymbol() {
return symbolGenerator.reserveSymbol().qualifyWith(moduleName);
}
@Override
public Symbol reserveSymbol(List<String> nestings) {
return symbolGenerator.reserveSymbol(nestings).qualifyWith(moduleName);
}
@Override
public void specialize(Type type) {
types.specialize(type);
}
protected abstract SymbolEntry define(Symbol symbol);
@Override
protected String getModuleName() {
return moduleName;
}
@Override
protected boolean isDefinedLocally(Symbol symbol) {
return entries.containsKey(symbol);
}
}