| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 | /* * JsonParser.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */#include "StdInc.h"#include "JsonParser.h"#include "../ScopeGuard.h"#include "../texts/TextOperations.h"#include "JsonFormatException.h"VCMI_LIB_NAMESPACE_BEGINJsonParser::JsonParser(const std::byte * inputString, size_t stringSize, const JsonParsingSettings & settings)	: settings(settings)	, input(reinterpret_cast<const char *>(inputString), stringSize)	, lineCount(1)	, currentDepth(0)	, lineStart(0)	, pos(0){}JsonNode JsonParser::parse(const std::string & fileName){	JsonNode root;	if(input.empty())	{		error("File is empty", false);	}	else	{		if(!TextOperations::isValidUnicodeString(input.data(), input.size()))			error("Not a valid UTF-8 file", false);		// If file starts with BOM - skip it		uint32_t firstCharacter = TextOperations::getUnicodeCodepoint(input.data(), input.size());		if (firstCharacter == 0xFEFF)			pos += TextOperations::getUnicodeCharacterSize(input[0]);		extractValue(root);		extractWhitespace(false);		//Warn if there are any non-whitespace symbols left		if(pos < input.size())			error("Not all file was parsed!", true);	}	if(!errors.empty())	{		logMod->warn("%s is not valid JSON!", fileName);		logMod->warn(errors);	}	return root;}bool JsonParser::isValid(){	return errors.empty();}bool JsonParser::extractSeparator(){	if(!extractWhitespace())		return false;	if(input[pos] != ':')		return error("Separator expected");	pos++;	return true;}bool JsonParser::extractValue(JsonNode & node){	if(!extractWhitespace())		return false;	switch(input[pos])	{		case '\"':		case '\'':			return extractString(node);		case 'n':			return extractNull(node);		case 't':			return extractTrue(node);		case 'f':			return extractFalse(node);		case '{':			return extractStruct(node);		case '[':			return extractArray(node);		case '-':		case '+':		case '.':			return extractFloat(node);		default:		{			if(input[pos] >= '0' && input[pos] <= '9')				return extractFloat(node);			return error("Value expected!");		}	}}bool JsonParser::extractWhitespace(bool verbose){	//TODO: JSON5 - C-style multi-line comments	//TODO: JSON5 - Additional white space characters are allowed	while(true)	{		while(pos < input.size() && static_cast<ui8>(input[pos]) <= ' ')		{			if(input[pos] == '\n')			{				lineCount++;				lineStart = pos + 1;			}			pos++;		}		if(pos >= input.size() || input[pos] != '/')			break;		if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON)			error("Comments are not permitted in json!", true);		pos++;		if(pos == input.size())			break;		if(input[pos] == '/')			pos++;		else			error("Comments must consist of two slashes!", true);		while(pos < input.size() && input[pos] != '\n')			pos++;	}	if(pos >= input.size() && verbose)		return error("Unexpected end of file!");	return true;}bool JsonParser::extractEscaping(std::string & str){	// TODO: support unicode escaping:	// \u1234	switch(input[pos])	{		case '\r':			if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON5 && input.size() > pos && input[pos+1] == '\n')			{				pos += 2;				return true;			}			break;		case '\n':			if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON5)			{				pos += 1;				return true;			}			break;		case '\"':			str += '\"';			pos++;			return true;		case '\\':			str += '\\';			pos++;			return true;		case 'b':			str += '\b';			pos++;			return true;		case 'f':			str += '\f';			pos++;			return true;		case 'n':			str += '\n';			pos++;			return true;		case 'r':			str += '\r';			pos++;			return true;		case 't':			str += '\t';			pos++;			return true;		case '/':			str += '/';			pos++;			return true;	}	return error("Unknown escape sequence!", true);}bool JsonParser::extractString(std::string & str){	if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)	{		if(input[pos] != '\"')			return error("String expected!");	}	else	{		if(input[pos] != '\"' && input[pos] != '\'')			return error("String expected!");	}	char lineTerminator = input[pos];	pos++;	size_t first = pos;	while(pos != input.size())	{		if(input[pos] == lineTerminator) // Correct end of string		{			str.append(&input[first], pos - first);			pos++;			return true;		}		else if(input[pos] == '\\') // Escaping		{			str.append(&input[first], pos - first);			pos++;			if(pos == input.size())				break;			extractEscaping(str);			first = pos;		}		else if(input[pos] == '\n') // end-of-line		{			str.append(&input[first], pos - first);			return error("Closing quote not found!", true);		}		else if(static_cast<unsigned char>(input[pos]) < ' ') // control character		{			str.append(&input[first], pos - first);			pos++;			first = pos;			error("Illegal character in the string!", true);		}		else			pos++;	}	return error("Unterminated string!");}bool JsonParser::extractString(JsonNode & node){	std::string str;	if(!extractString(str))		return false;	node.setType(JsonNode::JsonType::DATA_STRING);	node.String() = str;	return true;}bool JsonParser::extractLiteral(std::string & literal){	while(pos < input.size())	{		bool isUpperCase = input[pos] >= 'A' && input[pos] <= 'Z';		bool isLowerCase = input[pos] >= 'a' && input[pos] <= 'z';		bool isNumber = input[pos] >= '0' && input[pos] <= '9';		if(!isUpperCase && !isLowerCase && !isNumber)			break;		literal += input[pos];		pos++;	}	return true;}bool JsonParser::extractAndCompareLiteral(const std::string & expectedLiteral){	std::string literal;	if(!extractLiteral(literal))		return false;	if(literal != expectedLiteral)	{		return error("Expected " + expectedLiteral + ", but unknown literal found", true);		return false;	}	return true;}bool JsonParser::extractNull(JsonNode & node){	if(!extractAndCompareLiteral("null"))		return false;	node.clear();	return true;}bool JsonParser::extractTrue(JsonNode & node){	if(!extractAndCompareLiteral("true"))		return false;	node.Bool() = true;	return true;}bool JsonParser::extractFalse(JsonNode & node){	if(!extractAndCompareLiteral("false"))		return false;	node.Bool() = false;	return true;}bool JsonParser::extractStruct(JsonNode & node){	node.setType(JsonNode::JsonType::DATA_STRUCT);	if(currentDepth > settings.maxDepth)		error("Maximum allowed depth of json structure has been reached", true);	pos++;	currentDepth++;	auto guard = vstd::makeScopeGuard([this]()	{		currentDepth--;	});	if(!extractWhitespace())		return false;	//Empty struct found	if(input[pos] == '}')	{		pos++;		return true;	}	while(true)	{		if(!extractWhitespace())			return false;		bool overrideFlag = false;		std::string key;		if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)		{			if(!extractString(key))				return false;		}		else		{			if(input[pos] == '\'' || input[pos] == '\"')			{				if(!extractString(key))					return false;			}			else			{				if(!extractLiteral(key))					return false;			}		}		if(key.find('#') != std::string::npos)		{			// split key string into actual key and meta-flags			std::vector<std::string> keyAndFlags;			boost::split(keyAndFlags, key, boost::is_any_of("#"));			key = keyAndFlags[0];			for(int i = 1; i < keyAndFlags.size(); i++)			{				if(keyAndFlags[i] == "override")					overrideFlag = true;				else					error("Encountered unknown flag #" + keyAndFlags[i], true);			}		}		if(node.Struct().find(key) != node.Struct().end())			error("Duplicate element encountered!", true);		if(!extractSeparator())			return false;		if(!extractElement(node.Struct()[key], '}'))			return false;		node.Struct()[key].setOverrideFlag(overrideFlag);		if(input[pos] == '}')		{			pos++;			return true;		}	}}bool JsonParser::extractArray(JsonNode & node){	if(currentDepth > settings.maxDepth)		error("Macimum allowed depth of json structure has been reached", true);	currentDepth++;	auto guard = vstd::makeScopeGuard([this]()	{		currentDepth--;	});	pos++;	node.setType(JsonNode::JsonType::DATA_VECTOR);	if(!extractWhitespace())		return false;	//Empty array found	if(input[pos] == ']')	{		pos++;		return true;	}	while(true)	{		//NOTE: currently 50% of time is this vector resizing.		//May be useful to use list during parsing and then swap() all items to vector		node.Vector().resize(node.Vector().size() + 1);		if(!extractElement(node.Vector().back(), ']'))			return false;		if(input[pos] == ']')		{			pos++;			return true;		}	}}bool JsonParser::extractElement(JsonNode & node, char terminator){	if(!extractValue(node))		return false;	if(!extractWhitespace())		return false;	bool comma = (input[pos] == ',');	if(comma)	{		pos++;		if(!extractWhitespace())			return false;	}	if(input[pos] == terminator)	{		if(comma && settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)			error("Extra comma found!", true);		return true;	}	if(!comma)		error("Comma expected!", true);	return true;}bool JsonParser::extractFloat(JsonNode & node){	//TODO: JSON5 - hexacedimal support	//TODO: JSON5 - Numbers may be IEEE 754 positive infinity, negative infinity, and NaN (why?)	assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));	bool negative = false;	double result = 0;	si64 integerPart = 0;	bool isFloat = false;	if(input[pos] == '+')	{		if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)			error("Positive numbers should not have plus sign!", true);		pos++;	}	else if(input[pos] == '-')	{		pos++;		negative = true;	}	if(input[pos] < '0' || input[pos] > '9')	{		if(input[pos] != '.' && settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)			return error("Number expected!");	}	//Extract integer part	while(input[pos] >= '0' && input[pos] <= '9')	{		integerPart = integerPart * 10 + (input[pos] - '0');		pos++;	}	result = static_cast<double>(integerPart);	if(input[pos] == '.')	{		//extract fractional part		isFloat = true;		pos++;		double fractMult = 0.1;		if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5 && (input[pos] < '0' || input[pos] > '9'))			return error("Decimal part expected!");		while(input[pos] >= '0' && input[pos] <= '9')		{			result = result + fractMult * (input[pos] - '0');			fractMult /= 10;			pos++;		}	}	if(input[pos] == 'e')	{		//extract exponential part		pos++;		isFloat = true;		bool powerNegative = false;		double power = 0;		if(input[pos] == '-')		{			pos++;			powerNegative = true;		}		else if(input[pos] == '+')		{			pos++;		}		if(input[pos] < '0' || input[pos] > '9')			return error("Exponential part expected!");		while(input[pos] >= '0' && input[pos] <= '9')		{			power = power * 10 + (input[pos] - '0');			pos++;		}		if(powerNegative)			power = -power;		result *= std::pow(10, power);	}	if(isFloat)	{		if(negative)			result = -result;		node.setType(JsonNode::JsonType::DATA_FLOAT);		node.Float() = result;	}	else	{		if(negative)			integerPart = -integerPart;		node.setType(JsonNode::JsonType::DATA_INTEGER);		node.Integer() = integerPart;	}	return true;}bool JsonParser::error(const std::string & message, bool warning){	if(settings.strict)		throw JsonFormatException(message);	std::ostringstream stream;	std::string type(warning ? " warning: " : " error: ");	if(!errors.empty())	{		// only add the line breaks between error messages so we don't have a trailing line break		stream << "\n";	}	stream << "At line " << lineCount << ", position " << pos - lineStart << type << message;	errors += stream.str();	return warning;}VCMI_LIB_NAMESPACE_END
 |