package scotch.compiler.analyzer; 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.syntax.definition.Definitions.scopeDef; 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 scotch.compiler.error.SyntaxError; import scotch.symbol.Operator; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; 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.pattern.PatternCase; import scotch.compiler.syntax.value.Value; public class OperatorAccumulator { private final DefinitionGraph graph; private final Deque<Scope> scopes; private final Map<DefinitionReference, Scope> functionScopes; private final Map<DefinitionReference, DefinitionEntry> entries; private final List<SyntaxError> errors; public OperatorAccumulator(DefinitionGraph graph) { this.graph = graph; this.scopes = new ArrayDeque<>(); this.functionScopes = new HashMap<>(); this.entries = new HashMap<>(); this.errors = new ArrayList<>(); } public Definition collect(Definition definition) { entries.put(definition.getReference(), entry(scope(), definition)); return definition; } public Definition collect(PatternCase pattern) { return collect(scopeDef(pattern)); } public DefinitionGraph accumulateOperators() { Definition root = getDefinition(rootRef()).orElseThrow(() -> new IllegalStateException("No root found!")); scoped(root, () -> root.defineOperators(this)); return graph .copyWith(entries.values()) .appendErrors(errors) .build(); } public void enterScope(Definition definition) { enterScope(definition.getReference()); } public void error(SyntaxError error) { errors.add(error); } 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 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()); try { T result = supplier.get(); collect(result.getDefinition()); return result; } finally { leaveScope(); } } private void enterScope(DefinitionReference reference) { scopes.push(getScope(reference)); } private Scope getScope(DefinitionReference reference) { return graph.tryGetScope(reference).orElseGet(() -> functionScopes.get(reference)); } public void defineOperator(Symbol symbol, Operator operator) { scope().defineOperator(symbol, operator); } public void defineValue(Symbol symbol, Type type) { scope().defineValue(symbol, type); } public boolean isOperator(Symbol symbol) { return scope().isOperator(symbol); } public List<DefinitionReference> defineDefinitionOperators(List<DefinitionReference> references) { return references.stream() .map(this::getDefinition) .filter(Optional::isPresent) .map(Optional::get) .map(definition -> definition.defineOperators(this)) .map(Definition::getReference) .collect(toList()); } public List<Value> defineValueOperators(List<Value> values) { return values.stream() .map(value -> value.defineOperators(this)) .collect(toList()); } }