Skip to content

Commit

Permalink
Cleanup the range predicate and add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
renggli committed Apr 25, 2015
1 parent 22e7ff6 commit e8f9156
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 46 deletions.
Expand Up @@ -67,7 +67,7 @@ static CharacterPredicate range(char start, char stop) {
*/
static CharacterPredicate ranges(char[] starts, char[] stops) {
if (starts.length != stops.length) {
throw new IllegalArgumentException("Invalid ranges.");
throw new IllegalArgumentException("Invalid range sizes.");
}
for (int i = 0; i < starts.length; i++) {
if (starts[i] > stops[i]) {
Expand Down Expand Up @@ -139,43 +139,4 @@ public CharacterPredicate not() {
return predicate;
}
}

/**
* Matches either this character predicate or any of the other {@code predicates}.
*/
default CharacterPredicate or(CharacterPredicate... others) {
CharacterPredicate[] predicates = new CharacterPredicate[1 + others.length];
predicates[0] = this;
System.arraycopy(others, 0, predicates, 1, others.length);
return new AltCharacterPredicate(predicates);
}

/**
* The alternative character predicate.
*/
class AltCharacterPredicate implements CharacterPredicate {

private final CharacterPredicate[] predicates;

public AltCharacterPredicate(CharacterPredicate... predicates) {
this.predicates = predicates;
}

@Override
public boolean test(char value) {
for (CharacterPredicate predicate : predicates) {
if (predicate.test(value)) {
return true;
}
}
return false;
}

@Override
public CharacterPredicate or(CharacterPredicate... others) {
CharacterPredicate[] array = Arrays.copyOf(predicates, predicates.length + others.length);
System.arraycopy(others, 0, array, predicates.length, others.length);
return new AltCharacterPredicate(array);
}
}
}
Expand Up @@ -5,18 +5,21 @@
import java.util.List;

/**
* Internal class to build an optimized {@link CharacterPredicate} from single characters
* or ranges of characters.
* Internal class to build an optimized {@link CharacterPredicate} from single characters or ranges
* of characters.
*/
class CharacterRange {

static final Comparator<CharacterRange> CHARACTER_RANGE_COMPARATOR =
Comparator
.comparing((CharacterRange range) -> range.start)
.thenComparing((CharacterRange range) -> range.stop);

static CharacterPredicate toCharacterPredicate(List<CharacterRange> ranges) {

// 1. sort the ranges
List<CharacterRange> sortedRanges = new ArrayList<>(ranges);
sortedRanges.sort(Comparator
.comparing((CharacterRange range) -> range.start)
.thenComparing((CharacterRange range) -> range.stop));
sortedRanges.sort(CHARACTER_RANGE_COMPARATOR);

// 2. merge adjacent or overlapping ranges
List<CharacterRange> mergedRanges = new ArrayList<>();
Expand Down Expand Up @@ -51,7 +54,6 @@ static CharacterPredicate toCharacterPredicate(List<CharacterRange> ranges) {
}
return CharacterPredicate.ranges(starts, stops);
}

}

private final char start;
Expand Down
@@ -0,0 +1,213 @@
package org.petitparser.parser.primitive;

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

/**
* Tests {@link CharacterPredicate}.
*/
public class CharacterPredicateTest {

@Test
public void testAny() {
CharacterPredicate predicate = CharacterPredicate.any();
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
}

@Test
public void testAnyOf() {
CharacterPredicate predicate = CharacterPredicate.anyOf("uncopyrightable");
assertTrue(predicate.test('c'));
assertTrue(predicate.test('g'));
assertTrue(predicate.test('h'));
assertTrue(predicate.test('i'));
assertTrue(predicate.test('o'));
assertTrue(predicate.test('p'));
assertTrue(predicate.test('r'));
assertTrue(predicate.test('t'));
assertTrue(predicate.test('y'));
assertFalse(predicate.test('x'));
}

@Test
public void testAnyOfEmpty() {
CharacterPredicate predicate = CharacterPredicate.anyOf("");
assertFalse(predicate.test('a'));
assertFalse(predicate.test('b'));
}

@Test
public void testNone() {
CharacterPredicate predicate = CharacterPredicate.none();
assertFalse(predicate.test('a'));
assertFalse(predicate.test('b'));
}

@Test
public void testNoneOf() {
CharacterPredicate predicate = CharacterPredicate.noneOf("uncopyrightable");
assertTrue(predicate.test('x'));
assertFalse(predicate.test('c'));
assertFalse(predicate.test('g'));
assertFalse(predicate.test('h'));
assertFalse(predicate.test('i'));
assertFalse(predicate.test('o'));
assertFalse(predicate.test('p'));
assertFalse(predicate.test('r'));
assertFalse(predicate.test('t'));
assertFalse(predicate.test('y'));
}

@Test
public void testNoneOfEmpty() {
CharacterPredicate predicate = CharacterPredicate.noneOf("");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
}

@Test
public void testOf() {
CharacterPredicate predicate = CharacterPredicate.of('a');
assertTrue(predicate.test('a'));
assertFalse(predicate.test('b'));
}

@Test
public void testNot() {
CharacterPredicate source = CharacterPredicate.of('a');
CharacterPredicate predicate = source.not();
assertFalse(predicate.test('a'));
assertTrue(predicate.test('b'));
assertSame(source, predicate.not());
}

@Test(expected = IllegalArgumentException.class)
public void testRangesInvalidSize() {
CharacterPredicate.ranges(new char[] {}, new char[] {'a'});
}

@Test(expected = IllegalArgumentException.class)
public void testRangesInvalidOrder() {
CharacterPredicate.ranges(new char[] {'b'}, new char[] {'a'});
}

@Test(expected = IllegalArgumentException.class)
public void testRangesInvalidSequence() {
CharacterPredicate.ranges(new char[] {'a', 'c'}, new char[] {'c', 'f'});
}

@Test
public void testPatternWithSingle() {
CharacterPredicate predicate = CharacterPredicate.pattern("abc");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertFalse(predicate.test('d'));
}

@Test
public void testPatternWithRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("a-c");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertFalse(predicate.test('d'));
}

@Test
public void testPatternWithOverlappingRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("b-da-c");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertTrue(predicate.test('d'));
assertFalse(predicate.test('e'));
}

@Test
public void testPatternWithAdjacentRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("c-ea-c");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertTrue(predicate.test('d'));
assertTrue(predicate.test('e'));
assertFalse(predicate.test('f'));
}

