3#include "Foundation/Logging/Logger.h"
5#include <glm/gtc/type_ptr.hpp>
17 const bool traceParsing =
false;
19 void parseOrdinalDate(
int& day,
int& month,
int year) {
20 int dayCounts[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
22 if ((!(year % 4) && (year % 100)) || !(year % 400)) {
25 for (
int idx = 0; idx < 12; ++idx) {
28 if (day <= dayCounts[idx]) {
31 day -= dayCounts[idx];
36const size_t Cogs::Core::ParsedDataTypeSizes[] = {
59static_assert(std::size(Cogs::Core::ParsedDataTypeSizes) == (size_t(Cogs::Core::ParsedDataType::Buffer) + 1));
61const char * Cogs::Core::ParsedDataTypeNames[] = {
84static_assert(std::size(Cogs::Core::ParsedDataTypeNames) == (size_t(Cogs::Core::ParsedDataType::Buffer) + 1));
86Cogs::Core::ParsedValue::ParsedValue(
const ParsedValue & other)
93 float4x4Value = other.float4x4Value;
94 expressions = other.expressions;
95 values = other.values;
98Cogs::Core::ParsedValue::ParsedValue(ParsedValue && other)
noexcept
100 std::swap(key, other.key);
101 std::swap(value, other.value);
103 dimension = other.dimension;
104 valueHash = other.valueHash;
105 float4x4Value = other.float4x4Value;
106 std::swap(expressions, other.expressions);
107 std::swap(values, other.values);
115 dimension = other.dimension;
116 valueHash = other.valueHash;
117 float4x4Value = other.float4x4Value;
118 expressions = other.expressions;
119 values = other.values;
125bool Cogs::Core::ParsedValue::asBool(
bool& rv,
bool complain)
const
129 case Cogs::Core::ParsedDataType::String:
133 c =
static_cast<decltype(lc)::value_type
>(std::tolower(c));
148 case Cogs::Core::ParsedDataType::Float:
149 rv = floatValue != 0.f;
153 case Cogs::Core::ParsedDataType::Double:
154 rv = doubleValue != 0.f;
158 case Cogs::Core::ParsedDataType::Int:
162 case Cogs::Core::ParsedDataType::UInt:
167 case Cogs::Core::ParsedDataType::Bool:
177 LOG_ERROR(logger,
"Unable to interpret '%s' as bool.", value.c_str());
182bool Cogs::Core::ParsedValue::asFloat(
float& rv,
bool complain)
const
186 case Cogs::Core::ParsedDataType::Float:
190 case Cogs::Core::ParsedDataType::Double:
191 rv =
static_cast<float>(doubleValue);
194 case Cogs::Core::ParsedDataType::Int:
195 rv =
static_cast<float>(intValue);
198 case Cogs::Core::ParsedDataType::UInt:
199 rv =
static_cast<float>(uintValue);
202 case Cogs::Core::ParsedDataType::Bool:
203 rv = boolValue ? 1.f : 0.f;
212 LOG_ERROR(logger,
"Unable to interpret '%s' as float.", value.c_str());
217bool Cogs::Core::ParsedValue::asInt(
int& rv,
bool complain)
const
221 case Cogs::Core::ParsedDataType::Float:
222 rv =
static_cast<int>(floatValue);
225 case Cogs::Core::ParsedDataType::Double:
226 rv =
static_cast<int>(doubleValue);
229 case Cogs::Core::ParsedDataType::Int:
233 case Cogs::Core::ParsedDataType::UInt:
237 case Cogs::Core::ParsedDataType::Bool:
238 rv = boolValue ? 1 : 0;
246 LOG_ERROR(logger,
"Unable to interpret '%s' as int.", value.c_str());
253bool Cogs::Core::matchChars(
const char c,
const StringView & chars)
255 const size_t count = chars.
length();
256 const char *
const data = chars.
data();
258 for (
size_t i = 0; i < count; ++i) {
259 if (c == data[i])
return true;
267 const char * data = str.
data();
268 const size_t len = str.
size();
269 const char *
const end = data + len;
271 const char * token =
nullptr;
273 while (data != end) {
274 const bool isSplitter = matchChars(*data, splits);
276 if (!isSplitter && !token) {
278 }
else if (isSplitter && token) {
279 tokens.push_back(
StringView(token, data - token));
286 if (token) tokens.push_back(
StringView(token, data - token));
289void Cogs::Core::split(
const StringView & str,
const StringView & splits,
const int grouper, TokenStream& tokens)
291 const char * data = str.
data();
292 const size_t len = str.
size();
293 const char *
const end = data + len;
295 const char * token =
nullptr;
297 bool inGroup =
false;
298 while (data != end) {
300 if (*data == grouper) {
303 const bool isSplitter = !inGroup && matchChars(*data, splits);
305 if (!isSplitter && !token) {
308 else if (isSplitter && token) {
309 tokens.push_back(
StringView(token, data - token));
316 if (token) tokens.push_back(
StringView(token, data - token));
324 split(str, splits, tokens);
328Cogs::Core::TokenStream Cogs::Core::tokenize(
const StringView & valueStr)
333 split(valueStr,
" \t{},", tokens);
337void Cogs::Core::tokenize(
const StringView & valueStr, TokenStream & tokens)
339 split(valueStr,
" \t{},", tokens);
342void Cogs::Core::parseStringValue(
const StringView & value, ParsedValue & v)
344 thread_local static std::vector<StringView> tokens;
347 split(value,
" \t,{}[]",
'$', tokens);
349 if (tokens.empty()) {
350 LOG_ERROR(logger,
"Invalid value specification.");
352 }
else if (tokens.size() == 1) {
355 if (tokens[0] ==
"true") {
356 v.type = ParsedDataType::Bool;
358 }
else if (tokens[0] ==
"false") {
359 v.type = ParsedDataType::Bool;
362 v.type = ParsedDataType::String;
369 v.valueHash = tokens[1].hash();
371 size_t type_hash = tokens[0].hash();
372 tokens.erase(tokens.begin());
373 v.value = tokens[0].to_string();
378 v.type = ParsedDataType::String;
382 v.type = ParsedDataType::Float;
383 parseValues(v.expressions, &v.floatValue, tokens, 1);
386 v.type = ParsedDataType::Float2;
387 parseValues(v.expressions, glm::value_ptr(v.float2Value), tokens, 2);
390 v.type = ParsedDataType::Float3;
391 parseValues(v.expressions, glm::value_ptr(v.float3Value), tokens, 3);
394 v.type = ParsedDataType::Float4;
395 parseValues(v.expressions, glm::value_ptr(v.float4Value), tokens, 4);
399 v.type = ParsedDataType::Float4x4;
400 parseValues(v.expressions, glm::value_ptr(v.float4Value), tokens, 16);
404 v.type = ParsedDataType::Int;
405 parseValues(v.expressions, &v.intValue, tokens, 1);
408 v.type = ParsedDataType::Int2;
409 parseValues(v.expressions, glm::value_ptr(v.int2Value), tokens, 2);
412 v.type = ParsedDataType::Int3;
413 parseValues(v.expressions, glm::value_ptr(v.int3Value), tokens, 3);
416 v.type = ParsedDataType::Int4;
417 parseValues(v.expressions, glm::value_ptr(v.int4Value), tokens, 4);
421 v.type = ParsedDataType::UInt;
422 parseValues(v.expressions, &v.uintValue, tokens, 1);
425 v.type = ParsedDataType::UInt2;
426 parseValues(v.expressions, glm::value_ptr(v.uint2Value), tokens, 2);
429 v.type = ParsedDataType::UInt3;
430 parseValues(v.expressions, glm::value_ptr(v.uint3Value), tokens, 3);
433 v.type = ParsedDataType::UInt4;
434 parseValues(v.expressions, glm::value_ptr(v.uint4Value), tokens, 4);
438 v.type = ParsedDataType::Double;
439 parseValues(v.expressions, &v.doubleValue, tokens, 1);
443 v.type = ParsedDataType::Bool;
444 parseValues(v.expressions, &v.boolValue, tokens, 1);
449 v.type = ParsedDataType::Texture2D;
450 v.value = tokens[0].to_string();
451 for (
size_t i = 1, n = tokens.size(); i < n; i++) {
454 v.texture.flags |= ParsedValueTextureFlags::LinearSampler;
457 v.texture.flags &= ~ParsedValueTextureFlags::LinearSampler;
460 LOG_WARNING(logger,
"Unrecognized Texture2D keyword \"%.*s\"", StringViewFormat(tokens[i]));
466 v.type = ParsedDataType::ConstantBuffer;
467 v.value = tokens[0].to_string();
470 v.type = ParsedDataType::Buffer;
471 v.value = tokens[0].to_string();
474 v.type = ParsedDataType::String;
480Cogs::TextureFormat Cogs::Core::parseTextureFormat(
const StringView & format, Cogs::TextureFormat defaultFormat)
482 if (format.
empty())
return defaultFormat;
484 if (Cogs::TextureFormat textureFormat = Cogs::parseDataFormat(format); textureFormat != Cogs::TextureFormat::Unknown) {
485 return textureFormat;
488 LOG_ERROR(logger,
"Error parsing texture format: %.*s. Reverting to default.", StringViewFormat(format));
489 return defaultFormat;
492double Cogs::Core::parseDouble(
const StringView & token,
double defaultValue)
494 if (!token.
size())
return defaultValue;
496 return std::atof(token.
to_string().c_str());
499float Cogs::Core::parseFloat(
const StringView & token,
float defaultValue)
501 return static_cast<float>(parseDouble(token, defaultValue));
504int32_t Cogs::Core::parseInt(
const StringView & token, int32_t defaultValue)
506 if (!token.
size())
return defaultValue;
508 if (token.
size() > 2 && token[0] ==
'0' && (token[1] ==
'x' || token[1] ==
'X')) {
509 return std::strtol(token.
to_string().c_str(),
nullptr, 16);
511 return std::strtol(token.
to_string().c_str(),
nullptr, 10);
515uint32_t Cogs::Core::parseUInt(
const StringView & token, uint32_t defaultValue)
517 if (!token.
size())
return defaultValue;
519 if (token.
size() > 2 && token[0] ==
'0' && (token[1] ==
'x' || token[1] ==
'X')) {
520 return std::strtoul(token.
to_string().c_str(),
nullptr, 16);
522 return std::strtoul(token.
to_string().c_str(),
nullptr, 10);
526bool Cogs::Core::parseBool(
const StringView & token,
bool )
528 switch(token.
hash()) {
554 LOG_ERROR(logger,
"Failed to parse \"%.*s\" as bool", StringViewFormat(token));
561 if (!token.
size())
return false;
563 std::from_chars_result rc = std::from_chars(token.
data(),
566 if (rc.ec != std::errc()) {
567 LOG_ERROR(logger,
"Failed to parse \"%.*s\" as int: %s",
568 StringViewFormat(token),
569 std::make_error_code(rc.ec).message().c_str());
581 size_t t = iso8601.
find(
"T");
583 uint32_t milliseconds = 0;
589 assert(iso8601[0] !=
'P');
596 TokenStream tzTokens = split(time,
"-+Z");
597 TokenStream dateTokens = split(date,
"-");
598 TokenStream timeTokens = split(tzTokens[0],
":");
600 switch (dateTokens.size()) {
606 if (iso8601[0] ==
'-') {
608 tm.tm_mon = parseInt(dateTokens[0].substr(0, 2));
609 tm.tm_mday = parseInt(dateTokens[0].substr(2));
611 else if (dateTokens[0].size() == 8) {
613 tm.tm_year = parseInt(dateTokens[0].substr(0, 4));
614 tm.tm_mon = parseInt(dateTokens[0].substr(4, 2));
615 tm.tm_mday = parseInt(dateTokens[0].substr(6));
617 else if (dateTokens[0].size() == 7) {
619 tm.tm_year = parseInt(dateTokens[0].substr(0, 4));
620 tm.tm_mday = parseInt(dateTokens[0].substr(4));
622 parseOrdinalDate(tm.tm_mday, tm.tm_mon, tm.tm_year);
627 if (iso8601[0] ==
'-') {
629 tm.tm_mon = parseInt(dateTokens[0]);
630 tm.tm_mday = parseInt(dateTokens[1]);
635 assert(dateTokens[1][0] !=
'W');
637 if (dateTokens[1].size() == 3) {
639 tm.tm_year = parseInt(dateTokens[0]);
640 tm.tm_mday = parseInt(dateTokens[1]);
642 parseOrdinalDate(tm.tm_mday, tm.tm_mon, tm.tm_year);
646 tm.tm_year = parseInt(dateTokens[0]);
647 tm.tm_mon = parseInt(dateTokens[1]);
655 assert (dateTokens[1][0] !=
'W');
658 tm.tm_year = parseInt(dateTokens[0]);
659 tm.tm_mon = parseInt(dateTokens[1]);
660 tm.tm_mday = parseInt(dateTokens[2]);
665 switch (timeTokens.size()) {
670 tm.tm_hour = parseInt(timeTokens[0].substr(0, 2));
672 if ((timeTokens[0][2] !=
'.') && (timeTokens[0][2] !=
',')) {
673 tm.tm_min = parseInt(timeTokens[0].substr(2, 2));
675 if ((timeTokens[0][4] !=
'.') && (timeTokens[0][4] !=
',')) {
676 tm.tm_sec = parseInt(timeTokens[0].substr(4, 2));
678 if ((timeTokens[0][6] ==
'.') || (timeTokens[0][6] ==
',')) {
697 tm.tm_hour = parseInt(timeTokens[0]);
698 tm.tm_min = parseInt(timeTokens[1]);
700 if ((timeTokens[1][2] ==
'.') || (timeTokens[1][2] ==
',')) {
709 tm.tm_hour = parseInt(timeTokens[0]);
710 tm.tm_min = parseInt(timeTokens[1]);
711 tm.tm_sec = parseInt(timeTokens[2]);
713 if ((timeTokens[2][2] ==
'.') || (timeTokens[2][2] ==
',')) {
720 if (tzTokens.size() == 2) {
721 TokenStream parts = split(tzTokens[1],
":");
725 switch (parts.size()) {
727 tzHours = parseInt(parts[0].substr(0, 2));
728 tzMinutes = parseInt(parts[0].substr(2, 2));
732 tzHours = parseInt(parts[0]);
733 tzMinutes = parseInt(parts[1]);
736 tzOffset = (tzHours * 3600) + (tzMinutes * 60) * 1000;
738 if (tzTokens[1][0] ==
'-') {
739 tzOffset = -tzOffset;
750 std::time_t now = std::time(0);
752 tm.tm_year = std::localtime(&now)->tm_year;
760 std::time_t basetime = mktime(&tm);
763 tm = *std::localtime(&basetime);
767 tm = *std::gmtime(&basetime);
771 return ((basetime + diff) * 1000) + milliseconds;
780 thread_local static std::vector<StringView> tokens;
782 split(token,
"| ", tokens);
784 if (tokens.size() > 1) {
786 for (
auto & t : tokens) {
792 LOG_ERROR(logger,
"Could not find enumerator \"%.*s\" in %s.", StringViewFormat(t), type.
getName().
c_str());
804 LOG_ERROR(logger,
"Could not find enumerator \"%.*s\" in %s.", StringViewFormat(token), type.
getName().
c_str());
812 bool isCharacter(
char c)
814 return ((c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z'));
816 bool isNumber(
char c)
818 return (c >=
'0' && c <=
'9');
821 int parseEnumFlags(
const char *& current,
const char * end,
const Reflection::Type & type,
int defaultValue)
825 bool isLogicalAnd =
false;
826 while (current != end) {
827 if (*current ==
'~') {
830 }
else if (*current ==
'(') {
831 auto scopeValue = parseEnumFlags(++current, end, type, defaultValue);
834 value &= flip ? ~scopeValue : scopeValue;
836 value |= flip ? ~scopeValue : scopeValue;
838 }
else if (*current ==
')') {
841 }
else if (*current ==
'|') {
842 isLogicalAnd =
false;
844 }
else if (*current ==
'&') {
847 }
else if (isNumber(*current)) {
848 char* endptr =
nullptr;
849 value = strtoul(current, &endptr, 0);
851 }
else if (isCharacter(*current)) {
852 auto tokenFirst = current;
853 const char * tokenLast = tokenFirst;
854 const char * tokenCurrent = tokenFirst;
856 while (tokenCurrent && tokenCurrent != end) {
857 if (isCharacter(*tokenCurrent) || (tokenCurrent!=tokenFirst && isNumber(*tokenCurrent))) {
859 tokenLast = tokenCurrent;
861 tokenCurrent =
nullptr;
865 auto scopeValue = parseEnum(
StringView(tokenFirst, tokenLast - tokenFirst), type, defaultValue);
867 value &= flip ? ~scopeValue : scopeValue;
869 value |= flip ? ~scopeValue : scopeValue;
872 }
else if (current != end) {
883 if (token.
empty())
return defaultValue;
885 auto begin = token.
begin();
886 return parseEnumFlags(begin, token.
end(), type, defaultValue);
893 template<
typename T,
typename Func>
894 void parseValues(T * values,
const TokenStream & tokens,
size_t expected, Func f)
896 if (tokens.size() < expected) {
897 if (traceParsing) LOG_DEBUG(logger,
"Expected %zu values, got %zu.\n", expected, tokens.size());
900 const auto count = std::min(tokens.size(), expected);
902 for (
size_t i = 0; i < count; ++i) {
903 values[i] = f(tokens[i], T());
907 template<
typename T,
typename Func>
908 void parseValues(std::vector<std::pair<size_t, std::string>>& expressions, T * values,
const TokenStream & tokens,
size_t expected, Func f)
910 if (tokens.size() < expected) {
911 if (traceParsing) LOG_DEBUG(logger,
"Expected %zu values, got %zu.\n", expected, tokens.size());
914 const auto count = std::min(tokens.size(), expected);
916 for (
size_t i = 0; i < count; ++i) {
917 const auto & token = tokens[0];
919 if (!token.
empty() && *token.
begin() ==
'$') {
920 auto end = token.
find(
"$", 1);
922 expressions.push_back(std::make_pair(i, token.
substr(1, end).
to_string()));
927 values[i] = f(tokens[i], T());
936void Cogs::Core::parseQueryString(std::vector<ParsedValue>& attributes,
StringView query)
940 a = a + (a == 0 ? 0 : 1);
941 auto b = query.
find(
"&", a);
943 if (attributeValue.length()) {
944 auto sepLoc = attributeValue.
find(
"=");
945 auto key = attributeValue.substr(0, sepLoc);
947 ParsedValue attribute;
948 attribute.key = key.to_string();
950 parseStringValue(
"true", attribute);
953 parseStringValue(attributeValue.substr(sepLoc + 1), attribute);
955 attributes.push_back(attribute);
962void Cogs::Core::parseValues(
float * values,
const TokenStream & tokens,
size_t expected)
964 parseValues(values, tokens, expected, parseFloat);
967void Cogs::Core::parseValues(
double * values,
const TokenStream & tokens,
size_t expected)
969 parseValues(values, tokens, expected, parseDouble);
972void Cogs::Core::parseValues(
int * values,
const TokenStream & tokens,
size_t expected)
974 parseValues(values, tokens, expected, parseInt);
977void Cogs::Core::parseValues(uint32_t * values,
const TokenStream & tokens,
size_t expected)
979 parseValues(values, tokens, expected, parseUInt);
982void Cogs::Core::parseValues(
bool * values,
const TokenStream & tokens,
size_t expected)
984 parseValues(values, tokens, expected, parseBool);
988void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
float * values,
const TokenStream & tokens,
size_t expected)
990 parseValues(expressions, values, tokens, expected, parseFloat);
993void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
double * values,
const TokenStream & tokens,
size_t expected)
995 parseValues(expressions, values, tokens, expected, parseFloat);
998void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
int * values,
const TokenStream & tokens,
size_t expected)
1000 parseValues(expressions, values, tokens, expected, parseInt);
1003void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, uint32_t * values,
const TokenStream & tokens,
size_t expected)
1005 parseValues(expressions, values, tokens, expected, parseUInt);
1008void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
bool * values,
const TokenStream & tokens,
size_t expected)
1010 parseValues(expressions, values, tokens, expected, parseBool);
Log implementation class.
Represents a discrete type definition, describing a native type class.
const Enumerator * getEnumerator(const Name &name) const
Get a pointer to the enumerator with the given name.
bool isEnum() const
Get if the type is an enumeration type.
constexpr const Name & getName() const
Get the unique name of the type.
Provides a weakly referenced view over the contents of a string.
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
constexpr iterator begin() noexcept
Iterator to the beginning of the string.
constexpr size_t size() const noexcept
Get the size of the string.
constexpr iterator end() noexcept
Iterator to the end of the string.
constexpr size_t length() const noexcept
Get the length of the string.
constexpr bool empty() const noexcept
Check if the string is empty.
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
size_t hashLowercase(size_t hashValue=Cogs::hash()) const noexcept
Get the hash code of the string converted to lowercase.
static constexpr size_t NoPosition
No position.
size_t find(const StringView &other, size_t offset=0) const noexcept
Find the given string segment inside the string.
std::string to_string() const
String conversion method.
constexpr size_t hash() const noexcept
Get the hash code of the string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool parseBool_(bool &rv, const StringView &token)
Parse a bool putting return value in rv and returning whether or not parsing was successful.
uint64_t COGSCORE_DLL_API parseISO8601(const StringView &iso8601)
Parse ISO 8601 timestamps.
bool parseInt_(int32_t &rv, const StringView &token)
Parse an int putting return value in rv and returning whether or not parsing was successful.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
COGSFOUNDATION_API size_t hashLowercase(std::string_view str, size_t hashValue=Cogs::hash()) noexcept
Get the hash code of the string converted to lowercase.
Stores the parsed output of a key/value pair.
std::string key
The input key.
std::string value
The value is available for all non vector types.
size_t dimension
This value is for external use.
size_t valueHash
The hash is only set for values where the type is explicitly given.
ParsedDataType type
The data type of the value. Determines type stored in union below.
int getValue() const
Get the value of the enumerator.
const char * c_str() const
Gets the name as a null-terminated string.