Util: Improve JSON parsing and add error messages
This commit is contained in:
+56
-37
@@ -69,14 +69,16 @@ Value Parser::parse()
|
|||||||
case Token::Type::BraceOpen:
|
case Token::Type::BraceOpen:
|
||||||
result = getObject();
|
result = getObject();
|
||||||
break;
|
break;
|
||||||
case Token::Type::Comma:
|
case Token::Type::BracketClose:
|
||||||
// Error!
|
m_job->printErrorLine(token, "expecting value, not ']'");
|
||||||
// Multiple JSON root elements
|
m_index++;
|
||||||
m_job->printErrorLine(token, "multiple root elements");
|
break;
|
||||||
|
case Token::Type::BraceClose:
|
||||||
|
m_job->printErrorLine(token, "expecting string, not '}'");
|
||||||
m_index++;
|
m_index++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Error!
|
m_job->printErrorLine(token, "multiple root elements");
|
||||||
m_index++;
|
m_index++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -92,6 +94,18 @@ Token Parser::peek()
|
|||||||
return (*m_tokens)[m_index];
|
return (*m_tokens)[m_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Parser::seekForward(Token::Type type)
|
||||||
|
{
|
||||||
|
for (size_t index = m_index; index < m_tokens->size(); ++index) {
|
||||||
|
if ((*m_tokens)[index].type == type) {
|
||||||
|
m_index = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Token Parser::consume()
|
Token Parser::consume()
|
||||||
{
|
{
|
||||||
Token token = peek();
|
Token token = peek();
|
||||||
@@ -111,11 +125,17 @@ bool Parser::consumeSpecific(Token::Type type)
|
|||||||
|
|
||||||
Value Parser::getArray()
|
Value Parser::getArray()
|
||||||
{
|
{
|
||||||
size_t index = m_index;
|
|
||||||
m_index++;
|
m_index++;
|
||||||
|
|
||||||
Value array;
|
auto reportError = [this](Token token, const std::string& message) -> void {
|
||||||
|
m_job->printErrorLine(token, message.c_str());
|
||||||
|
|
||||||
|
// After an error, try to find the closing bracket
|
||||||
|
seekForward(Token::Type::BracketClose);
|
||||||
|
consumeSpecific(Token::Type::BracketClose);
|
||||||
|
};
|
||||||
|
|
||||||
|
Value array = Value::Type::Array;
|
||||||
Token token;
|
Token token;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
token = consume();
|
token = consume();
|
||||||
@@ -149,11 +169,14 @@ Value Parser::getArray()
|
|||||||
array.emplace_back(getObject());
|
array.emplace_back(getObject());
|
||||||
}
|
}
|
||||||
else if (token.type == Token::Type::BracketClose) {
|
else if (token.type == Token::Type::BracketClose) {
|
||||||
|
// Trailing comma
|
||||||
|
if (array.asArray().size() > 0) {
|
||||||
|
reportError((*m_tokens)[m_index - 2], "invalid comma, expecting ']'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
// Error!
|
|
||||||
printf("Invalid JSON! array:1\n");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
reportError(token, "expecting value or ']', not '" + token.symbol + "'");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,8 +189,7 @@ Value Parser::getArray()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Error!
|
reportError(token, "expecting comma or ']', not '" + token.symbol + "'");
|
||||||
printf("Invalid JSON! array:2\n");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,50 +199,49 @@ Value Parser::getArray()
|
|||||||
|
|
||||||
Value Parser::getObject()
|
Value Parser::getObject()
|
||||||
{
|
{
|
||||||
size_t index = m_index;
|
|
||||||
m_index++;
|
m_index++;
|
||||||
|
|
||||||
Value object;
|
auto reportError = [this](Token token, const std::string& message) -> void {
|
||||||
|
m_job->printErrorLine(token, message.c_str());
|
||||||
|
|
||||||
|
// After an error, try to find the closing bracket
|
||||||
|
seekForward(Token::Type::BraceClose);
|
||||||
|
consumeSpecific(Token::Type::BraceClose);
|
||||||
|
};
|
||||||
|
|
||||||
|
Value object = Value::Type::Object;
|
||||||
Token token;
|
Token token;
|
||||||
std::string key;
|
std::string key;
|
||||||
std::map<std::string, uint32_t> unique;
|
std::map<std::string, uint32_t> unique;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Find string key
|
|
||||||
token = consume();
|
token = consume();
|
||||||
|
// Empty object
|
||||||
|
if (token.type == Token::Type::BraceClose) {
|
||||||
|
// Trailing comma
|
||||||
|
if (object.asObject().size() > 0) {
|
||||||
|
reportError((*m_tokens)[m_index - 2], "invalid comma, expecting '}'");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Find string key
|
||||||
if (token.type != Token::Type::String) {
|
if (token.type != Token::Type::String) {
|
||||||
// Error!
|
reportError(token, "expecting string, or '}' not '" + token.symbol + "'");
|
||||||
printf("Invalid JSON! 1\n");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if key exists in hashmap
|
// Check if key exists in hashmap
|
||||||
key = token.symbol;
|
key = token.symbol;
|
||||||
if (unique.find(key) != unique.end()) {
|
if (unique.find(key) != unique.end()) {
|
||||||
// If exists, unique key fail!
|
reportError(token, "duplicate key '" + token.symbol + "', names should be unique");
|
||||||
// Error!
|
|
||||||
printf("Invalid JSON! 2\n");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Add key to hashmap
|
// Add key to hashmap
|
||||||
unique.insert({ key, 0 });
|
unique.insert({ key, 0 });
|
||||||
|
|
||||||
// Find :
|
// Find :
|
||||||
if (!consumeSpecific(Token::Type::Colon)) {
|
|
||||||
// Error!
|
|
||||||
printf("Invalid JSON! 3\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find string/number/literal value
|
|
||||||
token = consume();
|
token = consume();
|
||||||
if (token.type != Token::Type::String
|
if (token.type != Token::Type::Colon) {
|
||||||
&& token.type != Token::Type::Number
|
reportError(token, "expecting colon, not '" + token.symbol + "'");
|
||||||
&& token.type != Token::Type::Literal
|
|
||||||
&& token.type != Token::Type::BracketOpen
|
|
||||||
&& token.type != Token::Type::BraceOpen) {
|
|
||||||
// Error!
|
|
||||||
printf("Invalid JSON! 4\n");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,10 +274,9 @@ Value Parser::getObject()
|
|||||||
else if (token.type == Token::Type::BraceOpen) {
|
else if (token.type == Token::Type::BraceOpen) {
|
||||||
m_index--;
|
m_index--;
|
||||||
object[key] = getObject();
|
object[key] = getObject();
|
||||||
// Error!
|
|
||||||
printf("Invalid JSON! 5\n");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
reportError(token, "expecting value, not '" + token.symbol + "'");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +289,7 @@ Value Parser::getObject()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Error!
|
reportError(token, "expecting comma or '}', not '" + token.symbol + "'");
|
||||||
printf("Invalid JSON! 6\n");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Token peek();
|
Token peek();
|
||||||
|
bool seekForward(Token::Type type);
|
||||||
|
|
||||||
Token consume();
|
Token consume();
|
||||||
bool consumeSpecific(Token::Type type);
|
bool consumeSpecific(Token::Type type);
|
||||||
|
|||||||
Reference in New Issue
Block a user