@@ -25,28 +25,35 @@ #import "OFArray.h" #import "OFDictionary.h" #import "OFNumber.h" #import "OFNull.h" -#import "OFInvalidEncodingException.h" +#import "OFInvalidJSONException.h" #import "macros.h" int _OFString_JSONValue_reference; -static id nextObject(const char *restrict *, const char*); +static id nextObject(const char *restrict *, const char*, + size_t *restrict line); static void -skipWhitespaces(const char *restrict *pointer, const char *stop) +skipWhitespaces(const char *restrict *pointer, const char *stop, + size_t *restrict line) { while (*pointer < stop && (**pointer == ' ' || **pointer == '\t' || - **pointer == '\r' || **pointer == '\n')) + **pointer == '\r' || **pointer == '\n')) { + if (**pointer == '\n') + (*line)++; + (*pointer)++; + } } static void -skipComment(const char *restrict *pointer, const char *stop) +skipComment(const char *restrict *pointer, const char *stop, + size_t *restrict line) { if (**pointer != '/') return; if (*pointer + 1 >= stop) @@ -64,37 +71,43 @@ (*pointer)++; return; } lastIsAsterisk = (**pointer == '*'); + + if (**pointer == '\n') + (*line)++; (*pointer)++; } - } else { + } else if (**pointer == '/') { (*pointer)++; while (*pointer < stop) { if (**pointer == '\r' || **pointer == '\n') { (*pointer)++; + (*line)++; return; } (*pointer)++; } - } + } else + (*pointer)--; } static void -skipWhitespacesAndComments(const char *restrict *pointer, const char *stop) +skipWhitespacesAndComments(const char *restrict *pointer, const char *stop, + size_t *restrict line) { const char *old = NULL; while (old != *pointer) { old = *pointer; - skipWhitespaces(pointer, stop); - skipComment(pointer, stop); + skipWhitespaces(pointer, stop, line); + skipComment(pointer, stop, line); } } static inline uint16_t parseUnicodeEscape(const char *pointer, const char *stop) @@ -124,11 +137,12 @@ return ret; } static inline OFString* -parseString(const char *restrict *pointer, const char *stop) +parseString(const char *restrict *pointer, const char *stop, + size_t *restrict line) { char *buffer; size_t i = 0; char delimiter = **pointer; @@ -231,16 +245,19 @@ break; case '\r': (*pointer)++; - if (*pointer < stop && **pointer == '\n') + if (*pointer < stop && **pointer == '\n') { (*pointer)++; + (*line)++; + } break; case '\n': (*pointer)++; + (*line)++; break; default: free(buffer); return nil; } @@ -258,10 +275,11 @@ (*pointer)++; return ret; /* Newlines in strings are disallowed */ } else if (**pointer == '\n' || **pointer == '\r') { + (*line)++; free(buffer); return nil; } else { buffer[i++] = **pointer; (*pointer)++; @@ -371,49 +389,50 @@ */ return nil; } static inline OFMutableArray* -parseArray(const char *restrict *pointer, const char *stop) +parseArray(const char *restrict *pointer, const char *stop, + size_t *restrict line) { OFMutableArray *array = [OFMutableArray array]; if (++(*pointer) >= stop) return nil; while (**pointer != ']') { id object; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == ']') break; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop || **pointer != ']') return nil; break; } - if ((object = nextObject(pointer, stop)) == nil) + if ((object = nextObject(pointer, stop, line)) == nil) return nil; [array addObject: object]; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; } else if (**pointer != ']') return nil; @@ -423,70 +442,71 @@ return array; } static inline OFMutableDictionary* -parseDictionary(const char *restrict *pointer, const char *stop) +parseDictionary(const char *restrict *pointer, const char *stop, + size_t *restrict line) { OFMutableDictionary *dictionary = [OFMutableDictionary dictionary]; if (++(*pointer) >= stop) return nil; while (**pointer != '}') { id key, object; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == '}') break; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop || **pointer != '}') return nil; break; } - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer + 1 >= stop) return nil; if ((**pointer >= 'a' && **pointer <= 'z') || (**pointer >= 'A' && **pointer <= 'Z') || **pointer == '_' || **pointer == '$' || **pointer == '\\') key = parseIdentifier(pointer, stop); else - key = nextObject(pointer, stop); + key = nextObject(pointer, stop, line); if (key == nil) return nil; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer + 1 >= stop || **pointer != ':') return nil; (*pointer)++; - if ((object = nextObject(pointer, stop)) == nil) + if ((object = nextObject(pointer, stop, line)) == nil) return nil; [dictionary setObject: object forKey: key]; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; if (**pointer == ',') { (*pointer)++; - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; } else if (**pointer != '}') return nil; @@ -496,11 +516,12 @@ return dictionary; } static inline OFNumber* -parseNumber(const char *restrict *pointer, const char *stop) +parseNumber(const char *restrict *pointer, const char *stop, + size_t *restrict line) { BOOL isHex = (*pointer + 1 < stop && (*pointer)[1] == 'x'); BOOL hasDecimal = NO; size_t i; OFString *string; @@ -511,12 +532,16 @@ hasDecimal = YES; if ((*pointer)[i] == ' ' || (*pointer)[i] == '\t' || (*pointer)[i] == '\r' || (*pointer)[i] == '\n' || (*pointer)[i] == ',' || (*pointer)[i] == ']' || - (*pointer)[i] == '}') + (*pointer)[i] == '}') { + if ((*pointer)[i] == '\n') + (*line)++; + break; + } } string = [[OFString alloc] initWithUTF8String: *pointer length: i]; *pointer += i; @@ -537,25 +562,26 @@ return number; } static id -nextObject(const char *restrict *pointer, const char *stop) +nextObject(const char *restrict *pointer, const char *stop, + size_t *restrict line) { - skipWhitespacesAndComments(pointer, stop); + skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; switch (**pointer) { case '"': case '\'': - return parseString(pointer, stop); + return parseString(pointer, stop, line); case '[': - return parseArray(pointer, stop); + return parseArray(pointer, stop, line); case '{': - return parseDictionary(pointer, stop); + return parseDictionary(pointer, stop, line); case 't': if (*pointer + 3 >= stop) return nil; if (memcmp(*pointer, "true", 4)) @@ -594,11 +620,11 @@ case '7': case '8': case '9': case '-': case '.': - return parseNumber(pointer, stop); + return parseNumber(pointer, stop, line); default: return nil; } } @@ -606,15 +632,17 @@ - (id)JSONValue { const char *pointer = [self UTF8String]; const char *stop = pointer + [self UTF8StringLength]; id object; + size_t line = 1; - object = nextObject(&pointer, stop); - skipWhitespacesAndComments(&pointer, stop); + object = nextObject(&pointer, stop, &line); + skipWhitespacesAndComments(&pointer, stop, &line); if (pointer < stop || object == nil) - @throw [OFInvalidEncodingException exceptionWithClass: isa]; + @throw [OFInvalidJSONException exceptionWithClass: isa + line: line]; return object; } @end