package scotch.compiler.syntax.scope; import static java.util.stream.Collectors.toSet; import static scotch.compiler.syntax.definition.Import.moduleImport; import static scotch.compiler.syntax.reference.DefinitionReference.classRef; import static scotch.compiler.text.SourceLocation.NULL_SOURCE; import static scotch.compiler.text.TextUtil.quote; import static scotch.symbol.SymbolEntry.mutableEntry; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import scotch.compiler.syntax.definition.Import; import scotch.compiler.syntax.reference.ValueReference; import scotch.compiler.syntax.type.Types; 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; import scotch.compiler.syntax.type.Type; import scotch.compiler.syntax.type.TypeScope; import scotch.compiler.syntax.util.SymbolGenerator; public class ImportScope extends BlockScope { private final List<Import> imports; ImportScope(Scope parent, TypeScope types, SymbolResolver resolver, SymbolGenerator symbolGenerator, String moduleName, List<Import> imports) { super(parent, types, moduleName, resolver, symbolGenerator); this.imports = ImmutableList.<Import>builder() .addAll(imports) .add(moduleImport(NULL_SOURCE, moduleName)) // TODO should not require extra import for symbols defined in java .build(); } @Override public Scope enterScope() { return scope(this, types, resolver, symbolGenerator, moduleName); } @Override public Set<Symbol> getContext(Type type) { return ImmutableSet.<Symbol>builder() .addAll(types.getContext(type)) .addAll(imports.stream() .map(import_ -> import_.getContext(type, resolver)) .flatMap(Collection::stream) .collect(toSet())) .build(); } @Override public Optional<TypeClassDescriptor> getMemberOf(ValueReference valueRef) { return resolver.getEntry(valueRef.getSymbol()) .flatMap(SymbolEntry::getMemberOf) .flatMap(symbol -> getTypeClass(classRef(symbol))); } @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 boolean isDefined(Symbol symbol) { return getEntry(symbol).isPresent(); } @Override public Optional<Symbol> qualify(Symbol symbol) { return parent.qualify(symbol); } @Override protected SymbolEntry define(Symbol symbol) { if (!isDefinedLocally(symbol) && parent.isDefined(symbol)) { throw new UnsupportedOperationException(); // TODO report definition in other scope } return symbol.accept(new SymbolVisitor<SymbolEntry>() { @Override public SymbolEntry visit(QualifiedSymbol symbol) { if (Objects.equals(moduleName, symbol.getModuleName())) { return entries.computeIfAbsent(symbol, k -> mutableEntry(symbol)); } else { throw new IllegalArgumentException("Can't define symbol " + symbol.quote() + " within different module " + quote(moduleName)); } } @Override public SymbolEntry visit(UnqualifiedSymbol symbol) { throw new IllegalArgumentException("Can't define unqualified symbol " + symbol.quote()); } }); } @Override protected Optional<SymbolEntry> getEntry(Symbol symbol) { if (entries.containsKey(symbol)) { return Optional.of(entries.get(symbol)); } else { Optional<SymbolEntry> optionalEntry = parent.getSiblingEntry(symbol); if (optionalEntry.isPresent()) { return optionalEntry; } else { return imports.stream() .map(i -> i.qualify(symbol.getMemberName(), resolver)) .filter(Optional::isPresent) .map(Optional::get) .findFirst() .flatMap(parent::getEntry); } } } @Override protected boolean isDataConstructor(Symbol symbol) { return parent.isDataConstructor(symbol); } protected Optional<Symbol> qualify_(Symbol symbol) { return symbol.accept(new SymbolVisitor<Optional<Symbol>>() { @Override public Optional<Symbol> visit(QualifiedSymbol symbol) { if (Objects.equals(moduleName, symbol.getModuleName())) { return Optional.of(symbol); } else { return imports.stream() .filter(i -> i.isFrom(symbol.getModuleName())) .findFirst() .flatMap(i -> i.qualify(symbol.getMemberName(), resolver)); } } @Override public Optional<Symbol> visit(UnqualifiedSymbol symbol) { Symbol qualified = symbol.qualifyWith(moduleName); if (isDefinedLocally(qualified)) { return Optional.of(qualified); } else { return imports.stream() .map(i -> i.qualify(symbol.getMemberName(), resolver)) .filter(Optional::isPresent) .findFirst() .flatMap(s -> s); } } }); } }