package scotch.compiler.syntax.scope; import static scotch.compiler.text.TextUtil.quote; import static scotch.symbol.SymbolEntry.mutableEntry; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import scotch.compiler.syntax.reference.ValueReference; 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 ChildScope extends BlockScope { private final Set<ChildScope> children; private final List<String> captures; private final List<String> locals; ChildScope(Scope parent, TypeScope types, SymbolResolver resolver, SymbolGenerator symbolGenerator, String moduleName) { super(parent, types, moduleName, resolver, symbolGenerator); this.children = new HashSet<>(); this.captures = new ArrayList<>(); this.locals = new ArrayList<>(); } @Override public void addLocal(String argument) { if (captures.contains(argument)) { throw new IllegalStateException("Argument " + quote(argument) + " is a capture!"); } else if (!locals.contains(argument)) { locals.add(argument); } } @Override public void capture(String argument) { if (!locals.contains(argument) && !captures.contains(argument)) { captures.add(argument); } } @Override public Scope enterScope() { ChildScope child = scope(this, types, resolver, symbolGenerator, moduleName); children.add(child); return child; } @Override public List<String> getCaptures() { return ImmutableList.copyOf(captures); } @Override public Set<Symbol> getContext(Type type) { return ImmutableSet.<Symbol>builder() .addAll(types.getContext(type)) .addAll(parent.getContext(type)) .build(); } @Override public List<String> getLocals() { return ImmutableList.copyOf(locals); } @Override public Optional<TypeClassDescriptor> getMemberOf(ValueReference valueRef) { return parent.getMemberOf(valueRef); } @Override public Optional<Operator> getOperator(Symbol symbol) { return getEntry(symbol).flatMap(entry -> { Optional<Operator> operator = entry.getOperator(); if (operator.isPresent()) { return operator; } else { return parent.getOperator(symbol); } }); } @Override public Optional<Type> getRawValue(Symbol symbol) { return getEntry(symbol).flatMap(entry -> { Optional<Type> rawValue = entry.getValue().map(Types::toType); if (rawValue.isPresent()) { return rawValue; } else { return parent.getRawValue(symbol); } }); } @Override public void insertChild(Scope newChild) { insertChild_((ChildScope) newChild); } @Override public boolean isDefined(Symbol symbol) { return symbol.accept(new SymbolVisitor<Boolean>() { @Override public Boolean visit(QualifiedSymbol symbol) { return parent.isDefined(symbol); } @Override public Boolean visit(UnqualifiedSymbol symbol) { return isDefinedLocally(symbol) || parent.isDefined(symbol); } }); } @Override public void prependLocals(List<String> locals) { this.locals.addAll(0, locals); } @Override public Optional<Symbol> qualify(Symbol symbol) { return symbol.accept(new SymbolVisitor<Optional<Symbol>>() { @Override public Optional<Symbol> visit(QualifiedSymbol symbol) { if (moduleName.equals(symbol.getModuleName()) && entries.containsKey(symbol)) { return Optional.of(symbol); } else { return parent.qualify(symbol); } } @Override public Optional<Symbol> visit(UnqualifiedSymbol symbol) { if (entries.containsKey(symbol.qualifyWith(moduleName))) { return Optional.of(symbol.qualifyWith(moduleName)); } else if (entries.containsKey(symbol)) { return Optional.of(symbol); } else { return parent.qualify(symbol); } } }); } @Override public void setParent(Scope newParent) { setParent_((ChildScope) newParent); } protected SymbolEntry define(Symbol symbol) { return entries.computeIfAbsent(symbol, k -> mutableEntry(symbol)); } private void insertChild_(ChildScope newChild) { children.forEach(child -> child.setParent(newChild)); children.clear(); newChild.setParent(this); } private void setParent_(ChildScope newParent) { newParent.children.add(this); parent = newParent; } @Override protected Optional<SymbolEntry> getEntry(Symbol symbol) { Optional<SymbolEntry> localEntry = Optional.ofNullable(entries.get(symbol)); if (localEntry.isPresent()) { return localEntry; } else { return parent.getEntry(symbol); } } @Override protected boolean isDataConstructor(Symbol symbol) { return getEntry(symbol) .map(SymbolEntry::isDataConstructor) .orElseGet(() -> parent.isDataConstructor(symbol)); } }