mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-30 06:40:53 +00:00
Changes the JSON parser to use the SourceMgr.
Diagnostics are now emitted via the SourceMgr and we use MemoryBuffer for buffer management. Switched the code to make use of the trailing '0' that MemoryBuffer guarantees where it makes sense. llvm-svn: 147063
This commit is contained in:
parent
8c9b0dea02
commit
2147506d8b
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user