|
@@ -12,6 +12,7 @@
|
|
|
#endif // if !defined(JSON_IS_AMALGAMATION)
|
|
|
#include <algorithm>
|
|
|
#include <cassert>
|
|
|
+#include <cmath>
|
|
|
#include <cstring>
|
|
|
#include <iostream>
|
|
|
#include <istream>
|
|
@@ -128,7 +129,7 @@ bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
|
|
|
|
|
|
bool successful = readValue();
|
|
|
Token token;
|
|
|
- skipCommentTokens(token);
|
|
|
+ readTokenSkippingComments(token);
|
|
|
if (collectComments_ && !commentsBefore_.empty())
|
|
|
root.setComment(commentsBefore_, commentAfter);
|
|
|
if (features_.strictRoot_) {
|
|
@@ -156,7 +157,7 @@ bool Reader::readValue() {
|
|
|
throwRuntimeError("Exceeded stackLimit in readValue().");
|
|
|
|
|
|
Token token;
|
|
|
- skipCommentTokens(token);
|
|
|
+ readTokenSkippingComments(token);
|
|
|
bool successful = true;
|
|
|
|
|
|
if (collectComments_ && !commentsBefore_.empty()) {
|
|
@@ -224,14 +225,14 @@ bool Reader::readValue() {
|
|
|
return successful;
|
|
|
}
|
|
|
|
|
|
-void Reader::skipCommentTokens(Token& token) {
|
|
|
+bool Reader::readTokenSkippingComments(Token& token) {
|
|
|
+ bool success = readToken(token);
|
|
|
if (features_.allowComments_) {
|
|
|
- do {
|
|
|
- readToken(token);
|
|
|
- } while (token.type_ == tokenComment);
|
|
|
- } else {
|
|
|
- readToken(token);
|
|
|
+ while (success && token.type_ == tokenComment) {
|
|
|
+ success = readToken(token);
|
|
|
+ }
|
|
|
}
|
|
|
+ return success;
|
|
|
}
|
|
|
|
|
|
bool Reader::readToken(Token& token) {
|
|
@@ -445,12 +446,7 @@ bool Reader::readObject(Token& token) {
|
|
|
Value init(objectValue);
|
|
|
currentValue().swapPayload(init);
|
|
|
currentValue().setOffsetStart(token.start_ - begin_);
|
|
|
- while (readToken(tokenName)) {
|
|
|
- bool initialTokenOk = true;
|
|
|
- while (tokenName.type_ == tokenComment && initialTokenOk)
|
|
|
- initialTokenOk = readToken(tokenName);
|
|
|
- if (!initialTokenOk)
|
|
|
- break;
|
|
|
+ while (readTokenSkippingComments(tokenName)) {
|
|
|
if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
|
|
|
return true;
|
|
|
name.clear();
|
|
@@ -479,15 +475,11 @@ bool Reader::readObject(Token& token) {
|
|
|
return recoverFromError(tokenObjectEnd);
|
|
|
|
|
|
Token comma;
|
|
|
- if (!readToken(comma) ||
|
|
|
- (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
|
|
|
- comma.type_ != tokenComment)) {
|
|
|
+ if (!readTokenSkippingComments(comma) ||
|
|
|
+ (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
|
|
|
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;
|
|
|
}
|
|
@@ -517,10 +509,7 @@ bool Reader::readArray(Token& token) {
|
|
|
|
|
|
Token currentToken;
|
|
|
// Accept Comment after last item in the array.
|
|
|
- ok = readToken(currentToken);
|
|
|
- while (currentToken.type_ == tokenComment && ok) {
|
|
|
- ok = readToken(currentToken);
|
|
|
- }
|
|
|
+ ok = readTokenSkippingComments(currentToken);
|
|
|
bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
|
|
|
currentToken.type_ != tokenArrayEnd);
|
|
|
if (!ok || badTokenType) {
|
|
@@ -598,11 +587,16 @@ bool Reader::decodeDouble(Token& token) {
|
|
|
|
|
|
bool Reader::decodeDouble(Token& token, Value& decoded) {
|
|
|
double value = 0;
|
|
|
- String buffer(token.start_, token.end_);
|
|
|
- IStringStream is(buffer);
|
|
|
- if (!(is >> value))
|
|
|
- return addError(
|
|
|
- "'" + String(token.start_, token.end_) + "' is not a number.", token);
|
|
|
+ IStringStream is(String(token.start_, token.end_));
|
|
|
+ if (!(is >> value)) {
|
|
|
+ if (value == std::numeric_limits<double>::max())
|
|
|
+ value = std::numeric_limits<double>::infinity();
|
|
|
+ else if (value == std::numeric_limits<double>::lowest())
|
|
|
+ value = -std::numeric_limits<double>::infinity();
|
|
|
+ else if (!std::isinf(value))
|
|
|
+ return addError(
|
|
|
+ "'" + String(token.start_, token.end_) + "' is not a number.", token);
|
|
|
+ }
|
|
|
decoded = value;
|
|
|
return true;
|
|
|
}
|
|
@@ -766,7 +760,7 @@ void Reader::getLocationLineAndColumn(Location location, int& line,
|
|
|
while (current < location && current != end_) {
|
|
|
Char c = *current++;
|
|
|
if (c == '\r') {
|
|
|
- if (*current == '\n')
|
|
|
+ if (current != end_ && *current == '\n')
|
|
|
++current;
|
|
|
lastLineStart = current;
|
|
|
++line;
|
|
@@ -883,17 +877,12 @@ class OurReader {
|
|
|
public:
|
|
|
using Char = char;
|
|
|
using Location = const Char*;
|
|
|
- struct StructuredError {
|
|
|
- ptrdiff_t offset_start;
|
|
|
- ptrdiff_t offset_limit;
|
|
|
- String message;
|
|
|
- };
|
|
|
|
|
|
explicit OurReader(OurFeatures const& features);
|
|
|
bool parse(const char* beginDoc, const char* endDoc, Value& root,
|
|
|
bool collectComments = true);
|
|
|
String getFormattedErrorMessages() const;
|
|
|
- std::vector<StructuredError> getStructuredErrors() const;
|
|
|
+ std::vector<CharReader::StructuredError> getStructuredErrors() const;
|
|
|
|
|
|
private:
|
|
|
OurReader(OurReader const&); // no impl
|
|
@@ -936,6 +925,7 @@ private:
|
|
|
using Errors = std::deque<ErrorInfo>;
|
|
|
|
|
|
bool readToken(Token& token);
|
|
|
+ bool readTokenSkippingComments(Token& token);
|
|
|
void skipSpaces();
|
|
|
void skipBom(bool skipBom);
|
|
|
bool match(const Char* pattern, int patternLength);
|
|
@@ -969,7 +959,6 @@ private:
|
|
|
int& column) const;
|
|
|
String getLocationLineAndColumn(Location location) const;
|
|
|
void addComment(Location begin, Location end, CommentPlacement placement);
|
|
|
- void skipCommentTokens(Token& token);
|
|
|
|
|
|
static String normalizeEOL(Location begin, Location end);
|
|
|
static bool containsNewLine(Location begin, Location end);
|
|
@@ -1023,7 +1012,7 @@ bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
|
|
|
bool successful = readValue();
|
|
|
nodes_.pop();
|
|
|
Token token;
|
|
|
- skipCommentTokens(token);
|
|
|
+ readTokenSkippingComments(token);
|
|
|
if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
|
|
|
addError("Extra non-whitespace after JSON value.", token);
|
|
|
return false;
|
|
@@ -1051,7 +1040,7 @@ bool OurReader::readValue() {
|
|
|
if (nodes_.size() > features_.stackLimit_)
|
|
|
throwRuntimeError("Exceeded stackLimit in readValue().");
|
|
|
Token token;
|
|
|
- skipCommentTokens(token);
|
|
|
+ readTokenSkippingComments(token);
|
|
|
bool successful = true;
|
|
|
|
|
|
if (collectComments_ && !commentsBefore_.empty()) {
|
|
@@ -1138,14 +1127,14 @@ bool OurReader::readValue() {
|
|
|
return successful;
|
|
|
}
|
|
|
|
|
|
-void OurReader::skipCommentTokens(Token& token) {
|
|
|
+bool OurReader::readTokenSkippingComments(Token& token) {
|
|
|
+ bool success = readToken(token);
|
|
|
if (features_.allowComments_) {
|
|
|
- do {
|
|
|
- readToken(token);
|
|
|
- } while (token.type_ == tokenComment);
|
|
|
- } else {
|
|
|
- readToken(token);
|
|
|
+ while (success && token.type_ == tokenComment) {
|
|
|
+ success = readToken(token);
|
|
|
+ }
|
|
|
}
|
|
|
+ return success;
|
|
|
}
|
|
|
|
|
|
bool OurReader::readToken(Token& token) {
|
|
@@ -1442,12 +1431,7 @@ bool OurReader::readObject(Token& token) {
|
|
|
Value init(objectValue);
|
|
|
currentValue().swapPayload(init);
|
|
|
currentValue().setOffsetStart(token.start_ - begin_);
|
|
|
- while (readToken(tokenName)) {
|
|
|
- bool initialTokenOk = true;
|
|
|
- while (tokenName.type_ == tokenComment && initialTokenOk)
|
|
|
- initialTokenOk = readToken(tokenName);
|
|
|
- if (!initialTokenOk)
|
|
|
- break;
|
|
|
+ while (readTokenSkippingComments(tokenName)) {
|
|
|
if (tokenName.type_ == tokenObjectEnd &&
|
|
|
(name.empty() ||
|
|
|
features_.allowTrailingCommas_)) // empty object or trailing comma
|
|
@@ -1484,15 +1468,11 @@ bool OurReader::readObject(Token& token) {
|
|
|
return recoverFromError(tokenObjectEnd);
|
|
|
|
|
|
Token comma;
|
|
|
- if (!readToken(comma) ||
|
|
|
- (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
|
|
|
- comma.type_ != tokenComment)) {
|
|
|
+ if (!readTokenSkippingComments(comma) ||
|
|
|
+ (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
|
|
|
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;
|
|
|
}
|
|
@@ -1526,10 +1506,7 @@ bool OurReader::readArray(Token& token) {
|
|
|
|
|
|
Token currentToken;
|
|
|
// Accept Comment after last item in the array.
|
|
|
- ok = readToken(currentToken);
|
|
|
- while (currentToken.type_ == tokenComment && ok) {
|
|
|
- ok = readToken(currentToken);
|
|
|
- }
|
|
|
+ ok = readTokenSkippingComments(currentToken);
|
|
|
bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
|
|
|
currentToken.type_ != tokenArrayEnd);
|
|
|
if (!ok || badTokenType) {
|
|
@@ -1607,7 +1584,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
|
|
|
const auto digit(static_cast<Value::UInt>(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, meaing value == threshold,
|
|
|
+ // a) we've only just touched the limit, meaning value == threshold,
|
|
|
// b) this is the last digit, or
|
|
|
// c) it's small enough to fit in that rounding delta, we're okay.
|
|
|
// Otherwise treat this number as a double to avoid overflow.
|
|
@@ -1644,11 +1621,15 @@ bool OurReader::decodeDouble(Token& token) {
|
|
|
|
|
|
bool OurReader::decodeDouble(Token& token, Value& decoded) {
|
|
|
double value = 0;
|
|
|
- const String buffer(token.start_, token.end_);
|
|
|
- IStringStream is(buffer);
|
|
|
+ IStringStream is(String(token.start_, token.end_));
|
|
|
if (!(is >> value)) {
|
|
|
- return addError(
|
|
|
- "'" + String(token.start_, token.end_) + "' is not a number.", token);
|
|
|
+ if (value == std::numeric_limits<double>::max())
|
|
|
+ value = std::numeric_limits<double>::infinity();
|
|
|
+ else if (value == std::numeric_limits<double>::lowest())
|
|
|
+ value = -std::numeric_limits<double>::infinity();
|
|
|
+ else if (!std::isinf(value))
|
|
|
+ return addError(
|
|
|
+ "'" + String(token.start_, token.end_) + "' is not a number.", token);
|
|
|
}
|
|
|
decoded = value;
|
|
|
return true;
|
|
@@ -1813,7 +1794,7 @@ void OurReader::getLocationLineAndColumn(Location location, int& line,
|
|
|
while (current < location && current != end_) {
|
|
|
Char c = *current++;
|
|
|
if (c == '\r') {
|
|
|
- if (*current == '\n')
|
|
|
+ if (current != end_ && *current == '\n')
|
|
|
++current;
|
|
|
lastLineStart = current;
|
|
|
++line;
|
|
@@ -1848,10 +1829,11 @@ String OurReader::getFormattedErrorMessages() const {
|
|
|
return formattedMessage;
|
|
|
}
|
|
|
|
|
|
-std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
|
|
|
- std::vector<OurReader::StructuredError> allErrors;
|
|
|
+std::vector<CharReader::StructuredError>
|
|
|
+OurReader::getStructuredErrors() const {
|
|
|
+ std::vector<CharReader::StructuredError> allErrors;
|
|
|
for (const auto& error : errors_) {
|
|
|
- OurReader::StructuredError structured;
|
|
|
+ CharReader::StructuredError structured;
|
|
|
structured.offset_start = error.token_.start_ - begin_;
|
|
|
structured.offset_limit = error.token_.end_ - begin_;
|
|
|
structured.message = error.message_;
|
|
@@ -1861,20 +1843,36 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
|
|
|
}
|
|
|
|
|
|
class OurCharReader : public CharReader {
|
|
|
- bool const collectComments_;
|
|
|
- OurReader reader_;
|
|
|
|
|
|
public:
|
|
|
OurCharReader(bool collectComments, OurFeatures const& features)
|
|
|
- : collectComments_(collectComments), reader_(features) {}
|
|
|
- bool parse(char const* beginDoc, char const* endDoc, Value* root,
|
|
|
- String* errs) override {
|
|
|
- bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
|
|
|
- if (errs) {
|
|
|
- *errs = reader_.getFormattedErrorMessages();
|
|
|
+ : CharReader(
|
|
|
+ std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
|
|
|
+
|
|
|
+protected:
|
|
|
+ class OurImpl : public Impl {
|
|
|
+ public:
|
|
|
+ OurImpl(bool collectComments, OurFeatures const& features)
|
|
|
+ : collectComments_(collectComments), reader_(features) {}
|
|
|
+
|
|
|
+ bool parse(char const* beginDoc, char const* endDoc, Value* root,
|
|
|
+ String* errs) override {
|
|
|
+ bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
|
|
|
+ if (errs) {
|
|
|
+ *errs = reader_.getFormattedErrorMessages();
|
|
|
+ }
|
|
|
+ return ok;
|
|
|
}
|
|
|
- return ok;
|
|
|
- }
|
|
|
+
|
|
|
+ std::vector<CharReader::StructuredError>
|
|
|
+ getStructuredErrors() const override {
|
|
|
+ return reader_.getStructuredErrors();
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ bool const collectComments_;
|
|
|
+ OurReader reader_;
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
|
|
@@ -1963,6 +1961,32 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
|
|
|
(*settings)["skipBom"] = true;
|
|
|
//! [CharReaderBuilderDefaults]
|
|
|
}
|
|
|
+// static
|
|
|
+void CharReaderBuilder::ecma404Mode(Json::Value* settings) {
|
|
|
+ //! [CharReaderBuilderECMA404Mode]
|
|
|
+ (*settings)["allowComments"] = false;
|
|
|
+ (*settings)["allowTrailingCommas"] = false;
|
|
|
+ (*settings)["strictRoot"] = false;
|
|
|
+ (*settings)["allowDroppedNullPlaceholders"] = false;
|
|
|
+ (*settings)["allowNumericKeys"] = false;
|
|
|
+ (*settings)["allowSingleQuotes"] = false;
|
|
|
+ (*settings)["stackLimit"] = 1000;
|
|
|
+ (*settings)["failIfExtra"] = true;
|
|
|
+ (*settings)["rejectDupKeys"] = false;
|
|
|
+ (*settings)["allowSpecialFloats"] = false;
|
|
|
+ (*settings)["skipBom"] = false;
|
|
|
+ //! [CharReaderBuilderECMA404Mode]
|
|
|
+}
|
|
|
+
|
|
|
+std::vector<CharReader::StructuredError>
|
|
|
+CharReader::getStructuredErrors() const {
|
|
|
+ return _impl->getStructuredErrors();
|
|
|
+}
|
|
|
+
|
|
|
+bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
|
|
|
+ String* errs) {
|
|
|
+ return _impl->parse(beginDoc, endDoc, root, errs);
|
|
|
+}
|
|
|
|
|
|
//////////////////////////////////
|
|
|
// global functions
|
|
@@ -1971,7 +1995,7 @@ bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
|
|
|
String* errs) {
|
|
|
OStringStream ssin;
|
|
|
ssin << sin.rdbuf();
|
|
|
- String doc = ssin.str();
|
|
|
+ String doc = std::move(ssin).str();
|
|
|
char const* begin = doc.data();
|
|
|
char const* end = begin + doc.size();
|
|
|
// Note that we do not actually need a null-terminator.
|