package scotch.compiler.analyzer; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static scotch.compiler.syntax.definition.DefinitionEntry.entry; import static scotch.compiler.syntax.definition.Definitions.scopeDef; import static scotch.compiler.syntax.reference.DefinitionReference.rootRef; import static scotch.compiler.syntax.reference.DefinitionReference.valueRef; import static scotch.compiler.text.TextUtil.repeat; 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.BiFunction; import java.util.function.Supplier; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.ToString; 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.definition.UnshuffledDefinition; import scotch.compiler.syntax.definition.ValueDefinition; import scotch.compiler.syntax.pattern.PatternCase; import scotch.compiler.syntax.pattern.PatternMatch; import scotch.compiler.syntax.pattern.UnshuffledStructMatch; import scotch.compiler.syntax.reference.DefinitionReference; import scotch.compiler.syntax.scope.Scope; import scotch.compiler.syntax.value.Argument; import scotch.compiler.syntax.value.PatternMatcher; import scotch.compiler.syntax.value.UnshuffledValue; import scotch.compiler.syntax.value.Value; import scotch.compiler.text.SourceLocation; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; public class PrecedenceParser { private final DefinitionGraph graph; private final Deque<Scope> scopes; private final Map<DefinitionReference, Scope> patternScopes; private final Map<DefinitionReference, DefinitionEntry> entries; private final Deque<List<String>> memberNames; private final List<SyntaxError> errors; public PrecedenceParser(DefinitionGraph graph) { this.graph = graph; this.scopes = new ArrayDeque<>(); this.patternScopes = new HashMap<>(); this.entries = new HashMap<>(); this.memberNames = new ArrayDeque<>(asList(ImmutableList.of())); this.errors = new ArrayList<>(); } public void addPattern(Symbol symbol, PatternCase matcher) { scope().getParent().addPattern(symbol, matcher); } public void defineValue(Symbol symbol, Type type) { scope().defineValue(symbol, type); } 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); } public boolean isOperator(Symbol symbol) { return scope().isOperator(symbol); } @SuppressWarnings("unchecked") public <T extends Scoped> T keep(Scoped scoped) { return (T) scoped(scoped, () -> scoped); } public void leaveScope() { scopes.pop(); } public List<DefinitionReference> map( List<DefinitionReference> references, BiFunction<? super Definition, PrecedenceParser, ? extends Definition> function ) { return references.stream() .map(this::getDefinition) .filter(Optional::isPresent) .map(Optional::get) .map(definition -> function.apply(definition, this)) .map(Definition::getReference) .collect(toList()); } public List<DefinitionReference> mapOptional( List<DefinitionReference> references, BiFunction<? super Definition, PrecedenceParser, Optional<? extends Definition>> function ) { return references.stream() .map(this::getDefinition) .filter(Optional::isPresent) .map(Optional::get) .map(definition -> function.apply(definition, this)) .filter(Optional::isPresent) .map(Optional::get) .map(Definition::getReference) .collect(toList()); } public <T> T named(Symbol symbol, Supplier<? extends T> supplier) { memberNames.push(symbol.getMemberNames()); T result = supplier.get(); memberNames.pop(); return result; } public DefinitionGraph parsePrecedence() { Definition root = getDefinition(rootRef()).orElseThrow(() -> new IllegalStateException("No root found!")); scopedOptional(root, () -> root.parsePrecedence(this)); return graph .copyWith(entries.values()) .appendErrors(errors) .build(); } public List<DefinitionReference> processPatterns() { List<DefinitionReference> members = new ArrayList<>(); scope().getPatternCases().forEach((symbol, patternCases) -> { SourceLocation sourceLocation = patternCases.subList(1, patternCases.size()).stream() .map(PatternCase::getSourceLocation) .reduce(patternCases.get(0).getSourceLocation(), SourceLocation::extend); PatternMatcher matcher = buildMatcher(patternCases, sourceLocation); patternCases.stream() .map(this::collect) .map(Definition::getReference) .map(this::getScope) .forEach(scope -> scope.setParent(getScope(matcher.getReference()))); Scope scope = scope().enterScope(); patternScopes.put(valueRef(symbol), scope); getScope(matcher.getReference()).setParent(scope); ValueDefinition.builder() .withSourceLocation(sourceLocation) .withSymbol(symbol) .withBody(matcher) .build() .parsePrecedence(this) .map(Definition::getReference) .map(members::add); patternScopes.remove(valueRef(symbol)); }); return members; } public Optional<Symbol> qualify(Symbol symbol) { return scope().qualify(symbol); } public Symbol reserveSymbol() { return scope().reserveSymbol(memberNames.peek()); } public Type reserveType() { return scope().reserveType(); } 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(); } } public <T extends Definition> Optional<Definition> scopedOptional(T definition, Supplier<Optional<? extends T>> supplier) { enterScope(definition); try { return supplier.get().map(this::collect); } finally { leaveScope(); } } public List<PatternMatch> shuffle(List<PatternMatch> patternMatches) { return patternMatches.stream() .map(patternMatch -> patternMatch.shuffle(this)) .collect(toList()); } public Optional<Definition> shuffle(UnshuffledDefinition pattern) { return new PatternShuffler().shuffle(scope(), memberNames.peek(), pattern) .map(r -> { addPattern(r.getSymbol(), pattern.asPatternMatcher(r.getMatches())); return Optional.<Definition>empty(); }) .orElseGet(syntaxError -> { errors.add(syntaxError); return Optional.of((Definition) pattern); }); } public Value shuffle(UnshuffledValue value) { return new ValueShuffler(v -> v.parsePrecedence(this)) .shuffle(scope(), value.getValues()) .orElseGet(syntaxError -> { error(syntaxError); return value; }); } public PatternMatch shuffle(UnshuffledStructMatch patternMatch) { return new StructMatchShuffler() .shuffle(scope(), patternMatch.getPatternMatches()) .orElseGet(left -> { error(left); return patternMatch; }); } public void symbolNotFound(Symbol symbol, SourceLocation sourceLocation) { errors.add(SymbolNotFoundError.symbolNotFound(symbol, sourceLocation)); } private PatternMatcher buildMatcher(List<PatternCase> patternCases, SourceLocation sourceLocation) { List<Argument> arguments = buildMatcherArguments(patternCases, sourceLocation); PatternMatcher matcher = PatternMatcher.builder() .withSourceLocation(sourceLocation) .withSymbol(scope().reserveSymbol(ImmutableList.of())) .withType(scope().reserveType()) .withArguments(arguments) .withPatterns(patternCases) .build(); patternScopes.put(matcher.getReference(), scope().enterScope()); return matcher; } private List<Argument> buildMatcherArguments(List<PatternCase> patterns, SourceLocation sourceLocation) { int arity = patterns.get(0).getArity(); List<Argument> arguments = new ArrayList<>(); for (int i = 0; i < arity; i++) { arguments.add(Argument.builder() .withSourceLocation(sourceLocation) .withName("#" + i) .withType(scope().reserveType()) .build()); } return arguments; } private Definition collect(Definition definition) { entries.put(definition.getReference(), entry(scope(), definition)); return definition; } private Definition collect(PatternCase pattern) { return collect(scopeDef(pattern)); } private void enterScope(DefinitionReference reference) { scopes.push(getScope(reference)); } private Scope getScope(DefinitionReference reference) { return graph.tryGetScope(reference).orElseGet( () -> Optional.ofNullable(patternScopes.get(reference)).orElseGet( () -> entries.get(reference).getScope())); } @AllArgsConstructor @EqualsAndHashCode(callSuper = false) @ToString public static final class ArityMismatch extends SyntaxError { private final Symbol symbol; private final int expectedArity; private final int actualArity; private final SourceLocation sourceLocation; @Override public String prettyPrint() { return prettyPrint_() + " " + sourceLocation.prettyPrint(); } @Override public String report(String indent, int indentLevel) { return sourceLocation.report(indent, indentLevel) + "\n" + repeat(indent, indentLevel + 1) + prettyPrint_(); } private String prettyPrint_() { return "Arity mismatch in pattern " + symbol + ": expected arity " + expectedArity + " but got arity " + actualArity; } } }