package scotch.compiler.syntax.value; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; import lombok.AllArgsConstructor; import scotch.compiler.syntax.reference.DefinitionReference; public abstract class WithArguments { public static WithArguments withArguments(FunctionValue functionValue) { return new FunctionWithArguments(functionValue); } public static WithArguments withArguments(PatternMatcher patternMatcher) { return new PatternWithArguments(patternMatcher); } public static ValueWithoutArguments withoutArguments(Value value) { return new ValueWithoutArguments(value); } public abstract WithArguments map(BiFunction<DefinitionReference, List<Argument>, List<Argument>> function); public abstract Value orElseGet(Function<Value, Value> function); @AllArgsConstructor private static final class FunctionWithArguments extends WithArguments { private final FunctionValue functionValue; @Override public WithArguments map(BiFunction<DefinitionReference, List<Argument>, List<Argument>> function) { return withArguments(functionValue.withArguments(function.apply(functionValue.getReference(), functionValue.getArguments()))); } @Override public Value orElseGet(Function<Value, Value> function) { return functionValue; } } @AllArgsConstructor private static final class PatternWithArguments extends WithArguments { private final PatternMatcher patternMatcher; @Override public WithArguments map(BiFunction<DefinitionReference, List<Argument>, List<Argument>> function) { return withArguments(patternMatcher.withArguments(function.apply(patternMatcher.getReference(), patternMatcher.getArguments()))); } @Override public Value orElseGet(Function<Value, Value> function) { return patternMatcher; } } @AllArgsConstructor private static final class ValueWithoutArguments extends WithArguments { private final Value value; @Override public WithArguments map(BiFunction<DefinitionReference, List<Argument>, List<Argument>> function) { return this; } @Override public Value orElseGet(Function<Value, Value> function) { return function.apply(value); } } }