1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-02 00:10:22 +02:00

Unify formatting

This commit is contained in:
Ivan Savenko 2024-02-13 19:16:35 +02:00
parent 2632ab04f5
commit 18bbccd167
7 changed files with 315 additions and 261 deletions

View File

@ -11,9 +11,9 @@
#include "StdInc.h" #include "StdInc.h"
#include "JsonNode.h" #include "JsonNode.h"
#include "filesystem/Filesystem.h"
#include "JsonParser.h" #include "JsonParser.h"
#include "JsonWriter.h" #include "JsonWriter.h"
#include "filesystem/Filesystem.h"
namespace namespace
{ {
@ -40,7 +40,7 @@ Node & resolvePointer(Node & in, const std::string & pointer)
auto index = boost::lexical_cast<size_t>(entry); auto index = boost::lexical_cast<size_t>(entry);
if (in.Vector().size() > index) if(in.Vector().size() > index)
return in.Vector()[index].resolvePointer(remainer); return in.Vector()[index].resolvePointer(remainer);
} }
return in[entry].resolvePointer(remainer); return in[entry].resolvePointer(remainer);
@ -55,36 +55,43 @@ class CModHandler;
static const JsonNode nullNode; static const JsonNode nullNode;
JsonNode::JsonNode(bool boolean) JsonNode::JsonNode(bool boolean)
:data(boolean) : data(boolean)
{} {
}
JsonNode::JsonNode(int32_t number) JsonNode::JsonNode(int32_t number)
:data(static_cast<int64_t>(number)) : data(static_cast<int64_t>(number))
{} {
}
JsonNode::JsonNode(uint32_t number) JsonNode::JsonNode(uint32_t number)
:data(static_cast<int64_t>(number)) : data(static_cast<int64_t>(number))
{} {
}
JsonNode::JsonNode(int64_t number) JsonNode::JsonNode(int64_t number)
:data(number) : data(number)
{} {
}
JsonNode::JsonNode(double number) JsonNode::JsonNode(double number)
:data(number) : data(number)
{} {
}
JsonNode::JsonNode(const std::string & string) JsonNode::JsonNode(const std::string & string)
:data(string) : data(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, parserSettings); }
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, parserSettings);
*this = parser.parse("<unknown>"); *this = parser.parse("<unknown>");
} }
@ -92,33 +99,33 @@ JsonNode::JsonNode(const JsonPath & fileURI)
{ {
auto file = CResourceHandler::get()->load(fileURI)->readAll(); auto file = CResourceHandler::get()->load(fileURI)->readAll();
JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second, JsonParsingSettings()); JsonParser parser(reinterpret_cast<char *>(file.first.get()), file.second, JsonParsingSettings());
*this = parser.parse(fileURI.getName()); *this = parser.parse(fileURI.getName());
} }
JsonNode::JsonNode(const JsonPath & fileURI, const std::string & idx) JsonNode::JsonNode(const JsonPath & fileURI, const std::string & idx)
{ {
auto file = CResourceHandler::get(idx)->load(fileURI)->readAll(); auto file = CResourceHandler::get(idx)->load(fileURI)->readAll();
JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second, JsonParsingSettings()); JsonParser parser(reinterpret_cast<char *>(file.first.get()), file.second, JsonParsingSettings());
*this = parser.parse(fileURI.getName()); *this = parser.parse(fileURI.getName());
} }
JsonNode::JsonNode(const JsonPath & fileURI, bool &isValidSyntax) JsonNode::JsonNode(const JsonPath & fileURI, bool & isValidSyntax)
{ {
auto file = CResourceHandler::get()->load(fileURI)->readAll(); auto file = CResourceHandler::get()->load(fileURI)->readAll();
JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second, JsonParsingSettings()); JsonParser parser(reinterpret_cast<char *>(file.first.get()), file.second, JsonParsingSettings());
*this = parser.parse(fileURI.getName()); *this = parser.parse(fileURI.getName());
isValidSyntax = parser.isValid(); isValidSyntax = parser.isValid();
} }
bool JsonNode::operator == (const JsonNode &other) const bool JsonNode::operator==(const JsonNode & other) const
{ {
return data == other.data; return data == other.data;
} }
bool JsonNode::operator != (const JsonNode &other) const bool JsonNode::operator!=(const JsonNode & other) const
{ {
return !(*this == other); return !(*this == other);
} }
@ -146,18 +153,20 @@ bool JsonNode::getOverrideFlag() const
void JsonNode::setModScope(const std::string & metadata, bool recursive) void JsonNode::setModScope(const std::string & metadata, bool recursive)
{ {
modScope = metadata; modScope = metadata;
if (recursive) if(recursive)
{ {
switch (getType()) switch(getType())
{ {
break; case JsonType::DATA_VECTOR: break;
case JsonType::DATA_VECTOR:
{ {
for(auto & node : Vector()) for(auto & node : Vector())
{ {
node.setModScope(metadata); node.setModScope(metadata);
} }
} }
break; case JsonType::DATA_STRUCT: break;
case JsonType::DATA_STRUCT:
{ {
for(auto & node : Struct()) for(auto & node : Struct())
{ {
@ -170,7 +179,7 @@ void JsonNode::setModScope(const std::string & metadata, bool recursive)
void JsonNode::setType(JsonType Type) void JsonNode::setType(JsonType Type)
{ {
if (getType() == Type) if(getType() == Type)
return; return;
//float<->int conversion //float<->int conversion
@ -190,13 +199,27 @@ void JsonNode::setType(JsonType Type)
//Set new node type //Set new node type
switch(Type) switch(Type)
{ {
break; case JsonType::DATA_NULL: data = JsonData(); case JsonType::DATA_NULL:
break; case JsonType::DATA_BOOL: data = JsonData(false); data = JsonData();
break; case JsonType::DATA_FLOAT: data = JsonData(static_cast<double>(0.0)); break;
break; case JsonType::DATA_STRING: data = JsonData(std::string()); case JsonType::DATA_BOOL:
break; case JsonType::DATA_VECTOR: data = JsonData(JsonVector()); data = JsonData(false);
break; case JsonType::DATA_STRUCT: data = JsonData(JsonMap()); break;
break; case JsonType::DATA_INTEGER: data = JsonData(static_cast<si64>(0)); case JsonType::DATA_FLOAT:
data = JsonData(static_cast<double>(0.0));
break;
case JsonType::DATA_STRING:
data = JsonData(std::string());
break;
case JsonType::DATA_VECTOR:
data = JsonData(JsonVector());
break;
case JsonType::DATA_STRUCT:
data = JsonData(JsonMap());
break;
case JsonType::DATA_INTEGER:
data = JsonData(static_cast<si64>(0));
break;
} }
} }
@ -229,18 +252,18 @@ bool JsonNode::containsBaseData() const
{ {
switch(getType()) switch(getType())
{ {
case JsonType::DATA_NULL: case JsonType::DATA_NULL:
return false; return false;
case JsonType::DATA_STRUCT: case JsonType::DATA_STRUCT:
for(const auto & elem : Struct()) for(const auto & elem : Struct())
{ {
if(elem.second.containsBaseData()) if(elem.second.containsBaseData())
return true; return true;
} }
return false; return false;
default: default:
//other types (including vector) cannot be extended via merge //other types (including vector) cannot be extended via merge
return true; return true;
} }
} }
@ -248,14 +271,14 @@ bool JsonNode::isCompact() const
{ {
switch(getType()) switch(getType())
{ {
case JsonType::DATA_VECTOR: case JsonType::DATA_VECTOR:
for(const JsonNode & elem : Vector()) for(const JsonNode & elem : Vector())
{ {
if(!elem.isCompact()) if(!elem.isCompact())
return false; return false;
} }
return true; return true;
case JsonType::DATA_STRUCT: case JsonType::DATA_STRUCT:
{ {
auto propertyCount = Struct().size(); auto propertyCount = Struct().size();
if(propertyCount == 0) if(propertyCount == 0)
@ -263,9 +286,9 @@ bool JsonNode::isCompact() const
else if(propertyCount == 1) else if(propertyCount == 1)
return Struct().begin()->second.isCompact(); return Struct().begin()->second.isCompact();
} }
return false; return false;
default: default:
return true; return true;
} }
} }
@ -285,7 +308,7 @@ bool JsonNode::TryBoolFromString(bool & success) const
if(success) if(success)
return true; return true;
success = boolParamStr == "false"; success = boolParamStr == "false";
} }
return false; return false;
@ -337,7 +360,7 @@ bool JsonNode::Bool() const
{ {
assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_BOOL); assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_BOOL);
if (getType() == JsonType::DATA_BOOL) if(getType() == JsonType::DATA_BOOL)
return std::get<bool>(data); return std::get<bool>(data);
return boolDefault; return boolDefault;
@ -376,7 +399,7 @@ const std::string & JsonNode::String() const
{ {
assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRING); assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRING);
if (getType() == JsonType::DATA_STRING) if(getType() == JsonType::DATA_STRING)
return std::get<std::string>(data); return std::get<std::string>(data);
return stringDefault; return stringDefault;
@ -387,7 +410,7 @@ const JsonVector & JsonNode::Vector() const
{ {
assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_VECTOR); assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_VECTOR);
if (getType() == JsonType::DATA_VECTOR) if(getType() == JsonType::DATA_VECTOR)
return std::get<JsonVector>(data); return std::get<JsonVector>(data);
return vectorDefault; return vectorDefault;
@ -398,7 +421,7 @@ const JsonMap & JsonNode::Struct() const
{ {
assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRUCT); assert(getType() == JsonType::DATA_NULL || getType() == JsonType::DATA_STRUCT);
if (getType() == JsonType::DATA_STRUCT) if(getType() == JsonType::DATA_STRUCT)
return std::get<JsonMap>(data); return std::get<JsonMap>(data);
return mapDefault; return mapDefault;
@ -412,32 +435,32 @@ JsonNode & JsonNode::operator[](const std::string & child)
const JsonNode & JsonNode::operator[](const std::string & child) const const JsonNode & JsonNode::operator[](const std::string & child) const
{ {
auto it = Struct().find(child); auto it = Struct().find(child);
if (it != Struct().end()) if(it != Struct().end())
return it->second; return it->second;
return nullNode; return nullNode;
} }
JsonNode & JsonNode::operator[](size_t child) JsonNode & JsonNode::operator[](size_t child)
{ {
if (child >= Vector().size() ) if(child >= Vector().size())
Vector().resize(child + 1); Vector().resize(child + 1);
return Vector()[child]; return Vector()[child];
} }
const JsonNode & JsonNode::operator[](size_t child) const const JsonNode & JsonNode::operator[](size_t child) const
{ {
if (child < Vector().size() ) if(child < Vector().size())
return Vector()[child]; return Vector()[child];
return nullNode; return nullNode;
} }
const JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer) const const JsonNode & JsonNode::resolvePointer(const std::string & jsonPointer) const
{ {
return ::resolvePointer(*this, jsonPointer); return ::resolvePointer(*this, jsonPointer);
} }
JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer) JsonNode & JsonNode::resolvePointer(const std::string & jsonPointer)
{ {
return ::resolvePointer(*this, jsonPointer); return ::resolvePointer(*this, jsonPointer);
} }
@ -445,7 +468,7 @@ JsonNode & JsonNode::resolvePointer(const std::string &jsonPointer)
std::vector<std::byte> JsonNode::toBytes() const std::vector<std::byte> JsonNode::toBytes() const
{ {
std::string jsonString = toString(); std::string jsonString = toString();
auto dataBegin = reinterpret_cast<const std::byte*>(jsonString.data()); auto dataBegin = reinterpret_cast<const std::byte *>(jsonString.data());
auto dataEnd = dataBegin + jsonString.size(); auto dataEnd = dataBegin + jsonString.size();
std::vector<std::byte> result(dataBegin, dataEnd); std::vector<std::byte> result(dataBegin, dataEnd);
return result; return result;

View File

@ -58,6 +58,7 @@ private:
std::string modScope; std::string modScope;
bool overrideFlag = false; bool overrideFlag = false;
public: public:
JsonNode() = default; JsonNode() = default;
@ -78,8 +79,8 @@ public:
explicit JsonNode(const JsonPath & fileURI, const std::string & modName); explicit JsonNode(const JsonPath & fileURI, const std::string & modName);
explicit JsonNode(const JsonPath & fileURI, bool & isValidSyntax); explicit JsonNode(const JsonPath & fileURI, bool & isValidSyntax);
bool operator == (const JsonNode &other) const; bool operator==(const JsonNode & other) const;
bool operator != (const JsonNode &other) const; bool operator!=(const JsonNode & other) const;
const std::string & getModScope() const; const std::string & getModScope() const;
void setModScope(const std::string & metadata, bool recursive = true); void setModScope(const std::string & metadata, bool recursive = true);
@ -139,17 +140,18 @@ public:
const JsonNode & operator[](const std::string & child) const; const JsonNode & operator[](const std::string & child) const;
JsonNode & operator[](size_t child); JsonNode & operator[](size_t child);
const JsonNode & operator[](size_t child) const; const JsonNode & operator[](size_t child) const;
std::string toCompactString() const; std::string toCompactString() const;
std::string toString() const; std::string toString() const;
std::vector<std::byte> toBytes() const; std::vector<std::byte> toBytes() const;
template <typename Handler> void serialize(Handler &h) template<typename Handler>
void serialize(Handler & h)
{ {
h & modScope; h & modScope;
if (h.version >= Handler::Version::JSON_FLAGS) if(h.version >= Handler::Version::JSON_FLAGS)
{ {
h & overrideFlag; h & overrideFlag;
} }
@ -187,15 +189,15 @@ inline void convert(std::string & value, const JsonNode & node)
value = node.String(); value = node.String();
} }
template <typename Type> template<typename Type>
void convert(std::map<std::string,Type> & value, const JsonNode & node) void convert(std::map<std::string, Type> & value, const JsonNode & node)
{ {
value.clear(); value.clear();
for (const JsonMap::value_type & entry : node.Struct()) for(const JsonMap::value_type & entry : node.Struct())
value.insert(entry.first, entry.second.convertTo<Type>()); value.insert(entry.first, entry.second.convertTo<Type>());
} }
template <typename Type> template<typename Type>
void convert(std::set<Type> & value, const JsonNode & node) void convert(std::set<Type> & value, const JsonNode & node)
{ {
value.clear(); value.clear();
@ -205,7 +207,7 @@ void convert(std::set<Type> & value, const JsonNode & node)
} }
} }
template <typename Type> template<typename Type>
void convert(std::vector<Type> & value, const JsonNode & node) void convert(std::vector<Type> & value, const JsonNode & node)
{ {
value.clear(); value.clear();

View File

@ -11,18 +11,18 @@
#include "StdInc.h" #include "StdInc.h"
#include "JsonParser.h" #include "JsonParser.h"
#include "JsonFormatException.h"
#include "../TextOperations.h" #include "../TextOperations.h"
#include "JsonFormatException.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
JsonParser::JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings): JsonParser::JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings)
input(inputString, stringSize), : input(inputString, stringSize)
settings(settings), , settings(settings)
currentDepth(0), , currentDepth(0)
lineCount(1), , lineCount(1)
lineStart(0), , lineStart(0)
pos(0) , pos(0)
{ {
} }
@ -30,24 +30,24 @@ JsonNode JsonParser::parse(const std::string & fileName)
{ {
JsonNode root; JsonNode root;
if (input.empty()) if(input.empty())
{ {
error("File is empty", false); error("File is empty", false);
} }
else else
{ {
if (!TextOperations::isValidUnicodeString(input.data(), input.size())) if(!TextOperations::isValidUnicodeString(input.data(), input.size()))
error("Not a valid UTF-8 file", false); error("Not a valid UTF-8 file", false);
extractValue(root); extractValue(root);
extractWhitespace(false); extractWhitespace(false);
//Warn if there are any non-whitespace symbols left //Warn if there are any non-whitespace symbols left
if (pos < input.size()) if(pos < input.size())
error("Not all file was parsed!", true); error("Not all file was parsed!", true);
} }
if (!errors.empty()) if(!errors.empty())
{ {
logMod->warn("File %s is not a valid JSON file!", fileName); logMod->warn("File %s is not a valid JSON file!", fileName);
logMod->warn(errors); logMod->warn(errors);
@ -62,33 +62,40 @@ bool JsonParser::isValid()
bool JsonParser::extractSeparator() bool JsonParser::extractSeparator()
{ {
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
if ( input[pos] !=':') if(input[pos] != ':')
return error("Separator expected"); return error("Separator expected");
pos++; pos++;
return true; return true;
} }
bool JsonParser::extractValue(JsonNode &node) bool JsonParser::extractValue(JsonNode & node)
{ {
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
switch (input[pos]) switch(input[pos])
{ {
case '\"': return extractString(node); case '\"':
case 'n' : return extractNull(node); return extractString(node);
case 't' : return extractTrue(node); case 'n':
case 'f' : return extractFalse(node); return extractNull(node);
case '{' : return extractStruct(node); case 't':
case '[' : return extractArray(node); return extractTrue(node);
case '-' : return extractFloat(node); case 'f':
return extractFalse(node);
case '{':
return extractStruct(node);
case '[':
return extractArray(node);
case '-':
return extractFloat(node);
default: default:
{ {
if (input[pos] >= '0' && input[pos] <= '9') if(input[pos] >= '0' && input[pos] <= '9')
return extractFloat(node); return extractFloat(node);
return error("Value expected!"); return error("Value expected!");
} }
@ -100,73 +107,90 @@ bool JsonParser::extractWhitespace(bool verbose)
//TODO: JSON5 - C-style multi-line comments //TODO: JSON5 - C-style multi-line comments
//TODO: JSON5 - Additional white space characters are allowed //TODO: JSON5 - Additional white space characters are allowed
while (true) while(true)
{ {
while(pos < input.size() && static_cast<ui8>(input[pos]) <= ' ') while(pos < input.size() && static_cast<ui8>(input[pos]) <= ' ')
{ {
if (input[pos] == '\n') if(input[pos] == '\n')
{ {
lineCount++; lineCount++;
lineStart = pos+1; lineStart = pos + 1;
} }
pos++; pos++;
} }
if (pos >= input.size() || input[pos] != '/') if(pos >= input.size() || input[pos] != '/')
break; break;
if (settings.mode == JsonParsingSettings::JsonFormatMode::JSON) if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON)
error("Comments are not permitted in json!", true); error("Comments are not permitted in json!", true);
pos++; pos++;
if (pos == input.size()) if(pos == input.size())
break; break;
if (input[pos] == '/') if(input[pos] == '/')
pos++; pos++;
else else
error("Comments must consist of two slashes!", true); error("Comments must consist of two slashes!", true);
while (pos < input.size() && input[pos] != '\n') while(pos < input.size() && input[pos] != '\n')
pos++; pos++;
} }
if (pos >= input.size() && verbose) if(pos >= input.size() && verbose)
return error("Unexpected end of file!"); return error("Unexpected end of file!");
return true; return true;
} }
bool JsonParser::extractEscaping(std::string &str) bool JsonParser::extractEscaping(std::string & str)
{ {
// TODO: support unicode escaping: // TODO: support unicode escaping:
// \u1234 // \u1234
switch(input[pos]) switch(input[pos])
{ {
break; case '\"': str += '\"'; case '\"':
break; case '\\': str += '\\'; str += '\"';
break; case 'b': str += '\b'; break;
break; case 'f': str += '\f'; case '\\':
break; case 'n': str += '\n'; str += '\\';
break; case 'r': str += '\r'; break;
break; case 't': str += '\t'; case 'b':
break; case '/': str += '/'; str += '\b';
break; default: return error("Unknown escape sequence!", true); break;
case 'f':
str += '\f';
break;
case 'n':
str += '\n';
break;
case 'r':
str += '\r';
break;
case 't':
str += '\t';
break;
case '/':
str += '/';
break;
default:
return error("Unknown escape sequence!", true);
} }
return true; return true;
} }
bool JsonParser::extractString(std::string &str) bool JsonParser::extractString(std::string & str)
{ {
//TODO: JSON5 - line breaks escaping //TODO: JSON5 - line breaks escaping
if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
{ {
if (input[pos] != '\"') if(input[pos] != '\"')
return error("String expected!"); return error("String expected!");
} }
else else
{ {
if (input[pos] != '\"' && input[pos] != '\'') if(input[pos] != '\"' && input[pos] != '\'')
return error("String expected!"); return error("String expected!");
} }
@ -175,32 +199,32 @@ bool JsonParser::extractString(std::string &str)
size_t first = pos; size_t first = pos;
while (pos != input.size()) while(pos != input.size())
{ {
if (input[pos] == lineTerminator) // Correct end of string if(input[pos] == lineTerminator) // Correct end of string
{ {
str.append( &input[first], pos-first); str.append(&input[first], pos - first);
pos++; pos++;
return true; return true;
} }
if (input[pos] == '\\') // Escaping if(input[pos] == '\\') // Escaping
{ {
str.append( &input[first], pos-first); str.append(&input[first], pos - first);
pos++; pos++;
if (pos == input.size()) if(pos == input.size())
break; break;
extractEscaping(str); extractEscaping(str);
first = pos + 1; first = pos + 1;
} }
if (input[pos] == '\n') // end-of-line if(input[pos] == '\n') // end-of-line
{ {
str.append( &input[first], pos-first); str.append(&input[first], pos - first);
return error("Closing quote not found!", true); return error("Closing quote not found!", true);
} }
if(static_cast<unsigned char>(input[pos]) < ' ') // control character if(static_cast<unsigned char>(input[pos]) < ' ') // control character
{ {
str.append( &input[first], pos-first); str.append(&input[first], pos - first);
first = pos+1; first = pos + 1;
error("Illegal character in the string!", true); error("Illegal character in the string!", true);
} }
pos++; pos++;
@ -208,10 +232,10 @@ bool JsonParser::extractString(std::string &str)
return error("Unterminated string!"); return error("Unterminated string!");
} }
bool JsonParser::extractString(JsonNode &node) bool JsonParser::extractString(JsonNode & node)
{ {
std::string str; std::string str;
if (!extractString(str)) if(!extractString(str))
return false; return false;
node.setType(JsonNode::JsonType::DATA_STRING); node.setType(JsonNode::JsonType::DATA_STRING);
@ -221,13 +245,13 @@ bool JsonParser::extractString(JsonNode &node)
bool JsonParser::extractLiteral(std::string & literal) bool JsonParser::extractLiteral(std::string & literal)
{ {
while (pos < input.size() ) while(pos < input.size())
{ {
bool isUpperCase = input[pos]>='A' && input[pos]<='Z'; bool isUpperCase = input[pos] >= 'A' && input[pos] <= 'Z';
bool isLowerCase = input[pos]>='a' && input[pos]<='z'; bool isLowerCase = input[pos] >= 'a' && input[pos] <= 'z';
bool isNumber = input[pos]>='0' && input[pos]<='9'; bool isNumber = input[pos] >= '0' && input[pos] <= '9';
if (!isUpperCase && !isLowerCase && !isNumber) if(!isUpperCase && !isLowerCase && !isNumber)
break; break;
literal += input[pos]; literal += input[pos];
@ -238,13 +262,13 @@ bool JsonParser::extractLiteral(std::string & literal)
return true; return true;
} }
bool JsonParser::extractAndCompareLiteral(const std::string &expectedLiteral) bool JsonParser::extractAndCompareLiteral(const std::string & expectedLiteral)
{ {
std::string literal; std::string literal;
if (!extractLiteral(literal)) if(!extractLiteral(literal))
return false; return false;
if (literal != expectedLiteral) if(literal != expectedLiteral)
{ {
return error("Expected " + expectedLiteral + ", but unknown literal found", true); return error("Expected " + expectedLiteral + ", but unknown literal found", true);
return false; return false;
@ -253,81 +277,81 @@ bool JsonParser::extractAndCompareLiteral(const std::string &expectedLiteral)
return true; return true;
} }
bool JsonParser::extractNull(JsonNode &node) bool JsonParser::extractNull(JsonNode & node)
{ {
if (!extractAndCompareLiteral("null")) if(!extractAndCompareLiteral("null"))
return false; return false;
node.clear(); node.clear();
return true; return true;
} }
bool JsonParser::extractTrue(JsonNode &node) bool JsonParser::extractTrue(JsonNode & node)
{ {
if (!extractAndCompareLiteral("true")) if(!extractAndCompareLiteral("true"))
return false; return false;
node.Bool() = true; node.Bool() = true;
return true; return true;
} }
bool JsonParser::extractFalse(JsonNode &node) bool JsonParser::extractFalse(JsonNode & node)
{ {
if (!extractAndCompareLiteral("false")) if(!extractAndCompareLiteral("false"))
return false; return false;
node.Bool() = false; node.Bool() = false;
return true; return true;
} }
bool JsonParser::extractStruct(JsonNode &node) bool JsonParser::extractStruct(JsonNode & node)
{ {
node.setType(JsonNode::JsonType::DATA_STRUCT); node.setType(JsonNode::JsonType::DATA_STRUCT);
if (currentDepth > settings.maxDepth) if(currentDepth > settings.maxDepth)
error("Macimum allowed depth of json structure has been reached", true); error("Macimum allowed depth of json structure has been reached", true);
currentDepth++; currentDepth++;
pos++; pos++;
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
//Empty struct found //Empty struct found
if (input[pos] == '}') if(input[pos] == '}')
{ {
pos++; pos++;
return true; return true;
} }
while (true) while(true)
{ {
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
bool overrideFlag = false; bool overrideFlag = false;
std::string key; std::string key;
if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
{ {
if (!extractString(key)) if(!extractString(key))
return false; return false;
} }
else else
{ {
if (input[pos] == '\'' || input[pos] == '\"') if(input[pos] == '\'' || input[pos] == '\"')
{ {
if (!extractString(key)) if(!extractString(key))
return false; return false;
} }
else else
{ {
if (!extractLiteral(key)) if(!extractLiteral(key))
return false; return false;
} }
} }
if (key.find('#') != std::string::npos) if(key.find('#') != std::string::npos)
{ {
// split key string into actual key and meta-flags // split key string into actual key and meta-flags
std::vector<std::string> keyAndFlags; std::vector<std::string> keyAndFlags;
@ -337,25 +361,25 @@ bool JsonParser::extractStruct(JsonNode &node)
for(int i = 1; i < keyAndFlags.size(); i++) for(int i = 1; i < keyAndFlags.size(); i++)
{ {
if (keyAndFlags[i] == "override") if(keyAndFlags[i] == "override")
overrideFlag = true; overrideFlag = true;
else else
error("Encountered unknown flag #" + keyAndFlags[i], true); error("Encountered unknown flag #" + keyAndFlags[i], true);
} }
} }
if (node.Struct().find(key) != node.Struct().end()) if(node.Struct().find(key) != node.Struct().end())
error("Duplicate element encountered!", true); error("Duplicate element encountered!", true);
if (!extractSeparator()) if(!extractSeparator())
return false; return false;
if (!extractElement(node.Struct()[key], '}')) if(!extractElement(node.Struct()[key], '}'))
return false; return false;
node.Struct()[key].setOverrideFlag(overrideFlag); node.Struct()[key].setOverrideFlag(overrideFlag);
if (input[pos] == '}') if(input[pos] == '}')
{ {
pos++; pos++;
return true; return true;
@ -363,35 +387,35 @@ bool JsonParser::extractStruct(JsonNode &node)
} }
} }
bool JsonParser::extractArray(JsonNode &node) bool JsonParser::extractArray(JsonNode & node)
{ {
if (currentDepth > settings.maxDepth) if(currentDepth > settings.maxDepth)
error("Macimum allowed depth of json structure has been reached", true); error("Macimum allowed depth of json structure has been reached", true);
currentDepth++; currentDepth++;
pos++; pos++;
node.setType(JsonNode::JsonType::DATA_VECTOR); node.setType(JsonNode::JsonType::DATA_VECTOR);
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
//Empty array found //Empty array found
if (input[pos] == ']') if(input[pos] == ']')
{ {
pos++; pos++;
return true; return true;
} }
while (true) while(true)
{ {
//NOTE: currently 50% of time is this vector resizing. //NOTE: currently 50% of time is this vector resizing.
//May be useful to use list during parsing and then swap() all items to vector //May be useful to use list during parsing and then swap() all items to vector
node.Vector().resize(node.Vector().size()+1); node.Vector().resize(node.Vector().size() + 1);
if (!extractElement(node.Vector().back(), ']')) if(!extractElement(node.Vector().back(), ']'))
return false; return false;
if (input[pos] == ']') if(input[pos] == ']')
{ {
pos++; pos++;
return true; return true;
@ -399,92 +423,92 @@ bool JsonParser::extractArray(JsonNode &node)
} }
} }
bool JsonParser::extractElement(JsonNode &node, char terminator) bool JsonParser::extractElement(JsonNode & node, char terminator)
{ {
if (!extractValue(node)) if(!extractValue(node))
return false; return false;
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
bool comma = (input[pos] == ','); bool comma = (input[pos] == ',');
if (comma ) if(comma)
{ {
pos++; pos++;
if (!extractWhitespace()) if(!extractWhitespace())
return false; return false;
} }
if (input[pos] == terminator) if(input[pos] == terminator)
{ {
if (comma) if(comma)
{ {
if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
error("Extra comma found!", true); error("Extra comma found!", true);
} }
return true; return true;
} }
if (!comma) if(!comma)
error("Comma expected!", true); error("Comma expected!", true);
return true; return true;
} }
bool JsonParser::extractFloat(JsonNode &node) bool JsonParser::extractFloat(JsonNode & node)
{ {
//TODO: JSON5 - hexacedimal support //TODO: JSON5 - hexacedimal support
//TODO: JSON5 - Numbers may be IEEE 754 positive infinity, negative infinity, and NaN (why?) //TODO: JSON5 - Numbers may be IEEE 754 positive infinity, negative infinity, and NaN (why?)
assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9')); assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));
bool negative=false; bool negative = false;
double result=0; double result = 0;
si64 integerPart = 0; si64 integerPart = 0;
bool isFloat = false; bool isFloat = false;
if (input[pos] == '+') if(input[pos] == '+')
{ {
if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
error("Positive numbers should not have plus sign!", true); error("Positive numbers should not have plus sign!", true);
pos++; pos++;
} }
else if (input[pos] == '-') else if(input[pos] == '-')
{ {
pos++; pos++;
negative = true; negative = true;
} }
if (input[pos] < '0' || input[pos] > '9') if(input[pos] < '0' || input[pos] > '9')
{ {
if (input[pos] != '.' && settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) if(input[pos] != '.' && settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
return error("Number expected!"); return error("Number expected!");
} }
//Extract integer part //Extract integer part
while (input[pos] >= '0' && input[pos] <= '9') while(input[pos] >= '0' && input[pos] <= '9')
{ {
integerPart = integerPart*10+(input[pos]-'0'); integerPart = integerPart * 10 + (input[pos] - '0');
pos++; pos++;
} }
result = static_cast<double>(integerPart); result = static_cast<double>(integerPart);
if (input[pos] == '.') if(input[pos] == '.')
{ {
//extract fractional part //extract fractional part
isFloat = true; isFloat = true;
pos++; pos++;
double fractMult = 0.1; double fractMult = 0.1;
if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5) if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
{ {
if (input[pos] < '0' || input[pos] > '9') if(input[pos] < '0' || input[pos] > '9')
return error("Decimal part expected!"); return error("Decimal part expected!");
} }
while (input[pos] >= '0' && input[pos] <= '9') while(input[pos] >= '0' && input[pos] <= '9')
{ {
result = result + fractMult*(input[pos]-'0'); result = result + fractMult * (input[pos] - '0');
fractMult /= 10; fractMult /= 10;
pos++; pos++;
} }
@ -508,12 +532,12 @@ bool JsonParser::extractFloat(JsonNode &node)
pos++; pos++;
} }
if (input[pos] < '0' || input[pos] > '9') if(input[pos] < '0' || input[pos] > '9')
return error("Exponential part expected!"); return error("Exponential part expected!");
while (input[pos] >= '0' && input[pos] <= '9') while(input[pos] >= '0' && input[pos] <= '9')
{ {
power = power*10 + (input[pos]-'0'); power = power * 10 + (input[pos] - '0');
pos++; pos++;
} }
@ -543,16 +567,15 @@ bool JsonParser::extractFloat(JsonNode &node)
return true; return true;
} }
bool JsonParser::error(const std::string &message, bool warning) bool JsonParser::error(const std::string & message, bool warning)
{ {
if (settings.strict) if(settings.strict)
throw JsonFormatException(message); throw JsonFormatException(message);
std::ostringstream stream; std::ostringstream stream;
std::string type(warning?" warning: ":" error: "); std::string type(warning ? " warning: " : " error: ");
stream << "At line " << lineCount << ", position "<<pos-lineStart stream << "At line " << lineCount << ", position " << pos - lineStart << type << message << "\n";
<< type << message <<"\n";
errors += stream.str(); errors += stream.str();
return warning; return warning;

View File

@ -18,34 +18,34 @@ class JsonParser
{ {
const JsonParsingSettings settings; const JsonParsingSettings settings;
std::string errors; // Contains description of all encountered errors std::string errors; // Contains description of all encountered errors
std::string_view input; // Input data std::string_view input; // Input data
uint32_t lineCount; // Currently parsed line, starting from 1 uint32_t lineCount; // Currently parsed line, starting from 1
uint32_t currentDepth; uint32_t currentDepth;
size_t lineStart; // Position of current line start size_t lineStart; // Position of current line start
size_t pos; // Current position of parser size_t pos; // Current position of parser
//Helpers //Helpers
bool extractEscaping(std::string &str); bool extractEscaping(std::string & str);
bool extractLiteral(std::string &literal); bool extractLiteral(std::string & literal);
bool extractAndCompareLiteral(const std::string &expectedLiteral); bool extractAndCompareLiteral(const std::string & expectedLiteral);
bool extractString(std::string &string); bool extractString(std::string & string);
bool extractWhitespace(bool verbose = true); bool extractWhitespace(bool verbose = true);
bool extractSeparator(); bool extractSeparator();
bool extractElement(JsonNode &node, char terminator); bool extractElement(JsonNode & node, char terminator);
//Methods for extracting JSON data //Methods for extracting JSON data
bool extractArray(JsonNode &node); bool extractArray(JsonNode & node);
bool extractFalse(JsonNode &node); bool extractFalse(JsonNode & node);
bool extractFloat(JsonNode &node); bool extractFloat(JsonNode & node);
bool extractNull(JsonNode &node); bool extractNull(JsonNode & node);
bool extractString(JsonNode &node); bool extractString(JsonNode & node);
bool extractStruct(JsonNode &node); bool extractStruct(JsonNode & node);
bool extractTrue(JsonNode &node); bool extractTrue(JsonNode & node);
bool extractValue(JsonNode &node); bool extractValue(JsonNode & node);
//Add error\warning message to list //Add error\warning message to list
bool error(const std::string &message, bool warning=false); bool error(const std::string & message, bool warning = false);
public: public:
JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings); JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings);

View File

@ -9,9 +9,9 @@
*/ */
#pragma once #pragma once
#include "GameCallbackHolder.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "ResourceSet.h" #include "ResourceSet.h"
#include "GameCallbackHolder.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN

View File

@ -16,28 +16,28 @@ VCMI_LIB_NAMESPACE_BEGIN
template<typename Iterator> template<typename Iterator>
void JsonWriter::writeContainer(Iterator begin, Iterator end) void JsonWriter::writeContainer(Iterator begin, Iterator end)
{ {
if (begin == end) if(begin == end)
return; return;
prefix += '\t'; prefix += '\t';
writeEntry(begin++); writeEntry(begin++);
while (begin != end) while(begin != end)
{ {
out << (compactMode ? ", " : ",\n"); out << (compactMode ? ", " : ",\n");
writeEntry(begin++); writeEntry(begin++);
} }
out << (compactMode ? "" : "\n"); out << (compactMode ? "" : "\n");
prefix.resize(prefix.size()-1); prefix.resize(prefix.size() - 1);
} }
void JsonWriter::writeEntry(JsonMap::const_iterator entry) void JsonWriter::writeEntry(JsonMap::const_iterator entry)
{ {
if(!compactMode) if(!compactMode)
{ {
if (!entry->second.getModScope().empty()) if(!entry->second.getModScope().empty())
out << prefix << " // " << entry->second.getModScope() << "\n"; out << prefix << " // " << entry->second.getModScope() << "\n";
out << prefix; out << prefix;
} }
@ -50,27 +50,25 @@ void JsonWriter::writeEntry(JsonVector::const_iterator entry)
{ {
if(!compactMode) if(!compactMode)
{ {
if (!entry->getModScope().empty()) if(!entry->getModScope().empty())
out << prefix << " // " << entry->getModScope() << "\n"; out << prefix << " // " << entry->getModScope() << "\n";
out << prefix; out << prefix;
} }
writeNode(*entry); writeNode(*entry);
} }
void JsonWriter::writeString(const std::string &string) void JsonWriter::writeString(const std::string & string)
{ {
static const std::string escaped = "\"\\\b\f\n\r\t"; static const std::string escaped = "\"\\\b\f\n\r\t";
static const std::array escaped_code = {'\"', '\\', 'b', 'f', 'n', 'r', 't'}; static const std::array escaped_code = {'\"', '\\', 'b', 'f', 'n', 'r', 't'};
out <<'\"'; out << '\"';
size_t pos = 0; size_t pos = 0;
size_t start = 0; size_t start = 0;
for (; pos<string.size(); pos++) for(; pos < string.size(); pos++)
{ {
//we need to check if special character was been already escaped //we need to check if special character was been already escaped
if((string[pos] == '\\') if((string[pos] == '\\') && (pos + 1 < string.size()) && (std::find(escaped_code.begin(), escaped_code.end(), string[pos + 1]) != escaped_code.end()))
&& (pos+1 < string.size())
&& (std::find(escaped_code.begin(), escaped_code.end(), string[pos+1]) != escaped_code.end()) )
{ {
pos++; //write unchanged, next simbol also checked pos++; //write unchanged, next simbol also checked
} }
@ -78,20 +76,19 @@ void JsonWriter::writeString(const std::string &string)
{ {
size_t escapedPos = escaped.find(string[pos]); size_t escapedPos = escaped.find(string[pos]);
if (escapedPos != std::string::npos) if(escapedPos != std::string::npos)
{ {
out.write(string.data()+start, pos - start); out.write(string.data() + start, pos - start);
out << '\\' << escaped_code[escapedPos]; out << '\\' << escaped_code[escapedPos];
start = pos+1; start = pos + 1;
} }
} }
} }
out.write(string.data()+start, pos - start); out.write(string.data() + start, pos - start);
out <<'\"'; out << '\"';
} }
void JsonWriter::writeNode(const JsonNode &node) void JsonWriter::writeNode(const JsonNode & node)
{ {
bool originalMode = compactMode; bool originalMode = compactMode;
if(compact && !compactMode && node.isCompact()) if(compact && !compactMode && node.isCompact())
@ -99,40 +96,48 @@ void JsonWriter::writeNode(const JsonNode &node)
switch(node.getType()) switch(node.getType())
{ {
break; case JsonNode::JsonType::DATA_NULL: case JsonNode::JsonType::DATA_NULL:
out << "null"; out << "null";
break;
break; case JsonNode::JsonType::DATA_BOOL: case JsonNode::JsonType::DATA_BOOL:
if (node.Bool()) if(node.Bool())
out << "true"; out << "true";
else else
out << "false"; out << "false";
break;
break; case JsonNode::JsonType::DATA_FLOAT: case JsonNode::JsonType::DATA_FLOAT:
out << node.Float(); out << node.Float();
break;
break; case JsonNode::JsonType::DATA_STRING: case JsonNode::JsonType::DATA_STRING:
writeString(node.String()); writeString(node.String());
break;
break; case JsonNode::JsonType::DATA_VECTOR: case JsonNode::JsonType::DATA_VECTOR:
out << "[" << (compactMode ? " " : "\n"); out << "[" << (compactMode ? " " : "\n");
writeContainer(node.Vector().begin(), node.Vector().end()); writeContainer(node.Vector().begin(), node.Vector().end());
out << (compactMode ? " " : prefix) << "]"; out << (compactMode ? " " : prefix) << "]";
break;
break; case JsonNode::JsonType::DATA_STRUCT: case JsonNode::JsonType::DATA_STRUCT:
out << "{" << (compactMode ? " " : "\n"); out << "{" << (compactMode ? " " : "\n");
writeContainer(node.Struct().begin(), node.Struct().end()); writeContainer(node.Struct().begin(), node.Struct().end());
out << (compactMode ? " " : prefix) << "}"; out << (compactMode ? " " : prefix) << "}";
break;
break; case JsonNode::JsonType::DATA_INTEGER: case JsonNode::JsonType::DATA_INTEGER:
out << node.Integer(); out << node.Integer();
break;
} }
compactMode = originalMode; compactMode = originalMode;
} }
JsonWriter::JsonWriter(std::ostream & output, bool compact) JsonWriter::JsonWriter(std::ostream & output, bool compact)
: out(output), compact(compact) : out(output)
, compact(compact)
{ {
} }

View File

@ -22,6 +22,7 @@ class JsonWriter
bool compact; bool compact;
//tracks whether we are currently using single-line format //tracks whether we are currently using single-line format
bool compactMode = false; bool compactMode = false;
public: public:
template<typename Iterator> template<typename Iterator>
void writeContainer(Iterator begin, Iterator end); void writeContainer(Iterator begin, Iterator end);