From 3693d61f5a8625cfea75305d4c7b84cd5f189993 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 27 Jun 2014 01:53:56 +0800 Subject: [PATCH 1/6] Add parse error codes and API for converting error code to text. Parse errors is represented as enum type `ParseErrorCode`. Error texts are optional for user. Added `GetParseError_En()` in `error/en.h`, user can localize this file into other files. User may dynamically change the locale in runtime. --- example/condense/condense.cpp | 3 +- example/pretty/pretty.cpp | 3 +- example/prettyauto/prettyauto.cpp | 3 +- include/rapidjson/document.h | 12 ++--- include/rapidjson/error/en.h | 41 ++++++++++++++ include/rapidjson/error/error.h | 24 +++++++++ include/rapidjson/reader.h | 89 ++++++++++++++++++++----------- 7 files changed, 135 insertions(+), 40 deletions(-) create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h diff --git a/example/condense/condense.cpp b/example/condense/condense.cpp index 8f1da83..ae6d36a 100644 --- a/example/condense/condense.cpp +++ b/example/condense/condense.cpp @@ -7,6 +7,7 @@ #include "rapidjson/writer.h" #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" +#include "rapidjson/error/en.h" using namespace rapidjson; @@ -23,7 +24,7 @@ int main(int, char*[]) { // JSON reader parse from the input stream and let writer generate the output. if (!reader.Parse<0>(is, writer)) { - fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), reader.GetParseError()); + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); return 1; } diff --git a/example/pretty/pretty.cpp b/example/pretty/pretty.cpp index b09fc78..cfb3f0f 100644 --- a/example/pretty/pretty.cpp +++ b/example/pretty/pretty.cpp @@ -5,6 +5,7 @@ #include "rapidjson/prettywriter.h" #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" +#include "rapidjson/error/en.h" using namespace rapidjson; @@ -21,7 +22,7 @@ int main(int, char*[]) { // JSON reader parse from the input stream and let writer generate the output. if (!reader.Parse(is, writer)) { - fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), reader.GetParseError()); + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); return 1; } diff --git a/example/prettyauto/prettyauto.cpp b/example/prettyauto/prettyauto.cpp index 2e52e9e..3f85f40 100644 --- a/example/prettyauto/prettyauto.cpp +++ b/example/prettyauto/prettyauto.cpp @@ -7,6 +7,7 @@ #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" #include "rapidjson/encodedstream.h" // NEW +#include "rapidjson/error/en.h" #ifdef _WIN32 #include #include @@ -47,7 +48,7 @@ int main(int, char*[]) { // JSON reader parse from the input stream and let writer generate the output. //if (!reader.Parse(is, writer)) { if (!reader.Parse(eis, writer)) { // CHANGED - fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), reader.GetParseError()); + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); return 1; } diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 40a0442..a5e718f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -788,7 +788,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), parseError_(0), errorOffset_(0) {} + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} //! Parse JSON text from an input stream. /*! \tparam parseFlags Combination of ParseFlag. @@ -802,11 +802,11 @@ public: if (reader.template Parse(is, *this)) { 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. - parseError_ = 0; + parseErrorCode_ = kParseErrorNone; errorOffset_ = 0; } else { - parseError_ = reader.GetParseError(); + parseErrorCode_ = reader.GetParseErrorCode(); errorOffset_ = reader.GetErrorOffset(); ClearStack(); } @@ -851,10 +851,10 @@ public: } //! Whether a parse error was occured in the last parsing. - bool HasParseError() const { return parseError_ != 0; } + bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } //! Get the message of parsing error. - const char* GetParseError() const { return parseError_; } + ParseErrorCode GetParseError() const { return parseErrorCode_; } //! Get the offset in character of the parsing error. size_t GetErrorOffset() const { return errorOffset_; } @@ -914,7 +914,7 @@ private: static const size_t kDefaultStackCapacity = 1024; internal::Stack stack_; - const char* parseError_; + ParseErrorCode parseErrorCode_; size_t errorOffset_; }; diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..bbdb024 --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,41 @@ +#ifndef RAPIDJSON_ERROR_EN_H__ +#define RAPIDJSON_ERROR_EN_H__ + +#include "error.h" + +namespace rapidjson { + +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotObjectOrArray: return RAPIDJSON_ERROR_STRING("The document root must be either object or array."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Must be a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + 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 kParesErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to store in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + default: + return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +} // namespace rapidjson + +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..a24178a --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,24 @@ +#ifndef RAPIDJSON_ERROR_ERROR_H__ +#define RAPIDJSON_ERROR_ERROR_H__ + +// For example, on Windows, user can define this macro as TCHAR +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +// For example, on Windows, user can define this macro as _T(x) +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +namespace rapidjson { + +// User can dynamically change locale in runtime, e.g.: +// GetParseErrorFunc GetParseError = GetParseError_En; // or whatever +// const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); + +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +} // namespace rapidjson + +#endif // RAPIDJSON_ERROR_ERROR_H__ \ No newline at end of file diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 4a184c0..1b83485 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -21,19 +21,19 @@ #endif #ifndef RAPIDJSON_PARSE_ERROR_NORETURN -#define RAPIDJSON_PARSE_ERROR_NORETURN(msg, offset) \ +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ if (!HasParseError()) {\ - parseError_ = msg; \ + parseErrorCode_ = parseErrorCode; \ errorOffset_ = offset; \ }\ RAPIDJSON_MULTILINEMACRO_END #endif #ifndef RAPIDJSON_PARSE_ERROR -#define RAPIDJSON_PARSE_ERROR(msg, offset) \ +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ - RAPIDJSON_PARSE_ERROR_NORETURN(msg, offset); \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ return; \ RAPIDJSON_MULTILINEMACRO_END #endif @@ -50,6 +50,33 @@ 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, //!< Must be 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. + + kParesErrorNumberTooBig, //!< Number too big to store in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent //!< Miss exponent in number. +}; + /////////////////////////////////////////////////////////////////////////////// // Handler @@ -218,7 +245,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), parseError_(0), errorOffset_(0) {} + GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} //! Parse JSON text. /*! \tparam parseFlags Combination of ParseFlag. @@ -230,31 +257,31 @@ public: */ template bool Parse(InputStream& is, Handler& handler) { - parseError_ = 0; + parseErrorCode_ = kParseErrorNone; errorOffset_ = 0; SkipWhitespace(is); if (is.Peek() == '\0') - RAPIDJSON_PARSE_ERROR_NORETURN("Text only contains white space(s)", is.Tell()); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); else { switch (is.Peek()) { case '{': ParseObject(is, handler); break; case '[': ParseArray(is, handler); break; - default: RAPIDJSON_PARSE_ERROR_NORETURN("Expect either an object or array at root", is.Tell()); + default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); } SkipWhitespace(is); if (is.Peek() != '\0') - RAPIDJSON_PARSE_ERROR_NORETURN("Nothing should follow the root object or array.", is.Tell()); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); } stack_.Clear(); return !HasParseError(); } - bool HasParseError() const { return parseError_ != 0; } - const char* GetParseError() const { return parseError_; } + bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } size_t GetErrorOffset() const { return errorOffset_; } private: @@ -274,7 +301,7 @@ private: for (SizeType memberCount = 0;;) { if (is.Peek() != '"') - RAPIDJSON_PARSE_ERROR("Name of an object member must be a string", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler); if (HasParseError()) @@ -283,7 +310,7 @@ private: SkipWhitespace(is); if (is.Take() != ':') - RAPIDJSON_PARSE_ERROR("There must be a colon after the name of object member", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); SkipWhitespace(is); @@ -298,7 +325,7 @@ private: switch(is.Take()) { case ',': SkipWhitespace(is); break; case '}': handler.EndObject(memberCount); return; - default: RAPIDJSON_PARSE_ERROR("Must be a comma or '}' after an object member", is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); } } } @@ -328,7 +355,7 @@ private: switch (is.Take()) { case ',': SkipWhitespace(is); break; case ']': handler.EndArray(elementCount); return; - default: RAPIDJSON_PARSE_ERROR("Must be a comma or ']' after an array element.", is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); } } } @@ -341,7 +368,7 @@ private: if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') handler.Null(); else - RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } template @@ -352,7 +379,7 @@ private: if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') handler.Bool(true); else - RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -363,7 +390,7 @@ private: if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') handler.Bool(false); else - RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). @@ -382,7 +409,7 @@ private: else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { - RAPIDJSON_PARSE_ERROR_NORETURN("Incorrect hex digit after \\u escape", s.Tell() - 1); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, s.Tell() - 1); return 0; } } @@ -455,16 +482,16 @@ private: if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { // Handle UTF-16 surrogate pair if (is.Take() != '\\' || is.Take() != 'u') - RAPIDJSON_PARSE_ERROR("Missing the second \\u in surrogate pair", is.Tell() - 2); + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); unsigned codepoint2 = ParseHex4(is); if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) - RAPIDJSON_PARSE_ERROR("The second \\u in surrogate pair is invalid", is.Tell() - 2); + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR("Unknown escape character", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); } else if (c == '"') { // Closing double quote is.Take(); @@ -472,14 +499,14 @@ private: return; } else if (c == '\0') - RAPIDJSON_PARSE_ERROR("lacks ending quotation before the end of string", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - RAPIDJSON_PARSE_ERROR("Incorrect unescaped character in string", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); else { if (parseFlags & kParseValidateEncodingFlag ? !Transcoder::Validate(is, os) : !Transcoder::Transcode(is, os)) - RAPIDJSON_PARSE_ERROR("Invalid encoding", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } } } @@ -526,7 +553,7 @@ private: } } else - RAPIDJSON_PARSE_ERROR("Expect a value here.", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); // Parse 64bit int uint64_t i64 = 0; @@ -559,7 +586,7 @@ private: d = (double)i64; while (s.Peek() >= '0' && s.Peek() <= '9') { if (d >= 1E307) - RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParesErrorNumberTooBig, is.Tell()); d = d * 10 + (s.Take() - '0'); } } @@ -578,7 +605,7 @@ private: --expFrac; } else - RAPIDJSON_PARSE_ERROR("At least one digit in fraction part", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, is.Tell()); while (s.Peek() >= '0' && s.Peek() <= '9') { if (expFrac > -16) { @@ -611,11 +638,11 @@ private: while (s.Peek() >= '0' && s.Peek() <= '9') { exp = exp * 10 + (s.Take() - '0'); if (exp > 308) - RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParesErrorNumberTooBig, is.Tell()); } } else - RAPIDJSON_PARSE_ERROR("At least one digit in exponent", s.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); if (expMinus) exp = -exp; @@ -660,7 +687,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. - const char* parseError_; + ParseErrorCode parseErrorCode_; size_t errorOffset_; }; // class GenericReader From b4df717675f79a44378826108f56cda773016dd7 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 27 Jun 2014 16:13:54 +0800 Subject: [PATCH 2/6] Fixes grammar mistakes in error messages. --- include/rapidjson/error/en.h | 4 ++-- include/rapidjson/reader.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index bbdb024..dc6dc48 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -19,7 +19,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); - case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Must be a comma or ']' after an array element."); + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); @@ -27,7 +27,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoidng in string."); - case kParesErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to store in double."); + case kParesErrorNumberTooBig: 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."); diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 1b83485..5483bd7 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -64,7 +64,7 @@ enum ParseErrorCode { kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. - kParseErrorArrayMissCommaOrSquareBracket, //!< Must be a comma or ']' after an array element. + 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. @@ -72,7 +72,7 @@ enum ParseErrorCode { kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. kParseErrorStringInvalidEncoding, //!< Invalid encoidng in string. - kParesErrorNumberTooBig, //!< Number too big to store in double. + kParesErrorNumberTooBig, //!< Number too big to be stored in double. kParseErrorNumberMissFraction, //!< Miss fraction part in number. kParseErrorNumberMissExponent //!< Miss exponent in number. }; From bb889b6ab653f0ee1f9097d48e33fd4ef5745c7b Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 27 Jun 2014 16:19:04 +0800 Subject: [PATCH 3/6] Verify all error codes in unit tests of parsing. --- test/unittest/readertest.cpp | 202 +++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 1fa33f1..88ed9a0 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -147,8 +147,8 @@ TEST(Reader, ParseNumberHandler) { #undef TEST_DOUBLE } -TEST(Reader, ParseNumberHandler_Error) { -#define TEST_NUMBER_ERROR(str) \ +TEST(Reader, ParseNumber_Error) { +#define TEST_NUMBER_ERROR(errorCode, str) \ { \ char buffer[1001]; \ sprintf(buffer, "[%s]", str); \ @@ -156,23 +156,27 @@ TEST(Reader, ParseNumberHandler_Error) { BaseReaderHandler<> h; \ Reader reader; \ EXPECT_FALSE(reader.Parse<0>(s, h)); \ + EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ } - TEST_NUMBER_ERROR("a"); // At least one digit in integer part - TEST_NUMBER_ERROR(".1"); // At least one digit in integer part - + // Number too big to be stored in double. { char n1e309[311]; // '1' followed by 309 '0' n1e309[0] = '1'; for (int i = 1; i < 310; i++) n1e309[i] = '0'; n1e309[310] = '\0'; - TEST_NUMBER_ERROR(n1e309); // Number too big to store in double + TEST_NUMBER_ERROR(kParesErrorNumberTooBig, n1e309); } + TEST_NUMBER_ERROR(kParesErrorNumberTooBig, "1e309"); - TEST_NUMBER_ERROR("1."); // At least one digit in fraction part - TEST_NUMBER_ERROR("1e309"); // Number too big to store in double - TEST_NUMBER_ERROR("1e_"); // At least one digit in exponent + // Miss fraction part in number. + TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1."); + TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a"); + + // Miss exponent in number. + TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e"); + TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_"); #undef TEST_NUMBER_ERROR } @@ -304,27 +308,38 @@ TEST(Reader, ParseString_NonDestructive) { EXPECT_EQ(11u, h.length_); } -bool TestString(const char* str) { +ParseErrorCode TestString(const char* str) { StringStream s(str); BaseReaderHandler<> h; Reader reader; - return reader.Parse(s, h); + reader.Parse(s, h); + return reader.GetParseErrorCode(); } TEST(Reader, ParseString_Error) { +#define TEST_STRING_ERROR(errorCode, str)\ + EXPECT_EQ(errorCode, TestString(str)) + #define ARRAY(...) { __VA_ARGS__ } -#define TEST_STRINGARRAY_ERROR(Encoding, utype, array) \ +#define TEST_STRINGENCODING_ERROR(Encoding, utype, array) \ { \ static const utype ue[] = array; \ static const Encoding::Ch* e = reinterpret_cast(&ue[0]); \ - EXPECT_FALSE(TestString(e)); \ + EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString(e));\ } - EXPECT_FALSE(TestString("[\"\\a\"]")); // Unknown escape character - EXPECT_FALSE(TestString("[\"\\uABCG\"]")); // Incorrect hex digit after \\u escape - EXPECT_FALSE(TestString("[\"\\uD800X\"]")); // Missing the second \\u in surrogate pair - EXPECT_FALSE(TestString("[\"\\uD800\\uFFFF\"]")); // The second \\u in surrogate pair is invalid - EXPECT_FALSE(TestString("[\"Test]")); // lacks ending quotation before the end of string + // Invalid escape character in string. + TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]"); + + // Incorrect hex digit after \\u escape in string. + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]"); + + // The surrogate pair in string is invalid. + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800X\"]"); + TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFFF\"]"); + + // Missing a closing quotation mark in string. + TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]"); // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt @@ -335,9 +350,9 @@ TEST(Reader, ParseString_Error) { char e[] = { '[', '\"', 0, '\"', ']', '\0' }; for (unsigned char c = 0x80u; c <= 0xBFu; c++) { e[2] = c; - bool b = TestString(e); - EXPECT_FALSE(b); - if (b) + ParseErrorCode error = TestString(e); + EXPECT_EQ(kParseErrorStringInvalidEncoding, error); + if (error != kParseErrorNone) std::cout << (unsigned)(unsigned char)c << std::endl; } } @@ -347,37 +362,37 @@ TEST(Reader, ParseString_Error) { char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' }; for (unsigned c = 0xC0u; c <= 0xFFu; c++) { e[2] = (char)c; - EXPECT_FALSE(TestString(e)); + TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e); } } // 4 Overlong sequences // 4.1 Examples of an overlong ASCII character - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); // 4.2 Maximum overlong sequences - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0')); // 4.3 Overlong representation of the NUL character - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0')); // 5 Illegal code positions // 5.1 Single UTF-16 surrogates - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0')); - TEST_STRINGARRAY_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); #undef ARRAY #undef TEST_STRINGARRAY_ERROR @@ -416,7 +431,7 @@ TEST(Reader, ParseArray) { } TEST(Reader, ParseArray_Error) { -#define TEST_ARRAY_ERROR(str) \ +#define TEST_ARRAY_ERROR(errorCode, str) \ { \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ @@ -424,12 +439,13 @@ TEST(Reader, ParseArray_Error) { BaseReaderHandler<> h; \ GenericReader, UTF8<>, CrtAllocator> reader; \ EXPECT_FALSE(reader.Parse<0>(s, h)); \ + EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ } - // Must be a comma or ']' after an array element. - TEST_ARRAY_ERROR("["); - TEST_ARRAY_ERROR("[}"); - TEST_ARRAY_ERROR("[1 2]"); + // Missing a comma or ']' after an array element. + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "["); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[}"); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]"); #undef TEST_ARRAY_ERROR } @@ -519,39 +535,7 @@ TEST(Reader, Parse_EmptyObject) { EXPECT_EQ(2u, h.step_); } -TEST(Reader, ParseObject_Error) { -#define TEST_OBJECT_ERROR(str) \ - { \ - char buffer[1001]; \ - strncpy(buffer, str, 1000); \ - InsituStringStream s(buffer); \ - BaseReaderHandler<> h; \ - GenericReader, UTF8<>, CrtAllocator> reader; \ - EXPECT_FALSE(reader.Parse<0>(s, h)); \ - } - - // Name of an object member must be a string - TEST_OBJECT_ERROR("{null:1}"); - TEST_OBJECT_ERROR("{true:1}"); - TEST_OBJECT_ERROR("{false:1}"); - TEST_OBJECT_ERROR("{1:1}"); - TEST_OBJECT_ERROR("{[]:1}"); - TEST_OBJECT_ERROR("{{}:1}"); - TEST_OBJECT_ERROR("{xyz:1}"); - - // There must be a colon after the name of object member - TEST_OBJECT_ERROR("{\"a\" 1}"); - TEST_OBJECT_ERROR("{\"a\",1}"); - - // Must be a comma or '}' after an object member - TEST_OBJECT_ERROR("{]"); - TEST_OBJECT_ERROR("{\"a\":1]"); - -#undef TEST_OBJECT_ERROR -} - -TEST(Reader, Parse_Error) { -#define TEST_ERROR(str) \ +#define TEST_ERROR(errorCode, str) \ { \ char buffer[1001]; \ strncpy(buffer, str, 1000); \ @@ -559,31 +543,59 @@ TEST(Reader, Parse_Error) { BaseReaderHandler<> h; \ Reader reader; \ EXPECT_FALSE(reader.Parse<0>(s, h)); \ + EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ } - // Text only contains white space(s) - TEST_ERROR(""); - TEST_ERROR(" "); - TEST_ERROR(" \n"); +TEST(Reader, ParseDocument_Error) { + // The document is empty. + TEST_ERROR(kParseErrorDocumentEmpty, ""); + TEST_ERROR(kParseErrorDocumentEmpty, " "); + TEST_ERROR(kParseErrorDocumentEmpty, " \n"); - // Expect either an object or array at root - TEST_ERROR("null"); - TEST_ERROR("true"); - TEST_ERROR("false"); - TEST_ERROR("\"s\""); - TEST_ERROR("0"); + // The document root must be either object or array. + TEST_ERROR(kParseErrorDocumentRootNotObjectOrArray, "null"); + TEST_ERROR(kParseErrorDocumentRootNotObjectOrArray, "true"); + TEST_ERROR(kParseErrorDocumentRootNotObjectOrArray, "false"); + TEST_ERROR(kParseErrorDocumentRootNotObjectOrArray, "\"s\""); + TEST_ERROR(kParseErrorDocumentRootNotObjectOrArray, "0"); - // Nothing should follow the root object or array - TEST_ERROR("[] 0"); - TEST_ERROR("{} 0"); + // The document root must not follow by other values. + TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0"); + TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0"); +} - // Invalid value - TEST_ERROR("nulL"); - TEST_ERROR("truE"); - TEST_ERROR("falsE"); +TEST(Reader, ParseValue_Error) { + // Invalid value. + TEST_ERROR(kParseErrorValueInvalid, "[nulL]"); + TEST_ERROR(kParseErrorValueInvalid, "[truE]"); + TEST_ERROR(kParseErrorValueInvalid, "[falsE]"); + TEST_ERROR(kParseErrorValueInvalid, "[a]"); + TEST_ERROR(kParseErrorValueInvalid, "[.1]"); +} + +TEST(Reader, ParseObject_Error) { + // Missing a name for object member. + TEST_ERROR(kParseErrorObjectMissName, "{1}"); + TEST_ERROR(kParseErrorObjectMissName, "{:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{null:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{true:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{false:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{1:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{[]:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{{}:1}"); + TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}"); + + // Missing a colon after a name of object member. + TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}"); + TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}"); + + // Must be a comma or '}' after an object member + TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{"); + TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{]"); + TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]"); +} #undef TEST_ERROR -} TEST(Reader, SkipWhitespace) { StringStream ss(" A \t\tB\n \n\nC\r\r \rD \t\n\r E"); @@ -592,4 +604,4 @@ TEST(Reader, SkipWhitespace) { SkipWhitespace(ss); EXPECT_EQ(expected[i], ss.Take()); } -} \ No newline at end of file +} From b0059483c8e57489083a01b07e877226367a1fce Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 27 Jun 2014 22:13:02 +0800 Subject: [PATCH 4/6] Error can only be assigned once --- include/rapidjson/reader.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 5483bd7..ff4b098 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -23,11 +23,10 @@ #ifndef RAPIDJSON_PARSE_ERROR_NORETURN #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ - if (!HasParseError()) {\ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ parseErrorCode_ = parseErrorCode; \ - errorOffset_ = offset; \ - }\ -RAPIDJSON_MULTILINEMACRO_END + errorOffset_ = offset; \ + RAPIDJSON_MULTILINEMACRO_END #endif #ifndef RAPIDJSON_PARSE_ERROR From 69ca7487bce6a86f08f5c0af2c28ce75018e68e0 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 27 Jun 2014 22:27:18 +0800 Subject: [PATCH 5/6] Manually merge the segfault fix from main branch and fix several unit tests about error code --- include/rapidjson/reader.h | 8 ++++++++ test/unittest/readertest.cpp | 12 +++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index ff4b098..7c60f3c 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -269,12 +269,16 @@ public: case '[': ParseArray(is, handler); break; default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); } + if (HasParseError()) + goto out; + SkipWhitespace(is); if (is.Peek() != '\0') RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); } + out: stack_.Clear(); return !HasParseError(); } @@ -440,6 +444,8 @@ private: if (parseFlags & kParseInsituFlag) { Ch *head = s.PutBegin(); ParseStringToStream(s, s); + if (HasParseError()) + return; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false); @@ -447,6 +453,8 @@ private: else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); + if (HasParseError()) + return; handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true); } is = s; // Restore is diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 88ed9a0..06b694e 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -335,8 +335,8 @@ TEST(Reader, ParseString_Error) { TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]"); // The surrogate pair in string is invalid. - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800X\"]"); - TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFFF\"]"); + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]"); + TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]"); // Missing a closing quotation mark in string. TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]"); @@ -352,7 +352,7 @@ TEST(Reader, ParseString_Error) { e[2] = c; ParseErrorCode error = TestString(e); EXPECT_EQ(kParseErrorStringInvalidEncoding, error); - if (error != kParseErrorNone) + if (error != kParseErrorStringInvalidEncoding) std::cout << (unsigned)(unsigned char)c << std::endl; } } @@ -443,8 +443,8 @@ TEST(Reader, ParseArray_Error) { } // Missing a comma or ']' after an array element. - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "["); - TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[}"); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1"); + TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}"); TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]"); #undef TEST_ARRAY_ERROR @@ -590,8 +590,6 @@ TEST(Reader, ParseObject_Error) { TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}"); // Must be a comma or '}' after an object member - TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{"); - TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{]"); TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]"); } From c14c5ff23627c2671799991d65527695c625c7d8 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 27 Jun 2014 22:43:21 +0800 Subject: [PATCH 6/6] Documentation on error related files and include dependent header. --- include/rapidjson/error/en.h | 7 +++++++ include/rapidjson/error/error.h | 34 +++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index dc6dc48..45017d3 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -5,6 +5,13 @@ namespace rapidjson { +//! Maps error code of parsing into error message. +/*! + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { switch (parseErrorCode) { case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index a24178a..ba45e7e 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -1,24 +1,46 @@ #ifndef RAPIDJSON_ERROR_ERROR_H__ #define RAPIDJSON_ERROR_ERROR_H__ -// For example, on Windows, user can define this macro as TCHAR +#include "../reader.h" // ParseErrorCode + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! The default charater type is char. + On Windows, user can define this macro as TCHAR for supporting both + unicode/non-unicode settings. +*/ #ifndef RAPIDJSON_ERROR_CHARTYPE #define RAPIDJSON_ERROR_CHARTYPE char #endif -// For example, on Windows, user can define this macro as _T(x) +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to RAPIDJSON_ERROR_CHARTYPE[]. +/*! By default this conversion macro does nothing. + On Windows, user can define this macro as _T(x) for supporting both + unicode/non-unicode settings. +*/ #ifndef RAPIDJSON_ERROR_STRING #define RAPIDJSON_ERROR_STRING(x) x #endif namespace rapidjson { -// User can dynamically change locale in runtime, e.g.: -// GetParseErrorFunc GetParseError = GetParseError_En; // or whatever -// const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +//! 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.: + +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); } // namespace rapidjson -#endif // RAPIDJSON_ERROR_ERROR_H__ \ No newline at end of file +#endif // RAPIDJSON_ERROR_ERROR_H__