summaryrefslogtreecommitdiff
path: root/base/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'base/src/main/java')
-rw-r--r--base/src/main/java/bjc/utils/patterns/ComplexPattern.java183
-rw-r--r--base/src/main/java/bjc/utils/patterns/FunctionalPattern.java47
-rw-r--r--base/src/main/java/bjc/utils/patterns/FunctionalPatternMatcher.java31
-rw-r--r--base/src/main/java/bjc/utils/patterns/IPatternMatcher.java85
-rw-r--r--base/src/main/java/bjc/utils/patterns/MutablePatternMatcher.java84
-rw-r--r--base/src/main/java/bjc/utils/patterns/NonExhaustiveMatch.java20
-rw-r--r--base/src/main/java/bjc/utils/patterns/Pattern.java20
-rw-r--r--base/src/main/java/bjc/utils/patterns/PatternMatcher.java41
-rw-r--r--base/src/main/java/bjc/utils/patterns/SimplePatttern.java40
-rw-r--r--base/src/main/java/bjc/utils/patterns/package-info.java6
10 files changed, 557 insertions, 0 deletions
diff --git a/base/src/main/java/bjc/utils/patterns/ComplexPattern.java b/base/src/main/java/bjc/utils/patterns/ComplexPattern.java
new file mode 100644
index 0000000..3926f2c
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/ComplexPattern.java
@@ -0,0 +1,183 @@
+package bjc.utils.patterns;
+
+import java.util.function.*;
+import java.util.regex.*;
+
+import bjc.data.*;
+
+/**
+ * A pattern that can be matched against.
+ *
+ * @author Ben Culkin
+ *
+ * @param <InputType> The type of object being matched against.
+ * @param <ReturnType> The type returned by the pattern.
+ * @param <PredType> The state type returned by the predicate.
+ */
+public interface ComplexPattern<ReturnType, PredType, InputType> {
+ /**
+ * Whether or not the given input matches this pattern.
+ *
+ * @param input The object to check against this pattern.
+ *
+ * @return Whether or not this pattern is matched, as well as a state value
+ * that will get passed to the pattern if it did match.
+ */
+ IPair<Boolean, PredType> matches(InputType input);
+
+ /**
+ * Apply this pattern, once it has matched.
+ *
+ * @param input The object to apply this pattern to.
+ * @param state The state from the matcher.
+ *
+ * @return The result of applying this pattern.
+ */
+ ReturnType apply(InputType input, PredType state);
+
+ /**
+ * Create a pattern composed from a predicate & a function.
+ *
+ * @param <RetType> The type returned by the pattern.
+ * @param <PreType> The type used as intermediate state.
+ * @param <InpType> The type initially matched against.
+ *
+ * @param matcher The predicate that says what this pattern matches.
+ * @param accepter The action that happens when this pattern matches.
+ *
+ * @return A pattern composed from the passed in functions.
+ */
+ static <RetType, PreType, InpType> ComplexPattern<RetType, PreType, InpType> from(
+ Function<InpType, IPair<Boolean, PreType>> matcher,
+ BiFunction<InpType, PreType, RetType> accepter) {
+ return new FunctionalPattern<>(matcher, accepter);
+ }
+
+ /**
+ * Create a pattern which checks if an object is of a given type (or a subtype of it).
+ *
+ * @param <ClassType> The type to check if the object is an instance of.
+ * @param <RetType> The type returned by the action.
+ * @param <InpType> The type of the thing to match.
+ *
+ * @param clasz The Class instance for the type you want to check.
+ * @param action The action to execute if the pattern does match.
+ *
+ * @return A pattern which follows the specified condition.
+ */
+ @SuppressWarnings("unchecked")
+ static <ClassType, RetType, InpType> ComplexPattern<RetType, ?, InpType> ofClass(
+ Class<ClassType> clasz,
+ Function<ClassType, RetType> action) {
+ return from(
+ (input) -> IPair.pair(clasz.isInstance(input), null),
+ (input, ignored) -> action.apply((ClassType)input)
+ );
+ }
+
+ /**
+ * Creates a pattern which matches a given object.
+ *
+ * @param <RetType> The type returned when the pattern matches.
+ * @param <InpType> The type of the thing to match.
+ *
+ * @param obj The object being tested for equality.
+ * @param action The action to execute when the object matches.
+ *
+ * @return A pattern which tests against the equality of an object.
+ */
+ static <RetType, InpType> ComplexPattern<RetType, ?, InpType> matchesObject(
+ InpType obj,
+ Function<InpType, RetType> action
+ ) {
+ return from(
+ (input) -> IPair.pair(obj.equals(input), null),
+ (input, ignored) -> action.apply(input)
+ );
+ }
+
+ /**
+ * Tests if the toString rendition of an object matches a given condition.
+ *
+ * @param <RetType> The type returned by the pattern.
+ * @param <InpType> The type of the thing to match.
+ *
+ * @param pattern The string to check against.
+ * @param action The action to check when the toString of the object matches
+ * the provided string. This is passed both the object, and its
+ * string form (in the event that you don't want to call toString
+ * multiple times, for whatever reason)
+ *
+ * @return A pattern which tests against the toString representation of an object.
+ */
+ static <RetType, InpType> ComplexPattern<RetType, ?, InpType> equalsString(
+ String pattern,
+ BiFunction<InpType, String, RetType> action
+ ) {
+ return from(
+ (input) -> {
+ String objString = input.toString();
+
+ return IPair.pair(pattern.equals(objString), objString);
+ },
+ (input, objString) -> action.apply(input, objString)
+ );
+ }
+
+ /**
+ * Check if the toString of a given object matches a regex.
+ *
+ * @param <RetType> The type returned by the pattern.
+ * @param <InpType> The type of object to match against.
+ *
+ * @param regex The regex to match against.
+ * @param cond The predicate to use to determine if the regex matched.
+ * @param action The action to call when the regex matched.
+ *
+ * @return A pattern which does the regex matching.
+ */
+ static <RetType, InpType> ComplexPattern<RetType, Matcher, InpType> matchesRegex(
+ String regex,
+ Predicate<Matcher> cond,
+ BiFunction<InpType, Matcher, RetType> action
+ ) {
+ java.util.regex.Pattern regexPat = java.util.regex.Pattern.compile(regex);
+
+ return from(
+ (input) -> {
+ String inpString = input.toString();
+
+ Matcher mat = regexPat.matcher(inpString);
+
+ if (cond.test(mat)) {
+ return IPair.pair(true, mat);
+ } else {
+ return IPair.pair(false, null);
+ }
+ },
+ (input, res) -> action.apply(input, res)
+ );
+ }
+
+ // @TODO Nov 21, 2020 Ben Culkin :MorePatterns
+ // Try and write something to iterate over Iterator in a type-safe manner
+ // Also, something for doing a sub-pattern match
+ /**
+ * Create a pattern which will always execute.
+ *
+ * @param <RetType> The type returned.
+ * @param <InpType> The type being matched against.
+ *
+ * @param action The action to execute.
+ *
+ * @return A pattern which will be executed.
+ */
+ static <RetType, InpType> ComplexPattern<RetType, ?, InpType> otherwise(
+ Function<InpType, RetType> action
+ ) {
+ return from(
+ (input) -> IPair.pair(true, null),
+ (input, ignored) -> action.apply(input)
+ );
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/bjc/utils/patterns/FunctionalPattern.java b/base/src/main/java/bjc/utils/patterns/FunctionalPattern.java
new file mode 100644
index 0000000..e4b4a3d
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/FunctionalPattern.java
@@ -0,0 +1,47 @@
+package bjc.utils.patterns;
+
+import java.util.*;
+import java.util.function.*;
+
+import bjc.data.*;
+
+class FunctionalPattern<ReturnType, PredType, InputType>
+ implements ComplexPattern<ReturnType, PredType, InputType> {
+ private final Function<InputType, IPair<Boolean, PredType>> matcher;
+ private final BiFunction<InputType, PredType, ReturnType> accepter;
+
+ FunctionalPattern(
+ Function<InputType, IPair<Boolean, PredType>> matcher,
+ BiFunction<InputType, PredType, ReturnType> accepter) {
+ super();
+ this.matcher = matcher;
+ this.accepter = accepter;
+ }
+
+ @Override
+ public IPair<Boolean, PredType> matches(InputType input) {
+ return matcher.apply(input);
+ }
+
+ @Override
+ public ReturnType apply(InputType input, PredType state) {
+ return accepter.apply(input, state);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(accepter, matcher);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ FunctionalPattern<?, ?, ?> other = (FunctionalPattern<?, ?, ?>) obj;
+
+ return Objects.equals(accepter, other.accepter)
+ && Objects.equals(matcher, other.matcher);
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/bjc/utils/patterns/FunctionalPatternMatcher.java b/base/src/main/java/bjc/utils/patterns/FunctionalPatternMatcher.java
new file mode 100644
index 0000000..5a214d3
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/FunctionalPatternMatcher.java
@@ -0,0 +1,31 @@
+package bjc.utils.patterns;
+
+import bjc.functypes.*;
+
+/**
+ * A simple pattern matcher backed by a function.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ReturnType> The type returned by the matcher.
+ * @param <InputType> The type to match against.
+ */
+public class FunctionalPatternMatcher<ReturnType, InputType>
+ implements IPatternMatcher<ReturnType, InputType> {
+
+ private final ThrowFunction<InputType, ReturnType, NonExhaustiveMatch> matcher;
+
+ /**
+ * Create a new function-backed pattern matcher.
+ *
+ * @param matcher The function backing this matcher.
+ */
+ public FunctionalPatternMatcher(ThrowFunction<InputType, ReturnType, NonExhaustiveMatch> matcher) {
+ this.matcher = matcher;
+ }
+
+ @Override
+ public ReturnType matchFor(InputType input) throws NonExhaustiveMatch {
+ return matcher.apply(input);
+ }
+}
diff --git a/base/src/main/java/bjc/utils/patterns/IPatternMatcher.java b/base/src/main/java/bjc/utils/patterns/IPatternMatcher.java
new file mode 100644
index 0000000..b688a47
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/IPatternMatcher.java
@@ -0,0 +1,85 @@
+package bjc.utils.patterns;
+
+import java.util.function.*;
+
+import bjc.functypes.*;
+
+/**
+ * Represents a pattern matcher against a series of patterns.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ReturnType> The type returned from matching the patterns.
+ * @param <InputType> The type to match against.
+ */
+@FunctionalInterface
+public interface IPatternMatcher<ReturnType, InputType> {
+ /**
+ * Match an input object against a set of patterns.
+ *
+ * @param input The object to match against.
+ *
+ * @return The result of matching against the object.
+ *
+ * @throws NonExhaustiveMatch If none of the patterns in this set match
+ */
+ ReturnType matchFor(InputType input) throws NonExhaustiveMatch;
+
+ /**
+ * Create a pattern matcher against a static set of patterns.
+ *
+ * @param <RetType> The type returned from matching the patterns.
+ * @param <InpType> The type to match against.
+ *
+ * @param patterns The set of patterns to match on.
+ *
+ * @return A pattern matcher which matches on the given patterns.
+ */
+ @SafeVarargs
+ static <RetType, InpType> IPatternMatcher<RetType, InpType> matchingOn(
+ ComplexPattern<RetType, ?, InpType>... patterns) {
+ return new PatternMatcher<>(patterns);
+ }
+
+ /**
+ * Create a pattern matcher from a handler function.
+ *
+ * @param <RetType> The type returned by the matcher.
+ * @param <InpType> The type to match against.
+ *
+ * @param handler The handler function.
+ *
+ * @return A pattern matcher defined by the given handler.
+ */
+ static <RetType, InpType> IPatternMatcher<RetType, InpType> from(
+ ThrowFunction<InpType, RetType, NonExhaustiveMatch> handler) {
+ return new FunctionalPatternMatcher<>(handler);
+ }
+
+ /**
+ * Create a pattern matcher which applies a transform to its input.
+ *
+ * @param <NewInput> The new input type to use.
+ * @param transformer The function to convert from the new input to the old input.
+ *
+ * @return A pattern matcher which takes values of the new type instead.
+ */
+ default <NewInput> IPatternMatcher<ReturnType, NewInput> transformInput(
+ Function<NewInput, InputType> transformer) {
+ return from(inp -> matchFor(transformer.apply(inp)));
+ }
+
+ /**
+ * Create a pattern matcher which applies a transform to its output.
+ *
+ * @param <NewOutput> The new output type to use.
+ *
+ * @param transformer The function to convert from the new output to the old output.
+ *
+ * @return A pattern matcher which takes values of the new type instead.
+ */
+ default <NewOutput> IPatternMatcher<NewOutput, InputType> transformOutput(
+ Function<ReturnType, NewOutput> transformer) {
+ return from(inp -> transformer.apply(matchFor(inp)));
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/bjc/utils/patterns/MutablePatternMatcher.java b/base/src/main/java/bjc/utils/patterns/MutablePatternMatcher.java
new file mode 100644
index 0000000..7900262
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/MutablePatternMatcher.java
@@ -0,0 +1,84 @@
+package bjc.utils.patterns;
+
+import java.util.*;
+
+import bjc.data.*;
+
+/**
+ * A pattern matcher over a mutable set of patterns.
+ *
+ * Note that modifying a pattern matcher while it is currently doing pattern
+ * matching is a wonderful way to cause strange behavior.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ReturnType> The type returned by the pattern matcher.
+ */
+public class MutablePatternMatcher<ReturnType, InputType>
+ implements IPatternMatcher<ReturnType, InputType>{
+ private final List<ComplexPattern<ReturnType, Object, InputType>> patterns;
+
+ /**
+ * Create a new mutable pattern matcher with no patterns.
+ */
+ public MutablePatternMatcher() {
+ patterns = new ArrayList<>();
+ }
+
+ /**
+ * Create a new mutable pattern matcher with the given set of patterns.
+ *
+ * @param patterns The set of patterns to match on.
+ */
+ @SuppressWarnings("unchecked")
+ public MutablePatternMatcher(ComplexPattern<ReturnType, ?, InputType>... patterns) {
+ this();
+
+ for (ComplexPattern<ReturnType, ?, InputType> pattern : patterns) {
+ // Note: this may seem a somewhat questionable cast, but because we never
+ // actually do anything with the value who has a type matching the second
+ // parameter, this should be safe
+ this.patterns.add((ComplexPattern<ReturnType, Object, InputType>) pattern);
+ }
+ }
+
+ @Override
+ public ReturnType matchFor(InputType input) throws NonExhaustiveMatch {
+ Iterator<ComplexPattern<ReturnType, Object, InputType>> iterator;
+ for (iterator = new NonCMEIterator<>(patterns);
+ iterator.hasNext();) {
+ ComplexPattern<ReturnType, Object, InputType> pattern = iterator.next();
+
+ IPair<Boolean, Object> matches = pattern.matches(input);
+
+ if (matches.getLeft()) {
+ pattern.apply(input, matches.getRight());
+ }
+ }
+
+ throw new NonExhaustiveMatch("Non-exhaustive match against " + input);
+ }
+
+ /**
+ * Add a pattern to this pattern matcher.
+ *
+ * @param pattern The pattern to add.
+ *
+ * @return Whether or not the pattern was added.
+ */
+ @SuppressWarnings("unchecked")
+ public boolean addPattern(ComplexPattern<ReturnType, ?, InputType> pattern) {
+ return patterns.add((ComplexPattern<ReturnType, Object, InputType>) pattern);
+ }
+
+ /**
+ * Remove a pattern from this pattern matcher.
+ *
+ * @param pattern The pattern to remove.
+ *
+ * @return Whether or not the pattern was removed.
+ */
+ public boolean removePattern(ComplexPattern<ReturnType, ?, InputType> pattern) {
+ return patterns.remove(pattern);
+ }
+}
diff --git a/base/src/main/java/bjc/utils/patterns/NonExhaustiveMatch.java b/base/src/main/java/bjc/utils/patterns/NonExhaustiveMatch.java
new file mode 100644
index 0000000..aaa7e89
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/NonExhaustiveMatch.java
@@ -0,0 +1,20 @@
+package bjc.utils.patterns;
+
+/**
+ * Exception thrown when a non-exhaustive match happens.
+ * @author Ben Culkin
+ *
+ */
+public class NonExhaustiveMatch extends Exception {
+ private static final long serialVersionUID = 3892904574888418544L;
+
+ /**
+ * Create a new non-exhaustive match.
+ *
+ * @param message The message for the exception.
+ */
+ public NonExhaustiveMatch(String message) {
+ super(message);
+ }
+
+}
diff --git a/base/src/main/java/bjc/utils/patterns/Pattern.java b/base/src/main/java/bjc/utils/patterns/Pattern.java
new file mode 100644
index 0000000..e03623e
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/Pattern.java
@@ -0,0 +1,20 @@
+package bjc.utils.patterns;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.regex.*;
+
+import bjc.data.*;
+
+/**
+ * A simpler version of ComplexPattern, which always applies against Object
+ *
+ * @author Ben Culkin
+ *
+ * @param <ReturnType> The type returned by the pattern.
+ * @param <PredType> The state type returned by the predicate.
+ */
+public interface Pattern<ReturnType, PredType>
+ extends ComplexPattern<ReturnType, PredType, Object> {
+ /* Pattern factory methods */
+} \ No newline at end of file
diff --git a/base/src/main/java/bjc/utils/patterns/PatternMatcher.java b/base/src/main/java/bjc/utils/patterns/PatternMatcher.java
new file mode 100644
index 0000000..e2ae9f6
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/PatternMatcher.java
@@ -0,0 +1,41 @@
+package bjc.utils.patterns;
+
+import bjc.data.*;
+
+/**
+ * Implements pattern-matching (of a sort) against a collection of patterns.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ReturnType> The type returned by the pattern.
+ */
+public class PatternMatcher<ReturnType, InputType>
+ implements IPatternMatcher<ReturnType, InputType> {
+ private final ComplexPattern<ReturnType, Object, InputType>[] patterns;
+
+ /**
+ * Create a new pattern matcher.
+ *
+ * @param patterns The set of patterns to match against.
+ */
+ @SuppressWarnings("unchecked")
+ @SafeVarargs
+ public PatternMatcher(ComplexPattern<ReturnType, ?, InputType>...patterns) {
+ // Note: this may seem a somewhat questionable cast, but because we never
+ // actually do anything with the value who has a type matching the second
+ // parameter, this should be safe
+ this.patterns = (ComplexPattern<ReturnType, Object, InputType>[]) patterns;
+ }
+
+ @Override
+ public ReturnType matchFor(InputType input) throws NonExhaustiveMatch {
+ for (ComplexPattern<ReturnType, Object, InputType> pattern : patterns) {
+ IPair<Boolean, Object> matches = pattern.matches(input);
+ if (matches.getLeft()) {
+ pattern.apply(input, matches.getRight());
+ }
+ }
+
+ throw new NonExhaustiveMatch("Non-exhaustive match against " + input);
+ }
+}
diff --git a/base/src/main/java/bjc/utils/patterns/SimplePatttern.java b/base/src/main/java/bjc/utils/patterns/SimplePatttern.java
new file mode 100644
index 0000000..1601894
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/SimplePatttern.java
@@ -0,0 +1,40 @@
+package bjc.utils.patterns;
+
+import bjc.data.*;
+
+/**
+ * A simpler form of a pattern.
+ *
+ * @author Ben Culkin
+ *
+ * @param <ReturnType> The type returned by matching the pattern.
+ */
+public interface SimplePatttern<ReturnType> extends Pattern<ReturnType, Void> {
+ /**
+ * Test if this pattern does match a given object.
+ *
+ * @param input The object to test against.
+ *
+ * @return Whether the object matches this pattern.
+ */
+ boolean doesMatch(Object input);
+
+ /**
+ * Applies this pattern to the input object.
+ *
+ * @param input The object that passed the condition.
+ *
+ * @return The result of applying this action to the input.
+ */
+ ReturnType doApply(Object input);
+
+ @Override
+ default ReturnType apply(Object input, Void state) {
+ return doApply(input);
+ }
+
+ @Override
+ default IPair<Boolean, Void> matches(Object input) {
+ return new Pair<>(doesMatch(input), null);
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/bjc/utils/patterns/package-info.java b/base/src/main/java/bjc/utils/patterns/package-info.java
new file mode 100644
index 0000000..a562d95
--- /dev/null
+++ b/base/src/main/java/bjc/utils/patterns/package-info.java
@@ -0,0 +1,6 @@
+package bjc.utils.patterns;
+
+/*
+ * Pattern matching (of a sort) in Java. Based off of
+ * http://kerflyn.wordpress.com/2012/05/09/towards-pattern-matching-in-java/
+ */ \ No newline at end of file