[jsemver] 43/95: Create Lexer for Expression parser
Alexandre Viau
reazem-guest at moszumanska.debian.org
Mon Feb 16 14:58:29 UTC 2015
This is an automated email from the git hooks/post-receive script.
reazem-guest pushed a commit to branch master
in repository jsemver.
commit 2dc8bd893050709177ecf3add72b94a45d02e1bd
Author: Zafar Khaja <zafarkhaja at gmail.com>
Date: Mon Nov 4 21:06:55 2013 +0400
Create Lexer for Expression parser
Factored out VersionParser.CharStream into a separate
class and made it generic to use in the Lexer. Did some
refactoring of VersionParser.
---
.../github/zafarkhaja/semver/ParserException.java | 4 +-
.../github/zafarkhaja/semver/VersionParser.java | 121 ++++--------
.../com/github/zafarkhaja/semver/expr/Lexer.java | 145 +++++++++++++++
.../LexerException.java} | 15 +-
.../UnexpectedTokenException.java} | 17 +-
.../com/github/zafarkhaja/semver/util/Stream.java | 148 +++++++++++++++
.../UnexpectedElementTypeException.java} | 16 +-
.../semver/VersionParserCharStreamTest.java | 118 ------------
.../zafarkhaja/semver/VersionParserCharTest.java | 51 +++--
.../github/zafarkhaja/semver/expr/LexerTest.java | 74 ++++++++
.../zafarkhaja/semver/expr/LexerTokenTest.java | 113 +++++++++++
.../github/zafarkhaja/semver/util/StreamTest.java | 206 +++++++++++++++++++++
12 files changed, 771 insertions(+), 257 deletions(-)
diff --git a/src/main/java/com/github/zafarkhaja/semver/ParserException.java b/src/main/java/com/github/zafarkhaja/semver/ParserException.java
index 469c060..e839b20 100644
--- a/src/main/java/com/github/zafarkhaja/semver/ParserException.java
+++ b/src/main/java/com/github/zafarkhaja/semver/ParserException.java
@@ -29,11 +29,11 @@ package com.github.zafarkhaja.semver;
*/
public class ParserException extends RuntimeException {
- ParserException(String message) {
+ public ParserException(String message) {
super(message);
}
- ParserException() {
+ public ParserException() {
}
}
diff --git a/src/main/java/com/github/zafarkhaja/semver/VersionParser.java b/src/main/java/com/github/zafarkhaja/semver/VersionParser.java
index ef28f61..a79a0d0 100644
--- a/src/main/java/com/github/zafarkhaja/semver/VersionParser.java
+++ b/src/main/java/com/github/zafarkhaja/semver/VersionParser.java
@@ -23,7 +23,7 @@
*/
package com.github.zafarkhaja.semver;
-import com.github.zafarkhaja.semver.VersionParser.CharStream;
+import com.github.zafarkhaja.semver.util.Stream;
import java.util.ArrayList;
import java.util.List;
import static com.github.zafarkhaja.semver.VersionParser.Char.*;
@@ -34,127 +34,70 @@ import static com.github.zafarkhaja.semver.VersionParser.Char.*;
*/
class VersionParser implements Parser<Version> {
- static class CharStream {
-
- static interface CharType {
- boolean isMatchedBy(char chr);
- }
-
- private final char[] data;
-
- private int offset = 0;
-
- static final char EOL = (char) -1;
-
- CharStream(String input) {
- data = input.toCharArray();
- }
-
- char consume() {
- if (offset + 1 <= data.length) {
- return data[offset++];
- }
- return EOL;
- }
-
- char consume(CharType... expected) {
- char la = lookahead(1);
- for (CharType charType : expected) {
- if (charType.isMatchedBy(la)) {
- return consume();
- }
- }
- throw new UnexpectedCharacterException(la, expected);
- }
-
- char lookahead() {
- return lookahead(1);
- }
-
- char lookahead(int pos) {
- int idx = offset + pos - 1;
- if (idx < data.length) {
- return data[idx];
- }
- return EOL;
- }
-
- boolean positiveLookahead(CharType... expected) {
- char la = lookahead(1);
- for (CharType charType : expected) {
- if (charType.isMatchedBy(la)) {
- return true;
- }
- }
- return false;
- }
-
- boolean positiveLookaheadBefore(CharType before, CharType... expected) {
- char la;
- for (int i = 1; i <= data.length; i++) {
- la = lookahead(i);
- if (before.isMatchedBy(la)) {
- break;
- }
- for (CharType charType : expected) {
- if (charType.isMatchedBy(la)) {
- return true;
- }
- }
- }
- return false;
- }
-
- char[] toArray() {
- return data.clone();
- }
- }
-
- static enum Char implements CharStream.CharType {
+ static enum Char implements Stream.ElementType<Character> {
DIGIT {
@Override
- public boolean isMatchedBy(char chr) {
+ public boolean isMatchedBy(Character chr) {
+ if (chr == null) {
+ return false;
+ }
return chr >= '0' && chr <= '9';
}
},
LETTER {
@Override
- public boolean isMatchedBy(char chr) {
+ public boolean isMatchedBy(Character chr) {
+ if (chr == null) {
+ return false;
+ }
return (chr >= 'a' && chr <= 'z')
|| (chr >= 'A' && chr <= 'Z');
}
},
DOT {
@Override
- public boolean isMatchedBy(char chr) {
+ public boolean isMatchedBy(Character chr) {
+ if (chr == null) {
+ return false;
+ }
return chr == '.';
}
},
HYPHEN {
@Override
- public boolean isMatchedBy(char chr) {
+ public boolean isMatchedBy(Character chr) {
+ if (chr == null) {
+ return false;
+ }
return chr == '-';
}
},
PLUS {
@Override
- public boolean isMatchedBy(char chr) {
+ public boolean isMatchedBy(Character chr) {
+ if (chr == null) {
+ return false;
+ }
return chr == '+';
}
},
EOL {
@Override
- public boolean isMatchedBy(char chr) {
- return chr == CharStream.EOL;
+ public boolean isMatchedBy(Character chr) {
+ return chr == null;
}
};
}
- private final CharStream chars;
+ private final Stream<Character> chars;
VersionParser(String input) {
- chars = new CharStream(input);
+ Character[] elements = new Character[input.length()];
+ for (int i = 0; i < input.length(); i++) {
+ elements[i] = Character.valueOf(input.charAt(i));
+ }
+ chars = new Stream<Character>(elements);
}
@Override
@@ -298,8 +241,8 @@ class VersionParser implements Parser<Version> {
}
private void checkForLeadingZeroes() {
- char la1 = chars.lookahead(1);
- char la2 = chars.lookahead(2);
+ Character la1 = chars.lookahead(1);
+ Character la2 = chars.lookahead(2);
if (la1 == '0' && DIGIT.isMatchedBy(la2)) {
throw new GrammarException(
"Numeric identifier MUST NOT contain leading zeroes"
diff --git a/src/main/java/com/github/zafarkhaja/semver/expr/Lexer.java b/src/main/java/com/github/zafarkhaja/semver/expr/Lexer.java
new file mode 100644
index 0000000..c6bff31
--- /dev/null
+++ b/src/main/java/com/github/zafarkhaja/semver/expr/Lexer.java
@@ -0,0 +1,145 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.expr;
+
+import com.github.zafarkhaja.semver.util.Stream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+class Lexer {
+
+ static class Token {
+
+ enum Type implements Stream.ElementType<Token> {
+
+ NUMERIC("0|[1-9][0-9]*"),
+ DOT("\\."),
+ HYPHEN("-"),
+ EQUAL("="),
+ NOT_EQUAL("!="),
+ GREATER(">(?!=)"),
+ GREATER_EQUAL(">="),
+ LESS("<(?!=)"),
+ LESS_EQUAL("<="),
+ TILDE("~"),
+ STAR("\\*"),
+ AND("&"),
+ OR("\\|"),
+ NOT("!(?!=)"),
+ LEFT_PAREN("\\("),
+ RIGHT_PAREN("\\)"),
+ WHITESPACE("\\s+"),
+ EOL("?!") {
+ @Override
+ public boolean isMatchedBy(Token token) {
+ return token == null;
+ }
+ };
+
+ final Pattern pattern;
+
+ private Type(String regexp) {
+ pattern = Pattern.compile("^(" + regexp + ")");
+ }
+
+ @Override
+ public String toString() {
+ return name() + "(" + pattern + ")";
+ }
+
+ @Override
+ public boolean isMatchedBy(Token token) {
+ if (token == null) {
+ return false;
+ }
+ return this == token.type;
+ }
+ }
+
+ final Type type;
+ final String lexeme;
+
+ Token(Type type, String lexeme) {
+ this.type = type;
+ this.lexeme = (lexeme == null) ? "" : lexeme;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Token)) {
+ return false;
+ }
+ Token token = (Token) other;
+ return type.equals(token.type) && lexeme.equals(token.lexeme);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 71 * hash + type.hashCode();
+ hash = 71 * hash + lexeme.hashCode();
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return type.name() + "(" + lexeme + ")";
+ }
+ }
+
+ Lexer() {
+
+ }
+
+ Stream<Token> tokenize(String input) {
+ List<Token> tokens = new ArrayList<Token>();
+ while (!input.isEmpty()) {
+ boolean matched = false;
+ for (Token.Type tokenType : Token.Type.values()) {
+ Matcher matcher = tokenType.pattern.matcher(input);
+ if (matcher.find()) {
+ matched = true;
+ input = matcher.replaceFirst("");
+ if (tokenType != Token.Type.WHITESPACE) {
+ tokens.add(new Token(tokenType, matcher.group()));
+ }
+ break;
+ }
+ }
+ if (!matched) {
+ throw new LexerException(input);
+ }
+ }
+ return new Stream<Token>(tokens.toArray(new Token[tokens.size()]));
+ }
+}
diff --git a/src/main/java/com/github/zafarkhaja/semver/ParserException.java b/src/main/java/com/github/zafarkhaja/semver/expr/LexerException.java
similarity index 80%
copy from src/main/java/com/github/zafarkhaja/semver/ParserException.java
copy to src/main/java/com/github/zafarkhaja/semver/expr/LexerException.java
index 469c060..2927c56 100644
--- a/src/main/java/com/github/zafarkhaja/semver/ParserException.java
+++ b/src/main/java/com/github/zafarkhaja/semver/expr/LexerException.java
@@ -21,19 +21,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package com.github.zafarkhaja.semver;
+package com.github.zafarkhaja.semver.expr;
/**
*
* @author Zafar Khaja <zafarkhaja at gmail.com>
*/
-public class ParserException extends RuntimeException {
+public class LexerException extends RuntimeException {
- ParserException(String message) {
- super(message);
- }
+ private final String expr;
- ParserException() {
+ LexerException(String expr) {
+ this.expr = expr;
+ }
+ @Override
+ public String toString() {
+ return "Illegal character near '" + expr + "'";
}
}
diff --git a/src/main/java/com/github/zafarkhaja/semver/UnexpectedCharacterException.java b/src/main/java/com/github/zafarkhaja/semver/expr/UnexpectedTokenException.java
similarity index 76%
copy from src/main/java/com/github/zafarkhaja/semver/UnexpectedCharacterException.java
copy to src/main/java/com/github/zafarkhaja/semver/expr/UnexpectedTokenException.java
index f52fda0..67ebd55 100644
--- a/src/main/java/com/github/zafarkhaja/semver/UnexpectedCharacterException.java
+++ b/src/main/java/com/github/zafarkhaja/semver/expr/UnexpectedTokenException.java
@@ -21,28 +21,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package com.github.zafarkhaja.semver;
+package com.github.zafarkhaja.semver.expr;
-import com.github.zafarkhaja.semver.VersionParser.CharStream.CharType;
+import com.github.zafarkhaja.semver.ParserException;
+import com.github.zafarkhaja.semver.expr.Lexer.*;
import java.util.Arrays;
/**
*
* @author Zafar Khaja <zafarkhaja at gmail.com>
*/
-public class UnexpectedCharacterException extends ParserException {
+public class UnexpectedTokenException extends ParserException {
- private final char unexpected;
- private final CharType[] expected;
+ private final Token unexpected;
+ private final Token.Type[] expected;
- UnexpectedCharacterException(char chr, CharType... expected) {
- unexpected = chr;
+ UnexpectedTokenException(Token token, Token.Type... expected) {
+ unexpected = token;
this.expected = expected;
}
@Override
public String toString() {
- String message = "Unexpected character '" + unexpected + "'";
+ String message = "Unexpected token '" + unexpected + "'";
if (expected.length > 0) {
message += ", expecting '" + Arrays.toString(expected) + "'";
}
diff --git a/src/main/java/com/github/zafarkhaja/semver/util/Stream.java b/src/main/java/com/github/zafarkhaja/semver/util/Stream.java
new file mode 100644
index 0000000..50dddf7
--- /dev/null
+++ b/src/main/java/com/github/zafarkhaja/semver/util/Stream.java
@@ -0,0 +1,148 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.util;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+public class Stream<E> implements Iterable<E> {
+
+ public static interface ElementType<E> {
+ boolean isMatchedBy(E element);
+ }
+
+ private final E[] elements;
+
+ private int offset = 0;
+
+ public Stream(E[] elements) {
+ this.elements = elements.clone();
+ }
+
+ public E consume() {
+ if (offset >= elements.length) {
+ return null;
+ }
+ return elements[offset++];
+ }
+
+ public E consume(ElementType<E>... expected) {
+ E lookahead = lookahead(1);
+ for (ElementType<E> type : expected) {
+ if (type.isMatchedBy(lookahead)) {
+ return consume();
+ }
+ }
+ throw new UnexpectedElementTypeException(lookahead, expected);
+ }
+
+ public E lookahead() {
+ return lookahead(1);
+ }
+
+ public E lookahead(int position) {
+ int idx = offset + position - 1;
+ if (idx < elements.length) {
+ return elements[idx];
+ }
+ return null;
+ }
+
+ public boolean positiveLookahead(ElementType<E>... expected) {
+ for (ElementType<E> type : expected) {
+ if (type.isMatchedBy(lookahead(1))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean positiveLookaheadBefore(
+ ElementType<E> before,
+ ElementType<E>... expected
+ ) {
+ E lookahead;
+ for (int i = 1; i <= elements.length; i++) {
+ lookahead = lookahead(i);
+ if (before.isMatchedBy(lookahead)) {
+ break;
+ }
+ for (ElementType<E> type : expected) {
+ if (type.isMatchedBy(lookahead)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean positiveLookaheadUntil(
+ int until,
+ ElementType<E>... expected
+ ) {
+ for (int i = 1; i <= until; i++) {
+ for (ElementType<E> type : expected) {
+ if (type.isMatchedBy(lookahead(i))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new Iterator<E>() {
+
+ private int index = offset;
+
+ @Override
+ public boolean hasNext() {
+ return index < elements.length;
+ }
+
+ @Override
+ public E next() {
+ if (index >= elements.length) {
+ throw new NoSuchElementException();
+ }
+ return elements[index++];
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public E[] toArray() {
+ return Arrays.copyOfRange(elements, offset, elements.length);
+ }
+}
diff --git a/src/main/java/com/github/zafarkhaja/semver/UnexpectedCharacterException.java b/src/main/java/com/github/zafarkhaja/semver/util/UnexpectedElementTypeException.java
similarity index 77%
rename from src/main/java/com/github/zafarkhaja/semver/UnexpectedCharacterException.java
rename to src/main/java/com/github/zafarkhaja/semver/util/UnexpectedElementTypeException.java
index f52fda0..7eb5bcb 100644
--- a/src/main/java/com/github/zafarkhaja/semver/UnexpectedCharacterException.java
+++ b/src/main/java/com/github/zafarkhaja/semver/util/UnexpectedElementTypeException.java
@@ -21,28 +21,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package com.github.zafarkhaja.semver;
+package com.github.zafarkhaja.semver.util;
-import com.github.zafarkhaja.semver.VersionParser.CharStream.CharType;
+import com.github.zafarkhaja.semver.util.Stream.ElementType;
import java.util.Arrays;
/**
*
* @author Zafar Khaja <zafarkhaja at gmail.com>
*/
-public class UnexpectedCharacterException extends ParserException {
+public class UnexpectedElementTypeException extends RuntimeException {
- private final char unexpected;
- private final CharType[] expected;
+ private final Object unexpected;
+ private final ElementType<?>[] expected;
- UnexpectedCharacterException(char chr, CharType... expected) {
- unexpected = chr;
+ UnexpectedElementTypeException(Object element, ElementType<?>... expected) {
+ unexpected = element;
this.expected = expected;
}
@Override
public String toString() {
- String message = "Unexpected character '" + unexpected + "'";
+ String message = "Unexpected element '" + unexpected + "'";
if (expected.length > 0) {
message += ", expecting '" + Arrays.toString(expected) + "'";
}
diff --git a/src/test/java/com/github/zafarkhaja/semver/VersionParserCharStreamTest.java b/src/test/java/com/github/zafarkhaja/semver/VersionParserCharStreamTest.java
deleted file mode 100644
index 1c3bf77..0000000
--- a/src/test/java/com/github/zafarkhaja/semver/VersionParserCharStreamTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package com.github.zafarkhaja.semver;
-
-import com.github.zafarkhaja.semver.VersionParser.Char;
-import com.github.zafarkhaja.semver.VersionParser.CharStream;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-/**
- *
- * @author Zafar Khaja <zafarkhaja at gmail.com>
- */
-public class VersionParserCharStreamTest {
-
- @Test
- public void shouldBeBackedByCharArray() {
- String input = "abc";
- CharStream chars = new CharStream(input);
- assertArrayEquals(input.toCharArray(), chars.toArray());
- }
-
- @Test
- public void shouldNotReturnRealCharArray() {
- CharStream chars = new CharStream("abc");
- char[] charArray = chars.toArray();
- charArray[0] = 'z';
- assertEquals('z', charArray[0]);
- assertEquals('a', chars.lookahead());
- }
-
- @Test
- public void shouldConsumeCharactersOneByOne() {
- CharStream chars = new CharStream("abc");
- assertEquals('a', chars.consume());
- assertEquals('b', chars.consume());
- assertEquals('c', chars.consume());
- }
-
- @Test
- public void shouldReturnEolWhenNothingLeftToConsume() {
- CharStream chars = new CharStream("abc");
- assertEquals('a', chars.consume());
- assertEquals('b', chars.consume());
- assertEquals('c', chars.consume());
- assertEquals(CharStream.EOL, chars.consume());
- }
-
- @Test
- public void shouldRaiseErrorWhenUnexpectedCharTypeConsumed() {
- CharStream chars = new CharStream("abc");
- try {
- chars.consume(Char.DIGIT);
- } catch (UnexpectedCharacterException e) {
- return;
- }
- fail("Should raise error when unexpected character type is consumed");
- }
-
- @Test
- public void shouldLookaheadWithoutConsuming() {
- CharStream chars = new CharStream("abc");
- assertEquals('a', chars.lookahead());
- assertEquals('a', chars.lookahead());
- }
-
- @Test
- public void shouldLookaheadArbitraryNumberOfCharacters() {
- CharStream chars = new CharStream("abc");
- assertEquals('a', chars.lookahead(1));
- assertEquals('b', chars.lookahead(2));
- assertEquals('c', chars.lookahead(3));
- }
-
- @Test
- public void shouldReturnEolWhenNothingLeftToLookahead() {
- CharStream chars = new CharStream("abc");
- assertEquals('a', chars.consume());
- assertEquals('b', chars.consume());
- assertEquals('c', chars.consume());
- assertEquals(CharStream.EOL, chars.lookahead());
- }
-
- @Test
- public void shouldCheckIfLookaheadIsOfExpectedTypes() {
- CharStream chars = new CharStream("abc");
- assertTrue(chars.positiveLookahead(Char.LETTER));
- assertFalse(chars.positiveLookahead(Char.DIGIT, Char.PLUS));
- }
-
- @Test
- public void shouldCheckIfCharOfExpectedTypesExistBeforeGivenType() {
- CharStream chars = new CharStream("1.0.0");
- assertTrue(chars.positiveLookaheadBefore(Char.EOL, Char.DOT));
- assertFalse(chars.positiveLookaheadBefore(Char.EOL, Char.LETTER));
- }
-}
diff --git a/src/test/java/com/github/zafarkhaja/semver/VersionParserCharTest.java b/src/test/java/com/github/zafarkhaja/semver/VersionParserCharTest.java
index 3d4741c..d6ccd1b 100644
--- a/src/test/java/com/github/zafarkhaja/semver/VersionParserCharTest.java
+++ b/src/test/java/com/github/zafarkhaja/semver/VersionParserCharTest.java
@@ -23,9 +23,8 @@
*/
package com.github.zafarkhaja.semver;
-import com.github.zafarkhaja.semver.VersionParser.Char;
-import com.github.zafarkhaja.semver.VersionParser.CharStream;
import org.junit.Test;
+import static com.github.zafarkhaja.semver.VersionParser.Char.*;
import static org.junit.Assert.*;
/**
@@ -36,49 +35,49 @@ public class VersionParserCharTest {
@Test
public void shouldBeMatchedByDigit() {
- assertTrue(Char.DIGIT.isMatchedBy('0'));
- assertTrue(Char.DIGIT.isMatchedBy('9'));
- assertFalse(Char.DIGIT.isMatchedBy('a'));
- assertFalse(Char.DIGIT.isMatchedBy('A'));
+ assertTrue(DIGIT.isMatchedBy('0'));
+ assertTrue(DIGIT.isMatchedBy('9'));
+ assertFalse(DIGIT.isMatchedBy('a'));
+ assertFalse(DIGIT.isMatchedBy('A'));
}
@Test
public void shouldBeMatchedByLetter() {
- assertTrue(Char.LETTER.isMatchedBy('a'));
- assertTrue(Char.LETTER.isMatchedBy('A'));
- assertFalse(Char.LETTER.isMatchedBy('0'));
- assertFalse(Char.LETTER.isMatchedBy('9'));
+ assertTrue(LETTER.isMatchedBy('a'));
+ assertTrue(LETTER.isMatchedBy('A'));
+ assertFalse(LETTER.isMatchedBy('0'));
+ assertFalse(LETTER.isMatchedBy('9'));
}
@Test
public void shouldBeMatchedByDot() {
- assertTrue(Char.DOT.isMatchedBy('.'));
- assertFalse(Char.DOT.isMatchedBy('-'));
- assertFalse(Char.DOT.isMatchedBy('0'));
- assertFalse(Char.DOT.isMatchedBy('9'));
+ assertTrue(DOT.isMatchedBy('.'));
+ assertFalse(DOT.isMatchedBy('-'));
+ assertFalse(DOT.isMatchedBy('0'));
+ assertFalse(DOT.isMatchedBy('9'));
}
@Test
public void shouldBeMatchedByHyphen() {
- assertTrue(Char.HYPHEN.isMatchedBy('-'));
- assertFalse(Char.HYPHEN.isMatchedBy('+'));
- assertFalse(Char.HYPHEN.isMatchedBy('a'));
- assertFalse(Char.HYPHEN.isMatchedBy('0'));
+ assertTrue(HYPHEN.isMatchedBy('-'));
+ assertFalse(HYPHEN.isMatchedBy('+'));
+ assertFalse(HYPHEN.isMatchedBy('a'));
+ assertFalse(HYPHEN.isMatchedBy('0'));
}
@Test
public void shouldBeMatchedByPlus() {
- assertTrue(Char.PLUS.isMatchedBy('+'));
- assertFalse(Char.PLUS.isMatchedBy('-'));
- assertFalse(Char.PLUS.isMatchedBy('a'));
- assertFalse(Char.PLUS.isMatchedBy('0'));
+ assertTrue(PLUS.isMatchedBy('+'));
+ assertFalse(PLUS.isMatchedBy('-'));
+ assertFalse(PLUS.isMatchedBy('a'));
+ assertFalse(PLUS.isMatchedBy('0'));
}
@Test
public void shouldBeMatchedByEol() {
- assertTrue(Char.EOL.isMatchedBy(CharStream.EOL));
- assertFalse(Char.EOL.isMatchedBy('-'));
- assertFalse(Char.EOL.isMatchedBy('a'));
- assertFalse(Char.EOL.isMatchedBy('0'));
+ assertTrue(EOL.isMatchedBy(null));
+ assertFalse(EOL.isMatchedBy('-'));
+ assertFalse(EOL.isMatchedBy('a'));
+ assertFalse(EOL.isMatchedBy('0'));
}
}
diff --git a/src/test/java/com/github/zafarkhaja/semver/expr/LexerTest.java b/src/test/java/com/github/zafarkhaja/semver/expr/LexerTest.java
new file mode 100644
index 0000000..1da42c7
--- /dev/null
+++ b/src/test/java/com/github/zafarkhaja/semver/expr/LexerTest.java
@@ -0,0 +1,74 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.expr;
+
+import com.github.zafarkhaja.semver.expr.Lexer.*;
+import com.github.zafarkhaja.semver.util.Stream;
+import org.junit.Test;
+import static com.github.zafarkhaja.semver.expr.Lexer.Token.Type.*;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+public class LexerTest {
+
+ @Test
+ public void shouldTokenizeVersionString() {
+ Token[] expected = {
+ new Token(GREATER, ">"),
+ new Token(NUMERIC, "1"),
+ new Token(DOT, "."),
+ new Token(NUMERIC, "0"),
+ new Token(DOT, "."),
+ new Token(NUMERIC, "0"),
+ };
+ Lexer lexer = new Lexer();
+ Stream<Token> stream = lexer.tokenize(">1.0.0");
+ assertArrayEquals(expected, stream.toArray());
+ }
+
+ @Test
+ public void shouldSkipWhitespaces() {
+ Token[] expected = {
+ new Token(GREATER, ">"),
+ new Token(NUMERIC, "1"),
+ };
+ Lexer lexer = new Lexer();
+ Stream<Token> stream = lexer.tokenize("> 1");
+ assertArrayEquals(expected, stream.toArray());
+ }
+
+ @Test
+ public void shouldRaiseErrorOnIllegalCharacter() {
+ Lexer lexer = new Lexer();
+ try {
+ lexer.tokenize("@1.0.0");
+ } catch (LexerException e) {
+ return;
+ }
+ fail("Should raise error on illegal character");
+ }
+}
diff --git a/src/test/java/com/github/zafarkhaja/semver/expr/LexerTokenTest.java b/src/test/java/com/github/zafarkhaja/semver/expr/LexerTokenTest.java
new file mode 100644
index 0000000..36c4729
--- /dev/null
+++ b/src/test/java/com/github/zafarkhaja/semver/expr/LexerTokenTest.java
@@ -0,0 +1,113 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.expr;
+
+import com.github.zafarkhaja.semver.expr.Lexer.Token;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import static com.github.zafarkhaja.semver.expr.Lexer.Token.Type.*;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+ at RunWith(Enclosed.class)
+public class LexerTokenTest {
+
+ public static class EqualsMethodTest {
+
+ @Test
+ public void shouldBeReflexive() {
+ Token token = new Token(NUMERIC, "1");
+ assertTrue(token.equals(token));
+ }
+
+ @Test
+ public void shouldBeSymmetric() {
+ Token t1 = new Token(EQUAL, "=");
+ Token t2 = new Token(EQUAL, "=");
+ assertTrue(t1.equals(t2));
+ assertTrue(t2.equals(t1));
+ }
+
+ @Test
+ public void shouldBeTransitive() {
+ Token t1 = new Token(GREATER, ">");
+ Token t2 = new Token(GREATER, ">");
+ Token t3 = new Token(GREATER, ">");
+ assertTrue(t1.equals(t2));
+ assertTrue(t2.equals(t3));
+ assertTrue(t1.equals(t3));
+ }
+
+ @Test
+ public void shouldBeConsistent() {
+ Token t1 = new Token(HYPHEN, "-");
+ Token t2 = new Token(HYPHEN, "-");
+ assertTrue(t1.equals(t2));
+ assertTrue(t1.equals(t2));
+ assertTrue(t1.equals(t2));
+ }
+
+ @Test
+ public void shouldReturnFalseIfOtherVersionIsOfDifferentType() {
+ Token t1 = new Token(DOT, ".");
+ assertFalse(t1.equals(new String(".")));
+ }
+
+ @Test
+ public void shouldReturnFalseIfOtherVersionIsNull() {
+ Token t1 = new Token(AND, "&");
+ Token t2 = null;
+ assertFalse(t1.equals(t2));
+ }
+
+ @Test
+ public void shouldReturnFalseIfTypesAreDifferent() {
+ Token t1 = new Token(EQUAL, "=");
+ Token t2 = new Token(NOT_EQUAL, "!=");
+ assertFalse(t1.equals(t2));
+ }
+
+ @Test
+ public void shouldReturnFalseIfLexemesAreDifferent() {
+ Token t1 = new Token(NUMERIC, "1");
+ Token t2 = new Token(NUMERIC, "2");
+ assertFalse(t1.equals(t2));
+ }
+ }
+
+ public static class HashCodeMethodTest {
+
+ @Test
+ public void shouldReturnSameHashCodeIfTokensAreEqual() {
+ Token t1 = new Token(NUMERIC, "1");
+ Token t2 = new Token(NUMERIC, "1");
+ assertTrue(t1.equals(t2));
+ assertEquals(t1.hashCode(), t2.hashCode());
+ }
+ }
+}
diff --git a/src/test/java/com/github/zafarkhaja/semver/util/StreamTest.java b/src/test/java/com/github/zafarkhaja/semver/util/StreamTest.java
new file mode 100644
index 0000000..2ea8642
--- /dev/null
+++ b/src/test/java/com/github/zafarkhaja/semver/util/StreamTest.java
@@ -0,0 +1,206 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.util;
+
+import com.github.zafarkhaja.semver.util.Stream.ElementType;
+import java.util.Iterator;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+public class StreamTest {
+
+ @Test
+ public void shouldBeBackedByArray() {
+ Character[] input = {'a', 'b', 'c'};
+ Stream<Character> stream = new Stream<Character>(input);
+ assertArrayEquals(input, stream.toArray());
+ }
+
+ @Test
+ public void shouldImplementIterable() {
+ Character[] input = {'a', 'b', 'c'};
+ Stream<Character> stream = new Stream<Character>(input);
+ Iterator<Character> it = stream.iterator();
+ for (Character chr : input) {
+ assertEquals(chr, it.next());
+ }
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void shouldNotReturnRealElementsArray() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ Character[] charArray = stream.toArray();
+ charArray[0] = Character.valueOf('z');
+ assertEquals(Character.valueOf('z'), charArray[0]);
+ assertEquals(Character.valueOf('a'), stream.toArray()[0]);
+ }
+
+ @Test
+ public void shouldReturnArrayOfElementsThatAreLeftInStream() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ stream.consume();
+ stream.consume();
+ assertEquals(1, stream.toArray().length);
+ assertEquals(Character.valueOf('c'), stream.toArray()[0]);
+ }
+
+ @Test
+ public void shouldConsumeElementsOneByOne() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ assertEquals(Character.valueOf('a'), stream.consume());
+ assertEquals(Character.valueOf('b'), stream.consume());
+ assertEquals(Character.valueOf('c'), stream.consume());
+ }
+
+ @Test
+ public void shouldRaiseErrorWhenUnexpectedElementTypeConsumed() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ try {
+ stream.consume(new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return false;
+ }
+ });
+ } catch (UnexpectedElementTypeException e) {
+ return;
+ }
+ fail("Should raise error when unexpected element type is consumed");
+ }
+
+ @Test
+ public void shouldLookaheadWithoutConsuming() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ assertEquals(Character.valueOf('a'), stream.lookahead());
+ assertEquals(Character.valueOf('a'), stream.lookahead());
+ }
+
+ @Test
+ public void shouldLookaheadArbitraryNumberOfElements() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ assertEquals(Character.valueOf('a'), stream.lookahead(1));
+ assertEquals(Character.valueOf('b'), stream.lookahead(2));
+ assertEquals(Character.valueOf('c'), stream.lookahead(3));
+ }
+
+ @Test
+ public void shouldCheckIfLookaheadIsOfExpectedTypes() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'a', 'b', 'c'}
+ );
+ assertTrue(stream.positiveLookahead(
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == 'a';
+ }
+ }
+ ));
+ assertFalse(stream.positiveLookahead(
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == 'c';
+ }
+ }
+ ));
+ }
+
+ @Test
+ public void shouldCheckIfElementOfExpectedTypesExistBeforeGivenType() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'1', '.', '0', '.', '0'}
+ );
+ assertTrue(stream.positiveLookaheadBefore(
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == '.';
+ }
+ },
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == '1';
+ }
+ }
+ ));
+ assertFalse(stream.positiveLookaheadBefore(
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == '1';
+ }
+ },
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == '.';
+ }
+ }
+ ));
+ }
+
+ @Test
+ public void shouldCheckIfElementOfExpectedTypesExistUntilGivenPosition() {
+ Stream<Character> stream = new Stream<Character>(
+ new Character[] {'1', '.', '0', '.', '0'}
+ );
+ assertTrue(stream.positiveLookaheadUntil(
+ 3,
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == '0';
+ }
+ }
+ ));
+ assertFalse(stream.positiveLookaheadUntil(
+ 3,
+ new ElementType<Character>() {
+ @Override
+ public boolean isMatchedBy(Character element) {
+ return element == 'a';
+ }
+ }
+ ));
+ }
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jsemver.git
More information about the pkg-java-commits
mailing list