1 /*
2 This file is part of Magnum.
3
4 Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
5 Vladimír Vondruš <mosra@centrum.cz>
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 and/or sell copies of the Software, and to permit persons to whom the
12 Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "Parsers.h"
27
28 #include <cstring>
29 #include <limits>
30 #include <tuple>
31 #include <Corrade/Utility/Debug.h>
32
33 #include "Magnum/OpenDdl/Type.h"
34
35 #ifdef CORRADE_TARGET_APPLE
36 #include <climits>
37 #endif
38
39 namespace Magnum { namespace OpenDdl { namespace Implementation {
40
operator <<(Debug & debug,const ParseErrorType value)41 Debug& operator<<(Debug& debug, const ParseErrorType value) {
42 switch(value) {
43 #define _c(value) case ParseErrorType::value: return debug << "OpenDdl::ParseErrorType::" #value;
44 _c(NoError)
45 _c(InvalidEscapeSequence)
46 _c(InvalidIdentifier)
47 _c(InvalidName)
48 _c(InvalidCharacterLiteral)
49 _c(InvalidLiteral)
50 _c(InvalidPropertyValue)
51 _c(InvalidSubArraySize)
52 _c(LiteralOutOfRange)
53 _c(ExpectedIdentifier)
54 _c(ExpectedName)
55 _c(ExpectedLiteral)
56 _c(ExpectedSeparator)
57 _c(ExpectedListStart)
58 _c(ExpectedListEnd)
59 _c(ExpectedArraySizeEnd)
60 _c(ExpectedPropertyValue)
61 _c(ExpectedPropertyAssignment)
62 _c(ExpectedPropertyListEnd)
63 #undef _c
64 }
65
66 return debug << "OpenDdl::ParseErrorType::(invalid)";
67 }
68
69 namespace {
70
71 /* Cannot use std::isnum(), std::isalpha() etc., because they depend on locale */
72 template<Int> constexpr bool isBaseN(char);
isBaseN(char c)73 template<> constexpr bool isBaseN<2>(char c) {
74 return c == '0' || c == '1';
75 }
isBaseN(char c)76 template<> constexpr bool isBaseN<8>(char c) {
77 return c >= '0' && c <= '7';
78 }
isBaseN(char c)79 template<> constexpr bool isBaseN<10>(char c) {
80 return c >= '0' && c <= '9';
81 }
isBaseN(char c)82 template<> constexpr bool isBaseN<16>(char c) {
83 return isBaseN<10>(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
84 }
isBinaryPrefix(char c)85 constexpr bool isBinaryPrefix(char c) {
86 return c == 'b' || c == 'o' || c == 'x' ||
87 c == 'B' || c == 'O' || c == 'X';
88 }
isAlpha(char c)89 constexpr bool isAlpha(char c) {
90 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
91 }
isAlphaDecimal(char c)92 constexpr bool isAlphaDecimal(char c) {
93 return isAlpha(c) || isBaseN<10>(c);
94 }
95
parseHex(const char * data)96 template<class T> inline T parseHex(const char* data) {
97 T out{};
98 for(std::size_t i = 0; i != sizeof(T)*2; ++i)
99 out |= ((data[i] >= 'a' ?
100 0xa - 'a' : (data[i] >= 'A' ?
101 0xA - 'A' :
102 0x0 - '0')) + data[i]) << (sizeof(T)*2 - i - 1)*8;
103 return out;
104 }
105
equalsPrefix(const Containers::ArrayView<const char> data,const char * const prefix)106 inline bool equalsPrefix(const Containers::ArrayView<const char> data, const char* const prefix) {
107 return std::strncmp(data, prefix, data.size()) == 0;
108 }
109
prefix(const Containers::ArrayView<const char> data,const char (& compare)[n])110 template<std::size_t n> inline const char* prefix(const Containers::ArrayView<const char> data, const char(&compare)[n]) {
111 /* Propagate errors */
112 if(!data) return nullptr;
113
114 if(data.size() < n - 1) return nullptr;
115
116 const char* const end = data + n - 1;
117 return equalsPrefix(data.prefix(end), compare) ? end : nullptr;
118 }
119
extractWithoutUnderscore(const Containers::ArrayView<const char> data,std::string & buffer)120 void extractWithoutUnderscore(const Containers::ArrayView<const char> data, std::string& buffer) {
121 buffer.clear();
122 for(char c: data) if(c != '_') buffer += c;
123 }
124
125 }
126
equals(const Containers::ArrayView<const char> a,const Containers::ArrayView<const char> b)127 bool equals(const Containers::ArrayView<const char> a, const Containers::ArrayView<const char> b) {
128 return a.size() == b.size() && equalsPrefix(a, b);
129 }
130
whitespace(const Containers::ArrayView<const char> data)131 const char* whitespace(const Containers::ArrayView<const char> data) {
132 /* Propagate error */
133 if(!data) return nullptr;
134
135 const char* i = data;
136 while(i != data.end()) {
137 /* Whitespace */
138 if(*i <= 32) ++i;
139
140 /* Comment */
141 else if(*i == '/' && i + 1 < data.end() && (i[1] == '*' || i[1] == '/'))
142 {
143 /* Single-line comment */
144 if(i[1] == '/') for(const char* j = i + 2; j != data.end(); ++j) {
145 if(*j == '\n') {
146 i = j + 1;
147 break;
148 }
149
150 /* Multi-line comment */
151 } else for(const char* j = i + 2; j != data.end(); ++j) {
152 if(*j == '*' && j + 1 != data.end() && *(j + 1) == '/') {
153 i = j + 2;
154 break;
155 }
156 }
157 }
158
159 /* Something else, done */
160 else break;
161 }
162
163 return i;
164 }
165
escapedChar(const Containers::ArrayView<const char> data,ParseError & error)166 std::pair<const char*, char> escapedChar(const Containers::ArrayView<const char> data, ParseError& error) {
167 /* Escape sequence is not standalone, thus we can't expect it anywhere */
168 CORRADE_INTERNAL_ASSERT(!data.empty() && *data == '\\');
169
170 if(data.size() < 2) {
171 error = {ParseErrorType::InvalidEscapeSequence, data};
172 return {};
173 }
174
175 /* Two-character escape */
176 switch(data[1]) {
177 case '\\': return {data + 2, '\\'};
178 case '\'': return {data + 2, '\\'};
179 case 'a': return {data + 2, '\a'};
180 case 'b': return {data + 2, '\b'};
181 case 'f': return {data + 2, '\f'};
182 case 'n': return {data + 2, '\n'};
183 case 'r': return {data + 2, '\r'};
184 case 't': return {data + 2, '\t'};
185 case 'v': return {data + 2, '\v'};
186 case '?':
187 case '"':
188 return {data + 2, data[1]};
189 }
190
191 /* Four-character escape */
192 if(data.size() >= 4 && data[1] == 'x' && isBaseN<16>(data[2]) && isBaseN<16>(data[3]))
193 return {data + 4, parseHex<char>(data + 2)};
194
195 error = {ParseErrorType::InvalidEscapeSequence, data};
196 return {};
197 }
198
escapedUnicode(const Containers::ArrayView<const char> data,std::string & out,ParseError & error)199 const char* escapedUnicode(const Containers::ArrayView<const char> data, std::string& out, ParseError& error) {
200 /* Escape sequence is not standalone, thus we can't expect it anywhere */
201 CORRADE_INTERNAL_ASSERT(!data.empty() && *data == '\\');
202
203 if(data.size() < 2) {
204 error = {ParseErrorType::InvalidEscapeSequence, data};
205 return {};
206 }
207
208 if(data.size() >= 6 && data[1] == 'u') {
209 Warning() << "Trade::OpenGexImporter::openData(): Unicode parsing not implemented";
210 out += '?';
211 return data + 6;
212 }
213
214 if(data.size() >= 8 && data[1] == 'U') {
215 Warning() << "Trade::OpenGexImporter::openData(): Unicode parsing not implemented";
216 out += '?';
217 return data + 8;
218 }
219
220 char result;
221 const char* end;
222 std::tie(end, result) = escapedChar(data, error);
223 out += result;
224 return end;
225 }
226
identifier(const Containers::ArrayView<const char> data,ParseError & error)227 const char* identifier(const Containers::ArrayView<const char> data, ParseError& error) {
228 /* Propagate errors */
229 if(!data) return nullptr;
230
231 if(data.empty()) {
232 error = {ParseErrorType::ExpectedIdentifier};
233 return nullptr;
234 }
235
236 if(!isAlpha(*data) && *data != '_') {
237 error = {ParseErrorType::InvalidIdentifier, data};
238 return nullptr;
239 }
240
241 const char* i = data + 1;
242 for(; i != data.end(); ++i) {
243 const char c = *i;
244
245 if(!isAlphaDecimal(c) && c != '_') break;
246 }
247
248 return i;
249 }
250
boolLiteral(const Containers::ArrayView<const char> data,ParseError & error)251 std::pair<const char*, bool> boolLiteral(const Containers::ArrayView<const char> data, ParseError& error) {
252 /* Propagate errors */
253 if(!data) return {};
254
255 if(const char* const end = prefix(data, "true")) return {end, true};
256 if(const char* const end = prefix(data, "false")) return {end, false};
257
258 error = {ParseErrorType::InvalidLiteral, Type::Bool, data};
259 return {};
260 }
261
characterLiteral(const Containers::ArrayView<const char> data,ParseError & error)262 std::pair<const char*, char> characterLiteral(const Containers::ArrayView<const char> data, ParseError& error) {
263 if(data.size() >= 3 && data[0] == '\'') {
264 /* Allowed ASCII character */
265 if(((data[1] >= 0x20 && data[1] < '\'') ||
266 (data[1] > '\'' && data[1] < '\\') ||
267 (data[1] > '\\' && data[1] <= 0x7e)) && data[2] == '\'')
268 return {data + 3, data[1]};
269
270 /* Escaped char */
271 if(data[1] == '\\') {
272 const char* i;
273 char result;
274 std::tie(i, result) = escapedChar(data.suffix(1), error);
275 if(i && i != data.end() && *i == '\'') return {i + 1, result};
276 }
277 }
278
279 error = {ParseErrorType::InvalidCharacterLiteral, data};
280 return {};
281 }
282
283 namespace {
284
285 template<class> struct IntegralType;
286 template<> struct IntegralType<Float> { typedef UnsignedInt Type; };
287 template<> struct IntegralType<Double> {
288 #ifndef CORRADE_TARGET_EMSCRIPTEN
289 typedef UnsignedLong Type;
290 #else
291 typedef UnsignedInt Type;
292 #endif
293 };
294 template<class T> using IntegralTypeFor = typename IntegralType<T>::Type;
295
296 template<class> struct ExtractToType;
297 /** @todo isn't there something better for extracting to unsigned int on webgl? */
298 #ifndef CORRADE_TARGET_EMSCRIPTEN
299 template<> struct ExtractToType<UnsignedLong> {
300 typedef UnsignedLong Type;
extractMagnum::OpenDdl::Implementation::__anona1fcb7be0211::ExtractToType301 static UnsignedLong extract(const std::string& buffer, Int base) {
302 return std::stoul(buffer, nullptr, base);
303 }
304 };
305 template<> struct ExtractToType<UnsignedByte>: ExtractToType<UnsignedLong> {};
306 template<> struct ExtractToType<Byte>: ExtractToType<UnsignedLong> {};
307 template<> struct ExtractToType<UnsignedShort>: ExtractToType<UnsignedLong> {};
308 template<> struct ExtractToType<Short>: ExtractToType<UnsignedLong> {};
309 template<> struct ExtractToType<UnsignedInt>: ExtractToType<UnsignedLong> {};
310 template<> struct ExtractToType<Int>: ExtractToType<UnsignedLong> {};
311 template<> struct ExtractToType<Long>: ExtractToType<UnsignedLong> {};
312 #ifdef CORRADE_TARGET_APPLE
313 template<> struct ExtractToType<unsigned long>: ExtractToType<UnsignedLong> {};
314 template<> struct ExtractToType<long>: ExtractToType<UnsignedLong> {};
315 #endif
316 #else
317 template<> struct ExtractToType<UnsignedInt> {
318 typedef UnsignedInt Type;
extractMagnum::OpenDdl::Implementation::__anona1fcb7be0211::ExtractToType319 static UnsignedInt extract(const std::string& buffer, Int base) {
320 return std::stoi(buffer, nullptr, base);
321 }
322 };
323 template<> struct ExtractToType<UnsignedByte>: ExtractToType<UnsignedInt> {};
324 template<> struct ExtractToType<Byte>: ExtractToType<UnsignedInt> {};
325 template<> struct ExtractToType<UnsignedShort>: ExtractToType<UnsignedInt> {};
326 template<> struct ExtractToType<Short>: ExtractToType<UnsignedInt> {};
327 template<> struct ExtractToType<Int>: ExtractToType<UnsignedInt> {};
328 /* Emscripten 1.38.10 and newer has std::size_t defined as unsigned long, while
329 it was unsigned int before. We should support both cases,
330 integralLiteral<std::size_t>() is used in some places in the parsers. */
331 static_assert(sizeof(unsigned long) == 4, "unsigned long is not four bytes on Emscripten");
332 template<> struct ExtractToType<unsigned long>: ExtractToType<UnsignedInt> {};
333 #endif
334 template<class T> using ExtractedType = typename ExtractToType<T>::Type;
335
336 template<> struct ExtractToType<Float> {
extractMagnum::OpenDdl::Implementation::__anona1fcb7be0211::ExtractToType337 static Float extract(const std::string& buffer) {
338 return std::stof(buffer);
339 }
340 };
341 template<> struct ExtractToType<Double> {
extractMagnum::OpenDdl::Implementation::__anona1fcb7be0211::ExtractToType342 static Double extract(const std::string& buffer) {
343 return std::stod(buffer);
344 }
345 };
346
347 template<class> constexpr Type typeFor();
348 #define _c(T) template<> constexpr Type typeFor<T>() { return Type::T; }
349 _c(UnsignedByte)
_c(Byte)350 _c(Byte)
351 _c(UnsignedShort)
352 _c(Short)
353 _c(UnsignedInt)
354 _c(Int)
355 #ifndef CORRADE_TARGET_EMSCRIPTEN
356 _c(UnsignedLong)
357 _c(Long)
358 #else
359 /* Emscripten 1.38.10 and newer has std::size_t defined as unsigned long, while
360 it was unsigned int before. We should support both cases,
361 integralLiteral<std::size_t>() is used in some places in the parsers. */
362 static_assert(sizeof(unsigned long) == 4, "unsigned long is not four bytes on Emscripten");
363 template<> constexpr Type typeFor<unsigned long>() { return Type::UnsignedInt; }
364 #endif
365 _c(Float)
366 _c(Double)
367 #undef _c
368 #ifdef CORRADE_TARGET_APPLE
369 #if LONG_MAX == 0x7FFFFFFFFFFFFFFF
370 template<> constexpr Type typeFor<unsigned long>() { return Type::UnsignedLong; }
typeFor()371 template<> constexpr Type typeFor<long>() { return Type::Long; }
372 #elif LONG_MAX == 0x7FFFFFFF
373 template<> constexpr Type typeFor<unsigned long>() { return Type::UnsignedInt; }
374 template<> constexpr Type typeFor<long>() { return Type::Int; }
375 #elif !defined(LONG_MAX)
376 #error wat
377 #else
378 blah LONG_MAX i/ugh
379 //#error Unexpected size of builtin long type
380 #endif
381 #endif
382
possiblyNumericCharacters(const Containers::ArrayView<const char> data)383 template<Int base> const char* possiblyNumericCharacters(const Containers::ArrayView<const char> data) {
384 /* Propagate errors */
385 if(!data) return {};
386
387 const char* i = data;
388 while(i != data.end() && (isBaseN<base>(*i) || (i != data && *i == '_'))) ++i;
389
390 return i;
391 }
392
numericCharacters(const Containers::ArrayView<const char> data,ParseError & error)393 template<Int base, class T> const char* numericCharacters(const Containers::ArrayView<const char> data, ParseError& error) {
394 /* Propagate errors */
395 if(!data) return {};
396
397 const char* const i = possiblyNumericCharacters<base>(data);
398
399 if(i == data) {
400 error = {ParseErrorType::InvalidLiteral, typeFor<T>(), data};
401 return {};
402 }
403
404 return i;
405 }
406
baseNLiteral(const Containers::ArrayView<const char> data,std::string & buffer,ParseError & error)407 template<Int base, class T> std::pair<const char*, T> baseNLiteral(const Containers::ArrayView<const char> data, std::string& buffer, ParseError& error) {
408 /* Propagate errors */
409 if(!data) return {};
410
411 const char* i = numericCharacters<base, T>(data, error);
412
413 /* Propagate errors */
414 if(!i) return {};
415
416 extractWithoutUnderscore(data.prefix(i), buffer);
417 const ExtractedType<T> out = ExtractToType<T>::extract(buffer, base);
418 if(out > ExtractedType<T>(std::numeric_limits<T>::max())) {
419 error = {ParseErrorType::LiteralOutOfRange, typeFor<T>(), data};
420 return {};
421 }
422
423 return {i, T(out)};
424 }
425
426 }
427
integralLiteral(const Containers::ArrayView<const char> data,std::string & buffer,ParseError & error)428 template<class T> std::tuple<const char*, T, Int> integralLiteral(const Containers::ArrayView<const char> data, std::string& buffer, ParseError& error) {
429 /* Propagate errors */
430 if(!data) return {};
431
432 if(data.empty()) {
433 error = {ParseErrorType::ExpectedLiteral, typeFor<T>(), data};
434 return {};
435 }
436
437 const char* i = data;
438
439 /* Sign */
440 T sign = T(1);
441 if(*i == '+') ++i;
442 else if(*i == '-') {
443 if(!std::is_signed<T>{}) {
444 error = {ParseErrorType::LiteralOutOfRange, typeFor<T>(), data};
445 return {};
446 }
447
448 sign = T(-1);
449 ++i;
450 }
451
452 T value;
453 Int base;
454
455 /* Char literal */
456 if(i != data.end() && *i == '\'') {
457 base = 256;
458 std::tie(i, value) = characterLiteral(data.suffix(i), error);
459
460 /* Binary/octal/hex literal */
461 } else if(i + 1 < data.end() && *i == '0' && isBinaryPrefix(i[1])) switch(i[1]) {
462 case 'x':
463 case 'X': {
464 base = 16;
465 std::tie(i, value) = baseNLiteral<16, T>(data.suffix(i + 2), buffer, error);
466 break;
467 }
468 case 'o':
469 case 'O': {
470 base = 8;
471 std::tie(i, value) = baseNLiteral<8, T>(data.suffix(i + 2), buffer, error);
472 break;
473 }
474 case 'b':
475 case 'B': {
476 base = 2;
477 std::tie(i, value) = baseNLiteral<2, T>(data.suffix(i + 2), buffer, error);
478 break;
479 }
480
481 default: std::abort(); /* LCOV_EXCL_LINE */
482
483 /* Decimal literal */
484 } else {
485 base = 10;
486 std::tie(i, value) = baseNLiteral<10, T>(data.suffix(i), buffer, error);
487 }
488
489 /** @todo C++14: use {} */
490 return std::make_tuple(i, sign*value, base);
491 }
492
493 template std::tuple<const char*, UnsignedByte, Int> integralLiteral<UnsignedByte>(Containers::ArrayView<const char>, std::string&, ParseError&);
494 template std::tuple<const char*, Byte, Int> integralLiteral<Byte>(Containers::ArrayView<const char>, std::string&, ParseError&);
495 template std::tuple<const char*, UnsignedShort, Int> integralLiteral<UnsignedShort>(Containers::ArrayView<const char>, std::string&, ParseError&);
496 template std::tuple<const char*, Short, Int> integralLiteral<Short>(Containers::ArrayView<const char>, std::string&, ParseError&);
497 template std::tuple<const char*, UnsignedInt, Int> integralLiteral<UnsignedInt>(Containers::ArrayView<const char>, std::string&, ParseError&);
498 template std::tuple<const char*, Int, Int> integralLiteral<Int>(Containers::ArrayView<const char>, std::string&, ParseError&);
499 #ifdef CORRADE_TARGET_APPLE
500 template std::tuple<const char*, unsigned long, Int> integralLiteral<unsigned long>(Containers::ArrayView<const char>, std::string&, ParseError&);
501 template std::tuple<const char*, long, Int> integralLiteral<long>(Containers::ArrayView<const char>, std::string&, ParseError&);
502 #endif
503 #ifndef CORRADE_TARGET_EMSCRIPTEN
504 template std::tuple<const char*, UnsignedLong, Int> integralLiteral<UnsignedLong>(Containers::ArrayView<const char>, std::string&, ParseError&);
505 template std::tuple<const char*, Long, Int> integralLiteral<Long>(Containers::ArrayView<const char>, std::string&, ParseError&);
506 #else
507 /* Emscripten 1.38.10 and newer has std::size_t defined as unsigned long, while
508 it was unsigned int before. We should support both cases,
509 integralLiteral<std::size_t>() is used in some places in the parsers. */
510 static_assert(sizeof(unsigned long) == 4, "unsigned long is not four bytes on Emscripten");
511 template std::tuple<const char*, unsigned long, Int> integralLiteral<unsigned long>(Containers::ArrayView<const char>, std::string&, ParseError&);
512 #endif
513
floatingPointLiteral(const Containers::ArrayView<const char> data,std::string & buffer,ParseError & error)514 template<class T> std::pair<const char*, T> floatingPointLiteral(const Containers::ArrayView<const char> data, std::string& buffer, ParseError& error) {
515 /* Propagate errors */
516 if(!data) return {};
517
518 if(data.empty()) {
519 error = {ParseErrorType::ExpectedLiteral, Type::Float, data};
520 return {};
521 }
522
523 const char* i = data;
524
525 /* Sign */
526 T sign = T(1);
527 if(*i == '+') ++i;
528 else if(*i == '-') {
529 sign = T(-1);
530 ++i;
531 }
532
533 /* Binary literal */
534 if(i + 1 < data.end() && *i == '0' && isBinaryPrefix(i[1])) {
535 IntegralTypeFor<T> integralValue;
536 switch(i[1]) {
537 case 'x':
538 case 'X': {
539 std::tie(i, integralValue) = baseNLiteral<16, IntegralTypeFor<T>>(data.suffix(i + 2), buffer, error);
540 break;
541 }
542 case 'o':
543 case 'O': {
544 std::tie(i, integralValue) = baseNLiteral<8, IntegralTypeFor<T>>(data.suffix(i + 2), buffer, error);
545 break;
546 }
547 case 'b':
548 case 'B': {
549 std::tie(i, integralValue) = baseNLiteral<2, IntegralTypeFor<T>>(data.suffix(i + 2), buffer, error);
550 break;
551 }
552
553 default: std::abort(); /* LCOV_EXCL_LINE */
554 }
555
556 const T floatValue = reinterpret_cast<T&>(integralValue);
557 return {i, sign*floatValue};
558 }
559
560 /* Decimal before dot */
561 const char* const before = i;
562 i = possiblyNumericCharacters<10>(data.suffix(i));
563
564 /* Dot and decimal after dot */
565 if(i && i + 1 < data.end() && *i == '.') {
566 i = possiblyNumericCharacters<10>(data.suffix(i + 1));
567
568 /* Expecting at least .0 or 0. */
569 if(before + 1 == i) {
570 error = {ParseErrorType::InvalidLiteral, typeFor<T>(), data};
571 return {};
572 }
573
574 /* Expecting at least one numeric character */
575 } else if(before == i) {
576 error = {ParseErrorType::InvalidLiteral, typeFor<T>(), data};
577 return {};
578 }
579
580 /* Exponent etc */
581 if(i && i != data.end() && (*i == 'e' || *i == 'E')) {
582 ++i;
583
584 /* Exponent sign */
585 if(*i == '+' || *i == '-') ++i;
586
587 i = numericCharacters<10, T>(data.suffix(i), error);
588 }
589
590 /* Propagate errors */
591 if(!i) return {};
592
593 /** @todo verifying out-of-range */
594
595 extractWithoutUnderscore(data.prefix(i), buffer);
596 return {i, ExtractToType<T>::extract(buffer)};
597 }
598
599 template std::pair<const char*, Float> floatingPointLiteral<Float>(Containers::ArrayView<const char>, std::string&, ParseError&);
600 template std::pair<const char*, Double> floatingPointLiteral<Double>(Containers::ArrayView<const char>, std::string&, ParseError&);
601
stringLiteral(const Containers::ArrayView<const char> data,ParseError & error)602 std::pair<const char*, std::string> stringLiteral(const Containers::ArrayView<const char> data, ParseError& error) {
603 /* Propagate errors */
604 if(!data) return {};
605
606 if(data.empty() || *data != '"') {
607 error = {ParseErrorType::ExpectedLiteral, Type::String, data};
608 return {};
609 }
610
611 std::string out;
612
613 for(const char* i = data + 1; i != data.end(); ) {
614 const char c = *i;
615
616 if(UnsignedByte(c) < 0x20) {
617 error = {ParseErrorType::InvalidLiteral, Type::String, i};
618 return {};
619 }
620
621 /* Escaped character */
622 if(c == '\\')
623 i = escapedUnicode(data.suffix(i), out, error);
624
625 /* End of string, try searching for continuation */
626 else if(c == '"') {
627 const char* j = whitespace(data.suffix(i + 1));
628
629 /* Continuation not found, done */
630 if(j + 1 >= data.end() || *j != '"')
631 return {j, out};
632
633 i = j + 1;
634
635 /* Any other character, append it to result */
636 } else {
637 out += c;
638 ++i;
639 }
640 }
641
642 error = {ParseErrorType::LiteralOutOfRange, Type::String};
643 return {};
644 }
645
nameLiteral(const Containers::ArrayView<const char> data,ParseError & error)646 std::pair<const char*, std::string> nameLiteral(const Containers::ArrayView<const char> data, ParseError& error) {
647 /* Propagate errors */
648 if(!data) return {};
649
650 if(data.empty()) {
651 error = {ParseErrorType::ExpectedName, data};
652 return {};
653 }
654
655 const char* i = data;
656 if(*i != '$' && *i != '%') {
657 error = {ParseErrorType::InvalidName, data};
658 return {};
659 }
660
661 i = identifier(data.suffix(data + 1), error);
662 return {i, i ? std::string{data, std::size_t(i - data)} : std::string{}};
663 }
664
referenceLiteral(const Containers::ArrayView<const char> data,ParseError & error)665 std::pair<const char*, Containers::ArrayView<const char>> referenceLiteral(const Containers::ArrayView<const char> data, ParseError& error) {
666 /* Propagate errors */
667 if(!data) return {};
668
669 if(data.empty()) {
670 error = {ParseErrorType::ExpectedLiteral, Type::Reference};
671 return {};
672 }
673
674 if(const char* const end = prefix(data, "null"))
675 return {end, nullptr};
676
677 const char* i = data;
678 if(*i != '$' && *i != '%') {
679 error = {ParseErrorType::InvalidLiteral, Type::Reference, data};
680 return {};
681 }
682
683 i = identifier(data.suffix(1), error);
684
685 for(; i && i != data.end(); ) {
686 if(*i != '%') break;
687 i = identifier(data.suffix(i + 1), error);
688 }
689
690 return {i, i ? data.prefix(i) : nullptr};
691 }
692
possiblyTypeLiteral(const Containers::ArrayView<const char> data)693 std::pair<const char*, Type> possiblyTypeLiteral(const Containers::ArrayView<const char> data) {
694 #define _c(identifier, type) \
695 if(const char* const c = prefix(data, #identifier)) return {c, Type::type};
696 _c(bool, Bool)
697 _c(unsigned_int8, UnsignedByte)
698 _c(int8, Byte)
699 _c(unsigned_int16, UnsignedShort)
700 _c(int16, Short)
701 _c(unsigned_int32, UnsignedInt)
702 _c(int32, Int)
703 #ifndef CORRADE_TARGET_EMSCRIPTEN
704 _c(unsigned_int64, UnsignedLong)
705 _c(int64, Long)
706 #endif
707 /** @todo Half */
708 _c(float, Float)
709 _c(double, Double)
710 _c(string, String)
711 _c(ref, Reference)
712 _c(type, Type)
713 #undef _c
714
715 return {};
716 }
717
typeLiteral(const Containers::ArrayView<const char> data,ParseError & error)718 std::pair<const char*, Type> typeLiteral(const Containers::ArrayView<const char> data, ParseError& error) {
719 /* Propagate errors */
720 if(!data) return {};
721
722 if(data.empty()) {
723 error = {ParseErrorType::ExpectedLiteral, Type::Type, data};
724 return {};
725 }
726
727 const char* i;
728 Type type;
729 std::tie(i, type) = possiblyTypeLiteral(data);
730 if(i) return {i, type};
731
732 error = {ParseErrorType::InvalidLiteral, Type::Type, data};
733 return {};
734 }
735
propertyValue(const Containers::ArrayView<const char> data,bool & boolValue,Int & integerValue,Float & floatingPointValue,std::string & stringValue,Containers::ArrayView<const char> & referenceValue,Type & typeValue,std::string & buffer,ParseError & error)736 std::pair<const char*, InternalPropertyType> propertyValue(const Containers::ArrayView<const char> data, bool& boolValue, Int& integerValue, Float& floatingPointValue, std::string& stringValue, Containers::ArrayView<const char>& referenceValue, Type& typeValue, std::string& buffer, ParseError& error) {
737 /* Propagate errors */
738 if(!data) return {};
739
740 if(data.empty()) {
741 error = {ParseErrorType::ExpectedPropertyValue};
742 return {};
743 }
744
745 const char* i = data;
746
747 /* String literal */
748 if(*i == '"') {
749 std::tie(i, stringValue) = stringLiteral(data, error);
750 return {i, InternalPropertyType::String};
751 }
752
753 /* Reference literal */
754 if(*i == '%' || *i == '$') {
755 std::tie(i, referenceValue) = referenceLiteral(data, error);
756 return {i, InternalPropertyType::Reference};
757 }
758
759 /* Numeric literal */
760 if((*i >= '0' && *i <= '9') || *i == '.' || *i == '\'') {
761 /* Float literal if there is dot */
762 for(const char* j = i; j != data.end(); ++j) {
763 if(*j == '.') {
764 std::tie(i, floatingPointValue) = floatingPointLiteral<Float>(data, buffer, error);
765 return {i, InternalPropertyType::Float};
766 }
767
768 if(*j != '+' && *j != '-' && *j != '_' && !isBaseN<10>(*j)) break;
769 }
770
771 /* Integer literal otherwise */
772 Int base;
773 std::tie(i, integerValue, base) = integralLiteral<Int>(data, buffer, error);
774 switch(base) {
775 case 2:
776 case 8:
777 case 16: return {i, InternalPropertyType::Binary};
778 case 10: return {i, InternalPropertyType::Integral};
779 case 256: return {i, InternalPropertyType::Character};
780 }
781
782 std::abort(); /* LCOV_EXCL_LINE */
783 }
784
785 /* Null reference literal */
786 if(const char* const end = prefix(data, "null")) {
787 stringValue = "null";
788 return {end, InternalPropertyType::Reference};
789 }
790
791 /* Boolean literals */
792 if(const char* const end = prefix(data, "true")) {
793 boolValue = true;
794 return {end, InternalPropertyType::Bool};
795 }
796 if(const char* const end = prefix(data, "false")) {
797 boolValue = false;
798 return {end, InternalPropertyType::Bool};
799 }
800
801 /* Possibly type literal */
802 std::tie(i, typeValue) = possiblyTypeLiteral(data);
803 if(i) return {i, InternalPropertyType::Type};
804
805 error = {ParseErrorType::InvalidPropertyValue, data};
806 return {};
807 }
808
809 }}}
810