More work on async parsing
diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java
index d53bdd9..6fb067b 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java
@@ -207,6 +207,14 @@
         // NOTE: caller ensures availability of at least one byte
 
         switch (_minorState) {
+        case MINOR_VALUE_TOKEN_NULL:
+            return _finishKeywordToken("null", _pending32, JsonToken.VALUE_NULL);
+        case MINOR_VALUE_TOKEN_TRUE:
+            return _finishKeywordToken("null", _pending32, JsonToken.VALUE_TRUE);
+        case MINOR_VALUE_TOKEN_FALSE:
+            return _finishKeywordToken("null", _pending32, JsonToken.VALUE_FALSE);
+        case MINOR_VALUE_TOKEN_ERROR: // case of "almost token", just need tokenize for error
+            return _finishErrorToken();
         }
         return null;
     }
@@ -345,17 +353,109 @@
 
     protected JsonToken _startFalseToken() throws IOException
     {
-        return null;
+        int ptr = _inputPtr;
+        if ((ptr + 4) < _inputEnd) { // yes, can determine efficiently
+            byte[] buf = _inputBuffer;
+            if ((buf[ptr++] == 'a') 
+                   && (buf[ptr++] == 'l')
+                   && (buf[ptr++] == 's')
+                   && (buf[ptr++] == 'e')) {
+                int ch = buf[ptr] & 0xFF;
+                if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
+                    _inputPtr = ptr;
+                    return _valueComplete(JsonToken.VALUE_FALSE);
+                }
+            }
+        }
+        _minorState = MINOR_VALUE_TOKEN_FALSE;
+        return _finishKeywordToken("false", 1, JsonToken.VALUE_FALSE);
     }
 
     protected JsonToken _startTrueToken() throws IOException
     {
-        return null;
+        int ptr = _inputPtr;
+        if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently
+            byte[] buf = _inputBuffer;
+            if ((buf[ptr++] == 'r') 
+                   && (buf[ptr++] == 'u')
+                   && (buf[ptr++] == 'e')) {
+                int ch = buf[ptr] & 0xFF;
+                if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
+                    _inputPtr = ptr;
+                    return _valueComplete(JsonToken.VALUE_TRUE);
+                }
+            }
+        }
+        _minorState = MINOR_VALUE_TOKEN_TRUE;
+        return _finishKeywordToken("true", 1, JsonToken.VALUE_TRUE);
     }
 
     protected JsonToken _startNullToken() throws IOException
     {
-        return null;
+        int ptr = _inputPtr;
+        if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently
+            byte[] buf = _inputBuffer;
+            if ((buf[ptr++] == 'u') 
+                   && (buf[ptr++] == 'l')
+                   && (buf[ptr++] == 'l')) {
+                int ch = buf[ptr] & 0xFF;
+                if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
+                    _inputPtr = ptr;
+                    return _valueComplete(JsonToken.VALUE_NULL);
+                }
+            }
+        }
+        _minorState = MINOR_VALUE_TOKEN_TRUE;
+        return _finishKeywordToken("null", 1, JsonToken.VALUE_NULL);
+    }
+
+    protected JsonToken _finishKeywordToken(String expToken, int matched,
+            JsonToken result) throws IOException
+    {
+        final int end = expToken.length();
+
+        while (true) {
+            if (_inputPtr >= _inputEnd) {
+                _pending32 = matched;
+                return (_currToken = JsonToken.NOT_AVAILABLE);
+            }
+            int ch = _inputBuffer[_inputPtr] & 0xFF;
+            if (matched == end) { // need to verify trailing separator
+                if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
+                    return _valueComplete(JsonToken.VALUE_NULL);
+                }
+            }
+            if (ch != expToken.charAt(matched)) {
+                break;
+            }
+            ++_inputPtr;
+        }
+        _minorState = MINOR_VALUE_TOKEN_ERROR;
+        _textBuffer.resetWithString(expToken.substring(0, matched));
+        return _finishErrorToken();
+    }
+
+    protected JsonToken _finishErrorToken() throws IOException
+    {
+        while (_inputPtr < _inputEnd) {
+            int i = (int) _inputBuffer[_inputPtr++];
+
+// !!! TODO: Decode UTF-8 characters properly...
+//            char c = (char) _decodeCharForError(i);
+
+            char ch = (char) i;
+            if (Character.isJavaIdentifierPart(ch)) {
+                // 11-Jan-2016, tatu: note: we will fully consume the character,
+                // included or not, so if recovery was possible, it'd be off-by-one...
+                _textBuffer.append(ch);
+                if (_textBuffer.size() < MAX_ERROR_TOKEN_LENGTH) {
+                    continue;
+                }
+            }
+            _reportError("Unrecognized token '%s': was expecting %s", _textBuffer.contentsAsString(),
+                    "'null', 'true' or 'false'");
+        }
+        return (_currToken = JsonToken.NOT_AVAILABLE);
     }
 
     /*