NfaMatcher { public boolean match(List<T> args) { Set<State> clist = new HashSet<>(); compile(); addState(clist, start); for (T arg : args) { Set<State> nlist = new HashSet<>(); clist.stream() .filter(s -> !Objects.equals(State.Match, s.c) && !Objects.equals(State.Split, s.c)) .filter(s -> matcher.apply(arg, s.c)) .forEach(s -> addState(nlist, s.out)); clist = nlist; } return clist.stream() .anyMatch(s -> Objects.equals(State.Match, s.c)); } NfaMatcher(String regexp, BiFunction<T, String, Boolean> matcher); void compile(); boolean match(List<T> args); Set<String> matchPartial(List<T> args);  }
@Test public void testMultiplicity() { assertFalse(match("C5")); assertTrue(match("C5", "arg")); assertFalse(match("C5", "arg", "foo")); assertTrue(match("C5?")); assertTrue(match("C5?", "arg")); assertFalse(match("C5?", "arg", "foo")); assertFalse(match("C5+")); assertTrue(match("C5+", "arg")); assertTrue(match("C5+", "arg", "foo")); assertTrue(match("C5*")); assertTrue(match("C5*", "arg")); assertTrue(match("C5*", "arg", "foo")); }
@Test public void testWeird() { assertTrue(match("a? a? a? a a a", "a", "a", "a", "a")); assertTrue(match("a ? * +", "a", "a", "a", "a")); }
@Test public void testConcat() { assertTrue(match("C4? C5+", "arg", "foo")); assertTrue(match("(C1 | C2 | C3)* C4? C5+", "arg", "foo")); assertTrue(match("(C1 | C2 | C3)* C4? C5+", "--opt1=a", "--opt2=b", "--myopt", "arg", "foo")); }