mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Partial support for json5
This commit is contained in:
		| @@ -23,10 +23,10 @@ struct JsonParsingSettings | ||||
| 	{ | ||||
| 		JSON, // strict implementation of json format | ||||
| 		JSONC, // json format that also allows comments that start from '//' | ||||
| 		//JSON5 // TODO? | ||||
| 		JSON5 // Partial support of 'json5' format | ||||
| 	}; | ||||
|  | ||||
| 	JsonFormatMode mode = JsonFormatMode::JSONC; | ||||
| 	JsonFormatMode mode = JsonFormatMode::JSON5; | ||||
|  | ||||
| 	/// Maximum depth of elements | ||||
| 	uint32_t maxDepth = 30; | ||||
|   | ||||
| @@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN | ||||
| JsonParser::JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings): | ||||
| 	input(inputString, stringSize), | ||||
| 	settings(settings), | ||||
| 	currentDepth(0), | ||||
| 	lineCount(1), | ||||
| 	lineStart(0), | ||||
| 	pos(0) | ||||
| @@ -29,13 +30,13 @@ JsonNode JsonParser::parse(const std::string & fileName) | ||||
| { | ||||
| 	JsonNode root; | ||||
|  | ||||
| 	if (input.size() == 0) | ||||
| 	if (input.empty()) | ||||
| 	{ | ||||
| 		error("File is empty", false); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (!TextOperations::isValidUnicodeString(&input[0], input.size())) | ||||
| 		if (!TextOperations::isValidUnicodeString(input.data(), input.size())) | ||||
| 			error("Not a valid UTF-8 file", false); | ||||
|  | ||||
| 		extractValue(root); | ||||
| @@ -96,6 +97,9 @@ bool JsonParser::extractValue(JsonNode &node) | ||||
|  | ||||
| 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]) <= ' ') | ||||
| @@ -133,6 +137,9 @@ bool JsonParser::extractWhitespace(bool verbose) | ||||
|  | ||||
| bool JsonParser::extractEscaping(std::string &str) | ||||
| { | ||||
| 	// TODO: support unicode escaping: | ||||
| 	// \u1234 | ||||
|  | ||||
| 	switch(input[pos]) | ||||
| 	{ | ||||
| 		break; case '\"': str += '\"'; | ||||
| @@ -150,15 +157,27 @@ bool JsonParser::extractEscaping(std::string &str) | ||||
|  | ||||
| bool JsonParser::extractString(std::string &str) | ||||
| { | ||||
| 	if (input[pos] != '\"') | ||||
| 		return error("String expected!"); | ||||
| 	//TODO: JSON5 - line breaks escaping | ||||
|  | ||||
| 	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] == '\"') // Correct end of string | ||||
| 		if (input[pos] == lineTerminator) // Correct end of string | ||||
| 		{ | ||||
| 			str.append( &input[first], pos-first); | ||||
| 			pos++; | ||||
| @@ -200,23 +219,43 @@ bool JsonParser::extractString(JsonNode &node) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool JsonParser::extractLiteral(const std::string &literal) | ||||
| bool JsonParser::extractLiteral(std::string & literal) | ||||
| { | ||||
| 	if (literal.compare(0, literal.size(), &input[pos], literal.size()) != 0) | ||||
| 	while (pos < input.size() ) | ||||
| 	{ | ||||
| 		while (pos < input.size() && ((input[pos]>'a' && input[pos]<'z') | ||||
| 								   || (input[pos]>'A' && input[pos]<'Z'))) | ||||
| 			pos++; | ||||
| 		return error("Unknown literal found", true); | ||||
| 		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++; | ||||
| 	} | ||||
|  | ||||
| 	pos += literal.size(); | ||||
| 	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 (!extractLiteral("null")) | ||||
| 	if (!extractAndCompareLiteral("null")) | ||||
| 		return false; | ||||
|  | ||||
| 	node.clear(); | ||||
| @@ -225,7 +264,7 @@ bool JsonParser::extractNull(JsonNode &node) | ||||
|  | ||||
| bool JsonParser::extractTrue(JsonNode &node) | ||||
| { | ||||
| 	if (!extractLiteral("true")) | ||||
| 	if (!extractAndCompareLiteral("true")) | ||||
| 		return false; | ||||
|  | ||||
| 	node.Bool() = true; | ||||
| @@ -234,7 +273,7 @@ bool JsonParser::extractTrue(JsonNode &node) | ||||
|  | ||||
| bool JsonParser::extractFalse(JsonNode &node) | ||||
| { | ||||
| 	if (!extractLiteral("false")) | ||||
| 	if (!extractAndCompareLiteral("false")) | ||||
| 		return false; | ||||
|  | ||||
| 	node.Bool() = false; | ||||
| @@ -244,6 +283,11 @@ bool JsonParser::extractFalse(JsonNode &node) | ||||
| bool JsonParser::extractStruct(JsonNode &node) | ||||
| { | ||||
| 	node.setType(JsonNode::JsonType::DATA_STRUCT); | ||||
|  | ||||
| 	if (currentDepth > settings.maxDepth) | ||||
| 		error("Macimum allowed depth of json structure has been reached", true); | ||||
|  | ||||
| 	currentDepth++; | ||||
| 	pos++; | ||||
|  | ||||
| 	if (!extractWhitespace()) | ||||
| @@ -263,8 +307,25 @@ bool JsonParser::extractStruct(JsonNode &node) | ||||
|  | ||||
| 		bool overrideFlag = false; | ||||
| 		std::string key; | ||||
| 		if (!extractString(key)) | ||||
| 			return false; | ||||
|  | ||||
| 		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) | ||||
| 		{ | ||||
| @@ -304,6 +365,10 @@ bool JsonParser::extractStruct(JsonNode &node) | ||||
|  | ||||
| bool JsonParser::extractArray(JsonNode &node) | ||||
| { | ||||
| 	if (currentDepth > settings.maxDepth) | ||||
| 		error("Macimum allowed depth of json structure has been reached", true); | ||||
|  | ||||
| 	currentDepth++; | ||||
| 	pos++; | ||||
| 	node.setType(JsonNode::JsonType::DATA_VECTOR); | ||||
|  | ||||
| @@ -353,7 +418,10 @@ bool JsonParser::extractElement(JsonNode &node, char terminator) | ||||
| 	if (input[pos] == terminator) | ||||
| 	{ | ||||
| 		if (comma) | ||||
| 			error("Extra comma found!", true); | ||||
| 		{ | ||||
| 			if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) | ||||
| 				error("Extra comma found!", true); | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| @@ -365,20 +433,32 @@ bool JsonParser::extractElement(JsonNode &node, char terminator) | ||||
|  | ||||
| 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 (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') | ||||
| 		return error("Number expected!"); | ||||
| 	{ | ||||
| 		if (input[pos] != '.'  && settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) | ||||
| 			return error("Number expected!"); | ||||
| 	} | ||||
|  | ||||
| 	//Extract integer part | ||||
| 	while (input[pos] >= '0' && input[pos] <= '9') | ||||
| @@ -395,8 +475,12 @@ bool JsonParser::extractFloat(JsonNode &node) | ||||
| 		isFloat = true; | ||||
| 		pos++; | ||||
| 		double fractMult = 0.1; | ||||
| 		if (input[pos] < '0' || input[pos] > '9') | ||||
| 			return error("Decimal part expected!"); | ||||
|  | ||||
| 		if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) | ||||
| 		{ | ||||
| 			if (input[pos] < '0' || input[pos] > '9') | ||||
| 				return error("Decimal part expected!"); | ||||
| 		} | ||||
|  | ||||
| 		while (input[pos] >= '0' && input[pos] <= '9') | ||||
| 		{ | ||||
|   | ||||
| @@ -20,13 +20,15 @@ class JsonParser | ||||
|  | ||||
| 	std::string errors;     // Contains description of all encountered errors | ||||
| 	std::string_view input;      // Input data | ||||
| 	ui32 lineCount; // Currently parsed line, starting from 1 | ||||
| 	uint32_t lineCount; // Currently parsed line, starting from 1 | ||||
| 	uint32_t currentDepth; | ||||
| 	size_t lineStart;       // Position of current line start | ||||
| 	size_t pos;             // Current position of parser | ||||
|  | ||||
| 	//Helpers | ||||
| 	bool extractEscaping(std::string &str); | ||||
| 	bool extractLiteral(const std::string &literal); | ||||
| 	bool extractLiteral(std::string &literal); | ||||
| 	bool extractAndCompareLiteral(const std::string &expectedLiteral); | ||||
| 	bool extractString(std::string &string); | ||||
| 	bool extractWhitespace(bool verbose = true); | ||||
| 	bool extractSeparator(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user