From 4c0f972c4616eb549a098c3ef40d527eb542d7a6 Mon Sep 17 00:00:00 2001 From: Ben Culkin Date: Sat, 21 Nov 2020 16:51:04 -0500 Subject: Add basic pattern matching Adds a basic pattern matching implementation. Not perfect, but pretty good, considering what we have to work with --- .../java/bjc/utils/patterns/ComplexPattern.java | 183 +++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 base/src/main/java/bjc/utils/patterns/ComplexPattern.java (limited to 'base/src/main/java/bjc/utils/patterns/ComplexPattern.java') 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 The type of object being matched against. + * @param The type returned by the pattern. + * @param The state type returned by the predicate. + */ +public interface ComplexPattern { + /** + * 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 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 The type returned by the pattern. + * @param The type used as intermediate state. + * @param 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 ComplexPattern from( + Function> matcher, + BiFunction 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 The type to check if the object is an instance of. + * @param The type returned by the action. + * @param 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 ComplexPattern ofClass( + Class clasz, + Function 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 The type returned when the pattern matches. + * @param 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 ComplexPattern matchesObject( + InpType obj, + Function 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 The type returned by the pattern. + * @param 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 ComplexPattern equalsString( + String pattern, + BiFunction 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 The type returned by the pattern. + * @param 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 ComplexPattern matchesRegex( + String regex, + Predicate cond, + BiFunction 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 The type returned. + * @param The type being matched against. + * + * @param action The action to execute. + * + * @return A pattern which will be executed. + */ + static ComplexPattern otherwise( + Function action + ) { + return from( + (input) -> IPair.pair(true, null), + (input, ignored) -> action.apply(input) + ); + } +} \ No newline at end of file -- cgit v1.2.3