package scotch.compiler.syntax.pattern; import static lombok.AccessLevel.PACKAGE; import static scotch.compiler.syntax.TypeError.typeError; import static scotch.compiler.syntax.builder.BuilderUtil.require; import static scotch.compiler.syntax.value.Values.let; import static scotch.util.Either.right; import static scotch.util.Pair.pair; import java.util.Optional; 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.Either; import scotch.util.Pair; import scotch.symbol.Operator; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; import scotch.compiler.syntax.util.SymbolGenerator; @AllArgsConstructor(access = PACKAGE) @EqualsAndHashCode(callSuper = false, doNotUseGetters = true) @ToString(exclude = "sourceLocation", doNotUseGetters = true) public class CaptureMatch extends PatternMatch { public static Builder builder() { return new Builder(); } private final SourceLocation sourceLocation; private final Optional<Value> argument; private final Symbol symbol; private final Type type; @Override public PatternMatch accumulateDependencies(DependencyAccumulator state) { return this; } @Override public PatternMatch accumulateNames(NameAccumulator state) { state.defineValue(symbol, type); state.specialize(type); return this; } @Override public Either<PatternMatch, CaptureMatch> asCapture() { return right(this); } @Override public Optional<Pair<CaptureMatch, Operator>> asCaptureOperator(Scope scope) { return scope.qualify(getSymbol()) .flatMap(scope::getOperator) .map(operator -> pair(this, operator)); } @Override public PatternMatch bind(Value argument, Scope scope) { return new CaptureMatch(sourceLocation, Optional.of(argument), symbol, type); } @Override public PatternMatch bindMethods(TypeChecker state) { return new CaptureMatch(sourceLocation, Optional.of(getArgument().bindMethods(state)), symbol, type); } @Override public PatternMatch bindTypes(TypeChecker state) { return new CaptureMatch(sourceLocation, Optional.of(getArgument().bindTypes(state)), symbol, state.generate(type)); } @Override public PatternMatch checkTypes(TypeChecker state) { Scope scope = state.scope(); state.addLocal(symbol); Value checkedArgument = getArgument().checkTypes(state); return new CaptureMatch( sourceLocation, Optional.of(checkedArgument), symbol, type.unify(checkedArgument.getType(), scope) .orElseGet(unification -> { state.error(typeError(unification, sourceLocation)); return type; }) ); } public Value getArgument() { return argument.orElseThrow(IllegalStateException::new); } public String getName() { return symbol.getCanonicalName(); } @Override public SourceLocation getSourceLocation() { return sourceLocation; } public Symbol getSymbol() { return symbol; } @Override public Type getType() { return type; } @Override public boolean isOperator(Scope scope) { return scope.isOperator(symbol); } @Override public PatternMatch qualifyNames(NameQualifier state) { return this; } public Value reducePattern(ArgumentMap argumentMap, SymbolGenerator generator, Value result) { Value argument = argumentMap.getTaggedArgument(getArgument()); return let(sourceLocation, generator.reserveType(), symbol.getCanonicalName(), argument, result); } @Override public void reducePatterns(PatternReducer reducer) { reducer.addAssignment(this); } public CaptureMatch withSourceLocation(SourceLocation sourceLocation) { return new CaptureMatch(sourceLocation, argument, symbol, type); } public CaptureMatch withSymbol(Symbol symbol) { return new CaptureMatch(sourceLocation, argument, symbol, type); } @Override public PatternMatch withType(Type type) { return new CaptureMatch(sourceLocation, argument, symbol, type); } public static class Builder implements SyntaxBuilder<CaptureMatch> { private Optional<SourceLocation> sourceLocation = Optional.empty(); private Optional<Symbol> symbol = Optional.empty(); private Optional<Type> type = Optional.empty(); private Builder() { // intentionally empty } @Override public CaptureMatch build() { return Patterns.capture( require(sourceLocation, "Source location"), Optional.empty(), require(symbol, "Capture symbol"), require(type, "Capture type") ); } public Builder withIdentifier(Identifier identifier) { return withSymbol(identifier.getSymbol()) .withType(identifier.getType()); } @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; } public Builder withType(Type type) { this.type = Optional.of(type); return this; } } }