|
|
|
@@ -41,6 +41,24 @@ namespace minipp
|
|
|
|
InvalidDataType = -6,
|
|
|
|
InvalidDataType = -6,
|
|
|
|
FormatError = -7,
|
|
|
|
FormatError = -7,
|
|
|
|
ArrayDataTypeInconsistency = -8,
|
|
|
|
ArrayDataTypeInconsistency = -8,
|
|
|
|
|
|
|
|
BadEscapeSequence = -9,
|
|
|
|
|
|
|
|
UnknownEscapeSequence = -10,
|
|
|
|
|
|
|
|
UnescapedStringValue = -11,
|
|
|
|
|
|
|
|
ValueEmpty = -12,
|
|
|
|
|
|
|
|
IntegerValueInvalid = -13,
|
|
|
|
|
|
|
|
IntegerValueOutOfRange = -14,
|
|
|
|
|
|
|
|
IntegerStyleInvalid = -15,
|
|
|
|
|
|
|
|
FloatValueInvalid = -16,
|
|
|
|
|
|
|
|
BooleanValueInvalid = -17,
|
|
|
|
|
|
|
|
ArrayNotEnclosed = -18,
|
|
|
|
|
|
|
|
ArrayBracketsInbalanced = -19,
|
|
|
|
|
|
|
|
InvalidName = -20,
|
|
|
|
|
|
|
|
SectionExpectedClosingBracket = -21,
|
|
|
|
|
|
|
|
EmptySectionName = -22,
|
|
|
|
|
|
|
|
KeyValuePairNotInSection = -23,
|
|
|
|
|
|
|
|
ExpectedKeyValuePair = -24,
|
|
|
|
|
|
|
|
KeyEmpty = -25,
|
|
|
|
|
|
|
|
MissingQuote = -26,
|
|
|
|
|
|
|
|
|
|
|
|
/* OK Codes */
|
|
|
|
/* OK Codes */
|
|
|
|
Success = +1,
|
|
|
|
Success = +1,
|
|
|
|
@@ -70,6 +88,9 @@ namespace minipp
|
|
|
|
virtual ~Value() = default;
|
|
|
|
virtual ~Value() = default;
|
|
|
|
std::vector<std::string>& GetComments() noexcept { return m_comments; }
|
|
|
|
std::vector<std::string>& GetComments() noexcept { return m_comments; }
|
|
|
|
const std::vector<std::string>& GetComments() const noexcept { return m_comments; }
|
|
|
|
const std::vector<std::string>& GetComments() const noexcept { return m_comments; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
static std::unique_ptr<Value> ParseValue(std::string value, EResult* result = nullptr);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Values
|
|
|
|
class Values
|
|
|
|
@@ -77,78 +98,101 @@ namespace minipp
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
class StringValue : public Value
|
|
|
|
class StringValue : public Value
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
using BaseType = std::string;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
std::string m_value;
|
|
|
|
BaseType m_value;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
StringValue() = default;
|
|
|
|
StringValue() = default;
|
|
|
|
StringValue(const std::string& str) : m_value(str) {};
|
|
|
|
StringValue(const BaseType& str) : m_value(str) {};
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
const std::string& GetValue() const noexcept { return m_value; }
|
|
|
|
const BaseType& GetValue() const noexcept { return m_value; }
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class IntValue : public Value
|
|
|
|
class IntValue : public Value
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
using BaseType = int64_t;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
int64_t m_value = 0;
|
|
|
|
BaseType m_value = 0;
|
|
|
|
EIntStyle m_style = EIntStyle::Decimal;
|
|
|
|
EIntStyle m_style = EIntStyle::Decimal;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
IntValue() = default;
|
|
|
|
IntValue() = default;
|
|
|
|
IntValue(int64_t value) : m_value(value) {};
|
|
|
|
IntValue(BaseType value) : m_value(value) {};
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
int64_t GetValue() const noexcept { return m_value; }
|
|
|
|
BaseType GetValue() const noexcept { return m_value; }
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class BooleanValue : public Value
|
|
|
|
class BooleanValue : public Value
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
using BaseType = bool;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
bool m_value = false;
|
|
|
|
BaseType m_value = false;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
BooleanValue() = default;
|
|
|
|
BooleanValue() = default;
|
|
|
|
BooleanValue(bool value) : m_value(value) {};
|
|
|
|
BooleanValue(BaseType value) : m_value(value) {};
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
bool GetValue() const noexcept { return m_value; }
|
|
|
|
BaseType GetValue() const noexcept { return m_value; }
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class FloatValue : public Value
|
|
|
|
class FloatValue : public Value
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
using BaseType = double;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
double m_value = 0.0f;
|
|
|
|
BaseType m_value = 0.0f;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
FloatValue() = default;
|
|
|
|
FloatValue() = default;
|
|
|
|
FloatValue(double value) : m_value(value) {};
|
|
|
|
FloatValue(BaseType value) : m_value(value) {};
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
double GetValue() const noexcept { return m_value; }
|
|
|
|
BaseType GetValue() const noexcept { return m_value; }
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class ArrayValue : public Value
|
|
|
|
class ArrayValue : public Value
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
using BaseType = std::vector<Value*>;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
std::vector<std::unique_ptr<Value>> m_values;
|
|
|
|
BaseType m_values;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
ArrayValue() = default;
|
|
|
|
ArrayValue() = default;
|
|
|
|
ArrayValue(const ArrayValue&) = delete;
|
|
|
|
ArrayValue(const ArrayValue&) = delete;
|
|
|
|
ArrayValue& operator=(const ArrayValue&) = delete;
|
|
|
|
ArrayValue& operator=(const ArrayValue&) = delete;
|
|
|
|
|
|
|
|
virtual ~ArrayValue();
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult Parse(const std::string& str) noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
EResult ToString(std::string& destination) const noexcept override;
|
|
|
|
std::vector<std::unique_ptr<Value>>& GetValues() noexcept { return m_values; }
|
|
|
|
BaseType& GetValue() noexcept { return m_values; }
|
|
|
|
const std::vector<std::unique_ptr<Value>>& GetValues() const noexcept { return m_values; }
|
|
|
|
const BaseType& GetValue() const noexcept { return m_values; }
|
|
|
|
|
|
|
|
|
|
|
|
Value* operator[](size_t index) noexcept
|
|
|
|
Value* operator[](size_t index) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (index >= m_values.size())
|
|
|
|
if (index >= m_values.size())
|
|
|
|
return nullptr;
|
|
|
|
return nullptr;
|
|
|
|
return m_values[index].get();
|
|
|
|
return m_values[index];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Value* operator[](size_t index) const noexcept
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (index >= m_values.size())
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return m_values[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@@ -165,6 +209,10 @@ namespace minipp
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
std::vector<std::string>& GetComments() noexcept { return m_comments; }
|
|
|
|
std::vector<std::string>& GetComments() noexcept { return m_comments; }
|
|
|
|
const std::vector<std::string>& GetComments() const noexcept { return m_comments; }
|
|
|
|
const std::vector<std::string>& GetComments() const noexcept { return m_comments; }
|
|
|
|
|
|
|
|
std::unordered_map<std::string, Value*>& GetValues() noexcept { return m_values; }
|
|
|
|
|
|
|
|
const std::unordered_map<std::string, Value*>& GetValues() const noexcept { return m_values; }
|
|
|
|
|
|
|
|
std::unordered_map<std::string, Section*>& GetSubSections() noexcept { return m_subSections; }
|
|
|
|
|
|
|
|
const std::unordered_map<std::string, Section*>& GetSubSections() const noexcept { return m_subSections; }
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
Section() = default;
|
|
|
|
Section() = default;
|
|
|
|
@@ -177,16 +225,20 @@ namespace minipp
|
|
|
|
EResult GetValue(const std::string& key, ValueDataType** target) noexcept
|
|
|
|
EResult GetValue(const std::string& key, ValueDataType** target) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
|
|
|
|
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
|
|
|
|
|
|
|
|
|
|
|
|
int64_t firstSeparatorIndex = Tools::FirstIndexOf(key, '.');
|
|
|
|
int64_t firstSeparatorIndex = Tools::FirstIndexOf(key, '.');
|
|
|
|
if (firstSeparatorIndex != -1)
|
|
|
|
if (firstSeparatorIndex != -1)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::string thisKey = key.substr(0, firstSeparatorIndex);
|
|
|
|
std::string thisKey = key.substr(0, firstSeparatorIndex);
|
|
|
|
std::string rest = key.substr(firstSeparatorIndex + 1);
|
|
|
|
std::string rest = key.substr(firstSeparatorIndex + 1);
|
|
|
|
|
|
|
|
|
|
|
|
auto it = m_subSections.find(thisKey);
|
|
|
|
auto it = m_subSections.find(thisKey);
|
|
|
|
if (it == m_subSections.end())
|
|
|
|
if (it == m_subSections.end())
|
|
|
|
return EResult::SectionNotPresent;
|
|
|
|
return EResult::SectionNotPresent;
|
|
|
|
|
|
|
|
|
|
|
|
return it->second->GetValue(rest, target);
|
|
|
|
return it->second->GetValue(rest, target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto it = m_values.find(key);
|
|
|
|
auto it = m_values.find(key);
|
|
|
|
if (it == m_values.end())
|
|
|
|
if (it == m_values.end())
|
|
|
|
return EResult::KeyNotPresent;
|
|
|
|
return EResult::KeyNotPresent;
|
|
|
|
@@ -203,14 +255,34 @@ namespace minipp
|
|
|
|
EResult SetValue(const std::string& name, std::unique_ptr<ValueDataType> value, bool allowOverwrite = false) noexcept
|
|
|
|
EResult SetValue(const std::string& name, std::unique_ptr<ValueDataType> value, bool allowOverwrite = false) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
|
|
|
|
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
|
|
|
|
|
|
|
|
bool overwritten = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (m_values.find(name) != m_values.end())
|
|
|
|
if (m_values.find(name) != m_values.end())
|
|
|
|
if (!allowOverwrite)
|
|
|
|
if (!allowOverwrite)
|
|
|
|
return EResult::KeyAlreadyPresent;
|
|
|
|
return EResult::KeyAlreadyPresent;
|
|
|
|
else
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
delete m_values[name];
|
|
|
|
delete m_values[name];
|
|
|
|
|
|
|
|
overwritten = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_values[name] = value.release();
|
|
|
|
m_values[name] = value.release();
|
|
|
|
return EResult::Success;
|
|
|
|
return overwritten ? EResult::ValueOverwritten : EResult::Success;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename ValueDataType>
|
|
|
|
|
|
|
|
typename ValueDataType::BaseType GetValueOrDefault(const std::string& key,
|
|
|
|
|
|
|
|
const typename ValueDataType::BaseType& defaultValue = typename ValueDataType::BaseType{})
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ValueDataType* value = nullptr;
|
|
|
|
|
|
|
|
if (GetValue(key, &value) != EResult::Success)
|
|
|
|
|
|
|
|
return defaultValue;
|
|
|
|
|
|
|
|
auto casted = dynamic_cast<ValueDataType*>(value);
|
|
|
|
|
|
|
|
if (casted == nullptr)
|
|
|
|
|
|
|
|
return defaultValue;
|
|
|
|
|
|
|
|
return casted->GetValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
@@ -225,12 +297,13 @@ namespace minipp
|
|
|
|
Section m_rootSection{};
|
|
|
|
Section m_rootSection{};
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
static std::unique_ptr<Value> ParseValue(std::string value);
|
|
|
|
|
|
|
|
static minipp::EResult WriteSection(const Section* section, std::ofstream& ofs, std::string partTreeName) noexcept;
|
|
|
|
static minipp::EResult WriteSection(const Section* section, std::ofstream& ofs, std::string partTreeName) noexcept;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
EResult Parse(const std::string& path) noexcept;
|
|
|
|
EResult Parse(const std::string& path, bool additional = false) noexcept;
|
|
|
|
|
|
|
|
EResult Parse(std::ifstream& ifs, bool additional = false) noexcept;
|
|
|
|
EResult Write(const std::string& path) const noexcept;
|
|
|
|
EResult Write(const std::string& path) const noexcept;
|
|
|
|
|
|
|
|
EResult Write(std::ofstream& ofs) const noexcept;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
const Section& GetRoot() const noexcept { return m_rootSection; }
|
|
|
|
const Section& GetRoot() const noexcept { return m_rootSection; }
|
|
|
|
@@ -262,32 +335,90 @@ namespace minipp
|
|
|
|
#include <fstream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <typeinfo>
|
|
|
|
#include <typeinfo>
|
|
|
|
#include <sstream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <bitset>
|
|
|
|
#include <bitset>
|
|
|
|
|
|
|
|
|
|
|
|
#if MINIPP_ENABLE_DEBUG_OUTPUT
|
|
|
|
#if MINIPP_ENABLE_DEBUG_OUTPUT
|
|
|
|
#include <iostream>
|
|
|
|
#include <iostream>
|
|
|
|
#define COUT(msg) std::cout << "[minipp] " << msg << std::endl
|
|
|
|
#define PP_COUT(msg) std::cout << "[minipp] " << msg << std::endl
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
#define COUT(msg)
|
|
|
|
#define PP_COUT(msg)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#define ASSERT(condition) if (!condition) abort();
|
|
|
|
#define PP_COUT_SYNTAX_ERROR_LINE(line, msg) PP_COUT(line << ": " << msg)
|
|
|
|
#define COUT_SYNTAX_ERROR(line, msg) COUT("Error in line " << line << ": " << msg)
|
|
|
|
#define PP_COUT_SYNTAX_ERROR(msg) PP_COUT("Syntax error: " << msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<minipp::MiniPPFile::Value> minipp::MiniPPFile::Value::ParseValue(std::string value, EResult* result)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
#define RETURN_NULLPTR_WITH_RESULT(r) { if (result != nullptr) *result = r; return nullptr; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char valueFirstChar = value.front();
|
|
|
|
|
|
|
|
char valueLastChar = value.back();
|
|
|
|
|
|
|
|
if (valueFirstChar == '"')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (valueLastChar != '"')
|
|
|
|
|
|
|
|
RETURN_NULLPTR_WITH_RESULT(EResult::MissingQuote);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// is string
|
|
|
|
|
|
|
|
value = value.substr(1, value.size() - 2);
|
|
|
|
|
|
|
|
auto strValue = std::make_unique<Values::StringValue>();
|
|
|
|
|
|
|
|
auto parseResult = strValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
RETURN_NULLPTR_WITH_RESULT(parseResult);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return strValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (valueLastChar == 'e')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto boolValue = std::make_unique<Values::BooleanValue>();
|
|
|
|
|
|
|
|
auto parseResult = boolValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
RETURN_NULLPTR_WITH_RESULT(parseResult);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return boolValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (valueLastChar == 'f')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto floatValue = std::make_unique<Values::FloatValue>();
|
|
|
|
|
|
|
|
auto parseResult = floatValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
RETURN_NULLPTR_WITH_RESULT(parseResult);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return floatValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (valueLastChar == ']')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto arrayValue = std::make_unique<Values::ArrayValue>();
|
|
|
|
|
|
|
|
auto parseResult = arrayValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
RETURN_NULLPTR_WITH_RESULT(parseResult);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return arrayValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto intValue = std::make_unique<Values::IntValue>();
|
|
|
|
|
|
|
|
auto parseResult = intValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
RETURN_NULLPTR_WITH_RESULT(parseResult);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return intValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma region Value Types
|
|
|
|
#pragma region Value Types
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::StringValue::Parse(const std::string& str) noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::StringValue::Parse(const std::string& str) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_value = "";
|
|
|
|
m_value = "";
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (str[i] == '\\')
|
|
|
|
if (str[i] == '\\')
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (i + 1 >= str.size())
|
|
|
|
if (i + 1 >= str.size())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Syntax error: '\\' at end of string");
|
|
|
|
PP_COUT_SYNTAX_ERROR("'\\' at end of string");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::BadEscapeSequence;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (str[i + 1])
|
|
|
|
switch (str[i + 1])
|
|
|
|
@@ -308,22 +439,24 @@ minipp::EResult minipp::MiniPPFile::Values::StringValue::Parse(const std::string
|
|
|
|
m_value.push_back('\\');
|
|
|
|
m_value.push_back('\\');
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
COUT("Syntax error: Unknown escape sequence '\\" << str[i + 1] << "'");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Unknown escape sequence '\\" << str[i + 1] << "'");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::UnknownEscapeSequence;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (str[i] == '"')
|
|
|
|
else if (str[i] == '"')
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::UnescapedStringValue;
|
|
|
|
else
|
|
|
|
else
|
|
|
|
m_value.push_back(str[i]);
|
|
|
|
m_value.push_back(str[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::StringValue::ToString(std::string& destination) const noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::StringValue::ToString(std::string& destination) const noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::string sanitizedValue = m_value;
|
|
|
|
std::string sanitizedValue = m_value;
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sanitizedValue.size(); ++i)
|
|
|
|
for (size_t i = 0; i < sanitizedValue.size(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
switch (sanitizedValue[i])
|
|
|
|
switch (sanitizedValue[i])
|
|
|
|
@@ -348,6 +481,7 @@ minipp::EResult minipp::MiniPPFile::Values::StringValue::ToString(std::string& d
|
|
|
|
sanitizedValue.insert(i, 1, '\\');
|
|
|
|
sanitizedValue.insert(i, 1, '\\');
|
|
|
|
i++;
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -361,7 +495,7 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::Parse(const std::string& s
|
|
|
|
Tools::RemoveAll(sanitizedValue, '_');
|
|
|
|
Tools::RemoveAll(sanitizedValue, '_');
|
|
|
|
if (sanitizedValue.empty())
|
|
|
|
if (sanitizedValue.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Empty string for integer value.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Empty string for integer value.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::FormatError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -383,8 +517,8 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::Parse(const std::string& s
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!Tools::IsIntegerDecimal(sanitizedValue))
|
|
|
|
if (!Tools::IsIntegerDecimal(sanitizedValue))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Invalid decimal integer value: " << sanitizedValue);
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid decimal integer value: " << sanitizedValue);
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::IntegerValueInvalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_value = std::stoll(sanitizedValue);
|
|
|
|
m_value = std::stoll(sanitizedValue);
|
|
|
|
m_style = EIntStyle::Decimal;
|
|
|
|
m_style = EIntStyle::Decimal;
|
|
|
|
@@ -392,13 +526,13 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::Parse(const std::string& s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::invalid_argument&)
|
|
|
|
catch (const std::invalid_argument&)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Invalid integer value: " << sanitizedValue);
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid integer value: " << sanitizedValue);
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::IntegerValueInvalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::out_of_range&)
|
|
|
|
catch (const std::out_of_range&)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Integer value out of range: " << sanitizedValue);
|
|
|
|
PP_COUT_SYNTAX_ERROR("Integer value out of range: " << sanitizedValue);
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::IntegerValueOutOfRange;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
@@ -424,6 +558,7 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::ToString(std::string& dest
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::bitset<64> bs(m_value);
|
|
|
|
std::bitset<64> bs(m_value);
|
|
|
|
destination = bs.to_string() + "b";
|
|
|
|
destination = bs.to_string() + "b";
|
|
|
|
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < destination.size(); ++i) // cut of leading zeros
|
|
|
|
for (i = 0; i < destination.size(); ++i) // cut of leading zeros
|
|
|
|
if (destination[i] == '1')
|
|
|
|
if (destination[i] == '1')
|
|
|
|
@@ -436,8 +571,8 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::ToString(std::string& dest
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
COUT("Invalid integer style.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid integer style.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::IntegerStyleInvalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
@@ -451,8 +586,8 @@ minipp::EResult minipp::MiniPPFile::Values::BooleanValue::Parse(const std::strin
|
|
|
|
m_value = false;
|
|
|
|
m_value = false;
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Invalid boolean value: " << str << " (may only contain lowercase true and false)");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid boolean value: " << str << " (may only contain lowercase true and false)");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::BooleanValueInvalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -464,28 +599,39 @@ minipp::EResult minipp::MiniPPFile::Values::BooleanValue::ToString(std::string&
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::FloatValue::Parse(const std::string& str) noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::FloatValue::Parse(const std::string& str) noexcept
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_value = std::stod(str);
|
|
|
|
m_value = std::stod(str);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (...)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid float value: " << str);
|
|
|
|
|
|
|
|
return EResult::FloatValueInvalid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::FloatValue::ToString(std::string& destination) const noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::FloatValue::ToString(std::string& destination) const noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
destination = std::to_string(m_value);
|
|
|
|
destination = std::to_string(m_value) + "f";
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
minipp::MiniPPFile::Values::ArrayValue::~ArrayValue()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (auto& val : m_values)
|
|
|
|
|
|
|
|
delete val;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string& str) noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string& str) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::string nValue = str;
|
|
|
|
|
|
|
|
if (str.front() != '[' || str.back() != ']')
|
|
|
|
if (str.front() != '[' || str.back() != ']')
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Array value must be enclosed in [] brackets.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Array value must be enclosed in [] brackets.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::FormatError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nValue = str.substr(1, str.size() - 2);
|
|
|
|
|
|
|
|
if (nValue.empty())
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int64_t bracketCounter = 0;
|
|
|
|
int64_t bracketCounter = 0;
|
|
|
|
bool isInString = false; // we may encounter array value separators "," inside strings; we need to ignore those
|
|
|
|
bool isInString = false; // we may encounter array value separators "," inside strings; we need to ignore those
|
|
|
|
@@ -500,7 +646,16 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
|
|
|
|
if (isInString)
|
|
|
|
if (isInString)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (c == '\\')
|
|
|
|
if (c == '\\')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (i + 1 >= str.size())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
PP_COUT_SYNTAX_ERROR("Bad escape sequence: '\\' at end of string");
|
|
|
|
|
|
|
|
return EResult::BadEscapeSequence;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
currentElement += c;
|
|
|
|
|
|
|
|
currentElement += str[i + 1];
|
|
|
|
++i;
|
|
|
|
++i;
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (c == '"')
|
|
|
|
else if (c == '"')
|
|
|
|
{
|
|
|
|
{
|
|
|
|
isInString = false;
|
|
|
|
isInString = false;
|
|
|
|
@@ -520,6 +675,7 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (c == '[')
|
|
|
|
if (c == '[')
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
// TODO: This would not be allowed by standard. Add EXT_ALLOW_MULTIDIMENSIONAL_ARRAY
|
|
|
|
if (++bracketCounter > 1)
|
|
|
|
if (++bracketCounter > 1)
|
|
|
|
currentElement += c;
|
|
|
|
currentElement += c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -528,8 +684,8 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
|
|
|
|
--bracketCounter;
|
|
|
|
--bracketCounter;
|
|
|
|
if (bracketCounter < 0)
|
|
|
|
if (bracketCounter < 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Array brackets are not balanced. (One ] too much or encountered too early)");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Array brackets are not balanced. (One ] too much or encountered too early)");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::ArrayBracketsInbalanced;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (bracketCounter >= 1)
|
|
|
|
else if (bracketCounter >= 1)
|
|
|
|
currentElement += c;
|
|
|
|
currentElement += c;
|
|
|
|
@@ -546,8 +702,8 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
|
|
|
|
|
|
|
|
|
|
|
|
if (bracketCounter != 0) // will always be a positive value because negative values are caught earlier
|
|
|
|
if (bracketCounter != 0) // will always be a positive value because negative values are caught earlier
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Array brackets are not balanced. (Missing " << bracketCounter << " closing brackets)");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Array brackets are not balanced. (Missing " << bracketCounter << " closing brackets)");
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::ArrayBracketsInbalanced;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!currentElement.empty())
|
|
|
|
if (!currentElement.empty())
|
|
|
|
@@ -555,11 +711,12 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
|
|
|
|
size_t lastTypeIdHash = 0;
|
|
|
|
size_t lastTypeIdHash = 0;
|
|
|
|
bool hasTypeHash = false;
|
|
|
|
bool hasTypeHash = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EResult result;
|
|
|
|
for (auto& elem : elements)
|
|
|
|
for (auto& elem : elements)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto parsed = ParseValue(elem);
|
|
|
|
auto parsed = ParseValue(elem, &result);
|
|
|
|
if (parsed == nullptr)
|
|
|
|
if (parsed == nullptr)
|
|
|
|
return EResult::FormatError;
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasTypeHash)
|
|
|
|
if (!hasTypeHash)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -569,8 +726,9 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
|
|
|
|
else if (typeid(*parsed).hash_code() != lastTypeIdHash)
|
|
|
|
else if (typeid(*parsed).hash_code() != lastTypeIdHash)
|
|
|
|
return EResult::ArrayDataTypeInconsistency;
|
|
|
|
return EResult::ArrayDataTypeInconsistency;
|
|
|
|
|
|
|
|
|
|
|
|
m_values.push_back(std::move(parsed));
|
|
|
|
m_values.push_back(parsed.release());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -602,6 +760,7 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::ToString(std::string& de
|
|
|
|
valueString = valueString.substr(0, valueString.size() - 2);
|
|
|
|
valueString = valueString.substr(0, valueString.size() - 2);
|
|
|
|
|
|
|
|
|
|
|
|
destination = "[" + valueString + "]";
|
|
|
|
destination = "[" + valueString + "]";
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -629,7 +788,7 @@ minipp::EResult minipp::MiniPPFile::Section::GetSubSection(const std::string& ke
|
|
|
|
auto it = m_subSections.find(thisKey);
|
|
|
|
auto it = m_subSections.find(thisKey);
|
|
|
|
if (it == m_subSections.end())
|
|
|
|
if (it == m_subSections.end())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Sub-Section not found: " << thisKey);
|
|
|
|
PP_COUT_SYNTAX_ERROR("Sub-Section not found: " << thisKey);
|
|
|
|
return EResult::SectionNotPresent;
|
|
|
|
return EResult::SectionNotPresent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rest.empty())
|
|
|
|
if (rest.empty())
|
|
|
|
@@ -643,70 +802,31 @@ minipp::EResult minipp::MiniPPFile::Section::GetSubSection(const std::string& ke
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Section::SetSubSection(const std::string& name, std::unique_ptr<Section> value, bool allowOverwrite) noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Section::SetSubSection(const std::string& name, std::unique_ptr<Section> value, bool allowOverwrite) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (m_subSections.find(name) != m_subSections.end())
|
|
|
|
auto pathIndex = name.find('.');
|
|
|
|
|
|
|
|
std::string thisName = name;
|
|
|
|
|
|
|
|
std::string rest{};
|
|
|
|
|
|
|
|
if (pathIndex != std::string::npos)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
thisName = name.substr(0, pathIndex);
|
|
|
|
|
|
|
|
rest = name.substr(pathIndex + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (rest.empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (m_subSections.find(thisName) != m_subSections.end())
|
|
|
|
if (!allowOverwrite)
|
|
|
|
if (!allowOverwrite)
|
|
|
|
return EResult::SectionAlreadyPresent;
|
|
|
|
return EResult::SectionAlreadyPresent;
|
|
|
|
else
|
|
|
|
else
|
|
|
|
delete m_subSections[name];
|
|
|
|
delete m_subSections[thisName];
|
|
|
|
|
|
|
|
|
|
|
|
m_subSections[name] = value.release();
|
|
|
|
m_subSections[thisName] = value.release();
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<minipp::MiniPPFile::Value> minipp::MiniPPFile::ParseValue(std::string value)
|
|
|
|
if (m_subSections.find(thisName) == m_subSections.end())
|
|
|
|
{
|
|
|
|
m_subSections[thisName] = new Section();
|
|
|
|
char valueFirstChar = value.front();
|
|
|
|
|
|
|
|
char valueLastChar = value.back();
|
|
|
|
|
|
|
|
if (valueFirstChar == '"')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (valueLastChar != '"')
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// is string
|
|
|
|
return m_subSections[thisName]->SetSubSection(rest, std::move(value), allowOverwrite);
|
|
|
|
value = value.substr(1, value.size() - 2);
|
|
|
|
|
|
|
|
auto strValue = std::make_unique<Values::StringValue>();
|
|
|
|
|
|
|
|
auto parseResult = strValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return strValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (valueLastChar == 'e')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto boolValue = std::make_unique<Values::BooleanValue>();
|
|
|
|
|
|
|
|
auto parseResult = boolValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return boolValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (valueLastChar == 'f')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto floatValue = std::make_unique<Values::FloatValue>();
|
|
|
|
|
|
|
|
auto parseResult = floatValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return floatValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (valueLastChar == ']')
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto arrayValue = std::make_unique<Values::ArrayValue>();
|
|
|
|
|
|
|
|
auto parseResult = arrayValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return arrayValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
auto intValue = std::make_unique<Values::IntValue>();
|
|
|
|
|
|
|
|
auto parseResult = intValue->Parse(value);
|
|
|
|
|
|
|
|
if (!IsResultOk(parseResult))
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return intValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::ofstream& ofs, std::string partTreeName) noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::ofstream& ofs, std::string partTreeName) noexcept
|
|
|
|
@@ -718,12 +838,12 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!Tools::IsNameValid(pair.first))
|
|
|
|
if (!Tools::IsNameValid(pair.first))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Invalid name for key: " << pair.first);
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid name for key: " << pair.first);
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::InvalidName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& comment : pair.second->m_comments)
|
|
|
|
for (const auto& comment : pair.second->m_comments)
|
|
|
|
ofs << comment << std::endl;
|
|
|
|
ofs << "# " << comment << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
ofs << pair.first << " = ";
|
|
|
|
ofs << pair.first << " = ";
|
|
|
|
auto result = pair.second->ToString(valueString);
|
|
|
|
auto result = pair.second->ToString(valueString);
|
|
|
|
@@ -733,6 +853,7 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ofs << std::endl;
|
|
|
|
ofs << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!partTreeName.empty())
|
|
|
|
if (!partTreeName.empty())
|
|
|
|
partTreeName += ".";
|
|
|
|
partTreeName += ".";
|
|
|
|
|
|
|
|
|
|
|
|
@@ -740,8 +861,8 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!Tools::IsNameValid(pair.first))
|
|
|
|
if (!Tools::IsNameValid(pair.first))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT("Invalid name for section: " << pair.first);
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid name for section: " << pair.first);
|
|
|
|
return EResult::FormatError;
|
|
|
|
return EResult::InvalidName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& comment : pair.second->m_comments)
|
|
|
|
for (const auto& comment : pair.second->m_comments)
|
|
|
|
@@ -756,10 +877,23 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
|
|
|
|
minipp::EResult minipp::MiniPPFile::Parse(const std::string& path, bool additional) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::ifstream ifs;
|
|
|
|
std::ifstream ifs;
|
|
|
|
ifs.open(path);
|
|
|
|
ifs.open(path);
|
|
|
|
|
|
|
|
return Parse(ifs, additional);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Parse(std::ifstream& ifs, bool additional) noexcept
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
#define PP_COUT_HERE() PP_COUT_SYNTAX_ERROR_LINE(lineCounter, currentLine << " <- HERE");
|
|
|
|
|
|
|
|
if (!additional)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_rootSection.m_comments.clear();
|
|
|
|
|
|
|
|
m_rootSection.m_values.clear();
|
|
|
|
|
|
|
|
m_rootSection.m_subSections.clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!ifs.is_open())
|
|
|
|
if (!ifs.is_open())
|
|
|
|
return EResult::FileIOError;
|
|
|
|
return EResult::FileIOError;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -788,38 +922,41 @@ minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (lastChar != ']')
|
|
|
|
if (lastChar != ']')
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Expected ']' at the end of the line.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Expected ']' at the end of the line.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::SectionExpectedClosingBracket;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::string sectionName = currentLine.substr(1, currentLine.size() - 2);
|
|
|
|
std::string sectionPathStr = currentLine.substr(1, currentLine.size() - 2);
|
|
|
|
Tools::StringTrim(sectionName);
|
|
|
|
Tools::StringTrim(sectionPathStr);
|
|
|
|
if (sectionName.empty())
|
|
|
|
if (sectionPathStr.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Expected section path. Found empty section begin notation.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Expected section path. Found empty section begin notation.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::EmptySectionName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create section tree
|
|
|
|
// Create section tree
|
|
|
|
Section* ubSection = &m_rootSection;
|
|
|
|
Section* ubSection = &m_rootSection;
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> sectionPath = Tools::SplitByDelimiter(sectionName, '.');
|
|
|
|
std::vector<std::string> sectionPath = Tools::SplitByDelimiter(sectionPathStr, '.');
|
|
|
|
|
|
|
|
EResult result;
|
|
|
|
for (size_t i = 0; i < sectionPath.size(); ++i)
|
|
|
|
for (size_t i = 0; i < sectionPath.size(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const std::string& sectionName = sectionPath[i];
|
|
|
|
const std::string& sectionName = sectionPath[i];
|
|
|
|
if (!Tools::IsNameValid(sectionName))
|
|
|
|
if (!Tools::IsNameValid(sectionName))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Invalid section name. (\"" << sectionName << "\") May only contain [a - z][A - Z][0 - 9] and _.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid section name. (\"" << sectionName << "\") May only contain [a - z][A - Z][0 - 9] and _.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::InvalidName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EResult result;
|
|
|
|
|
|
|
|
result = ubSection->SetSubSection(sectionName, std::make_unique<Section>(), false);
|
|
|
|
result = ubSection->SetSubSection(sectionName, std::make_unique<Section>(), false);
|
|
|
|
if (result == EResult::SectionAlreadyPresent && i == sectionPath.size() - 1)
|
|
|
|
if (result == EResult::SectionAlreadyPresent && i == sectionPath.size() - 1)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "All (sub-) sections may only be defined once.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("All (sub-) sections may only be defined once.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::SectionAlreadyPresent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result = ubSection->GetSubSection(sectionName, &ubSection);
|
|
|
|
ubSection->GetSubSection(sectionName, &ubSection);
|
|
|
|
ASSERT(IsResultOk(result)); // should never happen
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentSection = ubSection;
|
|
|
|
currentSection = ubSection;
|
|
|
|
currentSection->m_comments = commentBuffer;
|
|
|
|
currentSection->m_comments = commentBuffer;
|
|
|
|
@@ -828,40 +965,59 @@ minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (currentSection == nullptr)
|
|
|
|
if (currentSection == nullptr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Expected section begin before key-value pair.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Expected section begin before key-value pair.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::KeyValuePairNotInSection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int64_t keyValueDelimiterIndex = Tools::FirstIndexOf(currentLine, '=');
|
|
|
|
int64_t keyValueDelimiterIndex = Tools::FirstIndexOf(currentLine, '=');
|
|
|
|
if (keyValueDelimiterIndex == -1)
|
|
|
|
if (keyValueDelimiterIndex == -1)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Expected '=' in line.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Expected '=' in line.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::ExpectedKeyValuePair;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto keyValuePair = Tools::SplitInTwo(currentLine, keyValueDelimiterIndex);
|
|
|
|
auto keyValuePair = Tools::SplitInTwo(currentLine, keyValueDelimiterIndex);
|
|
|
|
Tools::StringTrim(keyValuePair.first);
|
|
|
|
Tools::StringTrim(keyValuePair.first);
|
|
|
|
Tools::StringTrim(keyValuePair.second);
|
|
|
|
Tools::StringTrim(keyValuePair.second);
|
|
|
|
|
|
|
|
|
|
|
|
if (keyValuePair.first.empty())
|
|
|
|
if (keyValuePair.first.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Expected key in line.");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Expected key in line.");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::KeyEmpty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Tools::IsNameValid(keyValuePair.first))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
PP_COUT_SYNTAX_ERROR("Invalid key name. (\"" << keyValuePair.first << "\") May only contain [a - z][A - Z][0 - 9] and _.");
|
|
|
|
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::InvalidName;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (keyValuePair.second.empty())
|
|
|
|
if (keyValuePair.second.empty())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Empty keys are not allowed");
|
|
|
|
PP_COUT_SYNTAX_ERROR("Empty keys are not allowed");
|
|
|
|
return EResult::FormatError;
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return EResult::ValueEmpty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto parsedValue = ParseValue(keyValuePair.second);
|
|
|
|
EResult parseResult;
|
|
|
|
|
|
|
|
auto parsedValue = Value::ParseValue(keyValuePair.second, &parseResult);
|
|
|
|
if (parsedValue == nullptr)
|
|
|
|
if (parsedValue == nullptr)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
COUT_SYNTAX_ERROR(lineCounter, "Invalid value");
|
|
|
|
PP_COUT_HERE();
|
|
|
|
return EResult::FormatError;
|
|
|
|
return parseResult;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parsedValue->m_comments = commentBuffer;
|
|
|
|
parsedValue->m_comments = commentBuffer;
|
|
|
|
commentBuffer.clear();
|
|
|
|
commentBuffer.clear();
|
|
|
|
|
|
|
|
|
|
|
|
currentSection->SetValue(keyValuePair.first, std::move(parsedValue), false);
|
|
|
|
auto valueSetResult = currentSection->SetValue(keyValuePair.first, std::move(parsedValue), false);
|
|
|
|
|
|
|
|
if (valueSetResult != EResult::Success)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
PP_COUT_SYNTAX_ERROR("Key already present: " << keyValuePair.first);
|
|
|
|
|
|
|
|
PP_COUT_HERE();
|
|
|
|
|
|
|
|
return valueSetResult;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EResult::Success;
|
|
|
|
return EResult::Success;
|
|
|
|
@@ -871,6 +1027,12 @@ minipp::EResult minipp::MiniPPFile::Write(const std::string& path) const noexcep
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::ofstream ofs;
|
|
|
|
std::ofstream ofs;
|
|
|
|
ofs.open(path);
|
|
|
|
ofs.open(path);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Write(ofs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
minipp::EResult minipp::MiniPPFile::Write(std::ofstream& ofs) const noexcept
|
|
|
|
|
|
|
|
{
|
|
|
|
if (!ofs.is_open())
|
|
|
|
if (!ofs.is_open())
|
|
|
|
return EResult::FileIOError;
|
|
|
|
return EResult::FileIOError;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -891,6 +1053,7 @@ bool minipp::MiniPPFile::Tools::StringStartsWith(const std::string& str, const s
|
|
|
|
for (size_t i = 0; i < beg.size(); ++i)
|
|
|
|
for (size_t i = 0; i < beg.size(); ++i)
|
|
|
|
if (str[i] != beg[i])
|
|
|
|
if (str[i] != beg[i])
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -902,6 +1065,7 @@ bool minipp::MiniPPFile::Tools::StringEndsWith(const std::string& str, const std
|
|
|
|
for (size_t i = 0; i < end.size(); ++i)
|
|
|
|
for (size_t i = 0; i < end.size(); ++i)
|
|
|
|
if (str[str.size() - i - 1] != end[end.size() - i - 1])
|
|
|
|
if (str[str.size() - i - 1] != end[end.size() - i - 1])
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -909,6 +1073,7 @@ void minipp::MiniPPFile::Tools::StringTrim(std::string& str)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (str.empty())
|
|
|
|
if (str.empty())
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
size_t start = 0;
|
|
|
|
size_t start = 0;
|
|
|
|
size_t end = str.size() - 1;
|
|
|
|
size_t end = str.size() - 1;
|
|
|
|
while (str[start] == ' ' || str[start] == '\t')
|
|
|
|
while (str[start] == ' ' || str[start] == '\t')
|
|
|
|
@@ -930,6 +1095,7 @@ bool minipp::MiniPPFile::Tools::IsNameValid(const std::string& name) noexcept
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -942,6 +1108,7 @@ int64_t minipp::MiniPPFile::Tools::FirstIndexOf(const std::string& str, char c)
|
|
|
|
return index;
|
|
|
|
return index;
|
|
|
|
++index;
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -954,6 +1121,7 @@ int64_t minipp::MiniPPFile::Tools::LastIndexOf(const std::string& str, char c) n
|
|
|
|
return index;
|
|
|
|
return index;
|
|
|
|
--index;
|
|
|
|
--index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -980,6 +1148,7 @@ std::vector<std::string> minipp::MiniPPFile::Tools::SplitByDelimiter(const std::
|
|
|
|
|
|
|
|
|
|
|
|
if (!tmp.empty())
|
|
|
|
if (!tmp.empty())
|
|
|
|
elements.push_back(tmp);
|
|
|
|
elements.push_back(tmp);
|
|
|
|
|
|
|
|
|
|
|
|
return elements;
|
|
|
|
return elements;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -993,6 +1162,7 @@ bool minipp::MiniPPFile::Tools::IsIntegerDecimal(const std::string& str) noexcep
|
|
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
|
|
if (str[i] < '0' || str[i] > '9')
|
|
|
|
if (str[i] < '0' || str[i] > '9')
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|