• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2007-2011 Baptiste Lepilleur
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5 
6 #if !defined(JSON_IS_AMALGAMATION)
7 # include <json/assertions.h>
8 # include <json/reader.h>
9 # include <json/value.h>
10 # include "json_tool.h"
11 #endif // if !defined(JSON_IS_AMALGAMATION)
12 #include <utility>
13 #include <cstdio>
14 #include <cassert>
15 #include <cstring>
16 #include <stdexcept>
17 
18 #if _MSC_VER >= 1400 // VC++ 8.0
19 #pragma warning( disable : 4996 )   // disable warning about strdup being deprecated.
20 #endif
21 
22 namespace Json {
23 
24 // Implementation of class Features
25 // ////////////////////////////////
26 
Features()27 Features::Features()
28    : allowComments_( true )
29    , strictRoot_( false )
30 {
31 }
32 
33 
34 Features
all()35 Features::all()
36 {
37    return Features();
38 }
39 
40 
41 Features
strictMode()42 Features::strictMode()
43 {
44    Features features;
45    features.allowComments_ = false;
46    features.strictRoot_ = true;
47    return features;
48 }
49 
50 // Implementation of class Reader
51 // ////////////////////////////////
52 
53 
54 static inline bool
in(Reader::Char c,Reader::Char c1,Reader::Char c2,Reader::Char c3,Reader::Char c4)55 in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
56 {
57    return c == c1  ||  c == c2  ||  c == c3  ||  c == c4;
58 }
59 
60 static inline bool
in(Reader::Char c,Reader::Char c1,Reader::Char c2,Reader::Char c3,Reader::Char c4,Reader::Char c5)61 in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
62 {
63    return c == c1  ||  c == c2  ||  c == c3  ||  c == c4  ||  c == c5;
64 }
65 
66 
67 static bool
containsNewLine(Reader::Location begin,Reader::Location end)68 containsNewLine( Reader::Location begin,
69                  Reader::Location end )
70 {
71    for ( ;begin < end; ++begin )
72       if ( *begin == '\n'  ||  *begin == '\r' )
73          return true;
74    return false;
75 }
76 
77 
78 // Class Reader
79 // //////////////////////////////////////////////////////////////////
80 
Reader()81 Reader::Reader()
82     : errors_(),
83       document_(),
84       begin_(),
85       end_(),
86       current_(),
87       lastValueEnd_(),
88       lastValue_(),
89       commentsBefore_(),
90       features_( Features::all() ),
91       collectComments_()
92 {
93 }
94 
95 
Reader(const Features & features)96 Reader::Reader( const Features &features )
97     : errors_(),
98       document_(),
99       begin_(),
100       end_(),
101       current_(),
102       lastValueEnd_(),
103       lastValue_(),
104       commentsBefore_(),
105       features_( features ),
106       collectComments_()
107 {
108 }
109 
110 
111 bool
parse(const std::string & document,Value & root,bool collectComments)112 Reader::parse( const std::string &document,
113                Value &root,
114                bool collectComments )
115 {
116    document_ = document;
117    const char *begin = document_.c_str();
118    const char *end = begin + document_.length();
119    return parse( begin, end, root, collectComments );
120 }
121 
122 
123 bool
parse(std::istream & sin,Value & root,bool collectComments)124 Reader::parse( std::istream& sin,
125                Value &root,
126                bool collectComments )
127 {
128    //std::istream_iterator<char> begin(sin);
129    //std::istream_iterator<char> end;
130    // Those would allow streamed input from a file, if parse() were a
131    // template function.
132 
133    // Since std::string is reference-counted, this at least does not
134    // create an extra copy.
135    std::string doc;
136    std::getline(sin, doc, (char)EOF);
137    return parse( doc, root, collectComments );
138 }
139 
140 bool
parse(const char * beginDoc,const char * endDoc,Value & root,bool collectComments)141 Reader::parse( const char *beginDoc, const char *endDoc,
142                Value &root,
143                bool collectComments )
144 {
145    if ( !features_.allowComments_ )
146    {
147       collectComments = false;
148    }
149 
150    begin_ = beginDoc;
151    end_ = endDoc;
152    collectComments_ = collectComments;
153    current_ = begin_;
154    lastValueEnd_ = 0;
155    lastValue_ = 0;
156    commentsBefore_ = "";
157    errors_.clear();
158    while ( !nodes_.empty() )
159       nodes_.pop();
160    nodes_.push( &root );
161 
162    bool successful = readValue();
163    Token token;
164    skipCommentTokens( token );
165    if ( collectComments_  &&  !commentsBefore_.empty() )
166       root.setComment( commentsBefore_, commentAfter );
167    if ( features_.strictRoot_ )
168    {
169       if ( !root.isArray()  &&  !root.isObject() )
170       {
171          // Set error location to start of doc, ideally should be first token found in doc
172          token.type_ = tokenError;
173          token.start_ = beginDoc;
174          token.end_ = endDoc;
175          addError( "A valid JSON document must be either an array or an object value.",
176                    token );
177          return false;
178       }
179    }
180    return successful;
181 }
182 
183 
184 bool
readValue()185 Reader::readValue()
186 {
187    Token token;
188    skipCommentTokens( token );
189    bool successful = true;
190 
191    if ( collectComments_  &&  !commentsBefore_.empty() )
192    {
193       currentValue().setComment( commentsBefore_, commentBefore );
194       commentsBefore_ = "";
195    }
196 
197 
198    switch ( token.type_ )
199    {
200    case tokenObjectBegin:
201       successful = readObject( token );
202       break;
203    case tokenArrayBegin:
204       successful = readArray( token );
205       break;
206    case tokenNumber:
207       successful = decodeNumber( token );
208       break;
209    case tokenString:
210       successful = decodeString( token );
211       break;
212    case tokenTrue:
213       currentValue() = true;
214       break;
215    case tokenFalse:
216       currentValue() = false;
217       break;
218    case tokenNull:
219       currentValue() = Value();
220       break;
221    default:
222       return addError( "Syntax error: value, object or array expected.", token );
223    }
224 
225    if ( collectComments_ )
226    {
227       lastValueEnd_ = current_;
228       lastValue_ = &currentValue();
229    }
230 
231    return successful;
232 }
233 
234 
235 void
skipCommentTokens(Token & token)236 Reader::skipCommentTokens( Token &token )
237 {
238    if ( features_.allowComments_ )
239    {
240       do
241       {
242          readToken( token );
243       }
244       while ( token.type_ == tokenComment );
245    }
246    else
247    {
248       readToken( token );
249    }
250 }
251 
252 
253 bool
expectToken(TokenType type,Token & token,const char * message)254 Reader::expectToken( TokenType type, Token &token, const char *message )
255 {
256    readToken( token );
257    if ( token.type_ != type )
258       return addError( message, token );
259    return true;
260 }
261 
262 
263 bool
readToken(Token & token)264 Reader::readToken( Token &token )
265 {
266    skipSpaces();
267    token.start_ = current_;
268    Char c = getNextChar();
269    bool ok = true;
270    switch ( c )
271    {
272    case '{':
273       token.type_ = tokenObjectBegin;
274       break;
275    case '}':
276       token.type_ = tokenObjectEnd;
277       break;
278    case '[':
279       token.type_ = tokenArrayBegin;
280       break;
281    case ']':
282       token.type_ = tokenArrayEnd;
283       break;
284    case '"':
285       token.type_ = tokenString;
286       ok = readString();
287       break;
288    case '/':
289       token.type_ = tokenComment;
290       ok = readComment();
291       break;
292    case '0':
293    case '1':
294    case '2':
295    case '3':
296    case '4':
297    case '5':
298    case '6':
299    case '7':
300    case '8':
301    case '9':
302    case '-':
303       token.type_ = tokenNumber;
304       readNumber();
305       break;
306    case 't':
307       token.type_ = tokenTrue;
308       ok = match( "rue", 3 );
309       break;
310    case 'f':
311       token.type_ = tokenFalse;
312       ok = match( "alse", 4 );
313       break;
314    case 'n':
315       token.type_ = tokenNull;
316       ok = match( "ull", 3 );
317       break;
318    case ',':
319       token.type_ = tokenArraySeparator;
320       break;
321    case ':':
322       token.type_ = tokenMemberSeparator;
323       break;
324    case 0:
325       token.type_ = tokenEndOfStream;
326       break;
327    default:
328       ok = false;
329       break;
330    }
331    if ( !ok )
332       token.type_ = tokenError;
333    token.end_ = current_;
334    return true;
335 }
336 
337 
338 void
skipSpaces()339 Reader::skipSpaces()
340 {
341    while ( current_ != end_ )
342    {
343       Char c = *current_;
344       if ( c == ' '  ||  c == '\t'  ||  c == '\r'  ||  c == '\n' )
345          ++current_;
346       else
347          break;
348    }
349 }
350 
351 
352 bool
match(Location pattern,int patternLength)353 Reader::match( Location pattern,
354                int patternLength )
355 {
356    if ( end_ - current_ < patternLength )
357       return false;
358    int index = patternLength;
359    while ( index-- )
360       if ( current_[index] != pattern[index] )
361          return false;
362    current_ += patternLength;
363    return true;
364 }
365 
366 
367 bool
readComment()368 Reader::readComment()
369 {
370    Location commentBegin = current_ - 1;
371    Char c = getNextChar();
372    bool successful = false;
373    if ( c == '*' )
374       successful = readCStyleComment();
375    else if ( c == '/' )
376       successful = readCppStyleComment();
377    if ( !successful )
378       return false;
379 
380    if ( collectComments_ )
381    {
382       CommentPlacement placement = commentBefore;
383       if ( lastValueEnd_  &&  !containsNewLine( lastValueEnd_, commentBegin ) )
384       {
385          if ( c != '*'  ||  !containsNewLine( commentBegin, current_ ) )
386             placement = commentAfterOnSameLine;
387       }
388 
389       addComment( commentBegin, current_, placement );
390    }
391    return true;
392 }
393 
394 
395 void
addComment(Location begin,Location end,CommentPlacement placement)396 Reader::addComment( Location begin,
397                     Location end,
398                     CommentPlacement placement )
399 {
400    assert( collectComments_ );
401    if ( placement == commentAfterOnSameLine )
402    {
403       assert( lastValue_ != 0 );
404       lastValue_->setComment( std::string( begin, end ), placement );
405    }
406    else
407    {
408       if ( !commentsBefore_.empty() )
409          commentsBefore_ += "\n";
410       commentsBefore_ += std::string( begin, end );
411    }
412 }
413 
414 
415 bool
readCStyleComment()416 Reader::readCStyleComment()
417 {
418    while ( current_ != end_ )
419    {
420       Char c = getNextChar();
421       if ( c == '*'  &&  *current_ == '/' )
422          break;
423    }
424    return getNextChar() == '/';
425 }
426 
427 
428 bool
readCppStyleComment()429 Reader::readCppStyleComment()
430 {
431    while ( current_ != end_ )
432    {
433       Char c = getNextChar();
434       if (  c == '\r'  ||  c == '\n' )
435          break;
436    }
437    return true;
438 }
439 
440 
441 void
readNumber()442 Reader::readNumber()
443 {
444    while ( current_ != end_ )
445    {
446       if ( !(*current_ >= '0'  &&  *current_ <= '9')  &&
447            !in( *current_, '.', 'e', 'E', '+', '-' ) )
448          break;
449       ++current_;
450    }
451 }
452 
453 bool
readString()454 Reader::readString()
455 {
456    Char c = 0;
457    while ( current_ != end_ )
458    {
459       c = getNextChar();
460       if ( c == '\\' )
461          getNextChar();
462       else if ( c == '"' )
463          break;
464    }
465    return c == '"';
466 }
467 
468 
469 bool
readObject(Token &)470 Reader::readObject( Token &/*tokenStart*/ )
471 {
472    Token tokenName;
473    std::string name;
474    currentValue() = Value( objectValue );
475    while ( readToken( tokenName ) )
476    {
477       bool initialTokenOk = true;
478       while ( tokenName.type_ == tokenComment  &&  initialTokenOk )
479          initialTokenOk = readToken( tokenName );
480       if  ( !initialTokenOk )
481          break;
482       if ( tokenName.type_ == tokenObjectEnd  &&  name.empty() )  // empty object
483          return true;
484       if ( tokenName.type_ != tokenString )
485          break;
486 
487       name = "";
488       if ( !decodeString( tokenName, name ) )
489          return recoverFromError( tokenObjectEnd );
490 
491       Token colon;
492       if ( !readToken( colon ) ||  colon.type_ != tokenMemberSeparator )
493       {
494          return addErrorAndRecover( "Missing ':' after object member name",
495                                     colon,
496                                     tokenObjectEnd );
497       }
498       Value &value = currentValue()[ name ];
499       nodes_.push( &value );
500       bool ok = readValue();
501       nodes_.pop();
502       if ( !ok ) // error already set
503          return recoverFromError( tokenObjectEnd );
504 
505       Token comma;
506       if ( !readToken( comma )
507             ||  ( comma.type_ != tokenObjectEnd  &&
508                   comma.type_ != tokenArraySeparator &&
509                   comma.type_ != tokenComment ) )
510       {
511          return addErrorAndRecover( "Missing ',' or '}' in object declaration",
512                                     comma,
513                                     tokenObjectEnd );
514       }
515       bool finalizeTokenOk = true;
516       while ( comma.type_ == tokenComment &&
517               finalizeTokenOk )
518          finalizeTokenOk = readToken( comma );
519       if ( comma.type_ == tokenObjectEnd )
520          return true;
521    }
522    return addErrorAndRecover( "Missing '}' or object member name",
523                               tokenName,
524                               tokenObjectEnd );
525 }
526 
527 
528 bool
readArray(Token &)529 Reader::readArray( Token &/*tokenStart*/ )
530 {
531    currentValue() = Value( arrayValue );
532    skipSpaces();
533    if ( *current_ == ']' ) // empty array
534    {
535       Token endArray;
536       readToken( endArray );
537       return true;
538    }
539    int index = 0;
540    for (;;)
541    {
542       Value &value = currentValue()[ index++ ];
543       nodes_.push( &value );
544       bool ok = readValue();
545       nodes_.pop();
546       if ( !ok ) // error already set
547          return recoverFromError( tokenArrayEnd );
548 
549       Token token;
550       // Accept Comment after last item in the array.
551       ok = readToken( token );
552       while ( token.type_ == tokenComment  &&  ok )
553       {
554          ok = readToken( token );
555       }
556       bool badTokenType = ( token.type_ != tokenArraySeparator  &&
557                             token.type_ != tokenArrayEnd );
558       if ( !ok  ||  badTokenType )
559       {
560          return addErrorAndRecover( "Missing ',' or ']' in array declaration",
561                                     token,
562                                     tokenArrayEnd );
563       }
564       if ( token.type_ == tokenArrayEnd )
565          break;
566    }
567    return true;
568 }
569 
570 
571 bool
decodeNumber(Token & token)572 Reader::decodeNumber( Token &token )
573 {
574    bool isDouble = false;
575    for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
576    {
577       isDouble = isDouble
578                  ||  in( *inspect, '.', 'e', 'E', '+' )
579                  ||  ( *inspect == '-'  &&  inspect != token.start_ );
580    }
581    if ( isDouble )
582       return decodeDouble( token );
583    // Attempts to parse the number as an integer. If the number is
584    // larger than the maximum supported value of an integer then
585    // we decode the number as a double.
586    Location current = token.start_;
587    bool isNegative = *current == '-';
588    if ( isNegative )
589       ++current;
590    Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt)
591                                                    : Value::maxLargestUInt;
592    Value::LargestUInt threshold = maxIntegerValue / 10;
593    Value::LargestUInt value = 0;
594    while ( current < token.end_ )
595    {
596       Char c = *current++;
597       if ( c < '0'  ||  c > '9' )
598          return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
599       Value::UInt digit(c - '0');
600       if ( value >= threshold )
601       {
602          // We've hit or exceeded the max value divided by 10 (rounded down). If
603          // a) we've only just touched the limit, b) this is the last digit, and
604          // c) it's small enough to fit in that rounding delta, we're okay.
605          // Otherwise treat this number as a double to avoid overflow.
606          if (value > threshold ||
607              current != token.end_ ||
608              digit > maxIntegerValue % 10)
609          {
610             return decodeDouble( token );
611          }
612       }
613       value = value * 10 + digit;
614    }
615    if ( isNegative )
616       currentValue() = -Value::LargestInt( value );
617    else if ( value <= Value::LargestUInt(Value::maxInt) )
618       currentValue() = Value::LargestInt( value );
619    else
620       currentValue() = value;
621    return true;
622 }
623 
624 
625 bool
decodeDouble(Token & token)626 Reader::decodeDouble( Token &token )
627 {
628    double value = 0;
629    const int bufferSize = 32;
630    int count;
631    int length = int(token.end_ - token.start_);
632 
633    // Sanity check to avoid buffer overflow exploits.
634    if (length < 0) {
635       return addError( "Unable to parse token length", token );
636    }
637 
638    // Avoid using a string constant for the format control string given to
639    // sscanf, as this can cause hard to debug crashes on OS X. See here for more
640    // info:
641    //
642    //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
643    char format[] = "%lf";
644 
645    if ( length <= bufferSize )
646    {
647       Char buffer[bufferSize+1];
648       memcpy( buffer, token.start_, length );
649       buffer[length] = 0;
650       count = sscanf( buffer, format, &value );
651    }
652    else
653    {
654       std::string buffer( token.start_, token.end_ );
655       count = sscanf( buffer.c_str(), format, &value );
656    }
657 
658    if ( count != 1 )
659       return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
660    currentValue() = value;
661    return true;
662 }
663 
664 
665 bool
decodeString(Token & token)666 Reader::decodeString( Token &token )
667 {
668    std::string decoded;
669    if ( !decodeString( token, decoded ) )
670       return false;
671    currentValue() = decoded;
672    return true;
673 }
674 
675 
676 bool
decodeString(Token & token,std::string & decoded)677 Reader::decodeString( Token &token, std::string &decoded )
678 {
679    decoded.reserve( token.end_ - token.start_ - 2 );
680    Location current = token.start_ + 1; // skip '"'
681    Location end = token.end_ - 1;      // do not include '"'
682    while ( current != end )
683    {
684       Char c = *current++;
685       if ( c == '"' )
686          break;
687       else if ( c == '\\' )
688       {
689          if ( current == end )
690             return addError( "Empty escape sequence in string", token, current );
691          Char escape = *current++;
692          switch ( escape )
693          {
694          case '"': decoded += '"'; break;
695          case '/': decoded += '/'; break;
696          case '\\': decoded += '\\'; break;
697          case 'b': decoded += '\b'; break;
698          case 'f': decoded += '\f'; break;
699          case 'n': decoded += '\n'; break;
700          case 'r': decoded += '\r'; break;
701          case 't': decoded += '\t'; break;
702          case 'u':
703             {
704                unsigned int unicode;
705                if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
706                   return false;
707                decoded += codePointToUTF8(unicode);
708             }
709             break;
710          default:
711             return addError( "Bad escape sequence in string", token, current );
712          }
713       }
714       else
715       {
716          decoded += c;
717       }
718    }
719    return true;
720 }
721 
722 bool
decodeUnicodeCodePoint(Token & token,Location & current,Location end,unsigned int & unicode)723 Reader::decodeUnicodeCodePoint( Token &token,
724                                      Location &current,
725                                      Location end,
726                                      unsigned int &unicode )
727 {
728 
729    if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
730       return false;
731    if (unicode >= 0xD800 && unicode <= 0xDBFF)
732    {
733       // surrogate pairs
734       if (end - current < 6)
735          return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
736       unsigned int surrogatePair;
737       if (*(current++) == '\\' && *(current++)== 'u')
738       {
739          if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
740          {
741             unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
742          }
743          else
744             return false;
745       }
746       else
747          return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
748    }
749    return true;
750 }
751 
752 bool
decodeUnicodeEscapeSequence(Token & token,Location & current,Location end,unsigned int & unicode)753 Reader::decodeUnicodeEscapeSequence( Token &token,
754                                      Location &current,
755                                      Location end,
756                                      unsigned int &unicode )
757 {
758    if ( end - current < 4 )
759       return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
760    unicode = 0;
761    for ( int index =0; index < 4; ++index )
762    {
763       Char c = *current++;
764       unicode *= 16;
765       if ( c >= '0'  &&  c <= '9' )
766          unicode += c - '0';
767       else if ( c >= 'a'  &&  c <= 'f' )
768          unicode += c - 'a' + 10;
769       else if ( c >= 'A'  &&  c <= 'F' )
770          unicode += c - 'A' + 10;
771       else
772          return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
773    }
774    return true;
775 }
776 
777 
778 bool
addError(const std::string & message,Token & token,Location extra)779 Reader::addError( const std::string &message,
780                   Token &token,
781                   Location extra )
782 {
783    ErrorInfo info;
784    info.token_ = token;
785    info.message_ = message;
786    info.extra_ = extra;
787    errors_.push_back( info );
788    return false;
789 }
790 
791 
792 bool
recoverFromError(TokenType skipUntilToken)793 Reader::recoverFromError( TokenType skipUntilToken )
794 {
795    int errorCount = int(errors_.size());
796    Token skip;
797    for (;;)
798    {
799       if ( !readToken(skip) )
800          errors_.resize( errorCount ); // discard errors caused by recovery
801       if ( skip.type_ == skipUntilToken  ||  skip.type_ == tokenEndOfStream )
802          break;
803    }
804    errors_.resize( errorCount );
805    return false;
806 }
807 
808 
809 bool
addErrorAndRecover(const std::string & message,Token & token,TokenType skipUntilToken)810 Reader::addErrorAndRecover( const std::string &message,
811                             Token &token,
812                             TokenType skipUntilToken )
813 {
814    addError( message, token );
815    return recoverFromError( skipUntilToken );
816 }
817 
818 
819 Value &
currentValue()820 Reader::currentValue()
821 {
822    return *(nodes_.top());
823 }
824 
825 
826 Reader::Char
getNextChar()827 Reader::getNextChar()
828 {
829    if ( current_ == end_ )
830       return 0;
831    return *current_++;
832 }
833 
834 
835 void
getLocationLineAndColumn(Location location,int & line,int & column) const836 Reader::getLocationLineAndColumn( Location location,
837                                   int &line,
838                                   int &column ) const
839 {
840    Location current = begin_;
841    Location lastLineStart = current;
842    line = 0;
843    while ( current < location  &&  current != end_ )
844    {
845       Char c = *current++;
846       if ( c == '\r' )
847       {
848          if ( *current == '\n' )
849             ++current;
850          lastLineStart = current;
851          ++line;
852       }
853       else if ( c == '\n' )
854       {
855          lastLineStart = current;
856          ++line;
857       }
858    }
859    // column & line start at 1
860    column = int(location - lastLineStart) + 1;
861    ++line;
862 }
863 
864 
865 std::string
getLocationLineAndColumn(Location location) const866 Reader::getLocationLineAndColumn( Location location ) const
867 {
868    int line, column;
869    getLocationLineAndColumn( location, line, column );
870    char buffer[18+16+16+1];
871    sprintf( buffer, "Line %d, Column %d", line, column );
872    return buffer;
873 }
874 
875 
876 // Deprecated. Preserved for backward compatibility
877 std::string
getFormatedErrorMessages() const878 Reader::getFormatedErrorMessages() const
879 {
880     return getFormattedErrorMessages();
881 }
882 
883 
884 std::string
getFormattedErrorMessages() const885 Reader::getFormattedErrorMessages() const
886 {
887    std::string formattedMessage;
888    for ( Errors::const_iterator itError = errors_.begin();
889          itError != errors_.end();
890          ++itError )
891    {
892       const ErrorInfo &error = *itError;
893       formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
894       formattedMessage += "  " + error.message_ + "\n";
895       if ( error.extra_ )
896          formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
897    }
898    return formattedMessage;
899 }
900 
901 
operator >>(std::istream & sin,Value & root)902 std::istream& operator>>( std::istream &sin, Value &root )
903 {
904     Json::Reader reader;
905     bool ok = reader.parse(sin, root, true);
906     if (!ok) {
907       fprintf(
908           stderr,
909           "Error from reader: %s",
910           reader.getFormattedErrorMessages().c_str());
911 
912       JSON_FAIL_MESSAGE("reader error");
913     }
914     return sin;
915 }
916 
917 
918 } // namespace Json
919