[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