Cogs.Core
Parsing.cpp
1#include "Parsing.h"
2
3#include "Foundation/Logging/Logger.h"
4
5#include <glm/gtc/type_ptr.hpp>
6
7#include <algorithm>
8#include <ctime>
9#include <charconv>
10
11using namespace Cogs;
12
13namespace
14{
16
17 const bool traceParsing = false;
18
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};
21
22 if ((!(year % 4) && (year % 100)) || !(year % 400)) {
23 dayCounts[1] = 29;
24 }
25 for (int idx = 0; idx < 12; ++idx) {
26 month++;
27
28 if (day <= dayCounts[idx]) {
29 break;
30 }
31 day -= dayCounts[idx];
32 }
33 }
34}
35
36const size_t Cogs::Core::ParsedDataTypeSizes[] = {
37 0, // Unknown
38 0, // Object,
39 0, // String,
40 4, // Float
41 8, // Float2
42 12, // Float3
43 16, // Float4
44 64, // Float4x4
45 4, // Int
46 8, // Int2
47 12, // Int3
48 16, // Int4
49 4, // UInt
50 8, // UInt2
51 12, // UInt3
52 16, // UInt4
53 8, // Double
54 1, // Bool
55 0, // Texture2D
56 0, // ConstantBuffer
57 0, // Buffer
58};
59static_assert(std::size(Cogs::Core::ParsedDataTypeSizes) == (size_t(Cogs::Core::ParsedDataType::Buffer) + 1));
60
61const char * Cogs::Core::ParsedDataTypeNames[] = {
62 "Unknown",
63 "Object",
64 "string",
65 "float",
66 "float2",
67 "float3",
68 "float4",
69 "float4x4",
70 "int",
71 "int2",
72 "int3",
73 "int4",
74 "uint",
75 "uint2",
76 "uint3",
77 "uint4",
78 "double",
79 "bool",
80 "Texture2D",
81 "ConstantBuffer",
82 "Buffer",
83};
84static_assert(std::size(Cogs::Core::ParsedDataTypeNames) == (size_t(Cogs::Core::ParsedDataType::Buffer) + 1));
85
86Cogs::Core::ParsedValue::ParsedValue(const ParsedValue & other)
87{
88 key = other.key;
89 value = other.value;
90 type = other.type;
91 dimension = other.dimension;
92 valueHash = other.valueHash;
93 float4x4Value = other.float4x4Value;
94 expressions = other.expressions;
95 values = other.values;
96}
97
98Cogs::Core::ParsedValue::ParsedValue(ParsedValue && other) noexcept
99{
100 std::swap(key, other.key);
101 std::swap(value, other.value);
102 type = other.type;
103 dimension = other.dimension;
104 valueHash = other.valueHash;
105 float4x4Value = other.float4x4Value;
106 std::swap(expressions, other.expressions);
107 std::swap(values, other.values);
108}
109
110Cogs::Core::ParsedValue & Cogs::Core::ParsedValue::operator=(const ParsedValue & other)
111{
112 key = other.key;
113 value = other.value;
114 type = other.type;
115 dimension = other.dimension;
116 valueHash = other.valueHash;
117 float4x4Value = other.float4x4Value;
118 expressions = other.expressions;
119 values = other.values;
120
121 return *this;
122}
123
124
125bool Cogs::Core::ParsedValue::asBool(bool& rv, bool complain) const
126{
127 switch (type)
128 {
129 case Cogs::Core::ParsedDataType::String:
130 {
131 auto lc = value;
132 for (auto& c : lc) {
133 c = static_cast<decltype(lc)::value_type>(std::tolower(c));
134 }
135 switch (StringView(lc).hash())
136 {
137 case Cogs::hash("true"):
138 rv = true;
139 return true;
140 case Cogs::hash("false"):
141 rv = false;
142 return true;
143 default:
144 break;
145 }
146 break;
147 }
148 case Cogs::Core::ParsedDataType::Float:
149 rv = floatValue != 0.f;
150 return true;
151 break;
152
153 case Cogs::Core::ParsedDataType::Double:
154 rv = doubleValue != 0.f;
155 return true;
156 break;
157
158 case Cogs::Core::ParsedDataType::Int:
159 rv = intValue != 0;
160 return true;
161 break;
162 case Cogs::Core::ParsedDataType::UInt:
163 rv = intValue != 0;
164 return true;
165 break;
166
167 case Cogs::Core::ParsedDataType::Bool:
168 rv = boolValue;
169 return true;
170 break;
171
172 default:
173 break;
174 }
175
176 if (complain) {
177 LOG_ERROR(logger, "Unable to interpret '%s' as bool.", value.c_str());
178 }
179 return false;
180}
181
182bool Cogs::Core::ParsedValue::asFloat(float& rv, bool complain) const
183{
184 switch (type)
185 {
186 case Cogs::Core::ParsedDataType::Float:
187 rv = floatValue;
188 return true;
189 break;
190 case Cogs::Core::ParsedDataType::Double:
191 rv = static_cast<float>(doubleValue);
192 return true;
193 break;
194 case Cogs::Core::ParsedDataType::Int:
195 rv = static_cast<float>(intValue);
196 return true;
197 break;
198 case Cogs::Core::ParsedDataType::UInt:
199 rv = static_cast<float>(uintValue);
200 return true;
201 break;
202 case Cogs::Core::ParsedDataType::Bool:
203 rv = boolValue ? 1.f : 0.f;
204 return true;
205 break;
206 //case Cogs::Core::ParsedDataType::Double:
207 // break;
208 default:
209 break;
210 }
211 if (complain) {
212 LOG_ERROR(logger, "Unable to interpret '%s' as float.", value.c_str());
213 }
214 return false;
215}
216
217bool Cogs::Core::ParsedValue::asInt(int& rv, bool complain) const
218{
219 switch (type)
220 {
221 case Cogs::Core::ParsedDataType::Float:
222 rv = static_cast<int>(floatValue);
223 return true;
224 break;
225 case Cogs::Core::ParsedDataType::Double:
226 rv = static_cast<int>(doubleValue);
227 return true;
228 break;
229 case Cogs::Core::ParsedDataType::Int:
230 rv = intValue;
231 return true;
232 break;
233 case Cogs::Core::ParsedDataType::UInt:
234 rv = uintValue;
235 return true;
236 break;
237 case Cogs::Core::ParsedDataType::Bool:
238 rv = boolValue ? 1 : 0;
239 return true;
240 break;
241
242 default:
243 break;
244 }
245 if (complain) {
246 LOG_ERROR(logger, "Unable to interpret '%s' as int.", value.c_str());
247 }
248 return false;
249}
250
251
252
253bool Cogs::Core::matchChars(const char c, const StringView & chars)
254{
255 const size_t count = chars.length();
256 const char * const data = chars.data();
257
258 for (size_t i = 0; i < count; ++i) {
259 if (c == data[i]) return true;
260 }
261
262 return false;
263}
264
265void Cogs::Core::split(const StringView & str, const StringView & splits, TokenStream& tokens)
266{
267 const char * data = str.data();
268 const size_t len = str.size();
269 const char * const end = data + len;
270
271 const char * token = nullptr;
272
273 while (data != end) {
274 const bool isSplitter = matchChars(*data, splits);
275
276 if (!isSplitter && !token) {
277 token = data;
278 } else if (isSplitter && token) {
279 tokens.push_back(StringView(token, data - token));
280 token = nullptr;
281 }
282
283 ++data;
284 }
285
286 if (token) tokens.push_back(StringView(token, data - token));
287}
288
289void Cogs::Core::split(const StringView & str, const StringView & splits, const int grouper, TokenStream& tokens)
290{
291 const char * data = str.data();
292 const size_t len = str.size();
293 const char * const end = data + len;
294
295 const char * token = nullptr;
296
297 bool inGroup = false;
298 while (data != end) {
299
300 if (*data == grouper) {
301 inGroup = !inGroup;
302 }
303 const bool isSplitter = !inGroup && matchChars(*data, splits);
304
305 if (!isSplitter && !token) {
306 token = data;
307 }
308 else if (isSplitter && token) {
309 tokens.push_back(StringView(token, data - token));
310 token = nullptr;
311 }
312
313 ++data;
314 }
315
316 if (token) tokens.push_back(StringView(token, data - token));
317}
318
319
320
321Cogs::Core::TokenStream Cogs::Core::split(const StringView & str, const StringView & splits)
322{
323 TokenStream tokens;
324 split(str, splits, tokens);
325 return tokens;
326}
327
328Cogs::Core::TokenStream Cogs::Core::tokenize(const StringView & valueStr)
329{
330 TokenStream tokens;
331 tokens.reserve(8);
332
333 split(valueStr, " \t{},", tokens);
334 return tokens;
335}
336
337void Cogs::Core::tokenize(const StringView & valueStr, TokenStream & tokens)
338{
339 split(valueStr, " \t{},", tokens);
340}
341
342void Cogs::Core::parseStringValue(const StringView & value, ParsedValue & v)
343{
344 thread_local static std::vector<StringView> tokens;
345 tokens.clear();
346
347 split(value, " \t,{}[]", '$', tokens);
348
349 if (tokens.empty()) {
350 LOG_ERROR(logger, "Invalid value specification.");
351 return;
352 } else if (tokens.size() == 1) {
353 // Make string value available.
354 v.value = value.to_string();
355 if (tokens[0] == "true") {
356 v.type = ParsedDataType::Bool;
357 v.boolValue = true;
358 } else if (tokens[0] == "false") {
359 v.type = ParsedDataType::Bool;
360 v.boolValue = false;
361 } else {
362 v.type = ParsedDataType::String;
363 v.value = value.to_string();
364 }
365 return;
366 }
367
368 // Several tokens:
369 v.valueHash = tokens[1].hash();
370
371 size_t type_hash = tokens[0].hash();
372 tokens.erase(tokens.begin());
373 v.value = tokens[0].to_string();
374
375 switch (type_hash)
376 {
377 case Cogs::hash("string"):
378 v.type = ParsedDataType::String;
379 break;
380
381 case Cogs::hash("float"):
382 v.type = ParsedDataType::Float;
383 parseValues(v.expressions, &v.floatValue, tokens, 1);
384 break;
385 case Cogs::hash("float2"):
386 v.type = ParsedDataType::Float2;
387 parseValues(v.expressions, glm::value_ptr(v.float2Value), tokens, 2);
388 break;
389 case Cogs::hash("float3"):
390 v.type = ParsedDataType::Float3;
391 parseValues(v.expressions, glm::value_ptr(v.float3Value), tokens, 3);
392 break;
393 case Cogs::hash("float4"):
394 v.type = ParsedDataType::Float4;
395 parseValues(v.expressions, glm::value_ptr(v.float4Value), tokens, 4);
396 break;
397
398 case Cogs::hash("float4x4"):
399 v.type = ParsedDataType::Float4x4;
400 parseValues(v.expressions, glm::value_ptr(v.float4Value), tokens, 16);
401 break;
402
403 case Cogs::hash("int"):
404 v.type = ParsedDataType::Int;
405 parseValues(v.expressions, &v.intValue, tokens, 1);
406 break;
407 case Cogs::hash("int2"):
408 v.type = ParsedDataType::Int2;
409 parseValues(v.expressions, glm::value_ptr(v.int2Value), tokens, 2);
410 break;
411 case Cogs::hash("int3"):
412 v.type = ParsedDataType::Int3;
413 parseValues(v.expressions, glm::value_ptr(v.int3Value), tokens, 3);
414 break;
415 case Cogs::hash("int4"):
416 v.type = ParsedDataType::Int4;
417 parseValues(v.expressions, glm::value_ptr(v.int4Value), tokens, 4);
418 break;
419
420 case Cogs::hash("uint"):
421 v.type = ParsedDataType::UInt;
422 parseValues(v.expressions, &v.uintValue, tokens, 1);
423 break;
424 case Cogs::hash("uint2"):
425 v.type = ParsedDataType::UInt2;
426 parseValues(v.expressions, glm::value_ptr(v.uint2Value), tokens, 2);
427 break;
428 case Cogs::hash("uint3"):
429 v.type = ParsedDataType::UInt3;
430 parseValues(v.expressions, glm::value_ptr(v.uint3Value), tokens, 3);
431 break;
432 case Cogs::hash("uint4"):
433 v.type = ParsedDataType::UInt4;
434 parseValues(v.expressions, glm::value_ptr(v.uint4Value), tokens, 4);
435 break;
436
437 case Cogs::hash("double"):
438 v.type = ParsedDataType::Double;
439 parseValues(v.expressions, &v.doubleValue, tokens, 1);
440 break;
441
442 case Cogs::hash("bool"):
443 v.type = ParsedDataType::Bool;
444 parseValues(v.expressions, &v.boolValue, tokens, 1);
445 break;
446
447 case Cogs::hash("Texture"):
448 case Cogs::hash("Texture2D"):
449 v.type = ParsedDataType::Texture2D;
450 v.value = tokens[0].to_string();
451 for (size_t i = 1, n = tokens.size(); i < n; i++) {
452 switch (tokens[i].hashLowercase()) {
453 case Cogs::hash("linear"):
454 v.texture.flags |= ParsedValueTextureFlags::LinearSampler;
455 break;
456 case Cogs::hash("nolinear"):
457 v.texture.flags &= ~ParsedValueTextureFlags::LinearSampler;
458 break;
459 default:
460 LOG_WARNING(logger, "Unrecognized Texture2D keyword \"%.*s\"", StringViewFormat(tokens[i]));
461 break;
462 }
463 }
464 break;
465 case Cogs::hash("ConstantBuffer"):
466 v.type = ParsedDataType::ConstantBuffer;
467 v.value = tokens[0].to_string();
468 break;
469 case Cogs::hash("Buffer"):
470 v.type = ParsedDataType::Buffer;
471 v.value = tokens[0].to_string();
472 break;
473 default:
474 v.type = ParsedDataType::String;
475 v.value = value.to_string();
476 break;
477 }
478}
479
480Cogs::TextureFormat Cogs::Core::parseTextureFormat(const StringView & format, Cogs::TextureFormat defaultFormat)
481{
482 if (format.empty()) return defaultFormat;
483
484 if (Cogs::TextureFormat textureFormat = Cogs::parseDataFormat(format); textureFormat != Cogs::TextureFormat::Unknown) {
485 return textureFormat;
486 }
487
488 LOG_ERROR(logger, "Error parsing texture format: %.*s. Reverting to default.", StringViewFormat(format));
489 return defaultFormat;
490}
491
492double Cogs::Core::parseDouble(const StringView & token, double defaultValue)
493{
494 if (!token.size()) return defaultValue;
495
496 return std::atof(token.to_string().c_str());
497}
498
499float Cogs::Core::parseFloat(const StringView & token, float defaultValue)
500{
501 return static_cast<float>(parseDouble(token, defaultValue));
502}
503
504int32_t Cogs::Core::parseInt(const StringView & token, int32_t defaultValue)
505{
506 if (!token.size()) return defaultValue;
507
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);
510 } else {
511 return std::strtol(token.to_string().c_str(), nullptr, 10);
512 }
513}
514
515uint32_t Cogs::Core::parseUInt(const StringView & token, uint32_t defaultValue)
516{
517 if (!token.size()) return defaultValue;
518
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);
521 } else {
522 return std::strtoul(token.to_string().c_str(), nullptr, 10);
523 }
524}
525
526bool Cogs::Core::parseBool(const StringView & token, bool /*defaultValue*/)
527{
528 switch(token.hash()) {
529 case Cogs::hash("True"): [[fallthrough]];
530 case Cogs::hash("true"): [[fallthrough]];
531 case Cogs::hash("1"): [[fallthrough]];
532 case Cogs::hash("On"): [[fallthrough]];
533 case Cogs::hash("on"): return true;
534 }
535 return false;
536}
537
538bool Cogs::Core::parseBool_(bool& rv, const StringView& token)
539{
540 switch (token.hashLowercase()) {
541 case Cogs::hash("true"): [[fallthrough]];
542 case Cogs::hash("1"): [[fallthrough]];
543 case Cogs::hash("on"):
544 rv = true;
545 return true;
546
547 case Cogs::hash("false"): [[fallthrough]];
548 case Cogs::hash("0"): [[fallthrough]];
549 case Cogs::hash("off"):
550 rv = false;
551 return true;
552
553 default:
554 LOG_ERROR(logger, "Failed to parse \"%.*s\" as bool", StringViewFormat(token));
555 return false;
556 }
557}
558
559bool Cogs::Core::parseInt_(int32_t& rv, const StringView& token)
560{
561 if (!token.size()) return false;
562
563 std::from_chars_result rc = std::from_chars(token.data(),
564 token.data() + token.length(),
565 rv);
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());
570 return false;
571 }
572 return true;
573}
574
575uint64_t Cogs::Core::parseISO8601(const StringView& iso8601) {
576 int tzOffset;
577 return parseISO8601(iso8601, tzOffset);
578}
579
580uint64_t Cogs::Core::parseISO8601(const StringView& iso8601, int& tzOffset) {
581 size_t t = iso8601.find("T");
582 std::tm tm = {};
583 uint32_t milliseconds = 0;
584
585 // Omitting T (yyyymmddhhmmss) is not supported.
586 assert (t != StringView::NoPosition);
587
588 // Durations are not supported.
589 assert(iso8601[0] != 'P');
590
591 // Time intervals are not supported.
592 assert(iso8601.find("/") == StringView::NoPosition);
593
594 StringView date = iso8601.substr(0, t);
595 StringView time = iso8601.substr(t + 1);
596 TokenStream tzTokens = split(time, "-+Z");
597 TokenStream dateTokens = split(date, "-");
598 TokenStream timeTokens = split(tzTokens[0], ":");
599
600 switch (dateTokens.size()) {
601 case 1: {
602 // TODO: Add support for week numbers
603 // yyyyWwwdd
604 assert(date.find("W") == StringView::NoPosition);
605
606 if (iso8601[0] == '-') {
607 // --mmdd...
608 tm.tm_mon = parseInt(dateTokens[0].substr(0, 2));
609 tm.tm_mday = parseInt(dateTokens[0].substr(2));
610 }
611 else if (dateTokens[0].size() == 8) {
612 // yyyymmdd...
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));
616 }
617 else if (dateTokens[0].size() == 7) {
618 // yyyyddd...
619 tm.tm_year = parseInt(dateTokens[0].substr(0, 4));
620 tm.tm_mday = parseInt(dateTokens[0].substr(4));
621
622 parseOrdinalDate(tm.tm_mday, tm.tm_mon, tm.tm_year);
623 }
624 break;
625 }
626 case 2: {
627 if (iso8601[0] == '-') {
628 // --mm-dd...
629 tm.tm_mon = parseInt(dateTokens[0]);
630 tm.tm_mday = parseInt(dateTokens[1]);
631 }
632 else {
633 // TODO: Add support for week numbers
634 // yyyy-Www
635 assert(dateTokens[1][0] != 'W');
636
637 if (dateTokens[1].size() == 3) {
638 // yyyy-ddd
639 tm.tm_year = parseInt(dateTokens[0]);
640 tm.tm_mday = parseInt(dateTokens[1]);
641
642 parseOrdinalDate(tm.tm_mday, tm.tm_mon, tm.tm_year);
643 }
644 else {
645 // yyyy-mm...
646 tm.tm_year = parseInt(dateTokens[0]);
647 tm.tm_mon = parseInt(dateTokens[1]);
648 }
649 }
650 break;
651 }
652 case 3: {
653 // TODO: Add support for dates with week numbers
654 // yyyy-Www-d
655 assert (dateTokens[1][0] != 'W');
656
657 // yyyy-mm-dd...
658 tm.tm_year = parseInt(dateTokens[0]);
659 tm.tm_mon = parseInt(dateTokens[1]);
660 tm.tm_mday = parseInt(dateTokens[2]);
661 break;
662 }
663 }
664
665 switch (timeTokens.size()) {
666 case 1: {
667 // hh[.hhhhh]...
668 // hhmm[.mmmmm]...
669 // hhmmss[.sssss]...
670 tm.tm_hour = parseInt(timeTokens[0].substr(0, 2));
671
672 if ((timeTokens[0][2] != '.') && (timeTokens[0][2] != ',')) {
673 tm.tm_min = parseInt(timeTokens[0].substr(2, 2));
674
675 if ((timeTokens[0][4] != '.') && (timeTokens[0][4] != ',')) {
676 tm.tm_sec = parseInt(timeTokens[0].substr(4, 2));
677
678 if ((timeTokens[0][6] == '.') || (timeTokens[0][6] == ',')) {
679 // .ssss ,ssss...
680 // TODO: Parse fractional seconds...
681 }
682 }
683 else {
684 // .mmmm ,mmmm...
685 // TODO: Parse fractional minutes...
686 }
687 }
688 else {
689 // .hhhh ,hhhh...
690 // TODO: Parse fractional hours...
691 }
692 break;
693 }
694 case 2: {
695 // hh:mm[.mmmmm]...
696
697 tm.tm_hour = parseInt(timeTokens[0]);
698 tm.tm_min = parseInt(timeTokens[1]);
699
700 if ((timeTokens[1][2] == '.') || (timeTokens[1][2] == ',')) {
701 // .mmmm ,mmmm...
702 // TODO: Parse fractional minutes...
703 }
704 break;
705 }
706 case 3: {
707 // hh:mm:ss[.sss]...
708
709 tm.tm_hour = parseInt(timeTokens[0]);
710 tm.tm_min = parseInt(timeTokens[1]);
711 tm.tm_sec = parseInt(timeTokens[2]);
712
713 if ((timeTokens[2][2] == '.') || (timeTokens[2][2] == ',')) {
714 // .ssss ,ssss...
715 // TODO: Parse fractional seconds...
716 }
717 break;
718 }
719 }
720 if (tzTokens.size() == 2) {
721 TokenStream parts = split(tzTokens[1], ":");
722 int tzHours = 0;
723 int tzMinutes = 0;
724
725 switch (parts.size()) {
726 case 1: {
727 tzHours = parseInt(parts[0].substr(0, 2));
728 tzMinutes = parseInt(parts[0].substr(2, 2));
729 break;
730 }
731 case 2: {
732 tzHours = parseInt(parts[0]);
733 tzMinutes = parseInt(parts[1]);
734 }
735 }
736 tzOffset = (tzHours * 3600) + (tzMinutes * 60) * 1000;
737
738 if (tzTokens[1][0] == '-') {
739 tzOffset = -tzOffset;
740 }
741 }
742 else {
743 tzOffset = 0;
744 }
745
746 if (tm.tm_year) {
747 tm.tm_year -= 1900;
748 }
749 else {
750 std::time_t now = std::time(0); // get time now
751
752 tm.tm_year = std::localtime(&now)->tm_year;
753 }
754 tm.tm_mon--;
755 tm.tm_isdst = -1;
756
757 // The following bullshit is necessary because there's no standard way to convert a
758 // UTC time into a Unix timestamp, so we have to calculate the difference between
759 // UTC and local time and then add that difference to the miscalculated time.
760 std::time_t basetime = mktime(&tm);
761 std::time_t diff;
762
763 tm = *std::localtime(&basetime);
764 tm.tm_isdst = -1;
765 diff = mktime(&tm);
766
767 tm = *std::gmtime(&basetime);
768 tm.tm_isdst = -1;
769 diff -= mktime(&tm);
770
771 return ((basetime + diff) * 1000) + milliseconds;
772}
773
774int Cogs::Core::parseEnum(const StringView & token, const Reflection::Type & type, int defaultValue)
775{
776 if (!type.isEnum()) {
777 return defaultValue;
778 }
779
780 thread_local static std::vector<StringView> tokens;
781 tokens.clear();
782 split(token, "| ", tokens);
783
784 if (tokens.size() > 1) {
785 int value = 0;
786 for (auto & t : tokens) {
787 auto enumerator = type.getEnumerator(t);
788
789 if (enumerator) {
790 value |= enumerator->getValue();
791 } else {
792 LOG_ERROR(logger, "Could not find enumerator \"%.*s\" in %s.", StringViewFormat(t), type.getName().c_str());
793 }
794 }
795
796 return value;
797 }
798
799 auto enumerator = type.getEnumerator(token);
800
801 if (enumerator) {
802 return enumerator->getValue();
803 } else {
804 LOG_ERROR(logger, "Could not find enumerator \"%.*s\" in %s.", StringViewFormat(token), type.getName().c_str());
805 }
806
807 return defaultValue;
808}
809
810namespace Cogs::Core
811{
812 bool isCharacter(char c)
813 {
814 return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
815 }
816 bool isNumber(char c)
817 {
818 return (c >= '0' && c <= '9');
819 }
820
821 int parseEnumFlags(const char *& current, const char * end, const Reflection::Type & type, int defaultValue)
822 {
823 int value = 0;
824 bool flip = false;
825 bool isLogicalAnd = false;
826 while (current != end) {
827 if (*current == '~') {
828 flip = true;
829 ++current;
830 } else if (*current == '(') {
831 auto scopeValue = parseEnumFlags(++current, end, type, defaultValue);
832
833 if (isLogicalAnd) {
834 value &= flip ? ~scopeValue : scopeValue;
835 } else {
836 value |= flip ? ~scopeValue : scopeValue;
837 }
838 } else if (*current == ')') {
839 ++current;
840 return value;
841 } else if (*current == '|') {
842 isLogicalAnd = false;
843 ++current;
844 } else if (*current == '&') {
845 ++current;
846 isLogicalAnd = true;
847 } else if (isNumber(*current)) {
848 char* endptr = nullptr;
849 value = strtoul(current, &endptr, 0);
850 current = endptr;
851 } else if (isCharacter(*current)) {
852 auto tokenFirst = current;
853 const char * tokenLast = tokenFirst;
854 const char * tokenCurrent = tokenFirst;
855
856 while (tokenCurrent && tokenCurrent != end) {
857 if (isCharacter(*tokenCurrent) || (tokenCurrent!=tokenFirst && isNumber(*tokenCurrent))) {
858 ++tokenCurrent;
859 tokenLast = tokenCurrent;
860 } else {
861 tokenCurrent = nullptr;
862 }
863 }
864
865 auto scopeValue = parseEnum(StringView(tokenFirst, tokenLast - tokenFirst), type, defaultValue);
866 if (isLogicalAnd) {
867 value &= flip ? ~scopeValue : scopeValue;
868 } else {
869 value |= flip ? ~scopeValue : scopeValue;
870 }
871 current = tokenLast;
872 } else if (current != end) {
873 ++current;
874 }
875 }
876
877 return value;
878 }
879}
880
881int Cogs::Core::parseEnumFlags(const StringView & token, const Reflection::Type & type, int defaultValue)
882{
883 if (token.empty()) return defaultValue;
884
885 auto begin = token.begin();
886 return parseEnumFlags(begin, token.end(), type, defaultValue);
887}
888
889namespace Cogs
890{
891 namespace Core
892 {
893 template<typename T, typename Func>
894 void parseValues(T * values, const TokenStream & tokens, size_t expected, Func f)
895 {
896 if (tokens.size() < expected) {
897 if (traceParsing) LOG_DEBUG(logger, "Expected %zu values, got %zu.\n", expected, tokens.size());
898 }
899
900 const auto count = std::min(tokens.size(), expected);
901
902 for (size_t i = 0; i < count; ++i) {
903 values[i] = f(tokens[i], T());
904 }
905 }
906
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)
909 {
910 if (tokens.size() < expected) {
911 if (traceParsing) LOG_DEBUG(logger, "Expected %zu values, got %zu.\n", expected, tokens.size());
912 }
913
914 const auto count = std::min(tokens.size(), expected);
915
916 for (size_t i = 0; i < count; ++i) {
917 const auto & token = tokens[0];
918
919 if (!token.empty() && *token.begin() == '$') {
920 auto end = token.find("$", 1);
921 if (end != StringView::NoPosition) end = end - 1;
922 expressions.push_back(std::make_pair(i, token.substr(1, end).to_string()));
923
924 values[i] = T();
925 }
926 else {
927 values[i] = f(tokens[i], T());
928 }
929 }
930 }
931
932
933 }
934}
935
936void Cogs::Core::parseQueryString(std::vector<ParsedValue>& attributes, StringView query)
937{
938 size_t a = 0;
939 do {
940 a = a + (a == 0 ? 0 : 1);
941 auto b = query.find("&", a);
942 auto attributeValue = query.substr(a, b != StringView::NoPosition ? b - a : b);
943 if (attributeValue.length()) {
944 auto sepLoc = attributeValue.find("=");
945 auto key = attributeValue.substr(0, sepLoc);
946
947 ParsedValue attribute;
948 attribute.key = key.to_string();
949 if (sepLoc == StringView::NoPosition) {
950 parseStringValue("true", attribute);
951 }
952 else {
953 parseStringValue(attributeValue.substr(sepLoc + 1), attribute);
954 }
955 attributes.push_back(attribute);
956 }
957 a = b;
958 } while (a != StringView::NoPosition);
959}
960
961
962void Cogs::Core::parseValues(float * values, const TokenStream & tokens, size_t expected)
963{
964 parseValues(values, tokens, expected, parseFloat);
965}
966
967void Cogs::Core::parseValues(double * values, const TokenStream & tokens, size_t expected)
968{
969 parseValues(values, tokens, expected, parseDouble);
970}
971
972void Cogs::Core::parseValues(int * values, const TokenStream & tokens, size_t expected)
973{
974 parseValues(values, tokens, expected, parseInt);
975}
976
977void Cogs::Core::parseValues(uint32_t * values, const TokenStream & tokens, size_t expected)
978{
979 parseValues(values, tokens, expected, parseUInt);
980}
981
982void Cogs::Core::parseValues(bool * values, const TokenStream & tokens, size_t expected)
983{
984 parseValues(values, tokens, expected, parseBool);
985}
986
987
988void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, float * values, const TokenStream & tokens, size_t expected)
989{
990 parseValues(expressions, values, tokens, expected, parseFloat);
991}
992
993void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, double * values, const TokenStream & tokens, size_t expected)
994{
995 parseValues(expressions, values, tokens, expected, parseFloat);
996}
997
998void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, int * values, const TokenStream & tokens, size_t expected)
999{
1000 parseValues(expressions, values, tokens, expected, parseInt);
1001}
1002
1003void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, uint32_t * values, const TokenStream & tokens, size_t expected)
1004{
1005 parseValues(expressions, values, tokens, expected, parseUInt);
1006}
1007
1008void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, bool * values, const TokenStream & tokens, size_t expected)
1009{
1010 parseValues(expressions, values, tokens, expected, parseBool);
1011}
1012
Log implementation class.
Definition: LogManager.h:139
Represents a discrete type definition, describing a native type class.
Definition: Type.h:89
const Enumerator * getEnumerator(const Name &name) const
Get a pointer to the enumerator with the given name.
Definition: Type.cpp:145
bool isEnum() const
Get if the type is an enumeration type.
Definition: Type.h:315
constexpr const Name & getName() const
Get the unique name of the type.
Definition: Type.h:198
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
Definition: StringView.h:171
constexpr iterator begin() noexcept
Iterator to the beginning of the string.
Definition: StringView.h:100
constexpr size_t size() const noexcept
Get the size of the string.
Definition: StringView.h:178
constexpr iterator end() noexcept
Iterator to the end of the string.
Definition: StringView.h:103
constexpr size_t length() const noexcept
Get the length of the string.
Definition: StringView.h:185
constexpr bool empty() const noexcept
Check if the string is empty.
Definition: StringView.h:122
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
Definition: StringView.h:258
size_t hashLowercase(size_t hashValue=Cogs::hash()) const noexcept
Get the hash code of the string converted to lowercase.
Definition: StringView.cpp:13
static constexpr size_t NoPosition
No position.
Definition: StringView.h:43
size_t find(const StringView &other, size_t offset=0) const noexcept
Find the given string segment inside the string.
Definition: StringView.cpp:18
std::string to_string() const
String conversion method.
Definition: StringView.cpp:9
constexpr size_t hash() const noexcept
Get the hash code of the string.
Definition: StringView.h:200
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.
Definition: Parsing.cpp:538
uint64_t COGSCORE_DLL_API parseISO8601(const StringView &iso8601)
Parse ISO 8601 timestamps.
Definition: Parsing.cpp:575
bool parseInt_(int32_t &rv, const StringView &token)
Parse an int putting return value in rv and returning whether or not parsing was successful.
Definition: Parsing.cpp:559
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
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.
Definition: Parsing.h:32
std::string key
The input key.
Definition: Parsing.h:39
std::string value
The value is available for all non vector types.
Definition: Parsing.h:42
size_t dimension
This value is for external use.
Definition: Parsing.h:50
size_t valueHash
The hash is only set for values where the type is explicitly given.
Definition: Parsing.h:47
ParsedDataType type
The data type of the value. Determines type stored in union below.
Definition: Parsing.h:45
int getValue() const
Get the value of the enumerator.
Definition: Type.h:71
const char * c_str() const
Gets the name as a null-terminated string.
Definition: Name.h:116