Index: trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java	(revision 3045)
+++ trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java	(revision 3046)
@@ -7,4 +7,6 @@
 import java.io.IOException;
 import java.io.Reader;
+import java.util.Arrays;
+import java.util.List;
 
 import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
@@ -34,6 +36,6 @@
     private Token currentToken;
     private String currentText;
-    private long currentNumber;
-    private long currentRange;
+    private Long currentNumber;
+    private Long currentRange;
     private int c;
 
@@ -46,6 +48,6 @@
         NOT(marktr("<not>")), OR(marktr("<or>")), LEFT_PARENT(marktr("<left parent>")),
         RIGHT_PARENT(marktr("<right parent>")), COLON(marktr("<colon>")), EQUALS(marktr("<equals>")),
-        KEY(marktr("<key>")), QUESTION_MARK(marktr("<question mark>")), NUMBER(marktr("<number>")),
-        RANGE(marktr("range")), EOF(marktr("<end-of-file>"));
+        KEY(marktr("<key>")), QUESTION_MARK(marktr("<question mark>")),
+        EOF(marktr("<end-of-file>"));
 
         private Token(String name) {
@@ -77,4 +79,27 @@
         }
         return result;
+    }
+
+    private static final List<Integer> specialChars = Arrays.asList(new Integer[] {'"', ':', '(', ')', '|', '=', '?'});
+    private static final List<Integer> specialCharsQuoted = Arrays.asList(new Integer[] {'"'});
+
+    private String getString(boolean quoted) {
+        List<Integer> sChars = quoted ? specialCharsQuoted : specialChars;
+        StringBuilder s = new StringBuilder();
+        boolean escape = false;
+        while (c != -1 && (escape || (!sChars.contains(c) && (quoted || !Character.isWhitespace(c))))) {
+            if (c == '\\' && !escape) {
+                escape = true;
+            } else {
+                s.append((char)c);
+                escape = false;
+            }
+            getChar();
+        }
+        return s.toString();
+    }
+
+    private String getString() {
+        return getString(false);
     }
 
@@ -107,11 +132,4 @@
             getChar();
             return Token.EQUALS;
-        case '-':
-            getChar();
-            if (Character.isDigit(c)) {
-                currentNumber = -1 * getNumber();
-                return Token.NUMBER;
-            } else
-                return Token.NOT;
         case '(':
             getChar();
@@ -127,45 +145,35 @@
             return Token.QUESTION_MARK;
         case '"':
-        {
-            getChar();
-            StringBuilder s = new StringBuilder();
-            boolean escape = false;
-            while (c != -1 && (c != '"' || escape)) {
-                if (c == '\\' && !escape) {
-                    escape = true;
-                } else {
-                    s.append((char)c);
-                    escape = false;
-                }
+            getChar();
+            currentText = getString(true);
+            getChar();
+            return Token.KEY;
+        default:
+            String prefix = "";
+            if (c == '-') {
                 getChar();
-            }
-            getChar();
-            currentText = s.toString();
-            return Token.KEY;
-        }
-        default:
-        {
-            if (Character.isDigit(c)) {
-                currentNumber = getNumber();
-                if (c == '-') {
-                    getChar();
-                    currentRange = getNumber();
-                    return Token.RANGE;
-                } else
-                    return Token.NUMBER;
-
-            }
-
-            StringBuilder s = new StringBuilder();
-            while (!(c == -1 || Character.isWhitespace(c) || c == '"'|| c == ':' || c == '(' || c == ')' || c == '|' || c == '=' || c == '?')) {
-                s.append((char)c);
-                getChar();
-            }
-            currentText = s.toString();
+                if (!Character.isDigit(c))
+                    return Token.NOT;
+                prefix = "-";
+            }
+            currentText = prefix + getString();
             if ("or".equals(currentText))
                 return Token.OR;
-            else
-                return Token.KEY;
-        }
+            try {
+                currentNumber = Long.parseLong(currentText);
+            } catch (NumberFormatException e) {
+                currentNumber = null;
+            }
+            int pos = currentText.indexOf('-', 1);
+            if (pos > 0) {
+                try {
+                    currentNumber = Long.parseLong(currentText.substring(0, pos));
+                    currentRange = Long.parseLong(currentText.substring(pos + 1));
+                } catch (NumberFormatException e) {
+                    currentNumber = null;
+                    currentRange = null;
+                }
+            }
+            return Token.KEY;
         }
     }
@@ -183,6 +191,4 @@
         if (nextTok == Token.KEY)
             return currentText;
-        if (nextTok == Token.NUMBER)
-            return String.valueOf(currentNumber);
         currentToken = nextTok;
         return null;
@@ -190,5 +196,5 @@
 
     public long readNumber(String errorMessage) throws ParseError {
-        if (nextToken() == Token.NUMBER)
+        if ((nextToken() == Token.KEY) && (currentNumber != null))
             return currentNumber;
         else
@@ -197,15 +203,15 @@
 
     public long getReadNumber() {
-        return currentNumber;
-    }
-
-    public Range readRange() throws ParseError {
-        Token token = nextToken();
-        if (token == Token.NUMBER)
-            return new Range(currentNumber, currentNumber);
-        else if (token == Token.RANGE)
-            return new Range(currentNumber, currentRange);
-        else
-            throw new ParseError(Token.NUMBER, token);
+        return (currentNumber != null) ? currentNumber : 0;
+    }
+
+    public Range readRange(String errorMessage) throws ParseError {
+        if ((nextToken() == Token.KEY) && (currentNumber != null)) {
+            if (currentRange == null)
+                return new Range(currentNumber, currentNumber);
+            else
+                return new Range(currentNumber, currentRange);
+        } else
+            throw new ParseError(errorMessage);
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 3045)
+++ trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 3046)
@@ -653,8 +653,8 @@
                     return new Id(tokenizer.readNumber(tr("Primitive id expected")));
                 else if ("tags".equals(key)) {
-                    Range range = tokenizer.readRange();
+                    Range range = tokenizer.readRange(tr("Range of numbers expected"));
                     return new TagCountRange((int)range.getStart(), (int)range.getEnd());
                 } else if ("nodes".equals(key)) {
-                    Range range = tokenizer.readRange();
+                    Range range = tokenizer.readRange(tr("Range of numbers expected"));
                     return new NodeCountRange((int)range.getStart(), (int)range.getEnd());
                 } else if ("changeset".equals(key))
