package scotch.compiler.syntax.scope;
import static scotch.compiler.syntax.definition.Import.moduleImport;
import static scotch.compiler.text.SourceLocation.NULL_SOURCE;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import scotch.compiler.syntax.definition.Import;
import scotch.compiler.syntax.reference.ValueReference;
import scotch.compiler.syntax.type.DefaultTypeScope;
import scotch.compiler.syntax.type.Type;
import scotch.compiler.syntax.type.TypeScope;
import scotch.compiler.syntax.type.Types;
import scotch.compiler.syntax.util.SymbolGenerator;
import scotch.symbol.Operator;
import scotch.symbol.Symbol;
import scotch.symbol.Symbol.QualifiedSymbol;
import scotch.symbol.Symbol.SymbolVisitor;
import scotch.symbol.Symbol.UnqualifiedSymbol;
import scotch.symbol.SymbolEntry;
import scotch.symbol.SymbolResolver;
import scotch.symbol.descriptor.TypeClassDescriptor;
public class ModuleScope extends BlockScope {
private final List<ImportScope> importScopes;
ModuleScope(Scope parent, TypeScope types, SymbolResolver resolver, SymbolGenerator symbolGenerator, String moduleName) {
super(parent, types, moduleName, resolver, symbolGenerator);
this.importScopes = new ArrayList<>();
}
@Override
public Scope enterScope(List<Import> imports) {
ImportScope scope = new ImportScope(this, new DefaultTypeScope(symbolGenerator, resolver), resolver, symbolGenerator, moduleName, ImmutableList.<Import>builder()
.add(moduleImport(NULL_SOURCE, "scotch.lang"))
.addAll(imports)
.build());
importScopes.add(scope);
return scope;
}
@Override
public Set<Symbol> getContext(Type type) {
throw new IllegalStateException();
}
@Override
public Optional<TypeClassDescriptor> getMemberOf(ValueReference valueRef) {
throw new IllegalStateException();
}
@Override
public Optional<Operator> getOperator(Symbol symbol) {
return getEntry(symbol).flatMap(SymbolEntry::getOperator);
}
@Override
public Optional<Type> getRawValue(Symbol symbol) {
return getEntry(symbol)
.flatMap(SymbolEntry::getValue)
.map(Types::toType);
}
@Override
public Optional<SymbolEntry> getSiblingEntry(Symbol symbol) {
return importScopes.stream()
.filter(importScope -> importScope.isDefinedLocally(symbol))
.findFirst()
.flatMap(importScope -> importScope.getEntry(symbol));
}
@Override
public boolean isDefined(Symbol symbol) {
return getEntry(symbol).isPresent();
}
@Override
public Optional<Symbol> qualify(Symbol symbol) {
Optional<Symbol> optionalSymbol = importScopes.stream()
.map(importScope -> importScope.qualify_(symbol))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
if (optionalSymbol.isPresent()) {
return optionalSymbol;
} else {
return parent.qualify(symbol);
}
}
@Override
protected SymbolEntry define(Symbol symbol) {
throw new IllegalStateException();
}
@Override
protected boolean isDefinedLocally(Symbol symbol) {
return importScopes.stream().anyMatch(importScope -> importScope.isDefinedLocally(symbol));
}
@Override
protected Optional<SymbolEntry> getEntry(Symbol symbol) {
return symbol.accept(new SymbolVisitor<Optional<SymbolEntry>>() {
@Override
public Optional<SymbolEntry> visit(QualifiedSymbol symbol) {
if (Objects.equals(symbol.getModuleName(), moduleName)) {
Optional<SymbolEntry> siblingEntry = getSiblingEntry(symbol);
if (siblingEntry.isPresent()) {
return siblingEntry;
} else {
return parent.getEntry(symbol);
}
} else {
return parent.getEntry(symbol);
}
}
@Override
public Optional<SymbolEntry> visit(UnqualifiedSymbol symbol) {
return qualify(symbol).flatMap(ModuleScope.this::getEntry);
}
});
}
@Override
protected boolean isDataConstructor(Symbol symbol) {
return parent.isDataConstructor(symbol);
}
}