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