1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-15 00:05:02 +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,18 +11,18 @@
#include "StdInc.h"
#include "JsonParser.h"
#include "JsonFormatException.h"
#include "../TextOperations.h"
#include "JsonFormatException.h"
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)
JsonParser::JsonParser(const char * inputString, size_t stringSize, const JsonParsingSettings & settings)
: input(inputString, stringSize)
, settings(settings)
, currentDepth(0)
, lineCount(1)
, lineStart(0)
, pos(0)
{
}
@ -30,24 +30,24 @@ JsonNode JsonParser::parse(const std::string & fileName)
{
JsonNode root;
if (input.empty())
if(input.empty())
{
error("File is empty", false);
}
else
{
if (!TextOperations::isValidUnicodeString(input.data(), input.size()))
if(!TextOperations::isValidUnicodeString(input.data(), input.size()))
error("Not a valid UTF-8 file", false);
extractValue(root);
extractWhitespace(false);
//Warn if there are any non-whitespace symbols left
if (pos < input.size())
if(pos < input.size())
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(errors);
@ -62,33 +62,40 @@ bool JsonParser::isValid()
bool JsonParser::extractSeparator()
{
if (!extractWhitespace())
if(!extractWhitespace())
return false;
if ( input[pos] !=':')
if(input[pos] != ':')
return error("Separator expected");
pos++;
return true;
}
bool JsonParser::extractValue(JsonNode &node)
bool JsonParser::extractValue(JsonNode & node)
{
if (!extractWhitespace())
if(!extractWhitespace())
return false;
switch (input[pos])
switch(input[pos])
{
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 '-' : return extractFloat(node);
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 '-':
return extractFloat(node);
default:
{
if (input[pos] >= '0' && input[pos] <= '9')
if(input[pos] >= '0' && input[pos] <= '9')
return extractFloat(node);
return error("Value expected!");
}
@ -100,73 +107,90 @@ bool JsonParser::extractWhitespace(bool verbose)
//TODO: JSON5 - C-style multi-line comments
//TODO: JSON5 - Additional white space characters are allowed
while (true)
while(true)
{
while(pos < input.size() && static_cast<ui8>(input[pos]) <= ' ')
{
if (input[pos] == '\n')
if(input[pos] == '\n')
{
lineCount++;
lineStart = pos+1;
lineStart = pos + 1;
}
pos++;
}
if (pos >= input.size() || input[pos] != '/')
if(pos >= input.size() || input[pos] != '/')
break;
if (settings.mode == JsonParsingSettings::JsonFormatMode::JSON)
if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON)
error("Comments are not permitted in json!", true);
pos++;
if (pos == input.size())
if(pos == input.size())
break;
if (input[pos] == '/')
if(input[pos] == '/')
pos++;
else
error("Comments must consist of two slashes!", true);
while (pos < input.size() && input[pos] != '\n')
while(pos < input.size() && input[pos] != '\n')
pos++;
}
if (pos >= input.size() && verbose)
if(pos >= input.size() && verbose)
return error("Unexpected end of file!");
return true;
}
bool JsonParser::extractEscaping(std::string &str)
bool JsonParser::extractEscaping(std::string & str)
{
// TODO: support unicode escaping:
// \u1234
switch(input[pos])
{
break; case '\"': str += '\"';
break; case '\\': str += '\\';
break; case 'b': str += '\b';
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);
case '\"':
str += '\"';
break;
case '\\':
str += '\\';
break;
case 'b':
str += '\b';
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;
}
bool JsonParser::extractString(std::string &str)
bool JsonParser::extractString(std::string & str)
{
//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!");
}
else
{
if (input[pos] != '\"' && input[pos] != '\'')
if(input[pos] != '\"' && input[pos] != '\'')
return error("String expected!");
}
@ -175,32 +199,32 @@ bool JsonParser::extractString(std::string &str)
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++;
return true;
}
if (input[pos] == '\\') // Escaping
if(input[pos] == '\\') // Escaping
{
str.append( &input[first], pos-first);
str.append(&input[first], pos - first);
pos++;
if (pos == input.size())
if(pos == input.size())
break;
extractEscaping(str);
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);
}
if(static_cast<unsigned char>(input[pos]) < ' ') // control character
{
str.append( &input[first], pos-first);
first = pos+1;
str.append(&input[first], pos - first);
first = pos + 1;
error("Illegal character in the string!", true);
}
pos++;
@ -208,10 +232,10 @@ bool JsonParser::extractString(std::string &str)
return error("Unterminated string!");
}
bool JsonParser::extractString(JsonNode &node)
bool JsonParser::extractString(JsonNode & node)
{
std::string str;
if (!extractString(str))
if(!extractString(str))
return false;
node.setType(JsonNode::JsonType::DATA_STRING);
@ -221,13 +245,13 @@ bool JsonParser::extractString(JsonNode &node)
bool JsonParser::extractLiteral(std::string & literal)
{
while (pos < input.size() )
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';
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)
if(!isUpperCase && !isLowerCase && !isNumber)
break;
literal += input[pos];
@ -238,13 +262,13 @@ bool JsonParser::extractLiteral(std::string & literal)
return true;
}
bool JsonParser::extractAndCompareLiteral(const std::string &expectedLiteral)
bool JsonParser::extractAndCompareLiteral(const std::string & expectedLiteral)
{
std::string literal;
if (!extractLiteral(literal))
if(!extractLiteral(literal))
return false;
if (literal != expectedLiteral)
if(literal != expectedLiteral)
{
return error("Expected " + expectedLiteral + ", but unknown literal found", true);
return false;
@ -253,81 +277,81 @@ bool JsonParser::extractAndCompareLiteral(const std::string &expectedLiteral)
return true;
}
bool JsonParser::extractNull(JsonNode &node)
bool JsonParser::extractNull(JsonNode & node)
{
if (!extractAndCompareLiteral("null"))
if(!extractAndCompareLiteral("null"))
return false;
node.clear();
return true;
}
bool JsonParser::extractTrue(JsonNode &node)
bool JsonParser::extractTrue(JsonNode & node)
{
if (!extractAndCompareLiteral("true"))
if(!extractAndCompareLiteral("true"))
return false;
node.Bool() = true;
return true;
}
bool JsonParser::extractFalse(JsonNode &node)
bool JsonParser::extractFalse(JsonNode & node)
{
if (!extractAndCompareLiteral("false"))
if(!extractAndCompareLiteral("false"))
return false;
node.Bool() = false;
return true;
}
bool JsonParser::extractStruct(JsonNode &node)
bool JsonParser::extractStruct(JsonNode & node)
{
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);
currentDepth++;
pos++;
if (!extractWhitespace())
if(!extractWhitespace())
return false;
//Empty struct found
if (input[pos] == '}')
if(input[pos] == '}')
{
pos++;
return true;
}
while (true)
while(true)
{
if (!extractWhitespace())
if(!extractWhitespace())
return false;
bool overrideFlag = false;
std::string key;
if (settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
{
if (!extractString(key))
if(!extractString(key))
return false;
}
else
{
if (input[pos] == '\'' || input[pos] == '\"')
if(input[pos] == '\'' || input[pos] == '\"')
{
if (!extractString(key))
if(!extractString(key))
return false;
}
else
{
if (!extractLiteral(key))
if(!extractLiteral(key))
return false;
}
}
if (key.find('#') != std::string::npos)
if(key.find('#') != std::string::npos)
{
// split key string into actual key and meta-flags
std::vector<std::string> keyAndFlags;
@ -337,25 +361,25 @@ bool JsonParser::extractStruct(JsonNode &node)
for(int i = 1; i < keyAndFlags.size(); i++)
{
if (keyAndFlags[i] == "override")
if(keyAndFlags[i] == "override")
overrideFlag = true;
else
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);
if (!extractSeparator())
if(!extractSeparator())
return false;
if (!extractElement(node.Struct()[key], '}'))
if(!extractElement(node.Struct()[key], '}'))
return false;
node.Struct()[key].setOverrideFlag(overrideFlag);
if (input[pos] == '}')
if(input[pos] == '}')
{
pos++;
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);
currentDepth++;
pos++;
node.setType(JsonNode::JsonType::DATA_VECTOR);
if (!extractWhitespace())
if(!extractWhitespace())
return false;
//Empty array found
if (input[pos] == ']')
if(input[pos] == ']')
{
pos++;
return true;
}
while (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);
node.Vector().resize(node.Vector().size() + 1);
if (!extractElement(node.Vector().back(), ']'))
if(!extractElement(node.Vector().back(), ']'))
return false;
if (input[pos] == ']')
if(input[pos] == ']')
{
pos++;
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;
if (!extractWhitespace())
if(!extractWhitespace())
return false;
bool comma = (input[pos] == ',');
if (comma )
if(comma)
{
pos++;
if (!extractWhitespace())
if(!extractWhitespace())
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);
}
return true;
}
if (!comma)
if(!comma)
error("Comma expected!", true);
return true;
}
bool JsonParser::extractFloat(JsonNode &node)
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;
bool negative = false;
double result = 0;
si64 integerPart = 0;
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);
pos++;
}
else if (input[pos] == '-')
else if(input[pos] == '-')
{
pos++;
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!");
}
//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++;
}
result = static_cast<double>(integerPart);
if (input[pos] == '.')
if(input[pos] == '.')
{
//extract fractional part
isFloat = true;
pos++;
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!");
}
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;
pos++;
}
@ -508,12 +532,12 @@ bool JsonParser::extractFloat(JsonNode &node)
pos++;
}
if (input[pos] < '0' || input[pos] > '9')
if(input[pos] < '0' || input[pos] > '9')
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++;
}
@ -543,16 +567,15 @@ bool JsonParser::extractFloat(JsonNode &node)
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);
std::ostringstream stream;
std::string type(warning?" warning: ":" error: ");
std::string type(warning ? " warning: " : " error: ");
stream << "At line " << lineCount << ", position "<<pos-lineStart
<< type << message <<"\n";
stream << "At line " << lineCount << ", position " << pos - lineStart << type << message << "\n";
errors += stream.str();
return warning;