package scotch.compiler.analyzer; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; import static scotch.compiler.syntax.definition.DefinitionEntry.entry; import static scotch.compiler.syntax.reference.DefinitionReference.rootRef; import static scotch.compiler.text.SourceLocation.NULL_SOURCE; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; import com.google.common.collect.ImmutableList; import scotch.compiler.error.SymbolNotFoundError; import scotch.compiler.error.SyntaxError; import scotch.compiler.syntax.Scoped; import scotch.compiler.syntax.definition.Definition; import scotch.compiler.syntax.definition.DefinitionEntry; import scotch.compiler.syntax.definition.DefinitionGraph; import scotch.compiler.syntax.reference.DefinitionReference; import scotch.compiler.syntax.scope.Scope; import scotch.compiler.syntax.value.FunctionValue; import scotch.compiler.syntax.value.Value; import scotch.compiler.text.SourceLocation; import scotch.symbol.Symbol; import scotch.symbol.Symbol.QualifiedSymbol; import scotch.symbol.Symbol.SymbolVisitor; import scotch.symbol.Symbol.UnqualifiedSymbol; import scotch.symbol.descriptor.DataConstructorDescriptor; import scotch.symbol.descriptor.DataTypeDescriptor; import scotch.compiler.syntax.type.Type; import scotch.compiler.syntax.type.TypeQualifier; public class NameQualifier implements TypeQualifier { private final DefinitionGraph graph; private final Deque<Scope> scopes; private final Map<DefinitionReference, Scope> functionScopes; private final Map<DefinitionReference, DefinitionEntry> entries; private final Deque<List<String>> memberNames; private final List<SyntaxError> errors; public NameQualifier(DefinitionGraph graph) { this.graph = graph; this.scopes = new ArrayDeque<>(); this.functionScopes = new HashMap<>(); this.entries = new HashMap<>(); this.memberNames = new ArrayDeque<>(singletonList(ImmutableList.of())); this.errors = new ArrayList<>(); } public Definition collect(Definition definition) { entries.put(definition.getReference(), entry(scope(), definition)); return definition; } public void defineValue(Symbol symbol, Type type) { scope().defineValue(symbol, type); } public void enterScope(Definition definition) { enterScope(definition.getReference()); } public Optional<Definition> getDefinition(DefinitionReference reference) { return graph.getDefinition(reference); } @SuppressWarnings("unchecked") public <T extends Scoped> T keep(Scoped scoped) { return (T) scoped(scoped, () -> scoped); } public void leaveScope() { scopes.pop(); } public <T> T named(Symbol symbol, Supplier<T> supplier) { memberNames.push(symbol.getMemberNames()); T result = supplier.get(); memberNames.pop(); return result; } public Optional<Symbol> qualify(Symbol symbol) { return symbol.accept(new SymbolVisitor<Optional<Symbol>>() { @Override public Optional<Symbol> visit(QualifiedSymbol symbol) { return Optional.of(symbol); } @Override public Optional<Symbol> visit(UnqualifiedSymbol symbol) { List<Symbol> result = memberNames.stream() .map(symbol::nest) .map(scope()::qualify) .filter(Optional::isPresent) .map(Optional::get) .collect(toList()); if (result.isEmpty()) { return Optional.empty(); } else { return Optional.of(result.get(0)); } } }); } public List<DefinitionReference> qualifyDefinitionNames(List<DefinitionReference> references) { return references.stream() .map(this::getDefinition) .filter(Optional::isPresent) .map(Optional::get) .map(definition -> definition.qualifyNames(this)) .map(Definition::getReference) .collect(toList()); } @Override public Symbol qualifyType(Symbol symbol) { return qualify(symbol) .orElseGet(() -> { symbolNotFound(symbol, NULL_SOURCE); // TODO actual source location return symbol; }); } public DefinitionGraph qualifyNames() { Definition root = getDefinition(rootRef()).orElseThrow(() -> new IllegalStateException("No root found!")); scoped(root, () -> root.qualifyNames(this)); return graph .copyWith(entries.values()) .appendErrors(errors) .build(); } public List<Type> qualifyTypeNames(List<Type> types) { return types.stream() .map(type -> type.qualifyNames(this)) .collect(toList()); } @SuppressWarnings("unchecked") public <T extends Value> List<T> qualifyValueNames(List<T> values) { return (List<T>) values.stream() .map(value -> value.qualifyNames(this)) .collect(toList()); } public void redefineDataConstructor(Symbol symbol, DataConstructorDescriptor descriptor) { scope().redefineDataConstructor(symbol, descriptor); } public void redefineDataType(Symbol symbol, DataTypeDescriptor descriptor) { scope().redefineDataType(symbol, descriptor); } public void redefineValue(Symbol symbol, Type type) { scope().redefineValue(symbol, type); } public Scope scope() { return scopes.peek(); } public <T extends Definition> T scoped(T definition, Supplier<? extends T> supplier) { enterScope(definition); try { T result = supplier.get(); collect(result); return result; } finally { leaveScope(); } } public <T extends Scoped> T scoped(Scoped value, Supplier<? extends T> supplier) { enterScope(value.getReference()); if (value instanceof FunctionValue) { memberNames.push(((FunctionValue) value).getSymbol().getMemberNames()); } try { T result = supplier.get(); collect(result.getDefinition()); return result; } finally { leaveScope(); } } public void symbolNotFound(Symbol symbol, SourceLocation sourceLocation) { errors.add(SymbolNotFoundError.symbolNotFound(symbol, sourceLocation)); } private void enterScope(DefinitionReference reference) { scopes.push(getScope(reference)); } private Scope getScope(DefinitionReference reference) { return graph.tryGetScope(reference).orElseGet(() -> functionScopes.get(reference)); } }