diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 9e1c97c..41dff72 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1211,7 +1211,7 @@ public: /*! \param allocator Optional allocator for allocating stack memory. \param stackCapacity Initial capacity of stack in bytes. */ - GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {} //!@name Parse from stream //!@{ @@ -1227,16 +1227,11 @@ public: GenericDocument& ParseStream(InputStream& is) { ValueType::SetNull(); // Remove existing root if exist GenericReader reader(&GetAllocator()); - if (reader.template Parse(is, *this)) { + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. - parseErrorCode_ = kParseErrorNone; - errorOffset_ = 0; - } - else { - parseErrorCode_ = reader.GetParseErrorCode(); - errorOffset_ = reader.GetErrorOffset(); - ClearStack(); } return *this; } @@ -1332,14 +1327,14 @@ public: //!@name Handling parse errors //!@{ - //! Whether a parse error was occured in the last parsing. - bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } - //! Get the message of parsing error. - ParseErrorCode GetParseError() const { return parseErrorCode_; } + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } - //! Get the offset in character of the parsing error. - size_t GetErrorOffset() const { return errorOffset_; } + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } //!@} @@ -1350,6 +1345,14 @@ public: size_t GetStackCapacity() const { return stack_.GetCapacity(); } private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + GenericDocument& d_; + }; + // callers of the following private Handler functions template friend class GenericReader; // for parsing friend class GenericValue; // for deep copying @@ -1401,8 +1404,7 @@ private: static const size_t kDefaultStackCapacity = 1024; internal::Stack stack_; - ParseErrorCode parseErrorCode_; - size_t errorOffset_; + ParseResult parseResult_; }; //! GenericDocument with UTF8 encoding diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index 81637ee..6317957 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -32,12 +32,14 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); - case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoidng in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + default: return RAPIDJSON_ERROR_STRING("Unknown error."); } diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index ba45e7e..b183917 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -1,8 +1,6 @@ #ifndef RAPIDJSON_ERROR_ERROR_H__ #define RAPIDJSON_ERROR_ERROR_H__ -#include "../reader.h" // ParseErrorCode - /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ERROR_CHARTYPE @@ -29,6 +27,84 @@ namespace rapidjson { +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination //!< Parsing was terminated. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + //! Function pointer type of GetParseError(). /*! This is the prototype for GetParseError_X(), where X is a locale. User can dynamically change locale in runtime, e.g.: diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index c5f32b6..39afcd8 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -24,12 +24,21 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (HasParseError()) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) + #ifndef RAPIDJSON_PARSE_ERROR_NORETURN #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ - parseErrorCode_ = parseErrorCode; \ - errorOffset_ = offset; \ + parseResult_.Set(parseErrorCode,offset); \ RAPIDJSON_MULTILINEMACRO_END #endif @@ -37,10 +46,12 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ - return; \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ RAPIDJSON_MULTILINEMACRO_END #endif +#include "error/error.h" // ParseErrorCode, ParseResult + namespace rapidjson { /////////////////////////////////////////////////////////////////////////////// @@ -55,35 +66,6 @@ enum ParseFlag { kParseValidateEncodingFlag = 2 //!< Validate encoding of JSON strings. }; -//! Error code of parsing. -enum ParseErrorCode { - kParseErrorNone = 0, //!< No error. - - kParseErrorDocumentEmpty, //!< The document is empty. - kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array. - kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. - - kParseErrorValueInvalid, //!< Invalid value. - - kParseErrorObjectMissName, //!< Missing a name for object member. - kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. - kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. - - kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. - - kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. - kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. - kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. - kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. - kParseErrorStringInvalidEncoding, //!< Invalid encoidng in string. - - kParseErrorNumberTooBig, //!< Number too big to be stored in double. - kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent, //!< Miss exponent in number. - - kParseErrorTermination //!< Parsing was terminated. -}; - /////////////////////////////////////////////////////////////////////////////// // Handler @@ -292,7 +274,7 @@ public: /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) */ - GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} + GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. @@ -303,32 +285,33 @@ public: \return Whether the parsing is successful. */ template - bool Parse(InputStream& is, Handler& handler) { - parseErrorCode_ = kParseErrorNone; - errorOffset_ = 0; + ParseResult Parse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); SkipWhitespace(is); - if (is.Peek() == '\0') + if (is.Peek() == '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } else { switch (is.Peek()) { case '{': ParseObject(is, handler); break; case '[': ParseArray(is, handler); break; default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); } - if (HasParseError()) - goto out; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); SkipWhitespace(is); - if (is.Peek() != '\0') + if (is.Peek() != '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } } - out: - stack_.Clear(); - return !HasParseError(); + return parseResult_; } //! Parse JSON text (with \ref kParseDefaultFlags) @@ -339,21 +322,34 @@ public: \return Whether the parsing is successful. */ template - bool Parse(InputStream& is, Handler& handler) { + ParseResult Parse(InputStream& is, Handler& handler) { return Parse(is, handler); } - bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } - ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } - size_t GetErrorOffset() const { return errorOffset_; } + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } private: // Prohibit copy constructor & assignment operator. GenericReader(const GenericReader&); GenericReader& operator=(const GenericReader&); + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + }; + // Parse object: { string : value, ... } template void ParseObject(InputStream& is, Handler& handler) { @@ -377,8 +373,7 @@ private: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -388,8 +383,7 @@ private: SkipWhitespace(is); ParseValue(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -427,8 +421,7 @@ private: for (SizeType elementCount = 0;;) { ParseValue(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; SkipWhitespace(is); @@ -500,7 +493,7 @@ private: codepoint -= 'a' - 10; else { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); - return 0; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } } return codepoint; @@ -532,8 +525,7 @@ private: if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); ParseStringToStream(s, s); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false)) @@ -542,8 +534,7 @@ private: else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; if (!handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } @@ -794,8 +785,7 @@ private: static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. - ParseErrorCode parseErrorCode_; - size_t errorOffset_; + ParseResult parseResult_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator.