• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ParadoxGameConverters / commonItems / 19965179072

05 Dec 2025 01:57PM UTC coverage: 78.841% (+0.03%) from 78.814%
19965179072

Pull #313

github

web-flow
Merge 4659c8053 into 7988e1b09
Pull Request #313: Shove intRanges unpacking into commons.

14 of 17 new or added lines in 1 file covered. (82.35%)

1755 of 2226 relevant lines covered (78.84%)

232.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.34
/ParserHelpers.cpp
1
#include "ParserHelpers.h"
2
#include "CommonRegexes.h"
3
#include "Log.h"
4
#include "StringUtils.h"
5
#include <charconv>
6
#include <sstream>
7

8

9

10
namespace commonItems
11
{
12

13
std::string getNextLexeme(std::istream& theStream);
14

15

16
void ignoreItem([[maybe_unused]] const std::string& unused, std::istream& theStream)
146✔
17
{
18
        auto next = getNextLexeme(theStream);
146✔
19
        if (next == "=" || next == "?=")
146✔
20
        {
21
                next = getNextLexeme(theStream);
138✔
22
        }
23
        if (next == "rgb" || next == "hsv") // Needed for ignoring color. Example: "color2 = rgb { 2 4 8 }"
146✔
24
        {
25
                if (theStream.peek() == '{')
6✔
26
                {
27
                        next = getNextLexeme(theStream);
4✔
28
                }
29
                else // don't go further in cases like "type = rgb"
30
                {
31
                        return;
2✔
32
                }
33
        }
34
        if (next == "{")
144✔
35
        {
36
                auto braceDepth = 1;
15✔
37
                while (true)
38
                {
39
                        if (theStream.eof())
51✔
40
                        {
41
                                return;
15✔
42
                        }
43

44
                        auto token = getNextLexeme(theStream);
51✔
45
                        if (token == "{")
51✔
46
                        {
47
                                braceDepth++;
4✔
48
                        }
49
                        else if (token == "}")
47✔
50
                        {
51
                                braceDepth--;
19✔
52
                                if (braceDepth == 0)
19✔
53
                                {
54
                                        return;
15✔
55
                                }
56
                        }
57
                }
87✔
58
        }
59
}
146✔
60

61
void ignoreAndLogItem(const std::string& keyword, std::istream& theStream)
7✔
62
{
63
        Log(LogLevel::Debug) << "Ignoring keyword: " << keyword;
7✔
64
        ignoreItem(keyword, theStream);
7✔
65
}
7✔
66

67

68
void ignoreObject([[maybe_unused]] const std::string& unused, std::istream& theStream)
×
69
{
70
        auto braceDepth = 0;
×
71
        while (true)
72
        {
73
                if (theStream.eof())
×
74
                {
75
                        return;
×
76
                }
77

78
                auto token = getNextLexeme(theStream);
×
79
                if (token == "{")
×
80
                {
81
                        braceDepth++;
×
82
                }
83
                else if (token == "}")
×
84
                {
85
                        braceDepth--;
×
86
                        if (braceDepth == 0)
×
87
                        {
88
                                return;
×
89
                        }
90
                }
91
        }
×
92
}
93

94

95
void ignoreString([[maybe_unused]] const std::string& unused, std::istream& theStream)
×
96
{
97
        singleString ignore(theStream);
×
98
}
×
99

100

101

102
template <std::integral T> T stringToInteger(const std::string& str, bool skipPartialMatchWarning) // for integral types only
116✔
103
{
104
        T theInteger = 0;
116✔
105
        const auto last = str.data() + str.size();
116✔
106
        const auto [ptr, ec] = std::from_chars(str.data(), last, theInteger);
116✔
107
        if (ec != std::errc() || (!skipPartialMatchWarning && ptr != last)) // conversion either failed or was successful but not all characters matched
116✔
108
        {
109
                Log(LogLevel::Warning) << "string to integer - invalid argument: " << str;
5✔
110
        }
111
        return theInteger;
116✔
112
}
113
template signed char stringToInteger<signed char>(const std::string& str, bool skipPartialMatchWarning);
114
template unsigned char stringToInteger<unsigned char>(const std::string& str, bool skipPartialMatchWarning);
115
template short stringToInteger<short>(const std::string& str, bool skipPartialMatchWarning);
116
template unsigned short stringToInteger<unsigned short>(const std::string& str, bool skipPartialMatchWarning);
117
template int stringToInteger<int>(const std::string& str, bool skipPartialMatchWarning);
118
template unsigned int stringToInteger<unsigned int>(const std::string& str, bool skipPartialMatchWarning);
119
template long stringToInteger<long>(const std::string& str, bool skipPartialMatchWarning);
120
template unsigned long stringToInteger<unsigned long>(const std::string& str, bool skipPartialMatchWarning);
121
template long long stringToInteger<long long>(const std::string& str, bool skipPartialMatchWarning);
122
template unsigned long long stringToInteger<unsigned long long>(const std::string& str, bool skipPartialMatchWarning);
123

124
double stringToDouble(const std::string& str)
77✔
125
{
126
        double theDouble = 0.0;
77✔
127
        const auto last = str.data() + str.size();
77✔
128
        if (const auto [ptr, ec] = std::from_chars(str.data(), last, theDouble);
77✔
129
                 ec != std::errc() || ptr != last) // conversion either failed or was successful but not all characters matched
77✔
130
        {
131
                Log(LogLevel::Warning) << "string to double - invalid argument: " << str;
4✔
132
        }
133
        return theDouble;
77✔
134
}
135

136

137
[[nodiscard]] std::vector<int> getInts(std::istream& theStream)
6✔
138
{
139
        return intList{theStream}.getInts();
6✔
140
}
141

142

143
[[nodiscard]] std::vector<long long> getLlongs(std::istream& theStream)
1✔
144
{
145
        return llongList{theStream}.getLlongs();
1✔
146
}
147

148

149
[[nodiscard]] std::vector<unsigned long long> getULlongs(std::istream& theStream)
1✔
150
{
151
        return ullongList{theStream}.getULlongs();
1✔
152
}
153

154

155
[[nodiscard]] std::vector<double> getDoubles(std::istream& theStream)
9✔
156
{
157
        return doubleList{theStream}.getDoubles();
9✔
158
}
159

160

161
[[nodiscard]] std::vector<std::string> getStrings(std::istream& theStream)
26✔
162
{
163
        return stringList{theStream}.getStrings();
26✔
164
}
165

166

167
[[nodiscard]] int getInt(std::istream& theStream)
10✔
168
{
169
        return singleInt{theStream}.getInt();
10✔
170
}
171

172

173
[[nodiscard]] long long getLlong(std::istream& theStream)
2✔
174
{
175
        return singleLlong{theStream}.getLlong();
2✔
176
}
177

178

179
[[nodiscard]] unsigned long long getULlong(std::istream& theStream)
2✔
180
{
181
        return singleULlong{theStream}.getULlong();
2✔
182
}
183

184

185
[[nodiscard]] double getDouble(std::istream& theStream)
2✔
186
{
187
        return singleDouble{theStream}.getDouble();
2✔
188
}
189

190

191
[[nodiscard]] std::string getString(std::istream& theStream)
643✔
192
{
193
        return singleString{theStream}.getString();
643✔
194
}
195

196

197
intList::intList(std::istream& theStream)
13✔
198
{
199
        registerRegex(integerRegex, [this](const std::string& theInt, [[maybe_unused]] std::istream& unused) {
39✔
200
                integers.push_back(stringToInteger<int>(theInt));
26✔
201
        });
26✔
202
        registerRegex(quotedIntegerRegex, [this](const std::string& theInt, [[maybe_unused]] std::istream& unused) {
39✔
203
                const auto newInt = theInt.substr(1, theInt.size() - 2);
9✔
204
                integers.push_back(stringToInteger<int>(newInt));
9✔
205
        });
9✔
206

207
        parseStream(theStream);
13✔
208
}
13✔
209

210

211
llongList::llongList(std::istream& theStream)
8✔
212
{
213
        registerRegex(integerRegex, [this](const std::string& theLongLong, [[maybe_unused]] std::istream& unused) {
24✔
214
                llongs.push_back(stringToInteger<long long>(theLongLong));
15✔
215
        });
15✔
216
        registerRegex(quotedIntegerRegex, [this](const std::string& theLongLong, [[maybe_unused]] std::istream& unused) {
24✔
217
                const auto newLlong = theLongLong.substr(1, theLongLong.size() - 2);
6✔
218
                llongs.push_back(stringToInteger<long long>(newLlong));
6✔
219
        });
6✔
220

221
        parseStream(theStream);
8✔
222
}
8✔
223

224

225
ullongList::ullongList(std::istream& theStream)
6✔
226
{
227
        registerRegex(integerRegex, [this](const std::string& theUnsignedLongLong, [[maybe_unused]] std::istream& unused) {
18✔
228
                ullongs.push_back(stringToInteger<unsigned long long>(theUnsignedLongLong));
12✔
229
        });
12✔
230
        registerRegex(quotedIntegerRegex, [this](const std::string& theUnsignedLongLong, [[maybe_unused]] std::istream& unused) {
18✔
231
                const auto newULlong = theUnsignedLongLong.substr(1, theUnsignedLongLong.size() - 2);
3✔
232
                ullongs.push_back(stringToInteger<unsigned long long>(newULlong));
3✔
233
        });
3✔
234

235
        parseStream(theStream);
6✔
236
}
6✔
237

238
intRange::intRange(std::istream& theStream)
2✔
239
{
240
        registerRegex(integerRegex, [this](const std::string& theInt, [[maybe_unused]] std::istream& unused) {
6✔
241
                integers.push_back(stringToInteger<int>(theInt));
11✔
242
        });
11✔
243
        registerRegex(quotedIntegerRegex, [this](const std::string& theInt, [[maybe_unused]] std::istream& unused) {
6✔
NEW
244
                const auto newInt = theInt.substr(1, theInt.size() - 2);
×
NEW
245
                integers.push_back(stringToInteger<int>(newInt));
×
NEW
246
        });
×
247

248
        parseStream(theStream);
2✔
249
        if ((integers.size() % 2) != 0)
2✔
250
        {
251
                throw std::runtime_error("IntRange has an odd number of items. We cannot parse it.");
1✔
252
        }
253
        for (unsigned int i = 0; i < integers.size(); i += 2U)
4✔
254
        {
255
                const int startingInt = integers[i];
3✔
256
                const int additionalInts = integers[i + 1U];
3✔
257
                for (int j = 0; j <= additionalInts; j++)
17✔
258
                {
259
                        rangedIntegers.emplace(startingInt + j);
14✔
260
                }
261
        }
262
}
4✔
263

264

265
singleInt::singleInt(std::istream& theStream)
16✔
266
{
267
        getNextTokenWithoutMatching(theStream); // remove equals
16✔
268
        const auto token = remQuotes(*getNextTokenWithoutMatching(theStream));
16✔
269

270
        theInt = stringToInteger<int>(token);
16✔
271
}
16✔
272

273

274
singleLlong::singleLlong(std::istream& theStream)
8✔
275
{
276
        getNextTokenWithoutMatching(theStream); // remove equals
8✔
277
        const auto token = remQuotes(*getNextTokenWithoutMatching(theStream));
8✔
278

279
        theLongLong = stringToInteger<long long>(token);
8✔
280
}
8✔
281

282

283
singleULlong::singleULlong(std::istream& theStream)
6✔
284
{
285
        getNextTokenWithoutMatching(theStream); // equals
6✔
286
        const auto token = remQuotes(*getNextTokenWithoutMatching(theStream));
6✔
287

288
        theUnsignedLongLong = stringToInteger<unsigned long long>(token);
6✔
289
}
6✔
290

291

292
simpleObject::simpleObject(std::istream& theStream)
5✔
293
{
294
        getNextTokenWithoutMatching(theStream); // remove equals
5✔
295

296
        auto braceDepth = 0;
5✔
297
        std::string key;
5✔
298
        while (true)
299
        {
300
                if (theStream.eof())
73✔
301
                {
302
                        return;
×
303
                }
304

305
                char inputChar;
306
                theStream >> inputChar;
73✔
307

308
                if (inputChar == '{')
73✔
309
                {
310
                        braceDepth++;
6✔
311
                }
312
                else if (inputChar == '}')
67✔
313
                {
314
                        braceDepth--;
6✔
315
                        if (braceDepth == 0)
6✔
316
                        {
317
                                return;
5✔
318
                        }
319
                }
320
                else if (braceDepth > 1)
61✔
321
                {
322
                        // Internal object; ignore.
323
                }
324
                else if (inputChar == '=')
36✔
325
                {
326
                        auto value = getNextTokenWithoutMatching(theStream);
4✔
327
                        values[key] = *value;
4✔
328
                        key.clear();
4✔
329
                }
4✔
330
                else if (!std::isspace(inputChar))
32✔
331
                {
332
                        key += inputChar;
12✔
333
                }
334
        }
68✔
335
}
5✔
336

337

338
std::string simpleObject::getValue(const std::string& key) const
8✔
339
{
340
        if (const auto valueItr = values.find(key); valueItr != values.end())
8✔
341
        {
342
                return valueItr->second;
4✔
343
        }
344
        return {};
4✔
345
}
346

347

348
int simpleObject::getValueAsInt(const std::string& key) const
5✔
349
{
350
        const auto value = getValue(key);
5✔
351
        if (value.empty())
5✔
352
        {
353
                return 0;
3✔
354
        }
355
        return stringToInteger<int>(value);
2✔
356
}
5✔
357

358

359
doubleList::doubleList(std::istream& theStream)
24✔
360
{
361
        registerRegex(floatRegex, [this](const std::string& theDouble, [[maybe_unused]] std::istream& unused) {
72✔
362
                doubles.push_back(stringToDouble(theDouble));
61✔
363
        });
61✔
364
        registerRegex(quotedFloatRegex, [this](const std::string& theDouble, [[maybe_unused]] std::istream& unused) {
72✔
365
                const auto newDouble = remQuotes(theDouble);
6✔
366
                doubles.push_back(stringToDouble(newDouble));
6✔
367
        });
6✔
368

369
        parseStream(theStream);
24✔
370
}
24✔
371

372

373
singleDouble::singleDouble(std::istream& theStream)
7✔
374
{
375
        getNextTokenWithoutMatching(theStream); // remove equals
7✔
376
        const auto token = remQuotes(*getNextTokenWithoutMatching(theStream));
7✔
377

378
        theDouble = stringToDouble(token);
7✔
379
}
7✔
380

381

382
blobList::blobList(std::istream& theStream)
9✔
383
{
384
        auto next = getNextLexeme(theStream);
9✔
385
        if (next == "=" || next == "?=")
9✔
386
        {
387
                next = getNextLexeme(theStream);
8✔
388
        }
389
        while (true)
390
        {
391
                if (next != "{")
9✔
392
                {
393
                        break;
2✔
394
                }
395

396
                auto braceDepth = 0;
7✔
397
                std::string toReturn;
7✔
398
                while (true)
399
                {
400
                        if (theStream.eof())
316✔
401
                        {
402
                                return;
×
403
                        }
404
                        char inputChar;
405
                        theStream >> inputChar;
316✔
406
                        if (inputChar == '{')
316✔
407
                        {
408
                                if (braceDepth > 0)
18✔
409
                                {
410
                                        toReturn += inputChar;
2✔
411
                                }
412
                                braceDepth++;
18✔
413
                        }
414
                        else if (inputChar == '}')
298✔
415
                        {
416
                                braceDepth--;
25✔
417
                                if (braceDepth > 0)
25✔
418
                                {
419
                                        toReturn += inputChar;
2✔
420
                                }
421
                                else if (braceDepth == 0)
23✔
422
                                {
423
                                        blobs.emplace_back(toReturn);
16✔
424
                                        toReturn.clear();
16✔
425
                                }
426
                                else if (braceDepth == -1)
7✔
427
                                {
428
                                        return;
7✔
429
                                }
430
                        }
431
                        else if (braceDepth == 0)
273✔
432
                        {
433
                                // Ignore this character. Only look for blobs.
434
                        }
435
                        else
436
                        {
437
                                toReturn += inputChar;
171✔
438
                        }
439
                }
309✔
440
        }
7✔
441
}
9✔
442

443

444
stringList::stringList(std::istream& theStream)
31✔
445
{
446
        registerKeyword(R"("")", []([[maybe_unused]] std::istream& unused) {
93✔
447
        });
×
448
        registerRegex(stringRegex, [this](const std::string& theString, [[maybe_unused]] std::istream& unused) {
93✔
449
                strings.push_back(theString);
34✔
450
        });
34✔
451
        registerRegex(quotedStringRegex, [this](const std::string& theString, [[maybe_unused]] std::istream& unused) {
93✔
452
                strings.emplace_back(remQuotes(theString));
31✔
453
        });
31✔
454

455
        parseStream(theStream);
31✔
456
}
31✔
457

458

459
singleString::singleString(std::istream& theStream)
656✔
460
{
461
        getNextTokenWithoutMatching(theStream); // equals sign
656✔
462
        theString = remQuotes(*getNextTokenWithoutMatching(theStream));
656✔
463
}
656✔
464

465

466
stringOfItem::stringOfItem(std::istream& theStream)
16✔
467
{
468
        auto next = getNextLexeme(theStream);
16✔
469
        if (next == "=" || next == "?=")
16✔
470
        {
471
                theString += next + " ";
8✔
472
                next = getNextLexeme(theStream);
8✔
473
        }
474
        theString += next;
16✔
475

476
        if (next == "{")
16✔
477
        {
478
                bool inQuotes = false;
13✔
479
                auto braceDepth = 1;
13✔
480
                unsigned char previousCharacter = '\0';
13✔
481
                char inputChar;
482
                while (theStream >> inputChar)
271✔
483
                {
484
                        theString += inputChar;
271✔
485

486
                        if (inputChar == '\"' && previousCharacter != '\\')
271✔
487
                        {
488
                                if (!inQuotes)
10✔
489
                                {
490
                                        inQuotes = true;
5✔
491
                                }
492
                                else
493
                                {
494
                                        inQuotes = false;
5✔
495
                                }
496
                        }
497
                        if (inputChar == '{' && !inQuotes)
271✔
498
                        {
499
                                braceDepth++;
3✔
500
                        }
501
                        else if (inputChar == '}' && !inQuotes)
268✔
502
                        {
503
                                braceDepth--;
16✔
504
                                if (braceDepth == 0)
16✔
505
                                {
506
                                        return;
13✔
507
                                }
508
                        }
509
                        previousCharacter = inputChar;
258✔
510
                }
511
        }
512
}
16✔
513

514

515
stringsOfItems::stringsOfItems(std::istream& theStream)
1✔
516
{
517
        registerRegex(catchallRegex, [this](const std::string& itemName, std::istream& lambda_stream) {
3✔
518
                const stringOfItem theItem(lambda_stream);
2✔
519
                theStrings.push_back(itemName + " " + theItem.getString() + "\n");
2✔
520
        });
2✔
521

522
        parseStream(theStream);
1✔
523
}
1✔
524

525

526
stringsOfItemNames::stringsOfItemNames(std::istream& theStream)
2✔
527
{
528
        registerRegex(catchallRegex, [this](const std::string& itemName, std::istream& lambda_stream) {
6✔
529
                ignoreItem(itemName, lambda_stream);
4✔
530
                theStrings.push_back(itemName);
4✔
531
        });
4✔
532

533
        parseStream(theStream);
2✔
534
}
2✔
535

536

537
assignments::assignments(std::istream& theStream)
1✔
538
{
539
        registerRegex(catchallRegex, [this](const std::string& assignmentName, std::istream& lambda_stream) {
3✔
540
                getNextTokenWithoutMatching(lambda_stream); // remove equals
2✔
541
                auto assignmentValue = getNextTokenWithoutMatching(lambda_stream);
2✔
542
                theAssignments.emplace(std::make_pair(assignmentName, *assignmentValue));
2✔
543
        });
2✔
544

545
        parseStream(theStream);
1✔
546
}
1✔
547

548
} // namespace commonItems
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc