ian@0: // Copyright 2007-2011 Baptiste Lepilleur ian@0: // Distributed under MIT license, or public domain if desired and ian@0: // recognized in your jurisdiction. ian@0: // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE ian@0: ian@0: #if !defined(JSON_IS_AMALGAMATION) ian@0: # include ian@0: # include ian@0: # include ian@0: # include "json_tool.h" ian@0: #endif // if !defined(JSON_IS_AMALGAMATION) ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: ian@0: #if _MSC_VER >= 1400 // VC++ 8.0 ian@0: #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. ian@0: #endif ian@0: ian@0: namespace Json { ian@0: ian@0: // Implementation of class Features ian@0: // //////////////////////////////// ian@0: ian@0: Features::Features() ian@0: : allowComments_( true ) ian@0: , strictRoot_( false ) ian@0: { ian@0: } ian@0: ian@0: ian@0: Features ian@0: Features::all() ian@0: { ian@0: return Features(); ian@0: } ian@0: ian@0: ian@0: Features ian@0: Features::strictMode() ian@0: { ian@0: Features features; ian@0: features.allowComments_ = false; ian@0: features.strictRoot_ = true; ian@0: return features; ian@0: } ian@0: ian@0: // Implementation of class Reader ian@0: // //////////////////////////////// ian@0: ian@0: ian@0: static inline bool ian@0: in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) ian@0: { ian@0: return c == c1 || c == c2 || c == c3 || c == c4; ian@0: } ian@0: ian@0: static inline bool ian@0: in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) ian@0: { ian@0: return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; ian@0: } ian@0: ian@0: ian@0: static bool ian@0: containsNewLine( Reader::Location begin, ian@0: Reader::Location end ) ian@0: { ian@0: for ( ;begin < end; ++begin ) ian@0: if ( *begin == '\n' || *begin == '\r' ) ian@0: return true; ian@0: return false; ian@0: } ian@0: ian@0: ian@0: // Class Reader ian@0: // ////////////////////////////////////////////////////////////////// ian@0: ian@0: Reader::Reader() ian@0: : errors_(), ian@0: document_(), ian@0: begin_(), ian@0: end_(), ian@0: current_(), ian@0: lastValueEnd_(), ian@0: lastValue_(), ian@0: commentsBefore_(), ian@0: features_( Features::all() ), ian@0: collectComments_() ian@0: { ian@0: } ian@0: ian@0: ian@0: Reader::Reader( const Features &features ) ian@0: : errors_(), ian@0: document_(), ian@0: begin_(), ian@0: end_(), ian@0: current_(), ian@0: lastValueEnd_(), ian@0: lastValue_(), ian@0: commentsBefore_(), ian@0: features_( features ), ian@0: collectComments_() ian@0: { ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::parse( const std::string &document, ian@0: Value &root, ian@0: bool collectComments ) ian@0: { ian@0: document_ = document; ian@0: const char *begin = document_.c_str(); ian@0: const char *end = begin + document_.length(); ian@0: return parse( begin, end, root, collectComments ); ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::parse( std::istream& sin, ian@0: Value &root, ian@0: bool collectComments ) ian@0: { ian@0: //std::istream_iterator begin(sin); ian@0: //std::istream_iterator end; ian@0: // Those would allow streamed input from a file, if parse() were a ian@0: // template function. ian@0: ian@0: // Since std::string is reference-counted, this at least does not ian@0: // create an extra copy. ian@0: std::string doc; ian@0: std::getline(sin, doc, (char)EOF); ian@0: return parse( doc, root, collectComments ); ian@0: } ian@0: ian@0: bool ian@0: Reader::parse( const char *beginDoc, const char *endDoc, ian@0: Value &root, ian@0: bool collectComments ) ian@0: { ian@0: if ( !features_.allowComments_ ) ian@0: { ian@0: collectComments = false; ian@0: } ian@0: ian@0: begin_ = beginDoc; ian@0: end_ = endDoc; ian@0: collectComments_ = collectComments; ian@0: current_ = begin_; ian@0: lastValueEnd_ = 0; ian@0: lastValue_ = 0; ian@0: commentsBefore_ = ""; ian@0: errors_.clear(); ian@0: while ( !nodes_.empty() ) ian@0: nodes_.pop(); ian@0: nodes_.push( &root ); ian@0: ian@0: bool successful = readValue(); ian@0: Token token; ian@0: skipCommentTokens( token ); ian@0: if ( collectComments_ && !commentsBefore_.empty() ) ian@0: root.setComment( commentsBefore_, commentAfter ); ian@0: if ( features_.strictRoot_ ) ian@0: { ian@0: if ( !root.isArray() && !root.isObject() ) ian@0: { ian@0: // Set error location to start of doc, ideally should be first token found in doc ian@0: token.type_ = tokenError; ian@0: token.start_ = beginDoc; ian@0: token.end_ = endDoc; ian@0: addError( "A valid JSON document must be either an array or an object value.", ian@0: token ); ian@0: return false; ian@0: } ian@0: } ian@0: return successful; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readValue() ian@0: { ian@0: Token token; ian@0: skipCommentTokens( token ); ian@0: bool successful = true; ian@0: ian@0: if ( collectComments_ && !commentsBefore_.empty() ) ian@0: { ian@0: currentValue().setComment( commentsBefore_, commentBefore ); ian@0: commentsBefore_ = ""; ian@0: } ian@0: ian@0: ian@0: switch ( token.type_ ) ian@0: { ian@0: case tokenObjectBegin: ian@0: successful = readObject( token ); ian@0: break; ian@0: case tokenArrayBegin: ian@0: successful = readArray( token ); ian@0: break; ian@0: case tokenNumber: ian@0: successful = decodeNumber( token ); ian@0: break; ian@0: case tokenString: ian@0: successful = decodeString( token ); ian@0: break; ian@0: case tokenTrue: ian@0: currentValue() = true; ian@0: break; ian@0: case tokenFalse: ian@0: currentValue() = false; ian@0: break; ian@0: case tokenNull: ian@0: currentValue() = Value(); ian@0: break; ian@0: default: ian@0: return addError( "Syntax error: value, object or array expected.", token ); ian@0: } ian@0: ian@0: if ( collectComments_ ) ian@0: { ian@0: lastValueEnd_ = current_; ian@0: lastValue_ = ¤tValue(); ian@0: } ian@0: ian@0: return successful; ian@0: } ian@0: ian@0: ian@0: void ian@0: Reader::skipCommentTokens( Token &token ) ian@0: { ian@0: if ( features_.allowComments_ ) ian@0: { ian@0: do ian@0: { ian@0: readToken( token ); ian@0: } ian@0: while ( token.type_ == tokenComment ); ian@0: } ian@0: else ian@0: { ian@0: readToken( token ); ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::expectToken( TokenType type, Token &token, const char *message ) ian@0: { ian@0: readToken( token ); ian@0: if ( token.type_ != type ) ian@0: return addError( message, token ); ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readToken( Token &token ) ian@0: { ian@0: skipSpaces(); ian@0: token.start_ = current_; ian@0: Char c = getNextChar(); ian@0: bool ok = true; ian@0: switch ( c ) ian@0: { ian@0: case '{': ian@0: token.type_ = tokenObjectBegin; ian@0: break; ian@0: case '}': ian@0: token.type_ = tokenObjectEnd; ian@0: break; ian@0: case '[': ian@0: token.type_ = tokenArrayBegin; ian@0: break; ian@0: case ']': ian@0: token.type_ = tokenArrayEnd; ian@0: break; ian@0: case '"': ian@0: token.type_ = tokenString; ian@0: ok = readString(); ian@0: break; ian@0: case '/': ian@0: token.type_ = tokenComment; ian@0: ok = readComment(); ian@0: break; ian@0: case '0': ian@0: case '1': ian@0: case '2': ian@0: case '3': ian@0: case '4': ian@0: case '5': ian@0: case '6': ian@0: case '7': ian@0: case '8': ian@0: case '9': ian@0: case '-': ian@0: token.type_ = tokenNumber; ian@0: readNumber(); ian@0: break; ian@0: case 't': ian@0: token.type_ = tokenTrue; ian@0: ok = match( "rue", 3 ); ian@0: break; ian@0: case 'f': ian@0: token.type_ = tokenFalse; ian@0: ok = match( "alse", 4 ); ian@0: break; ian@0: case 'n': ian@0: token.type_ = tokenNull; ian@0: ok = match( "ull", 3 ); ian@0: break; ian@0: case ',': ian@0: token.type_ = tokenArraySeparator; ian@0: break; ian@0: case ':': ian@0: token.type_ = tokenMemberSeparator; ian@0: break; ian@0: case 0: ian@0: token.type_ = tokenEndOfStream; ian@0: break; ian@0: default: ian@0: ok = false; ian@0: break; ian@0: } ian@0: if ( !ok ) ian@0: token.type_ = tokenError; ian@0: token.end_ = current_; ian@0: return true; ian@0: } ian@0: ian@0: ian@0: void ian@0: Reader::skipSpaces() ian@0: { ian@0: while ( current_ != end_ ) ian@0: { ian@0: Char c = *current_; ian@0: if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) ian@0: ++current_; ian@0: else ian@0: break; ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::match( Location pattern, ian@0: int patternLength ) ian@0: { ian@0: if ( end_ - current_ < patternLength ) ian@0: return false; ian@0: int index = patternLength; ian@0: while ( index-- ) ian@0: if ( current_[index] != pattern[index] ) ian@0: return false; ian@0: current_ += patternLength; ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readComment() ian@0: { ian@0: Location commentBegin = current_ - 1; ian@0: Char c = getNextChar(); ian@0: bool successful = false; ian@0: if ( c == '*' ) ian@0: successful = readCStyleComment(); ian@0: else if ( c == '/' ) ian@0: successful = readCppStyleComment(); ian@0: if ( !successful ) ian@0: return false; ian@0: ian@0: if ( collectComments_ ) ian@0: { ian@0: CommentPlacement placement = commentBefore; ian@0: if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) ian@0: { ian@0: if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) ian@0: placement = commentAfterOnSameLine; ian@0: } ian@0: ian@0: addComment( commentBegin, current_, placement ); ian@0: } ian@0: return true; ian@0: } ian@0: ian@0: ian@0: void ian@0: Reader::addComment( Location begin, ian@0: Location end, ian@0: CommentPlacement placement ) ian@0: { ian@0: assert( collectComments_ ); ian@0: if ( placement == commentAfterOnSameLine ) ian@0: { ian@0: assert( lastValue_ != 0 ); ian@0: lastValue_->setComment( std::string( begin, end ), placement ); ian@0: } ian@0: else ian@0: { ian@0: if ( !commentsBefore_.empty() ) ian@0: commentsBefore_ += "\n"; ian@0: commentsBefore_ += std::string( begin, end ); ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readCStyleComment() ian@0: { ian@0: while ( current_ != end_ ) ian@0: { ian@0: Char c = getNextChar(); ian@0: if ( c == '*' && *current_ == '/' ) ian@0: break; ian@0: } ian@0: return getNextChar() == '/'; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readCppStyleComment() ian@0: { ian@0: while ( current_ != end_ ) ian@0: { ian@0: Char c = getNextChar(); ian@0: if ( c == '\r' || c == '\n' ) ian@0: break; ian@0: } ian@0: return true; ian@0: } ian@0: ian@0: ian@0: void ian@0: Reader::readNumber() ian@0: { ian@0: while ( current_ != end_ ) ian@0: { ian@0: if ( !(*current_ >= '0' && *current_ <= '9') && ian@0: !in( *current_, '.', 'e', 'E', '+', '-' ) ) ian@0: break; ian@0: ++current_; ian@0: } ian@0: } ian@0: ian@0: bool ian@0: Reader::readString() ian@0: { ian@0: Char c = 0; ian@0: while ( current_ != end_ ) ian@0: { ian@0: c = getNextChar(); ian@0: if ( c == '\\' ) ian@0: getNextChar(); ian@0: else if ( c == '"' ) ian@0: break; ian@0: } ian@0: return c == '"'; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readObject( Token &/*tokenStart*/ ) ian@0: { ian@0: Token tokenName; ian@0: std::string name; ian@0: currentValue() = Value( objectValue ); ian@0: while ( readToken( tokenName ) ) ian@0: { ian@0: bool initialTokenOk = true; ian@0: while ( tokenName.type_ == tokenComment && initialTokenOk ) ian@0: initialTokenOk = readToken( tokenName ); ian@0: if ( !initialTokenOk ) ian@0: break; ian@0: if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object ian@0: return true; ian@0: if ( tokenName.type_ != tokenString ) ian@0: break; ian@0: ian@0: name = ""; ian@0: if ( !decodeString( tokenName, name ) ) ian@0: return recoverFromError( tokenObjectEnd ); ian@0: ian@0: Token colon; ian@0: if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) ian@0: { ian@0: return addErrorAndRecover( "Missing ':' after object member name", ian@0: colon, ian@0: tokenObjectEnd ); ian@0: } ian@0: Value &value = currentValue()[ name ]; ian@0: nodes_.push( &value ); ian@0: bool ok = readValue(); ian@0: nodes_.pop(); ian@0: if ( !ok ) // error already set ian@0: return recoverFromError( tokenObjectEnd ); ian@0: ian@0: Token comma; ian@0: if ( !readToken( comma ) ian@0: || ( comma.type_ != tokenObjectEnd && ian@0: comma.type_ != tokenArraySeparator && ian@0: comma.type_ != tokenComment ) ) ian@0: { ian@0: return addErrorAndRecover( "Missing ',' or '}' in object declaration", ian@0: comma, ian@0: tokenObjectEnd ); ian@0: } ian@0: bool finalizeTokenOk = true; ian@0: while ( comma.type_ == tokenComment && ian@0: finalizeTokenOk ) ian@0: finalizeTokenOk = readToken( comma ); ian@0: if ( comma.type_ == tokenObjectEnd ) ian@0: return true; ian@0: } ian@0: return addErrorAndRecover( "Missing '}' or object member name", ian@0: tokenName, ian@0: tokenObjectEnd ); ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::readArray( Token &/*tokenStart*/ ) ian@0: { ian@0: currentValue() = Value( arrayValue ); ian@0: skipSpaces(); ian@0: if ( *current_ == ']' ) // empty array ian@0: { ian@0: Token endArray; ian@0: readToken( endArray ); ian@0: return true; ian@0: } ian@0: int index = 0; ian@0: for (;;) ian@0: { ian@0: Value &value = currentValue()[ index++ ]; ian@0: nodes_.push( &value ); ian@0: bool ok = readValue(); ian@0: nodes_.pop(); ian@0: if ( !ok ) // error already set ian@0: return recoverFromError( tokenArrayEnd ); ian@0: ian@0: Token token; ian@0: // Accept Comment after last item in the array. ian@0: ok = readToken( token ); ian@0: while ( token.type_ == tokenComment && ok ) ian@0: { ian@0: ok = readToken( token ); ian@0: } ian@0: bool badTokenType = ( token.type_ != tokenArraySeparator && ian@0: token.type_ != tokenArrayEnd ); ian@0: if ( !ok || badTokenType ) ian@0: { ian@0: return addErrorAndRecover( "Missing ',' or ']' in array declaration", ian@0: token, ian@0: tokenArrayEnd ); ian@0: } ian@0: if ( token.type_ == tokenArrayEnd ) ian@0: break; ian@0: } ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::decodeNumber( Token &token ) ian@0: { ian@0: bool isDouble = false; ian@0: for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) ian@0: { ian@0: isDouble = isDouble ian@0: || in( *inspect, '.', 'e', 'E', '+' ) ian@0: || ( *inspect == '-' && inspect != token.start_ ); ian@0: } ian@0: if ( isDouble ) ian@0: return decodeDouble( token ); ian@0: // Attempts to parse the number as an integer. If the number is ian@0: // larger than the maximum supported value of an integer then ian@0: // we decode the number as a double. ian@0: Location current = token.start_; ian@0: bool isNegative = *current == '-'; ian@0: if ( isNegative ) ian@0: ++current; ian@0: Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) ian@0: : Value::maxLargestUInt; ian@0: Value::LargestUInt threshold = maxIntegerValue / 10; ian@0: Value::LargestUInt value = 0; ian@0: while ( current < token.end_ ) ian@0: { ian@0: Char c = *current++; ian@0: if ( c < '0' || c > '9' ) ian@0: return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); ian@0: Value::UInt digit(c - '0'); ian@0: if ( value >= threshold ) ian@0: { ian@0: // We've hit or exceeded the max value divided by 10 (rounded down). If ian@0: // a) we've only just touched the limit, b) this is the last digit, and ian@0: // c) it's small enough to fit in that rounding delta, we're okay. ian@0: // Otherwise treat this number as a double to avoid overflow. ian@0: if (value > threshold || ian@0: current != token.end_ || ian@0: digit > maxIntegerValue % 10) ian@0: { ian@0: return decodeDouble( token ); ian@0: } ian@0: } ian@0: value = value * 10 + digit; ian@0: } ian@0: if ( isNegative ) ian@0: currentValue() = -Value::LargestInt( value ); ian@0: else if ( value <= Value::LargestUInt(Value::maxInt) ) ian@0: currentValue() = Value::LargestInt( value ); ian@0: else ian@0: currentValue() = value; ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::decodeDouble( Token &token ) ian@0: { ian@0: double value = 0; ian@0: const int bufferSize = 32; ian@0: int count; ian@0: int length = int(token.end_ - token.start_); ian@0: ian@0: // Sanity check to avoid buffer overflow exploits. ian@0: if (length < 0) { ian@0: return addError( "Unable to parse token length", token ); ian@0: } ian@0: ian@0: // Avoid using a string constant for the format control string given to ian@0: // sscanf, as this can cause hard to debug crashes on OS X. See here for more ian@0: // info: ian@0: // ian@0: // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html ian@0: char format[] = "%lf"; ian@0: ian@0: if ( length <= bufferSize ) ian@0: { ian@0: Char buffer[bufferSize+1]; ian@0: memcpy( buffer, token.start_, length ); ian@0: buffer[length] = 0; ian@0: count = sscanf( buffer, format, &value ); ian@0: } ian@0: else ian@0: { ian@0: std::string buffer( token.start_, token.end_ ); ian@0: count = sscanf( buffer.c_str(), format, &value ); ian@0: } ian@0: ian@0: if ( count != 1 ) ian@0: return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); ian@0: currentValue() = value; ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::decodeString( Token &token ) ian@0: { ian@0: std::string decoded; ian@0: if ( !decodeString( token, decoded ) ) ian@0: return false; ian@0: currentValue() = decoded; ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::decodeString( Token &token, std::string &decoded ) ian@0: { ian@0: decoded.reserve( token.end_ - token.start_ - 2 ); ian@0: Location current = token.start_ + 1; // skip '"' ian@0: Location end = token.end_ - 1; // do not include '"' ian@0: while ( current != end ) ian@0: { ian@0: Char c = *current++; ian@0: if ( c == '"' ) ian@0: break; ian@0: else if ( c == '\\' ) ian@0: { ian@0: if ( current == end ) ian@0: return addError( "Empty escape sequence in string", token, current ); ian@0: Char escape = *current++; ian@0: switch ( escape ) ian@0: { ian@0: case '"': decoded += '"'; break; ian@0: case '/': decoded += '/'; break; ian@0: case '\\': decoded += '\\'; break; ian@0: case 'b': decoded += '\b'; break; ian@0: case 'f': decoded += '\f'; break; ian@0: case 'n': decoded += '\n'; break; ian@0: case 'r': decoded += '\r'; break; ian@0: case 't': decoded += '\t'; break; ian@0: case 'u': ian@0: { ian@0: unsigned int unicode; ian@0: if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) ian@0: return false; ian@0: decoded += codePointToUTF8(unicode); ian@0: } ian@0: break; ian@0: default: ian@0: return addError( "Bad escape sequence in string", token, current ); ian@0: } ian@0: } ian@0: else ian@0: { ian@0: decoded += c; ian@0: } ian@0: } ian@0: return true; ian@0: } ian@0: ian@0: bool ian@0: Reader::decodeUnicodeCodePoint( Token &token, ian@0: Location ¤t, ian@0: Location end, ian@0: unsigned int &unicode ) ian@0: { ian@0: ian@0: if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) ian@0: return false; ian@0: if (unicode >= 0xD800 && unicode <= 0xDBFF) ian@0: { ian@0: // surrogate pairs ian@0: if (end - current < 6) ian@0: return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); ian@0: unsigned int surrogatePair; ian@0: if (*(current++) == '\\' && *(current++)== 'u') ian@0: { ian@0: if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) ian@0: { ian@0: unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); ian@0: } ian@0: else ian@0: return false; ian@0: } ian@0: else ian@0: return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); ian@0: } ian@0: return true; ian@0: } ian@0: ian@0: bool ian@0: Reader::decodeUnicodeEscapeSequence( Token &token, ian@0: Location ¤t, ian@0: Location end, ian@0: unsigned int &unicode ) ian@0: { ian@0: if ( end - current < 4 ) ian@0: return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); ian@0: unicode = 0; ian@0: for ( int index =0; index < 4; ++index ) ian@0: { ian@0: Char c = *current++; ian@0: unicode *= 16; ian@0: if ( c >= '0' && c <= '9' ) ian@0: unicode += c - '0'; ian@0: else if ( c >= 'a' && c <= 'f' ) ian@0: unicode += c - 'a' + 10; ian@0: else if ( c >= 'A' && c <= 'F' ) ian@0: unicode += c - 'A' + 10; ian@0: else ian@0: return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); ian@0: } ian@0: return true; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::addError( const std::string &message, ian@0: Token &token, ian@0: Location extra ) ian@0: { ian@0: ErrorInfo info; ian@0: info.token_ = token; ian@0: info.message_ = message; ian@0: info.extra_ = extra; ian@0: errors_.push_back( info ); ian@0: return false; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::recoverFromError( TokenType skipUntilToken ) ian@0: { ian@0: int errorCount = int(errors_.size()); ian@0: Token skip; ian@0: for (;;) ian@0: { ian@0: if ( !readToken(skip) ) ian@0: errors_.resize( errorCount ); // discard errors caused by recovery ian@0: if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) ian@0: break; ian@0: } ian@0: errors_.resize( errorCount ); ian@0: return false; ian@0: } ian@0: ian@0: ian@0: bool ian@0: Reader::addErrorAndRecover( const std::string &message, ian@0: Token &token, ian@0: TokenType skipUntilToken ) ian@0: { ian@0: addError( message, token ); ian@0: return recoverFromError( skipUntilToken ); ian@0: } ian@0: ian@0: ian@0: Value & ian@0: Reader::currentValue() ian@0: { ian@0: return *(nodes_.top()); ian@0: } ian@0: ian@0: ian@0: Reader::Char ian@0: Reader::getNextChar() ian@0: { ian@0: if ( current_ == end_ ) ian@0: return 0; ian@0: return *current_++; ian@0: } ian@0: ian@0: ian@0: void ian@0: Reader::getLocationLineAndColumn( Location location, ian@0: int &line, ian@0: int &column ) const ian@0: { ian@0: Location current = begin_; ian@0: Location lastLineStart = current; ian@0: line = 0; ian@0: while ( current < location && current != end_ ) ian@0: { ian@0: Char c = *current++; ian@0: if ( c == '\r' ) ian@0: { ian@0: if ( *current == '\n' ) ian@0: ++current; ian@0: lastLineStart = current; ian@0: ++line; ian@0: } ian@0: else if ( c == '\n' ) ian@0: { ian@0: lastLineStart = current; ian@0: ++line; ian@0: } ian@0: } ian@0: // column & line start at 1 ian@0: column = int(location - lastLineStart) + 1; ian@0: ++line; ian@0: } ian@0: ian@0: ian@0: std::string ian@0: Reader::getLocationLineAndColumn( Location location ) const ian@0: { ian@0: int line, column; ian@0: getLocationLineAndColumn( location, line, column ); ian@0: char buffer[18+16+16+1]; ian@0: sprintf( buffer, "Line %d, Column %d", line, column ); ian@0: return buffer; ian@0: } ian@0: ian@0: ian@0: // Deprecated. Preserved for backward compatibility ian@0: std::string ian@0: Reader::getFormatedErrorMessages() const ian@0: { ian@0: return getFormattedErrorMessages(); ian@0: } ian@0: ian@0: ian@0: std::string ian@0: Reader::getFormattedErrorMessages() const ian@0: { ian@0: std::string formattedMessage; ian@0: for ( Errors::const_iterator itError = errors_.begin(); ian@0: itError != errors_.end(); ian@0: ++itError ) ian@0: { ian@0: const ErrorInfo &error = *itError; ian@0: formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; ian@0: formattedMessage += " " + error.message_ + "\n"; ian@0: if ( error.extra_ ) ian@0: formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; ian@0: } ian@0: return formattedMessage; ian@0: } ian@0: ian@0: ian@0: std::istream& operator>>( std::istream &sin, Value &root ) ian@0: { ian@0: Json::Reader reader; ian@0: bool ok = reader.parse(sin, root, true); ian@0: if (!ok) JSON_FAIL_MESSAGE(reader.getFormattedErrorMessages()); ian@0: return sin; ian@0: } ian@0: ian@0: ian@0: } // namespace Json