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