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