package scotch.compiler.syntax.pattern; import static java.util.stream.Collectors.toList; import static scotch.compiler.syntax.builder.BuilderUtil.require; import static scotch.compiler.syntax.definition.Definitions.scopeDef; import static scotch.compiler.syntax.reference.DefinitionReference.scopeRef; import static scotch.compiler.syntax.value.Values.arg; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.collect.ImmutableList; import lombok.EqualsAndHashCode; import lombok.ToString; import scotch.compiler.analyzer.NameAccumulator; import scotch.compiler.analyzer.OperatorAccumulator; import scotch.compiler.analyzer.PrecedenceParser; import scotch.compiler.analyzer.NameQualifier; import scotch.compiler.syntax.Scoped; import scotch.compiler.syntax.builder.SyntaxBuilder; import scotch.compiler.syntax.definition.Definition; import scotch.compiler.syntax.reference.DefinitionReference; import scotch.compiler.syntax.value.Value; import scotch.compiler.text.SourceLocation; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; @EqualsAndHashCode(callSuper = false) @ToString(exclude = "sourceLocation") public class PatternCase implements Scoped { public static Builder builder() { return new Builder(); } private final SourceLocation sourceLocation; private final Symbol symbol; private final List<PatternMatch> patternMatches; private final Value body; PatternCase(SourceLocation sourceLocation, Symbol symbol, List<PatternMatch> patternMatches, Value body) { this.sourceLocation = sourceLocation; this.symbol = symbol; this.patternMatches = ImmutableList.copyOf(patternMatches); this.body = body; } public PatternCase accumulateNames(NameAccumulator state) { return state.scoped(this, () -> withMatches(patternMatches.stream() .map(match -> match.accumulateNames(state)) .collect(toList())) .withBody(body.accumulateNames(state))); } public PatternCase defineOperators(OperatorAccumulator state) { return state.scoped(this, () -> withBody(body.defineOperators(state))); } public int getArity() { return patternMatches.size(); } public Value getBody() { return body; } @Override public Definition getDefinition() { return scopeDef(sourceLocation, symbol); } public DefinitionReference getReference() { return scopeRef(symbol); } public SourceLocation getSourceLocation() { return sourceLocation; } public Symbol getSymbol() { return symbol; } public Type getType() { return body.getType(); } public PatternCase parsePrecedence(PrecedenceParser state) { return state.scoped(this, () -> { AtomicInteger counter = new AtomicInteger(); List<PatternMatch> boundMatches = state.shuffle(patternMatches).stream() .map(match -> match.bind( arg(sourceLocation.getStartPoint(), "#" + counter.getAndIncrement(), state.reserveType(), Optional.empty()), state.scope())) .collect(toList()); return withSymbol(state.reserveSymbol()) .withMatches(boundMatches) .withBody(body.parsePrecedence(state).unwrap()); }); } public PatternCase qualifyNames(NameQualifier state) { return state.scoped(this, () -> withMatches(patternMatches.stream() .map(match -> match.qualifyNames(state)) .collect(toList())) .withBody(body.qualifyNames(state))); } public void reducePatterns(PatternReducer reducer) { reducer.beginPatternCase(withBody(body.reducePatterns(reducer))); patternMatches.forEach(patternMatch -> patternMatch.reducePatterns(reducer)); reducer.endPatternCase(); } public PatternCase withBody(Value body) { return new PatternCase(sourceLocation, symbol, patternMatches, body); } public PatternCase withMatches(List<PatternMatch> matches) { return new PatternCase(sourceLocation, symbol, matches, body); } public PatternCase withType(Type type) { return new PatternCase(sourceLocation, symbol, patternMatches, body.withType(type)); } private PatternCase withSymbol(Symbol symbol) { return new PatternCase(sourceLocation, symbol, patternMatches, body); } public static class Builder implements SyntaxBuilder<PatternCase> { private Optional<SourceLocation> sourceLocation = Optional.empty(); private Optional<Symbol> symbol = Optional.empty(); private Optional<List<PatternMatch>> matches = Optional.empty(); private Optional<Value> body = Optional.empty(); @Override public PatternCase build() { return new PatternCase( require(sourceLocation, "Source location"), require(symbol, "Symbol"), require(matches, "Pattern matches"), require(body, "Pattern body") ); } public Builder withBody(Value body) { this.body = Optional.of(body); return this; } public Builder withMatches(List<PatternMatch> matches) { this.matches = Optional.of(matches); return this; } @Override public Builder withSourceLocation(SourceLocation sourceLocation) { this.sourceLocation = Optional.of(sourceLocation); return this; } public Builder withSymbol(Symbol symbol) { this.symbol = Optional.of(symbol); return this; } } }