package scotch.compiler.syntax.pattern; import static lombok.AccessLevel.PACKAGE; import static scotch.compiler.syntax.builder.BuilderUtil.require; import static scotch.util.Pair.pair; import java.util.Optional; import java.util.function.Function; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.ToString; import scotch.compiler.analyzer.DependencyAccumulator; import scotch.compiler.analyzer.NameAccumulator; import scotch.compiler.analyzer.NameQualifier; import scotch.compiler.analyzer.TypeChecker; import scotch.compiler.syntax.builder.SyntaxBuilder; import scotch.compiler.syntax.scope.Scope; import scotch.compiler.syntax.value.Identifier; import scotch.compiler.syntax.value.Value; import scotch.compiler.text.SourceLocation; import scotch.util.Pair; import scotch.symbol.Operator; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; @AllArgsConstructor(access = PACKAGE) @EqualsAndHashCode(callSuper = false, doNotUseGetters = true) @ToString(exclude = "sourceLocation", doNotUseGetters = true) public class EqualMatch extends PatternMatch { public static Builder builder() { return new Builder(); } private final SourceLocation sourceLocation; private final Optional<Value> argument; private final Value value; @Override public PatternMatch accumulateDependencies(DependencyAccumulator state) { return map(value -> value.accumulateDependencies(state)); } @Override public PatternMatch accumulateNames(NameAccumulator state) { return this; } @Override public Optional<Pair<EqualMatch, Operator>> asConstructorOperator(Scope scope) { return scope.qualify(getSymbol()) .flatMap(scope::getOperator) .map(operator -> pair(this, operator)); } @Override public PatternMatch bind(Value argument, Scope scope) { if (this.argument.isPresent()) { throw new IllegalStateException(); } else { return new EqualMatch(sourceLocation, Optional.of(argument), value); } } @Override public PatternMatch bindMethods(TypeChecker state) { return map(value -> value.bindMethods(state)); } @Override public PatternMatch bindTypes(TypeChecker state) { return map(value -> value.bindTypes(state)); } @Override public PatternMatch checkTypes(TypeChecker state) { return map(value -> value.checkTypes(state)); } @Override public SourceLocation getSourceLocation() { return sourceLocation; } public Symbol getSymbol() { return getSymbol_().orElseThrow(IllegalStateException::new); } @Override public Type getType() { return value.getType(); } public Value getValue() { return value; } @Override public boolean isOperator(Scope scope) { return getSymbol_().map(scope::isOperator).orElse(false); } @Override public PatternMatch qualifyNames(NameQualifier state) { return withValue(value.qualifyNames(state)); } @Override public void reducePatterns(PatternReducer reducer) { reducer.addCondition(reducer.getTaggedArgument(getArgument()), value); } public EqualMatch withSourceLocation(SourceLocation sourceLocation) { return new EqualMatch(sourceLocation, argument, value); } @Override public EqualMatch withType(Type type) { throw new UnsupportedOperationException(); // TODO } private Value getArgument() { return argument.orElseThrow(IllegalStateException::new); } private Optional<Symbol> getSymbol_() { if (value instanceof Identifier) { return Optional.of(((Identifier) value).getSymbol()); } else { return Optional.empty(); } } private PatternMatch map(Function<Value, Value> function) { return new EqualMatch( sourceLocation, Optional.of(function.apply(getArgument())), function.apply(value) ); } private EqualMatch withValue(Value value) { return new EqualMatch(sourceLocation, argument, value); } public static class Builder implements SyntaxBuilder<EqualMatch> { private Optional<Value> value; private Optional<SourceLocation> sourceLocation; private Builder() { // intentionally empty } @Override public EqualMatch build() { return Patterns.equal( require(sourceLocation, "Source location"), Optional.empty(), require(value, "Capture value") ); } @Override public Builder withSourceLocation(SourceLocation sourceLocation) { this.sourceLocation = Optional.of(sourceLocation); return this; } public Builder withValue(Value value) { this.value = Optional.of(value); return this; } } }