Browse Source

Util: Improve JSON parsing and add error messages

master
Riyyi 3 years ago
parent
commit
fdeba07fd4
  1. 93
      src/util/json/parser.cpp
  2. 1
      src/util/json/parser.h

93
src/util/json/parser.cpp

@ -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;
} }
} }

1
src/util/json/parser.h

@ -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);

Loading…
Cancel
Save