Browse Source

Implemented 'strict' json support

Ivan Savenko 1 year ago
parent
commit
41493d6f67

+ 1 - 0
lib/CMakeLists.txt

@@ -405,6 +405,7 @@ set(lib_HEADERS
 	filesystem/ResourcePath.h
 
 	json/JsonBonus.h
+	json/JsonFormatException.h
 	json/JsonNode.h
 	json/JsonParser.h
 	json/JsonRandom.h

+ 5 - 0
lib/json/JsonBonus.h

@@ -14,6 +14,11 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+struct Bonus;
+class ILimiter;
+class CSelector;
+class CAddInfo;
+
 namespace JsonUtils
 {
 	DLL_LINKAGE std::shared_ptr<Bonus> parseBonus(const JsonVector & ability_vec);

+ 20 - 0
lib/json/JsonFormatException.h

@@ -0,0 +1,20 @@
+/*
+ * JsonFormatException.h, 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
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class JsonFormatException : public std::runtime_error
+{
+public:
+	using runtime_error::runtime_error;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 8 - 4
lib/json/JsonNode.cpp

@@ -79,8 +79,12 @@ JsonNode::JsonNode(const std::string & string)
 {}
 
 JsonNode::JsonNode(const std::byte *data, size_t datasize)
+	:JsonNode(data, datasize, JsonParsingSettings())
+{}
+
+JsonNode::JsonNode(const std::byte *data, size_t datasize, const JsonParsingSettings & parserSettings)
 {
-	JsonParser parser(reinterpret_cast<const char*>(data), datasize);
+	JsonParser parser(reinterpret_cast<const char*>(data), datasize, parserSettings);
 	*this = parser.parse("<unknown>");
 }
 
@@ -88,7 +92,7 @@ JsonNode::JsonNode(const JsonPath & fileURI)
 {
 	auto file = CResourceHandler::get()->load(fileURI)->readAll();
 
-	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
+	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second, JsonParsingSettings());
 	*this = parser.parse(fileURI.getName());
 }
 
@@ -96,7 +100,7 @@ JsonNode::JsonNode(const JsonPath & fileURI, const std::string & idx)
 {
 	auto file = CResourceHandler::get(idx)->load(fileURI)->readAll();
 	
-	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
+	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second, JsonParsingSettings());
 	*this = parser.parse(fileURI.getName());
 }
 
@@ -104,7 +108,7 @@ JsonNode::JsonNode(const JsonPath & fileURI, bool &isValidSyntax)
 {
 	auto file = CResourceHandler::get()->load(fileURI)->readAll();
 
-	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
+	JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second, JsonParsingSettings());
 	*this = parser.parse(fileURI.getName());
 	isValidSyntax = parser.isValid();
 }

+ 18 - 4
lib/json/JsonNode.h

@@ -17,10 +17,23 @@ class JsonNode;
 using JsonMap = std::map<std::string, JsonNode>;
 using JsonVector = std::vector<JsonNode>;
 
-struct Bonus;
-class CSelector;
-class CAddInfo;
-class ILimiter;
+struct JsonParsingSettings
+{
+	enum class JsonFormatMode
+	{
+		JSON, // strict implementation of json format
+		JSONC, // json format that also allows comments that start from '//'
+		//JSON5 // TODO?
+	};
+
+	JsonFormatMode mode = JsonFormatMode::JSONC;
+
+	/// Maximum depth of elements
+	uint32_t maxDepth = 30;
+
+	/// If set to true, parser will throw on any encountered error
+	bool strict = false;
+};
 
 class DLL_LINKAGE JsonNode
 {
@@ -58,6 +71,7 @@ public:
 
 	/// Create tree from Json-formatted input
 	explicit JsonNode(const std::byte * data, size_t datasize);
+	explicit JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings);
 
 	/// Create tree from JSON file
 	explicit JsonNode(const JsonPath & fileURI);

+ 12 - 4
lib/json/JsonParser.cpp

@@ -11,12 +11,14 @@
 #include "StdInc.h"
 #include "JsonParser.h"
 
+#include "JsonFormatException.h"
 #include "../TextOperations.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-JsonParser::JsonParser(const char * inputString, size_t stringSize):
+JsonParser::JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings):
 	input(inputString, stringSize),
+	settings(settings),
 	lineCount(1),
 	lineStart(0),
 	pos(0)
@@ -105,9 +107,13 @@ bool JsonParser::extractWhitespace(bool verbose)
 			}
 			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;
@@ -346,9 +352,8 @@ bool JsonParser::extractElement(JsonNode &node, char terminator)
 
 	if (input[pos] == terminator)
 	{
-		//FIXME: MOD COMPATIBILITY: Too many of these right now, re-enable later
-		//if (comma)
-			//error("Extra comma found!", true);
+		if (comma)
+			error("Extra comma found!", true);
 		return true;
 	}
 
@@ -456,6 +461,9 @@ bool JsonParser::extractFloat(JsonNode &node)
 
 bool JsonParser::error(const std::string &message, bool warning)
 {
+	if (settings.strict)
+		throw JsonFormatException(message);
+
 	std::ostringstream stream;
 	std::string type(warning?" warning: ":" error: ");
 

+ 3 - 1
lib/json/JsonParser.h

@@ -16,6 +16,8 @@ VCMI_LIB_NAMESPACE_BEGIN
 //Internal class for string -> JsonNode conversion
 class JsonParser
 {
+	const JsonParsingSettings settings;
+
 	std::string errors;     // Contains description of all encountered errors
 	std::string_view input;      // Input data
 	ui32 lineCount; // Currently parsed line, starting from 1
@@ -44,7 +46,7 @@ class JsonParser
 	bool error(const std::string &message, bool warning=false);
 
 public:
-	JsonParser(const char * inputString, size_t stringSize);
+	JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings);
 
 	/// do actual parsing. filename is name of file that will printed to console if any errors were found
 	JsonNode parse(const std::string & fileName);

+ 2 - 0
lib/spells/effects/Moat.h

@@ -14,6 +14,8 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+struct Bonus;
+
 namespace spells
 {
 namespace effects