|
|
@ -129,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_) { |
|
|
@ -157,7 +157,7 @@ bool Reader::readValue() { |
|
|
|
throwRuntimeError("Exceeded stackLimit in readValue()."); |
|
|
|
|
|
|
|
Token token; |
|
|
|
skipCommentTokens(token); |
|
|
|
readTokenSkippingComments(token); |
|
|
|
bool successful = true; |
|
|
|
|
|
|
|
if (collectComments_ && !commentsBefore_.empty()) { |
|
|
@ -225,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) { |
|
|
@ -446,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(); |
|
|
@ -480,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; |
|
|
|
} |
|
|
@ -518,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) { |
|
|
@ -599,8 +587,7 @@ bool Reader::decodeDouble(Token& token) { |
|
|
|
|
|
|
|
bool Reader::decodeDouble(Token& token, Value& decoded) { |
|
|
|
double value = 0; |
|
|
|
String buffer(token.start_, token.end_); |
|
|
|
IStringStream is(buffer); |
|
|
|
IStringStream is(String(token.start_, token.end_)); |
|
|
|
if (!(is >> value)) { |
|
|
|
if (value == std::numeric_limits<double>::max()) |
|
|
|
value = std::numeric_limits<double>::infinity(); |
|
|
@ -608,7 +595,7 @@ bool Reader::decodeDouble(Token& token, Value& decoded) { |
|
|
|
value = -std::numeric_limits<double>::infinity(); |
|
|
|
else if (!std::isinf(value)) |
|
|
|
return addError( |
|
|
|
"'" + String(token.start_, token.end_) + "' is not a number.", token); |
|
|
|
"'" + String(token.start_, token.end_) + "' is not a number.", token); |
|
|
|
} |
|
|
|
decoded = value; |
|
|
|
return true; |
|
|
@ -773,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; |
|
|
@ -890,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
|
|
|
@ -943,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); |
|
|
@ -976,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); |
|
|
@ -1030,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; |
|
|
@ -1058,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()) { |
|
|
@ -1145,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) { |
|
|
@ -1449,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
|
|
|
@ -1491,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; |
|
|
|
} |
|
|
@ -1533,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) { |
|
|
@ -1651,8 +1621,7 @@ 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)) { |
|
|
|
if (value == std::numeric_limits<double>::max()) |
|
|
|
value = std::numeric_limits<double>::infinity(); |
|
|
@ -1660,7 +1629,7 @@ bool OurReader::decodeDouble(Token& token, Value& decoded) { |
|
|
|
value = -std::numeric_limits<double>::infinity(); |
|
|
|
else if (!std::isinf(value)) |
|
|
|
return addError( |
|
|
|
"'" + String(token.start_, token.end_) + "' is not a number.", token); |
|
|
|
"'" + String(token.start_, token.end_) + "' is not a number.", token); |
|
|
|
} |
|
|
|
decoded = value; |
|
|
|
return true; |
|
|
@ -1826,7 +1795,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; |
|
|
@ -1861,10 +1830,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_; |
|
|
@ -1874,20 +1844,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_); } |
|
|
@ -1976,6 +1962,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
|
|
|
@ -1984,7 +1996,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.
|
|
|
|