diff options
-rw-r--r-- | llvm/include/llvm/Support/JSONParser.h | 19 | ||||
-rw-r--r-- | llvm/lib/Support/JSONParser.cpp | 59 | ||||
-rw-r--r-- | llvm/unittests/Support/JSONParserTest.cpp | 55 | ||||
-rw-r--r-- | llvm/utils/json-bench/JSONBench.cpp | 3 |
4 files changed, 61 insertions, 75 deletions
diff --git a/llvm/include/llvm/Support/JSONParser.h b/llvm/include/llvm/Support/JSONParser.h index f4cdfa56088..f7cc1190cd7 100644 --- a/llvm/include/llvm/Support/JSONParser.h +++ b/llvm/include/llvm/Support/JSONParser.h @@ -23,6 +23,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SourceMgr.h" namespace llvm { @@ -67,7 +68,7 @@ public: /// /// Parsing is started via parseRoot(). Access to the object returned from /// parseRoot() will parse the input lazily. - JSONParser(StringRef Input); + JSONParser(StringRef Input, SourceMgr *SM); /// \brief Returns the outermost JSON value (either an array or an object). /// @@ -90,9 +91,6 @@ public: /// iterating over the result of 'parseRoot', 'failed' will return true. bool failed() const; - /// \brief Returns an error message when 'failed' returns true. - std::string getErrorMessage() const; - private: /// \brief These methods manage the implementation details of parsing new JSON /// atoms. @@ -147,13 +145,20 @@ private: BumpPtrAllocator ValueAllocator; /// \brief The original input to the parser. - const StringRef Input; + MemoryBuffer *InputBuffer; + + /// \brief The source manager used for diagnostics and buffer management. + SourceMgr *SM; /// \brief The current position in the parse stream. StringRef::iterator Position; - /// \brief If non-empty, an error has occurred. - std::string ErrorMessage; + /// \brief The end position for fast EOF checks without introducing + /// unnecessary dereferences. + StringRef::iterator End; + + /// \brief If true, an error has occurred. + bool Failed; template <typename AtomT, char StartChar, char EndChar, JSONAtom::Kind ContainerKind> diff --git a/llvm/lib/Support/JSONParser.cpp b/llvm/lib/Support/JSONParser.cpp index 994ca9fc966..205ac05c6f3 100644 --- a/llvm/lib/Support/JSONParser.cpp +++ b/llvm/lib/Support/JSONParser.cpp @@ -15,14 +15,20 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/MemoryBuffer.h" using namespace llvm; -JSONParser::JSONParser(StringRef Input) - : Input(Input), Position(Input.begin()) {} +JSONParser::JSONParser(StringRef Input, SourceMgr *SM) + : SM(SM), Failed(false) { + InputBuffer = MemoryBuffer::getMemBuffer(Input, "JSON"); + SM->AddNewSourceBuffer(InputBuffer, SMLoc()); + End = InputBuffer->getBuffer().end(); + Position = InputBuffer->getBuffer().begin(); +} JSONValue *JSONParser::parseRoot() { - if (Position != Input.begin()) + if (Position != InputBuffer->getBuffer().begin()) report_fatal_error("Cannot resuse JSONParser."); if (isWhitespace()) nextNonWhitespace(); @@ -40,7 +46,11 @@ JSONValue *JSONParser::parseRoot() { } bool JSONParser::validate() { - return skip(*parseRoot()); + JSONValue *Root = parseRoot(); + if (Root == NULL) { + return false; + } + return skip(*Root); } bool JSONParser::skip(const JSONAtom &Atom) { @@ -55,22 +65,23 @@ bool JSONParser::skip(const JSONAtom &Atom) { } // Sets the current error to: -// "Error while parsing JSON: expected <Expected>, but found <Found>". +// "expected <Expected>, but found <Found>". void JSONParser::setExpectedError(StringRef Expected, StringRef Found) { - ErrorMessage = ("Error while parsing JSON: expected " + - Expected + ", but found " + Found + ".").str(); + SM->PrintMessage(SMLoc::getFromPointer(Position), SourceMgr::DK_Error, + "expected " + Expected + ", but found " + Found + ".", ArrayRef<SMRange>()); + Failed = true; } // Sets the current error to: -// "Error while parsing JSON: expected <Expected>, but found <Found>". +// "expected <Expected>, but found <Found>". void JSONParser::setExpectedError(StringRef Expected, char Found) { - setExpectedError(Expected, StringRef(&Found, 1)); + setExpectedError(Expected, ("'" + StringRef(&Found, 1) + "'").str()); } // If there is no character available, returns true and sets the current error -// to: "Error while parsing JSON: expected <Expected>, but found EOF.". +// to: "expected <Expected>, but found EOF.". bool JSONParser::errorIfAtEndOfFile(StringRef Expected) { - if (Position == Input.end()) { + if (Position == End) { setExpectedError(Expected, "EOF"); return true; } @@ -78,12 +89,12 @@ bool JSONParser::errorIfAtEndOfFile(StringRef Expected) { } // Sets the current error if the current character is not C to: -// "Error while parsing JSON: expected 'C', but got <current character>". +// "expected 'C', but got <current character>". bool JSONParser::errorIfNotAt(char C, StringRef Message) { - if (Position == Input.end() || *Position != C) { + if (*Position != C) { std::string Expected = ("'" + StringRef(&C, 1) + "' " + Message).str(); - if (Position == Input.end()) + if (Position == End) setExpectedError(Expected, "EOF"); else setExpectedError(Expected, *Position); @@ -113,7 +124,7 @@ static bool wasEscaped(StringRef::iterator First, // Parses a JSONString, assuming that the current position is on a quote. JSONString *JSONParser::parseString() { - assert(Position != Input.end()); + assert(Position != End); assert(!isWhitespace()); if (errorIfNotAt('"', "at start of string")) return 0; @@ -136,9 +147,9 @@ JSONString *JSONParser::parseString() { // Step over the current quote. ++Position; // Find the next quote. - while (Position != Input.end() && *Position != '"') + while (Position != End && *Position != '"') ++Position; - if (errorIfAtEndOfFile("\" at end of string")) + if (errorIfAtEndOfFile("'\"' at end of string")) return 0; // Repeat until the previous character was not a '\' or was an escaped // backslash. @@ -158,22 +169,18 @@ void JSONParser::nextNonWhitespace() { // Checks if there is a whitespace character at the current position. bool JSONParser::isWhitespace() { - return Position != Input.end() && (*Position == ' ' || *Position == '\t' || - *Position == '\n' || *Position == '\r'); + return *Position == ' ' || *Position == '\t' || + *Position == '\n' || *Position == '\r'; } bool JSONParser::failed() const { - return !ErrorMessage.empty(); -} - -std::string JSONParser::getErrorMessage() const { - return ErrorMessage; + return Failed; } // Parses a JSONValue, assuming that the current position is at the first // character of the value. JSONValue *JSONParser::parseValue() { - assert(Position != Input.end()); + assert(Position != End); assert(!isWhitespace()); switch (*Position) { case '[': @@ -191,7 +198,7 @@ JSONValue *JSONParser::parseValue() { // Parses a JSONKeyValuePair, assuming that the current position is at the first // character of the key, value pair. JSONKeyValuePair *JSONParser::parseKeyValuePair() { - assert(Position != Input.end()); + assert(Position != End); assert(!isWhitespace()); JSONString *Key = parseString(); diff --git a/llvm/unittests/Support/JSONParserTest.cpp b/llvm/unittests/Support/JSONParserTest.cpp index 1cd987daf1e..e9efb817c29 100644 --- a/llvm/unittests/Support/JSONParserTest.cpp +++ b/llvm/unittests/Support/JSONParserTest.cpp @@ -14,57 +14,28 @@ namespace llvm { -// Returns a buffer that contains the content of the given string without -// the trailing zero, in order to get valgrind to catch out-of-bound reads. -static std::vector<char> CutTrailingZero(StringRef String) { - std::vector<char> InputWithoutZero(String.size()); - memcpy(&InputWithoutZero[0], String.data(), String.size()); - return InputWithoutZero; -} - // Checks that the given input gives a parse error. Makes sure that an error // text is available and the parse fails. -static void ExpectParseError(StringRef Message, - const std::vector<char> &InputWithoutZero) { - StringRef Input = StringRef(&InputWithoutZero[0], InputWithoutZero.size()); - JSONParser Parser(Input); +static void ExpectParseError(StringRef Message, StringRef Input) { + SourceMgr SM; + JSONParser Parser(Input, &SM); EXPECT_FALSE(Parser.validate()) << Message << ": " << Input; EXPECT_TRUE(Parser.failed()) << Message << ": " << Input; - EXPECT_FALSE(Parser.getErrorMessage().empty()) << Message << ": " << Input; -} - -// Overloads the above to allow using const char * as Input. -static void ExpectParseError(StringRef Message, StringRef Input) { - return ExpectParseError(Message, CutTrailingZero(Input)); } // Checks that the given input can be parsed without error. -static void ExpectParseSuccess(StringRef Message, - const std::vector<char> &InputWithoutZero) { - StringRef Input = StringRef(&InputWithoutZero[0], InputWithoutZero.size()); - JSONParser Parser(Input); - EXPECT_TRUE(Parser.validate()) - << Message << ": " << Input << " - " << Parser.getErrorMessage(); -} - -// Overloads the above to allow using const char * as Input. static void ExpectParseSuccess(StringRef Message, StringRef Input) { - return ExpectParseSuccess(Message, CutTrailingZero(Input)); + SourceMgr SM; + JSONParser Parser(Input, &SM); + EXPECT_TRUE(Parser.validate()) << Message << ": " << Input; } TEST(JSONParser, FailsOnEmptyString) { - JSONParser Parser(""); - EXPECT_EQ(NULL, Parser.parseRoot()); + ExpectParseError("Empty JSON text", ""); } - -TEST(JSONParser, DoesNotReadAfterInput) { - JSONParser Parser(llvm::StringRef(NULL, 0)); - EXPECT_EQ(NULL, Parser.parseRoot()); -} - + TEST(JSONParser, FailsIfStartsWithString) { - JSONParser Character("\"x\""); - EXPECT_EQ(NULL, Character.parseRoot()); + ExpectParseError("Top-level string", "\"x\""); } TEST(JSONParser, ParsesEmptyArray) { @@ -177,11 +148,12 @@ TEST(JSONParser, HandlesEndOfFileGracefully) { // of an array. static void ExpectCanParseString(StringRef String) { std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str(); - JSONParser Parser(StringInArray); + SourceMgr SM; + JSONParser Parser(StringInArray, &SM); const JSONArray *ParsedArray = dyn_cast<JSONArray>(Parser.parseRoot()); StringRef ParsedString = dyn_cast<JSONString>(*ParsedArray->begin())->getRawText(); - EXPECT_EQ(String, ParsedString.str()) << Parser.getErrorMessage(); + EXPECT_EQ(String, ParsedString.str()); } // Checks that parsing the given string inside an array fails. @@ -210,7 +182,8 @@ TEST(JSONParser, ParsesStrings) { } TEST(JSONParser, WorksWithIteratorAlgorithms) { - JSONParser Parser("[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"]"); + SourceMgr SM; + JSONParser Parser("[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"]", &SM); const JSONArray *Array = dyn_cast<JSONArray>(Parser.parseRoot()); EXPECT_EQ(6, std::distance(Array->begin(), Array->end())); } diff --git a/llvm/utils/json-bench/JSONBench.cpp b/llvm/utils/json-bench/JSONBench.cpp index be2d8d027ef..ca8a36a03ab 100644 --- a/llvm/utils/json-bench/JSONBench.cpp +++ b/llvm/utils/json-bench/JSONBench.cpp @@ -41,7 +41,8 @@ void benchmark(llvm::TimerGroup &Group, llvm::StringRef Name, llvm::Timer Parsing((Name + ": Parsing").str(), Group); Parsing.startTimer(); - llvm::JSONParser Parser(JSONText); + llvm::SourceMgr SM; + llvm::JSONParser Parser(JSONText, &SM); if (!Parser.validate()) { llvm::errs() << "Parsing error in JSON parser benchmark.\n"; exit(1); |