summaryrefslogtreecommitdiffstats
path: root/Classes/Source/JSON/CJSONScanner.m
diff options
context:
space:
mode:
Diffstat (limited to 'Classes/Source/JSON/CJSONScanner.m')
-rw-r--r--Classes/Source/JSON/CJSONScanner.m676
1 files changed, 676 insertions, 0 deletions
diff --git a/Classes/Source/JSON/CJSONScanner.m b/Classes/Source/JSON/CJSONScanner.m
new file mode 100644
index 0000000..c5ffeb4
--- /dev/null
+++ b/Classes/Source/JSON/CJSONScanner.m
@@ -0,0 +1,676 @@
+//
+// CJSONScanner.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// 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.
+//
+
+#import "CJSONScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+#if !defined(TREAT_COMMENTS_AS_WHITESPACE)
+#define TREAT_COMMENTS_AS_WHITESPACE 0
+#endif // !defined(TREAT_COMMENTS_AS_WHITESPACE)
+
+NSString *const kJSONScannerErrorDomain = @"kJSONScannerErrorDomain";
+
+inline static int HexToInt(char inCharacter)
+ {
+ int theValues[] = { 0x0 /* 48 '0' */, 0x1 /* 49 '1' */, 0x2 /* 50 '2' */, 0x3 /* 51 '3' */, 0x4 /* 52 '4' */, 0x5 /* 53 '5' */, 0x6 /* 54 '6' */, 0x7 /* 55 '7' */, 0x8 /* 56 '8' */, 0x9 /* 57 '9' */, -1 /* 58 ':' */, -1 /* 59 ';' */, -1 /* 60 '<' */, -1 /* 61 '=' */, -1 /* 62 '>' */, -1 /* 63 '?' */, -1 /* 64 '@' */, 0xa /* 65 'A' */, 0xb /* 66 'B' */, 0xc /* 67 'C' */, 0xd /* 68 'D' */, 0xe /* 69 'E' */, 0xf /* 70 'F' */, -1 /* 71 'G' */, -1 /* 72 'H' */, -1 /* 73 'I' */, -1 /* 74 'J' */, -1 /* 75 'K' */, -1 /* 76 'L' */, -1 /* 77 'M' */, -1 /* 78 'N' */, -1 /* 79 'O' */, -1 /* 80 'P' */, -1 /* 81 'Q' */, -1 /* 82 'R' */, -1 /* 83 'S' */, -1 /* 84 'T' */, -1 /* 85 'U' */, -1 /* 86 'V' */, -1 /* 87 'W' */, -1 /* 88 'X' */, -1 /* 89 'Y' */, -1 /* 90 'Z' */, -1 /* 91 '[' */, -1 /* 92 '\' */, -1 /* 93 ']' */, -1 /* 94 '^' */, -1 /* 95 '_' */, -1 /* 96 '`' */, 0xa /* 97 'a' */, 0xb /* 98 'b' */, 0xc /* 99 'c' */, 0xd /* 100 'd' */, 0xe /* 101 'e' */, 0xf /* 102 'f' */, };
+ if (inCharacter >= '0' && inCharacter <= 'f')
+ return(theValues[inCharacter - '0']);
+ else
+ return(-1);
+ }
+
+@interface CJSONScanner ()
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue;
+@end
+
+#pragma mark -
+
+@implementation CJSONScanner
+
+@synthesize strictEscapeCodes;
+@synthesize nullObject;
+@synthesize allowedEncoding;
+@synthesize options;
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ strictEscapeCodes = NO;
+ nullObject = [[NSNull null] retain];
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [nullObject release];
+ nullObject = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+ {
+ NSData *theData = inData;
+ if (theData && theData.length >= 4)
+ {
+ // This code is lame, but it works. Because the first character of any JSON string will always be a (ascii) control character we can work out the Unicode encoding by the bit pattern. See section 3 of http://www.ietf.org/rfc/rfc4627.txt
+ const char *theChars = theData.bytes;
+ NSStringEncoding theEncoding = NSUTF8StringEncoding;
+ if (theChars[0] != 0 && theChars[1] == 0)
+ {
+ if (theChars[2] != 0 && theChars[3] == 0)
+ theEncoding = NSUTF16LittleEndianStringEncoding;
+ else if (theChars[2] == 0 && theChars[3] == 0)
+ theEncoding = NSUTF32LittleEndianStringEncoding;
+ }
+ else if (theChars[0] == 0 && theChars[2] == 0 && theChars[3] != 0)
+ {
+ if (theChars[1] == 0)
+ theEncoding = NSUTF32BigEndianStringEncoding;
+ else if (theChars[1] != 0)
+ theEncoding = NSUTF16BigEndianStringEncoding;
+ }
+
+ NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding];
+ if (theString == NULL && self.allowedEncoding != 0)
+ {
+ theString = [[NSString alloc] initWithData:theData encoding:self.allowedEncoding];
+ }
+ theData = [theString dataUsingEncoding:NSUTF8StringEncoding];
+ [theString release];
+ }
+
+ if (theData)
+ {
+ [super setData:theData];
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan data. Data wasn't encoded properly?", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotDecodeData userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+- (void)setData:(NSData *)inData
+ {
+ [self setData:inData error:NULL];
+ }
+
+#pragma mark -
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError
+ {
+ BOOL theResult = YES;
+
+ [self skipWhitespace];
+
+ id theObject = NULL;
+
+ const unichar C = [self currentCharacter];
+ switch (C)
+ {
+ case 't':
+ if ([self scanUTF8String:"true" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:YES];
+ }
+ break;
+ case 'f':
+ if ([self scanUTF8String:"false" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:NO];
+ }
+ break;
+ case 'n':
+ if ([self scanUTF8String:"null" intoString:NULL])
+ {
+ theObject = self.nullObject;
+ }
+ break;
+ case '\"':
+ case '\'':
+ theResult = [self scanJSONStringConstant:&theObject error:outError];
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ theResult = [self scanJSONNumberConstant:&theObject error:outError];
+ break;
+ case '{':
+ theResult = [self scanJSONDictionary:&theObject error:outError];
+ break;
+ case '[':
+ theResult = [self scanJSONArray:&theObject error:outError];
+ break;
+ default:
+ theResult = NO;
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotScanObject userInfo:theUserInfo];
+ }
+ break;
+ }
+
+ if (outObject != NULL)
+ *outObject = theObject;
+
+ return(theResult);
+ }
+
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'{'] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableDictionary *theDictionary = [[NSMutableDictionary alloc] init];
+
+ while ([self currentCharacter] != '}')
+ {
+ [self skipWhitespace];
+
+ if ([self currentCharacter] == '}')
+ break;
+
+ NSString *theKey = NULL;
+ if ([self scanJSONStringConstant:&theKey error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:':'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ id theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryValueScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (theValue == NULL && self.nullObject == NULL)
+ {
+ // If the value is a null and nullObject is also null then we're skipping this key/value pair.
+ }
+ else
+ {
+ [theDictionary setValue:theValue forKey:theKey];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ if ([self currentCharacter] != '}')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+ break;
+ }
+ else
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] == '}')
+ break;
+ }
+ }
+
+ if ([self scanCharacter:'}'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (outDictionary != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outDictionary = [theDictionary autorelease];
+ }
+ else
+ {
+ *outDictionary = [[theDictionary copy] autorelease];
+ [theDictionary release];
+ }
+ }
+ else
+ {
+ [theDictionary release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'['] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not started by a '[' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableArray *theArray = [[NSMutableArray alloc] init];
+
+ [self skipWhitespace];
+ while ([self currentCharacter] != ']')
+ {
+ NSString *theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueScanFailed userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (theValue == NULL)
+ {
+ if (self.nullObject != NULL)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Value is NULL.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueIsNull userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+ }
+ else
+ {
+ [theArray addObject:theValue];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] != ']')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ break;
+ }
+ [self skipWhitespace];
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:']'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (outArray != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outArray = [theArray autorelease];
+ }
+ else
+ {
+ *outArray = [[theArray copy] autorelease];
+ [theArray release];
+ }
+ }
+ else
+ {
+ [theArray release];
+ }
+ return(YES);
+ }
+
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ NSMutableString *theString = [[NSMutableString alloc] init];
+
+ if ([self scanCharacter:'"'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotStartedWithBackslash userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+
+ while ([self scanCharacter:'"'] == NO)
+ {
+ NSString *theStringChunk = NULL;
+ if ([self scanNotQuoteCharactersIntoString:&theStringChunk])
+ {
+ CFStringAppend((CFMutableStringRef)theString, (CFStringRef)theStringChunk);
+ }
+ else if ([self scanCharacter:'\\'] == YES)
+ {
+ unichar theCharacter = [self scanCharacter];
+ switch (theCharacter)
+ {
+ case '"':
+ case '\\':
+ case '/':
+ break;
+ case 'b':
+ theCharacter = '\b';
+ break;
+ case 'f':
+ theCharacter = '\f';
+ break;
+ case 'n':
+ theCharacter = '\n';
+ break;
+ case 'r':
+ theCharacter = '\r';
+ break;
+ case 't':
+ theCharacter = '\t';
+ break;
+ case 'u':
+ {
+ theCharacter = 0;
+
+ int theShift;
+ for (theShift = 12; theShift >= 0; theShift -= 4)
+ {
+ const int theDigit = HexToInt([self scanCharacter]);
+ if (theDigit == -1)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnicodeNotDecoded userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ theCharacter |= (theDigit << theShift);
+ }
+ }
+ break;
+ default:
+ {
+ if (strictEscapeCodes == YES)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnknownEscapeCode userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+ break;
+ }
+ CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. No terminating double quote character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotTerminated userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+
+ if (outStringConstant != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableLeaves)
+ {
+ *outStringConstant = [theString autorelease];
+ }
+ else
+ {
+ *outStringConstant = [[theString copy] autorelease];
+ [theString release];
+ }
+ }
+ else
+ {
+ [theString release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError
+ {
+ NSNumber *theNumber = NULL;
+
+ [self skipWhitespace];
+
+ if ([self scanNumber:&theNumber] == YES)
+ {
+ if (outNumberConstant != NULL)
+ *outNumberConstant = theNumber;
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan number constant.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_NumberNotScannable userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+#if TREAT_COMMENTS_AS_WHITESPACE
+- (void)skipWhitespace
+ {
+ [super skipWhitespace];
+ [self scanCStyleComment:NULL];
+ [self scanCPlusPlusStyleComment:NULL];
+ [super skipWhitespace];
+ }
+#endif // TREAT_COMMENTS_AS_WHITESPACE
+
+#pragma mark -
+
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && *P != '\"' && *P != '\\'; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+@end