@Test
public void testPatternWithPrefixRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("a-ea-c");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertTrue(predicate.test('d'));
assertTrue(predicate.test('e'));
assertFalse(predicate.test('f'));
}

@Test
public void testPatternWithPostfixRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("a-ec-e");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertTrue(predicate.test('d'));
assertTrue(predicate.test('e'));
assertFalse(predicate.test('f'));
}

@Test
public void testPatternWithRepeatedRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("a-ea-e");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('b'));
assertTrue(predicate.test('c'));
assertTrue(predicate.test('d'));
assertTrue(predicate.test('e'));
assertFalse(predicate.test('f'));
}

@Test
public void testPatternWithComposed() {
CharacterPredicate predicate = CharacterPredicate.pattern("ac-df-");
assertTrue(predicate.test('a'));
assertTrue(predicate.test('c'));
assertTrue(predicate.test('d'));
assertTrue(predicate.test('f'));
assertTrue(predicate.test('-'));
assertFalse(predicate.test('b'));
assertFalse(predicate.test('e'));
assertFalse(predicate.test('g'));
}

@Test
public void testPatternWithNegatedSingle() {
CharacterPredicate predicate = CharacterPredicate.pattern("^a");
assertTrue(predicate.test('b'));
assertFalse(predicate.test('a'));
}

@Test
public void testPatternWithNegatedRange() {
CharacterPredicate predicate = CharacterPredicate.pattern("^a-c");
assertTrue(predicate.test('d'));
assertFalse(predicate.test('a'));
assertFalse(predicate.test('b'));
assertFalse(predicate.test('c'));
}

@Test
public void testRange() {
CharacterPredicate predicate = CharacterPredicate.range('e', 'o');
assertFalse(predicate.test('d'));
assertTrue(predicate.test('e'));
assertTrue(predicate.test('i'));
assertTrue(predicate.test('o'));
assertFalse(predicate.test('p'));
}
}

0 comments on commit e8f9156

Please sign in to comment.