Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/SharedDependencies/Sources/toml/include/toml.hpp
2 views
1
#ifndef TOML11_VERSION_HPP
2
#define TOML11_VERSION_HPP
3
4
#define TOML11_VERSION_MAJOR 4
5
#define TOML11_VERSION_MINOR 4
6
#define TOML11_VERSION_PATCH 0
7
8
#ifndef __cplusplus
9
# error "__cplusplus is not defined"
10
#endif
11
12
/*
13
* Defines a name for an inline namespace that includes the current version
14
* number. This becomes necessary if multiple software packages use toml11 as an
15
* internal build-time dependency, since multiple packages will in general not
16
* use the same version of toml11. An inline namespace with a version number
17
* ensures that the symbols emitted by compiling toml11 into downstream
18
* applications will be distinguished by the specific used version of toml11,
19
* making it possible to link multiple packages that internally use toml11 in
20
* different versions.
21
*/
22
#define TOML11_CONCAT(a, b, c, d, e, f) a##b##c##d##e##f
23
24
#define TOML11_GENERATE_INLINE_VERSION_NAMESPACE(major, minor, patch) \
25
TOML11_CONCAT(toml11_, major, _, minor, _, patch)
26
27
#define TOML11_INLINE_VERSION_NAMESPACE \
28
TOML11_GENERATE_INLINE_VERSION_NAMESPACE( \
29
TOML11_VERSION_MAJOR, TOML11_VERSION_MINOR, TOML11_VERSION_PATCH)
30
31
// Since MSVC does not define `__cplusplus` correctly unless you pass
32
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
33
//
34
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
35
// MSVC ignores the value, maybe because of backward compatibility. Instead,
36
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
37
// the C++ standard. So we check if _MSVC_LANG is defined before using `__cplusplus`.
38
//
39
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
40
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
41
//
42
43
#if defined(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER
44
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
45
#else
46
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
47
#endif
48
49
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L
50
# error "toml11 requires C++11 or later."
51
#endif
52
53
#if ! defined(__has_include)
54
# define __has_include(x) 0
55
#endif
56
57
#if ! defined(__has_cpp_attribute)
58
# define __has_cpp_attribute(x) 0
59
#endif
60
61
#if ! defined(__has_builtin)
62
# define __has_builtin(x) 0
63
#endif
64
65
// hard to remember
66
67
#ifndef TOML11_CXX14_VALUE
68
#define TOML11_CXX14_VALUE 201402L
69
#endif//TOML11_CXX14_VALUE
70
71
#ifndef TOML11_CXX17_VALUE
72
#define TOML11_CXX17_VALUE 201703L
73
#endif//TOML11_CXX17_VALUE
74
75
#ifndef TOML11_CXX20_VALUE
76
#define TOML11_CXX20_VALUE 202002L
77
#endif//TOML11_CXX20_VALUE
78
79
#if defined(__cpp_char8_t)
80
# if __cpp_char8_t >= 201811L
81
# define TOML11_HAS_CHAR8_T 1
82
# endif
83
#endif
84
85
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
86
# if __has_include(<string_view>)
87
# define TOML11_HAS_STRING_VIEW 1
88
# endif
89
#endif
90
91
#ifndef TOML11_DISABLE_STD_FILESYSTEM
92
# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
93
# if __has_include(<filesystem>)
94
# define TOML11_HAS_FILESYSTEM 1
95
# endif
96
# endif
97
#endif
98
99
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
100
# if __has_include(<optional>)
101
# define TOML11_HAS_OPTIONAL 1
102
# endif
103
#endif
104
105
#if defined(TOML11_COMPILE_SOURCES)
106
# define TOML11_INLINE
107
#else
108
# define TOML11_INLINE inline
109
#endif
110
111
namespace toml
112
{
113
inline namespace TOML11_INLINE_VERSION_NAMESPACE
114
{
115
116
inline const char* license_notice() noexcept
117
{
118
return R"(The MIT License (MIT)
119
120
Copyright (c) 2017-now Toru Niina
121
122
Permission is hereby granted, free of charge, to any person obtaining a copy
123
of this software and associated documentation files (the "Software"), to deal
124
in the Software without restriction, including without limitation the rights
125
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
126
copies of the Software, and to permit persons to whom the Software is
127
furnished to do so, subject to the following conditions:
128
129
The above copyright notice and this permission notice shall be included in
130
all copies or substantial portions of the Software.
131
132
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
133
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
134
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
135
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
136
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
137
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
138
THE SOFTWARE.)";
139
}
140
141
} // TOML11_INLINE_VERSION_NAMESPACE
142
} // toml
143
#endif // TOML11_VERSION_HPP
144
#ifndef TOML11_SPEC_HPP
145
#define TOML11_SPEC_HPP
146
147
#include <array>
148
#include <functional>
149
#include <ostream>
150
#include <sstream>
151
#include <utility>
152
153
#include <cstdint>
154
155
156
namespace toml
157
{
158
inline namespace TOML11_INLINE_VERSION_NAMESPACE
159
{
160
161
struct semantic_version
162
{
163
constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
164
: major{mjr}, minor{mnr}, patch{p}
165
{}
166
167
std::uint32_t major;
168
std::uint32_t minor;
169
std::uint32_t patch;
170
};
171
172
constexpr inline semantic_version
173
make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
174
{
175
return semantic_version(mjr, mnr, p);
176
}
177
178
constexpr inline bool
179
operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept
180
{
181
return lhs.major == rhs.major &&
182
lhs.minor == rhs.minor &&
183
lhs.patch == rhs.patch;
184
}
185
constexpr inline bool
186
operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept
187
{
188
return !(lhs == rhs);
189
}
190
constexpr inline bool
191
operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept
192
{
193
return lhs.major < rhs.major ||
194
(lhs.major == rhs.major && lhs.minor < rhs.minor) ||
195
(lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch);
196
}
197
constexpr inline bool
198
operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept
199
{
200
return rhs < lhs;
201
}
202
constexpr inline bool
203
operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept
204
{
205
return !(lhs > rhs);
206
}
207
constexpr inline bool
208
operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept
209
{
210
return !(lhs < rhs);
211
}
212
213
inline std::ostream& operator<<(std::ostream& os, const semantic_version& v)
214
{
215
os << v.major << '.' << v.minor << '.' << v.patch;
216
return os;
217
}
218
219
inline std::string to_string(const semantic_version& v)
220
{
221
std::ostringstream oss;
222
oss << v;
223
return oss.str();
224
}
225
226
struct spec
227
{
228
constexpr static spec default_version() noexcept
229
{
230
return spec::v(1, 0, 0);
231
}
232
233
constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
234
{
235
return spec(make_semver(mjr, mnr, p));
236
}
237
238
constexpr explicit spec(const semantic_version& semver) noexcept
239
: version{semver},
240
v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver},
241
v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver},
242
v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver},
243
v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver},
244
v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver},
245
ext_allow_control_characters_in_comments{false},
246
ext_allow_non_english_in_bare_keys{false},
247
ext_hex_float {false},
248
ext_num_suffix{false},
249
ext_null_value{false}
250
{}
251
252
semantic_version version; // toml version
253
254
// diff from v1.0.0 -> v1.1.0
255
bool v1_1_0_allow_newlines_in_inline_tables;
256
bool v1_1_0_allow_trailing_comma_in_inline_tables;
257
bool v1_1_0_add_escape_sequence_e;
258
bool v1_1_0_add_escape_sequence_x;
259
bool v1_1_0_make_seconds_optional;
260
261
// discussed in toml-lang, but currently not in it
262
bool ext_allow_control_characters_in_comments;
263
bool ext_allow_non_english_in_bare_keys;
264
265
// library extensions
266
bool ext_hex_float; // allow hex float (in C++ style)
267
bool ext_num_suffix; // allow number suffix (in C++ style)
268
bool ext_null_value; // allow `null` as a value
269
};
270
271
namespace detail
272
{
273
inline std::pair<const semantic_version&, std::array<bool, 10>>
274
to_tuple(const spec& s) noexcept
275
{
276
return std::make_pair(std::cref(s.version), std::array<bool, 10>{{
277
s.v1_1_0_allow_newlines_in_inline_tables,
278
s.v1_1_0_allow_trailing_comma_in_inline_tables,
279
s.v1_1_0_add_escape_sequence_e,
280
s.v1_1_0_add_escape_sequence_x,
281
s.v1_1_0_make_seconds_optional,
282
s.ext_allow_control_characters_in_comments,
283
s.ext_allow_non_english_in_bare_keys,
284
s.ext_hex_float,
285
s.ext_num_suffix,
286
s.ext_null_value
287
}});
288
}
289
} // detail
290
291
inline bool operator==(const spec& lhs, const spec& rhs) noexcept
292
{
293
return detail::to_tuple(lhs) == detail::to_tuple(rhs);
294
}
295
inline bool operator!=(const spec& lhs, const spec& rhs) noexcept
296
{
297
return detail::to_tuple(lhs) != detail::to_tuple(rhs);
298
}
299
inline bool operator< (const spec& lhs, const spec& rhs) noexcept
300
{
301
return detail::to_tuple(lhs) < detail::to_tuple(rhs);
302
}
303
inline bool operator<=(const spec& lhs, const spec& rhs) noexcept
304
{
305
return detail::to_tuple(lhs) <= detail::to_tuple(rhs);
306
}
307
inline bool operator> (const spec& lhs, const spec& rhs) noexcept
308
{
309
return detail::to_tuple(lhs) > detail::to_tuple(rhs);
310
}
311
inline bool operator>=(const spec& lhs, const spec& rhs) noexcept
312
{
313
return detail::to_tuple(lhs) >= detail::to_tuple(rhs);
314
}
315
316
} // TOML11_INLINE_VERSION_NAMESPACE
317
} // namespace toml
318
#endif // TOML11_SPEC_HPP
319
#ifndef TOML11_ORDERED_MAP_HPP
320
#define TOML11_ORDERED_MAP_HPP
321
322
#include <algorithm>
323
#include <stdexcept>
324
#include <utility>
325
#include <vector>
326
327
328
namespace toml
329
{
330
inline namespace TOML11_INLINE_VERSION_NAMESPACE
331
{
332
333
namespace detail
334
{
335
template<typename Cmp>
336
struct ordered_map_ebo_container
337
{
338
Cmp cmp_; // empty base optimization for empty Cmp type
339
};
340
} // detail
341
342
template<typename Key, typename Val, typename Cmp = std::equal_to<Key>,
343
typename Allocator = std::allocator<std::pair<Key, Val>>>
344
class ordered_map : detail::ordered_map_ebo_container<Cmp>
345
{
346
public:
347
using key_type = Key;
348
using mapped_type = Val;
349
using value_type = std::pair<Key, Val>;
350
351
using key_compare = Cmp;
352
using allocator_type = Allocator;
353
354
using container_type = std::vector<value_type, Allocator>;
355
using reference = typename container_type::reference;
356
using pointer = typename container_type::pointer;
357
using const_reference = typename container_type::const_reference;
358
using const_pointer = typename container_type::const_pointer;
359
using iterator = typename container_type::iterator;
360
using const_iterator = typename container_type::const_iterator;
361
using size_type = typename container_type::size_type;
362
using difference_type = typename container_type::difference_type;
363
364
private:
365
366
using ebo_base = detail::ordered_map_ebo_container<Cmp>;
367
368
public:
369
370
ordered_map() = default;
371
~ordered_map() = default;
372
ordered_map(const ordered_map&) = default;
373
ordered_map(ordered_map&&) = default;
374
ordered_map& operator=(const ordered_map&) = default;
375
ordered_map& operator=(ordered_map&&) = default;
376
377
ordered_map(const ordered_map& other, const Allocator& alloc)
378
: container_(other.container_, alloc)
379
{}
380
ordered_map(ordered_map&& other, const Allocator& alloc)
381
: container_(std::move(other.container_), alloc)
382
{}
383
384
explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator())
385
: ebo_base{cmp}, container_(alloc)
386
{}
387
explicit ordered_map(const Allocator& alloc)
388
: container_(alloc)
389
{}
390
391
template<typename InputIterator>
392
ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator())
393
: ebo_base{cmp}, container_(first, last, alloc)
394
{}
395
template<typename InputIterator>
396
ordered_map(InputIterator first, InputIterator last, const Allocator& alloc)
397
: container_(first, last, alloc)
398
{}
399
400
ordered_map(std::initializer_list<value_type> v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator())
401
: ebo_base{cmp}, container_(std::move(v), alloc)
402
{}
403
ordered_map(std::initializer_list<value_type> v, const Allocator& alloc)
404
: container_(std::move(v), alloc)
405
{}
406
ordered_map& operator=(std::initializer_list<value_type> v)
407
{
408
this->container_ = std::move(v);
409
return *this;
410
}
411
412
iterator begin() noexcept {return container_.begin();}
413
iterator end() noexcept {return container_.end();}
414
const_iterator begin() const noexcept {return container_.begin();}
415
const_iterator end() const noexcept {return container_.end();}
416
const_iterator cbegin() const noexcept {return container_.cbegin();}
417
const_iterator cend() const noexcept {return container_.cend();}
418
419
bool empty() const noexcept {return container_.empty();}
420
std::size_t size() const noexcept {return container_.size();}
421
std::size_t max_size() const noexcept {return container_.max_size();}
422
423
void clear() {container_.clear();}
424
425
void push_back(const value_type& v)
426
{
427
if(this->contains(v.first))
428
{
429
throw std::out_of_range("ordered_map: value already exists");
430
}
431
container_.push_back(v);
432
}
433
void push_back(value_type&& v)
434
{
435
if(this->contains(v.first))
436
{
437
throw std::out_of_range("ordered_map: value already exists");
438
}
439
container_.push_back(std::move(v));
440
}
441
void emplace_back(key_type k, mapped_type v)
442
{
443
if(this->contains(k))
444
{
445
throw std::out_of_range("ordered_map: value already exists");
446
}
447
container_.emplace_back(std::move(k), std::move(v));
448
}
449
void pop_back() {container_.pop_back();}
450
451
void insert(value_type kv)
452
{
453
if(this->contains(kv.first))
454
{
455
throw std::out_of_range("ordered_map: value already exists");
456
}
457
container_.push_back(std::move(kv));
458
}
459
void emplace(key_type k, mapped_type v)
460
{
461
if(this->contains(k))
462
{
463
throw std::out_of_range("ordered_map: value already exists");
464
}
465
container_.emplace_back(std::move(k), std::move(v));
466
}
467
468
std::size_t count(const key_type& key) const
469
{
470
if(this->find(key) != this->end())
471
{
472
return 1;
473
}
474
else
475
{
476
return 0;
477
}
478
}
479
bool contains(const key_type& key) const
480
{
481
return this->find(key) != this->end();
482
}
483
iterator find(const key_type& key) noexcept
484
{
485
return std::find_if(this->begin(), this->end(),
486
[&key, this](const value_type& v) {return this->cmp_(v.first, key);});
487
}
488
const_iterator find(const key_type& key) const noexcept
489
{
490
return std::find_if(this->begin(), this->end(),
491
[&key, this](const value_type& v) {return this->cmp_(v.first, key);});
492
}
493
494
mapped_type& at(const key_type& k)
495
{
496
const auto iter = this->find(k);
497
if(iter == this->end())
498
{
499
throw std::out_of_range("ordered_map: no such element");
500
}
501
return iter->second;
502
}
503
mapped_type const& at(const key_type& k) const
504
{
505
const auto iter = this->find(k);
506
if(iter == this->end())
507
{
508
throw std::out_of_range("ordered_map: no such element");
509
}
510
return iter->second;
511
}
512
513
iterator erase(iterator pos)
514
{
515
return container_.erase(pos);
516
}
517
518
iterator erase(const_iterator pos)
519
{
520
return container_.erase(pos);
521
}
522
523
iterator erase(const_iterator first, const_iterator last)
524
{
525
return container_.erase(first, last);
526
}
527
528
size_type erase(const key_type& key)
529
{
530
auto it = this->find(key);
531
if (it != this->end())
532
{
533
container_.erase(it);
534
return 1;
535
}
536
return 0;
537
}
538
539
mapped_type& operator[](const key_type& k)
540
{
541
const auto iter = this->find(k);
542
if(iter == this->end())
543
{
544
this->container_.emplace_back(k, mapped_type{});
545
return this->container_.back().second;
546
}
547
return iter->second;
548
}
549
550
mapped_type const& operator[](const key_type& k) const
551
{
552
const auto iter = this->find(k);
553
if(iter == this->end())
554
{
555
throw std::out_of_range("ordered_map: no such element");
556
}
557
return iter->second;
558
}
559
560
key_compare key_comp() const {return this->cmp_;}
561
562
void swap(ordered_map& other)
563
{
564
container_.swap(other.container_);
565
}
566
567
private:
568
569
container_type container_;
570
};
571
572
template<typename K, typename V, typename C, typename A>
573
bool operator==(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
574
{
575
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
576
}
577
template<typename K, typename V, typename C, typename A>
578
bool operator!=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
579
{
580
return !(lhs == rhs);
581
}
582
template<typename K, typename V, typename C, typename A>
583
bool operator<(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
584
{
585
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
586
}
587
template<typename K, typename V, typename C, typename A>
588
bool operator>(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
589
{
590
return rhs < lhs;
591
}
592
template<typename K, typename V, typename C, typename A>
593
bool operator<=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
594
{
595
return !(lhs > rhs);
596
}
597
template<typename K, typename V, typename C, typename A>
598
bool operator>=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
599
{
600
return !(lhs < rhs);
601
}
602
603
template<typename K, typename V, typename C, typename A>
604
void swap(ordered_map<K,V,C,A>& lhs, ordered_map<K,V,C,A>& rhs)
605
{
606
lhs.swap(rhs);
607
return;
608
}
609
610
611
} // TOML11_INLINE_VERSION_NAMESPACE
612
} // toml
613
#endif // TOML11_ORDERED_MAP_HPP
614
#ifndef TOML11_INTO_HPP
615
#define TOML11_INTO_HPP
616
617
namespace toml
618
{
619
inline namespace TOML11_INLINE_VERSION_NAMESPACE
620
{
621
622
template<typename T>
623
struct into;
624
// {
625
// static toml::value into_toml(const T& user_defined_type)
626
// {
627
// // User-defined conversions ...
628
// }
629
// };
630
631
} // TOML11_INLINE_VERSION_NAMESPACE
632
} // toml
633
#endif // TOML11_INTO_HPP
634
#ifndef TOML11_FROM_HPP
635
#define TOML11_FROM_HPP
636
637
namespace toml
638
{
639
inline namespace TOML11_INLINE_VERSION_NAMESPACE
640
{
641
642
template<typename T>
643
struct from;
644
// {
645
// static T from_toml(const toml::value& v)
646
// {
647
// // User-defined conversions ...
648
// }
649
// };
650
651
} // TOML11_INLINE_VERSION_NAMESPACE
652
} // toml
653
#endif // TOML11_FROM_HPP
654
#ifndef TOML11_FORMAT_HPP
655
#define TOML11_FORMAT_HPP
656
657
#ifndef TOML11_FORMAT_FWD_HPP
658
#define TOML11_FORMAT_FWD_HPP
659
660
#include <iosfwd>
661
#include <string>
662
#include <utility>
663
664
#include <cstddef>
665
#include <cstdint>
666
667
668
namespace toml
669
{
670
inline namespace TOML11_INLINE_VERSION_NAMESPACE
671
{
672
673
// toml types with serialization info
674
675
enum class indent_char : std::uint8_t
676
{
677
space, // use space
678
tab, // use tab
679
none // no indent
680
};
681
682
std::ostream& operator<<(std::ostream& os, const indent_char& c);
683
std::string to_string(const indent_char c);
684
685
// ----------------------------------------------------------------------------
686
// boolean
687
688
struct boolean_format_info
689
{
690
// nothing, for now
691
};
692
693
inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept
694
{
695
return true;
696
}
697
inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept
698
{
699
return false;
700
}
701
702
// ----------------------------------------------------------------------------
703
// integer
704
705
enum class integer_format : std::uint8_t
706
{
707
dec = 0,
708
bin = 1,
709
oct = 2,
710
hex = 3,
711
};
712
713
std::ostream& operator<<(std::ostream& os, const integer_format f);
714
std::string to_string(const integer_format);
715
716
struct integer_format_info
717
{
718
integer_format fmt = integer_format::dec;
719
bool uppercase = true; // hex with uppercase
720
std::size_t width = 0; // minimal width (may exceed)
721
std::size_t spacer = 0; // position of `_` (if 0, no spacer)
722
std::string suffix = ""; // _suffix (library extension)
723
};
724
725
bool operator==(const integer_format_info&, const integer_format_info&) noexcept;
726
bool operator!=(const integer_format_info&, const integer_format_info&) noexcept;
727
728
// ----------------------------------------------------------------------------
729
// floating
730
731
enum class floating_format : std::uint8_t
732
{
733
defaultfloat = 0,
734
fixed = 1, // does not include exponential part
735
scientific = 2, // always include exponential part
736
hex = 3 // hexfloat extension
737
};
738
739
std::ostream& operator<<(std::ostream& os, const floating_format f);
740
std::string to_string(const floating_format);
741
742
struct floating_format_info
743
{
744
floating_format fmt = floating_format::defaultfloat;
745
std::size_t prec = 0; // precision (if 0, use the default)
746
std::string suffix = ""; // 1.0e+2_suffix (library extension)
747
};
748
749
bool operator==(const floating_format_info&, const floating_format_info&) noexcept;
750
bool operator!=(const floating_format_info&, const floating_format_info&) noexcept;
751
752
// ----------------------------------------------------------------------------
753
// string
754
755
enum class string_format : std::uint8_t
756
{
757
basic = 0,
758
literal = 1,
759
multiline_basic = 2,
760
multiline_literal = 3
761
};
762
763
std::ostream& operator<<(std::ostream& os, const string_format f);
764
std::string to_string(const string_format);
765
766
struct string_format_info
767
{
768
string_format fmt = string_format::basic;
769
bool start_with_newline = false;
770
};
771
772
bool operator==(const string_format_info&, const string_format_info&) noexcept;
773
bool operator!=(const string_format_info&, const string_format_info&) noexcept;
774
775
// ----------------------------------------------------------------------------
776
// datetime
777
778
enum class datetime_delimiter_kind : std::uint8_t
779
{
780
upper_T = 0,
781
lower_t = 1,
782
space = 2,
783
};
784
std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d);
785
std::string to_string(const datetime_delimiter_kind);
786
787
struct offset_datetime_format_info
788
{
789
datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T;
790
bool has_seconds = true;
791
std::size_t subsecond_precision = 6; // [us]
792
};
793
794
bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept;
795
bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept;
796
797
struct local_datetime_format_info
798
{
799
datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T;
800
bool has_seconds = true;
801
std::size_t subsecond_precision = 6; // [us]
802
};
803
804
bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept;
805
bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept;
806
807
struct local_date_format_info
808
{
809
// nothing, for now
810
};
811
812
bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept;
813
bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept;
814
815
struct local_time_format_info
816
{
817
bool has_seconds = true;
818
std::size_t subsecond_precision = 6; // [us]
819
};
820
821
bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept;
822
bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept;
823
824
// ----------------------------------------------------------------------------
825
// array
826
827
enum class array_format : std::uint8_t
828
{
829
default_format = 0,
830
oneline = 1,
831
multiline = 2,
832
array_of_tables = 3 // [[format.in.this.way]]
833
};
834
835
std::ostream& operator<<(std::ostream& os, const array_format f);
836
std::string to_string(const array_format);
837
838
struct array_format_info
839
{
840
array_format fmt = array_format::default_format;
841
indent_char indent_type = indent_char::space;
842
std::int32_t body_indent = 4; // indent in case of multiline
843
std::int32_t closing_indent = 0; // indent of `]`
844
};
845
846
bool operator==(const array_format_info&, const array_format_info&) noexcept;
847
bool operator!=(const array_format_info&, const array_format_info&) noexcept;
848
849
// ----------------------------------------------------------------------------
850
// table
851
852
enum class table_format : std::uint8_t
853
{
854
multiline = 0, // [foo] \n bar = "baz"
855
oneline = 1, // foo = {bar = "baz"}
856
dotted = 2, // foo.bar = "baz"
857
multiline_oneline = 3, // foo = { \n bar = "baz" \n }
858
implicit = 4 // [x] defined by [x.y.z]. skip in serializer.
859
};
860
861
std::ostream& operator<<(std::ostream& os, const table_format f);
862
std::string to_string(const table_format);
863
864
struct table_format_info
865
{
866
table_format fmt = table_format::multiline;
867
indent_char indent_type = indent_char::space;
868
std::int32_t body_indent = 0; // indent of values
869
std::int32_t name_indent = 0; // indent of [table]
870
std::int32_t closing_indent = 0; // in case of {inline-table}
871
};
872
873
bool operator==(const table_format_info&, const table_format_info&) noexcept;
874
bool operator!=(const table_format_info&, const table_format_info&) noexcept;
875
876
// ----------------------------------------------------------------------------
877
// wrapper
878
879
namespace detail
880
{
881
template<typename T, typename F>
882
struct value_with_format
883
{
884
using value_type = T;
885
using format_type = F;
886
887
value_with_format() = default;
888
~value_with_format() = default;
889
value_with_format(const value_with_format&) = default;
890
value_with_format(value_with_format&&) = default;
891
value_with_format& operator=(const value_with_format&) = default;
892
value_with_format& operator=(value_with_format&&) = default;
893
894
value_with_format(value_type v, format_type f)
895
: value{std::move(v)}, format{std::move(f)}
896
{}
897
898
template<typename U>
899
value_with_format(value_with_format<U, format_type> other)
900
: value{std::move(other.value)}, format{std::move(other.format)}
901
{}
902
903
value_type value;
904
format_type format;
905
};
906
} // detail
907
908
} // TOML11_INLINE_VERSION_NAMESPACE
909
} // namespace toml
910
#endif // TOML11_FORMAT_FWD_HPP
911
912
#if ! defined(TOML11_COMPILE_SOURCES)
913
#ifndef TOML11_FORMAT_IMPL_HPP
914
#define TOML11_FORMAT_IMPL_HPP
915
916
917
#include <ostream>
918
#include <sstream>
919
920
namespace toml
921
{
922
inline namespace TOML11_INLINE_VERSION_NAMESPACE
923
{
924
925
// toml types with serialization info
926
927
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c)
928
{
929
switch(c)
930
{
931
case indent_char::space: {os << "space" ; break;}
932
case indent_char::tab: {os << "tab" ; break;}
933
case indent_char::none: {os << "none" ; break;}
934
default:
935
{
936
os << "unknown indent char: " << static_cast<std::uint8_t>(c);
937
}
938
}
939
return os;
940
}
941
942
TOML11_INLINE std::string to_string(const indent_char c)
943
{
944
std::ostringstream oss;
945
oss << c;
946
return oss.str();
947
}
948
949
// ----------------------------------------------------------------------------
950
// boolean
951
952
// ----------------------------------------------------------------------------
953
// integer
954
955
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f)
956
{
957
switch(f)
958
{
959
case integer_format::dec: {os << "dec"; break;}
960
case integer_format::bin: {os << "bin"; break;}
961
case integer_format::oct: {os << "oct"; break;}
962
case integer_format::hex: {os << "hex"; break;}
963
default:
964
{
965
os << "unknown integer_format: " << static_cast<std::uint8_t>(f);
966
break;
967
}
968
}
969
return os;
970
}
971
TOML11_INLINE std::string to_string(const integer_format c)
972
{
973
std::ostringstream oss;
974
oss << c;
975
return oss.str();
976
}
977
978
979
TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept
980
{
981
return lhs.fmt == rhs.fmt &&
982
lhs.uppercase == rhs.uppercase &&
983
lhs.width == rhs.width &&
984
lhs.spacer == rhs.spacer &&
985
lhs.suffix == rhs.suffix ;
986
}
987
TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept
988
{
989
return !(lhs == rhs);
990
}
991
992
// ----------------------------------------------------------------------------
993
// floating
994
995
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f)
996
{
997
switch(f)
998
{
999
case floating_format::defaultfloat: {os << "defaultfloat"; break;}
1000
case floating_format::fixed : {os << "fixed" ; break;}
1001
case floating_format::scientific : {os << "scientific" ; break;}
1002
case floating_format::hex : {os << "hex" ; break;}
1003
default:
1004
{
1005
os << "unknown floating_format: " << static_cast<std::uint8_t>(f);
1006
break;
1007
}
1008
}
1009
return os;
1010
}
1011
TOML11_INLINE std::string to_string(const floating_format c)
1012
{
1013
std::ostringstream oss;
1014
oss << c;
1015
return oss.str();
1016
}
1017
1018
TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept
1019
{
1020
return lhs.fmt == rhs.fmt &&
1021
lhs.prec == rhs.prec &&
1022
lhs.suffix == rhs.suffix ;
1023
}
1024
TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept
1025
{
1026
return !(lhs == rhs);
1027
}
1028
1029
// ----------------------------------------------------------------------------
1030
// string
1031
1032
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f)
1033
{
1034
switch(f)
1035
{
1036
case string_format::basic : {os << "basic" ; break;}
1037
case string_format::literal : {os << "literal" ; break;}
1038
case string_format::multiline_basic : {os << "multiline_basic" ; break;}
1039
case string_format::multiline_literal: {os << "multiline_literal"; break;}
1040
default:
1041
{
1042
os << "unknown string_format: " << static_cast<std::uint8_t>(f);
1043
break;
1044
}
1045
}
1046
return os;
1047
}
1048
TOML11_INLINE std::string to_string(const string_format c)
1049
{
1050
std::ostringstream oss;
1051
oss << c;
1052
return oss.str();
1053
}
1054
1055
TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept
1056
{
1057
return lhs.fmt == rhs.fmt &&
1058
lhs.start_with_newline == rhs.start_with_newline ;
1059
}
1060
TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept
1061
{
1062
return !(lhs == rhs);
1063
}
1064
// ----------------------------------------------------------------------------
1065
// datetime
1066
1067
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d)
1068
{
1069
switch(d)
1070
{
1071
case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; }
1072
case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; }
1073
case datetime_delimiter_kind::space: { os << "space, "; break; }
1074
default:
1075
{
1076
os << "unknown datetime delimiter: " << static_cast<std::uint8_t>(d);
1077
break;
1078
}
1079
}
1080
return os;
1081
}
1082
TOML11_INLINE std::string to_string(const datetime_delimiter_kind c)
1083
{
1084
std::ostringstream oss;
1085
oss << c;
1086
return oss.str();
1087
}
1088
1089
TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept
1090
{
1091
return lhs.delimiter == rhs.delimiter &&
1092
lhs.has_seconds == rhs.has_seconds &&
1093
lhs.subsecond_precision == rhs.subsecond_precision ;
1094
}
1095
TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept
1096
{
1097
return !(lhs == rhs);
1098
}
1099
1100
TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept
1101
{
1102
return lhs.delimiter == rhs.delimiter &&
1103
lhs.has_seconds == rhs.has_seconds &&
1104
lhs.subsecond_precision == rhs.subsecond_precision ;
1105
}
1106
TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept
1107
{
1108
return !(lhs == rhs);
1109
}
1110
1111
TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept
1112
{
1113
return true;
1114
}
1115
TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept
1116
{
1117
return !(lhs == rhs);
1118
}
1119
1120
TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept
1121
{
1122
return lhs.has_seconds == rhs.has_seconds &&
1123
lhs.subsecond_precision == rhs.subsecond_precision ;
1124
}
1125
TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept
1126
{
1127
return !(lhs == rhs);
1128
}
1129
1130
// ----------------------------------------------------------------------------
1131
// array
1132
1133
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f)
1134
{
1135
switch(f)
1136
{
1137
case array_format::default_format : {os << "default_format" ; break;}
1138
case array_format::oneline : {os << "oneline" ; break;}
1139
case array_format::multiline : {os << "multiline" ; break;}
1140
case array_format::array_of_tables: {os << "array_of_tables"; break;}
1141
default:
1142
{
1143
os << "unknown array_format: " << static_cast<std::uint8_t>(f);
1144
break;
1145
}
1146
}
1147
return os;
1148
}
1149
TOML11_INLINE std::string to_string(const array_format c)
1150
{
1151
std::ostringstream oss;
1152
oss << c;
1153
return oss.str();
1154
}
1155
1156
TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept
1157
{
1158
return lhs.fmt == rhs.fmt &&
1159
lhs.indent_type == rhs.indent_type &&
1160
lhs.body_indent == rhs.body_indent &&
1161
lhs.closing_indent == rhs.closing_indent ;
1162
}
1163
TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept
1164
{
1165
return !(lhs == rhs);
1166
}
1167
1168
// ----------------------------------------------------------------------------
1169
// table
1170
1171
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f)
1172
{
1173
switch(f)
1174
{
1175
case table_format::multiline : {os << "multiline" ; break;}
1176
case table_format::oneline : {os << "oneline" ; break;}
1177
case table_format::dotted : {os << "dotted" ; break;}
1178
case table_format::multiline_oneline: {os << "multiline_oneline"; break;}
1179
case table_format::implicit : {os << "implicit" ; break;}
1180
default:
1181
{
1182
os << "unknown table_format: " << static_cast<std::uint8_t>(f);
1183
break;
1184
}
1185
}
1186
return os;
1187
}
1188
TOML11_INLINE std::string to_string(const table_format c)
1189
{
1190
std::ostringstream oss;
1191
oss << c;
1192
return oss.str();
1193
}
1194
1195
TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept
1196
{
1197
return lhs.fmt == rhs.fmt &&
1198
lhs.indent_type == rhs.indent_type &&
1199
lhs.body_indent == rhs.body_indent &&
1200
lhs.name_indent == rhs.name_indent &&
1201
lhs.closing_indent == rhs.closing_indent ;
1202
}
1203
TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept
1204
{
1205
return !(lhs == rhs);
1206
}
1207
1208
} // TOML11_INLINE_VERSION_NAMESPACE
1209
} // namespace toml
1210
#endif // TOML11_FORMAT_IMPL_HPP
1211
#endif
1212
1213
#endif// TOML11_FORMAT_HPP
1214
#ifndef TOML11_EXCEPTION_HPP
1215
#define TOML11_EXCEPTION_HPP
1216
1217
#include <exception>
1218
1219
1220
namespace toml
1221
{
1222
inline namespace TOML11_INLINE_VERSION_NAMESPACE
1223
{
1224
1225
struct exception : public std::exception
1226
{
1227
public:
1228
virtual ~exception() noexcept override = default;
1229
virtual const char* what() const noexcept override {return "";}
1230
};
1231
1232
} // TOML11_INLINE_VERSION_NAMESPACE
1233
} // toml
1234
#endif // TOMl11_EXCEPTION_HPP
1235
#ifndef TOML11_DATETIME_HPP
1236
#define TOML11_DATETIME_HPP
1237
1238
#ifndef TOML11_DATETIME_FWD_HPP
1239
#define TOML11_DATETIME_FWD_HPP
1240
1241
#include <chrono>
1242
#include <iosfwd>
1243
#include <string>
1244
1245
#include <cstdint>
1246
#include <cstdlib>
1247
#include <ctime>
1248
1249
1250
namespace toml
1251
{
1252
inline namespace TOML11_INLINE_VERSION_NAMESPACE
1253
{
1254
1255
enum class month_t : std::uint8_t
1256
{
1257
Jan = 0,
1258
Feb = 1,
1259
Mar = 2,
1260
Apr = 3,
1261
May = 4,
1262
Jun = 5,
1263
Jul = 6,
1264
Aug = 7,
1265
Sep = 8,
1266
Oct = 9,
1267
Nov = 10,
1268
Dec = 11
1269
};
1270
1271
// ----------------------------------------------------------------------------
1272
1273
struct local_date
1274
{
1275
std::int16_t year{0}; // A.D. (like, 2018)
1276
std::uint8_t month{0}; // [0, 11]
1277
std::uint8_t day{0}; // [1, 31]
1278
1279
local_date(int y, month_t m, int d)
1280
: year {static_cast<std::int16_t>(y)},
1281
month{static_cast<std::uint8_t>(m)},
1282
day {static_cast<std::uint8_t>(d)}
1283
{}
1284
1285
explicit local_date(const std::tm& t)
1286
: year {static_cast<std::int16_t>(t.tm_year + 1900)},
1287
month{static_cast<std::uint8_t>(t.tm_mon)},
1288
day {static_cast<std::uint8_t>(t.tm_mday)}
1289
{}
1290
1291
explicit local_date(const std::chrono::system_clock::time_point& tp);
1292
explicit local_date(const std::time_t t);
1293
1294
operator std::chrono::system_clock::time_point() const;
1295
operator std::time_t() const;
1296
1297
local_date() = default;
1298
~local_date() = default;
1299
local_date(local_date const&) = default;
1300
local_date(local_date&&) = default;
1301
local_date& operator=(local_date const&) = default;
1302
local_date& operator=(local_date&&) = default;
1303
};
1304
bool operator==(const local_date& lhs, const local_date& rhs);
1305
bool operator!=(const local_date& lhs, const local_date& rhs);
1306
bool operator< (const local_date& lhs, const local_date& rhs);
1307
bool operator<=(const local_date& lhs, const local_date& rhs);
1308
bool operator> (const local_date& lhs, const local_date& rhs);
1309
bool operator>=(const local_date& lhs, const local_date& rhs);
1310
1311
std::ostream& operator<<(std::ostream& os, const local_date& date);
1312
std::string to_string(const local_date& date);
1313
1314
// -----------------------------------------------------------------------------
1315
1316
struct local_time
1317
{
1318
std::uint8_t hour{0}; // [0, 23]
1319
std::uint8_t minute{0}; // [0, 59]
1320
std::uint8_t second{0}; // [0, 60]
1321
std::uint16_t millisecond{0}; // [0, 999]
1322
std::uint16_t microsecond{0}; // [0, 999]
1323
std::uint16_t nanosecond{0}; // [0, 999]
1324
1325
local_time(int h, int m, int s,
1326
int ms = 0, int us = 0, int ns = 0)
1327
: hour {static_cast<std::uint8_t>(h)},
1328
minute{static_cast<std::uint8_t>(m)},
1329
second{static_cast<std::uint8_t>(s)},
1330
millisecond{static_cast<std::uint16_t>(ms)},
1331
microsecond{static_cast<std::uint16_t>(us)},
1332
nanosecond {static_cast<std::uint16_t>(ns)}
1333
{}
1334
1335
explicit local_time(const std::tm& t)
1336
: hour {static_cast<std::uint8_t>(t.tm_hour)},
1337
minute{static_cast<std::uint8_t>(t.tm_min )},
1338
second{static_cast<std::uint8_t>(t.tm_sec )},
1339
millisecond{0}, microsecond{0}, nanosecond{0}
1340
{}
1341
1342
template<typename Rep, typename Period>
1343
explicit local_time(const std::chrono::duration<Rep, Period>& t)
1344
{
1345
const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
1346
this->hour = static_cast<std::uint8_t>(h.count());
1347
const auto t2 = t - h;
1348
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
1349
this->minute = static_cast<std::uint8_t>(m.count());
1350
const auto t3 = t2 - m;
1351
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
1352
this->second = static_cast<std::uint8_t>(s.count());
1353
const auto t4 = t3 - s;
1354
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
1355
this->millisecond = static_cast<std::uint16_t>(ms.count());
1356
const auto t5 = t4 - ms;
1357
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
1358
this->microsecond = static_cast<std::uint16_t>(us.count());
1359
const auto t6 = t5 - us;
1360
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
1361
this->nanosecond = static_cast<std::uint16_t>(ns.count());
1362
}
1363
1364
operator std::chrono::nanoseconds() const;
1365
1366
local_time() = default;
1367
~local_time() = default;
1368
local_time(local_time const&) = default;
1369
local_time(local_time&&) = default;
1370
local_time& operator=(local_time const&) = default;
1371
local_time& operator=(local_time&&) = default;
1372
};
1373
1374
bool operator==(const local_time& lhs, const local_time& rhs);
1375
bool operator!=(const local_time& lhs, const local_time& rhs);
1376
bool operator< (const local_time& lhs, const local_time& rhs);
1377
bool operator<=(const local_time& lhs, const local_time& rhs);
1378
bool operator> (const local_time& lhs, const local_time& rhs);
1379
bool operator>=(const local_time& lhs, const local_time& rhs);
1380
1381
std::ostream& operator<<(std::ostream& os, const local_time& time);
1382
std::string to_string(const local_time& time);
1383
1384
// ----------------------------------------------------------------------------
1385
1386
struct time_offset
1387
{
1388
std::int8_t hour{0}; // [-12, 12]
1389
std::int8_t minute{0}; // [-59, 59]
1390
1391
time_offset(int h, int m)
1392
: hour {static_cast<std::int8_t>(h)},
1393
minute{static_cast<std::int8_t>(m)}
1394
{}
1395
1396
operator std::chrono::minutes() const;
1397
1398
time_offset() = default;
1399
~time_offset() = default;
1400
time_offset(time_offset const&) = default;
1401
time_offset(time_offset&&) = default;
1402
time_offset& operator=(time_offset const&) = default;
1403
time_offset& operator=(time_offset&&) = default;
1404
};
1405
1406
bool operator==(const time_offset& lhs, const time_offset& rhs);
1407
bool operator!=(const time_offset& lhs, const time_offset& rhs);
1408
bool operator< (const time_offset& lhs, const time_offset& rhs);
1409
bool operator<=(const time_offset& lhs, const time_offset& rhs);
1410
bool operator> (const time_offset& lhs, const time_offset& rhs);
1411
bool operator>=(const time_offset& lhs, const time_offset& rhs);
1412
1413
std::ostream& operator<<(std::ostream& os, const time_offset& offset);
1414
1415
std::string to_string(const time_offset& offset);
1416
1417
// -----------------------------------------------------------------------------
1418
1419
struct local_datetime
1420
{
1421
local_date date{};
1422
local_time time{};
1423
1424
local_datetime(local_date d, local_time t): date{d}, time{t} {}
1425
1426
explicit local_datetime(const std::tm& t): date{t}, time{t}{}
1427
1428
explicit local_datetime(const std::chrono::system_clock::time_point& tp);
1429
explicit local_datetime(const std::time_t t);
1430
1431
operator std::chrono::system_clock::time_point() const;
1432
operator std::time_t() const;
1433
1434
local_datetime() = default;
1435
~local_datetime() = default;
1436
local_datetime(local_datetime const&) = default;
1437
local_datetime(local_datetime&&) = default;
1438
local_datetime& operator=(local_datetime const&) = default;
1439
local_datetime& operator=(local_datetime&&) = default;
1440
};
1441
1442
bool operator==(const local_datetime& lhs, const local_datetime& rhs);
1443
bool operator!=(const local_datetime& lhs, const local_datetime& rhs);
1444
bool operator< (const local_datetime& lhs, const local_datetime& rhs);
1445
bool operator<=(const local_datetime& lhs, const local_datetime& rhs);
1446
bool operator> (const local_datetime& lhs, const local_datetime& rhs);
1447
bool operator>=(const local_datetime& lhs, const local_datetime& rhs);
1448
1449
std::ostream& operator<<(std::ostream& os, const local_datetime& dt);
1450
1451
std::string to_string(const local_datetime& dt);
1452
1453
// -----------------------------------------------------------------------------
1454
1455
struct offset_datetime
1456
{
1457
local_date date{};
1458
local_time time{};
1459
time_offset offset{};
1460
1461
offset_datetime(local_date d, local_time t, time_offset o)
1462
: date{d}, time{t}, offset{o}
1463
{}
1464
offset_datetime(const local_datetime& dt, time_offset o)
1465
: date{dt.date}, time{dt.time}, offset{o}
1466
{}
1467
// use the current local timezone offset
1468
explicit offset_datetime(const local_datetime& ld);
1469
explicit offset_datetime(const std::chrono::system_clock::time_point& tp);
1470
explicit offset_datetime(const std::time_t& t);
1471
explicit offset_datetime(const std::tm& t);
1472
1473
operator std::chrono::system_clock::time_point() const;
1474
1475
operator std::time_t() const;
1476
1477
offset_datetime() = default;
1478
~offset_datetime() = default;
1479
offset_datetime(offset_datetime const&) = default;
1480
offset_datetime(offset_datetime&&) = default;
1481
offset_datetime& operator=(offset_datetime const&) = default;
1482
offset_datetime& operator=(offset_datetime&&) = default;
1483
1484
private:
1485
1486
static time_offset get_local_offset(const std::time_t* tp);
1487
};
1488
1489
bool operator==(const offset_datetime& lhs, const offset_datetime& rhs);
1490
bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs);
1491
bool operator< (const offset_datetime& lhs, const offset_datetime& rhs);
1492
bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs);
1493
bool operator> (const offset_datetime& lhs, const offset_datetime& rhs);
1494
bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs);
1495
1496
std::ostream& operator<<(std::ostream& os, const offset_datetime& dt);
1497
1498
std::string to_string(const offset_datetime& dt);
1499
1500
} // TOML11_INLINE_VERSION_NAMESPACE
1501
} // toml
1502
#endif // TOML11_DATETIME_FWD_HPP
1503
1504
#if ! defined(TOML11_COMPILE_SOURCES)
1505
#ifndef TOML11_DATETIME_IMPL_HPP
1506
#define TOML11_DATETIME_IMPL_HPP
1507
1508
1509
#include <array>
1510
#include <iomanip>
1511
#include <ostream>
1512
#include <sstream>
1513
#include <tuple>
1514
1515
#include <cstdlib>
1516
#include <ctime>
1517
1518
namespace toml
1519
{
1520
inline namespace TOML11_INLINE_VERSION_NAMESPACE
1521
{
1522
1523
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
1524
// provided in the absolutely same purpose, but C++11 is actually not compatible
1525
// with C11. We need to dispatch the function depending on the OS.
1526
namespace detail
1527
{
1528
// TODO: find more sophisticated way to handle this
1529
#if defined(_MSC_VER)
1530
TOML11_INLINE std::tm localtime_s(const std::time_t* src)
1531
{
1532
std::tm dst;
1533
const auto result = ::localtime_s(&dst, src);
1534
if (result) { throw std::runtime_error("localtime_s failed."); }
1535
return dst;
1536
}
1537
TOML11_INLINE std::tm gmtime_s(const std::time_t* src)
1538
{
1539
std::tm dst;
1540
const auto result = ::gmtime_s(&dst, src);
1541
if (result) { throw std::runtime_error("gmtime_s failed."); }
1542
return dst;
1543
}
1544
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
1545
TOML11_INLINE std::tm localtime_s(const std::time_t* src)
1546
{
1547
std::tm dst;
1548
const auto result = ::localtime_r(src, &dst);
1549
if (!result) { throw std::runtime_error("localtime_r failed."); }
1550
return dst;
1551
}
1552
TOML11_INLINE std::tm gmtime_s(const std::time_t* src)
1553
{
1554
std::tm dst;
1555
const auto result = ::gmtime_r(src, &dst);
1556
if (!result) { throw std::runtime_error("gmtime_r failed."); }
1557
return dst;
1558
}
1559
#else // fallback. not threadsafe
1560
TOML11_INLINE std::tm localtime_s(const std::time_t* src)
1561
{
1562
const auto result = std::localtime(src);
1563
if (!result) { throw std::runtime_error("localtime failed."); }
1564
return *result;
1565
}
1566
TOML11_INLINE std::tm gmtime_s(const std::time_t* src)
1567
{
1568
const auto result = std::gmtime(src);
1569
if (!result) { throw std::runtime_error("gmtime failed."); }
1570
return *result;
1571
}
1572
#endif
1573
} // detail
1574
1575
// ----------------------------------------------------------------------------
1576
1577
TOML11_INLINE local_date::local_date(const std::chrono::system_clock::time_point& tp)
1578
{
1579
const auto t = std::chrono::system_clock::to_time_t(tp);
1580
const auto time = detail::localtime_s(&t);
1581
*this = local_date(time);
1582
}
1583
1584
TOML11_INLINE local_date::local_date(const std::time_t t)
1585
: local_date{std::chrono::system_clock::from_time_t(t)}
1586
{}
1587
1588
TOML11_INLINE local_date::operator std::chrono::system_clock::time_point() const
1589
{
1590
// std::mktime returns date as local time zone. no conversion needed
1591
std::tm t;
1592
t.tm_sec = 0;
1593
t.tm_min = 0;
1594
t.tm_hour = 0;
1595
t.tm_mday = static_cast<int>(this->day);
1596
t.tm_mon = static_cast<int>(this->month);
1597
t.tm_year = static_cast<int>(this->year) - 1900;
1598
t.tm_wday = 0; // the value will be ignored
1599
t.tm_yday = 0; // the value will be ignored
1600
t.tm_isdst = -1;
1601
return std::chrono::system_clock::from_time_t(std::mktime(&t));
1602
}
1603
1604
TOML11_INLINE local_date::operator std::time_t() const
1605
{
1606
return std::chrono::system_clock::to_time_t(
1607
std::chrono::system_clock::time_point(*this));
1608
}
1609
1610
TOML11_INLINE bool operator==(const local_date& lhs, const local_date& rhs)
1611
{
1612
return std::make_tuple(lhs.year, lhs.month, lhs.day) ==
1613
std::make_tuple(rhs.year, rhs.month, rhs.day);
1614
}
1615
TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs)
1616
{
1617
return !(lhs == rhs);
1618
}
1619
TOML11_INLINE bool operator< (const local_date& lhs, const local_date& rhs)
1620
{
1621
return std::make_tuple(lhs.year, lhs.month, lhs.day) <
1622
std::make_tuple(rhs.year, rhs.month, rhs.day);
1623
}
1624
TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs)
1625
{
1626
return (lhs < rhs) || (lhs == rhs);
1627
}
1628
TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs)
1629
{
1630
return !(lhs <= rhs);
1631
}
1632
TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs)
1633
{
1634
return !(lhs < rhs);
1635
}
1636
1637
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_date& date)
1638
{
1639
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
1640
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
1641
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
1642
return os;
1643
}
1644
1645
TOML11_INLINE std::string to_string(const local_date& date)
1646
{
1647
std::ostringstream oss;
1648
oss.imbue(std::locale::classic());
1649
oss << date;
1650
return oss.str();
1651
}
1652
1653
// -----------------------------------------------------------------------------
1654
1655
TOML11_INLINE local_time::operator std::chrono::nanoseconds() const
1656
{
1657
return std::chrono::nanoseconds (this->nanosecond) +
1658
std::chrono::microseconds(this->microsecond) +
1659
std::chrono::milliseconds(this->millisecond) +
1660
std::chrono::seconds(this->second) +
1661
std::chrono::minutes(this->minute) +
1662
std::chrono::hours(this->hour);
1663
}
1664
1665
TOML11_INLINE bool operator==(const local_time& lhs, const local_time& rhs)
1666
{
1667
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) ==
1668
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
1669
}
1670
TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs)
1671
{
1672
return !(lhs == rhs);
1673
}
1674
TOML11_INLINE bool operator< (const local_time& lhs, const local_time& rhs)
1675
{
1676
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) <
1677
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
1678
}
1679
TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs)
1680
{
1681
return (lhs < rhs) || (lhs == rhs);
1682
}
1683
TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs)
1684
{
1685
return !(lhs <= rhs);
1686
}
1687
TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs)
1688
{
1689
return !(lhs < rhs);
1690
}
1691
1692
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_time& time)
1693
{
1694
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':';
1695
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':';
1696
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second);
1697
if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0)
1698
{
1699
os << '.';
1700
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond);
1701
if(time.microsecond != 0 || time.nanosecond != 0)
1702
{
1703
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond);
1704
if(time.nanosecond != 0)
1705
{
1706
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond);
1707
}
1708
}
1709
}
1710
return os;
1711
}
1712
1713
TOML11_INLINE std::string to_string(const local_time& time)
1714
{
1715
std::ostringstream oss;
1716
oss.imbue(std::locale::classic());
1717
oss << time;
1718
return oss.str();
1719
}
1720
1721
// ----------------------------------------------------------------------------
1722
1723
TOML11_INLINE time_offset::operator std::chrono::minutes() const
1724
{
1725
return std::chrono::minutes(this->minute) +
1726
std::chrono::hours(this->hour);
1727
}
1728
1729
TOML11_INLINE bool operator==(const time_offset& lhs, const time_offset& rhs)
1730
{
1731
return std::make_tuple(lhs.hour, lhs.minute) ==
1732
std::make_tuple(rhs.hour, rhs.minute);
1733
}
1734
TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs)
1735
{
1736
return !(lhs == rhs);
1737
}
1738
TOML11_INLINE bool operator< (const time_offset& lhs, const time_offset& rhs)
1739
{
1740
return std::make_tuple(lhs.hour, lhs.minute) <
1741
std::make_tuple(rhs.hour, rhs.minute);
1742
}
1743
TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs)
1744
{
1745
return (lhs < rhs) || (lhs == rhs);
1746
}
1747
TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs)
1748
{
1749
return !(lhs <= rhs);
1750
}
1751
TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs)
1752
{
1753
return !(lhs < rhs);
1754
}
1755
1756
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const time_offset& offset)
1757
{
1758
if(offset.hour == 0 && offset.minute == 0)
1759
{
1760
os << 'Z';
1761
return os;
1762
}
1763
int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
1764
if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
1765
os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
1766
os << std::setfill('0') << std::setw(2) << minute % 60;
1767
return os;
1768
}
1769
1770
TOML11_INLINE std::string to_string(const time_offset& offset)
1771
{
1772
std::ostringstream oss;
1773
oss.imbue(std::locale::classic());
1774
oss << offset;
1775
return oss.str();
1776
}
1777
1778
// -----------------------------------------------------------------------------
1779
1780
TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp)
1781
{
1782
const auto t = std::chrono::system_clock::to_time_t(tp);
1783
std::tm ltime = detail::localtime_s(&t);
1784
1785
this->date = local_date(ltime);
1786
this->time = local_time(ltime);
1787
1788
// std::tm lacks subsecond information, so diff between tp and tm
1789
// can be used to get millisecond & microsecond information.
1790
const auto t_diff = tp -
1791
std::chrono::system_clock::from_time_t(std::mktime(&ltime));
1792
this->time.millisecond = static_cast<std::uint16_t>(
1793
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
1794
this->time.microsecond = static_cast<std::uint16_t>(
1795
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
1796
this->time.nanosecond = static_cast<std::uint16_t>(
1797
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
1798
}
1799
1800
TOML11_INLINE local_datetime::local_datetime(const std::time_t t)
1801
: local_datetime{std::chrono::system_clock::from_time_t(t)}
1802
{}
1803
1804
TOML11_INLINE local_datetime::operator std::chrono::system_clock::time_point() const
1805
{
1806
using internal_duration =
1807
typename std::chrono::system_clock::time_point::duration;
1808
1809
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
1810
// of local_date and local_time independently, the conversion fails if
1811
// it is the day when DST begins or ends. Since local_date considers the
1812
// time is 00:00 A.M. and local_time does not consider DST because it
1813
// does not have any date information. We need to consider both date and
1814
// time information at the same time to convert it correctly.
1815
1816
std::tm t;
1817
t.tm_sec = static_cast<int>(this->time.second);
1818
t.tm_min = static_cast<int>(this->time.minute);
1819
t.tm_hour = static_cast<int>(this->time.hour);
1820
t.tm_mday = static_cast<int>(this->date.day);
1821
t.tm_mon = static_cast<int>(this->date.month);
1822
t.tm_year = static_cast<int>(this->date.year) - 1900;
1823
t.tm_wday = 0; // the value will be ignored
1824
t.tm_yday = 0; // the value will be ignored
1825
t.tm_isdst = -1;
1826
1827
// std::mktime returns date as local time zone. no conversion needed
1828
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
1829
dt += std::chrono::duration_cast<internal_duration>(
1830
std::chrono::milliseconds(this->time.millisecond) +
1831
std::chrono::microseconds(this->time.microsecond) +
1832
std::chrono::nanoseconds (this->time.nanosecond));
1833
return dt;
1834
}
1835
1836
TOML11_INLINE local_datetime::operator std::time_t() const
1837
{
1838
return std::chrono::system_clock::to_time_t(
1839
std::chrono::system_clock::time_point(*this));
1840
}
1841
1842
TOML11_INLINE bool operator==(const local_datetime& lhs, const local_datetime& rhs)
1843
{
1844
return std::make_tuple(lhs.date, lhs.time) ==
1845
std::make_tuple(rhs.date, rhs.time);
1846
}
1847
TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs)
1848
{
1849
return !(lhs == rhs);
1850
}
1851
TOML11_INLINE bool operator< (const local_datetime& lhs, const local_datetime& rhs)
1852
{
1853
return std::make_tuple(lhs.date, lhs.time) <
1854
std::make_tuple(rhs.date, rhs.time);
1855
}
1856
TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs)
1857
{
1858
return (lhs < rhs) || (lhs == rhs);
1859
}
1860
TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs)
1861
{
1862
return !(lhs <= rhs);
1863
}
1864
TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs)
1865
{
1866
return !(lhs < rhs);
1867
}
1868
1869
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
1870
{
1871
os << dt.date << 'T' << dt.time;
1872
return os;
1873
}
1874
1875
TOML11_INLINE std::string to_string(const local_datetime& dt)
1876
{
1877
std::ostringstream oss;
1878
oss.imbue(std::locale::classic());
1879
oss << dt;
1880
return oss.str();
1881
}
1882
1883
// -----------------------------------------------------------------------------
1884
1885
1886
TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld)
1887
: date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)}
1888
// use the current local timezone offset
1889
{}
1890
TOML11_INLINE offset_datetime::offset_datetime(const std::chrono::system_clock::time_point& tp)
1891
: offset{0, 0} // use gmtime
1892
{
1893
const auto timet = std::chrono::system_clock::to_time_t(tp);
1894
const auto tm = detail::gmtime_s(&timet);
1895
this->date = local_date(tm);
1896
this->time = local_time(tm);
1897
}
1898
TOML11_INLINE offset_datetime::offset_datetime(const std::time_t& t)
1899
: offset{0, 0} // use gmtime
1900
{
1901
const auto tm = detail::gmtime_s(&t);
1902
this->date = local_date(tm);
1903
this->time = local_time(tm);
1904
}
1905
TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t)
1906
: offset{0, 0} // assume gmtime
1907
{
1908
this->date = local_date(t);
1909
this->time = local_time(t);
1910
}
1911
1912
TOML11_INLINE offset_datetime::operator std::chrono::system_clock::time_point() const
1913
{
1914
// get date-time
1915
using internal_duration =
1916
typename std::chrono::system_clock::time_point::duration;
1917
1918
// first, convert it to local date-time information in the same way as
1919
// local_datetime does. later we will use time_t to adjust time offset.
1920
std::tm t;
1921
t.tm_sec = static_cast<int>(this->time.second);
1922
t.tm_min = static_cast<int>(this->time.minute);
1923
t.tm_hour = static_cast<int>(this->time.hour);
1924
t.tm_mday = static_cast<int>(this->date.day);
1925
t.tm_mon = static_cast<int>(this->date.month);
1926
t.tm_year = static_cast<int>(this->date.year) - 1900;
1927
t.tm_wday = 0; // the value will be ignored
1928
t.tm_yday = 0; // the value will be ignored
1929
t.tm_isdst = -1;
1930
const std::time_t tp_loc = std::mktime(std::addressof(t));
1931
1932
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
1933
tp += std::chrono::duration_cast<internal_duration>(
1934
std::chrono::milliseconds(this->time.millisecond) +
1935
std::chrono::microseconds(this->time.microsecond) +
1936
std::chrono::nanoseconds (this->time.nanosecond));
1937
1938
// Since mktime uses local time zone, it should be corrected.
1939
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
1940
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
1941
// to add `+09:00` to `03:00:00Z`.
1942
// Here, it uses the time_t converted from date-time info to handle
1943
// daylight saving time.
1944
const auto ofs = get_local_offset(std::addressof(tp_loc));
1945
tp += std::chrono::hours (ofs.hour);
1946
tp += std::chrono::minutes(ofs.minute);
1947
1948
// We got `12:00:00Z` by correcting local timezone applied by mktime.
1949
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
1950
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
1951
// So we need to subtract the offset.
1952
tp -= std::chrono::minutes(this->offset);
1953
return tp;
1954
}
1955
1956
TOML11_INLINE offset_datetime::operator std::time_t() const
1957
{
1958
return std::chrono::system_clock::to_time_t(
1959
std::chrono::system_clock::time_point(*this));
1960
}
1961
1962
TOML11_INLINE time_offset offset_datetime::get_local_offset(const std::time_t* tp)
1963
{
1964
// get local timezone with the same date-time information as mktime
1965
const auto t = detail::localtime_s(tp);
1966
1967
std::array<char, 6> buf;
1968
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
1969
if(result != 5)
1970
{
1971
throw std::runtime_error("toml::offset_datetime: cannot obtain "
1972
"timezone information of current env");
1973
}
1974
const int ofs = std::atoi(buf.data());
1975
const int ofs_h = ofs / 100;
1976
const int ofs_m = ofs - (ofs_h * 100);
1977
return time_offset(ofs_h, ofs_m);
1978
}
1979
1980
TOML11_INLINE bool operator==(const offset_datetime& lhs, const offset_datetime& rhs)
1981
{
1982
return std::make_tuple(lhs.date, lhs.time, lhs.offset) ==
1983
std::make_tuple(rhs.date, rhs.time, rhs.offset);
1984
}
1985
TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs)
1986
{
1987
return !(lhs == rhs);
1988
}
1989
TOML11_INLINE bool operator< (const offset_datetime& lhs, const offset_datetime& rhs)
1990
{
1991
return std::make_tuple(lhs.date, lhs.time, lhs.offset) <
1992
std::make_tuple(rhs.date, rhs.time, rhs.offset);
1993
}
1994
TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs)
1995
{
1996
return (lhs < rhs) || (lhs == rhs);
1997
}
1998
TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs)
1999
{
2000
return !(lhs <= rhs);
2001
}
2002
TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs)
2003
{
2004
return !(lhs < rhs);
2005
}
2006
2007
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
2008
{
2009
os << dt.date << 'T' << dt.time << dt.offset;
2010
return os;
2011
}
2012
2013
TOML11_INLINE std::string to_string(const offset_datetime& dt)
2014
{
2015
std::ostringstream oss;
2016
oss.imbue(std::locale::classic());
2017
oss << dt;
2018
return oss.str();
2019
}
2020
2021
} // TOML11_INLINE_VERSION_NAMESPACE
2022
} // toml
2023
#endif // TOML11_DATETIME_IMPL_HPP
2024
#endif
2025
2026
#endif // TOML11_DATETIME_HPP
2027
#ifndef TOML11_COMPAT_HPP
2028
#define TOML11_COMPAT_HPP
2029
2030
2031
#include <algorithm>
2032
#include <iterator>
2033
#include <memory>
2034
#include <string>
2035
#include <type_traits>
2036
2037
#include <cassert>
2038
2039
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
2040
# if __has_include(<bit>)
2041
# include <bit>
2042
# endif
2043
#endif
2044
2045
#include <cstring>
2046
2047
// ----------------------------------------------------------------------------
2048
2049
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
2050
# if __has_cpp_attribute(deprecated)
2051
# define TOML11_HAS_ATTR_DEPRECATED 1
2052
# endif
2053
#endif
2054
2055
#if defined(TOML11_HAS_ATTR_DEPRECATED)
2056
# define TOML11_DEPRECATED(msg) [[deprecated(msg)]]
2057
#elif defined(__GNUC__)
2058
# define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg)))
2059
#elif defined(_MSC_VER)
2060
# define TOML11_DEPRECATED(msg) __declspec(deprecated(msg))
2061
#else
2062
# define TOML11_DEPRECATED(msg)
2063
#endif
2064
2065
// ----------------------------------------------------------------------------
2066
2067
#if defined(__cpp_if_constexpr)
2068
# if __cpp_if_constexpr >= 201606L
2069
# define TOML11_HAS_CONSTEXPR_IF 1
2070
# endif
2071
#endif
2072
2073
#if defined(TOML11_HAS_CONSTEXPR_IF)
2074
# define TOML11_CONSTEXPR_IF if constexpr
2075
#else
2076
# define TOML11_CONSTEXPR_IF if
2077
#endif
2078
2079
// ----------------------------------------------------------------------------
2080
2081
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
2082
# if defined(__cpp_lib_make_unique)
2083
# if __cpp_lib_make_unique >= 201304L
2084
# define TOML11_HAS_STD_MAKE_UNIQUE 1
2085
# endif
2086
# endif
2087
#endif
2088
2089
namespace toml
2090
{
2091
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2092
{
2093
namespace cxx
2094
{
2095
2096
#if defined(TOML11_HAS_STD_MAKE_UNIQUE)
2097
2098
using std::make_unique;
2099
2100
#else
2101
2102
template<typename T, typename ... Ts>
2103
std::unique_ptr<T> make_unique(Ts&& ... args)
2104
{
2105
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
2106
}
2107
2108
#endif // TOML11_HAS_STD_MAKE_UNIQUE
2109
2110
} // cxx
2111
} // TOML11_INLINE_VERSION_NAMESPACE
2112
} // toml
2113
2114
// ---------------------------------------------------------------------------
2115
2116
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
2117
# if defined(__cpp_lib_make_reverse_iterator)
2118
# if __cpp_lib_make_reverse_iterator >= 201402L
2119
# define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1
2120
# endif
2121
# endif
2122
#endif
2123
2124
namespace toml
2125
{
2126
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2127
{
2128
namespace cxx
2129
{
2130
# if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR)
2131
2132
using std::make_reverse_iterator;
2133
2134
#else
2135
2136
template<typename Iterator>
2137
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator iter)
2138
{
2139
return std::reverse_iterator<Iterator>(iter);
2140
}
2141
2142
#endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR
2143
2144
} // cxx
2145
} // TOML11_INLINE_VERSION_NAMESPACE
2146
} // toml
2147
2148
// ---------------------------------------------------------------------------
2149
2150
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
2151
# if defined(__cpp_lib_clamp)
2152
# if __cpp_lib_clamp >= 201603L
2153
# define TOML11_HAS_STD_CLAMP 1
2154
# endif
2155
# endif
2156
#endif
2157
2158
namespace toml
2159
{
2160
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2161
{
2162
namespace cxx
2163
{
2164
#if defined(TOML11_HAS_STD_CLAMP)
2165
2166
using std::clamp;
2167
2168
#else
2169
2170
template<typename T>
2171
T clamp(const T& x, const T& low, const T& high) noexcept
2172
{
2173
assert(low <= high);
2174
return (std::min)((std::max)(x, low), high);
2175
}
2176
2177
#endif // TOML11_HAS_STD_CLAMP
2178
2179
} // cxx
2180
} // TOML11_INLINE_VERSION_NAMESPACE
2181
} // toml
2182
2183
// ---------------------------------------------------------------------------
2184
2185
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
2186
# if defined(__cpp_lib_bit_cast)
2187
# if __cpp_lib_bit_cast >= 201806L
2188
# define TOML11_HAS_STD_BIT_CAST 1
2189
# endif
2190
# endif
2191
#endif
2192
2193
namespace toml
2194
{
2195
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2196
{
2197
namespace cxx
2198
{
2199
#if defined(TOML11_HAS_STD_BIT_CAST)
2200
2201
using std::bit_cast;
2202
2203
#else
2204
2205
template<typename U, typename T>
2206
U bit_cast(const T& x) noexcept
2207
{
2208
static_assert(sizeof(T) == sizeof(U), "");
2209
static_assert(std::is_default_constructible<T>::value, "");
2210
2211
U z;
2212
std::memcpy(reinterpret_cast<char*>(std::addressof(z)),
2213
reinterpret_cast<const char*>(std::addressof(x)),
2214
sizeof(T));
2215
2216
return z;
2217
}
2218
2219
#endif // TOML11_HAS_STD_BIT_CAST
2220
2221
} // cxx
2222
} // TOML11_INLINE_VERSION_NAMESPACE
2223
} // toml
2224
2225
// ---------------------------------------------------------------------------
2226
// C++20 remove_cvref_t
2227
2228
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
2229
# if defined(__cpp_lib_remove_cvref)
2230
# if __cpp_lib_remove_cvref >= 201711L
2231
# define TOML11_HAS_STD_REMOVE_CVREF 1
2232
# endif
2233
# endif
2234
#endif
2235
2236
namespace toml
2237
{
2238
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2239
{
2240
namespace cxx
2241
{
2242
#if defined(TOML11_HAS_STD_REMOVE_CVREF)
2243
2244
using std::remove_cvref;
2245
using std::remove_cvref_t;
2246
2247
#else
2248
2249
template<typename T>
2250
struct remove_cvref
2251
{
2252
using type = typename std::remove_cv<
2253
typename std::remove_reference<T>::type>::type;
2254
};
2255
2256
template<typename T>
2257
using remove_cvref_t = typename remove_cvref<T>::type;
2258
2259
#endif // TOML11_HAS_STD_REMOVE_CVREF
2260
2261
} // cxx
2262
} // TOML11_INLINE_VERSION_NAMESPACE
2263
} // toml
2264
2265
// ---------------------------------------------------------------------------
2266
// C++17 and/or/not
2267
2268
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
2269
# if defined(__cpp_lib_logical_traits)
2270
# if __cpp_lib_logical_traits >= 201510L
2271
# define TOML11_HAS_STD_CONJUNCTION 1
2272
# endif
2273
# endif
2274
#endif
2275
2276
namespace toml
2277
{
2278
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2279
{
2280
namespace cxx
2281
{
2282
#if defined(TOML11_HAS_STD_CONJUNCTION)
2283
2284
using std::conjunction;
2285
using std::disjunction;
2286
using std::negation;
2287
2288
#else
2289
2290
template<typename ...> struct conjunction : std::true_type{};
2291
template<typename T> struct conjunction<T> : T{};
2292
template<typename T, typename ... Ts>
2293
struct conjunction<T, Ts...> :
2294
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
2295
{};
2296
2297
template<typename ...> struct disjunction : std::false_type{};
2298
template<typename T> struct disjunction<T> : T {};
2299
template<typename T, typename ... Ts>
2300
struct disjunction<T, Ts...> :
2301
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
2302
{};
2303
2304
template<typename T>
2305
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
2306
2307
#endif // TOML11_HAS_STD_CONJUNCTION
2308
2309
} // cxx
2310
} // TOML11_INLINE_VERSION_NAMESPACE
2311
} // toml
2312
2313
// ---------------------------------------------------------------------------
2314
// C++14 index_sequence
2315
2316
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
2317
# if defined(__cpp_lib_integer_sequence)
2318
# if __cpp_lib_integer_sequence >= 201304L
2319
# define TOML11_HAS_STD_INTEGER_SEQUENCE 1
2320
# endif
2321
# endif
2322
#endif
2323
2324
namespace toml
2325
{
2326
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2327
{
2328
namespace cxx
2329
{
2330
#if defined(TOML11_HAS_STD_INTEGER_SEQUENCE)
2331
2332
using std::index_sequence;
2333
using std::make_index_sequence;
2334
2335
#else
2336
2337
template<std::size_t ... Ns> struct index_sequence{};
2338
2339
template<bool B, std::size_t N, typename T>
2340
struct double_index_sequence;
2341
2342
template<std::size_t N, std::size_t ... Is>
2343
struct double_index_sequence<true, N, index_sequence<Is...>>
2344
{
2345
using type = index_sequence<Is..., (Is+N)..., N*2>;
2346
};
2347
template<std::size_t N, std::size_t ... Is>
2348
struct double_index_sequence<false, N, index_sequence<Is...>>
2349
{
2350
using type = index_sequence<Is..., (Is+N)...>;
2351
};
2352
2353
template<std::size_t N>
2354
struct index_sequence_maker
2355
{
2356
using type = typename double_index_sequence<
2357
N % 2 == 1, N/2, typename index_sequence_maker<N/2>::type
2358
>::type;
2359
};
2360
template<>
2361
struct index_sequence_maker<0>
2362
{
2363
using type = index_sequence<>;
2364
};
2365
2366
template<std::size_t N>
2367
using make_index_sequence = typename index_sequence_maker<N>::type;
2368
2369
#endif // TOML11_HAS_STD_INTEGER_SEQUENCE
2370
2371
} // cxx
2372
} // TOML11_INLINE_VERSION_NAMESPACE
2373
} // toml
2374
2375
// ---------------------------------------------------------------------------
2376
// C++14 enable_if_t
2377
2378
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
2379
# if defined(__cpp_lib_transformation_trait_aliases)
2380
# if __cpp_lib_transformation_trait_aliases >= 201304L
2381
# define TOML11_HAS_STD_ENABLE_IF_T 1
2382
# endif
2383
# endif
2384
#endif
2385
2386
namespace toml
2387
{
2388
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2389
{
2390
namespace cxx
2391
{
2392
#if defined(TOML11_HAS_STD_ENABLE_IF_T)
2393
2394
using std::enable_if_t;
2395
2396
#else
2397
2398
template<bool B, typename T>
2399
using enable_if_t = typename std::enable_if<B, T>::type;
2400
2401
#endif // TOML11_HAS_STD_ENABLE_IF_T
2402
2403
} // cxx
2404
} // TOML11_INLINE_VERSION_NAMESPACE
2405
} // toml
2406
2407
// ---------------------------------------------------------------------------
2408
// return_type_of_t
2409
2410
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
2411
# if defined(__cpp_lib_is_invocable)
2412
# if __cpp_lib_is_invocable >= 201703
2413
# define TOML11_HAS_STD_INVOKE_RESULT 1
2414
# endif
2415
# endif
2416
#endif
2417
2418
namespace toml
2419
{
2420
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2421
{
2422
namespace cxx
2423
{
2424
#if defined(TOML11_HAS_STD_INVOKE_RESULT)
2425
2426
template<typename F, typename ... Args>
2427
using return_type_of_t = std::invoke_result_t<F, Args...>;
2428
2429
#else
2430
2431
// result_of is deprecated after C++17
2432
template<typename F, typename ... Args>
2433
using return_type_of_t = typename std::result_of<F(Args...)>::type;
2434
2435
#endif // TOML11_HAS_STD_INVOKE_RESULT
2436
2437
} // cxx
2438
} // TOML11_INLINE_VERSION_NAMESPACE
2439
} // toml
2440
2441
// ---------------------------------------------------------------------------
2442
// C++17 void_t
2443
2444
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
2445
# if defined(__cpp_lib_void_t)
2446
# if __cpp_lib_void_t >= 201411L
2447
# define TOML11_HAS_STD_VOID_T 1
2448
# endif
2449
# endif
2450
#endif
2451
2452
namespace toml
2453
{
2454
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2455
{
2456
namespace cxx
2457
{
2458
#if defined(TOML11_HAS_STD_VOID_T)
2459
2460
using std::void_t;
2461
2462
#else
2463
2464
template<typename ...>
2465
using void_t = void;
2466
2467
#endif // TOML11_HAS_STD_VOID_T
2468
2469
} // cxx
2470
} // TOML11_INLINE_VERSION_NAMESPACE
2471
} // toml
2472
2473
// ----------------------------------------------------------------------------
2474
// (subset of) source_location
2475
2476
#if ! defined(TOML11_DISABLE_SOURCE_LOCATION) && TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L
2477
# if __has_include(<source_location>)
2478
# define TOML11_HAS_STD_SOURCE_LOCATION
2479
# endif // has_include
2480
#endif // c++20
2481
2482
#if ! defined(TOML11_DISABLE_SOURCE_LOCATION) && ! defined(TOML11_HAS_STD_SOURCE_LOCATION)
2483
# if defined(__GNUC__) && ! defined(__clang__)
2484
# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
2485
# if __has_include(<experimental/source_location>)
2486
# define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION
2487
# endif
2488
# endif
2489
# endif // GNU g++
2490
#endif // not TOML11_HAS_STD_SOURCE_LOCATION
2491
2492
#if ! defined(TOML11_DISABLE_SOURCE_LOCATION) && ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION)
2493
# if defined(__GNUC__) && ! defined(__clang__)
2494
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
2495
# define TOML11_HAS_BUILTIN_FILE_LINE 1
2496
# define TOML11_BUILTIN_LINE_TYPE int
2497
# endif
2498
# elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE
2499
# if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE)
2500
# define TOML11_HAS_BUILTIN_FILE_LINE 1
2501
# define TOML11_BUILTIN_LINE_TYPE unsigned int
2502
# endif
2503
# elif defined(_MSVC_LANG) && defined(_MSC_VER)
2504
# if _MSC_VER > 1926
2505
# define TOML11_HAS_BUILTIN_FILE_LINE 1
2506
# define TOML11_BUILTIN_LINE_TYPE int
2507
# endif
2508
# endif
2509
#endif
2510
2511
#if defined(TOML11_HAS_STD_SOURCE_LOCATION)
2512
#include <source_location>
2513
namespace toml
2514
{
2515
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2516
{
2517
namespace cxx
2518
{
2519
using source_location = std::source_location;
2520
2521
inline std::string to_string(const source_location& loc)
2522
{
2523
const char* fname = loc.file_name();
2524
if(fname)
2525
{
2526
return std::string(" at line ") + std::to_string(loc.line()) +
2527
std::string(" in file ") + std::string(fname);
2528
}
2529
else
2530
{
2531
return std::string(" at line ") + std::to_string(loc.line()) +
2532
std::string(" in unknown file");
2533
}
2534
}
2535
2536
} // cxx
2537
} // TOML11_INLINE_VERSION_NAMESPACE
2538
} // toml
2539
#elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION)
2540
#include <experimental/source_location>
2541
namespace toml
2542
{
2543
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2544
{
2545
namespace cxx
2546
{
2547
using source_location = std::experimental::source_location;
2548
2549
inline std::string to_string(const source_location& loc)
2550
{
2551
const char* fname = loc.file_name();
2552
if(fname)
2553
{
2554
return std::string(" at line ") + std::to_string(loc.line()) +
2555
std::string(" in file ") + std::string(fname);
2556
}
2557
else
2558
{
2559
return std::string(" at line ") + std::to_string(loc.line()) +
2560
std::string(" in unknown file");
2561
}
2562
}
2563
2564
} // cxx
2565
} // TOML11_INLINE_VERSION_NAMESPACE
2566
} // toml
2567
#elif defined(TOML11_HAS_BUILTIN_FILE_LINE)
2568
namespace toml
2569
{
2570
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2571
{
2572
namespace cxx
2573
{
2574
struct source_location
2575
{
2576
using line_type = TOML11_BUILTIN_LINE_TYPE;
2577
static source_location current(const line_type line = __builtin_LINE(),
2578
const char* file = __builtin_FILE())
2579
{
2580
return source_location(line, file);
2581
}
2582
2583
source_location(const line_type line, const char* file)
2584
: line_(line), file_name_(file)
2585
{}
2586
2587
line_type line() const noexcept {return line_;}
2588
const char* file_name() const noexcept {return file_name_;}
2589
2590
private:
2591
2592
line_type line_;
2593
const char* file_name_;
2594
};
2595
2596
inline std::string to_string(const source_location& loc)
2597
{
2598
const char* fname = loc.file_name();
2599
if(fname)
2600
{
2601
return std::string(" at line ") + std::to_string(loc.line()) +
2602
std::string(" in file ") + std::string(fname);
2603
}
2604
else
2605
{
2606
return std::string(" at line ") + std::to_string(loc.line()) +
2607
std::string(" in unknown file");
2608
}
2609
}
2610
2611
} // cxx
2612
} // TOML11_INLINE_VERSION_NAMESPACE
2613
} // toml
2614
#else // no builtin
2615
namespace toml
2616
{
2617
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2618
{
2619
namespace cxx
2620
{
2621
struct source_location
2622
{
2623
static source_location current() { return source_location{}; }
2624
};
2625
2626
inline std::string to_string(const source_location&)
2627
{
2628
return std::string("");
2629
}
2630
} // cxx
2631
} // TOML11_INLINE_VERSION_NAMESPACE
2632
} // toml
2633
#endif // TOML11_HAS_STD_SOURCE_LOCATION
2634
2635
// ----------------------------------------------------------------------------
2636
// (subset of) optional
2637
2638
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
2639
# if __has_include(<optional>)
2640
# include <optional>
2641
# endif // has_include(optional)
2642
#endif // C++17
2643
2644
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
2645
# if defined(__cpp_lib_optional)
2646
# if __cpp_lib_optional >= 201606L
2647
# define TOML11_HAS_STD_OPTIONAL 1
2648
# endif
2649
# endif
2650
#endif
2651
2652
#if defined(TOML11_HAS_STD_OPTIONAL)
2653
2654
namespace toml
2655
{
2656
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2657
{
2658
namespace cxx
2659
{
2660
using std::optional;
2661
2662
inline std::nullopt_t make_nullopt() {return std::nullopt;}
2663
2664
template<typename charT, typename traitsT>
2665
std::basic_ostream<charT, traitsT>&
2666
operator<<(std::basic_ostream<charT, traitsT>& os, const std::nullopt_t&)
2667
{
2668
os << "nullopt";
2669
return os;
2670
}
2671
2672
} // cxx
2673
} // TOML11_INLINE_VERSION_NAMESPACE
2674
} // toml
2675
2676
#else // TOML11_HAS_STD_OPTIONAL
2677
2678
namespace toml
2679
{
2680
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2681
{
2682
namespace cxx
2683
{
2684
2685
struct nullopt_t{};
2686
inline nullopt_t make_nullopt() {return nullopt_t{};}
2687
2688
inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;}
2689
inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;}
2690
inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;}
2691
inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;}
2692
inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;}
2693
inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;}
2694
2695
template<typename charT, typename traitsT>
2696
std::basic_ostream<charT, traitsT>&
2697
operator<<(std::basic_ostream<charT, traitsT>& os, const nullopt_t&)
2698
{
2699
os << "nullopt";
2700
return os;
2701
}
2702
2703
template<typename T>
2704
class optional
2705
{
2706
public:
2707
2708
using value_type = T;
2709
2710
public:
2711
2712
optional() noexcept : has_value_(false), null_('\0') {}
2713
optional(nullopt_t) noexcept : has_value_(false), null_('\0') {}
2714
2715
optional(const T& x): has_value_(true), value_(x) {}
2716
optional(T&& x): has_value_(true), value_(std::move(x)) {}
2717
2718
template<typename U, enable_if_t<std::is_constructible<T, U>::value, std::nullptr_t> = nullptr>
2719
explicit optional(U&& x): has_value_(true), value_(std::forward<U>(x)) {}
2720
2721
optional(const optional& rhs): has_value_(rhs.has_value_)
2722
{
2723
if(rhs.has_value_)
2724
{
2725
this->assigner(rhs.value_);
2726
}
2727
}
2728
optional(optional&& rhs): has_value_(rhs.has_value_)
2729
{
2730
if(this->has_value_)
2731
{
2732
this->assigner(std::move(rhs.value_));
2733
}
2734
}
2735
2736
optional& operator=(const optional& rhs)
2737
{
2738
if(this == std::addressof(rhs)) {return *this;}
2739
2740
this->cleanup();
2741
this->has_value_ = rhs.has_value_;
2742
if(this->has_value_)
2743
{
2744
this->assigner(rhs.value_);
2745
}
2746
return *this;
2747
}
2748
optional& operator=(optional&& rhs)
2749
{
2750
if(this == std::addressof(rhs)) {return *this;}
2751
2752
this->cleanup();
2753
this->has_value_ = rhs.has_value_;
2754
if(this->has_value_)
2755
{
2756
this->assigner(std::move(rhs.value_));
2757
}
2758
return *this;
2759
}
2760
2761
template<typename U, enable_if_t<conjunction<
2762
negation<std::is_same<T, U>>, std::is_constructible<T, U>
2763
>::value, std::nullptr_t> = nullptr>
2764
explicit optional(const optional<U>& rhs): has_value_(rhs.has_value_), null_('\0')
2765
{
2766
if(rhs.has_value_)
2767
{
2768
this->assigner(rhs.value_);
2769
}
2770
}
2771
template<typename U, enable_if_t<conjunction<
2772
negation<std::is_same<T, U>>, std::is_constructible<T, U>
2773
>::value, std::nullptr_t> = nullptr>
2774
explicit optional(optional<U>&& rhs): has_value_(rhs.has_value_), null_('\0')
2775
{
2776
if(this->has_value_)
2777
{
2778
this->assigner(std::move(rhs.value_));
2779
}
2780
}
2781
2782
template<typename U, enable_if_t<conjunction<
2783
negation<std::is_same<T, U>>, std::is_constructible<T, U>
2784
>::value, std::nullptr_t> = nullptr>
2785
optional& operator=(const optional<U>& rhs)
2786
{
2787
if(this == std::addressof(rhs)) {return *this;}
2788
2789
this->cleanup();
2790
this->has_value_ = rhs.has_value_;
2791
if(this->has_value_)
2792
{
2793
this->assigner(rhs.value_);
2794
}
2795
return *this;
2796
}
2797
2798
template<typename U, enable_if_t<conjunction<
2799
negation<std::is_same<T, U>>, std::is_constructible<T, U>
2800
>::value, std::nullptr_t> = nullptr>
2801
optional& operator=(optional<U>&& rhs)
2802
{
2803
if(this == std::addressof(rhs)) {return *this;}
2804
2805
this->cleanup();
2806
this->has_value_ = rhs.has_value_;
2807
if(this->has_value_)
2808
{
2809
this->assigner(std::move(rhs.value_));
2810
}
2811
return *this;
2812
}
2813
~optional() noexcept
2814
{
2815
this->cleanup();
2816
}
2817
2818
explicit operator bool() const noexcept
2819
{
2820
return has_value_;
2821
}
2822
2823
bool has_value() const noexcept {return has_value_;}
2824
2825
value_type const& value(source_location loc = source_location::current()) const
2826
{
2827
if( ! this->has_value_)
2828
{
2829
throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc));
2830
}
2831
return this->value_;
2832
}
2833
value_type& value(source_location loc = source_location::current())
2834
{
2835
if( ! this->has_value_)
2836
{
2837
throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc));
2838
}
2839
return this->value_;
2840
}
2841
2842
value_type const& value_or(const value_type& opt) const
2843
{
2844
if(this->has_value_) {return this->value_;} else {return opt;}
2845
}
2846
value_type& value_or(value_type& opt)
2847
{
2848
if(this->has_value_) {return this->value_;} else {return opt;}
2849
}
2850
2851
private:
2852
2853
void cleanup() noexcept
2854
{
2855
if(this->has_value_)
2856
{
2857
value_.~T();
2858
}
2859
}
2860
2861
template<typename U>
2862
void assigner(U&& x)
2863
{
2864
const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward<U>(x));
2865
assert(tmp == std::addressof(this->value_));
2866
(void)tmp;
2867
}
2868
2869
private:
2870
2871
bool has_value_;
2872
union
2873
{
2874
char null_;
2875
T value_;
2876
};
2877
};
2878
} // cxx
2879
} // TOML11_INLINE_VERSION_NAMESPACE
2880
} // toml
2881
#endif // TOML11_HAS_STD_OPTIONAL
2882
2883
#endif // TOML11_COMPAT_HPP
2884
#ifndef TOML11_VALUE_T_HPP
2885
#define TOML11_VALUE_T_HPP
2886
2887
#ifndef TOML11_VALUE_T_FWD_HPP
2888
#define TOML11_VALUE_T_FWD_HPP
2889
2890
2891
#include <iosfwd>
2892
#include <string>
2893
#include <type_traits>
2894
2895
#include <cstdint>
2896
2897
namespace toml
2898
{
2899
inline namespace TOML11_INLINE_VERSION_NAMESPACE
2900
{
2901
2902
// forward decl
2903
template<typename TypeConfig>
2904
class basic_value;
2905
2906
// ----------------------------------------------------------------------------
2907
// enum representing toml types
2908
2909
enum class value_t : std::uint8_t
2910
{
2911
empty = 0,
2912
boolean = 1,
2913
integer = 2,
2914
floating = 3,
2915
string = 4,
2916
offset_datetime = 5,
2917
local_datetime = 6,
2918
local_date = 7,
2919
local_time = 8,
2920
array = 9,
2921
table = 10
2922
};
2923
2924
std::ostream& operator<<(std::ostream& os, value_t t);
2925
std::string to_string(value_t t);
2926
2927
2928
// ----------------------------------------------------------------------------
2929
// meta functions for internal use
2930
2931
namespace detail
2932
{
2933
2934
template<value_t V>
2935
using value_t_constant = std::integral_constant<value_t, V>;
2936
2937
template<typename T, typename Value>
2938
struct type_to_enum : value_t_constant<value_t::empty> {};
2939
2940
template<typename V> struct type_to_enum<typename V::boolean_type , V> : value_t_constant<value_t::boolean > {};
2941
template<typename V> struct type_to_enum<typename V::integer_type , V> : value_t_constant<value_t::integer > {};
2942
template<typename V> struct type_to_enum<typename V::floating_type , V> : value_t_constant<value_t::floating > {};
2943
template<typename V> struct type_to_enum<typename V::string_type , V> : value_t_constant<value_t::string > {};
2944
template<typename V> struct type_to_enum<typename V::offset_datetime_type, V> : value_t_constant<value_t::offset_datetime> {};
2945
template<typename V> struct type_to_enum<typename V::local_datetime_type , V> : value_t_constant<value_t::local_datetime > {};
2946
template<typename V> struct type_to_enum<typename V::local_date_type , V> : value_t_constant<value_t::local_date > {};
2947
template<typename V> struct type_to_enum<typename V::local_time_type , V> : value_t_constant<value_t::local_time > {};
2948
template<typename V> struct type_to_enum<typename V::array_type , V> : value_t_constant<value_t::array > {};
2949
template<typename V> struct type_to_enum<typename V::table_type , V> : value_t_constant<value_t::table > {};
2950
2951
template<value_t V, typename Value>
2952
struct enum_to_type { using type = void; };
2953
2954
template<typename V> struct enum_to_type<value_t::boolean , V> { using type = typename V::boolean_type ; };
2955
template<typename V> struct enum_to_type<value_t::integer , V> { using type = typename V::integer_type ; };
2956
template<typename V> struct enum_to_type<value_t::floating , V> { using type = typename V::floating_type ; };
2957
template<typename V> struct enum_to_type<value_t::string , V> { using type = typename V::string_type ; };
2958
template<typename V> struct enum_to_type<value_t::offset_datetime, V> { using type = typename V::offset_datetime_type; };
2959
template<typename V> struct enum_to_type<value_t::local_datetime , V> { using type = typename V::local_datetime_type ; };
2960
template<typename V> struct enum_to_type<value_t::local_date , V> { using type = typename V::local_date_type ; };
2961
template<typename V> struct enum_to_type<value_t::local_time , V> { using type = typename V::local_time_type ; };
2962
template<typename V> struct enum_to_type<value_t::array , V> { using type = typename V::array_type ; };
2963
template<typename V> struct enum_to_type<value_t::table , V> { using type = typename V::table_type ; };
2964
2965
template<value_t V, typename Value>
2966
using enum_to_type_t = typename enum_to_type<V, Value>::type;
2967
2968
template<value_t V>
2969
struct enum_to_fmt_type { using type = void; };
2970
2971
template<> struct enum_to_fmt_type<value_t::boolean > { using type = boolean_format_info ; };
2972
template<> struct enum_to_fmt_type<value_t::integer > { using type = integer_format_info ; };
2973
template<> struct enum_to_fmt_type<value_t::floating > { using type = floating_format_info ; };
2974
template<> struct enum_to_fmt_type<value_t::string > { using type = string_format_info ; };
2975
template<> struct enum_to_fmt_type<value_t::offset_datetime> { using type = offset_datetime_format_info; };
2976
template<> struct enum_to_fmt_type<value_t::local_datetime > { using type = local_datetime_format_info ; };
2977
template<> struct enum_to_fmt_type<value_t::local_date > { using type = local_date_format_info ; };
2978
template<> struct enum_to_fmt_type<value_t::local_time > { using type = local_time_format_info ; };
2979
template<> struct enum_to_fmt_type<value_t::array > { using type = array_format_info ; };
2980
template<> struct enum_to_fmt_type<value_t::table > { using type = table_format_info ; };
2981
2982
template<value_t V>
2983
using enum_to_fmt_type_t = typename enum_to_fmt_type<V>::type;
2984
2985
template<typename T, typename Value>
2986
struct is_exact_toml_type0 : cxx::disjunction<
2987
std::is_same<T, typename Value::boolean_type >,
2988
std::is_same<T, typename Value::integer_type >,
2989
std::is_same<T, typename Value::floating_type >,
2990
std::is_same<T, typename Value::string_type >,
2991
std::is_same<T, typename Value::offset_datetime_type>,
2992
std::is_same<T, typename Value::local_datetime_type >,
2993
std::is_same<T, typename Value::local_date_type >,
2994
std::is_same<T, typename Value::local_time_type >,
2995
std::is_same<T, typename Value::array_type >,
2996
std::is_same<T, typename Value::table_type >
2997
>{};
2998
template<typename T, typename V> struct is_exact_toml_type: is_exact_toml_type0<cxx::remove_cvref_t<T>, V> {};
2999
template<typename T, typename V> struct is_not_toml_type : cxx::negation<is_exact_toml_type<T, V>> {};
3000
3001
} // namespace detail
3002
} // TOML11_INLINE_VERSION_NAMESPACE
3003
} // namespace toml
3004
#endif // TOML11_VALUE_T_FWD_HPP
3005
3006
#if ! defined(TOML11_COMPILE_SOURCES)
3007
#ifndef TOML11_VALUE_T_IMPL_HPP
3008
#define TOML11_VALUE_T_IMPL_HPP
3009
3010
3011
#include <ostream>
3012
#include <sstream>
3013
#include <string>
3014
3015
namespace toml
3016
{
3017
inline namespace TOML11_INLINE_VERSION_NAMESPACE
3018
{
3019
3020
TOML11_INLINE std::ostream& operator<<(std::ostream& os, value_t t)
3021
{
3022
switch(t)
3023
{
3024
case value_t::boolean : os << "boolean"; return os;
3025
case value_t::integer : os << "integer"; return os;
3026
case value_t::floating : os << "floating"; return os;
3027
case value_t::string : os << "string"; return os;
3028
case value_t::offset_datetime : os << "offset_datetime"; return os;
3029
case value_t::local_datetime : os << "local_datetime"; return os;
3030
case value_t::local_date : os << "local_date"; return os;
3031
case value_t::local_time : os << "local_time"; return os;
3032
case value_t::array : os << "array"; return os;
3033
case value_t::table : os << "table"; return os;
3034
case value_t::empty : os << "empty"; return os;
3035
default : os << "unknown"; return os;
3036
}
3037
}
3038
3039
TOML11_INLINE std::string to_string(value_t t)
3040
{
3041
std::ostringstream oss;
3042
oss << t;
3043
return oss.str();
3044
}
3045
3046
} //TOML11_INLINE_VERSION_NAMESPACE
3047
} // namespace toml
3048
#endif // TOML11_VALUE_T_IMPL_HPP
3049
#endif
3050
3051
#endif // TOML11_VALUE_T_HPP
3052
#ifndef TOML11_TRAITS_HPP
3053
#define TOML11_TRAITS_HPP
3054
3055
3056
#include <array>
3057
#include <chrono>
3058
#include <forward_list>
3059
#include <string>
3060
#include <tuple>
3061
#include <type_traits>
3062
#include <unordered_set>
3063
#include <utility>
3064
3065
#if defined(TOML11_HAS_STRING_VIEW)
3066
#include <string_view>
3067
#endif
3068
3069
#if defined(TOML11_HAS_OPTIONAL)
3070
#include <optional>
3071
#endif
3072
3073
namespace toml
3074
{
3075
inline namespace TOML11_INLINE_VERSION_NAMESPACE
3076
{
3077
template<typename TypeConcig>
3078
class basic_value;
3079
3080
namespace detail
3081
{
3082
// ---------------------------------------------------------------------------
3083
// check whether type T is a kind of container/map class
3084
3085
struct has_iterator_impl
3086
{
3087
template<typename T> static std::true_type check(typename T::iterator*);
3088
template<typename T> static std::false_type check(...);
3089
};
3090
struct has_value_type_impl
3091
{
3092
template<typename T> static std::true_type check(typename T::value_type*);
3093
template<typename T> static std::false_type check(...);
3094
};
3095
struct has_key_type_impl
3096
{
3097
template<typename T> static std::true_type check(typename T::key_type*);
3098
template<typename T> static std::false_type check(...);
3099
};
3100
struct has_mapped_type_impl
3101
{
3102
template<typename T> static std::true_type check(typename T::mapped_type*);
3103
template<typename T> static std::false_type check(...);
3104
};
3105
struct has_reserve_method_impl
3106
{
3107
template<typename T> static std::false_type check(...);
3108
template<typename T> static std::true_type check(
3109
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
3110
};
3111
struct has_push_back_method_impl
3112
{
3113
template<typename T> static std::false_type check(...);
3114
template<typename T> static std::true_type check(
3115
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
3116
};
3117
struct is_comparable_impl
3118
{
3119
template<typename T> static std::false_type check(...);
3120
template<typename T> static std::true_type check(
3121
decltype(std::declval<T>() < std::declval<T>())*);
3122
};
3123
3124
struct has_from_toml_method_impl
3125
{
3126
template<typename T, typename TC>
3127
static std::true_type check(
3128
decltype(std::declval<T>().from_toml(std::declval<::toml::basic_value<TC>>()))*);
3129
3130
template<typename T, typename TC>
3131
static std::false_type check(...);
3132
};
3133
struct has_into_toml_method_impl
3134
{
3135
template<typename T>
3136
static std::true_type check(decltype(std::declval<T>().into_toml())*);
3137
template<typename T>
3138
static std::false_type check(...);
3139
};
3140
3141
struct has_template_into_toml_method_impl
3142
{
3143
template<typename T, typename TypeConfig>
3144
static std::true_type check(decltype(std::declval<T>().template into_toml<TypeConfig>())*);
3145
template<typename T, typename TypeConfig>
3146
static std::false_type check(...);
3147
};
3148
3149
struct has_specialized_from_impl
3150
{
3151
template<typename T>
3152
static std::false_type check(...);
3153
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
3154
static std::true_type check(::toml::from<T>*);
3155
};
3156
struct has_specialized_into_impl
3157
{
3158
template<typename T>
3159
static std::false_type check(...);
3160
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
3161
static std::true_type check(::toml::into<T>*);
3162
};
3163
3164
3165
/// Intel C++ compiler can not use decltype in parent class declaration, here
3166
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
3167
#ifdef __INTEL_COMPILER
3168
#define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
3169
#endif
3170
3171
template<typename T>
3172
struct has_iterator: decltype(has_iterator_impl::check<T>(nullptr)){};
3173
template<typename T>
3174
struct has_value_type: decltype(has_value_type_impl::check<T>(nullptr)){};
3175
template<typename T>
3176
struct has_key_type: decltype(has_key_type_impl::check<T>(nullptr)){};
3177
template<typename T>
3178
struct has_mapped_type: decltype(has_mapped_type_impl::check<T>(nullptr)){};
3179
template<typename T>
3180
struct has_reserve_method: decltype(has_reserve_method_impl::check<T>(nullptr)){};
3181
template<typename T>
3182
struct has_push_back_method: decltype(has_push_back_method_impl::check<T>(nullptr)){};
3183
template<typename T>
3184
struct is_comparable: decltype(is_comparable_impl::check<T>(nullptr)){};
3185
3186
template<typename T, typename TC>
3187
struct has_from_toml_method: decltype(has_from_toml_method_impl::check<T, TC>(nullptr)){};
3188
3189
template<typename T>
3190
struct has_into_toml_method: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
3191
3192
template<typename T, typename TypeConfig>
3193
struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check<T, TypeConfig>(nullptr)){};
3194
3195
template<typename T>
3196
struct has_specialized_from: decltype(has_specialized_from_impl::check<T>(nullptr)){};
3197
template<typename T>
3198
struct has_specialized_into: decltype(has_specialized_into_impl::check<T>(nullptr)){};
3199
3200
#ifdef __INTEL_COMPILER
3201
#undef decltype
3202
#endif
3203
3204
// ---------------------------------------------------------------------------
3205
// type checkers
3206
3207
template<typename T> struct is_std_pair_impl : std::false_type{};
3208
template<typename T1, typename T2>
3209
struct is_std_pair_impl<std::pair<T1, T2>> : std::true_type{};
3210
template<typename T>
3211
using is_std_pair = is_std_pair_impl<cxx::remove_cvref_t<T>>;
3212
3213
template<typename T> struct is_std_tuple_impl : std::false_type{};
3214
template<typename ... Ts>
3215
struct is_std_tuple_impl<std::tuple<Ts...>> : std::true_type{};
3216
template<typename T>
3217
using is_std_tuple = is_std_tuple_impl<cxx::remove_cvref_t<T>>;
3218
3219
template<typename T> struct is_unordered_set_impl : std::false_type {};
3220
template<typename T>
3221
struct is_unordered_set_impl<std::unordered_set<T>> : std::true_type {};
3222
template<typename T>
3223
using is_unordered_set = is_unordered_set_impl<cxx::remove_cvref_t<T>>;
3224
3225
#if defined(TOML11_HAS_OPTIONAL)
3226
template<typename T> struct is_std_optional_impl : std::false_type{};
3227
template<typename T>
3228
struct is_std_optional_impl<std::optional<T>> : std::true_type{};
3229
template<typename T>
3230
using is_std_optional = is_std_optional_impl<cxx::remove_cvref_t<T>>;
3231
#else
3232
template<typename T> struct is_std_optional : std::false_type{};
3233
#endif // > C++17
3234
3235
template<typename T> struct is_std_array_impl : std::false_type{};
3236
template<typename T, std::size_t N>
3237
struct is_std_array_impl<std::array<T, N>> : std::true_type{};
3238
template<typename T>
3239
using is_std_array = is_std_array_impl<cxx::remove_cvref_t<T>>;
3240
3241
template<typename T> struct is_std_forward_list_impl : std::false_type{};
3242
template<typename T>
3243
struct is_std_forward_list_impl<std::forward_list<T>> : std::true_type{};
3244
template<typename T>
3245
using is_std_forward_list = is_std_forward_list_impl<cxx::remove_cvref_t<T>>;
3246
3247
template<typename T> struct is_std_basic_string_impl : std::false_type{};
3248
template<typename C, typename T, typename A>
3249
struct is_std_basic_string_impl<std::basic_string<C, T, A>> : std::true_type{};
3250
template<typename T>
3251
using is_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>;
3252
3253
template<typename T> struct is_1byte_std_basic_string_impl : std::false_type{};
3254
template<typename C, typename T, typename A>
3255
struct is_1byte_std_basic_string_impl<std::basic_string<C, T, A>>
3256
: std::integral_constant<bool, sizeof(C) == sizeof(char)> {};
3257
template<typename T>
3258
using is_1byte_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>;
3259
3260
#if defined(TOML11_HAS_STRING_VIEW)
3261
template<typename T> struct is_std_basic_string_view_impl : std::false_type{};
3262
template<typename C, typename T>
3263
struct is_std_basic_string_view_impl<std::basic_string_view<C, T>> : std::true_type{};
3264
template<typename T>
3265
using is_std_basic_string_view = is_std_basic_string_view_impl<cxx::remove_cvref_t<T>>;
3266
3267
template<typename V, typename S>
3268
struct is_string_view_of : std::false_type {};
3269
template<typename C, typename T>
3270
struct is_string_view_of<std::basic_string_view<C, T>, std::basic_string<C, T>> : std::true_type {};
3271
#else
3272
template<typename T>
3273
struct is_std_basic_string_view : std::false_type {};
3274
template<typename V, typename S>
3275
struct is_string_view_of : std::false_type {};
3276
#endif
3277
3278
template<typename T> struct is_chrono_duration_impl: std::false_type{};
3279
template<typename Rep, typename Period>
3280
struct is_chrono_duration_impl<std::chrono::duration<Rep, Period>>: std::true_type{};
3281
template<typename T>
3282
using is_chrono_duration = is_chrono_duration_impl<cxx::remove_cvref_t<T>>;
3283
3284
template<typename T>
3285
struct is_map_impl : cxx::conjunction< // map satisfies all the following conditions
3286
has_iterator<T>, // has T::iterator
3287
has_value_type<T>, // has T::value_type
3288
has_key_type<T>, // has T::key_type
3289
has_mapped_type<T> // has T::mapped_type
3290
>{};
3291
template<typename T>
3292
using is_map = is_map_impl<cxx::remove_cvref_t<T>>;
3293
3294
template<typename T>
3295
struct is_container_impl : cxx::conjunction<
3296
cxx::negation<is_map<T>>, // not a map
3297
cxx::negation<std::is_same<T, std::string>>, // not a std::string
3298
#ifdef TOML11_HAS_STRING_VIEW
3299
cxx::negation<std::is_same<T, std::string_view>>, // not a std::string_view
3300
#endif
3301
has_iterator<T>, // has T::iterator
3302
has_value_type<T> // has T::value_type
3303
>{};
3304
template<typename T>
3305
using is_container = is_container_impl<cxx::remove_cvref_t<T>>;
3306
3307
template<typename T>
3308
struct is_basic_value_impl: std::false_type{};
3309
template<typename TC>
3310
struct is_basic_value_impl<::toml::basic_value<TC>>: std::true_type{};
3311
template<typename T>
3312
using is_basic_value = is_basic_value_impl<cxx::remove_cvref_t<T>>;
3313
3314
}// detail
3315
} // TOML11_INLINE_VERSION_NAMESPACE
3316
} // toml
3317
#endif // TOML11_TRAITS_HPP
3318
#ifndef TOML11_STORAGE_HPP
3319
#define TOML11_STORAGE_HPP
3320
3321
3322
namespace toml
3323
{
3324
inline namespace TOML11_INLINE_VERSION_NAMESPACE
3325
{
3326
namespace detail
3327
{
3328
3329
// It owns a pointer to T. It does deep-copy when copied.
3330
// This struct is introduced to implement a recursive type.
3331
//
3332
// `toml::value` contains `std::vector<toml::value>` to represent a toml array.
3333
// But, in the definition of `toml::value`, `toml::value` is still incomplete.
3334
// `std::vector` of an incomplete type is not allowed in C++11 (it is allowed
3335
// after C++17). To avoid this, we need to use a pointer to `toml::value`, like
3336
// `std::vector<std::unique_ptr<toml::value>>`. Although `std::unique_ptr` is
3337
// noncopyable, we want to make `toml::value` copyable. `storage` is introduced
3338
// to resolve those problems.
3339
template<typename T>
3340
struct storage
3341
{
3342
using value_type = T;
3343
3344
explicit storage(value_type v): ptr_(cxx::make_unique<T>(std::move(v))) {}
3345
~storage() = default;
3346
3347
storage(const storage& rhs): ptr_(cxx::make_unique<T>(*rhs.ptr_)) {}
3348
storage& operator=(const storage& rhs)
3349
{
3350
this->ptr_ = cxx::make_unique<T>(*rhs.ptr_);
3351
return *this;
3352
}
3353
3354
storage(storage&&) = default;
3355
storage& operator=(storage&&) = default;
3356
3357
bool is_ok() const noexcept {return static_cast<bool>(ptr_);}
3358
3359
value_type& get() const noexcept {return *ptr_;}
3360
3361
private:
3362
std::unique_ptr<value_type> ptr_;
3363
};
3364
3365
} // detail
3366
} // TOML11_INLINE_VERSION_NAMESPACE
3367
} // toml
3368
#endif // TOML11_STORAGE_HPP
3369
#ifndef TOML11_RESULT_HPP
3370
#define TOML11_RESULT_HPP
3371
3372
3373
#include <ostream>
3374
#include <string>
3375
#include <type_traits>
3376
#include <utility>
3377
3378
#include <cassert>
3379
3380
namespace toml
3381
{
3382
inline namespace TOML11_INLINE_VERSION_NAMESPACE
3383
{
3384
3385
struct bad_result_access final : public ::toml::exception
3386
{
3387
public:
3388
explicit bad_result_access(std::string what_arg)
3389
: what_(std::move(what_arg))
3390
{}
3391
~bad_result_access() noexcept override = default;
3392
const char* what() const noexcept override {return what_.c_str();}
3393
3394
private:
3395
std::string what_;
3396
};
3397
3398
// -----------------------------------------------------------------------------
3399
3400
template<typename T>
3401
struct success
3402
{
3403
static_assert( ! std::is_void<T>::value, "");
3404
3405
using value_type = T;
3406
3407
explicit success(value_type v)
3408
noexcept(std::is_nothrow_move_constructible<value_type>::value)
3409
: value(std::move(v))
3410
{}
3411
3412
template<typename U, cxx::enable_if_t<
3413
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
3414
std::nullptr_t> = nullptr>
3415
explicit success(U&& v): value(std::forward<U>(v)) {}
3416
3417
template<typename U>
3418
explicit success(success<U> v): value(std::move(v.value)) {}
3419
3420
~success() = default;
3421
success(const success&) = default;
3422
success(success&&) = default;
3423
success& operator=(const success&) = default;
3424
success& operator=(success&&) = default;
3425
3426
value_type& get() noexcept {return value;}
3427
value_type const& get() const noexcept {return value;}
3428
3429
private:
3430
3431
value_type value;
3432
};
3433
3434
template<typename T>
3435
struct success<std::reference_wrapper<T>>
3436
{
3437
static_assert( ! std::is_void<T>::value, "");
3438
3439
using value_type = T;
3440
3441
explicit success(std::reference_wrapper<value_type> v) noexcept
3442
: value(std::move(v))
3443
{}
3444
3445
~success() = default;
3446
success(const success&) = default;
3447
success(success&&) = default;
3448
success& operator=(const success&) = default;
3449
success& operator=(success&&) = default;
3450
3451
value_type& get() noexcept {return value.get();}
3452
value_type const& get() const noexcept {return value.get();}
3453
3454
private:
3455
3456
std::reference_wrapper<value_type> value;
3457
};
3458
3459
template<typename T>
3460
success<typename std::decay<T>::type> ok(T&& v)
3461
{
3462
return success<typename std::decay<T>::type>(std::forward<T>(v));
3463
}
3464
template<std::size_t N>
3465
success<std::string> ok(const char (&literal)[N])
3466
{
3467
return success<std::string>(std::string(literal));
3468
}
3469
3470
// -----------------------------------------------------------------------------
3471
3472
template<typename T>
3473
struct failure
3474
{
3475
using value_type = T;
3476
3477
explicit failure(value_type v)
3478
noexcept(std::is_nothrow_move_constructible<value_type>::value)
3479
: value(std::move(v))
3480
{}
3481
3482
template<typename U, cxx::enable_if_t<
3483
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
3484
std::nullptr_t> = nullptr>
3485
explicit failure(U&& v): value(std::forward<U>(v)) {}
3486
3487
template<typename U>
3488
explicit failure(failure<U> v): value(std::move(v.value)) {}
3489
3490
~failure() = default;
3491
failure(const failure&) = default;
3492
failure(failure&&) = default;
3493
failure& operator=(const failure&) = default;
3494
failure& operator=(failure&&) = default;
3495
3496
value_type& get() noexcept {return value;}
3497
value_type const& get() const noexcept {return value;}
3498
3499
private:
3500
3501
value_type value;
3502
};
3503
3504
template<typename T>
3505
struct failure<std::reference_wrapper<T>>
3506
{
3507
using value_type = T;
3508
3509
explicit failure(std::reference_wrapper<value_type> v) noexcept
3510
: value(std::move(v))
3511
{}
3512
3513
~failure() = default;
3514
failure(const failure&) = default;
3515
failure(failure&&) = default;
3516
failure& operator=(const failure&) = default;
3517
failure& operator=(failure&&) = default;
3518
3519
value_type& get() noexcept {return value.get();}
3520
value_type const& get() const noexcept {return value.get();}
3521
3522
private:
3523
3524
std::reference_wrapper<value_type> value;
3525
};
3526
3527
template<typename T>
3528
failure<typename std::decay<T>::type> err(T&& v)
3529
{
3530
return failure<typename std::decay<T>::type>(std::forward<T>(v));
3531
}
3532
3533
template<std::size_t N>
3534
failure<std::string> err(const char (&literal)[N])
3535
{
3536
return failure<std::string>(std::string(literal));
3537
}
3538
3539
/* ============================================================================
3540
* _ _
3541
* _ _ ___ ____ _| | |_
3542
* | '_/ -_|_-< || | | _|
3543
* |_| \___/__/\_,_|_|\__|
3544
*/
3545
3546
template<typename T, typename E>
3547
struct result
3548
{
3549
using success_type = success<T>;
3550
using failure_type = failure<E>;
3551
using value_type = typename success_type::value_type;
3552
using error_type = typename failure_type::value_type;
3553
3554
result(success_type s): is_ok_(true), succ_(std::move(s)) {}
3555
result(failure_type f): is_ok_(false), fail_(std::move(f)) {}
3556
3557
template<typename U, cxx::enable_if_t<cxx::conjunction<
3558
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
3559
std::is_convertible<cxx::remove_cvref_t<U>, value_type>
3560
>::value, std::nullptr_t> = nullptr>
3561
result(success<U> s): is_ok_(true), succ_(std::move(s.value)) {}
3562
3563
template<typename U, cxx::enable_if_t<cxx::conjunction<
3564
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>,
3565
std::is_convertible<cxx::remove_cvref_t<U>, error_type>
3566
>::value, std::nullptr_t> = nullptr>
3567
result(failure<U> f): is_ok_(false), fail_(std::move(f.value)) {}
3568
3569
result& operator=(success_type s)
3570
{
3571
this->cleanup();
3572
this->is_ok_ = true;
3573
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s));
3574
assert(tmp == std::addressof(this->succ_));
3575
(void)tmp;
3576
return *this;
3577
}
3578
result& operator=(failure_type f)
3579
{
3580
this->cleanup();
3581
this->is_ok_ = false;
3582
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f));
3583
assert(tmp == std::addressof(this->fail_));
3584
(void)tmp;
3585
return *this;
3586
}
3587
3588
template<typename U>
3589
result& operator=(success<U> s)
3590
{
3591
this->cleanup();
3592
this->is_ok_ = true;
3593
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(s.value));
3594
assert(tmp == std::addressof(this->succ_));
3595
(void)tmp;
3596
return *this;
3597
}
3598
template<typename U>
3599
result& operator=(failure<U> f)
3600
{
3601
this->cleanup();
3602
this->is_ok_ = false;
3603
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(f.value));
3604
assert(tmp == std::addressof(this->fail_));
3605
(void)tmp;
3606
return *this;
3607
}
3608
3609
~result() noexcept {this->cleanup();}
3610
3611
result(const result& other): is_ok_(other.is_ok())
3612
{
3613
if(other.is_ok())
3614
{
3615
auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_);
3616
assert(tmp == std::addressof(this->succ_));
3617
(void)tmp;
3618
}
3619
else
3620
{
3621
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
3622
assert(tmp == std::addressof(this->fail_));
3623
(void)tmp;
3624
}
3625
}
3626
result(result&& other): is_ok_(other.is_ok())
3627
{
3628
if(other.is_ok())
3629
{
3630
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_));
3631
assert(tmp == std::addressof(this->succ_));
3632
(void)tmp;
3633
}
3634
else
3635
{
3636
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
3637
assert(tmp == std::addressof(this->fail_));
3638
(void)tmp;
3639
}
3640
}
3641
3642
result& operator=(const result& other)
3643
{
3644
this->cleanup();
3645
if(other.is_ok())
3646
{
3647
auto tmp = ::new(std::addressof(this->succ_)) success_type(other.succ_);
3648
assert(tmp == std::addressof(this->succ_));
3649
(void)tmp;
3650
}
3651
else
3652
{
3653
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
3654
assert(tmp == std::addressof(this->fail_));
3655
(void)tmp;
3656
}
3657
is_ok_ = other.is_ok();
3658
return *this;
3659
}
3660
result& operator=(result&& other)
3661
{
3662
this->cleanup();
3663
if(other.is_ok())
3664
{
3665
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.succ_));
3666
assert(tmp == std::addressof(this->succ_));
3667
(void)tmp;
3668
}
3669
else
3670
{
3671
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
3672
assert(tmp == std::addressof(this->fail_));
3673
(void)tmp;
3674
}
3675
is_ok_ = other.is_ok();
3676
return *this;
3677
}
3678
3679
template<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
3680
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
3681
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
3682
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
3683
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
3684
>::value, std::nullptr_t> = nullptr>
3685
result(result<U, F> other): is_ok_(other.is_ok())
3686
{
3687
if(other.is_ok())
3688
{
3689
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok()));
3690
assert(tmp == std::addressof(this->succ_));
3691
(void)tmp;
3692
}
3693
else
3694
{
3695
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err()));
3696
assert(tmp == std::addressof(this->fail_));
3697
(void)tmp;
3698
}
3699
}
3700
3701
template<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
3702
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
3703
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
3704
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
3705
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
3706
>::value, std::nullptr_t> = nullptr>
3707
result& operator=(result<U, F> other)
3708
{
3709
this->cleanup();
3710
if(other.is_ok())
3711
{
3712
auto tmp = ::new(std::addressof(this->succ_)) success_type(std::move(other.as_ok()));
3713
assert(tmp == std::addressof(this->succ_));
3714
(void)tmp;
3715
}
3716
else
3717
{
3718
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.as_err()));
3719
assert(tmp == std::addressof(this->fail_));
3720
(void)tmp;
3721
}
3722
is_ok_ = other.is_ok();
3723
return *this;
3724
}
3725
3726
bool is_ok() const noexcept {return is_ok_;}
3727
bool is_err() const noexcept {return !is_ok_;}
3728
3729
explicit operator bool() const noexcept {return is_ok_;}
3730
3731
value_type& unwrap(cxx::source_location loc = cxx::source_location::current())
3732
{
3733
if(this->is_err())
3734
{
3735
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
3736
}
3737
return this->succ_.get();
3738
}
3739
value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const
3740
{
3741
if(this->is_err())
3742
{
3743
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
3744
}
3745
return this->succ_.get();
3746
}
3747
3748
value_type& unwrap_or(value_type& opt) noexcept
3749
{
3750
if(this->is_err()) {return opt;}
3751
return this->succ_.get();
3752
}
3753
value_type const& unwrap_or(value_type const& opt) const noexcept
3754
{
3755
if(this->is_err()) {return opt;}
3756
return this->succ_.get();
3757
}
3758
3759
error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current())
3760
{
3761
if(this->is_ok())
3762
{
3763
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
3764
}
3765
return this->fail_.get();
3766
}
3767
error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const
3768
{
3769
if(this->is_ok())
3770
{
3771
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
3772
}
3773
return this->fail_.get();
3774
}
3775
3776
value_type& as_ok() noexcept
3777
{
3778
assert(this->is_ok());
3779
return this->succ_.get();
3780
}
3781
value_type const& as_ok() const noexcept
3782
{
3783
assert(this->is_ok());
3784
return this->succ_.get();
3785
}
3786
3787
error_type& as_err() noexcept
3788
{
3789
assert(this->is_err());
3790
return this->fail_.get();
3791
}
3792
error_type const& as_err() const noexcept
3793
{
3794
assert(this->is_err());
3795
return this->fail_.get();
3796
}
3797
3798
private:
3799
3800
void cleanup() noexcept
3801
{
3802
#if defined(__GNUC__) && ! defined(__clang__)
3803
#pragma GCC diagnostic push
3804
#pragma GCC diagnostic ignored "-Wduplicated-branches"
3805
#endif
3806
3807
if(this->is_ok_) {this->succ_.~success_type();}
3808
else {this->fail_.~failure_type();}
3809
3810
#if defined(__GNUC__) && ! defined(__clang__)
3811
#pragma GCC diagnostic pop
3812
#endif
3813
return;
3814
}
3815
3816
private:
3817
3818
bool is_ok_;
3819
union
3820
{
3821
success_type succ_;
3822
failure_type fail_;
3823
};
3824
};
3825
3826
// ----------------------------------------------------------------------------
3827
3828
namespace detail
3829
{
3830
struct none_t {};
3831
inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
3832
inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
3833
inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
3834
inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
3835
inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
3836
inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
3837
inline std::ostream& operator<<(std::ostream& os, const none_t&)
3838
{
3839
os << "none";
3840
return os;
3841
}
3842
} // detail
3843
3844
inline success<detail::none_t> ok() noexcept
3845
{
3846
return success<detail::none_t>(detail::none_t{});
3847
}
3848
inline failure<detail::none_t> err() noexcept
3849
{
3850
return failure<detail::none_t>(detail::none_t{});
3851
}
3852
3853
} // TOML11_INLINE_VERSION_NAMESPACE
3854
} // toml
3855
#endif // TOML11_RESULT_HPP
3856
#ifndef TOML11_UTILITY_HPP
3857
#define TOML11_UTILITY_HPP
3858
3859
3860
#include <array>
3861
#include <sstream>
3862
3863
#include <cassert>
3864
#include <cctype>
3865
#include <cstring>
3866
3867
namespace toml
3868
{
3869
inline namespace TOML11_INLINE_VERSION_NAMESPACE
3870
{
3871
namespace detail
3872
{
3873
3874
// to output character in an error message.
3875
inline std::string show_char(const int c)
3876
{
3877
using char_type = unsigned char;
3878
if(std::isgraph(c))
3879
{
3880
return std::string(1, static_cast<char>(c));
3881
}
3882
else
3883
{
3884
std::array<char, 5> buf;
3885
buf.fill('\0');
3886
const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF);
3887
assert(r == static_cast<int>(buf.size()) - 1);
3888
(void) r; // Unused variable warning
3889
auto in_hex = std::string(buf.data());
3890
switch(c)
3891
{
3892
case char_type('\0'): {in_hex += "(NUL)"; break;}
3893
case char_type(' ') : {in_hex += "(SPACE)"; break;}
3894
case char_type('\n'): {in_hex += "(LINE FEED)"; break;}
3895
case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;}
3896
case char_type('\t'): {in_hex += "(TAB)"; break;}
3897
case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;}
3898
case char_type('\f'): {in_hex += "(FORM FEED)"; break;}
3899
case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;}
3900
default: break;
3901
}
3902
return in_hex;
3903
}
3904
}
3905
3906
// ---------------------------------------------------------------------------
3907
3908
template<typename Container>
3909
void try_reserve_impl(Container& container, std::size_t N, std::true_type)
3910
{
3911
container.reserve(N);
3912
return;
3913
}
3914
template<typename Container>
3915
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
3916
{
3917
return;
3918
}
3919
3920
template<typename Container>
3921
void try_reserve(Container& container, std::size_t N)
3922
{
3923
try_reserve_impl(container, N, has_reserve_method<Container>{});
3924
return;
3925
}
3926
3927
// ---------------------------------------------------------------------------
3928
3929
template<typename T>
3930
result<T, none_t> from_string(const std::string& str)
3931
{
3932
T v;
3933
std::istringstream iss(str);
3934
iss >> v;
3935
if(iss.fail())
3936
{
3937
return err();
3938
}
3939
return ok(v);
3940
}
3941
3942
// ---------------------------------------------------------------------------
3943
3944
// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
3945
template<typename Iterator>
3946
std::string make_string(Iterator first, Iterator last)
3947
{
3948
if(first == last) {return "";}
3949
return std::string(first, last);
3950
}
3951
inline std::string make_string(std::size_t len, char c)
3952
{
3953
if(len == 0) {return "";}
3954
return std::string(len, c);
3955
}
3956
3957
// ---------------------------------------------------------------------------
3958
3959
template<typename Char, typename Traits, typename Alloc,
3960
typename Char2, typename Traits2, typename Alloc2>
3961
struct string_conv_impl
3962
{
3963
static_assert(sizeof(Char) == sizeof(char), "");
3964
static_assert(sizeof(Char2) == sizeof(char), "");
3965
3966
static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char2, Traits2, Alloc2> s)
3967
{
3968
std::basic_string<Char, Traits, Alloc> retval;
3969
std::transform(s.begin(), s.end(), std::back_inserter(retval),
3970
[](const Char2 c) {return static_cast<Char>(c);});
3971
return retval;
3972
}
3973
template<std::size_t N>
3974
static std::basic_string<Char, Traits, Alloc> invoke(const Char2 (&s)[N])
3975
{
3976
std::basic_string<Char, Traits, Alloc> retval;
3977
// "string literal" has null-char at the end. to skip it, we use prev.
3978
std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval),
3979
[](const Char2 c) {return static_cast<Char>(c);});
3980
return retval;
3981
}
3982
};
3983
3984
template<typename Char, typename Traits, typename Alloc>
3985
struct string_conv_impl<Char, Traits, Alloc, Char, Traits, Alloc>
3986
{
3987
static_assert(sizeof(Char) == sizeof(char), "");
3988
3989
static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char, Traits, Alloc> s)
3990
{
3991
return s;
3992
}
3993
template<std::size_t N>
3994
static std::basic_string<Char, Traits, Alloc> invoke(const Char (&s)[N])
3995
{
3996
return std::basic_string<Char, Traits, Alloc>(s);
3997
}
3998
};
3999
4000
template<typename S, typename Char2, typename Traits2, typename Alloc2>
4001
cxx::enable_if_t<is_std_basic_string<S>::value, S>
4002
string_conv(std::basic_string<Char2, Traits2, Alloc2> s)
4003
{
4004
using C = typename S::value_type;
4005
using T = typename S::traits_type;
4006
using A = typename S::allocator_type;
4007
return string_conv_impl<C, T, A, Char2, Traits2, Alloc2>::invoke(std::move(s));
4008
}
4009
template<typename S, std::size_t N>
4010
cxx::enable_if_t<is_std_basic_string<S>::value, S>
4011
string_conv(const char (&s)[N])
4012
{
4013
using C = typename S::value_type;
4014
using T = typename S::traits_type;
4015
using A = typename S::allocator_type;
4016
using C2 = char;
4017
using T2 = std::char_traits<C2>;
4018
using A2 = std::allocator<C2>;
4019
4020
return string_conv_impl<C, T, A, C2, T2, A2>::template invoke<N>(s);
4021
}
4022
4023
} // namespace detail
4024
} // TOML11_INLINE_VERSION_NAMESPACE
4025
} // namespace toml
4026
#endif // TOML11_UTILITY_HPP
4027
#ifndef TOML11_LOCATION_HPP
4028
#define TOML11_LOCATION_HPP
4029
4030
#ifndef TOML11_LOCATION_FWD_HPP
4031
#define TOML11_LOCATION_FWD_HPP
4032
4033
4034
#include <memory>
4035
#include <string>
4036
#include <vector>
4037
4038
namespace toml
4039
{
4040
inline namespace TOML11_INLINE_VERSION_NAMESPACE
4041
{
4042
namespace detail
4043
{
4044
4045
class region; // fwd decl
4046
4047
//
4048
// To represent where we are reading in the parse functions.
4049
// Since it "points" somewhere in the input stream, the length is always 1.
4050
//
4051
class location
4052
{
4053
public:
4054
4055
using char_type = unsigned char; // must be unsigned
4056
using container_type = std::vector<char_type>;
4057
using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning
4058
using source_ptr = std::shared_ptr<const container_type>;
4059
4060
public:
4061
4062
location(source_ptr src, std::string src_name)
4063
: source_(std::move(src)), source_name_(std::move(src_name)),
4064
location_(0), line_number_(1), column_number_(1)
4065
{}
4066
4067
location(const location&) = default;
4068
location(location&&) = default;
4069
location& operator=(const location&) = default;
4070
location& operator=(location&&) = default;
4071
~location() = default;
4072
4073
void advance(std::size_t n = 1) noexcept;
4074
void retrace() noexcept;
4075
4076
bool is_ok() const noexcept { return static_cast<bool>(this->source_); }
4077
4078
bool eof() const noexcept;
4079
char_type current() const;
4080
4081
char_type peek();
4082
4083
std::size_t get_location() const noexcept
4084
{
4085
return this->location_;
4086
}
4087
4088
std::size_t line_number() const noexcept
4089
{
4090
return this->line_number_;
4091
}
4092
std::size_t column_number() const noexcept
4093
{
4094
return this->column_number_;
4095
}
4096
std::string get_line() const;
4097
4098
source_ptr const& source() const noexcept {return this->source_;}
4099
std::string const& source_name() const noexcept {return this->source_name_;}
4100
4101
private:
4102
4103
void advance_impl(const std::size_t n);
4104
void retrace_impl();
4105
std::size_t calc_column_number() const noexcept;
4106
4107
private:
4108
4109
friend region;
4110
4111
private:
4112
4113
source_ptr source_;
4114
std::string source_name_;
4115
std::size_t location_; // std::vector<>::difference_type is signed
4116
std::size_t line_number_;
4117
std::size_t column_number_;
4118
};
4119
4120
bool operator==(const location& lhs, const location& rhs) noexcept;
4121
bool operator!=(const location& lhs, const location& rhs);
4122
4123
location prev(const location& loc);
4124
location next(const location& loc);
4125
location make_temporary_location(const std::string& str) noexcept;
4126
4127
template<typename F>
4128
result<location, none_t>
4129
find_if(const location& first, const location& last, const F& func) noexcept
4130
{
4131
if(first.source() != last.source()) { return err(); }
4132
if(first.get_location() >= last.get_location()) { return err(); }
4133
4134
auto loc = first;
4135
while(loc.get_location() != last.get_location())
4136
{
4137
if(func(loc.current()))
4138
{
4139
return ok(loc);
4140
}
4141
loc.advance();
4142
}
4143
return err();
4144
}
4145
4146
template<typename F>
4147
result<location, none_t>
4148
rfind_if(location first, const location& last, const F& func)
4149
{
4150
if(first.source() != last.source()) { return err(); }
4151
if(first.get_location() >= last.get_location()) { return err(); }
4152
4153
auto loc = last;
4154
while(loc.get_location() != first.get_location())
4155
{
4156
if(func(loc.current()))
4157
{
4158
return ok(loc);
4159
}
4160
loc.retrace();
4161
}
4162
if(func(first.current()))
4163
{
4164
return ok(first);
4165
}
4166
return err();
4167
}
4168
4169
result<location, none_t> find(const location& first, const location& last,
4170
const location::char_type val);
4171
result<location, none_t> rfind(const location& first, const location& last,
4172
const location::char_type val);
4173
4174
std::size_t count(const location& first, const location& last,
4175
const location::char_type& c);
4176
4177
} // detail
4178
} // TOML11_INLINE_VERSION_NAMESPACE
4179
} // toml
4180
#endif // TOML11_LOCATION_FWD_HPP
4181
4182
#if ! defined(TOML11_COMPILE_SOURCES)
4183
#ifndef TOML11_LOCATION_IMPL_HPP
4184
#define TOML11_LOCATION_IMPL_HPP
4185
4186
4187
namespace toml
4188
{
4189
inline namespace TOML11_INLINE_VERSION_NAMESPACE
4190
{
4191
namespace detail
4192
{
4193
4194
TOML11_INLINE void location::advance(std::size_t n) noexcept
4195
{
4196
assert(this->is_ok());
4197
if(this->location_ + n < this->source_->size())
4198
{
4199
this->advance_impl(n);
4200
}
4201
else
4202
{
4203
this->advance_impl(this->source_->size() - this->location_);
4204
4205
assert(this->location_ == this->source_->size());
4206
}
4207
}
4208
TOML11_INLINE void location::retrace(/*restricted to n=1*/) noexcept
4209
{
4210
assert(this->is_ok());
4211
if(this->location_ == 0)
4212
{
4213
this->location_ = 0;
4214
this->line_number_ = 1;
4215
this->column_number_ = 1;
4216
}
4217
else
4218
{
4219
this->retrace_impl();
4220
}
4221
}
4222
4223
TOML11_INLINE bool location::eof() const noexcept
4224
{
4225
assert(this->is_ok());
4226
return this->location_ >= this->source_->size();
4227
}
4228
TOML11_INLINE location::char_type location::current() const
4229
{
4230
assert(this->is_ok());
4231
if(this->eof()) {return '\0';}
4232
4233
assert(this->location_ < this->source_->size());
4234
return this->source_->at(this->location_);
4235
}
4236
4237
TOML11_INLINE location::char_type location::peek()
4238
{
4239
assert(this->is_ok());
4240
if(this->location_ >= this->source_->size())
4241
{
4242
return '\0';
4243
}
4244
else
4245
{
4246
return this->source_->at(this->location_ + 1);
4247
}
4248
}
4249
4250
TOML11_INLINE std::string location::get_line() const
4251
{
4252
assert(this->is_ok());
4253
const auto iter = std::next(this->source_->cbegin(), static_cast<difference_type>(this->location_));
4254
const auto riter = cxx::make_reverse_iterator(iter);
4255
4256
const auto prev = std::find(riter, this->source_->crend(), char_type('\n'));
4257
const auto next = std::find(iter, this->source_->cend(), char_type('\n'));
4258
4259
return make_string(std::next(prev.base()), next);
4260
}
4261
4262
TOML11_INLINE std::size_t location::calc_column_number() const noexcept
4263
{
4264
assert(this->is_ok());
4265
const auto iter = std::next(this->source_->cbegin(), static_cast<difference_type>(this->location_));
4266
const auto riter = cxx::make_reverse_iterator(iter);
4267
const auto prev = std::find(riter, this->source_->crend(), char_type('\n'));
4268
4269
assert(prev.base() <= iter);
4270
return static_cast<std::size_t>(std::distance(prev.base(), iter) + 1); // 1-origin
4271
}
4272
4273
TOML11_INLINE void location::advance_impl(const std::size_t n)
4274
{
4275
assert(this->is_ok());
4276
assert(this->location_ + n <= this->source_->size());
4277
4278
auto iter = this->source_->cbegin();
4279
std::advance(iter, static_cast<difference_type>(this->location_));
4280
4281
for(std::size_t i=0; i<n; ++i)
4282
{
4283
const auto c = *iter;
4284
if(c == char_type('\n'))
4285
{
4286
this->line_number_ += 1;
4287
this->column_number_ = 1;
4288
}
4289
else
4290
{
4291
this->column_number_ += 1;
4292
}
4293
iter++;
4294
}
4295
this->location_ += n;
4296
return;
4297
}
4298
TOML11_INLINE void location::retrace_impl(/*n == 1*/)
4299
{
4300
assert(this->is_ok());
4301
assert(this->location_ != 0);
4302
4303
this->location_ -= 1;
4304
4305
auto iter = this->source_->cbegin();
4306
std::advance(iter, static_cast<difference_type>(this->location_));
4307
if(*iter == '\n')
4308
{
4309
this->line_number_ -= 1;
4310
this->column_number_ = this->calc_column_number();
4311
}
4312
return;
4313
}
4314
4315
TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept
4316
{
4317
if( ! lhs.is_ok() || ! rhs.is_ok())
4318
{
4319
return (!lhs.is_ok()) && (!rhs.is_ok());
4320
}
4321
return lhs.source() == rhs.source() &&
4322
lhs.source_name() == rhs.source_name() &&
4323
lhs.get_location() == rhs.get_location();
4324
}
4325
TOML11_INLINE bool operator!=(const location& lhs, const location& rhs)
4326
{
4327
return !(lhs == rhs);
4328
}
4329
4330
TOML11_INLINE location prev(const location& loc)
4331
{
4332
location p(loc);
4333
p.retrace();
4334
return p;
4335
}
4336
TOML11_INLINE location next(const location& loc)
4337
{
4338
location p(loc);
4339
p.advance(1);
4340
return p;
4341
}
4342
4343
TOML11_INLINE location make_temporary_location(const std::string& str) noexcept
4344
{
4345
location::container_type cont(str.size());
4346
std::transform(str.begin(), str.end(), cont.begin(),
4347
[](const std::string::value_type& c) {
4348
return cxx::bit_cast<location::char_type>(c);
4349
});
4350
return location(std::make_shared<const location::container_type>(
4351
std::move(cont)), "internal temporary");
4352
}
4353
4354
TOML11_INLINE result<location, none_t>
4355
find(const location& first, const location& last, const location::char_type val)
4356
{
4357
return find_if(first, last, [val](const location::char_type c) {
4358
return c == val;
4359
});
4360
}
4361
TOML11_INLINE result<location, none_t>
4362
rfind(const location& first, const location& last, const location::char_type val)
4363
{
4364
return rfind_if(first, last, [val](const location::char_type c) {
4365
return c == val;
4366
});
4367
}
4368
4369
TOML11_INLINE std::size_t
4370
count(const location& first, const location& last, const location::char_type& c)
4371
{
4372
if(first.source() != last.source()) { return 0; }
4373
if(first.get_location() >= last.get_location()) { return 0; }
4374
4375
auto loc = first;
4376
std::size_t num = 0;
4377
while(loc.get_location() != last.get_location())
4378
{
4379
if(loc.current() == c)
4380
{
4381
num += 1;
4382
}
4383
loc.advance();
4384
}
4385
return num;
4386
}
4387
4388
} // detail
4389
} // TOML11_INLINE_VERSION_NAMESPACE
4390
} // toml
4391
#endif // TOML11_LOCATION_HPP
4392
#endif
4393
4394
#endif // TOML11_LOCATION_HPP
4395
#ifndef TOML11_REGION_HPP
4396
#define TOML11_REGION_HPP
4397
4398
#ifndef TOML11_REGION_FWD_HPP
4399
#define TOML11_REGION_FWD_HPP
4400
4401
4402
#include <string>
4403
#include <vector>
4404
4405
#include <cassert>
4406
4407
namespace toml
4408
{
4409
inline namespace TOML11_INLINE_VERSION_NAMESPACE
4410
{
4411
namespace detail
4412
{
4413
4414
//
4415
// To represent where is a toml::value defined, or where does an error occur.
4416
// Stored in toml::value. source_location will be constructed based on this.
4417
//
4418
class region
4419
{
4420
public:
4421
4422
using char_type = location::char_type;
4423
using container_type = location::container_type;
4424
using difference_type = location::difference_type;
4425
using source_ptr = location::source_ptr;
4426
4427
using iterator = typename container_type::iterator;
4428
using const_iterator = typename container_type::const_iterator;
4429
4430
public:
4431
4432
// a value that is constructed manually does not have input stream info
4433
region()
4434
: source_(nullptr), source_name_(""), length_(0),
4435
first_(0), first_line_(0), first_column_(0), last_(0), last_line_(0),
4436
last_column_(0)
4437
{}
4438
4439
// a value defined in [first, last).
4440
// Those source must be the same. Instread, `region` does not make sense.
4441
region(const location& first, const location& last);
4442
4443
// shorthand of [loc, loc+1)
4444
explicit region(const location& loc);
4445
4446
~region() = default;
4447
region(const region&) = default;
4448
region(region&&) = default;
4449
region& operator=(const region&) = default;
4450
region& operator=(region&&) = default;
4451
4452
bool is_ok() const noexcept { return static_cast<bool>(this->source_); }
4453
4454
operator bool() const noexcept { return this->is_ok(); }
4455
4456
std::size_t length() const noexcept {return this->length_;}
4457
4458
std::size_t first_line_number() const noexcept
4459
{
4460
return this->first_line_;
4461
}
4462
std::size_t first_column_number() const noexcept
4463
{
4464
return this->first_column_;
4465
}
4466
std::size_t last_line_number() const noexcept
4467
{
4468
return this->last_line_;
4469
}
4470
std::size_t last_column_number() const noexcept
4471
{
4472
return this->last_column_;
4473
}
4474
4475
char_type at(std::size_t i) const;
4476
4477
const_iterator begin() const noexcept;
4478
const_iterator end() const noexcept;
4479
const_iterator cbegin() const noexcept;
4480
const_iterator cend() const noexcept;
4481
4482
std::string as_string() const;
4483
std::vector<std::pair<std::string, std::size_t>> as_lines() const;
4484
4485
source_ptr const& source() const noexcept {return this->source_;}
4486
std::string const& source_name() const noexcept {return this->source_name_;}
4487
4488
private:
4489
4490
std::pair<std::string, std::size_t>
4491
take_line(const_iterator begin, const_iterator end) const;
4492
4493
private:
4494
4495
source_ptr source_;
4496
std::string source_name_;
4497
std::size_t length_;
4498
std::size_t first_;
4499
std::size_t first_line_;
4500
std::size_t first_column_;
4501
std::size_t last_;
4502
std::size_t last_line_;
4503
std::size_t last_column_;
4504
};
4505
4506
} // namespace detail
4507
} // TOML11_INLINE_VERSION_NAMESPACE
4508
} // namespace toml
4509
#endif // TOML11_REGION_FWD_HPP
4510
4511
#if ! defined(TOML11_COMPILE_SOURCES)
4512
#ifndef TOML11_REGION_IMPL_HPP
4513
#define TOML11_REGION_IMPL_HPP
4514
4515
4516
#include <algorithm>
4517
#include <iterator>
4518
#include <string>
4519
#include <sstream>
4520
#include <vector>
4521
#include <cassert>
4522
4523
namespace toml
4524
{
4525
inline namespace TOML11_INLINE_VERSION_NAMESPACE
4526
{
4527
namespace detail
4528
{
4529
4530
// a value defined in [first, last).
4531
// Those source must be the same. Instread, `region` does not make sense.
4532
TOML11_INLINE region::region(const location& first, const location& last)
4533
: source_(first.source()), source_name_(first.source_name()),
4534
length_(last.get_location() - first.get_location()),
4535
first_(first.get_location()),
4536
first_line_(first.line_number()),
4537
first_column_(first.column_number()),
4538
last_(last.get_location()),
4539
last_line_(last.line_number()),
4540
last_column_(last.column_number())
4541
{
4542
assert(first.source() == last.source());
4543
assert(first.source_name() == last.source_name());
4544
}
4545
4546
// shorthand of [loc, loc+1)
4547
TOML11_INLINE region::region(const location& loc)
4548
: source_(loc.source()), source_name_(loc.source_name()), length_(0),
4549
first_line_(0), first_column_(0), last_line_(0), last_column_(0)
4550
{
4551
// if the file ends with LF, the resulting region points no char.
4552
if(loc.eof())
4553
{
4554
if(loc.get_location() == 0)
4555
{
4556
this->length_ = 0;
4557
this->first_ = 0;
4558
this->first_line_ = 0;
4559
this->first_column_ = 0;
4560
this->last_ = 0;
4561
this->last_line_ = 0;
4562
this->last_column_ = 0;
4563
}
4564
else
4565
{
4566
const auto first = prev(loc);
4567
this->first_ = first.get_location();
4568
this->first_line_ = first.line_number();
4569
this->first_column_ = first.column_number();
4570
this->last_ = loc.get_location();
4571
this->last_line_ = loc.line_number();
4572
this->last_column_ = loc.column_number();
4573
this->length_ = 1;
4574
}
4575
}
4576
else
4577
{
4578
this->first_ = loc.get_location();
4579
this->first_line_ = loc.line_number();
4580
this->first_column_ = loc.column_number();
4581
this->last_ = loc.get_location() + 1;
4582
this->last_line_ = loc.line_number();
4583
this->last_column_ = loc.column_number() + 1;
4584
this->length_ = 1;
4585
}
4586
}
4587
4588
TOML11_INLINE region::char_type region::at(std::size_t i) const
4589
{
4590
if(this->last_ <= this->first_ + i)
4591
{
4592
throw std::out_of_range("range::at: index " + std::to_string(i) +
4593
" exceeds length " + std::to_string(this->length_));
4594
}
4595
const auto iter = std::next(this->source_->cbegin(),
4596
static_cast<difference_type>(this->first_ + i));
4597
return *iter;
4598
}
4599
4600
TOML11_INLINE region::const_iterator region::begin() const noexcept
4601
{
4602
return std::next(this->source_->cbegin(),
4603
static_cast<difference_type>(this->first_));
4604
}
4605
TOML11_INLINE region::const_iterator region::end() const noexcept
4606
{
4607
return std::next(this->source_->cbegin(),
4608
static_cast<difference_type>(this->last_));
4609
}
4610
TOML11_INLINE region::const_iterator region::cbegin() const noexcept
4611
{
4612
return std::next(this->source_->cbegin(),
4613
static_cast<difference_type>(this->first_));
4614
}
4615
TOML11_INLINE region::const_iterator region::cend() const noexcept
4616
{
4617
return std::next(this->source_->cbegin(),
4618
static_cast<difference_type>(this->last_));
4619
}
4620
4621
TOML11_INLINE std::string region::as_string() const
4622
{
4623
if(this->is_ok())
4624
{
4625
const auto begin = std::next(this->source_->cbegin(), static_cast<difference_type>(this->first_));
4626
const auto end = std::next(this->source_->cbegin(), static_cast<difference_type>(this->last_ ));
4627
return ::toml::detail::make_string(begin, end);
4628
}
4629
else
4630
{
4631
return std::string("");
4632
}
4633
}
4634
4635
TOML11_INLINE std::pair<std::string, std::size_t>
4636
region::take_line(const_iterator begin, const_iterator end) const
4637
{
4638
// To omit long line, we cap region by before/after 30 chars
4639
const auto dist_before = std::distance(source_->cbegin(), begin);
4640
const auto dist_after = std::distance(end, source_->cend());
4641
4642
const const_iterator capped_begin = (dist_before <= 30) ? source_->cbegin() : std::prev(begin, 30);
4643
const const_iterator capped_end = (dist_after <= 30) ? source_->cend() : std::next(end, 30);
4644
4645
const auto lf = char_type('\n');
4646
const auto lf_before = std::find(cxx::make_reverse_iterator(begin),
4647
cxx::make_reverse_iterator(capped_begin), lf);
4648
const auto lf_after = std::find(end, capped_end, lf);
4649
4650
auto offset = static_cast<std::size_t>(std::distance(lf_before.base(), begin));
4651
4652
std::string retval = make_string(lf_before.base(), lf_after);
4653
4654
if(lf_before.base() != source_->cbegin() && *lf_before != lf)
4655
{
4656
retval = "... " + retval;
4657
offset += 4;
4658
}
4659
4660
if(lf_after != source_->cend() && *lf_after != lf)
4661
{
4662
retval = retval + " ...";
4663
}
4664
4665
return std::make_pair(retval, offset);
4666
}
4667
4668
TOML11_INLINE std::vector<std::pair<std::string, std::size_t>> region::as_lines() const
4669
{
4670
assert(this->is_ok());
4671
if(this->length_ == 0)
4672
{
4673
return std::vector<std::pair<std::string, std::size_t>>{
4674
std::make_pair("", std::size_t(0))
4675
};
4676
}
4677
4678
// Consider the following toml file
4679
// ```
4680
// array = [
4681
// 1, 2, 3,
4682
// ] # comment
4683
// ```
4684
// and the region represnets
4685
// ```
4686
// [
4687
// 1, 2, 3,
4688
// ]
4689
// ```
4690
// but we want to show the following.
4691
// ```
4692
// array = [
4693
// 1, 2, 3,
4694
// ] # comment
4695
// ```
4696
// So we need to find LFs before `begin` and after `end`.
4697
//
4698
// But, if region ends with LF, it should not include the next line.
4699
// ```
4700
// a = 42
4701
// ^^^- with the last LF
4702
// ```
4703
// So we start from `end-1` when looking for LF.
4704
4705
const auto begin_idx = static_cast<difference_type>(this->first_);
4706
const auto end_idx = static_cast<difference_type>(this->last_) - 1;
4707
4708
// length_ != 0, so begin < end. then begin <= end-1
4709
assert(begin_idx <= end_idx);
4710
4711
const auto begin = std::next(this->source_->cbegin(), begin_idx);
4712
const auto end = std::next(this->source_->cbegin(), end_idx);
4713
4714
assert(this->first_line_number() <= this->last_line_number());
4715
4716
if(this->first_line_number() == this->last_line_number())
4717
{
4718
return std::vector<std::pair<std::string, std::size_t>>{
4719
this->take_line(begin, end)
4720
};
4721
}
4722
4723
// we have multiple lines. `begin` and `end` points different lines.
4724
// that means that there is at least one `LF` between `begin` and `end`.
4725
4726
const auto after_begin = std::distance(begin, this->source_->cend());
4727
const auto before_end = std::distance(this->source_->cbegin(), end);
4728
4729
const_iterator capped_file_end = this->source_->cend();
4730
const_iterator capped_file_begin = this->source_->cbegin();
4731
if(60 < after_begin) {capped_file_end = std::next(begin, 50);}
4732
if(60 < before_end) {capped_file_begin = std::prev(end, 50);}
4733
4734
const auto lf = char_type('\n');
4735
const auto first_line_end = std::find(begin, capped_file_end, lf);
4736
const auto last_line_begin = std::find(capped_file_begin, end, lf);
4737
4738
const auto first_line = this->take_line(begin, first_line_end);
4739
const auto last_line = this->take_line(last_line_begin, end);
4740
4741
if(this->first_line_number() + 1 == this->last_line_number())
4742
{
4743
return std::vector<std::pair<std::string, std::size_t>>{
4744
first_line, last_line
4745
};
4746
}
4747
else
4748
{
4749
return std::vector<std::pair<std::string, std::size_t>>{
4750
first_line, std::make_pair("...", 0), last_line
4751
};
4752
}
4753
}
4754
4755
} // namespace detail
4756
} // TOML11_INLINE_VERSION_NAMESPACE
4757
} // namespace toml
4758
#endif // TOML11_REGION_IMPL_HPP
4759
#endif
4760
4761
#endif // TOML11_REGION_HPP
4762
#ifndef TOML11_SCANNER_HPP
4763
#define TOML11_SCANNER_HPP
4764
4765
#ifndef TOML11_SCANNER_FWD_HPP
4766
#define TOML11_SCANNER_FWD_HPP
4767
4768
4769
#include <memory>
4770
#include <string>
4771
#include <utility>
4772
#include <vector>
4773
4774
#include <cassert>
4775
#include <cstdio>
4776
#include <cctype>
4777
4778
namespace toml
4779
{
4780
inline namespace TOML11_INLINE_VERSION_NAMESPACE
4781
{
4782
namespace detail
4783
{
4784
4785
class scanner_base
4786
{
4787
public:
4788
virtual ~scanner_base() = default;
4789
virtual region scan(location& loc) const = 0;
4790
virtual scanner_base* clone() const = 0;
4791
4792
// returns expected character or set of characters or literal.
4793
// to show the error location, it changes loc (in `sequence`, especially).
4794
virtual std::string expected_chars(location& loc) const = 0;
4795
virtual std::string name() const = 0;
4796
};
4797
4798
// make `scanner*` copyable
4799
struct scanner_storage
4800
{
4801
template<typename Scanner, cxx::enable_if_t<
4802
std::is_base_of<scanner_base, cxx::remove_cvref_t<Scanner>>::value,
4803
std::nullptr_t> = nullptr>
4804
explicit scanner_storage(Scanner&& s)
4805
: scanner_(cxx::make_unique<cxx::remove_cvref_t<Scanner>>(std::forward<Scanner>(s)))
4806
{}
4807
~scanner_storage() = default;
4808
4809
scanner_storage(const scanner_storage& other);
4810
scanner_storage& operator=(const scanner_storage& other);
4811
scanner_storage(scanner_storage&&) = default;
4812
scanner_storage& operator=(scanner_storage&&) = default;
4813
4814
bool is_ok() const noexcept {return static_cast<bool>(scanner_);}
4815
4816
region scan(location& loc) const;
4817
4818
std::string expected_chars(location& loc) const;
4819
4820
scanner_base& get() const noexcept;
4821
4822
std::string name() const;
4823
4824
private:
4825
4826
std::unique_ptr<scanner_base> scanner_;
4827
};
4828
4829
// ----------------------------------------------------------------------------
4830
4831
class character final : public scanner_base
4832
{
4833
public:
4834
4835
using char_type = location::char_type;
4836
4837
public:
4838
4839
explicit character(const char_type c) noexcept
4840
: value_(c)
4841
{}
4842
~character() override = default;
4843
4844
region scan(location& loc) const override;
4845
4846
std::string expected_chars(location&) const override;
4847
4848
scanner_base* clone() const override;
4849
4850
std::string name() const override;
4851
4852
private:
4853
char_type value_;
4854
};
4855
4856
// ----------------------------------------------------------------------------
4857
4858
class character_either final : public scanner_base
4859
{
4860
public:
4861
4862
using char_type = location::char_type;
4863
4864
public:
4865
4866
template<std::size_t N>
4867
explicit character_either(const char (&cs)[N]) noexcept
4868
: value_(cs), size_(N-1) // remove null character at the end
4869
{}
4870
~character_either() override = default;
4871
4872
region scan(location& loc) const override;
4873
4874
std::string expected_chars(location&) const override;
4875
4876
scanner_base* clone() const override;
4877
4878
std::string name() const override;
4879
4880
private:
4881
const char* value_;
4882
std::size_t size_;
4883
};
4884
4885
// ----------------------------------------------------------------------------
4886
4887
class character_in_range final : public scanner_base
4888
{
4889
public:
4890
4891
using char_type = location::char_type;
4892
4893
public:
4894
4895
explicit character_in_range(const char_type from, const char_type to) noexcept
4896
: from_(from), to_(to)
4897
{}
4898
~character_in_range() override = default;
4899
4900
region scan(location& loc) const override;
4901
4902
std::string expected_chars(location&) const override;
4903
4904
scanner_base* clone() const override;
4905
4906
std::string name() const override;
4907
4908
private:
4909
char_type from_;
4910
char_type to_;
4911
};
4912
4913
// ----------------------------------------------------------------------------
4914
4915
class literal final : public scanner_base
4916
{
4917
public:
4918
4919
using char_type = location::char_type;
4920
4921
public:
4922
4923
template<std::size_t N>
4924
explicit literal(const char (&cs)[N]) noexcept
4925
: value_(cs), size_(N-1) // remove null character at the end
4926
{}
4927
~literal() override = default;
4928
4929
region scan(location& loc) const override;
4930
4931
std::string expected_chars(location&) const override;
4932
4933
scanner_base* clone() const override;
4934
4935
std::string name() const override;
4936
4937
private:
4938
const char* value_;
4939
std::size_t size_;
4940
};
4941
4942
// ----------------------------------------------------------------------------
4943
4944
class sequence final: public scanner_base
4945
{
4946
public:
4947
using char_type = location::char_type;
4948
4949
public:
4950
4951
template<typename ... Ts>
4952
explicit sequence(Ts&& ... args)
4953
{
4954
push_back_all(std::forward<Ts>(args)...);
4955
}
4956
sequence(const sequence&) = default;
4957
sequence(sequence&&) = default;
4958
sequence& operator=(const sequence&) = default;
4959
sequence& operator=(sequence&&) = default;
4960
~sequence() override = default;
4961
4962
region scan(location& loc) const override;
4963
4964
std::string expected_chars(location& loc) const override;
4965
4966
scanner_base* clone() const override;
4967
4968
std::string name() const override;
4969
4970
private:
4971
4972
void push_back_all()
4973
{
4974
return;
4975
}
4976
template<typename T, typename ... Ts>
4977
void push_back_all(T&& head, Ts&& ... args)
4978
{
4979
others_.emplace_back(std::forward<T>(head));
4980
push_back_all(std::forward<Ts>(args)...);
4981
return;
4982
}
4983
4984
private:
4985
std::vector<scanner_storage> others_;
4986
};
4987
4988
// ----------------------------------------------------------------------------
4989
4990
class either final: public scanner_base
4991
{
4992
public:
4993
using char_type = location::char_type;
4994
4995
public:
4996
4997
template<typename ... Ts>
4998
explicit either(Ts&& ... args)
4999
{
5000
push_back_all(std::forward<Ts>(args)...);
5001
}
5002
either(const either&) = default;
5003
either(either&&) = default;
5004
either& operator=(const either&) = default;
5005
either& operator=(either&&) = default;
5006
~either() override = default;
5007
5008
region scan(location& loc) const override;
5009
5010
std::string expected_chars(location& loc) const override;
5011
5012
scanner_base* clone() const override;
5013
5014
std::string name() const override;
5015
5016
private:
5017
5018
void push_back_all()
5019
{
5020
return;
5021
}
5022
template<typename T, typename ... Ts>
5023
void push_back_all(T&& head, Ts&& ... args)
5024
{
5025
others_.emplace_back(std::forward<T>(head));
5026
push_back_all(std::forward<Ts>(args)...);
5027
return;
5028
}
5029
5030
private:
5031
std::vector<scanner_storage> others_;
5032
};
5033
5034
// ----------------------------------------------------------------------------
5035
5036
class repeat_exact final: public scanner_base
5037
{
5038
public:
5039
using char_type = location::char_type;
5040
5041
public:
5042
5043
template<typename Scanner>
5044
repeat_exact(const std::size_t length, Scanner&& other)
5045
: length_(length), other_(std::forward<Scanner>(other))
5046
{}
5047
repeat_exact(const repeat_exact&) = default;
5048
repeat_exact(repeat_exact&&) = default;
5049
repeat_exact& operator=(const repeat_exact&) = default;
5050
repeat_exact& operator=(repeat_exact&&) = default;
5051
~repeat_exact() override = default;
5052
5053
region scan(location& loc) const override;
5054
5055
std::string expected_chars(location& loc) const override;
5056
5057
scanner_base* clone() const override;
5058
5059
std::string name() const override;
5060
5061
private:
5062
std::size_t length_;
5063
scanner_storage other_;
5064
};
5065
5066
// ----------------------------------------------------------------------------
5067
5068
class repeat_at_least final: public scanner_base
5069
{
5070
public:
5071
using char_type = location::char_type;
5072
5073
public:
5074
5075
template<typename Scanner>
5076
repeat_at_least(const std::size_t length, Scanner&& s)
5077
: length_(length), other_(std::forward<Scanner>(s))
5078
{}
5079
repeat_at_least(const repeat_at_least&) = default;
5080
repeat_at_least(repeat_at_least&&) = default;
5081
repeat_at_least& operator=(const repeat_at_least&) = default;
5082
repeat_at_least& operator=(repeat_at_least&&) = default;
5083
~repeat_at_least() override = default;
5084
5085
region scan(location& loc) const override;
5086
5087
std::string expected_chars(location& loc) const override;
5088
5089
scanner_base* clone() const override;
5090
5091
std::string name() const override;
5092
5093
private:
5094
std::size_t length_;
5095
scanner_storage other_;
5096
};
5097
5098
// ----------------------------------------------------------------------------
5099
5100
class maybe final: public scanner_base
5101
{
5102
public:
5103
using char_type = location::char_type;
5104
5105
public:
5106
5107
template<typename Scanner>
5108
explicit maybe(Scanner&& s)
5109
: other_(std::forward<Scanner>(s))
5110
{}
5111
maybe(const maybe&) = default;
5112
maybe(maybe&&) = default;
5113
maybe& operator=(const maybe&) = default;
5114
maybe& operator=(maybe&&) = default;
5115
~maybe() override = default;
5116
5117
region scan(location& loc) const override;
5118
5119
std::string expected_chars(location&) const override;
5120
5121
scanner_base* clone() const override;
5122
5123
std::string name() const override;
5124
5125
private:
5126
scanner_storage other_;
5127
};
5128
5129
} // detail
5130
} // TOML11_INLINE_VERSION_NAMESPACE
5131
} // toml
5132
#endif // TOML11_SCANNER_FWD_HPP
5133
5134
#if ! defined(TOML11_COMPILE_SOURCES)
5135
#ifndef TOML11_SCANNER_IMPL_HPP
5136
#define TOML11_SCANNER_IMPL_HPP
5137
5138
5139
namespace toml
5140
{
5141
inline namespace TOML11_INLINE_VERSION_NAMESPACE
5142
{
5143
namespace detail
5144
{
5145
5146
TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other)
5147
: scanner_(nullptr)
5148
{
5149
if(other.is_ok())
5150
{
5151
scanner_.reset(other.get().clone());
5152
}
5153
}
5154
TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other)
5155
{
5156
if(this == std::addressof(other)) {return *this;}
5157
if(other.is_ok())
5158
{
5159
scanner_.reset(other.get().clone());
5160
}
5161
return *this;
5162
}
5163
5164
TOML11_INLINE region scanner_storage::scan(location& loc) const
5165
{
5166
assert(this->is_ok());
5167
return this->scanner_->scan(loc);
5168
}
5169
5170
TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const
5171
{
5172
assert(this->is_ok());
5173
return this->scanner_->expected_chars(loc);
5174
}
5175
5176
TOML11_INLINE scanner_base& scanner_storage::get() const noexcept
5177
{
5178
assert(this->is_ok());
5179
return *scanner_;
5180
}
5181
5182
TOML11_INLINE std::string scanner_storage::name() const
5183
{
5184
assert(this->is_ok());
5185
return this->scanner_->name();
5186
}
5187
5188
// ----------------------------------------------------------------------------
5189
5190
TOML11_INLINE region character::scan(location& loc) const
5191
{
5192
if(loc.eof()) {return region{};}
5193
5194
if(loc.current() == this->value_)
5195
{
5196
const auto first = loc;
5197
loc.advance(1);
5198
return region(first, loc);
5199
}
5200
return region{};
5201
}
5202
5203
TOML11_INLINE std::string character::expected_chars(location&) const
5204
{
5205
return show_char(value_);
5206
}
5207
5208
TOML11_INLINE scanner_base* character::clone() const
5209
{
5210
return new character(*this);
5211
}
5212
5213
TOML11_INLINE std::string character::name() const
5214
{
5215
return "character{" + show_char(value_) + "}";
5216
}
5217
5218
// ----------------------------------------------------------------------------
5219
5220
TOML11_INLINE region character_either::scan(location& loc) const
5221
{
5222
if(loc.eof()) {return region{};}
5223
5224
for(std::size_t i=0; i<this->size_; ++i)
5225
{
5226
const auto c = char_type(this->value_[i]);
5227
if(loc.current() == c)
5228
{
5229
const auto first = loc;
5230
loc.advance(1);
5231
return region(first, loc);
5232
}
5233
}
5234
return region{};
5235
}
5236
5237
TOML11_INLINE std::string character_either::expected_chars(location&) const
5238
{
5239
assert( this->value_ );
5240
assert( this->size_ != 0 );
5241
5242
std::string expected;
5243
if(this->size_ == 1)
5244
{
5245
expected += show_char(char_type(value_[0]));
5246
}
5247
else if(this->size_ == 2)
5248
{
5249
expected += show_char(char_type(value_[0])) + " or " +
5250
show_char(char_type(value_[1]));
5251
}
5252
else
5253
{
5254
for(std::size_t i=0; i<this->size_; ++i)
5255
{
5256
if(i != 0)
5257
{
5258
expected += ", ";
5259
}
5260
if(i + 1 == this->size_)
5261
{
5262
expected += "or ";
5263
}
5264
expected += show_char(char_type(value_[i]));
5265
}
5266
}
5267
return expected;
5268
}
5269
5270
TOML11_INLINE scanner_base* character_either::clone() const
5271
{
5272
return new character_either(*this);
5273
}
5274
5275
TOML11_INLINE std::string character_either::name() const
5276
{
5277
std::string n("character_either{");
5278
for(std::size_t i=0; i<this->size_; ++i)
5279
{
5280
const auto c = char_type(this->value_[i]);
5281
n += show_char(c);
5282
n += ", ";
5283
}
5284
if(this->size_ != 0)
5285
{
5286
n.pop_back();
5287
n.pop_back();
5288
}
5289
n += "}";
5290
return n;
5291
}
5292
5293
// ----------------------------------------------------------------------------
5294
// character_in_range
5295
5296
TOML11_INLINE region character_in_range::scan(location& loc) const
5297
{
5298
if(loc.eof()) {return region{};}
5299
5300
const auto curr = loc.current();
5301
if(this->from_ <= curr && curr <= this->to_)
5302
{
5303
const auto first = loc;
5304
loc.advance(1);
5305
return region(first, loc);
5306
}
5307
return region{};
5308
}
5309
5310
TOML11_INLINE std::string character_in_range::expected_chars(location&) const
5311
{
5312
std::string expected("from `");
5313
expected += show_char(from_);
5314
expected += "` to `";
5315
expected += show_char(to_);
5316
expected += "`";
5317
return expected;
5318
}
5319
5320
TOML11_INLINE scanner_base* character_in_range::clone() const
5321
{
5322
return new character_in_range(*this);
5323
}
5324
5325
TOML11_INLINE std::string character_in_range::name() const
5326
{
5327
return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}";
5328
}
5329
5330
// ----------------------------------------------------------------------------
5331
// literal
5332
5333
TOML11_INLINE region literal::scan(location& loc) const
5334
{
5335
const auto first = loc;
5336
for(std::size_t i=0; i<size_; ++i)
5337
{
5338
if(loc.eof() || char_type(value_[i]) != loc.current())
5339
{
5340
loc = first;
5341
return region{};
5342
}
5343
loc.advance(1);
5344
}
5345
return region(first, loc);
5346
}
5347
5348
TOML11_INLINE std::string literal::expected_chars(location&) const
5349
{
5350
return std::string(value_);
5351
}
5352
5353
TOML11_INLINE scanner_base* literal::clone() const
5354
{
5355
return new literal(*this);
5356
}
5357
5358
TOML11_INLINE std::string literal::name() const
5359
{
5360
return std::string("literal{") + std::string(value_, size_) + "}";
5361
}
5362
5363
// ----------------------------------------------------------------------------
5364
// sequence
5365
5366
TOML11_INLINE region sequence::scan(location& loc) const
5367
{
5368
const auto first = loc;
5369
for(const auto& other : others_)
5370
{
5371
const auto reg = other.scan(loc);
5372
if( ! reg.is_ok())
5373
{
5374
loc = first;
5375
return region{};
5376
}
5377
}
5378
return region(first, loc);
5379
}
5380
5381
TOML11_INLINE std::string sequence::expected_chars(location& loc) const
5382
{
5383
const auto first = loc;
5384
for(const auto& other : others_)
5385
{
5386
const auto reg = other.scan(loc);
5387
if( ! reg.is_ok())
5388
{
5389
return other.expected_chars(loc);
5390
}
5391
}
5392
assert(false);
5393
return ""; // XXX
5394
}
5395
5396
TOML11_INLINE scanner_base* sequence::clone() const
5397
{
5398
return new sequence(*this);
5399
}
5400
5401
TOML11_INLINE std::string sequence::name() const
5402
{
5403
std::string n("sequence{");
5404
for(const auto& other : others_)
5405
{
5406
n += other.name();
5407
n += ", ";
5408
}
5409
if( ! this->others_.empty())
5410
{
5411
n.pop_back();
5412
n.pop_back();
5413
}
5414
n += "}";
5415
return n;
5416
}
5417
5418
// ----------------------------------------------------------------------------
5419
// either
5420
5421
TOML11_INLINE region either::scan(location& loc) const
5422
{
5423
for(const auto& other : others_)
5424
{
5425
const auto reg = other.scan(loc);
5426
if(reg.is_ok())
5427
{
5428
return reg;
5429
}
5430
}
5431
return region{};
5432
}
5433
5434
TOML11_INLINE std::string either::expected_chars(location& loc) const
5435
{
5436
assert( ! others_.empty());
5437
5438
std::string expected = others_.at(0).expected_chars(loc);
5439
if(others_.size() == 2)
5440
{
5441
expected += " or ";
5442
expected += others_.at(1).expected_chars(loc);
5443
}
5444
else
5445
{
5446
for(std::size_t i=1; i<others_.size(); ++i)
5447
{
5448
expected += ", ";
5449
if(i + 1 == others_.size())
5450
{
5451
expected += "or ";
5452
}
5453
expected += others_.at(i).expected_chars(loc);
5454
}
5455
}
5456
return expected;
5457
}
5458
5459
TOML11_INLINE scanner_base* either::clone() const
5460
{
5461
return new either(*this);
5462
}
5463
5464
TOML11_INLINE std::string either::name() const
5465
{
5466
std::string n("either{");
5467
for(const auto& other : others_)
5468
{
5469
n += other.name();
5470
n += ", ";
5471
}
5472
if( ! this->others_.empty())
5473
{
5474
n.pop_back();
5475
n.pop_back();
5476
}
5477
n += "}";
5478
return n;
5479
}
5480
5481
// ----------------------------------------------------------------------------
5482
// repeat_exact
5483
5484
TOML11_INLINE region repeat_exact::scan(location& loc) const
5485
{
5486
const auto first = loc;
5487
for(std::size_t i=0; i<length_; ++i)
5488
{
5489
const auto reg = other_.scan(loc);
5490
if( ! reg.is_ok())
5491
{
5492
loc = first;
5493
return region{};
5494
}
5495
}
5496
return region(first, loc);
5497
}
5498
5499
TOML11_INLINE std::string repeat_exact::expected_chars(location& loc) const
5500
{
5501
for(std::size_t i=0; i<length_; ++i)
5502
{
5503
const auto reg = other_.scan(loc);
5504
if( ! reg.is_ok())
5505
{
5506
return other_.expected_chars(loc);
5507
}
5508
}
5509
assert(false);
5510
return "";
5511
}
5512
5513
TOML11_INLINE scanner_base* repeat_exact::clone() const
5514
{
5515
return new repeat_exact(*this);
5516
}
5517
5518
TOML11_INLINE std::string repeat_exact::name() const
5519
{
5520
return "repeat_exact{" + std::to_string(length_) + ", " + other_.name() + "}";
5521
}
5522
5523
// ----------------------------------------------------------------------------
5524
// repeat_at_least
5525
5526
TOML11_INLINE region repeat_at_least::scan(location& loc) const
5527
{
5528
const auto first = loc;
5529
for(std::size_t i=0; i<length_; ++i)
5530
{
5531
const auto reg = other_.scan(loc);
5532
if( ! reg.is_ok())
5533
{
5534
loc = first;
5535
return region{};
5536
}
5537
}
5538
while( ! loc.eof())
5539
{
5540
const auto checkpoint = loc;
5541
const auto reg = other_.scan(loc);
5542
if( ! reg.is_ok())
5543
{
5544
loc = checkpoint;
5545
return region(first, loc);
5546
}
5547
}
5548
return region(first, loc);
5549
}
5550
5551
TOML11_INLINE std::string repeat_at_least::expected_chars(location& loc) const
5552
{
5553
for(std::size_t i=0; i<length_; ++i)
5554
{
5555
const auto reg = other_.scan(loc);
5556
if( ! reg.is_ok())
5557
{
5558
return other_.expected_chars(loc);
5559
}
5560
}
5561
assert(false);
5562
return "";
5563
}
5564
5565
TOML11_INLINE scanner_base* repeat_at_least::clone() const
5566
{
5567
return new repeat_at_least(*this);
5568
}
5569
5570
TOML11_INLINE std::string repeat_at_least::name() const
5571
{
5572
return "repeat_at_least{" + std::to_string(length_) + ", " + other_.name() + "}";
5573
}
5574
5575
// ----------------------------------------------------------------------------
5576
// maybe
5577
5578
TOML11_INLINE region maybe::scan(location& loc) const
5579
{
5580
const auto first = loc;
5581
const auto reg = other_.scan(loc);
5582
if( ! reg.is_ok())
5583
{
5584
loc = first;
5585
}
5586
return region(first, loc);
5587
}
5588
5589
TOML11_INLINE std::string maybe::expected_chars(location&) const
5590
{
5591
return "";
5592
}
5593
5594
TOML11_INLINE scanner_base* maybe::clone() const
5595
{
5596
return new maybe(*this);
5597
}
5598
5599
TOML11_INLINE std::string maybe::name() const
5600
{
5601
return "maybe{" + other_.name() + "}";
5602
}
5603
5604
} // detail
5605
} // TOML11_INLINE_VERSION_NAMESPACE
5606
} // toml
5607
#endif // TOML11_SCANNER_IMPL_HPP
5608
#endif
5609
5610
#endif // TOML11_SCANNER_HPP
5611
#ifndef TOML11_SYNTAX_HPP
5612
#define TOML11_SYNTAX_HPP
5613
5614
#ifndef TOML11_SYNTAX_FWD_HPP
5615
#define TOML11_SYNTAX_FWD_HPP
5616
5617
5618
namespace toml
5619
{
5620
inline namespace TOML11_INLINE_VERSION_NAMESPACE
5621
{
5622
namespace detail
5623
{
5624
namespace syntax
5625
{
5626
5627
using char_type = location::char_type;
5628
5629
// ===========================================================================
5630
// UTF-8
5631
5632
// avoid redundant representation and out-of-unicode sequence
5633
5634
character_in_range const& utf8_1byte (const spec&);
5635
sequence const& utf8_2bytes(const spec&);
5636
sequence const& utf8_3bytes(const spec&);
5637
sequence const& utf8_4bytes(const spec&);
5638
5639
class non_ascii final : public scanner_base
5640
{
5641
public:
5642
5643
using char_type = location::char_type;
5644
5645
public:
5646
5647
explicit non_ascii(const spec& s) noexcept
5648
: utf8_2B_(utf8_2bytes(s)),
5649
utf8_3B_(utf8_3bytes(s)),
5650
utf8_4B_(utf8_4bytes(s))
5651
{}
5652
~non_ascii() override = default;
5653
5654
region scan(location& loc) const override
5655
{
5656
{
5657
const auto reg = utf8_2B_.scan(loc);
5658
if(reg.is_ok()) {return reg;}
5659
}
5660
{
5661
const auto reg = utf8_3B_.scan(loc);
5662
if(reg.is_ok()) {return reg;}
5663
}
5664
{
5665
const auto reg = utf8_4B_.scan(loc);
5666
if(reg.is_ok()) {return reg;}
5667
}
5668
return region{};
5669
}
5670
5671
std::string expected_chars(location&) const override
5672
{
5673
return "non-ascii utf-8 bytes";
5674
}
5675
5676
scanner_base* clone() const override
5677
{
5678
return new non_ascii(*this);
5679
}
5680
5681
std::string name() const override
5682
{
5683
return "non_ascii";
5684
}
5685
5686
private:
5687
sequence utf8_2B_;
5688
sequence utf8_3B_;
5689
sequence utf8_4B_;
5690
};
5691
5692
// ===========================================================================
5693
// Whitespace
5694
5695
character_either const& wschar(const spec&);
5696
5697
repeat_at_least const& ws(const spec& s);
5698
5699
// ===========================================================================
5700
// Newline
5701
5702
either const& newline(const spec&);
5703
5704
// ===========================================================================
5705
// Comments
5706
5707
either const& allowed_comment_char(const spec& s);
5708
5709
// XXX Note that it does not take newline
5710
sequence const& comment(const spec& s);
5711
5712
// ===========================================================================
5713
// Boolean
5714
5715
either const& boolean(const spec&);
5716
5717
// ===========================================================================
5718
// Integer
5719
5720
class digit final : public scanner_base
5721
{
5722
public:
5723
5724
using char_type = location::char_type;
5725
5726
public:
5727
5728
explicit digit(const spec&) noexcept
5729
: scanner_(char_type('0'), char_type('9'))
5730
{}
5731
5732
5733
~digit() override = default;
5734
5735
region scan(location& loc) const override
5736
{
5737
return scanner_.scan(loc);
5738
}
5739
5740
std::string expected_chars(location&) const override
5741
{
5742
return "digit [0-9]";
5743
}
5744
5745
scanner_base* clone() const override
5746
{
5747
return new digit(*this);
5748
}
5749
5750
std::string name() const override
5751
{
5752
return "digit";
5753
}
5754
5755
private:
5756
5757
character_in_range scanner_;
5758
};
5759
5760
class alpha final : public scanner_base
5761
{
5762
public:
5763
5764
using char_type = location::char_type;
5765
5766
public:
5767
5768
explicit alpha(const spec&) noexcept
5769
: lowercase_(char_type('a'), char_type('z')),
5770
uppercase_(char_type('A'), char_type('Z'))
5771
{}
5772
~alpha() override = default;
5773
5774
region scan(location& loc) const override
5775
{
5776
{
5777
const auto reg = lowercase_.scan(loc);
5778
if(reg.is_ok()) {return reg;}
5779
}
5780
{
5781
const auto reg = uppercase_.scan(loc);
5782
if(reg.is_ok()) {return reg;}
5783
}
5784
return region{};
5785
}
5786
5787
std::string expected_chars(location&) const override
5788
{
5789
return "alpha [a-zA-Z]";
5790
}
5791
5792
scanner_base* clone() const override
5793
{
5794
return new alpha(*this);
5795
}
5796
5797
std::string name() const override
5798
{
5799
return "alpha";
5800
}
5801
5802
private:
5803
5804
character_in_range lowercase_;
5805
character_in_range uppercase_;
5806
};
5807
5808
class hexdig final : public scanner_base
5809
{
5810
public:
5811
5812
using char_type = location::char_type;
5813
5814
public:
5815
5816
explicit hexdig(const spec& s) noexcept
5817
: digit_(s),
5818
lowercase_(char_type('a'), char_type('f')),
5819
uppercase_(char_type('A'), char_type('F'))
5820
{}
5821
~hexdig() override = default;
5822
5823
region scan(location& loc) const override
5824
{
5825
{
5826
const auto reg = digit_.scan(loc);
5827
if(reg.is_ok()) {return reg;}
5828
}
5829
{
5830
const auto reg = lowercase_.scan(loc);
5831
if(reg.is_ok()) {return reg;}
5832
}
5833
{
5834
const auto reg = uppercase_.scan(loc);
5835
if(reg.is_ok()) {return reg;}
5836
}
5837
return region{};
5838
}
5839
5840
std::string expected_chars(location&) const override
5841
{
5842
return "hex [0-9a-fA-F]";
5843
}
5844
5845
scanner_base* clone() const override
5846
{
5847
return new hexdig(*this);
5848
}
5849
5850
std::string name() const override
5851
{
5852
return "hexdig";
5853
}
5854
5855
private:
5856
5857
digit digit_;
5858
character_in_range lowercase_;
5859
character_in_range uppercase_;
5860
};
5861
5862
sequence const& num_suffix(const spec& s);
5863
5864
sequence const& dec_int(const spec& s);
5865
sequence const& hex_int(const spec& s);
5866
sequence const& oct_int(const spec&);
5867
sequence const& bin_int(const spec&);
5868
either const& integer(const spec& s);
5869
5870
// ===========================================================================
5871
// Floating
5872
5873
sequence const& zero_prefixable_int(const spec& s);
5874
sequence const& fractional_part(const spec& s);
5875
sequence const& exponent_part(const spec& s);
5876
sequence const& hex_floating(const spec& s);
5877
either const& floating(const spec& s);
5878
5879
// ===========================================================================
5880
// Datetime
5881
5882
sequence const& local_date(const spec& s);
5883
sequence const& local_time(const spec& s);
5884
either const& time_offset(const spec& s);
5885
sequence const& full_time(const spec& s);
5886
character_either const& time_delim(const spec&);
5887
sequence const& local_datetime(const spec& s);
5888
sequence const& offset_datetime(const spec& s);
5889
5890
// ===========================================================================
5891
// String
5892
5893
sequence const& escaped_x2(const spec& s);
5894
sequence const& escaped_u4(const spec& s);
5895
sequence const& escaped_U8(const spec& s);
5896
5897
sequence const& escaped (const spec& s);
5898
either const& basic_char (const spec& s);
5899
sequence const& basic_string(const spec& s);
5900
5901
// ---------------------------------------------------------------------------
5902
// multiline string
5903
5904
sequence const& escaped_newline(const spec& s);
5905
sequence const& ml_basic_string(const spec& s);
5906
5907
// ---------------------------------------------------------------------------
5908
// literal string
5909
5910
either const& literal_char(const spec& s);
5911
sequence const& literal_string(const spec& s);
5912
sequence const& ml_literal_string(const spec& s);
5913
either const& string(const spec& s);
5914
5915
// ===========================================================================
5916
// Keys
5917
5918
// to keep `expected_chars` simple
5919
class non_ascii_key_char final : public scanner_base
5920
{
5921
public:
5922
5923
using char_type = location::char_type;
5924
5925
private:
5926
5927
using in_range = character_in_range; // make definition short
5928
5929
public:
5930
5931
explicit non_ascii_key_char(const spec& s) noexcept;
5932
~non_ascii_key_char() override = default;
5933
5934
region scan(location& loc) const override;
5935
5936
std::string expected_chars(location&) const override
5937
{
5938
return "bare key non-ASCII script";
5939
}
5940
5941
scanner_base* clone() const override
5942
{
5943
return new non_ascii_key_char(*this);
5944
}
5945
5946
std::string name() const override
5947
{
5948
return "non-ASCII bare key";
5949
}
5950
5951
private:
5952
5953
std::uint32_t read_utf8(location& loc) const;
5954
};
5955
5956
5957
repeat_at_least const& unquoted_key(const spec& s);
5958
either const& quoted_key(const spec& s);
5959
either const& simple_key(const spec& s);
5960
sequence const& dot_sep(const spec& s);
5961
sequence const& dotted_key(const spec& s);
5962
5963
class key final : public scanner_base
5964
{
5965
public:
5966
5967
using char_type = location::char_type;
5968
5969
public:
5970
5971
explicit key(const spec& s) noexcept
5972
: dotted_(dotted_key(s)),
5973
simple_(simple_key(s))
5974
{}
5975
~key() override = default;
5976
5977
region scan(location& loc) const override
5978
{
5979
{
5980
const auto reg = dotted_.scan(loc);
5981
if(reg.is_ok()) {return reg;}
5982
}
5983
{
5984
const auto reg = simple_.scan(loc);
5985
if(reg.is_ok()) {return reg;}
5986
}
5987
return region{};
5988
}
5989
5990
std::string expected_chars(location&) const override
5991
{
5992
return "basic key([a-zA-Z0-9_-]) or quoted key(\" or ')";
5993
}
5994
5995
scanner_base* clone() const override
5996
{
5997
return new key(*this);
5998
}
5999
6000
std::string name() const override
6001
{
6002
return "key";
6003
}
6004
6005
private:
6006
6007
sequence dotted_;
6008
either simple_;
6009
};
6010
6011
sequence const& keyval_sep(const spec& s);
6012
6013
// ===========================================================================
6014
// Table key
6015
6016
sequence const& std_table(const spec& s);
6017
6018
sequence const& array_table(const spec& s);
6019
6020
// ===========================================================================
6021
// extension: null
6022
6023
literal const& null_value(const spec&);
6024
6025
} // namespace syntax
6026
} // namespace detail
6027
} // TOML11_INLINE_VERSION_NAMESPACE
6028
} // namespace toml
6029
#endif // TOML11_SYNTAX_FWD_HPP
6030
6031
#if ! defined(TOML11_COMPILE_SOURCES)
6032
#ifndef TOML11_SYNTAX_IMPL_HPP
6033
#define TOML11_SYNTAX_IMPL_HPP
6034
6035
6036
namespace toml
6037
{
6038
inline namespace TOML11_INLINE_VERSION_NAMESPACE
6039
{
6040
namespace detail
6041
{
6042
namespace syntax
6043
{
6044
6045
using char_type = location::char_type;
6046
6047
template<typename F>
6048
struct syntax_cache
6049
{
6050
using value_type = cxx::return_type_of_t<F, const spec&>;
6051
static_assert(std::is_base_of<scanner_base, value_type>::value, "");
6052
6053
explicit syntax_cache(F f)
6054
: func_(std::move(f)), cache_(cxx::make_nullopt())
6055
{}
6056
6057
value_type const& at(const spec& s)
6058
{
6059
if( ! this->cache_.has_value() || this->cache_.value().first != s)
6060
{
6061
this->cache_ = std::make_pair(s, func_(s));
6062
}
6063
return this->cache_.value().second;
6064
}
6065
6066
private:
6067
F func_;
6068
cxx::optional<std::pair<spec, value_type>> cache_;
6069
};
6070
6071
template<typename F>
6072
syntax_cache<cxx::remove_cvref_t<F>> make_cache(F&& f)
6073
{
6074
return syntax_cache<cxx::remove_cvref_t<F>>(std::forward<F>(f));
6075
}
6076
6077
// ===========================================================================
6078
// UTF-8
6079
6080
// avoid redundant representation and out-of-unicode sequence
6081
6082
TOML11_INLINE character_in_range const& utf8_1byte(const spec&)
6083
{
6084
static thread_local character_in_range cache(0x00, 0x7F);
6085
return cache;
6086
}
6087
6088
TOML11_INLINE sequence const& utf8_2bytes(const spec&)
6089
{
6090
static thread_local sequence cache(
6091
character_in_range(0xC2, 0xDF),
6092
character_in_range(0x80, 0xBF));
6093
return cache;
6094
}
6095
6096
TOML11_INLINE sequence const& utf8_3bytes(const spec&)
6097
{
6098
static thread_local sequence cache(/*1~2 bytes = */either(
6099
sequence(character (0xE0), character_in_range(0xA0, 0xBF)),
6100
sequence(character_in_range(0xE1, 0xEC), character_in_range(0x80, 0xBF)),
6101
sequence(character (0xED), character_in_range(0x80, 0x9F)),
6102
sequence(character_in_range(0xEE, 0xEF), character_in_range(0x80, 0xBF))
6103
), /*3rd byte = */ character_in_range(0x80, 0xBF));
6104
6105
return cache;
6106
}
6107
6108
TOML11_INLINE sequence const& utf8_4bytes(const spec&)
6109
{
6110
static thread_local sequence cache(/*1~2 bytes = */either(
6111
sequence(character (0xF0), character_in_range(0x90, 0xBF)),
6112
sequence(character_in_range(0xF1, 0xF3), character_in_range(0x80, 0xBF)),
6113
sequence(character (0xF4), character_in_range(0x80, 0x8F))
6114
), character_in_range(0x80, 0xBF), character_in_range(0x80, 0xBF));
6115
6116
return cache;
6117
}
6118
6119
// ===========================================================================
6120
// Whitespace
6121
6122
TOML11_INLINE character_either const& wschar(const spec&)
6123
{
6124
static thread_local character_either cache(" \t");
6125
return cache;
6126
}
6127
6128
TOML11_INLINE repeat_at_least const& ws(const spec& sp)
6129
{
6130
static thread_local auto cache = make_cache([](const spec& s){
6131
return repeat_at_least(0, wschar(s));
6132
});
6133
return cache.at(sp);
6134
}
6135
6136
// ===========================================================================
6137
// Newline
6138
6139
TOML11_INLINE either const& newline(const spec&)
6140
{
6141
static thread_local either cache(character(char_type('\n')), literal("\r\n"));
6142
return cache;
6143
}
6144
6145
// ===========================================================================
6146
// Comments
6147
6148
TOML11_INLINE either const& allowed_comment_char(const spec& sp)
6149
{
6150
static thread_local auto cache = make_cache([](const spec& s){
6151
if(s.ext_allow_control_characters_in_comments)
6152
{
6153
return either(
6154
character_in_range(0x01, 0x09),
6155
character_in_range(0x0E, 0x7F),
6156
non_ascii(s)
6157
);
6158
}
6159
else
6160
{
6161
return either(
6162
character(0x09),
6163
character_in_range(0x20, 0x7E),
6164
non_ascii(s)
6165
);
6166
}
6167
});
6168
return cache.at(sp);
6169
}
6170
6171
// XXX Note that it does not take newline
6172
TOML11_INLINE sequence const& comment(const spec& sp)
6173
{
6174
static thread_local auto cache = make_cache([](const spec& s){
6175
return sequence(character(char_type('#')),
6176
repeat_at_least(0, allowed_comment_char(s)));
6177
});
6178
return cache.at(sp);
6179
}
6180
6181
// ===========================================================================
6182
// Boolean
6183
6184
TOML11_INLINE either const& boolean(const spec&)
6185
{
6186
static thread_local either cache(literal("true"), literal("false"));
6187
return cache;
6188
}
6189
6190
// ===========================================================================
6191
// Integer
6192
6193
// non-digit-graph = ([a-zA-Z]|unicode mb char)
6194
// graph = ([a-zA-Z0-9]|unicode mb char)
6195
// suffix = _ non-digit-graph (graph | _graph)
6196
TOML11_INLINE sequence const& num_suffix(const spec& sp)
6197
{
6198
static thread_local auto cache = make_cache([](const spec& s) {
6199
const auto non_digit_graph = [&s]() {
6200
return either(
6201
alpha(s),
6202
non_ascii(s)
6203
);
6204
};
6205
const auto graph = [&s]() {
6206
return either(
6207
alpha(s),
6208
digit(s),
6209
non_ascii(s)
6210
);
6211
};
6212
6213
return sequence(
6214
character(char_type('_')),
6215
non_digit_graph(),
6216
repeat_at_least(0,
6217
either(
6218
sequence(character(char_type('_')), graph()),
6219
graph()
6220
)
6221
)
6222
);
6223
});
6224
return cache.at(sp);
6225
}
6226
6227
TOML11_INLINE sequence const& dec_int(const spec& sp)
6228
{
6229
static thread_local auto cache = make_cache([](const spec& s) {
6230
const auto digit19 = []() {
6231
return character_in_range(char_type('1'), char_type('9'));
6232
};
6233
return sequence(
6234
maybe(character_either("+-")),
6235
either(
6236
sequence(
6237
digit19(),
6238
repeat_at_least(1,
6239
either(
6240
digit(s),
6241
sequence(character(char_type('_')), digit(s))
6242
)
6243
)
6244
),
6245
digit(s)
6246
)
6247
);
6248
});
6249
return cache.at(sp);
6250
}
6251
6252
TOML11_INLINE sequence const& hex_int(const spec& sp)
6253
{
6254
static thread_local auto cache = make_cache([](const spec& s) {
6255
return sequence(
6256
literal("0x"),
6257
hexdig(s),
6258
repeat_at_least(0,
6259
either(
6260
hexdig(s),
6261
sequence(character(char_type('_')), hexdig(s))
6262
)
6263
)
6264
);
6265
});
6266
return cache.at(sp);
6267
}
6268
6269
TOML11_INLINE sequence const& oct_int(const spec& s)
6270
{
6271
static thread_local auto cache = make_cache([](const spec&) {
6272
const auto digit07 = []() {
6273
return character_in_range(char_type('0'), char_type('7'));
6274
};
6275
return sequence(
6276
literal("0o"),
6277
digit07(),
6278
repeat_at_least(0,
6279
either(
6280
digit07(),
6281
sequence(character(char_type('_')), digit07())
6282
)
6283
)
6284
);
6285
});
6286
return cache.at(s);
6287
}
6288
6289
TOML11_INLINE sequence const& bin_int(const spec& s)
6290
{
6291
static thread_local auto cache = make_cache([](const spec&) {
6292
const auto digit01 = []() {
6293
return character_either("01");
6294
};
6295
return sequence(
6296
literal("0b"),
6297
digit01(),
6298
repeat_at_least(0,
6299
either(
6300
digit01(),
6301
sequence(character(char_type('_')), digit01())
6302
)
6303
)
6304
);
6305
});
6306
return cache.at(s);
6307
}
6308
6309
TOML11_INLINE either const& integer(const spec& sp)
6310
{
6311
static thread_local auto cache = make_cache([](const spec& s) {
6312
return either(
6313
hex_int(s),
6314
oct_int(s),
6315
bin_int(s),
6316
dec_int(s)
6317
);
6318
});
6319
return cache.at(sp);
6320
}
6321
6322
6323
// ===========================================================================
6324
// Floating
6325
6326
TOML11_INLINE sequence const& zero_prefixable_int(const spec& sp)
6327
{
6328
static thread_local auto cache = make_cache([](const spec& s) {
6329
return sequence(
6330
digit(s),
6331
repeat_at_least(0,
6332
either(
6333
digit(s),
6334
sequence(character('_'), digit(s))
6335
)
6336
)
6337
);
6338
});
6339
return cache.at(sp);
6340
}
6341
6342
TOML11_INLINE sequence const& fractional_part(const spec& sp)
6343
{
6344
static thread_local auto cache = make_cache([](const spec& s) {
6345
return sequence(
6346
character('.'),
6347
zero_prefixable_int(s)
6348
);
6349
});
6350
return cache.at(sp);
6351
}
6352
6353
TOML11_INLINE sequence const& exponent_part(const spec& sp)
6354
{
6355
static thread_local auto cache = make_cache([](const spec& s) {
6356
return sequence(
6357
character_either("eE"),
6358
maybe(character_either("+-")),
6359
zero_prefixable_int(s)
6360
);
6361
});
6362
return cache.at(sp);
6363
}
6364
6365
TOML11_INLINE sequence const& hex_floating(const spec& sp)
6366
{
6367
static thread_local auto cache = make_cache([](const spec& s) {
6368
// C99 hexfloat (%a)
6369
// [+-]? 0x ( [0-9a-fA-F]*\.[0-9a-fA-F]+ | [0-9a-fA-F]+\.? ) [pP] [+-]? [0-9]+
6370
6371
// - 0x(int).(frac)p[+-](int)
6372
// - 0x(int).p[+-](int)
6373
// - 0x.(frac)p[+-](int)
6374
// - 0x(int)p[+-](int)
6375
6376
return sequence(
6377
maybe(character_either("+-")),
6378
character('0'),
6379
character_either("xX"),
6380
either(
6381
sequence(
6382
repeat_at_least(0, hexdig(s)),
6383
character('.'),
6384
repeat_at_least(1, hexdig(s))
6385
),
6386
sequence(
6387
repeat_at_least(1, hexdig(s)),
6388
maybe(character('.'))
6389
)
6390
),
6391
character_either("pP"),
6392
maybe(character_either("+-")),
6393
repeat_at_least(1, character_in_range('0', '9'))
6394
);
6395
});
6396
return cache.at(sp);
6397
}
6398
6399
TOML11_INLINE either const& floating(const spec& sp)
6400
{
6401
static thread_local auto cache = make_cache([](const spec& s) {
6402
return either(
6403
sequence(
6404
dec_int(s),
6405
either(
6406
exponent_part(s),
6407
sequence(fractional_part(s), maybe(exponent_part(s)))
6408
)
6409
),
6410
sequence(
6411
maybe(character_either("+-")),
6412
either(literal("inf"), literal("nan"))
6413
)
6414
);
6415
});
6416
return cache.at(sp);
6417
}
6418
6419
// ===========================================================================
6420
// Datetime
6421
6422
TOML11_INLINE sequence const& local_date(const spec& sp)
6423
{
6424
static thread_local auto cache = make_cache([](const spec& s) {
6425
return sequence(
6426
repeat_exact(4, digit(s)),
6427
character('-'),
6428
repeat_exact(2, digit(s)),
6429
character('-'),
6430
repeat_exact(2, digit(s))
6431
);
6432
});
6433
return cache.at(sp);
6434
}
6435
TOML11_INLINE sequence const& local_time(const spec& sp)
6436
{
6437
static thread_local auto cache = make_cache([](const spec& s) {
6438
if(s.v1_1_0_make_seconds_optional)
6439
{
6440
return sequence(
6441
repeat_exact(2, digit(s)),
6442
character(':'),
6443
repeat_exact(2, digit(s)),
6444
maybe(sequence(
6445
character(':'),
6446
repeat_exact(2, digit(s)),
6447
maybe(sequence(character('.'), repeat_at_least(1, digit(s))))
6448
)));
6449
}
6450
else
6451
{
6452
return sequence(
6453
repeat_exact(2, digit(s)),
6454
character(':'),
6455
repeat_exact(2, digit(s)),
6456
character(':'),
6457
repeat_exact(2, digit(s)),
6458
maybe(sequence(character('.'), repeat_at_least(1, digit(s))))
6459
);
6460
}
6461
});
6462
return cache.at(sp);
6463
}
6464
TOML11_INLINE either const& time_offset(const spec& sp)
6465
{
6466
static thread_local auto cache = make_cache([](const spec& s) {
6467
return either(
6468
character_either("zZ"),
6469
sequence(character_either("+-"),
6470
repeat_exact(2, digit(s)),
6471
character(':'),
6472
repeat_exact(2, digit(s))
6473
)
6474
);
6475
});
6476
return cache.at(sp);
6477
}
6478
TOML11_INLINE sequence const& full_time(const spec& sp)
6479
{
6480
static thread_local auto cache = make_cache([](const spec& s) {
6481
return sequence(local_time(s), time_offset(s));
6482
});
6483
return cache.at(sp);
6484
}
6485
TOML11_INLINE character_either const& time_delim(const spec& sp)
6486
{
6487
static thread_local auto cache = make_cache([](const spec&) {
6488
return character_either("Tt ");
6489
});
6490
return cache.at(sp);
6491
}
6492
TOML11_INLINE sequence const& local_datetime(const spec& sp)
6493
{
6494
static thread_local auto cache = make_cache([](const spec& s) {
6495
return sequence(local_date(s), time_delim(s), local_time(s));
6496
});
6497
return cache.at(sp);
6498
}
6499
TOML11_INLINE sequence const& offset_datetime(const spec& sp)
6500
{
6501
static thread_local auto cache = make_cache([](const spec& s) {
6502
return sequence(local_date(s), time_delim(s), full_time(s));
6503
});
6504
return cache.at(sp);
6505
}
6506
6507
// ===========================================================================
6508
// String
6509
6510
TOML11_INLINE sequence const& escaped_x2(const spec& sp)
6511
{
6512
static thread_local auto cache = make_cache([](const spec& s) {
6513
return sequence(character('x'), repeat_exact(2, hexdig(s)));
6514
});
6515
return cache.at(sp);
6516
}
6517
TOML11_INLINE sequence const& escaped_u4(const spec& sp)
6518
{
6519
static thread_local auto cache = make_cache([](const spec& s) {
6520
return sequence(character('u'), repeat_exact(4, hexdig(s)));
6521
});
6522
return cache.at(sp);
6523
}
6524
TOML11_INLINE sequence const& escaped_U8(const spec& sp)
6525
{
6526
static thread_local auto cache = make_cache([](const spec& s) {
6527
return sequence(character('U'), repeat_exact(8, hexdig(s)));
6528
});
6529
return cache.at(sp);
6530
}
6531
6532
TOML11_INLINE sequence const& escaped(const spec& sp)
6533
{
6534
static thread_local auto cache = make_cache([](const spec& s) {
6535
const auto escape_char = [&s] {
6536
if(s.v1_1_0_add_escape_sequence_e)
6537
{
6538
return character_either("\"\\bfnrte");
6539
}
6540
else
6541
{
6542
return character_either("\"\\bfnrt");
6543
}
6544
};
6545
6546
const auto escape_seq = [&s, &escape_char] {
6547
if(s.v1_1_0_add_escape_sequence_x)
6548
{
6549
return either(
6550
escape_char(),
6551
escaped_u4(s),
6552
escaped_U8(s),
6553
escaped_x2(s)
6554
);
6555
}
6556
else
6557
{
6558
return either(
6559
escape_char(),
6560
escaped_u4(s),
6561
escaped_U8(s)
6562
);
6563
}
6564
};
6565
6566
return sequence(character('\\'), escape_seq());
6567
});
6568
return cache.at(sp);
6569
}
6570
6571
TOML11_INLINE either const& basic_char(const spec& sp)
6572
{
6573
static thread_local auto cache = make_cache([](const spec& s) {
6574
const auto basic_unescaped = [&s]() {
6575
return either(
6576
wschar(s),
6577
character(0x21), // 22 is "
6578
character_in_range(0x23, 0x5B), // 5C is backslash
6579
character_in_range(0x5D, 0x7E), // 7F is DEL
6580
non_ascii(s)
6581
);
6582
};
6583
return either(basic_unescaped(), escaped(s));
6584
});
6585
return cache.at(sp);
6586
}
6587
6588
TOML11_INLINE sequence const& basic_string(const spec& sp)
6589
{
6590
static thread_local auto cache = make_cache([](const spec& s) {
6591
return sequence(
6592
character('"'),
6593
repeat_at_least(0, basic_char(s)),
6594
character('"')
6595
);
6596
});
6597
return cache.at(sp);
6598
}
6599
6600
// ---------------------------------------------------------------------------
6601
// multiline string
6602
6603
TOML11_INLINE sequence const& escaped_newline(const spec& sp)
6604
{
6605
static thread_local auto cache = make_cache([](const spec& s) {
6606
return sequence(
6607
character('\\'), ws(s), newline(s),
6608
repeat_at_least(0, either(wschar(s), newline(s)))
6609
);
6610
});
6611
return cache.at(sp);
6612
}
6613
6614
TOML11_INLINE sequence const& ml_basic_string(const spec& sp)
6615
{
6616
static thread_local auto cache = make_cache([](const spec& s) {
6617
const auto mlb_content = [&s]() {
6618
return either(basic_char(s), newline(s), escaped_newline(s));
6619
};
6620
const auto mlb_quotes = []() {
6621
return either(literal("\"\""), character('\"'));
6622
};
6623
6624
return sequence(
6625
literal("\"\"\""),
6626
maybe(newline(s)),
6627
repeat_at_least(0, mlb_content()),
6628
repeat_at_least(0,
6629
sequence(
6630
mlb_quotes(),
6631
repeat_at_least(1, mlb_content())
6632
)
6633
),
6634
// XXX """ and mlb_quotes are intentionally reordered to avoid
6635
// unexpected match of mlb_quotes
6636
literal("\"\"\""),
6637
maybe(mlb_quotes())
6638
);
6639
});
6640
return cache.at(sp);
6641
}
6642
6643
// ---------------------------------------------------------------------------
6644
// literal string
6645
6646
TOML11_INLINE either const& literal_char(const spec& sp)
6647
{
6648
static thread_local auto cache = make_cache([](const spec& s) {
6649
return either(
6650
character (0x09),
6651
character_in_range(0x20, 0x26),
6652
character_in_range(0x28, 0x7E),
6653
non_ascii(s)
6654
);
6655
});
6656
return cache.at(sp);
6657
}
6658
6659
TOML11_INLINE sequence const& literal_string(const spec& sp)
6660
{
6661
static thread_local auto cache = make_cache([](const spec& s) {
6662
return sequence(
6663
character('\''),
6664
repeat_at_least(0, literal_char(s)),
6665
character('\'')
6666
);
6667
});
6668
return cache.at(sp);
6669
}
6670
6671
TOML11_INLINE sequence const& ml_literal_string(const spec& sp)
6672
{
6673
static thread_local auto cache = make_cache([](const spec& s) {
6674
const auto mll_quotes = []() {
6675
return either(literal("''"), character('\''));
6676
};
6677
const auto mll_content = [&s]() {
6678
return either(literal_char(s), newline(s));
6679
};
6680
6681
return sequence(
6682
literal("'''"),
6683
maybe(newline(s)),
6684
repeat_at_least(0, mll_content()),
6685
repeat_at_least(0, sequence(
6686
mll_quotes(),
6687
repeat_at_least(1, mll_content())
6688
)
6689
),
6690
literal("'''"),
6691
maybe(mll_quotes())
6692
// XXX ''' and mll_quotes are intentionally reordered to avoid
6693
// unexpected match of mll_quotes
6694
);
6695
});
6696
return cache.at(sp);
6697
}
6698
6699
TOML11_INLINE either const& string(const spec& sp)
6700
{
6701
static thread_local auto cache = make_cache([](const spec& s) {
6702
return either(
6703
ml_basic_string(s),
6704
ml_literal_string(s),
6705
basic_string(s),
6706
literal_string(s)
6707
);
6708
});
6709
return cache.at(sp);
6710
}
6711
6712
// ===========================================================================
6713
// Keys
6714
6715
// to keep `expected_chars` simple
6716
TOML11_INLINE non_ascii_key_char::non_ascii_key_char(const spec& s) noexcept
6717
{
6718
assert(s.ext_allow_non_english_in_bare_keys);
6719
(void)s; // for NDEBUG
6720
}
6721
6722
TOML11_INLINE std::uint32_t non_ascii_key_char::read_utf8(location& loc) const
6723
{
6724
// U+0000 ... U+0079 ; 0xxx_xxxx
6725
// U+0080 ... U+07FF ; 110y_yyyx 10xx_xxxx;
6726
// U+0800 ... U+FFFF ; 1110_yyyy 10yx_xxxx 10xx_xxxx
6727
// U+010000 ... U+10FFFF; 1111_0yyy 10yy_xxxx 10xx_xxxx 10xx_xxxx
6728
6729
const unsigned char b1 = loc.current(); loc.advance(1);
6730
if(b1 < 0x80)
6731
{
6732
return static_cast<std::uint32_t>(b1);
6733
}
6734
else if((b1 >> 5) == 6) // 0b110 == 6
6735
{
6736
const auto b2 = loc.current(); loc.advance(1);
6737
6738
const std::uint32_t c1 = b1 & ((1 << 5) - 1);
6739
const std::uint32_t c2 = b2 & ((1 << 6) - 1);
6740
const std::uint32_t codep = (c1 << 6) + c2;
6741
6742
if(codep < 0x80)
6743
{
6744
return 0xFFFFFFFF;
6745
}
6746
return codep;
6747
}
6748
else if((b1 >> 4) == 14) // 0b1110 == 14
6749
{
6750
const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;}
6751
const auto b3 = loc.current(); loc.advance(1);
6752
6753
const std::uint32_t c1 = b1 & ((1 << 4) - 1);
6754
const std::uint32_t c2 = b2 & ((1 << 6) - 1);
6755
const std::uint32_t c3 = b3 & ((1 << 6) - 1);
6756
6757
const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3;
6758
if(codep < 0x800)
6759
{
6760
return 0xFFFFFFFF;
6761
}
6762
return codep;
6763
}
6764
else if((b1 >> 3) == 30) // 0b11110 == 30
6765
{
6766
const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;}
6767
const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;}
6768
const auto b4 = loc.current(); loc.advance(1);
6769
6770
const std::uint32_t c1 = b1 & ((1 << 3) - 1);
6771
const std::uint32_t c2 = b2 & ((1 << 6) - 1);
6772
const std::uint32_t c3 = b3 & ((1 << 6) - 1);
6773
const std::uint32_t c4 = b4 & ((1 << 6) - 1);
6774
const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;
6775
6776
if(codep < 0x10000)
6777
{
6778
return 0xFFFFFFFF;
6779
}
6780
return codep;
6781
}
6782
else // not a Unicode codepoint in UTF-8
6783
{
6784
return 0xFFFFFFFF;
6785
}
6786
}
6787
6788
TOML11_INLINE region non_ascii_key_char::scan(location& loc) const
6789
{
6790
if(loc.eof()) {return region{};}
6791
6792
const auto first = loc;
6793
6794
const auto cp = read_utf8(loc);
6795
6796
if(cp == 0xFFFFFFFF)
6797
{
6798
return region{};
6799
}
6800
6801
// ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _
6802
// / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions
6803
// / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block
6804
// / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon
6805
// / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ
6806
// / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics
6807
// / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces
6808
// / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode)
6809
// / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF)
6810
6811
if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) ||
6812
(0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) ||
6813
(0x37F <= cp && cp <= 0x1FFF) ||
6814
(0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) ||
6815
(0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) ||
6816
(0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) ||
6817
(0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) ||
6818
(0x10000 <= cp && cp <= 0xEFFFF) )
6819
{
6820
return region(first, loc);
6821
}
6822
loc = first;
6823
return region{};
6824
}
6825
6826
TOML11_INLINE repeat_at_least const& unquoted_key(const spec& sp)
6827
{
6828
static thread_local auto cache = make_cache([](const spec& s) {
6829
const auto keychar = [&s] {
6830
if(s.ext_allow_non_english_in_bare_keys)
6831
{
6832
return either(alpha(s), digit(s), character{0x2D}, character{0x5F},
6833
non_ascii_key_char(s));
6834
}
6835
else
6836
{
6837
return either(alpha(s), digit(s), character{0x2D}, character{0x5F});
6838
}
6839
};
6840
return repeat_at_least(1, keychar());
6841
});
6842
return cache.at(sp);
6843
}
6844
6845
TOML11_INLINE either const& quoted_key(const spec& sp)
6846
{
6847
static thread_local auto cache = make_cache([](const spec& s) {
6848
return either(basic_string(s), literal_string(s));
6849
});
6850
return cache.at(sp);
6851
}
6852
6853
TOML11_INLINE either const& simple_key(const spec& sp)
6854
{
6855
static thread_local auto cache = make_cache([](const spec& s) {
6856
return either(unquoted_key(s), quoted_key(s));
6857
});
6858
return cache.at(sp);
6859
}
6860
6861
TOML11_INLINE sequence const& dot_sep(const spec& sp)
6862
{
6863
static thread_local auto cache = make_cache([](const spec& s) {
6864
return sequence(ws(s), character('.'), ws(s));
6865
});
6866
return cache.at(sp);
6867
}
6868
6869
TOML11_INLINE sequence const& dotted_key(const spec& sp)
6870
{
6871
static thread_local auto cache = make_cache([](const spec& s) {
6872
return sequence(
6873
simple_key(s),
6874
repeat_at_least(1, sequence(dot_sep(s), simple_key(s)))
6875
);
6876
});
6877
return cache.at(sp);
6878
}
6879
6880
TOML11_INLINE sequence const& keyval_sep(const spec& sp)
6881
{
6882
static thread_local auto cache = make_cache([](const spec& s) {
6883
return sequence(ws(s), character('='), ws(s));
6884
});
6885
return cache.at(sp);
6886
}
6887
6888
// ===========================================================================
6889
// Table key
6890
6891
TOML11_INLINE sequence const& std_table(const spec& sp)
6892
{
6893
static thread_local auto cache = make_cache([](const spec& s) {
6894
return sequence(character('['), ws(s), key(s), ws(s), character(']'));
6895
});
6896
return cache.at(sp);
6897
}
6898
6899
TOML11_INLINE sequence const& array_table(const spec& sp)
6900
{
6901
static thread_local auto cache = make_cache([](const spec& s) {
6902
return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]"));
6903
});
6904
return cache.at(sp);
6905
}
6906
6907
// ===========================================================================
6908
// extension: null
6909
6910
TOML11_INLINE literal const& null_value(const spec&)
6911
{
6912
static thread_local literal cache("null");
6913
return cache;
6914
}
6915
6916
} // namespace syntax
6917
} // namespace detail
6918
} // TOML11_INLINE_VERSION_NAMESPACE
6919
} // namespace toml
6920
#endif // TOML11_SYNTAX_IMPL_HPP
6921
#endif
6922
6923
#endif// TOML11_SYNTAX_HPP
6924
#ifndef TOML11_COMMENTS_HPP
6925
#define TOML11_COMMENTS_HPP
6926
6927
#ifndef TOML11_COMMENTS_FWD_HPP
6928
#define TOML11_COMMENTS_FWD_HPP
6929
6930
// to use __has_builtin
6931
6932
#include <exception>
6933
#include <initializer_list>
6934
#include <iterator>
6935
#include <stdexcept>
6936
#include <string>
6937
#include <type_traits>
6938
#include <utility>
6939
#include <vector>
6940
#include <ostream>
6941
6942
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
6943
// Those two are a container that have the same interface as `std::vector<std::string>`
6944
// but bahaves in the opposite way. `preserve_comments` is just the same as
6945
// `std::vector<std::string>` and each `std::string` corresponds to a comment line.
6946
// Conversely, `discard_comments` discards all the strings and ignores everything
6947
// assigned in it. `discard_comments` is always empty and you will encounter an
6948
// error whenever you access to the element.
6949
namespace toml
6950
{
6951
inline namespace TOML11_INLINE_VERSION_NAMESPACE
6952
{
6953
class discard_comments; // forward decl
6954
6955
class preserve_comments
6956
{
6957
public:
6958
// `container_type` is not provided in discard_comments.
6959
// do not use this inner-type in a generic code.
6960
using container_type = std::vector<std::string>;
6961
6962
using size_type = container_type::size_type;
6963
using difference_type = container_type::difference_type;
6964
using value_type = container_type::value_type;
6965
using reference = container_type::reference;
6966
using const_reference = container_type::const_reference;
6967
using pointer = container_type::pointer;
6968
using const_pointer = container_type::const_pointer;
6969
using iterator = container_type::iterator;
6970
using const_iterator = container_type::const_iterator;
6971
using reverse_iterator = container_type::reverse_iterator;
6972
using const_reverse_iterator = container_type::const_reverse_iterator;
6973
6974
public:
6975
6976
preserve_comments() = default;
6977
~preserve_comments() = default;
6978
preserve_comments(preserve_comments const&) = default;
6979
preserve_comments(preserve_comments &&) = default;
6980
preserve_comments& operator=(preserve_comments const&) = default;
6981
preserve_comments& operator=(preserve_comments &&) = default;
6982
6983
explicit preserve_comments(const std::vector<std::string>& c): comments(c){}
6984
explicit preserve_comments(std::vector<std::string>&& c)
6985
: comments(std::move(c))
6986
{}
6987
preserve_comments& operator=(const std::vector<std::string>& c)
6988
{
6989
comments = c;
6990
return *this;
6991
}
6992
preserve_comments& operator=(std::vector<std::string>&& c)
6993
{
6994
comments = std::move(c);
6995
return *this;
6996
}
6997
6998
explicit preserve_comments(const discard_comments&) {}
6999
7000
explicit preserve_comments(size_type n): comments(n) {}
7001
preserve_comments(size_type n, const std::string& x): comments(n, x) {}
7002
preserve_comments(std::initializer_list<std::string> x): comments(x) {}
7003
template<typename InputIterator>
7004
preserve_comments(InputIterator first, InputIterator last)
7005
: comments(first, last)
7006
{}
7007
7008
template<typename InputIterator>
7009
void assign(InputIterator first, InputIterator last) {comments.assign(first, last);}
7010
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
7011
void assign(size_type n, const std::string& val) {comments.assign(n, val);}
7012
7013
// Related to the issue #97.
7014
//
7015
// `std::vector::insert` and `std::vector::erase` in the STL implementation
7016
// included in GCC 4.8.5 takes `std::vector::iterator` instead of
7017
// `std::vector::const_iterator`. It causes compilation error in GCC 4.8.5.
7018
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
7019
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
7020
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
7021
# endif
7022
#endif
7023
7024
#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
7025
iterator insert(iterator p, const std::string& x)
7026
{
7027
return comments.insert(p, x);
7028
}
7029
iterator insert(iterator p, std::string&& x)
7030
{
7031
return comments.insert(p, std::move(x));
7032
}
7033
void insert(iterator p, size_type n, const std::string& x)
7034
{
7035
return comments.insert(p, n, x);
7036
}
7037
template<typename InputIterator>
7038
void insert(iterator p, InputIterator first, InputIterator last)
7039
{
7040
return comments.insert(p, first, last);
7041
}
7042
void insert(iterator p, std::initializer_list<std::string> ini)
7043
{
7044
return comments.insert(p, ini);
7045
}
7046
7047
template<typename ... Ts>
7048
iterator emplace(iterator p, Ts&& ... args)
7049
{
7050
return comments.emplace(p, std::forward<Ts>(args)...);
7051
}
7052
7053
iterator erase(iterator pos) {return comments.erase(pos);}
7054
iterator erase(iterator first, iterator last)
7055
{
7056
return comments.erase(first, last);
7057
}
7058
#else
7059
iterator insert(const_iterator p, const std::string& x)
7060
{
7061
return comments.insert(p, x);
7062
}
7063
iterator insert(const_iterator p, std::string&& x)
7064
{
7065
return comments.insert(p, std::move(x));
7066
}
7067
iterator insert(const_iterator p, size_type n, const std::string& x)
7068
{
7069
return comments.insert(p, n, x);
7070
}
7071
template<typename InputIterator>
7072
iterator insert(const_iterator p, InputIterator first, InputIterator last)
7073
{
7074
return comments.insert(p, first, last);
7075
}
7076
iterator insert(const_iterator p, std::initializer_list<std::string> ini)
7077
{
7078
return comments.insert(p, ini);
7079
}
7080
7081
template<typename ... Ts>
7082
iterator emplace(const_iterator p, Ts&& ... args)
7083
{
7084
return comments.emplace(p, std::forward<Ts>(args)...);
7085
}
7086
7087
iterator erase(const_iterator pos) {return comments.erase(pos);}
7088
iterator erase(const_iterator first, const_iterator last)
7089
{
7090
return comments.erase(first, last);
7091
}
7092
#endif
7093
7094
void swap(preserve_comments& other) {comments.swap(other.comments);}
7095
7096
void push_back(const std::string& v) {comments.push_back(v);}
7097
void push_back(std::string&& v) {comments.push_back(std::move(v));}
7098
void pop_back() {comments.pop_back();}
7099
7100
template<typename ... Ts>
7101
void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);}
7102
7103
void clear() {comments.clear();}
7104
7105
size_type size() const noexcept {return comments.size();}
7106
size_type max_size() const noexcept {return comments.max_size();}
7107
size_type capacity() const noexcept {return comments.capacity();}
7108
bool empty() const noexcept {return comments.empty();}
7109
7110
void reserve(size_type n) {comments.reserve(n);}
7111
void resize(size_type n) {comments.resize(n);}
7112
void resize(size_type n, const std::string& c) {comments.resize(n, c);}
7113
void shrink_to_fit() {comments.shrink_to_fit();}
7114
7115
reference operator[](const size_type n) noexcept {return comments[n];}
7116
const_reference operator[](const size_type n) const noexcept {return comments[n];}
7117
reference at(const size_type n) {return comments.at(n);}
7118
const_reference at(const size_type n) const {return comments.at(n);}
7119
reference front() noexcept {return comments.front();}
7120
const_reference front() const noexcept {return comments.front();}
7121
reference back() noexcept {return comments.back();}
7122
const_reference back() const noexcept {return comments.back();}
7123
7124
pointer data() noexcept {return comments.data();}
7125
const_pointer data() const noexcept {return comments.data();}
7126
7127
iterator begin() noexcept {return comments.begin();}
7128
iterator end() noexcept {return comments.end();}
7129
const_iterator begin() const noexcept {return comments.begin();}
7130
const_iterator end() const noexcept {return comments.end();}
7131
const_iterator cbegin() const noexcept {return comments.cbegin();}
7132
const_iterator cend() const noexcept {return comments.cend();}
7133
7134
reverse_iterator rbegin() noexcept {return comments.rbegin();}
7135
reverse_iterator rend() noexcept {return comments.rend();}
7136
const_reverse_iterator rbegin() const noexcept {return comments.rbegin();}
7137
const_reverse_iterator rend() const noexcept {return comments.rend();}
7138
const_reverse_iterator crbegin() const noexcept {return comments.crbegin();}
7139
const_reverse_iterator crend() const noexcept {return comments.crend();}
7140
7141
friend bool operator==(const preserve_comments&, const preserve_comments&);
7142
friend bool operator!=(const preserve_comments&, const preserve_comments&);
7143
friend bool operator< (const preserve_comments&, const preserve_comments&);
7144
friend bool operator<=(const preserve_comments&, const preserve_comments&);
7145
friend bool operator> (const preserve_comments&, const preserve_comments&);
7146
friend bool operator>=(const preserve_comments&, const preserve_comments&);
7147
7148
friend void swap(preserve_comments&, std::vector<std::string>&);
7149
friend void swap(std::vector<std::string>&, preserve_comments&);
7150
7151
private:
7152
7153
container_type comments;
7154
};
7155
7156
bool operator==(const preserve_comments& lhs, const preserve_comments& rhs);
7157
bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs);
7158
bool operator< (const preserve_comments& lhs, const preserve_comments& rhs);
7159
bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs);
7160
bool operator> (const preserve_comments& lhs, const preserve_comments& rhs);
7161
bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs);
7162
7163
void swap(preserve_comments& lhs, preserve_comments& rhs);
7164
void swap(preserve_comments& lhs, std::vector<std::string>& rhs);
7165
void swap(std::vector<std::string>& lhs, preserve_comments& rhs);
7166
7167
std::ostream& operator<<(std::ostream& os, const preserve_comments& com);
7168
7169
namespace detail
7170
{
7171
7172
// To provide the same interface with `preserve_comments`, `discard_comments`
7173
// should have an iterator. But it does not contain anything, so we need to
7174
// add an iterator that points nothing.
7175
//
7176
// It always points null, so DO NOT unwrap this iterator. It always crashes
7177
// your program.
7178
template<typename T, bool is_const>
7179
struct empty_iterator
7180
{
7181
using value_type = T;
7182
using reference_type = typename std::conditional<is_const, T const&, T&>::type;
7183
using pointer_type = typename std::conditional<is_const, T const*, T*>::type;
7184
using difference_type = std::ptrdiff_t;
7185
using iterator_category = std::random_access_iterator_tag;
7186
7187
empty_iterator() = default;
7188
~empty_iterator() = default;
7189
empty_iterator(empty_iterator const&) = default;
7190
empty_iterator(empty_iterator &&) = default;
7191
empty_iterator& operator=(empty_iterator const&) = default;
7192
empty_iterator& operator=(empty_iterator &&) = default;
7193
7194
// DO NOT call these operators.
7195
reference_type operator*() const noexcept {std::terminate();}
7196
pointer_type operator->() const noexcept {return nullptr;}
7197
reference_type operator[](difference_type) const noexcept {return this->operator*();}
7198
7199
// These operators do nothing.
7200
empty_iterator& operator++() noexcept {return *this;}
7201
empty_iterator operator++(int) noexcept {return *this;}
7202
empty_iterator& operator--() noexcept {return *this;}
7203
empty_iterator operator--(int) noexcept {return *this;}
7204
7205
empty_iterator& operator+=(difference_type) noexcept {return *this;}
7206
empty_iterator& operator-=(difference_type) noexcept {return *this;}
7207
7208
empty_iterator operator+(difference_type) const noexcept {return *this;}
7209
empty_iterator operator-(difference_type) const noexcept {return *this;}
7210
};
7211
7212
template<typename T, bool C>
7213
bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
7214
template<typename T, bool C>
7215
bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
7216
template<typename T, bool C>
7217
bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
7218
template<typename T, bool C>
7219
bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
7220
template<typename T, bool C>
7221
bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
7222
template<typename T, bool C>
7223
bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
7224
7225
template<typename T, bool C>
7226
typename empty_iterator<T, C>::difference_type
7227
operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;}
7228
7229
template<typename T, bool C>
7230
empty_iterator<T, C>
7231
operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;}
7232
template<typename T, bool C>
7233
empty_iterator<T, C>
7234
operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;}
7235
7236
} // detail
7237
7238
// The default comment type. It discards all the comments. It requires only one
7239
// byte to contain, so the memory footprint is smaller than preserve_comments.
7240
//
7241
// It just ignores `push_back`, `insert`, `erase`, and any other modifications.
7242
// IT always returns size() == 0, the iterator taken by `begin()` is always the
7243
// same as that of `end()`, and accessing through `operator[]` or iterators
7244
// always causes a segmentation fault. DO NOT access to the element of this.
7245
//
7246
// Why this is chose as the default type is because the last version (2.x.y)
7247
// does not contain any comments in a value. To minimize the impact on the
7248
// efficiency, this is chosen as a default.
7249
//
7250
// To reduce the memory footprint, later we can try empty base optimization (EBO).
7251
class discard_comments
7252
{
7253
public:
7254
using size_type = std::size_t;
7255
using difference_type = std::ptrdiff_t;
7256
using value_type = std::string;
7257
using reference = std::string&;
7258
using const_reference = std::string const&;
7259
using pointer = std::string*;
7260
using const_pointer = std::string const*;
7261
using iterator = detail::empty_iterator<std::string, false>;
7262
using const_iterator = detail::empty_iterator<std::string, true>;
7263
using reverse_iterator = detail::empty_iterator<std::string, false>;
7264
using const_reverse_iterator = detail::empty_iterator<std::string, true>;
7265
7266
public:
7267
discard_comments() = default;
7268
~discard_comments() = default;
7269
discard_comments(discard_comments const&) = default;
7270
discard_comments(discard_comments &&) = default;
7271
discard_comments& operator=(discard_comments const&) = default;
7272
discard_comments& operator=(discard_comments &&) = default;
7273
7274
explicit discard_comments(const std::vector<std::string>&) noexcept {}
7275
explicit discard_comments(std::vector<std::string>&&) noexcept {}
7276
discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
7277
discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;}
7278
7279
explicit discard_comments(const preserve_comments&) noexcept {}
7280
7281
explicit discard_comments(size_type) noexcept {}
7282
discard_comments(size_type, const std::string&) noexcept {}
7283
discard_comments(std::initializer_list<std::string>) noexcept {}
7284
template<typename InputIterator>
7285
discard_comments(InputIterator, InputIterator) noexcept {}
7286
7287
template<typename InputIterator>
7288
void assign(InputIterator, InputIterator) noexcept {}
7289
void assign(std::initializer_list<std::string>) noexcept {}
7290
void assign(size_type, const std::string&) noexcept {}
7291
7292
iterator insert(const_iterator, const std::string&) {return iterator{};}
7293
iterator insert(const_iterator, std::string&&) {return iterator{};}
7294
iterator insert(const_iterator, size_type, const std::string&) {return iterator{};}
7295
template<typename InputIterator>
7296
iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};}
7297
iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};}
7298
7299
template<typename ... Ts>
7300
iterator emplace(const_iterator, Ts&& ...) {return iterator{};}
7301
iterator erase(const_iterator) {return iterator{};}
7302
iterator erase(const_iterator, const_iterator) {return iterator{};}
7303
7304
void swap(discard_comments&) {return;}
7305
7306
void push_back(const std::string&) {return;}
7307
void push_back(std::string&& ) {return;}
7308
void pop_back() {return;}
7309
7310
template<typename ... Ts>
7311
void emplace_back(Ts&& ...) {return;}
7312
7313
void clear() {return;}
7314
7315
size_type size() const noexcept {return 0;}
7316
size_type max_size() const noexcept {return 0;}
7317
size_type capacity() const noexcept {return 0;}
7318
bool empty() const noexcept {return true;}
7319
7320
void reserve(size_type) {return;}
7321
void resize(size_type) {return;}
7322
void resize(size_type, const std::string&) {return;}
7323
void shrink_to_fit() {return;}
7324
7325
// DO NOT access to the element of this container. This container is always
7326
// empty, so accessing through operator[], front/back, data causes address
7327
// error.
7328
7329
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
7330
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
7331
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
7332
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
7333
reference front() noexcept {never_call("toml::discard_comment::front");}
7334
const_reference front() const noexcept {never_call("toml::discard_comment::front");}
7335
reference back() noexcept {never_call("toml::discard_comment::back");}
7336
const_reference back() const noexcept {never_call("toml::discard_comment::back");}
7337
7338
pointer data() noexcept {return nullptr;}
7339
const_pointer data() const noexcept {return nullptr;}
7340
7341
iterator begin() noexcept {return iterator{};}
7342
iterator end() noexcept {return iterator{};}
7343
const_iterator begin() const noexcept {return const_iterator{};}
7344
const_iterator end() const noexcept {return const_iterator{};}
7345
const_iterator cbegin() const noexcept {return const_iterator{};}
7346
const_iterator cend() const noexcept {return const_iterator{};}
7347
7348
reverse_iterator rbegin() noexcept {return iterator{};}
7349
reverse_iterator rend() noexcept {return iterator{};}
7350
const_reverse_iterator rbegin() const noexcept {return const_iterator{};}
7351
const_reverse_iterator rend() const noexcept {return const_iterator{};}
7352
const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
7353
const_reverse_iterator crend() const noexcept {return const_iterator{};}
7354
7355
private:
7356
7357
[[noreturn]] static void never_call(const char *const this_function)
7358
{
7359
#if __has_builtin(__builtin_unreachable)
7360
__builtin_unreachable();
7361
#endif
7362
throw std::logic_error{this_function};
7363
}
7364
};
7365
7366
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
7367
inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;}
7368
inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;}
7369
inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;}
7370
inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;}
7371
inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;}
7372
7373
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
7374
7375
inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;}
7376
7377
} // TOML11_INLINE_VERSION_NAMESPACE
7378
} // toml11
7379
#endif // TOML11_COMMENTS_FWD_HPP
7380
7381
#if ! defined(TOML11_COMPILE_SOURCES)
7382
#ifndef TOML11_COMMENTS_IMPL_HPP
7383
#define TOML11_COMMENTS_IMPL_HPP
7384
7385
7386
namespace toml
7387
{
7388
inline namespace TOML11_INLINE_VERSION_NAMESPACE
7389
{
7390
7391
TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;}
7392
TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;}
7393
TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;}
7394
TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;}
7395
TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;}
7396
TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;}
7397
7398
TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs)
7399
{
7400
lhs.swap(rhs);
7401
return;
7402
}
7403
TOML11_INLINE void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
7404
{
7405
lhs.comments.swap(rhs);
7406
return;
7407
}
7408
TOML11_INLINE void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
7409
{
7410
lhs.swap(rhs.comments);
7411
return;
7412
}
7413
7414
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com)
7415
{
7416
for(const auto& c : com)
7417
{
7418
if(c.front() != '#')
7419
{
7420
os << '#';
7421
}
7422
os << c << '\n';
7423
}
7424
return os;
7425
}
7426
7427
} // TOML11_INLINE_VERSION_NAMESPACE
7428
} // toml11
7429
#endif // TOML11_COMMENTS_IMPL_HPP
7430
#endif
7431
7432
#endif // TOML11_COMMENTS_HPP
7433
#ifndef TOML11_COLOR_HPP
7434
#define TOML11_COLOR_HPP
7435
7436
#ifndef TOML11_COLOR_FWD_HPP
7437
#define TOML11_COLOR_FWD_HPP
7438
7439
#include <iosfwd>
7440
7441
7442
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
7443
#define TOML11_ERROR_MESSAGE_COLORIZED true
7444
#else
7445
#define TOML11_ERROR_MESSAGE_COLORIZED false
7446
#endif
7447
7448
#ifdef TOML11_USE_THREAD_LOCAL_COLORIZATION
7449
#define TOML11_THREAD_LOCAL_COLORIZATION thread_local
7450
#else
7451
#define TOML11_THREAD_LOCAL_COLORIZATION
7452
#endif
7453
7454
namespace toml
7455
{
7456
inline namespace TOML11_INLINE_VERSION_NAMESPACE
7457
{
7458
namespace color
7459
{
7460
// put ANSI escape sequence to ostream
7461
inline namespace ansi
7462
{
7463
namespace detail
7464
{
7465
7466
// Control color mode globally
7467
class color_mode
7468
{
7469
public:
7470
7471
void enable() noexcept
7472
{
7473
should_color_ = true;
7474
}
7475
void disable() noexcept
7476
{
7477
should_color_ = false;
7478
}
7479
bool should_color() const noexcept
7480
{
7481
return should_color_;
7482
}
7483
7484
private:
7485
7486
bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED;
7487
};
7488
7489
inline color_mode& color_status() noexcept
7490
{
7491
static TOML11_THREAD_LOCAL_COLORIZATION color_mode status;
7492
return status;
7493
}
7494
7495
} // detail
7496
7497
std::ostream& reset (std::ostream& os);
7498
std::ostream& bold (std::ostream& os);
7499
std::ostream& grey (std::ostream& os);
7500
std::ostream& gray (std::ostream& os);
7501
std::ostream& red (std::ostream& os);
7502
std::ostream& green (std::ostream& os);
7503
std::ostream& yellow (std::ostream& os);
7504
std::ostream& blue (std::ostream& os);
7505
std::ostream& magenta(std::ostream& os);
7506
std::ostream& cyan (std::ostream& os);
7507
std::ostream& white (std::ostream& os);
7508
7509
} // ansi
7510
7511
inline void enable()
7512
{
7513
return detail::color_status().enable();
7514
}
7515
inline void disable()
7516
{
7517
return detail::color_status().disable();
7518
}
7519
inline bool should_color()
7520
{
7521
return detail::color_status().should_color();
7522
}
7523
7524
} // color
7525
} // TOML11_INLINE_VERSION_NAMESPACE
7526
} // toml
7527
#endif // TOML11_COLOR_FWD_HPP
7528
7529
#if ! defined(TOML11_COMPILE_SOURCES)
7530
#ifndef TOML11_COLOR_IMPL_HPP
7531
#define TOML11_COLOR_IMPL_HPP
7532
7533
7534
#include <ostream>
7535
7536
namespace toml
7537
{
7538
inline namespace TOML11_INLINE_VERSION_NAMESPACE
7539
{
7540
namespace color
7541
{
7542
// put ANSI escape sequence to ostream
7543
inline namespace ansi
7544
{
7545
7546
TOML11_INLINE std::ostream& reset(std::ostream& os)
7547
{
7548
if(detail::color_status().should_color()) {os << "\033[00m";}
7549
return os;
7550
}
7551
TOML11_INLINE std::ostream& bold(std::ostream& os)
7552
{
7553
if(detail::color_status().should_color()) {os << "\033[01m";}
7554
return os;
7555
}
7556
TOML11_INLINE std::ostream& grey(std::ostream& os)
7557
{
7558
if(detail::color_status().should_color()) {os << "\033[30m";}
7559
return os;
7560
}
7561
TOML11_INLINE std::ostream& gray(std::ostream& os)
7562
{
7563
if(detail::color_status().should_color()) {os << "\033[30m";}
7564
return os;
7565
}
7566
TOML11_INLINE std::ostream& red(std::ostream& os)
7567
{
7568
if(detail::color_status().should_color()) {os << "\033[31m";}
7569
return os;
7570
}
7571
TOML11_INLINE std::ostream& green(std::ostream& os)
7572
{
7573
if(detail::color_status().should_color()) {os << "\033[32m";}
7574
return os;
7575
}
7576
TOML11_INLINE std::ostream& yellow(std::ostream& os)
7577
{
7578
if(detail::color_status().should_color()) {os << "\033[33m";}
7579
return os;
7580
}
7581
TOML11_INLINE std::ostream& blue(std::ostream& os)
7582
{
7583
if(detail::color_status().should_color()) {os << "\033[34m";}
7584
return os;
7585
}
7586
TOML11_INLINE std::ostream& magenta(std::ostream& os)
7587
{
7588
if(detail::color_status().should_color()) {os << "\033[35m";}
7589
return os;
7590
}
7591
TOML11_INLINE std::ostream& cyan (std::ostream& os)
7592
{
7593
if(detail::color_status().should_color()) {os << "\033[36m";}
7594
return os;
7595
}
7596
TOML11_INLINE std::ostream& white (std::ostream& os)
7597
{
7598
if(detail::color_status().should_color()) {os << "\033[37m";}
7599
return os;
7600
}
7601
7602
} // ansi
7603
} // color
7604
} // TOML11_INLINE_VERSION_NAMESPACE
7605
} // toml
7606
#endif // TOML11_COLOR_IMPL_HPP
7607
#endif
7608
7609
#endif // TOML11_COLOR_HPP
7610
#ifndef TOML11_SOURCE_LOCATION_HPP
7611
#define TOML11_SOURCE_LOCATION_HPP
7612
7613
#ifndef TOML11_SOURCE_LOCATION_FWD_HPP
7614
#define TOML11_SOURCE_LOCATION_FWD_HPP
7615
7616
7617
#include <sstream>
7618
#include <string>
7619
#include <vector>
7620
7621
namespace toml
7622
{
7623
inline namespace TOML11_INLINE_VERSION_NAMESPACE
7624
{
7625
7626
//
7627
// A struct to contain location in a toml file.
7628
//
7629
// To reduce memory consumption, it omits unrelated parts of long lines. like:
7630
//
7631
// 1. one long line, short region
7632
// ```
7633
// |
7634
// 1 | ... "foo", "bar", baz, "qux", "foobar", ...
7635
// | ^-- unknown value
7636
// ```
7637
// 2. long region
7638
// ```
7639
// |
7640
// 1 | array = [ "foo", ... "bar" ]
7641
// | ^^^^^^^^^^^^^^^^^^^^- in this array
7642
// ```
7643
// 3. many lines
7644
// |
7645
// 1 | array = [ "foo",
7646
// | ^^^^^^^^
7647
// | ...
7648
// | ^^^
7649
// |
7650
// 10 | , "bar"]
7651
// | ^^^^^^^^- in this array
7652
// ```
7653
//
7654
struct source_location
7655
{
7656
public:
7657
7658
explicit source_location(const detail::region& r);
7659
~source_location() = default;
7660
source_location(source_location const&) = default;
7661
source_location(source_location &&) = default;
7662
source_location& operator=(source_location const&) = default;
7663
source_location& operator=(source_location &&) = default;
7664
7665
bool is_ok() const noexcept {return this->is_ok_;}
7666
std::size_t length() const noexcept {return this->length_;}
7667
7668
std::size_t first_line_number() const noexcept {return this->first_line_;}
7669
std::size_t first_column_number() const noexcept {return this->first_column_;}
7670
std::size_t last_line_number() const noexcept {return this->last_line_;}
7671
std::size_t last_column_number() const noexcept {return this->last_column_;}
7672
7673
std::string const& file_name() const noexcept {return this->file_name_;}
7674
7675
std::size_t num_lines() const noexcept {return this->line_str_.size();}
7676
7677
std::string const& first_line() const;
7678
std::string const& last_line() const;
7679
7680
std::vector<std::string> const& lines() const noexcept {return line_str_;}
7681
7682
// for internal use
7683
std::size_t first_column_offset() const noexcept {return this->first_offset_;}
7684
std::size_t last_column_offset() const noexcept {return this->last_offset_;}
7685
7686
private:
7687
7688
bool is_ok_;
7689
std::size_t first_line_;
7690
std::size_t first_column_; // column num in the actual file
7691
std::size_t first_offset_; // column num in the shown line
7692
std::size_t last_line_;
7693
std::size_t last_column_; // column num in the actual file
7694
std::size_t last_offset_; // column num in the shown line
7695
std::size_t length_;
7696
std::string file_name_;
7697
std::vector<std::string> line_str_;
7698
};
7699
7700
namespace detail
7701
{
7702
7703
std::size_t integer_width_base10(std::size_t i) noexcept;
7704
7705
inline std::size_t line_width() noexcept {return 0;}
7706
7707
template<typename ... Ts>
7708
std::size_t line_width(const source_location& loc, const std::string& /*msg*/,
7709
const Ts& ... tail) noexcept
7710
{
7711
return (std::max)(
7712
integer_width_base10(loc.last_line_number()), line_width(tail...));
7713
}
7714
7715
std::ostringstream&
7716
format_filename(std::ostringstream& oss, const source_location& loc);
7717
7718
std::ostringstream&
7719
format_empty_line(std::ostringstream& oss, const std::size_t lnw);
7720
7721
std::ostringstream& format_line(std::ostringstream& oss,
7722
const std::size_t lnw, const std::size_t linenum, const std::string& line);
7723
7724
std::ostringstream& format_underline(std::ostringstream& oss,
7725
const std::size_t lnw, const std::size_t col, const std::size_t len,
7726
const std::string& msg);
7727
7728
std::string format_location_impl(const std::size_t lnw,
7729
const std::string& prev_fname,
7730
const source_location& loc, const std::string& msg);
7731
7732
inline std::string format_location_rec(const std::size_t, const std::string&)
7733
{
7734
return "";
7735
}
7736
7737
template<typename ... Ts>
7738
std::string format_location_rec(const std::size_t lnw,
7739
const std::string& prev_fname,
7740
const source_location& loc, const std::string& msg,
7741
const Ts& ... tail)
7742
{
7743
return format_location_impl(lnw, prev_fname, loc, msg) +
7744
format_location_rec(lnw, loc.file_name(), tail...);
7745
}
7746
7747
} // namespace detail
7748
7749
// format a location info without title
7750
template<typename ... Ts>
7751
std::string format_location(
7752
const source_location& loc, const std::string& msg, const Ts& ... tail)
7753
{
7754
const auto lnw = detail::line_width(loc, msg, tail...);
7755
7756
const std::string f(""); // at the 1st iteration, no prev_filename is given
7757
return detail::format_location_rec(lnw, f, loc, msg, tail...);
7758
}
7759
7760
} // TOML11_INLINE_VERSION_NAMESPACE
7761
} // toml
7762
#endif // TOML11_SOURCE_LOCATION_FWD_HPP
7763
7764
#if ! defined(TOML11_COMPILE_SOURCES)
7765
#ifndef TOML11_SOURCE_LOCATION_IMPL_HPP
7766
#define TOML11_SOURCE_LOCATION_IMPL_HPP
7767
7768
7769
7770
#include <iomanip>
7771
#include <sstream>
7772
#include <string>
7773
#include <vector>
7774
7775
#include <cctype>
7776
7777
namespace toml
7778
{
7779
inline namespace TOML11_INLINE_VERSION_NAMESPACE
7780
{
7781
7782
TOML11_INLINE source_location::source_location(const detail::region& r)
7783
: is_ok_(false),
7784
first_line_(1),
7785
first_column_(1),
7786
first_offset_(1),
7787
last_line_(1),
7788
last_column_(1),
7789
last_offset_(1),
7790
length_(0),
7791
file_name_("unknown file")
7792
{
7793
if(r.is_ok())
7794
{
7795
this->is_ok_ = true;
7796
this->file_name_ = r.source_name();
7797
this->first_line_ = r.first_line_number();
7798
this->first_column_ = r.first_column_number();
7799
this->last_line_ = r.last_line_number();
7800
this->last_column_ = r.last_column_number();
7801
this->length_ = r.length();
7802
7803
const auto lines = r.as_lines();
7804
assert( ! lines.empty());
7805
7806
for(const auto& l : lines)
7807
{
7808
this->line_str_.push_back(l.first);
7809
}
7810
7811
this->first_offset_ = lines.at( 0).second + 1; // to 1-origin
7812
this->last_offset_ = lines.at(lines.size()-1).second + 1;
7813
}
7814
}
7815
7816
TOML11_INLINE std::string const& source_location::first_line() const
7817
{
7818
if(this->line_str_.size() == 0)
7819
{
7820
throw std::out_of_range("toml::source_location::first_line: `lines` is empty");
7821
}
7822
return this->line_str_.front();
7823
}
7824
TOML11_INLINE std::string const& source_location::last_line() const
7825
{
7826
if(this->line_str_.size() == 0)
7827
{
7828
throw std::out_of_range("toml::source_location::first_line: `lines` is empty");
7829
}
7830
return this->line_str_.back();
7831
}
7832
7833
namespace detail
7834
{
7835
7836
TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept
7837
{
7838
std::size_t width = 0;
7839
while(i != 0)
7840
{
7841
i /= 10;
7842
width += 1;
7843
}
7844
return width;
7845
}
7846
7847
TOML11_INLINE std::ostringstream&
7848
format_filename(std::ostringstream& oss, const source_location& loc)
7849
{
7850
// --> example.toml
7851
oss << color::bold << color::blue << " --> " << color::reset
7852
<< color::bold << loc.file_name() << '\n' << color::reset;
7853
return oss;
7854
}
7855
7856
TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss,
7857
const std::size_t lnw)
7858
{
7859
// |
7860
oss << detail::make_string(lnw + 1, ' ')
7861
<< color::bold << color::blue << " |\n" << color::reset;
7862
return oss;
7863
}
7864
7865
TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss,
7866
const std::size_t lnw, const std::size_t linenum, const std::string& line)
7867
{
7868
// 10 | key = "value"
7869
oss << ' ' << color::bold << color::blue
7870
<< std::setw(static_cast<int>(lnw))
7871
<< std::right << linenum << " | " << color::reset;
7872
for(const char c : line)
7873
{
7874
if(std::isgraph(c) || c == ' ')
7875
{
7876
oss << c;
7877
}
7878
else
7879
{
7880
oss << show_char(c);
7881
}
7882
}
7883
oss << '\n';
7884
return oss;
7885
}
7886
TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss,
7887
const std::size_t lnw, const std::size_t col, const std::size_t len,
7888
const std::string& msg)
7889
{
7890
// | ^^^^^^^-- this part
7891
oss << make_string(lnw + 1, ' ')
7892
<< color::bold << color::blue << " | " << color::reset;
7893
7894
// in case col is 0, so we don't create a string with size_t max length
7895
const std::size_t sanitized_col = col == 0 ? 0 : col - 1 /*1-origin*/;
7896
oss << make_string(sanitized_col, ' ')
7897
<< color::bold << color::red
7898
<< make_string(len, '^') << "-- "
7899
<< color::reset << msg << '\n';
7900
7901
return oss;
7902
}
7903
7904
TOML11_INLINE std::string format_location_impl(const std::size_t lnw,
7905
const std::string& prev_fname,
7906
const source_location& loc, const std::string& msg)
7907
{
7908
std::ostringstream oss;
7909
7910
if(loc.file_name() != prev_fname)
7911
{
7912
format_filename(oss, loc);
7913
if( ! loc.lines().empty())
7914
{
7915
format_empty_line(oss, lnw);
7916
}
7917
}
7918
7919
if(loc.lines().size() == 1)
7920
{
7921
// when column points LF, it exceeds the size of the first line.
7922
std::size_t underline_limit = 1;
7923
if(loc.first_line().size() < loc.first_column_offset())
7924
{
7925
underline_limit = 1;
7926
}
7927
else
7928
{
7929
underline_limit = loc.first_line().size() - loc.first_column_offset() + 1;
7930
}
7931
const auto underline_len = (std::min)(underline_limit, loc.length());
7932
7933
format_line(oss, lnw, loc.first_line_number(), loc.first_line());
7934
format_underline(oss, lnw, loc.first_column_offset(), underline_len, msg);
7935
}
7936
else if(loc.lines().size() == 2)
7937
{
7938
const auto first_underline_len =
7939
loc.first_line().size() - loc.first_column_offset() + 1;
7940
format_line(oss, lnw, loc.first_line_number(), loc.first_line());
7941
format_underline(oss, lnw, loc.first_column_offset(),
7942
first_underline_len, "");
7943
7944
format_line(oss, lnw, loc.last_line_number(), loc.last_line());
7945
format_underline(oss, lnw, 1, loc.last_column_offset(), msg);
7946
}
7947
else if(loc.lines().size() > 2)
7948
{
7949
const auto first_underline_len =
7950
loc.first_line().size() - loc.first_column_offset() + 1;
7951
format_line(oss, lnw, loc.first_line_number(), loc.first_line());
7952
format_underline(oss, lnw, loc.first_column_offset(),
7953
first_underline_len, "and");
7954
7955
if(loc.lines().size() == 3)
7956
{
7957
format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1));
7958
format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and");
7959
}
7960
else
7961
{
7962
format_line(oss, lnw, loc.first_line_number()+1, " ...");
7963
format_empty_line(oss, lnw);
7964
}
7965
format_line(oss, lnw, loc.last_line_number(), loc.last_line());
7966
format_underline(oss, lnw, 1, loc.last_column_offset(), msg);
7967
}
7968
// if loc is empty, do nothing.
7969
return oss.str();
7970
}
7971
7972
} // namespace detail
7973
} // TOML11_INLINE_VERSION_NAMESPACE
7974
} // toml
7975
#endif // TOML11_SOURCE_LOCATION_IMPL_HPP
7976
#endif
7977
7978
#endif // TOML11_SOURCE_LOCATION_HPP
7979
#ifndef TOML11_ERROR_INFO_HPP
7980
#define TOML11_ERROR_INFO_HPP
7981
7982
#ifndef TOML11_ERROR_INFO_FWD_HPP
7983
#define TOML11_ERROR_INFO_FWD_HPP
7984
7985
7986
namespace toml
7987
{
7988
inline namespace TOML11_INLINE_VERSION_NAMESPACE
7989
{
7990
7991
// error info returned from parser.
7992
struct error_info
7993
{
7994
error_info(std::string t, source_location l, std::string m, std::string s = "")
7995
: title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))},
7996
suffix_(std::move(s))
7997
{}
7998
7999
error_info(std::string t, std::vector<std::pair<source_location, std::string>> l,
8000
std::string s = "")
8001
: title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s))
8002
{}
8003
8004
std::string const& title() const noexcept {return title_;}
8005
std::string & title() noexcept {return title_;}
8006
8007
std::vector<std::pair<source_location, std::string>> const&
8008
locations() const noexcept {return locations_;}
8009
8010
void add_locations(source_location loc, std::string msg) noexcept
8011
{
8012
locations_.emplace_back(std::move(loc), std::move(msg));
8013
}
8014
8015
std::string const& suffix() const noexcept {return suffix_;}
8016
std::string & suffix() noexcept {return suffix_;}
8017
8018
private:
8019
8020
std::string title_;
8021
std::vector<std::pair<source_location, std::string>> locations_;
8022
std::string suffix_; // hint or something like that
8023
};
8024
8025
// forward decl
8026
template<typename TypeConfig>
8027
class basic_value;
8028
8029
namespace detail
8030
{
8031
inline error_info make_error_info_rec(error_info e)
8032
{
8033
return e;
8034
}
8035
inline error_info make_error_info_rec(error_info e, std::string s)
8036
{
8037
e.suffix() = s;
8038
return e;
8039
}
8040
8041
template<typename TC, typename ... Ts>
8042
error_info make_error_info_rec(error_info e,
8043
const basic_value<TC>& v, std::string msg, Ts&& ... tail);
8044
8045
template<typename ... Ts>
8046
error_info make_error_info_rec(error_info e,
8047
source_location loc, std::string msg, Ts&& ... tail)
8048
{
8049
e.add_locations(std::move(loc), std::move(msg));
8050
return make_error_info_rec(std::move(e), std::forward<Ts>(tail)...);
8051
}
8052
8053
} // detail
8054
8055
template<typename ... Ts>
8056
error_info make_error_info(
8057
std::string title, source_location loc, std::string msg, Ts&& ... tail)
8058
{
8059
error_info ei(std::move(title), std::move(loc), std::move(msg));
8060
return detail::make_error_info_rec(ei, std::forward<Ts>(tail) ... );
8061
}
8062
8063
std::string format_error(const std::string& errkind, const error_info& err);
8064
std::string format_error(const error_info& err);
8065
8066
// for custom error message
8067
template<typename ... Ts>
8068
std::string format_error(std::string title,
8069
source_location loc, std::string msg, Ts&& ... tail)
8070
{
8071
return format_error("", make_error_info(std::move(title),
8072
std::move(loc), std::move(msg), std::forward<Ts>(tail)...));
8073
}
8074
8075
std::ostream& operator<<(std::ostream& os, const error_info& e);
8076
8077
} // TOML11_INLINE_VERSION_NAMESPACE
8078
} // toml
8079
#endif // TOML11_ERROR_INFO_FWD_HPP
8080
8081
#if ! defined(TOML11_COMPILE_SOURCES)
8082
#ifndef TOML11_ERROR_INFO_IMPL_HPP
8083
#define TOML11_ERROR_INFO_IMPL_HPP
8084
8085
8086
#include <sstream>
8087
8088
namespace toml
8089
{
8090
inline namespace TOML11_INLINE_VERSION_NAMESPACE
8091
{
8092
8093
TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err)
8094
{
8095
std::string errmsg;
8096
if( ! errkind.empty())
8097
{
8098
errmsg = errkind;
8099
errmsg += ' ';
8100
}
8101
errmsg += err.title();
8102
errmsg += '\n';
8103
8104
const auto lnw = [&err]() {
8105
std::size_t width = 0;
8106
for(const auto& l : err.locations())
8107
{
8108
width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width);
8109
}
8110
return width;
8111
}();
8112
8113
bool first = true;
8114
std::string prev_fname;
8115
for(const auto& lm : err.locations())
8116
{
8117
if( ! first)
8118
{
8119
std::ostringstream oss;
8120
oss << detail::make_string(lnw + 1, ' ')
8121
<< color::bold << color::blue << " |" << color::reset
8122
<< color::bold << " ...\n" << color::reset;
8123
oss << detail::make_string(lnw + 1, ' ')
8124
<< color::bold << color::blue << " |\n" << color::reset;
8125
errmsg += oss.str();
8126
}
8127
8128
const auto& l = lm.first;
8129
const auto& m = lm.second;
8130
8131
errmsg += detail::format_location_impl(lnw, prev_fname, l, m);
8132
8133
prev_fname = l.file_name();
8134
first = false;
8135
}
8136
8137
errmsg += err.suffix();
8138
8139
return errmsg;
8140
}
8141
8142
TOML11_INLINE std::string format_error(const error_info& err)
8143
{
8144
std::ostringstream oss;
8145
oss << color::red << color::bold << "[error]" << color::reset;
8146
return format_error(oss.str(), err);
8147
}
8148
8149
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e)
8150
{
8151
os << format_error(e);
8152
return os;
8153
}
8154
8155
} // TOML11_INLINE_VERSION_NAMESPACE
8156
} // toml
8157
#endif // TOML11_ERROR_INFO_IMPL_HPP
8158
#endif
8159
8160
#endif // TOML11_ERROR_INFO_HPP
8161
#ifndef TOML11_VALUE_HPP
8162
#define TOML11_VALUE_HPP
8163
8164
8165
#ifdef TOML11_HAS_STRING_VIEW
8166
#include <string_view>
8167
#endif
8168
8169
#ifdef TOML11_ENABLE_ACCESS_CHECK
8170
#include <atomic>
8171
#endif
8172
8173
#include <cassert>
8174
8175
namespace toml
8176
{
8177
inline namespace TOML11_INLINE_VERSION_NAMESPACE
8178
{
8179
template<typename TypeConfig>
8180
class basic_value;
8181
8182
struct type_error final : public ::toml::exception
8183
{
8184
public:
8185
type_error(std::string what_arg, source_location loc)
8186
: what_(std::move(what_arg)), loc_(std::move(loc))
8187
{}
8188
~type_error() noexcept override = default;
8189
8190
const char* what() const noexcept override {return what_.c_str();}
8191
8192
source_location const& location() const noexcept {return loc_;}
8193
8194
private:
8195
std::string what_;
8196
source_location loc_;
8197
};
8198
8199
// only for internal use
8200
namespace detail
8201
{
8202
template<typename TC>
8203
error_info make_type_error(const basic_value<TC>&, const std::string&, const value_t);
8204
8205
template<typename TC>
8206
error_info make_not_found_error(const basic_value<TC>&, const std::string&, const typename basic_value<TC>::key_type&);
8207
8208
template<typename TC>
8209
void change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
8210
8211
template<typename TC, value_t V>
8212
struct getter;
8213
8214
#ifdef TOML11_ENABLE_ACCESS_CHECK
8215
template<typename TC>
8216
void unset_access_flag(basic_value<TC>&);
8217
#endif
8218
} // detail
8219
8220
template<typename TypeConfig>
8221
class basic_value
8222
{
8223
public:
8224
8225
using config_type = TypeConfig;
8226
using key_type = typename config_type::string_type;
8227
using value_type = basic_value<config_type>;
8228
using boolean_type = typename config_type::boolean_type;
8229
using integer_type = typename config_type::integer_type;
8230
using floating_type = typename config_type::floating_type;
8231
using string_type = typename config_type::string_type;
8232
using local_time_type = ::toml::local_time;
8233
using local_date_type = ::toml::local_date;
8234
using local_datetime_type = ::toml::local_datetime;
8235
using offset_datetime_type = ::toml::offset_datetime;
8236
using array_type = typename config_type::template array_type<value_type>;
8237
using table_type = typename config_type::template table_type<key_type, value_type>;
8238
using comment_type = typename config_type::comment_type;
8239
using char_type = typename string_type::value_type;
8240
8241
private:
8242
8243
using region_type = detail::region;
8244
8245
public:
8246
8247
basic_value() noexcept
8248
: type_(value_t::empty), empty_('\0'), region_{}, comments_{}
8249
#ifdef TOML11_ENABLE_ACCESS_CHECK
8250
, accessed_{false}
8251
#endif
8252
{}
8253
~basic_value() noexcept {this->cleanup();}
8254
8255
// copy/move constructor/assigner ===================================== {{{
8256
8257
basic_value(const basic_value& v)
8258
: type_(v.type_), region_(v.region_), comments_(v.comments_)
8259
#ifdef TOML11_ENABLE_ACCESS_CHECK
8260
, accessed_{v.accessed()}
8261
#endif
8262
{
8263
switch(this->type_)
8264
{
8265
case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
8266
case value_t::integer : assigner(integer_ , v.integer_ ); break;
8267
case value_t::floating : assigner(floating_ , v.floating_ ); break;
8268
case value_t::string : assigner(string_ , v.string_ ); break;
8269
case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
8270
case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
8271
case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
8272
case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
8273
case value_t::array : assigner(array_ , v.array_ ); break;
8274
case value_t::table : assigner(table_ , v.table_ ); break;
8275
default : assigner(empty_ , '\0' ); break;
8276
}
8277
}
8278
basic_value(basic_value&& v)
8279
: type_(v.type()), region_(std::move(v.region_)),
8280
comments_(std::move(v.comments_))
8281
#ifdef TOML11_ENABLE_ACCESS_CHECK
8282
, accessed_{v.accessed()}
8283
#endif
8284
{
8285
switch(this->type_)
8286
{
8287
case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break;
8288
case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break;
8289
case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break;
8290
case value_t::string : assigner(string_ , std::move(v.string_ )); break;
8291
case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break;
8292
case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break;
8293
case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break;
8294
case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break;
8295
case value_t::array : assigner(array_ , std::move(v.array_ )); break;
8296
case value_t::table : assigner(table_ , std::move(v.table_ )); break;
8297
default : assigner(empty_ , '\0' ); break;
8298
}
8299
}
8300
8301
basic_value& operator=(const basic_value& v)
8302
{
8303
if(this == std::addressof(v)) {return *this;}
8304
8305
this->cleanup();
8306
this->type_ = v.type_;
8307
this->region_ = v.region_;
8308
this->comments_ = v.comments_;
8309
#ifdef TOML11_ENABLE_ACCESS_CHECK
8310
this->accessed_ = v.accessed();
8311
#endif
8312
switch(this->type_)
8313
{
8314
case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
8315
case value_t::integer : assigner(integer_ , v.integer_ ); break;
8316
case value_t::floating : assigner(floating_ , v.floating_ ); break;
8317
case value_t::string : assigner(string_ , v.string_ ); break;
8318
case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
8319
case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
8320
case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
8321
case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
8322
case value_t::array : assigner(array_ , v.array_ ); break;
8323
case value_t::table : assigner(table_ , v.table_ ); break;
8324
default : assigner(empty_ , '\0' ); break;
8325
}
8326
return *this;
8327
}
8328
basic_value& operator=(basic_value&& v)
8329
{
8330
if(this == std::addressof(v)) {return *this;}
8331
8332
this->cleanup();
8333
this->type_ = v.type_;
8334
this->region_ = std::move(v.region_);
8335
this->comments_ = std::move(v.comments_);
8336
#ifdef TOML11_ENABLE_ACCESS_CHECK
8337
this->accessed_ = v.accessed();
8338
#endif
8339
switch(this->type_)
8340
{
8341
case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break;
8342
case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break;
8343
case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break;
8344
case value_t::string : assigner(string_ , std::move(v.string_ )); break;
8345
case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break;
8346
case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break;
8347
case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break;
8348
case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break;
8349
case value_t::array : assigner(array_ , std::move(v.array_ )); break;
8350
case value_t::table : assigner(table_ , std::move(v.table_ )); break;
8351
default : assigner(empty_ , '\0' ); break;
8352
}
8353
return *this;
8354
}
8355
// }}}
8356
8357
// constructor to overwrite commnets ================================== {{{
8358
8359
basic_value(basic_value v, std::vector<std::string> com)
8360
: type_(v.type()), region_(std::move(v.region_)),
8361
comments_(std::move(com))
8362
#ifdef TOML11_ENABLE_ACCESS_CHECK
8363
, accessed_{v.accessed()}
8364
#endif
8365
{
8366
switch(this->type_)
8367
{
8368
case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break;
8369
case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break;
8370
case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break;
8371
case value_t::string : assigner(string_ , std::move(v.string_ )); break;
8372
case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break;
8373
case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break;
8374
case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break;
8375
case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break;
8376
case value_t::array : assigner(array_ , std::move(v.array_ )); break;
8377
case value_t::table : assigner(table_ , std::move(v.table_ )); break;
8378
default : assigner(empty_ , '\0' ); break;
8379
}
8380
}
8381
// }}}
8382
8383
// conversion between different basic_values ========================== {{{
8384
8385
template<typename TI>
8386
basic_value(basic_value<TI> other)
8387
: type_(other.type_),
8388
region_(std::move(other.region_)),
8389
comments_(std::move(other.comments_))
8390
#ifdef TOML11_ENABLE_ACCESS_CHECK
8391
, accessed_{other.accessed()}
8392
#endif
8393
{
8394
switch(other.type_)
8395
{
8396
// use auto-convert in constructor
8397
case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break;
8398
case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break;
8399
case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break;
8400
case value_t::string : assigner(string_ , std::move(other.string_ )); break;
8401
case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break;
8402
case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break;
8403
case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break;
8404
case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break;
8405
8406
// may have different container type
8407
case value_t::array :
8408
{
8409
array_type tmp(
8410
std::make_move_iterator(other.array_.value.get().begin()),
8411
std::make_move_iterator(other.array_.value.get().end()));
8412
assigner(array_, array_storage(
8413
detail::storage<array_type>(std::move(tmp)),
8414
other.array_.format
8415
));
8416
break;
8417
}
8418
case value_t::table :
8419
{
8420
table_type tmp(
8421
std::make_move_iterator(other.table_.value.get().begin()),
8422
std::make_move_iterator(other.table_.value.get().end()));
8423
assigner(table_, table_storage(
8424
detail::storage<table_type>(std::move(tmp)),
8425
other.table_.format
8426
));
8427
break;
8428
}
8429
default: break;
8430
}
8431
}
8432
8433
template<typename TI>
8434
basic_value(basic_value<TI> other, std::vector<std::string> com)
8435
: type_(other.type_),
8436
region_(std::move(other.region_)),
8437
comments_(std::move(com))
8438
#ifdef TOML11_ENABLE_ACCESS_CHECK
8439
, accessed_{other.accessed()}
8440
#endif
8441
{
8442
switch(other.type_)
8443
{
8444
// use auto-convert in constructor
8445
case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break;
8446
case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break;
8447
case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break;
8448
case value_t::string : assigner(string_ , std::move(other.string_ )); break;
8449
case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break;
8450
case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break;
8451
case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break;
8452
case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break;
8453
8454
// may have different container type
8455
case value_t::array :
8456
{
8457
array_type tmp(
8458
std::make_move_iterator(other.array_.value.get().begin()),
8459
std::make_move_iterator(other.array_.value.get().end()));
8460
assigner(array_, array_storage(
8461
detail::storage<array_type>(std::move(tmp)),
8462
other.array_.format
8463
));
8464
break;
8465
}
8466
case value_t::table :
8467
{
8468
table_type tmp(
8469
std::make_move_iterator(other.table_.value.get().begin()),
8470
std::make_move_iterator(other.table_.value.get().end()));
8471
assigner(table_, table_storage(
8472
detail::storage<table_type>(std::move(tmp)),
8473
other.table_.format
8474
));
8475
break;
8476
}
8477
default: break;
8478
}
8479
}
8480
template<typename TI>
8481
basic_value& operator=(basic_value<TI> other)
8482
{
8483
this->cleanup();
8484
this->region_ = other.region_;
8485
this->comments_ = comment_type(other.comments_);
8486
this->type_ = other.type_;
8487
#ifdef TOML11_ENABLE_ACCESS_CHECK
8488
this->accessed_ = other.accessed();
8489
#endif
8490
8491
switch(other.type_)
8492
{
8493
// use auto-convert in constructor
8494
case value_t::boolean : assigner(boolean_ , std::move(other.boolean_ )); break;
8495
case value_t::integer : assigner(integer_ , std::move(other.integer_ )); break;
8496
case value_t::floating : assigner(floating_ , std::move(other.floating_ )); break;
8497
case value_t::string : assigner(string_ , std::move(other.string_ )); break;
8498
case value_t::offset_datetime: assigner(offset_datetime_, std::move(other.offset_datetime_)); break;
8499
case value_t::local_datetime : assigner(local_datetime_ , std::move(other.local_datetime_ )); break;
8500
case value_t::local_date : assigner(local_date_ , std::move(other.local_date_ )); break;
8501
case value_t::local_time : assigner(local_time_ , std::move(other.local_time_ )); break;
8502
8503
// may have different container type
8504
case value_t::array :
8505
{
8506
array_type tmp(
8507
std::make_move_iterator(other.array_.value.get().begin()),
8508
std::make_move_iterator(other.array_.value.get().end()));
8509
assigner(array_, array_storage(
8510
detail::storage<array_type>(std::move(tmp)),
8511
other.array_.format
8512
));
8513
break;
8514
}
8515
case value_t::table :
8516
{
8517
table_type tmp(
8518
std::make_move_iterator(other.table_.value.get().begin()),
8519
std::make_move_iterator(other.table_.value.get().end()));
8520
assigner(table_, table_storage(
8521
detail::storage<table_type>(std::move(tmp)),
8522
other.table_.format
8523
));
8524
break;
8525
}
8526
default: break;
8527
}
8528
return *this;
8529
}
8530
// }}}
8531
8532
// constructor (boolean) ============================================== {{{
8533
8534
basic_value(boolean_type x)
8535
: basic_value(x, boolean_format_info{}, std::vector<std::string>{}, region_type{})
8536
{}
8537
basic_value(boolean_type x, boolean_format_info fmt)
8538
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
8539
{}
8540
basic_value(boolean_type x, std::vector<std::string> com)
8541
: basic_value(x, boolean_format_info{}, std::move(com), region_type{})
8542
{}
8543
basic_value(boolean_type x, boolean_format_info fmt, std::vector<std::string> com)
8544
: basic_value(x, fmt, std::move(com), region_type{})
8545
{}
8546
basic_value(boolean_type x, boolean_format_info fmt,
8547
std::vector<std::string> com, region_type reg)
8548
: type_(value_t::boolean), boolean_(boolean_storage(x, fmt)),
8549
region_(std::move(reg)), comments_(std::move(com))
8550
#ifdef TOML11_ENABLE_ACCESS_CHECK
8551
, accessed_{false}
8552
#endif
8553
{}
8554
basic_value& operator=(boolean_type x)
8555
{
8556
boolean_format_info fmt;
8557
if(this->is_boolean())
8558
{
8559
fmt = this->as_boolean_fmt();
8560
}
8561
this->cleanup();
8562
this->type_ = value_t::boolean;
8563
this->region_ = region_type{};
8564
#ifdef TOML11_ENABLE_ACCESS_CHECK
8565
this->accessed_ = false;
8566
#endif
8567
assigner(this->boolean_, boolean_storage(x, fmt));
8568
return *this;
8569
}
8570
8571
// }}}
8572
8573
// constructor (integer) ============================================== {{{
8574
8575
basic_value(integer_type x)
8576
: basic_value(std::move(x), integer_format_info{}, std::vector<std::string>{}, region_type{})
8577
{}
8578
basic_value(integer_type x, integer_format_info fmt)
8579
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
8580
{}
8581
basic_value(integer_type x, std::vector<std::string> com)
8582
: basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{})
8583
{}
8584
basic_value(integer_type x, integer_format_info fmt, std::vector<std::string> com)
8585
: basic_value(std::move(x), std::move(fmt), std::move(com), region_type{})
8586
{}
8587
basic_value(integer_type x, integer_format_info fmt, std::vector<std::string> com, region_type reg)
8588
: type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))),
8589
region_(std::move(reg)), comments_(std::move(com))
8590
#ifdef TOML11_ENABLE_ACCESS_CHECK
8591
, accessed_{false}
8592
#endif
8593
{}
8594
basic_value& operator=(integer_type x)
8595
{
8596
integer_format_info fmt;
8597
if(this->is_integer())
8598
{
8599
fmt = this->as_integer_fmt();
8600
}
8601
this->cleanup();
8602
this->type_ = value_t::integer;
8603
this->region_ = region_type{};
8604
#ifdef TOML11_ENABLE_ACCESS_CHECK
8605
this->accessed_ = false;
8606
#endif
8607
assigner(this->integer_, integer_storage(std::move(x), std::move(fmt)));
8608
return *this;
8609
}
8610
8611
private:
8612
8613
template<typename T>
8614
using enable_if_integer_like_t = cxx::enable_if_t<cxx::conjunction<
8615
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, boolean_type>>,
8616
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, integer_type>>,
8617
std::is_integral<cxx::remove_cvref_t<T>>
8618
>::value, std::nullptr_t>;
8619
8620
public:
8621
8622
template<typename T, enable_if_integer_like_t<T> = nullptr>
8623
basic_value(T x)
8624
: basic_value(std::move(x), integer_format_info{}, std::vector<std::string>{}, region_type{})
8625
{}
8626
template<typename T, enable_if_integer_like_t<T> = nullptr>
8627
basic_value(T x, integer_format_info fmt)
8628
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
8629
{}
8630
template<typename T, enable_if_integer_like_t<T> = nullptr>
8631
basic_value(T x, std::vector<std::string> com)
8632
: basic_value(std::move(x), integer_format_info{}, std::move(com), region_type{})
8633
{}
8634
template<typename T, enable_if_integer_like_t<T> = nullptr>
8635
basic_value(T x, integer_format_info fmt, std::vector<std::string> com)
8636
: basic_value(std::move(x), std::move(fmt), std::move(com), region_type{})
8637
{}
8638
template<typename T, enable_if_integer_like_t<T> = nullptr>
8639
basic_value(T x, integer_format_info fmt, std::vector<std::string> com, region_type reg)
8640
: type_(value_t::integer), integer_(integer_storage(std::move(x), std::move(fmt))),
8641
region_(std::move(reg)), comments_(std::move(com))
8642
#ifdef TOML11_ENABLE_ACCESS_CHECK
8643
, accessed_{false}
8644
#endif
8645
{}
8646
template<typename T, enable_if_integer_like_t<T> = nullptr>
8647
basic_value& operator=(T x)
8648
{
8649
integer_format_info fmt;
8650
if(this->is_integer())
8651
{
8652
fmt = this->as_integer_fmt();
8653
}
8654
this->cleanup();
8655
this->type_ = value_t::integer;
8656
this->region_ = region_type{};
8657
#ifdef TOML11_ENABLE_ACCESS_CHECK
8658
this->accessed_ = false;
8659
#endif
8660
assigner(this->integer_, integer_storage(x, std::move(fmt)));
8661
return *this;
8662
}
8663
8664
// }}}
8665
8666
// constructor (floating) ============================================= {{{
8667
8668
basic_value(floating_type x)
8669
: basic_value(std::move(x), floating_format_info{}, std::vector<std::string>{}, region_type{})
8670
{}
8671
basic_value(floating_type x, floating_format_info fmt)
8672
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
8673
{}
8674
basic_value(floating_type x, std::vector<std::string> com)
8675
: basic_value(std::move(x), floating_format_info{}, std::move(com), region_type{})
8676
{}
8677
basic_value(floating_type x, floating_format_info fmt, std::vector<std::string> com)
8678
: basic_value(std::move(x), std::move(fmt), std::move(com), region_type{})
8679
{}
8680
basic_value(floating_type x, floating_format_info fmt, std::vector<std::string> com, region_type reg)
8681
: type_(value_t::floating), floating_(floating_storage(std::move(x), std::move(fmt))),
8682
region_(std::move(reg)), comments_(std::move(com))
8683
#ifdef TOML11_ENABLE_ACCESS_CHECK
8684
, accessed_{false}
8685
#endif
8686
{}
8687
basic_value& operator=(floating_type x)
8688
{
8689
floating_format_info fmt;
8690
if(this->is_floating())
8691
{
8692
fmt = this->as_floating_fmt();
8693
}
8694
this->cleanup();
8695
this->type_ = value_t::floating;
8696
this->region_ = region_type{};
8697
#ifdef TOML11_ENABLE_ACCESS_CHECK
8698
this->accessed_ = false;
8699
#endif
8700
assigner(this->floating_, floating_storage(std::move(x), std::move(fmt)));
8701
return *this;
8702
}
8703
8704
private:
8705
8706
template<typename T>
8707
using enable_if_floating_like_t = cxx::enable_if_t<cxx::conjunction<
8708
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, floating_type>>,
8709
std::is_floating_point<cxx::remove_cvref_t<T>>
8710
>::value, std::nullptr_t>;
8711
8712
public:
8713
8714
template<typename T, enable_if_floating_like_t<T> = nullptr>
8715
basic_value(T x)
8716
: basic_value(x, floating_format_info{}, std::vector<std::string>{}, region_type{})
8717
{}
8718
8719
template<typename T, enable_if_floating_like_t<T> = nullptr>
8720
basic_value(T x, floating_format_info fmt)
8721
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
8722
{}
8723
8724
template<typename T, enable_if_floating_like_t<T> = nullptr>
8725
basic_value(T x, std::vector<std::string> com)
8726
: basic_value(x, floating_format_info{}, std::move(com), region_type{})
8727
{}
8728
8729
template<typename T, enable_if_floating_like_t<T> = nullptr>
8730
basic_value(T x, floating_format_info fmt, std::vector<std::string> com)
8731
: basic_value(x, std::move(fmt), std::move(com), region_type{})
8732
{}
8733
8734
template<typename T, enable_if_floating_like_t<T> = nullptr>
8735
basic_value(T x, floating_format_info fmt, std::vector<std::string> com, region_type reg)
8736
: type_(value_t::floating), floating_(floating_storage(x, std::move(fmt))),
8737
region_(std::move(reg)), comments_(std::move(com))
8738
#ifdef TOML11_ENABLE_ACCESS_CHECK
8739
, accessed_{false}
8740
#endif
8741
{}
8742
8743
template<typename T, enable_if_floating_like_t<T> = nullptr>
8744
basic_value& operator=(T x)
8745
{
8746
floating_format_info fmt;
8747
if(this->is_floating())
8748
{
8749
fmt = this->as_floating_fmt();
8750
}
8751
this->cleanup();
8752
this->type_ = value_t::floating;
8753
this->region_ = region_type{};
8754
#ifdef TOML11_ENABLE_ACCESS_CHECK
8755
this->accessed_ = false;
8756
#endif
8757
assigner(this->floating_, floating_storage(x, std::move(fmt)));
8758
return *this;
8759
}
8760
8761
// }}}
8762
8763
// constructor (string) =============================================== {{{
8764
8765
basic_value(string_type x)
8766
: basic_value(std::move(x), string_format_info{}, std::vector<std::string>{}, region_type{})
8767
{}
8768
basic_value(string_type x, string_format_info fmt)
8769
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
8770
{}
8771
basic_value(string_type x, std::vector<std::string> com)
8772
: basic_value(std::move(x), string_format_info{}, std::move(com), region_type{})
8773
{}
8774
basic_value(string_type x, string_format_info fmt, std::vector<std::string> com)
8775
: basic_value(std::move(x), std::move(fmt), std::move(com), region_type{})
8776
{}
8777
basic_value(string_type x, string_format_info fmt,
8778
std::vector<std::string> com, region_type reg)
8779
: type_(value_t::string), string_(string_storage(std::move(x), std::move(fmt))),
8780
region_(std::move(reg)), comments_(std::move(com))
8781
#ifdef TOML11_ENABLE_ACCESS_CHECK
8782
, accessed_{false}
8783
#endif
8784
{}
8785
basic_value& operator=(string_type x)
8786
{
8787
string_format_info fmt;
8788
if(this->is_string())
8789
{
8790
fmt = this->as_string_fmt();
8791
}
8792
this->cleanup();
8793
this->type_ = value_t::string;
8794
this->region_ = region_type{};
8795
#ifdef TOML11_ENABLE_ACCESS_CHECK
8796
this->accessed_ = false;
8797
#endif
8798
assigner(this->string_, string_storage(x, std::move(fmt)));
8799
return *this;
8800
}
8801
8802
// "string literal"
8803
8804
basic_value(const typename string_type::value_type* x)
8805
: basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{})
8806
{}
8807
basic_value(const typename string_type::value_type* x, string_format_info fmt)
8808
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
8809
{}
8810
basic_value(const typename string_type::value_type* x, std::vector<std::string> com)
8811
: basic_value(x, string_format_info{}, std::move(com), region_type{})
8812
{}
8813
basic_value(const typename string_type::value_type* x, string_format_info fmt, std::vector<std::string> com)
8814
: basic_value(x, std::move(fmt), std::move(com), region_type{})
8815
{}
8816
basic_value(const typename string_type::value_type* x, string_format_info fmt,
8817
std::vector<std::string> com, region_type reg)
8818
: type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))),
8819
region_(std::move(reg)), comments_(std::move(com))
8820
#ifdef TOML11_ENABLE_ACCESS_CHECK
8821
, accessed_{false}
8822
#endif
8823
{}
8824
basic_value& operator=(const typename string_type::value_type* x)
8825
{
8826
string_format_info fmt;
8827
if(this->is_string())
8828
{
8829
fmt = this->as_string_fmt();
8830
}
8831
this->cleanup();
8832
this->type_ = value_t::string;
8833
this->region_ = region_type{};
8834
#ifdef TOML11_ENABLE_ACCESS_CHECK
8835
this->accessed_ = false;
8836
#endif
8837
assigner(this->string_, string_storage(string_type(x), std::move(fmt)));
8838
return *this;
8839
}
8840
8841
#if defined(TOML11_HAS_STRING_VIEW)
8842
using string_view_type = std::basic_string_view<
8843
typename string_type::value_type, typename string_type::traits_type>;
8844
8845
basic_value(string_view_type x)
8846
: basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{})
8847
{}
8848
basic_value(string_view_type x, string_format_info fmt)
8849
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
8850
{}
8851
basic_value(string_view_type x, std::vector<std::string> com)
8852
: basic_value(x, string_format_info{}, std::move(com), region_type{})
8853
{}
8854
basic_value(string_view_type x, string_format_info fmt, std::vector<std::string> com)
8855
: basic_value(x, std::move(fmt), std::move(com), region_type{})
8856
{}
8857
basic_value(string_view_type x, string_format_info fmt,
8858
std::vector<std::string> com, region_type reg)
8859
: type_(value_t::string), string_(string_storage(string_type(x), std::move(fmt))),
8860
region_(std::move(reg)), comments_(std::move(com))
8861
#ifdef TOML11_ENABLE_ACCESS_CHECK
8862
, accessed_{false}
8863
#endif
8864
{}
8865
basic_value& operator=(string_view_type x)
8866
{
8867
string_format_info fmt;
8868
if(this->is_string())
8869
{
8870
fmt = this->as_string_fmt();
8871
}
8872
this->cleanup();
8873
this->type_ = value_t::string;
8874
this->region_ = region_type{};
8875
#ifdef TOML11_ENABLE_ACCESS_CHECK
8876
this->accessed_ = false;
8877
#endif
8878
assigner(this->string_, string_storage(string_type(x), std::move(fmt)));
8879
return *this;
8880
}
8881
8882
#endif // TOML11_HAS_STRING_VIEW
8883
8884
template<typename T, cxx::enable_if_t<cxx::conjunction<
8885
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
8886
detail::is_1byte_std_basic_string<T>
8887
>::value, std::nullptr_t> = nullptr>
8888
basic_value(const T& x)
8889
: basic_value(x, string_format_info{}, std::vector<std::string>{}, region_type{})
8890
{}
8891
template<typename T, cxx::enable_if_t<cxx::conjunction<
8892
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
8893
detail::is_1byte_std_basic_string<T>
8894
>::value, std::nullptr_t> = nullptr>
8895
basic_value(const T& x, string_format_info fmt)
8896
: basic_value(x, std::move(fmt), std::vector<std::string>{}, region_type{})
8897
{}
8898
template<typename T, cxx::enable_if_t<cxx::conjunction<
8899
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
8900
detail::is_1byte_std_basic_string<T>
8901
>::value, std::nullptr_t> = nullptr>
8902
basic_value(const T& x, std::vector<std::string> com)
8903
: basic_value(x, string_format_info{}, std::move(com), region_type{})
8904
{}
8905
template<typename T, cxx::enable_if_t<cxx::conjunction<
8906
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
8907
detail::is_1byte_std_basic_string<T>
8908
>::value, std::nullptr_t> = nullptr>
8909
basic_value(const T& x, string_format_info fmt, std::vector<std::string> com)
8910
: basic_value(x, std::move(fmt), std::move(com), region_type{})
8911
{}
8912
template<typename T, cxx::enable_if_t<cxx::conjunction<
8913
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
8914
detail::is_1byte_std_basic_string<T>
8915
>::value, std::nullptr_t> = nullptr>
8916
basic_value(const T& x, string_format_info fmt,
8917
std::vector<std::string> com, region_type reg)
8918
: type_(value_t::string),
8919
string_(string_storage(detail::string_conv<string_type>(x), std::move(fmt))),
8920
region_(std::move(reg)), comments_(std::move(com))
8921
#ifdef TOML11_ENABLE_ACCESS_CHECK
8922
, accessed_{false}
8923
#endif
8924
{}
8925
template<typename T, cxx::enable_if_t<cxx::conjunction<
8926
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, string_type>>,
8927
detail::is_1byte_std_basic_string<T>
8928
>::value, std::nullptr_t> = nullptr>
8929
basic_value& operator=(const T& x)
8930
{
8931
string_format_info fmt;
8932
if(this->is_string())
8933
{
8934
fmt = this->as_string_fmt();
8935
}
8936
this->cleanup();
8937
this->type_ = value_t::string;
8938
this->region_ = region_type{};
8939
#ifdef TOML11_ENABLE_ACCESS_CHECK
8940
this->accessed_ = false;
8941
#endif
8942
assigner(this->string_, string_storage(detail::string_conv<string_type>(x), std::move(fmt)));
8943
return *this;
8944
}
8945
8946
// }}}
8947
8948
// constructor (local_date) =========================================== {{{
8949
8950
basic_value(local_date_type x)
8951
: basic_value(x, local_date_format_info{}, std::vector<std::string>{}, region_type{})
8952
{}
8953
basic_value(local_date_type x, local_date_format_info fmt)
8954
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
8955
{}
8956
basic_value(local_date_type x, std::vector<std::string> com)
8957
: basic_value(x, local_date_format_info{}, std::move(com), region_type{})
8958
{}
8959
basic_value(local_date_type x, local_date_format_info fmt, std::vector<std::string> com)
8960
: basic_value(x, fmt, std::move(com), region_type{})
8961
{}
8962
basic_value(local_date_type x, local_date_format_info fmt,
8963
std::vector<std::string> com, region_type reg)
8964
: type_(value_t::local_date), local_date_(local_date_storage(x, fmt)),
8965
region_(std::move(reg)), comments_(std::move(com))
8966
#ifdef TOML11_ENABLE_ACCESS_CHECK
8967
, accessed_{false}
8968
#endif
8969
{}
8970
basic_value& operator=(local_date_type x)
8971
{
8972
local_date_format_info fmt;
8973
if(this->is_local_date())
8974
{
8975
fmt = this->as_local_date_fmt();
8976
}
8977
this->cleanup();
8978
this->type_ = value_t::local_date;
8979
this->region_ = region_type{};
8980
#ifdef TOML11_ENABLE_ACCESS_CHECK
8981
this->accessed_ = false;
8982
#endif
8983
assigner(this->local_date_, local_date_storage(x, fmt));
8984
return *this;
8985
}
8986
8987
// }}}
8988
8989
// constructor (local_time) =========================================== {{{
8990
8991
basic_value(local_time_type x)
8992
: basic_value(x, local_time_format_info{}, std::vector<std::string>{}, region_type{})
8993
{}
8994
basic_value(local_time_type x, local_time_format_info fmt)
8995
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
8996
{}
8997
basic_value(local_time_type x, std::vector<std::string> com)
8998
: basic_value(x, local_time_format_info{}, std::move(com), region_type{})
8999
{}
9000
basic_value(local_time_type x, local_time_format_info fmt, std::vector<std::string> com)
9001
: basic_value(x, fmt, std::move(com), region_type{})
9002
{}
9003
basic_value(local_time_type x, local_time_format_info fmt,
9004
std::vector<std::string> com, region_type reg)
9005
: type_(value_t::local_time), local_time_(local_time_storage(x, fmt)),
9006
region_(std::move(reg)), comments_(std::move(com))
9007
#ifdef TOML11_ENABLE_ACCESS_CHECK
9008
, accessed_{false}
9009
#endif
9010
{}
9011
basic_value& operator=(local_time_type x)
9012
{
9013
local_time_format_info fmt;
9014
if(this->is_local_time())
9015
{
9016
fmt = this->as_local_time_fmt();
9017
}
9018
this->cleanup();
9019
this->type_ = value_t::local_time;
9020
this->region_ = region_type{};
9021
#ifdef TOML11_ENABLE_ACCESS_CHECK
9022
this->accessed_ = false;
9023
#endif
9024
assigner(this->local_time_, local_time_storage(x, fmt));
9025
return *this;
9026
}
9027
9028
template<typename Rep, typename Period>
9029
basic_value(const std::chrono::duration<Rep, Period>& x)
9030
: basic_value(local_time_type(x), local_time_format_info{}, std::vector<std::string>{}, region_type{})
9031
{}
9032
template<typename Rep, typename Period>
9033
basic_value(const std::chrono::duration<Rep, Period>& x, local_time_format_info fmt)
9034
: basic_value(local_time_type(x), std::move(fmt), std::vector<std::string>{}, region_type{})
9035
{}
9036
template<typename Rep, typename Period>
9037
basic_value(const std::chrono::duration<Rep, Period>& x, std::vector<std::string> com)
9038
: basic_value(local_time_type(x), local_time_format_info{}, std::move(com), region_type{})
9039
{}
9040
template<typename Rep, typename Period>
9041
basic_value(const std::chrono::duration<Rep, Period>& x, local_time_format_info fmt, std::vector<std::string> com)
9042
: basic_value(local_time_type(x), std::move(fmt), std::move(com), region_type{})
9043
{}
9044
template<typename Rep, typename Period>
9045
basic_value(const std::chrono::duration<Rep, Period>& x,
9046
local_time_format_info fmt,
9047
std::vector<std::string> com, region_type reg)
9048
: basic_value(local_time_type(x), std::move(fmt), std::move(com), std::move(reg))
9049
{}
9050
template<typename Rep, typename Period>
9051
basic_value& operator=(const std::chrono::duration<Rep, Period>& x)
9052
{
9053
local_time_format_info fmt;
9054
if(this->is_local_time())
9055
{
9056
fmt = this->as_local_time_fmt();
9057
}
9058
this->cleanup();
9059
this->type_ = value_t::local_time;
9060
this->region_ = region_type{};
9061
#ifdef TOML11_ENABLE_ACCESS_CHECK
9062
this->accessed_ = false;
9063
#endif
9064
assigner(this->local_time_, local_time_storage(local_time_type(x), std::move(fmt)));
9065
return *this;
9066
}
9067
9068
// }}}
9069
9070
// constructor (local_datetime) =========================================== {{{
9071
9072
basic_value(local_datetime_type x)
9073
: basic_value(x, local_datetime_format_info{}, std::vector<std::string>{}, region_type{})
9074
{}
9075
basic_value(local_datetime_type x, local_datetime_format_info fmt)
9076
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
9077
{}
9078
basic_value(local_datetime_type x, std::vector<std::string> com)
9079
: basic_value(x, local_datetime_format_info{}, std::move(com), region_type{})
9080
{}
9081
basic_value(local_datetime_type x, local_datetime_format_info fmt, std::vector<std::string> com)
9082
: basic_value(x, fmt, std::move(com), region_type{})
9083
{}
9084
basic_value(local_datetime_type x, local_datetime_format_info fmt,
9085
std::vector<std::string> com, region_type reg)
9086
: type_(value_t::local_datetime), local_datetime_(local_datetime_storage(x, fmt)),
9087
region_(std::move(reg)), comments_(std::move(com))
9088
#ifdef TOML11_ENABLE_ACCESS_CHECK
9089
, accessed_{false}
9090
#endif
9091
{}
9092
basic_value& operator=(local_datetime_type x)
9093
{
9094
local_datetime_format_info fmt;
9095
if(this->is_local_datetime())
9096
{
9097
fmt = this->as_local_datetime_fmt();
9098
}
9099
this->cleanup();
9100
this->type_ = value_t::local_datetime;
9101
this->region_ = region_type{};
9102
#ifdef TOML11_ENABLE_ACCESS_CHECK
9103
this->accessed_ = false;
9104
#endif
9105
assigner(this->local_datetime_, local_datetime_storage(x, fmt));
9106
return *this;
9107
}
9108
9109
// }}}
9110
9111
// constructor (offset_datetime) =========================================== {{{
9112
9113
basic_value(offset_datetime_type x)
9114
: basic_value(x, offset_datetime_format_info{}, std::vector<std::string>{}, region_type{})
9115
{}
9116
basic_value(offset_datetime_type x, offset_datetime_format_info fmt)
9117
: basic_value(x, fmt, std::vector<std::string>{}, region_type{})
9118
{}
9119
basic_value(offset_datetime_type x, std::vector<std::string> com)
9120
: basic_value(x, offset_datetime_format_info{}, std::move(com), region_type{})
9121
{}
9122
basic_value(offset_datetime_type x, offset_datetime_format_info fmt, std::vector<std::string> com)
9123
: basic_value(x, fmt, std::move(com), region_type{})
9124
{}
9125
basic_value(offset_datetime_type x, offset_datetime_format_info fmt,
9126
std::vector<std::string> com, region_type reg)
9127
: type_(value_t::offset_datetime), offset_datetime_(offset_datetime_storage(x, fmt)),
9128
region_(std::move(reg)), comments_(std::move(com))
9129
#ifdef TOML11_ENABLE_ACCESS_CHECK
9130
, accessed_{false}
9131
#endif
9132
{}
9133
basic_value& operator=(offset_datetime_type x)
9134
{
9135
offset_datetime_format_info fmt;
9136
if(this->is_offset_datetime())
9137
{
9138
fmt = this->as_offset_datetime_fmt();
9139
}
9140
this->cleanup();
9141
this->type_ = value_t::offset_datetime;
9142
this->region_ = region_type{};
9143
#ifdef TOML11_ENABLE_ACCESS_CHECK
9144
this->accessed_ = false;
9145
#endif
9146
assigner(this->offset_datetime_, offset_datetime_storage(x, fmt));
9147
return *this;
9148
}
9149
9150
// system_clock::time_point
9151
9152
basic_value(std::chrono::system_clock::time_point x)
9153
: basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::vector<std::string>{}, region_type{})
9154
{}
9155
basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt)
9156
: basic_value(offset_datetime_type(x), fmt, std::vector<std::string>{}, region_type{})
9157
{}
9158
basic_value(std::chrono::system_clock::time_point x, std::vector<std::string> com)
9159
: basic_value(offset_datetime_type(x), offset_datetime_format_info{}, std::move(com), region_type{})
9160
{}
9161
basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt, std::vector<std::string> com)
9162
: basic_value(offset_datetime_type(x), fmt, std::move(com), region_type{})
9163
{}
9164
basic_value(std::chrono::system_clock::time_point x, offset_datetime_format_info fmt,
9165
std::vector<std::string> com, region_type reg)
9166
: basic_value(offset_datetime_type(x), std::move(fmt), std::move(com), std::move(reg))
9167
{}
9168
basic_value& operator=(std::chrono::system_clock::time_point x)
9169
{
9170
offset_datetime_format_info fmt;
9171
if(this->is_offset_datetime())
9172
{
9173
fmt = this->as_offset_datetime_fmt();
9174
}
9175
this->cleanup();
9176
this->type_ = value_t::offset_datetime;
9177
this->region_ = region_type{};
9178
#ifdef TOML11_ENABLE_ACCESS_CHECK
9179
this->accessed_ = false;
9180
#endif
9181
assigner(this->offset_datetime_, offset_datetime_storage(offset_datetime_type(x), fmt));
9182
return *this;
9183
}
9184
9185
// }}}
9186
9187
// constructor (array) ================================================ {{{
9188
9189
basic_value(array_type x)
9190
: basic_value(std::move(x), array_format_info{}, std::vector<std::string>{}, region_type{})
9191
{}
9192
basic_value(array_type x, array_format_info fmt)
9193
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
9194
{}
9195
basic_value(array_type x, std::vector<std::string> com)
9196
: basic_value(std::move(x), array_format_info{}, std::move(com), region_type{})
9197
{}
9198
basic_value(array_type x, array_format_info fmt, std::vector<std::string> com)
9199
: basic_value(std::move(x), fmt, std::move(com), region_type{})
9200
{}
9201
basic_value(array_type x, array_format_info fmt,
9202
std::vector<std::string> com, region_type reg)
9203
: type_(value_t::array), array_(array_storage(
9204
detail::storage<array_type>(std::move(x)), std::move(fmt)
9205
)), region_(std::move(reg)), comments_(std::move(com))
9206
#ifdef TOML11_ENABLE_ACCESS_CHECK
9207
, accessed_{false}
9208
#endif
9209
{}
9210
basic_value& operator=(array_type x)
9211
{
9212
array_format_info fmt;
9213
if(this->is_array())
9214
{
9215
fmt = this->as_array_fmt();
9216
}
9217
this->cleanup();
9218
this->type_ = value_t::array;
9219
this->region_ = region_type{};
9220
#ifdef TOML11_ENABLE_ACCESS_CHECK
9221
this->accessed_ = false;
9222
#endif
9223
assigner(this->array_, array_storage(
9224
detail::storage<array_type>(std::move(x)), std::move(fmt)));
9225
return *this;
9226
}
9227
9228
private:
9229
9230
template<typename T>
9231
using enable_if_array_like_t = cxx::enable_if_t<cxx::conjunction<
9232
detail::is_container<T>,
9233
cxx::negation<std::is_same<T, array_type>>,
9234
cxx::negation<detail::is_std_basic_string<T>>,
9235
#if defined(TOML11_HAS_STRING_VIEW)
9236
cxx::negation<detail::is_std_basic_string_view<T>>,
9237
#endif
9238
cxx::negation<detail::has_from_toml_method<T, config_type>>,
9239
cxx::negation<detail::has_specialized_from<T>>
9240
>::value, std::nullptr_t>;
9241
9242
public:
9243
9244
template<typename T, enable_if_array_like_t<T> = nullptr>
9245
basic_value(T x)
9246
: basic_value(std::move(x), array_format_info{}, std::vector<std::string>{}, region_type{})
9247
{}
9248
template<typename T, enable_if_array_like_t<T> = nullptr>
9249
basic_value(T x, array_format_info fmt)
9250
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
9251
{}
9252
template<typename T, enable_if_array_like_t<T> = nullptr>
9253
basic_value(T x, std::vector<std::string> com)
9254
: basic_value(std::move(x), array_format_info{}, std::move(com), region_type{})
9255
{}
9256
template<typename T, enable_if_array_like_t<T> = nullptr>
9257
basic_value(T x, array_format_info fmt, std::vector<std::string> com)
9258
: basic_value(std::move(x), fmt, std::move(com), region_type{})
9259
{}
9260
template<typename T, enable_if_array_like_t<T> = nullptr>
9261
basic_value(T x, array_format_info fmt,
9262
std::vector<std::string> com, region_type reg)
9263
: type_(value_t::array), array_(array_storage(
9264
detail::storage<array_type>(array_type(
9265
std::make_move_iterator(x.begin()),
9266
std::make_move_iterator(x.end()))
9267
), std::move(fmt)
9268
)), region_(std::move(reg)), comments_(std::move(com))
9269
#ifdef TOML11_ENABLE_ACCESS_CHECK
9270
, accessed_{false}
9271
#endif
9272
{}
9273
template<typename T, enable_if_array_like_t<T> = nullptr>
9274
basic_value& operator=(T x)
9275
{
9276
array_format_info fmt;
9277
if(this->is_array())
9278
{
9279
fmt = this->as_array_fmt();
9280
}
9281
this->cleanup();
9282
this->type_ = value_t::array;
9283
this->region_ = region_type{};
9284
#ifdef TOML11_ENABLE_ACCESS_CHECK
9285
this->accessed_ = false;
9286
#endif
9287
array_type a(std::make_move_iterator(x.begin()),
9288
std::make_move_iterator(x.end()));
9289
assigner(this->array_, array_storage(
9290
detail::storage<array_type>(std::move(a)), std::move(fmt)));
9291
return *this;
9292
}
9293
9294
// }}}
9295
9296
// constructor (table) ================================================ {{{
9297
9298
basic_value(table_type x)
9299
: basic_value(std::move(x), table_format_info{}, std::vector<std::string>{}, region_type{})
9300
{}
9301
basic_value(table_type x, table_format_info fmt)
9302
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
9303
{}
9304
basic_value(table_type x, std::vector<std::string> com)
9305
: basic_value(std::move(x), table_format_info{}, std::move(com), region_type{})
9306
{}
9307
basic_value(table_type x, table_format_info fmt, std::vector<std::string> com)
9308
: basic_value(std::move(x), fmt, std::move(com), region_type{})
9309
{}
9310
basic_value(table_type x, table_format_info fmt,
9311
std::vector<std::string> com, region_type reg)
9312
: type_(value_t::table), table_(table_storage(
9313
detail::storage<table_type>(std::move(x)), std::move(fmt)
9314
)), region_(std::move(reg)), comments_(std::move(com))
9315
#ifdef TOML11_ENABLE_ACCESS_CHECK
9316
, accessed_{false}
9317
#endif
9318
{}
9319
basic_value& operator=(table_type x)
9320
{
9321
table_format_info fmt;
9322
if(this->is_table())
9323
{
9324
fmt = this->as_table_fmt();
9325
}
9326
this->cleanup();
9327
this->type_ = value_t::table;
9328
this->region_ = region_type{};
9329
#ifdef TOML11_ENABLE_ACCESS_CHECK
9330
this->accessed_ = false;
9331
#endif
9332
assigner(this->table_, table_storage(
9333
detail::storage<table_type>(std::move(x)), std::move(fmt)));
9334
return *this;
9335
}
9336
9337
// table-like
9338
9339
private:
9340
9341
template<typename T>
9342
using enable_if_table_like_t = cxx::enable_if_t<cxx::conjunction<
9343
cxx::negation<std::is_same<T, table_type>>,
9344
detail::is_map<T>,
9345
cxx::negation<detail::has_from_toml_method<T, config_type>>,
9346
cxx::negation<detail::has_specialized_from<T>>
9347
>::value, std::nullptr_t>;
9348
9349
public:
9350
9351
template<typename T, enable_if_table_like_t<T> = nullptr>
9352
basic_value(T x)
9353
: basic_value(std::move(x), table_format_info{}, std::vector<std::string>{}, region_type{})
9354
{}
9355
template<typename T, enable_if_table_like_t<T> = nullptr>
9356
basic_value(T x, table_format_info fmt)
9357
: basic_value(std::move(x), std::move(fmt), std::vector<std::string>{}, region_type{})
9358
{}
9359
template<typename T, enable_if_table_like_t<T> = nullptr>
9360
basic_value(T x, std::vector<std::string> com)
9361
: basic_value(std::move(x), table_format_info{}, std::move(com), region_type{})
9362
{}
9363
template<typename T, enable_if_table_like_t<T> = nullptr>
9364
basic_value(T x, table_format_info fmt, std::vector<std::string> com)
9365
: basic_value(std::move(x), fmt, std::move(com), region_type{})
9366
{}
9367
template<typename T, enable_if_table_like_t<T> = nullptr>
9368
basic_value(T x, table_format_info fmt,
9369
std::vector<std::string> com, region_type reg)
9370
: type_(value_t::table), table_(table_storage(
9371
detail::storage<table_type>(table_type(
9372
std::make_move_iterator(x.begin()),
9373
std::make_move_iterator(x.end())
9374
)), std::move(fmt)
9375
)), region_(std::move(reg)), comments_(std::move(com))
9376
#ifdef TOML11_ENABLE_ACCESS_CHECK
9377
, accessed_{false}
9378
#endif
9379
{}
9380
template<typename T, enable_if_table_like_t<T> = nullptr>
9381
basic_value& operator=(T x)
9382
{
9383
table_format_info fmt;
9384
if(this->is_table())
9385
{
9386
fmt = this->as_table_fmt();
9387
}
9388
this->cleanup();
9389
this->type_ = value_t::table;
9390
this->region_ = region_type{};
9391
#ifdef TOML11_ENABLE_ACCESS_CHECK
9392
this->accessed_ = false;
9393
#endif
9394
table_type t(std::make_move_iterator(x.begin()),
9395
std::make_move_iterator(x.end()));
9396
assigner(this->table_, table_storage(
9397
detail::storage<table_type>(std::move(t)), std::move(fmt)));
9398
return *this;
9399
}
9400
9401
// }}}
9402
9403
// constructor (user_defined) ========================================= {{{
9404
9405
template<typename T, cxx::enable_if_t<
9406
detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr>
9407
basic_value(const T& ud)
9408
: basic_value(
9409
into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud))
9410
{}
9411
template<typename T, cxx::enable_if_t<
9412
detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr>
9413
basic_value(const T& ud, std::vector<std::string> com)
9414
: basic_value(
9415
into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud),
9416
std::move(com))
9417
{}
9418
template<typename T, cxx::enable_if_t<
9419
detail::has_specialized_into<T>::value, std::nullptr_t> = nullptr>
9420
basic_value& operator=(const T& ud)
9421
{
9422
*this = into<cxx::remove_cvref_t<T>>::template into_toml<config_type>(ud);
9423
#ifdef TOML11_ENABLE_ACCESS_CHECK
9424
this->accessed_ = false;
9425
#endif
9426
return *this;
9427
}
9428
9429
template<typename T, cxx::enable_if_t<cxx::conjunction<
9430
detail::has_into_toml_method<T>,
9431
cxx::negation<detail::has_specialized_into<T>>
9432
>::value, std::nullptr_t> = nullptr>
9433
basic_value(const T& ud): basic_value(ud.into_toml()) {}
9434
9435
template<typename T, cxx::enable_if_t<cxx::conjunction<
9436
detail::has_into_toml_method<T>,
9437
cxx::negation<detail::has_specialized_into<T>>
9438
>::value, std::nullptr_t> = nullptr>
9439
basic_value(const T& ud, std::vector<std::string> com)
9440
: basic_value(ud.into_toml(), std::move(com))
9441
{}
9442
template<typename T, cxx::enable_if_t<cxx::conjunction<
9443
detail::has_into_toml_method<T>,
9444
cxx::negation<detail::has_specialized_into<T>>
9445
>::value, std::nullptr_t> = nullptr>
9446
basic_value& operator=(const T& ud)
9447
{
9448
*this = ud.into_toml();
9449
#ifdef TOML11_ENABLE_ACCESS_CHECK
9450
this->accessed_ = false;
9451
#endif
9452
return *this;
9453
}
9454
9455
template<typename T, cxx::enable_if_t<cxx::conjunction<
9456
detail::has_template_into_toml_method<T, TypeConfig>,
9457
cxx::negation<detail::has_specialized_into<T>>
9458
>::value, std::nullptr_t> = nullptr>
9459
basic_value(const T& ud): basic_value(ud.template into_toml<TypeConfig>()) {}
9460
9461
template<typename T, cxx::enable_if_t<cxx::conjunction<
9462
detail::has_template_into_toml_method<T, TypeConfig>,
9463
cxx::negation<detail::has_specialized_into<T>>
9464
>::value, std::nullptr_t> = nullptr>
9465
basic_value(const T& ud, std::vector<std::string> com)
9466
: basic_value(ud.template into_toml<TypeConfig>(), std::move(com))
9467
{}
9468
template<typename T, cxx::enable_if_t<cxx::conjunction<
9469
detail::has_template_into_toml_method<T, TypeConfig>,
9470
cxx::negation<detail::has_specialized_into<T>>
9471
>::value, std::nullptr_t> = nullptr>
9472
basic_value& operator=(const T& ud)
9473
{
9474
*this = ud.template into_toml<TypeConfig>();
9475
#ifdef TOML11_ENABLE_ACCESS_CHECK
9476
this->accessed_ = false;
9477
#endif
9478
return *this;
9479
}
9480
// }}}
9481
9482
// empty value with region info ======================================= {{{
9483
9484
// mainly for `null` extension
9485
basic_value(detail::none_t, region_type reg) noexcept
9486
: type_(value_t::empty), empty_('\0'), region_(std::move(reg)), comments_{}
9487
#ifdef TOML11_ENABLE_ACCESS_CHECK
9488
, accessed_{false}
9489
#endif
9490
{}
9491
9492
// }}}
9493
9494
// type checking ====================================================== {{{
9495
9496
template<typename T, cxx::enable_if_t<
9497
detail::is_exact_toml_type<cxx::remove_cvref_t<T>, value_type>::value,
9498
std::nullptr_t> = nullptr>
9499
bool is() const noexcept
9500
{
9501
return this->is(detail::type_to_enum<T, value_type>::value);
9502
}
9503
bool is(value_t t) const noexcept
9504
{
9505
this->set_accessed();
9506
return t == this->type_;
9507
}
9508
9509
bool is_empty() const noexcept {return this->is(value_t::empty );}
9510
bool is_boolean() const noexcept {return this->is(value_t::boolean );}
9511
bool is_integer() const noexcept {return this->is(value_t::integer );}
9512
bool is_floating() const noexcept {return this->is(value_t::floating );}
9513
bool is_string() const noexcept {return this->is(value_t::string );}
9514
bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);}
9515
bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );}
9516
bool is_local_date() const noexcept {return this->is(value_t::local_date );}
9517
bool is_local_time() const noexcept {return this->is(value_t::local_time );}
9518
bool is_array() const noexcept {return this->is(value_t::array );}
9519
bool is_table() const noexcept {return this->is(value_t::table );}
9520
9521
bool is_array_of_tables() const noexcept
9522
{
9523
this->set_accessed();
9524
if( ! this->is_array()) {return false;}
9525
const auto& a = this->as_array(std::nothrow); // already checked.
9526
9527
// when you define [[array.of.tables]], at least one empty table will be
9528
// assigned. In case of array of inline tables, `array_of_tables = []`,
9529
// there is no reason to consider this as an array of *tables*.
9530
// So empty array is not an array-of-tables.
9531
if(a.empty()) {return false;}
9532
9533
// since toml v1.0.0 allows array of heterogeneous types, we need to
9534
// check all the elements. if any of the elements is not a table, it
9535
// is a heterogeneous array and cannot be expressed by `[[aot]]` form.
9536
for(const auto& e : a)
9537
{
9538
if( ! e.is_table()) {return false;}
9539
}
9540
return true;
9541
}
9542
9543
value_t type() const noexcept
9544
{
9545
this->set_accessed();
9546
return type_;
9547
}
9548
9549
// }}}
9550
9551
// as_xxx (noexcept) version ========================================== {{{
9552
9553
template<value_t T>
9554
detail::enum_to_type_t<T, basic_value<config_type>> const&
9555
as(const std::nothrow_t&) const noexcept
9556
{
9557
this->set_accessed();
9558
return detail::getter<config_type, T>::get_nothrow(*this);
9559
}
9560
template<value_t T>
9561
detail::enum_to_type_t<T, basic_value<config_type>>&
9562
as(const std::nothrow_t&) noexcept
9563
{
9564
this->set_accessed();
9565
return detail::getter<config_type, T>::get_nothrow(*this);
9566
}
9567
9568
boolean_type const& as_boolean (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->boolean_.value;}
9569
integer_type const& as_integer (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->integer_.value;}
9570
floating_type const& as_floating (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->floating_.value;}
9571
string_type const& as_string (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->string_.value;}
9572
offset_datetime_type const& as_offset_datetime(const std::nothrow_t&) const noexcept {this->set_accessed(); return this->offset_datetime_.value;}
9573
local_datetime_type const& as_local_datetime (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->local_datetime_.value;}
9574
local_date_type const& as_local_date (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->local_date_.value;}
9575
local_time_type const& as_local_time (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->local_time_.value;}
9576
array_type const& as_array (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->array_.value.get();}
9577
table_type const& as_table (const std::nothrow_t&) const noexcept {this->set_accessed(); return this->table_.value.get();}
9578
9579
boolean_type & as_boolean (const std::nothrow_t&) noexcept {this->set_accessed(); return this->boolean_.value;}
9580
integer_type & as_integer (const std::nothrow_t&) noexcept {this->set_accessed(); return this->integer_.value;}
9581
floating_type & as_floating (const std::nothrow_t&) noexcept {this->set_accessed(); return this->floating_.value;}
9582
string_type & as_string (const std::nothrow_t&) noexcept {this->set_accessed(); return this->string_.value;}
9583
offset_datetime_type& as_offset_datetime(const std::nothrow_t&) noexcept {this->set_accessed(); return this->offset_datetime_.value;}
9584
local_datetime_type & as_local_datetime (const std::nothrow_t&) noexcept {this->set_accessed(); return this->local_datetime_.value;}
9585
local_date_type & as_local_date (const std::nothrow_t&) noexcept {this->set_accessed(); return this->local_date_.value;}
9586
local_time_type & as_local_time (const std::nothrow_t&) noexcept {this->set_accessed(); return this->local_time_.value;}
9587
array_type & as_array (const std::nothrow_t&) noexcept {this->set_accessed(); return this->array_.value.get();}
9588
table_type & as_table (const std::nothrow_t&) noexcept {this->set_accessed(); return this->table_.value.get();}
9589
9590
// }}}
9591
9592
// as_xxx (throw) ===================================================== {{{
9593
9594
template<value_t T>
9595
detail::enum_to_type_t<T, basic_value<config_type>> const& as() const
9596
{
9597
return detail::getter<config_type, T>::get(*this);
9598
}
9599
template<value_t T>
9600
detail::enum_to_type_t<T, basic_value<config_type>>& as()
9601
{
9602
return detail::getter<config_type, T>::get(*this);
9603
}
9604
9605
boolean_type const& as_boolean() const
9606
{
9607
if(this->type_ != value_t::boolean)
9608
{
9609
this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean);
9610
}
9611
this->set_accessed();
9612
return this->boolean_.value;
9613
}
9614
integer_type const& as_integer() const
9615
{
9616
if(this->type_ != value_t::integer)
9617
{
9618
this->throw_bad_cast("toml::value::as_integer()", value_t::integer);
9619
}
9620
this->set_accessed();
9621
return this->integer_.value;
9622
}
9623
floating_type const& as_floating() const
9624
{
9625
if(this->type_ != value_t::floating)
9626
{
9627
this->throw_bad_cast("toml::value::as_floating()", value_t::floating);
9628
}
9629
this->set_accessed();
9630
return this->floating_.value;
9631
}
9632
string_type const& as_string() const
9633
{
9634
if(this->type_ != value_t::string)
9635
{
9636
this->throw_bad_cast("toml::value::as_string()", value_t::string);
9637
}
9638
this->set_accessed();
9639
return this->string_.value;
9640
}
9641
offset_datetime_type const& as_offset_datetime() const
9642
{
9643
if(this->type_ != value_t::offset_datetime)
9644
{
9645
this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime);
9646
}
9647
this->set_accessed();
9648
return this->offset_datetime_.value;
9649
}
9650
local_datetime_type const& as_local_datetime() const
9651
{
9652
if(this->type_ != value_t::local_datetime)
9653
{
9654
this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime);
9655
}
9656
this->set_accessed();
9657
return this->local_datetime_.value;
9658
}
9659
local_date_type const& as_local_date() const
9660
{
9661
if(this->type_ != value_t::local_date)
9662
{
9663
this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date);
9664
}
9665
this->set_accessed();
9666
return this->local_date_.value;
9667
}
9668
local_time_type const& as_local_time() const
9669
{
9670
if(this->type_ != value_t::local_time)
9671
{
9672
this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time);
9673
}
9674
this->set_accessed();
9675
return this->local_time_.value;
9676
}
9677
array_type const& as_array() const
9678
{
9679
if(this->type_ != value_t::array)
9680
{
9681
this->throw_bad_cast("toml::value::as_array()", value_t::array);
9682
}
9683
this->set_accessed();
9684
return this->array_.value.get();
9685
}
9686
table_type const& as_table() const
9687
{
9688
if(this->type_ != value_t::table)
9689
{
9690
this->throw_bad_cast("toml::value::as_table()", value_t::table);
9691
}
9692
this->set_accessed();
9693
return this->table_.value.get();
9694
}
9695
9696
// ------------------------------------------------------------------------
9697
// nonconst reference
9698
9699
boolean_type& as_boolean()
9700
{
9701
if(this->type_ != value_t::boolean)
9702
{
9703
this->throw_bad_cast("toml::value::as_boolean()", value_t::boolean);
9704
}
9705
this->set_accessed();
9706
return this->boolean_.value;
9707
}
9708
integer_type& as_integer()
9709
{
9710
if(this->type_ != value_t::integer)
9711
{
9712
this->throw_bad_cast("toml::value::as_integer()", value_t::integer);
9713
}
9714
this->set_accessed();
9715
return this->integer_.value;
9716
}
9717
floating_type& as_floating()
9718
{
9719
if(this->type_ != value_t::floating)
9720
{
9721
this->throw_bad_cast("toml::value::as_floating()", value_t::floating);
9722
}
9723
this->set_accessed();
9724
return this->floating_.value;
9725
}
9726
string_type& as_string()
9727
{
9728
if(this->type_ != value_t::string)
9729
{
9730
this->throw_bad_cast("toml::value::as_string()", value_t::string);
9731
}
9732
this->set_accessed();
9733
return this->string_.value;
9734
}
9735
offset_datetime_type& as_offset_datetime()
9736
{
9737
if(this->type_ != value_t::offset_datetime)
9738
{
9739
this->throw_bad_cast("toml::value::as_offset_datetime()", value_t::offset_datetime);
9740
}
9741
this->set_accessed();
9742
return this->offset_datetime_.value;
9743
}
9744
local_datetime_type& as_local_datetime()
9745
{
9746
if(this->type_ != value_t::local_datetime)
9747
{
9748
this->throw_bad_cast("toml::value::as_local_datetime()", value_t::local_datetime);
9749
}
9750
this->set_accessed();
9751
return this->local_datetime_.value;
9752
}
9753
local_date_type& as_local_date()
9754
{
9755
if(this->type_ != value_t::local_date)
9756
{
9757
this->throw_bad_cast("toml::value::as_local_date()", value_t::local_date);
9758
}
9759
this->set_accessed();
9760
return this->local_date_.value;
9761
}
9762
local_time_type& as_local_time()
9763
{
9764
if(this->type_ != value_t::local_time)
9765
{
9766
this->throw_bad_cast("toml::value::as_local_time()", value_t::local_time);
9767
}
9768
this->set_accessed();
9769
return this->local_time_.value;
9770
}
9771
array_type& as_array()
9772
{
9773
if(this->type_ != value_t::array)
9774
{
9775
this->throw_bad_cast("toml::value::as_array()", value_t::array);
9776
}
9777
this->set_accessed();
9778
return this->array_.value.get();
9779
}
9780
table_type& as_table()
9781
{
9782
if(this->type_ != value_t::table)
9783
{
9784
this->throw_bad_cast("toml::value::as_table()", value_t::table);
9785
}
9786
this->set_accessed();
9787
return this->table_.value.get();
9788
}
9789
9790
// }}}
9791
9792
// format accessors (noexcept) ======================================== {{{
9793
9794
template<value_t T>
9795
detail::enum_to_fmt_type_t<T> const&
9796
as_fmt(const std::nothrow_t&) const noexcept
9797
{
9798
return detail::getter<config_type, T>::get_fmt_nothrow(*this);
9799
}
9800
template<value_t T>
9801
detail::enum_to_fmt_type_t<T>&
9802
as_fmt(const std::nothrow_t&) noexcept
9803
{
9804
return detail::getter<config_type, T>::get_fmt_nothrow(*this);
9805
}
9806
9807
boolean_format_info & as_boolean_fmt (const std::nothrow_t&) noexcept {return this->boolean_.format;}
9808
integer_format_info & as_integer_fmt (const std::nothrow_t&) noexcept {return this->integer_.format;}
9809
floating_format_info & as_floating_fmt (const std::nothrow_t&) noexcept {return this->floating_.format;}
9810
string_format_info & as_string_fmt (const std::nothrow_t&) noexcept {return this->string_.format;}
9811
offset_datetime_format_info& as_offset_datetime_fmt(const std::nothrow_t&) noexcept {return this->offset_datetime_.format;}
9812
local_datetime_format_info & as_local_datetime_fmt (const std::nothrow_t&) noexcept {return this->local_datetime_.format;}
9813
local_date_format_info & as_local_date_fmt (const std::nothrow_t&) noexcept {return this->local_date_.format;}
9814
local_time_format_info & as_local_time_fmt (const std::nothrow_t&) noexcept {return this->local_time_.format;}
9815
array_format_info & as_array_fmt (const std::nothrow_t&) noexcept {return this->array_.format;}
9816
table_format_info & as_table_fmt (const std::nothrow_t&) noexcept {return this->table_.format;}
9817
9818
boolean_format_info const& as_boolean_fmt (const std::nothrow_t&) const noexcept {return this->boolean_.format;}
9819
integer_format_info const& as_integer_fmt (const std::nothrow_t&) const noexcept {return this->integer_.format;}
9820
floating_format_info const& as_floating_fmt (const std::nothrow_t&) const noexcept {return this->floating_.format;}
9821
string_format_info const& as_string_fmt (const std::nothrow_t&) const noexcept {return this->string_.format;}
9822
offset_datetime_format_info const& as_offset_datetime_fmt(const std::nothrow_t&) const noexcept {return this->offset_datetime_.format;}
9823
local_datetime_format_info const& as_local_datetime_fmt (const std::nothrow_t&) const noexcept {return this->local_datetime_.format;}
9824
local_date_format_info const& as_local_date_fmt (const std::nothrow_t&) const noexcept {return this->local_date_.format;}
9825
local_time_format_info const& as_local_time_fmt (const std::nothrow_t&) const noexcept {return this->local_time_.format;}
9826
array_format_info const& as_array_fmt (const std::nothrow_t&) const noexcept {return this->array_.format;}
9827
table_format_info const& as_table_fmt (const std::nothrow_t&) const noexcept {return this->table_.format;}
9828
9829
// }}}
9830
9831
// format accessors (throw) =========================================== {{{
9832
9833
template<value_t T>
9834
detail::enum_to_fmt_type_t<T> const& as_fmt() const
9835
{
9836
return detail::getter<config_type, T>::get_fmt(*this);
9837
}
9838
template<value_t T>
9839
detail::enum_to_fmt_type_t<T>& as_fmt()
9840
{
9841
return detail::getter<config_type, T>::get_fmt(*this);
9842
}
9843
9844
boolean_format_info const& as_boolean_fmt() const
9845
{
9846
if(this->type_ != value_t::boolean)
9847
{
9848
this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean);
9849
}
9850
return this->boolean_.format;
9851
}
9852
integer_format_info const& as_integer_fmt() const
9853
{
9854
if(this->type_ != value_t::integer)
9855
{
9856
this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer);
9857
}
9858
return this->integer_.format;
9859
}
9860
floating_format_info const& as_floating_fmt() const
9861
{
9862
if(this->type_ != value_t::floating)
9863
{
9864
this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating);
9865
}
9866
return this->floating_.format;
9867
}
9868
string_format_info const& as_string_fmt() const
9869
{
9870
if(this->type_ != value_t::string)
9871
{
9872
this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string);
9873
}
9874
return this->string_.format;
9875
}
9876
offset_datetime_format_info const& as_offset_datetime_fmt() const
9877
{
9878
if(this->type_ != value_t::offset_datetime)
9879
{
9880
this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime);
9881
}
9882
return this->offset_datetime_.format;
9883
}
9884
local_datetime_format_info const& as_local_datetime_fmt() const
9885
{
9886
if(this->type_ != value_t::local_datetime)
9887
{
9888
this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime);
9889
}
9890
return this->local_datetime_.format;
9891
}
9892
local_date_format_info const& as_local_date_fmt() const
9893
{
9894
if(this->type_ != value_t::local_date)
9895
{
9896
this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date);
9897
}
9898
return this->local_date_.format;
9899
}
9900
local_time_format_info const& as_local_time_fmt() const
9901
{
9902
if(this->type_ != value_t::local_time)
9903
{
9904
this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time);
9905
}
9906
return this->local_time_.format;
9907
}
9908
array_format_info const& as_array_fmt() const
9909
{
9910
if(this->type_ != value_t::array)
9911
{
9912
this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array);
9913
}
9914
return this->array_.format;
9915
}
9916
table_format_info const& as_table_fmt() const
9917
{
9918
if(this->type_ != value_t::table)
9919
{
9920
this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table);
9921
}
9922
return this->table_.format;
9923
}
9924
9925
// ------------------------------------------------------------------------
9926
// nonconst reference
9927
9928
boolean_format_info& as_boolean_fmt()
9929
{
9930
if(this->type_ != value_t::boolean)
9931
{
9932
this->throw_bad_cast("toml::value::as_boolean_fmt()", value_t::boolean);
9933
}
9934
return this->boolean_.format;
9935
}
9936
integer_format_info& as_integer_fmt()
9937
{
9938
if(this->type_ != value_t::integer)
9939
{
9940
this->throw_bad_cast("toml::value::as_integer_fmt()", value_t::integer);
9941
}
9942
return this->integer_.format;
9943
}
9944
floating_format_info& as_floating_fmt()
9945
{
9946
if(this->type_ != value_t::floating)
9947
{
9948
this->throw_bad_cast("toml::value::as_floating_fmt()", value_t::floating);
9949
}
9950
return this->floating_.format;
9951
}
9952
string_format_info& as_string_fmt()
9953
{
9954
if(this->type_ != value_t::string)
9955
{
9956
this->throw_bad_cast("toml::value::as_string_fmt()", value_t::string);
9957
}
9958
return this->string_.format;
9959
}
9960
offset_datetime_format_info& as_offset_datetime_fmt()
9961
{
9962
if(this->type_ != value_t::offset_datetime)
9963
{
9964
this->throw_bad_cast("toml::value::as_offset_datetime_fmt()", value_t::offset_datetime);
9965
}
9966
return this->offset_datetime_.format;
9967
}
9968
local_datetime_format_info& as_local_datetime_fmt()
9969
{
9970
if(this->type_ != value_t::local_datetime)
9971
{
9972
this->throw_bad_cast("toml::value::as_local_datetime_fmt()", value_t::local_datetime);
9973
}
9974
return this->local_datetime_.format;
9975
}
9976
local_date_format_info& as_local_date_fmt()
9977
{
9978
if(this->type_ != value_t::local_date)
9979
{
9980
this->throw_bad_cast("toml::value::as_local_date_fmt()", value_t::local_date);
9981
}
9982
return this->local_date_.format;
9983
}
9984
local_time_format_info& as_local_time_fmt()
9985
{
9986
if(this->type_ != value_t::local_time)
9987
{
9988
this->throw_bad_cast("toml::value::as_local_time_fmt()", value_t::local_time);
9989
}
9990
return this->local_time_.format;
9991
}
9992
array_format_info& as_array_fmt()
9993
{
9994
if(this->type_ != value_t::array)
9995
{
9996
this->throw_bad_cast("toml::value::as_array_fmt()", value_t::array);
9997
}
9998
return this->array_.format;
9999
}
10000
table_format_info& as_table_fmt()
10001
{
10002
if(this->type_ != value_t::table)
10003
{
10004
this->throw_bad_cast("toml::value::as_table_fmt()", value_t::table);
10005
}
10006
return this->table_.format;
10007
}
10008
// }}}
10009
10010
// table accessors ==================================================== {{{
10011
10012
value_type& at(const key_type& k)
10013
{
10014
if(!this->is_table())
10015
{
10016
this->throw_bad_cast("toml::value::at(key_type)", value_t::table);
10017
}
10018
auto& table = this->as_table(std::nothrow);
10019
const auto found = table.find(k);
10020
if(found == table.end())
10021
{
10022
this->throw_key_not_found_error("toml::value::at", k);
10023
}
10024
assert(found->first == k);
10025
return found->second;
10026
}
10027
value_type const& at(const key_type& k) const
10028
{
10029
if(!this->is_table())
10030
{
10031
this->throw_bad_cast("toml::value::at(key_type)", value_t::table);
10032
}
10033
const auto& table = this->as_table(std::nothrow);
10034
const auto found = table.find(k);
10035
if(found == table.end())
10036
{
10037
this->throw_key_not_found_error("toml::value::at", k);
10038
}
10039
assert(found->first == k);
10040
return found->second;
10041
}
10042
value_type& operator[](const key_type& k)
10043
{
10044
if(this->is_empty())
10045
{
10046
(*this) = table_type{};
10047
}
10048
else if( ! this->is_table()) // initialized, but not a table
10049
{
10050
this->throw_bad_cast("toml::value::operator[](key_type)", value_t::table);
10051
}
10052
return (this->as_table(std::nothrow))[k];
10053
}
10054
std::size_t count(const key_type& k) const
10055
{
10056
if(!this->is_table())
10057
{
10058
this->throw_bad_cast("toml::value::count(key_type)", value_t::table);
10059
}
10060
return this->as_table(std::nothrow).count(k);
10061
}
10062
bool contains(const key_type& k) const
10063
{
10064
if(!this->is_table())
10065
{
10066
this->throw_bad_cast("toml::value::contains(key_type)", value_t::table);
10067
}
10068
const auto& table = this->as_table(std::nothrow);
10069
return table.find(k) != table.end();
10070
}
10071
// }}}
10072
10073
// array accessors ==================================================== {{{
10074
10075
value_type& at(const std::size_t idx)
10076
{
10077
if(!this->is_array())
10078
{
10079
this->throw_bad_cast("toml::value::at(idx)", value_t::array);
10080
}
10081
auto& ar = this->as_array(std::nothrow);
10082
10083
if(ar.size() <= idx)
10084
{
10085
std::ostringstream oss;
10086
oss << "actual length (" << ar.size()
10087
<< ") is shorter than the specified index (" << idx << ").";
10088
throw std::out_of_range(format_error(
10089
"toml::value::at(idx): no element corresponding to the index",
10090
this->location(), oss.str()
10091
));
10092
}
10093
return ar.at(idx);
10094
}
10095
value_type const& at(const std::size_t idx) const
10096
{
10097
if(!this->is_array())
10098
{
10099
this->throw_bad_cast("toml::value::at(idx)", value_t::array);
10100
}
10101
const auto& ar = this->as_array(std::nothrow);
10102
10103
if(ar.size() <= idx)
10104
{
10105
std::ostringstream oss;
10106
oss << "actual length (" << ar.size()
10107
<< ") is shorter than the specified index (" << idx << ").";
10108
10109
throw std::out_of_range(format_error(
10110
"toml::value::at(idx): no element corresponding to the index",
10111
this->location(), oss.str()
10112
));
10113
}
10114
return ar.at(idx);
10115
}
10116
10117
value_type& operator[](const std::size_t idx) noexcept
10118
{
10119
// no check...
10120
return this->as_array(std::nothrow)[idx];
10121
}
10122
value_type const& operator[](const std::size_t idx) const noexcept
10123
{
10124
// no check...
10125
return this->as_array(std::nothrow)[idx];
10126
}
10127
10128
void push_back(const value_type& x)
10129
{
10130
if(!this->is_array())
10131
{
10132
this->throw_bad_cast("toml::value::push_back(idx)", value_t::array);
10133
}
10134
this->as_array(std::nothrow).push_back(x);
10135
return;
10136
}
10137
void push_back(value_type&& x)
10138
{
10139
if(!this->is_array())
10140
{
10141
this->throw_bad_cast("toml::value::push_back(idx)", value_t::array);
10142
}
10143
this->as_array(std::nothrow).push_back(std::move(x));
10144
return;
10145
}
10146
10147
template<typename ... Ts>
10148
value_type& emplace_back(Ts&& ... args)
10149
{
10150
if(!this->is_array())
10151
{
10152
this->throw_bad_cast("toml::value::emplace_back(idx)", value_t::array);
10153
}
10154
auto& ar = this->as_array(std::nothrow);
10155
ar.emplace_back(std::forward<Ts>(args) ...);
10156
return ar.back();
10157
}
10158
10159
std::size_t size() const
10160
{
10161
switch(this->type_)
10162
{
10163
case value_t::array:
10164
{
10165
return this->as_array(std::nothrow).size();
10166
}
10167
case value_t::table:
10168
{
10169
return this->as_table(std::nothrow).size();
10170
}
10171
case value_t::string:
10172
{
10173
return this->as_string(std::nothrow).size();
10174
}
10175
default:
10176
{
10177
throw type_error(format_error(
10178
"toml::value::size(): bad_cast to container types",
10179
this->location(),
10180
"the actual type is " + to_string(this->type_)
10181
), this->location());
10182
}
10183
}
10184
}
10185
10186
// }}}
10187
10188
source_location location() const
10189
{
10190
return source_location(this->region_);
10191
}
10192
10193
comment_type const& comments() const noexcept {return this->comments_;}
10194
comment_type& comments() noexcept {return this->comments_;}
10195
10196
#ifdef TOML11_ENABLE_ACCESS_CHECK
10197
bool accessed() const {return this->accessed_.load();}
10198
#endif
10199
10200
private:
10201
10202
// private helper functions =========================================== {{{
10203
10204
void set_accessed() const noexcept
10205
{
10206
#ifdef TOML11_ENABLE_ACCESS_CHECK
10207
this->accessed_.store(true);
10208
#endif
10209
return;
10210
}
10211
10212
void cleanup() noexcept
10213
{
10214
switch(this->type_)
10215
{
10216
case value_t::boolean : { boolean_ .~boolean_storage (); break; }
10217
case value_t::integer : { integer_ .~integer_storage (); break; }
10218
case value_t::floating : { floating_ .~floating_storage (); break; }
10219
case value_t::string : { string_ .~string_storage (); break; }
10220
case value_t::offset_datetime : { offset_datetime_.~offset_datetime_storage (); break; }
10221
case value_t::local_datetime : { local_datetime_ .~local_datetime_storage (); break; }
10222
case value_t::local_date : { local_date_ .~local_date_storage (); break; }
10223
case value_t::local_time : { local_time_ .~local_time_storage (); break; }
10224
case value_t::array : { array_ .~array_storage (); break; }
10225
case value_t::table : { table_ .~table_storage (); break; }
10226
default : { break; }
10227
}
10228
#ifdef TOML11_ENABLE_ACCESS_CHECK
10229
this->accessed_ = false;
10230
#endif
10231
this->type_ = value_t::empty;
10232
return;
10233
}
10234
10235
template<typename T, typename U>
10236
static void assigner(T& dst, U&& v)
10237
{
10238
const auto tmp = ::new(std::addressof(dst)) T(std::forward<U>(v));
10239
assert(tmp == std::addressof(dst));
10240
(void)tmp;
10241
}
10242
10243
[[noreturn]]
10244
void throw_bad_cast(const std::string& funcname, const value_t ty) const
10245
{
10246
throw type_error(format_error(detail::make_type_error(*this, funcname, ty)),
10247
this->location());
10248
}
10249
10250
[[noreturn]]
10251
void throw_key_not_found_error(const std::string& funcname, const key_type& key) const
10252
{
10253
throw std::out_of_range(format_error(
10254
detail::make_not_found_error(*this, funcname, key)));
10255
}
10256
10257
template<typename TC>
10258
friend void detail::change_region_of_value(basic_value<TC>&, const basic_value<TC>&);
10259
10260
template<typename TC>
10261
friend class basic_value;
10262
10263
10264
#ifdef TOML11_ENABLE_ACCESS_CHECK
10265
template<typename TC>
10266
friend void detail::unset_access_flag(basic_value<TC>&);
10267
#endif
10268
10269
// }}}
10270
10271
private:
10272
10273
using boolean_storage = detail::value_with_format<boolean_type, boolean_format_info >;
10274
using integer_storage = detail::value_with_format<integer_type, integer_format_info >;
10275
using floating_storage = detail::value_with_format<floating_type, floating_format_info >;
10276
using string_storage = detail::value_with_format<string_type, string_format_info >;
10277
using offset_datetime_storage = detail::value_with_format<offset_datetime_type, offset_datetime_format_info>;
10278
using local_datetime_storage = detail::value_with_format<local_datetime_type, local_datetime_format_info >;
10279
using local_date_storage = detail::value_with_format<local_date_type, local_date_format_info >;
10280
using local_time_storage = detail::value_with_format<local_time_type, local_time_format_info >;
10281
using array_storage = detail::value_with_format<detail::storage<array_type>, array_format_info >;
10282
using table_storage = detail::value_with_format<detail::storage<table_type>, table_format_info >;
10283
10284
private:
10285
10286
value_t type_;
10287
union
10288
{
10289
char empty_; // the smallest type
10290
boolean_storage boolean_;
10291
integer_storage integer_;
10292
floating_storage floating_;
10293
string_storage string_;
10294
offset_datetime_storage offset_datetime_;
10295
local_datetime_storage local_datetime_;
10296
local_date_storage local_date_;
10297
local_time_storage local_time_;
10298
array_storage array_;
10299
table_storage table_;
10300
};
10301
region_type region_;
10302
comment_type comments_;
10303
10304
#ifdef TOML11_ENABLE_ACCESS_CHECK
10305
mutable std::atomic<bool> accessed_;
10306
#endif
10307
};
10308
10309
template<typename TC>
10310
bool operator==(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
10311
{
10312
if(lhs.type() != rhs.type()) {return false;}
10313
if(lhs.comments() != rhs.comments()) {return false;}
10314
10315
switch(lhs.type())
10316
{
10317
case value_t::boolean :
10318
{
10319
return lhs.as_boolean() == rhs.as_boolean();
10320
}
10321
case value_t::integer :
10322
{
10323
return lhs.as_integer() == rhs.as_integer();
10324
}
10325
case value_t::floating :
10326
{
10327
return lhs.as_floating() == rhs.as_floating();
10328
}
10329
case value_t::string :
10330
{
10331
return lhs.as_string() == rhs.as_string();
10332
}
10333
case value_t::offset_datetime:
10334
{
10335
return lhs.as_offset_datetime() == rhs.as_offset_datetime();
10336
}
10337
case value_t::local_datetime:
10338
{
10339
return lhs.as_local_datetime() == rhs.as_local_datetime();
10340
}
10341
case value_t::local_date:
10342
{
10343
return lhs.as_local_date() == rhs.as_local_date();
10344
}
10345
case value_t::local_time:
10346
{
10347
return lhs.as_local_time() == rhs.as_local_time();
10348
}
10349
case value_t::array :
10350
{
10351
return lhs.as_array() == rhs.as_array();
10352
}
10353
case value_t::table :
10354
{
10355
return lhs.as_table() == rhs.as_table();
10356
}
10357
case value_t::empty : {return true; }
10358
default: {return false;}
10359
}
10360
}
10361
10362
template<typename TC>
10363
bool operator!=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
10364
{
10365
return !(lhs == rhs);
10366
}
10367
10368
template<typename TC>
10369
cxx::enable_if_t<cxx::conjunction<
10370
detail::is_comparable<typename basic_value<TC>::array_type>,
10371
detail::is_comparable<typename basic_value<TC>::table_type>
10372
>::value, bool>
10373
operator<(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
10374
{
10375
if(lhs.type() != rhs.type())
10376
{
10377
return (lhs.type() < rhs.type());
10378
}
10379
switch(lhs.type())
10380
{
10381
case value_t::boolean :
10382
{
10383
return lhs.as_boolean() < rhs.as_boolean() ||
10384
(lhs.as_boolean() == rhs.as_boolean() &&
10385
lhs.comments() < rhs.comments());
10386
}
10387
case value_t::integer :
10388
{
10389
return lhs.as_integer() < rhs.as_integer() ||
10390
(lhs.as_integer() == rhs.as_integer() &&
10391
lhs.comments() < rhs.comments());
10392
}
10393
case value_t::floating :
10394
{
10395
return lhs.as_floating() < rhs.as_floating() ||
10396
(lhs.as_floating() == rhs.as_floating() &&
10397
lhs.comments() < rhs.comments());
10398
}
10399
case value_t::string :
10400
{
10401
return lhs.as_string() < rhs.as_string() ||
10402
(lhs.as_string() == rhs.as_string() &&
10403
lhs.comments() < rhs.comments());
10404
}
10405
case value_t::offset_datetime:
10406
{
10407
return lhs.as_offset_datetime() < rhs.as_offset_datetime() ||
10408
(lhs.as_offset_datetime() == rhs.as_offset_datetime() &&
10409
lhs.comments() < rhs.comments());
10410
}
10411
case value_t::local_datetime:
10412
{
10413
return lhs.as_local_datetime() < rhs.as_local_datetime() ||
10414
(lhs.as_local_datetime() == rhs.as_local_datetime() &&
10415
lhs.comments() < rhs.comments());
10416
}
10417
case value_t::local_date:
10418
{
10419
return lhs.as_local_date() < rhs.as_local_date() ||
10420
(lhs.as_local_date() == rhs.as_local_date() &&
10421
lhs.comments() < rhs.comments());
10422
}
10423
case value_t::local_time:
10424
{
10425
return lhs.as_local_time() < rhs.as_local_time() ||
10426
(lhs.as_local_time() == rhs.as_local_time() &&
10427
lhs.comments() < rhs.comments());
10428
}
10429
case value_t::array :
10430
{
10431
return lhs.as_array() < rhs.as_array() ||
10432
(lhs.as_array() == rhs.as_array() &&
10433
lhs.comments() < rhs.comments());
10434
}
10435
case value_t::table :
10436
{
10437
return lhs.as_table() < rhs.as_table() ||
10438
(lhs.as_table() == rhs.as_table() &&
10439
lhs.comments() < rhs.comments());
10440
}
10441
case value_t::empty :
10442
{
10443
return lhs.comments() < rhs.comments();
10444
}
10445
default:
10446
{
10447
return lhs.comments() < rhs.comments();
10448
}
10449
}
10450
}
10451
10452
template<typename TC>
10453
cxx::enable_if_t<cxx::conjunction<
10454
detail::is_comparable<typename basic_value<TC>::array_type>,
10455
detail::is_comparable<typename basic_value<TC>::table_type>
10456
>::value, bool>
10457
operator<=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
10458
{
10459
return (lhs < rhs) || (lhs == rhs);
10460
}
10461
template<typename TC>
10462
cxx::enable_if_t<cxx::conjunction<
10463
detail::is_comparable<typename basic_value<TC>::array_type>,
10464
detail::is_comparable<typename basic_value<TC>::table_type>
10465
>::value, bool>
10466
operator>(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
10467
{
10468
return !(lhs <= rhs);
10469
}
10470
template<typename TC>
10471
cxx::enable_if_t<cxx::conjunction<
10472
detail::is_comparable<typename basic_value<TC>::array_type>,
10473
detail::is_comparable<typename basic_value<TC>::table_type>
10474
>::value, bool>
10475
operator>=(const basic_value<TC>& lhs, const basic_value<TC>& rhs)
10476
{
10477
return !(lhs < rhs);
10478
}
10479
10480
// error_info helper
10481
namespace detail
10482
{
10483
template<typename TC, typename ... Ts>
10484
error_info make_error_info_rec(error_info e,
10485
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
10486
{
10487
return make_error_info_rec(std::move(e), v.location(), std::move(msg), std::forward<Ts>(tail)...);
10488
}
10489
} // detail
10490
10491
template<typename TC, typename ... Ts>
10492
error_info make_error_info(
10493
std::string title, const basic_value<TC>& v, std::string msg, Ts&& ... tail)
10494
{
10495
return make_error_info(std::move(title),
10496
v.location(), std::move(msg), std::forward<Ts>(tail)...);
10497
}
10498
template<typename TC, typename ... Ts>
10499
std::string format_error(std::string title,
10500
const basic_value<TC>& v, std::string msg, Ts&& ... tail)
10501
{
10502
return format_error(std::move(title),
10503
v.location(), std::move(msg), std::forward<Ts>(tail)...);
10504
}
10505
10506
namespace detail
10507
{
10508
10509
template<typename TC>
10510
error_info make_type_error(const basic_value<TC>& v, const std::string& fname, const value_t ty)
10511
{
10512
return make_error_info(fname + ": bad_cast to " + to_string(ty),
10513
v.location(), "the actual type is " + to_string(v.type()));
10514
}
10515
template<typename TC>
10516
error_info make_not_found_error(const basic_value<TC>& v, const std::string& fname, const typename basic_value<TC>::key_type& key)
10517
{
10518
const auto loc = v.location();
10519
const std::string title = fname + ": key \"" + string_conv<std::string>(key) + "\" not found";
10520
10521
std::vector<std::pair<source_location, std::string>> locs;
10522
if( ! loc.is_ok())
10523
{
10524
return error_info(title, locs);
10525
}
10526
10527
if(loc.first_line_number() == 1 && loc.first_column_number() == 1 && loc.length() == 1)
10528
{
10529
// The top-level table has its region at the 0th character of the file.
10530
// That means that, in the case when a key is not found in the top-level
10531
// table, the error message points to the first character. If the file has
10532
// the first table at the first line, the error message would be like this.
10533
// ```console
10534
// [error] key "a" not found
10535
// --> example.toml
10536
// |
10537
// 1 | [table]
10538
// | ^------ in this table
10539
// ```
10540
// It actually points to the top-level table at the first character, not
10541
// `[table]`. But it is too confusing. To avoid the confusion, the error
10542
// message should explicitly say "key not found in the top-level table".
10543
locs.emplace_back(v.location(), "at the top-level table");
10544
}
10545
else
10546
{
10547
locs.emplace_back(v.location(), "in this table");
10548
}
10549
return error_info(title, locs);
10550
}
10551
10552
#define TOML11_DETAIL_GENERATE_COMPTIME_GETTER(ty) \
10553
template<typename TC> \
10554
struct getter<TC, value_t::ty> \
10555
{ \
10556
using value_type = basic_value<TC>; \
10557
using result_type = enum_to_type_t<value_t::ty, value_type>; \
10558
using format_type = enum_to_fmt_type_t<value_t::ty>; \
10559
\
10560
static result_type& get(value_type& v) \
10561
{ \
10562
return v.as_ ## ty(); \
10563
} \
10564
static result_type const& get(const value_type& v) \
10565
{ \
10566
return v.as_ ## ty(); \
10567
} \
10568
\
10569
static result_type& get_nothrow(value_type& v) noexcept \
10570
{ \
10571
return v.as_ ## ty(std::nothrow); \
10572
} \
10573
static result_type const& get_nothrow(const value_type& v) noexcept \
10574
{ \
10575
return v.as_ ## ty(std::nothrow); \
10576
} \
10577
\
10578
static format_type& get_fmt(value_type& v) \
10579
{ \
10580
return v.as_ ## ty ## _fmt(); \
10581
} \
10582
static format_type const& get_fmt(const value_type& v) \
10583
{ \
10584
return v.as_ ## ty ## _fmt(); \
10585
} \
10586
\
10587
static format_type& get_fmt_nothrow(value_type& v) noexcept \
10588
{ \
10589
return v.as_ ## ty ## _fmt(std::nothrow); \
10590
} \
10591
static format_type const& get_fmt_nothrow(const value_type& v) noexcept \
10592
{ \
10593
return v.as_ ## ty ## _fmt(std::nothrow); \
10594
} \
10595
};
10596
10597
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(boolean )
10598
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(integer )
10599
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(floating )
10600
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(string )
10601
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(offset_datetime)
10602
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_datetime )
10603
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_date )
10604
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(local_time )
10605
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(array )
10606
TOML11_DETAIL_GENERATE_COMPTIME_GETTER(table )
10607
10608
#undef TOML11_DETAIL_GENERATE_COMPTIME_GETTER
10609
10610
template<typename TC>
10611
void change_region_of_value(basic_value<TC>& dst, const basic_value<TC>& src)
10612
{
10613
dst.region_ = std::move(src.region_);
10614
return;
10615
}
10616
10617
#ifdef TOML11_ENABLE_ACCESS_CHECK
10618
template<typename TC>
10619
void unset_access_flag(basic_value<TC>& v)
10620
{
10621
v.accessed_.store(false);
10622
}
10623
10624
template<typename TC>
10625
void unset_access_flag_recursively(basic_value<TC>& v)
10626
{
10627
switch(v.type())
10628
{
10629
case value_t::empty : { return unset_access_flag(v); }
10630
case value_t::boolean : { return unset_access_flag(v); }
10631
case value_t::integer : { return unset_access_flag(v); }
10632
case value_t::floating : { return unset_access_flag(v); }
10633
case value_t::string : { return unset_access_flag(v); }
10634
case value_t::offset_datetime : { return unset_access_flag(v); }
10635
case value_t::local_datetime : { return unset_access_flag(v); }
10636
case value_t::local_date : { return unset_access_flag(v); }
10637
case value_t::local_time : { return unset_access_flag(v); }
10638
case value_t::array:
10639
{
10640
for(auto& elem : v.as_array())
10641
{
10642
unset_access_flag_recursively(elem);
10643
}
10644
return unset_access_flag(v);
10645
}
10646
case value_t::table:
10647
{
10648
for(auto& kv : v.as_table())
10649
{
10650
unset_access_flag_recursively(kv.second);
10651
}
10652
return unset_access_flag(v);
10653
}
10654
default: { return unset_access_flag(v); }
10655
}
10656
}
10657
#endif
10658
10659
} // namespace detail
10660
} // TOML11_INLINE_VERSION_NAMESPACE
10661
} // namespace toml
10662
#endif // TOML11_VALUE_HPP
10663
#ifndef TOML11_VISIT_HPP
10664
#define TOML11_VISIT_HPP
10665
10666
10667
namespace toml
10668
{
10669
inline namespace TOML11_INLINE_VERSION_NAMESPACE
10670
{
10671
10672
namespace detail
10673
{
10674
10675
template<typename F, typename ... Ts>
10676
using visit_result_t = decltype(std::declval<F>()(std::declval<Ts>().as_boolean() ...));
10677
10678
template<typename F, typename T>
10679
struct front_binder
10680
{
10681
template<typename ... Args>
10682
auto operator()(Args&& ... args) -> decltype(std::declval<F>()(std::declval<T>(), std::forward<Args>(args)...))
10683
{
10684
return func(std::move(front), std::forward<Args>(args)...);
10685
}
10686
F func;
10687
T front;
10688
};
10689
10690
template<typename F, typename T>
10691
front_binder<cxx::remove_cvref_t<F>, cxx::remove_cvref_t<T>>
10692
bind_front(F&& f, T&& t)
10693
{
10694
return front_binder<cxx::remove_cvref_t<F>, cxx::remove_cvref_t<T>>{
10695
std::forward<F>(f), std::forward<T>(t)
10696
};
10697
}
10698
10699
template<typename Visitor, typename TC, typename ... Args>
10700
visit_result_t<Visitor, const basic_value<TC>&, Args...>
10701
visit_impl(Visitor&& visitor, const basic_value<TC>& v, Args&& ... args);
10702
10703
template<typename Visitor, typename TC, typename ... Args>
10704
visit_result_t<Visitor, basic_value<TC>&, Args...>
10705
visit_impl(Visitor&& visitor, basic_value<TC>& v, Args&& ... args);
10706
10707
template<typename Visitor, typename TC, typename ... Args>
10708
visit_result_t<Visitor, basic_value<TC>, Args...>
10709
visit_impl(Visitor&& visitor, basic_value<TC>&& v, Args&& ... args);
10710
10711
10712
template<typename Visitor>
10713
visit_result_t<Visitor> visit_impl(Visitor&& visitor)
10714
{
10715
return visitor();
10716
}
10717
10718
template<typename Visitor, typename TC, typename ... Args>
10719
visit_result_t<Visitor, basic_value<TC>&, Args...>
10720
visit_impl(Visitor&& visitor, basic_value<TC>& v, Args&& ... args)
10721
{
10722
switch(v.type())
10723
{
10724
case value_t::boolean : {return visit_impl(bind_front(visitor, std::ref(v.as_boolean ())), std::forward<Args>(args)...);}
10725
case value_t::integer : {return visit_impl(bind_front(visitor, std::ref(v.as_integer ())), std::forward<Args>(args)...);}
10726
case value_t::floating : {return visit_impl(bind_front(visitor, std::ref(v.as_floating ())), std::forward<Args>(args)...);}
10727
case value_t::string : {return visit_impl(bind_front(visitor, std::ref(v.as_string ())), std::forward<Args>(args)...);}
10728
case value_t::offset_datetime: {return visit_impl(bind_front(visitor, std::ref(v.as_offset_datetime())), std::forward<Args>(args)...);}
10729
case value_t::local_datetime : {return visit_impl(bind_front(visitor, std::ref(v.as_local_datetime ())), std::forward<Args>(args)...);}
10730
case value_t::local_date : {return visit_impl(bind_front(visitor, std::ref(v.as_local_date ())), std::forward<Args>(args)...);}
10731
case value_t::local_time : {return visit_impl(bind_front(visitor, std::ref(v.as_local_time ())), std::forward<Args>(args)...);}
10732
case value_t::array : {return visit_impl(bind_front(visitor, std::ref(v.as_array ())), std::forward<Args>(args)...);}
10733
case value_t::table : {return visit_impl(bind_front(visitor, std::ref(v.as_table ())), std::forward<Args>(args)...);}
10734
case value_t::empty : break;
10735
default: break;
10736
}
10737
throw type_error(format_error("[error] toml::visit: toml::basic_value "
10738
"does not have any valid type.", v.location(), "here"), v.location());
10739
}
10740
10741
template<typename Visitor, typename TC, typename ... Args>
10742
visit_result_t<Visitor, const basic_value<TC>&, Args...>
10743
visit_impl(Visitor&& visitor, const basic_value<TC>& v, Args&& ... args)
10744
{
10745
switch(v.type())
10746
{
10747
case value_t::boolean : {return visit_impl(bind_front(visitor, std::cref(v.as_boolean ())), std::forward<Args>(args)...);}
10748
case value_t::integer : {return visit_impl(bind_front(visitor, std::cref(v.as_integer ())), std::forward<Args>(args)...);}
10749
case value_t::floating : {return visit_impl(bind_front(visitor, std::cref(v.as_floating ())), std::forward<Args>(args)...);}
10750
case value_t::string : {return visit_impl(bind_front(visitor, std::cref(v.as_string ())), std::forward<Args>(args)...);}
10751
case value_t::offset_datetime: {return visit_impl(bind_front(visitor, std::cref(v.as_offset_datetime())), std::forward<Args>(args)...);}
10752
case value_t::local_datetime : {return visit_impl(bind_front(visitor, std::cref(v.as_local_datetime ())), std::forward<Args>(args)...);}
10753
case value_t::local_date : {return visit_impl(bind_front(visitor, std::cref(v.as_local_date ())), std::forward<Args>(args)...);}
10754
case value_t::local_time : {return visit_impl(bind_front(visitor, std::cref(v.as_local_time ())), std::forward<Args>(args)...);}
10755
case value_t::array : {return visit_impl(bind_front(visitor, std::cref(v.as_array ())), std::forward<Args>(args)...);}
10756
case value_t::table : {return visit_impl(bind_front(visitor, std::cref(v.as_table ())), std::forward<Args>(args)...);}
10757
case value_t::empty : break;
10758
default: break;
10759
}
10760
throw type_error(format_error("[error] toml::visit: toml::basic_value "
10761
"does not have any valid type.", v.location(), "here"), v.location());
10762
}
10763
10764
template<typename Visitor, typename TC, typename ... Args>
10765
visit_result_t<Visitor, basic_value<TC>, Args...>
10766
visit_impl(Visitor&& visitor, basic_value<TC>&& v, Args&& ... args)
10767
{
10768
switch(v.type())
10769
{
10770
case value_t::boolean : {return visit_impl(bind_front(visitor, std::move(v.as_boolean ())), std::forward<Args>(args)...);}
10771
case value_t::integer : {return visit_impl(bind_front(visitor, std::move(v.as_integer ())), std::forward<Args>(args)...);}
10772
case value_t::floating : {return visit_impl(bind_front(visitor, std::move(v.as_floating ())), std::forward<Args>(args)...);}
10773
case value_t::string : {return visit_impl(bind_front(visitor, std::move(v.as_string ())), std::forward<Args>(args)...);}
10774
case value_t::offset_datetime: {return visit_impl(bind_front(visitor, std::move(v.as_offset_datetime())), std::forward<Args>(args)...);}
10775
case value_t::local_datetime : {return visit_impl(bind_front(visitor, std::move(v.as_local_datetime ())), std::forward<Args>(args)...);}
10776
case value_t::local_date : {return visit_impl(bind_front(visitor, std::move(v.as_local_date ())), std::forward<Args>(args)...);}
10777
case value_t::local_time : {return visit_impl(bind_front(visitor, std::move(v.as_local_time ())), std::forward<Args>(args)...);}
10778
case value_t::array : {return visit_impl(bind_front(visitor, std::move(v.as_array ())), std::forward<Args>(args)...);}
10779
case value_t::table : {return visit_impl(bind_front(visitor, std::move(v.as_table ())), std::forward<Args>(args)...);}
10780
case value_t::empty : break;
10781
default: break;
10782
}
10783
throw type_error(format_error("[error] toml::visit: toml::basic_value "
10784
"does not have any valid type.", v.location(), "here"), v.location());
10785
}
10786
10787
} // detail
10788
10789
template<typename Visitor, typename ... Args>
10790
detail::visit_result_t<Visitor, Args...>
10791
visit(Visitor&& visitor, Args&& ... args)
10792
{
10793
return detail::visit_impl(std::forward<Visitor>(visitor), std::forward<Args>(args)...);
10794
}
10795
10796
} // TOML11_INLINE_VERSION_NAMESPACE
10797
} // toml
10798
#endif // TOML11_VISIT_HPP
10799
#ifndef TOML11_TYPES_HPP
10800
#define TOML11_TYPES_HPP
10801
10802
10803
#include <ostream>
10804
#include <sstream>
10805
#include <string>
10806
#include <type_traits>
10807
#include <unordered_map>
10808
#include <vector>
10809
10810
#include <cstdint>
10811
#include <cstdio>
10812
10813
namespace toml
10814
{
10815
inline namespace TOML11_INLINE_VERSION_NAMESPACE
10816
{
10817
10818
// forward decl
10819
template<typename TypeConfig>
10820
class basic_value;
10821
10822
// when you use a special integer type as toml::value::integer_type, parse must
10823
// be able to read it. So, type_config has static member functions that read the
10824
// integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<<
10825
// is enough. To make config easy, we provide the default read functions.
10826
//
10827
// Before this functions is called, syntax is checked and prefix(`0x` etc) and
10828
// spacer(`_`) are removed.
10829
10830
template<typename T>
10831
result<T, error_info>
10832
read_dec_int(const std::string& str, const source_location src)
10833
{
10834
constexpr auto max_digits = std::numeric_limits<T>::digits;
10835
assert( ! str.empty());
10836
10837
T val{0};
10838
std::istringstream iss(str);
10839
iss >> val;
10840
if(iss.fail())
10841
{
10842
return err(make_error_info("toml::parse_dec_integer: "
10843
"too large integer: current max digits = 2^" + std::to_string(max_digits),
10844
std::move(src), "must be < 2^" + std::to_string(max_digits)));
10845
}
10846
return ok(val);
10847
}
10848
10849
template<typename T>
10850
result<T, error_info>
10851
read_hex_int(const std::string& str, const source_location src)
10852
{
10853
constexpr auto max_digits = std::numeric_limits<T>::digits;
10854
assert( ! str.empty());
10855
10856
T val{0};
10857
std::istringstream iss(str);
10858
iss >> std::hex >> val;
10859
if(iss.fail())
10860
{
10861
return err(make_error_info("toml::parse_hex_integer: "
10862
"too large integer: current max value = 2^" + std::to_string(max_digits),
10863
std::move(src), "must be < 2^" + std::to_string(max_digits)));
10864
}
10865
return ok(val);
10866
}
10867
10868
template<typename T>
10869
result<T, error_info>
10870
read_oct_int(const std::string& str, const source_location src)
10871
{
10872
constexpr auto max_digits = std::numeric_limits<T>::digits;
10873
assert( ! str.empty());
10874
10875
T val{0};
10876
std::istringstream iss(str);
10877
iss >> std::oct >> val;
10878
if(iss.fail())
10879
{
10880
return err(make_error_info("toml::parse_oct_integer: "
10881
"too large integer: current max value = 2^" + std::to_string(max_digits),
10882
std::move(src), "must be < 2^" + std::to_string(max_digits)));
10883
}
10884
return ok(val);
10885
}
10886
10887
template<typename T>
10888
result<T, error_info>
10889
read_bin_int(const std::string& str, const source_location src)
10890
{
10891
constexpr auto is_bounded = std::numeric_limits<T>::is_bounded;
10892
constexpr auto max_digits = std::numeric_limits<T>::digits;
10893
const auto max_value = (std::numeric_limits<T>::max)();
10894
10895
T val{0};
10896
T base{1};
10897
for(auto i = str.rbegin(); i != str.rend(); ++i)
10898
{
10899
const auto c = *i;
10900
if(c == '1')
10901
{
10902
val += base;
10903
// prevent `base` from overflow
10904
if(is_bounded && max_value / 2 < base && std::next(i) != str.rend())
10905
{
10906
base = 0;
10907
}
10908
else
10909
{
10910
base *= 2;
10911
}
10912
}
10913
else
10914
{
10915
assert(c == '0');
10916
10917
if(is_bounded && max_value / 2 < base && std::next(i) != str.rend())
10918
{
10919
base = 0;
10920
}
10921
else
10922
{
10923
base *= 2;
10924
}
10925
}
10926
}
10927
if(base == 0)
10928
{
10929
return err(make_error_info("toml::parse_bin_integer: "
10930
"too large integer: current max value = 2^" + std::to_string(max_digits),
10931
std::move(src), "must be < 2^" + std::to_string(max_digits)));
10932
}
10933
return ok(val);
10934
}
10935
10936
template<typename T>
10937
result<T, error_info>
10938
read_int(const std::string& str, const source_location src, const std::uint8_t base)
10939
{
10940
assert(base == 10 || base == 16 || base == 8 || base == 2);
10941
switch(base)
10942
{
10943
case 2: { return read_bin_int<T>(str, src); }
10944
case 8: { return read_oct_int<T>(str, src); }
10945
case 16: { return read_hex_int<T>(str, src); }
10946
default:
10947
{
10948
assert(base == 10);
10949
return read_dec_int<T>(str, src);
10950
}
10951
}
10952
}
10953
10954
inline result<float, error_info>
10955
read_hex_float(const std::string& str, const source_location src, float val)
10956
{
10957
#if defined(_MSC_VER) && ! defined(__clang__)
10958
const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val));
10959
#else
10960
const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val));
10961
#endif
10962
if(res != 1)
10963
{
10964
return err(make_error_info("toml::parse_floating: "
10965
"failed to read hexadecimal floating point value ",
10966
std::move(src), "here"));
10967
}
10968
return ok(val);
10969
}
10970
inline result<double, error_info>
10971
read_hex_float(const std::string& str, const source_location src, double val)
10972
{
10973
#if defined(_MSC_VER) && ! defined(__clang__)
10974
const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val));
10975
#else
10976
const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val));
10977
#endif
10978
if(res != 1)
10979
{
10980
return err(make_error_info("toml::parse_floating: "
10981
"failed to read hexadecimal floating point value ",
10982
std::move(src), "here"));
10983
}
10984
return ok(val);
10985
}
10986
template<typename T>
10987
cxx::enable_if_t<cxx::conjunction<
10988
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, double>>,
10989
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, float>>
10990
>::value, result<T, error_info>>
10991
read_hex_float(const std::string&, const source_location src, T)
10992
{
10993
return err(make_error_info("toml::parse_floating: failed to read "
10994
"floating point value because of unknown type in type_config",
10995
std::move(src), "here"));
10996
}
10997
10998
template<typename T>
10999
result<T, error_info>
11000
read_dec_float(const std::string& str, const source_location src)
11001
{
11002
T val;
11003
std::istringstream iss(str);
11004
iss >> val;
11005
if(iss.fail())
11006
{
11007
return err(make_error_info("toml::parse_floating: "
11008
"failed to read floating point value from stream",
11009
std::move(src), "here"));
11010
}
11011
return ok(val);
11012
}
11013
11014
template<typename T>
11015
result<T, error_info>
11016
read_float(const std::string& str, const source_location src, const bool is_hex)
11017
{
11018
if(is_hex)
11019
{
11020
return read_hex_float(str, src, T{});
11021
}
11022
else
11023
{
11024
return read_dec_float<T>(str, src);
11025
}
11026
}
11027
11028
struct type_config
11029
{
11030
using comment_type = preserve_comments;
11031
11032
using boolean_type = bool;
11033
using integer_type = std::int64_t;
11034
using floating_type = double;
11035
using string_type = std::string;
11036
11037
template<typename T>
11038
using array_type = std::vector<T>;
11039
template<typename K, typename T>
11040
using table_type = std::unordered_map<K, T>;
11041
11042
static result<integer_type, error_info>
11043
parse_int(const std::string& str, const source_location src, const std::uint8_t base)
11044
{
11045
return read_int<integer_type>(str, src, base);
11046
}
11047
static result<floating_type, error_info>
11048
parse_float(const std::string& str, const source_location src, const bool is_hex)
11049
{
11050
return read_float<floating_type>(str, src, is_hex);
11051
}
11052
};
11053
11054
using value = basic_value<type_config>;
11055
using table = typename value::table_type;
11056
using array = typename value::array_type;
11057
11058
struct ordered_type_config
11059
{
11060
using comment_type = preserve_comments;
11061
11062
using boolean_type = bool;
11063
using integer_type = std::int64_t;
11064
using floating_type = double;
11065
using string_type = std::string;
11066
11067
template<typename T>
11068
using array_type = std::vector<T>;
11069
template<typename K, typename T>
11070
using table_type = ordered_map<K, T>;
11071
11072
static result<integer_type, error_info>
11073
parse_int(const std::string& str, const source_location src, const std::uint8_t base)
11074
{
11075
return read_int<integer_type>(str, src, base);
11076
}
11077
static result<floating_type, error_info>
11078
parse_float(const std::string& str, const source_location src, const bool is_hex)
11079
{
11080
return read_float<floating_type>(str, src, is_hex);
11081
}
11082
};
11083
11084
using ordered_value = basic_value<ordered_type_config>;
11085
using ordered_table = typename ordered_value::table_type;
11086
using ordered_array = typename ordered_value::array_type;
11087
11088
// ----------------------------------------------------------------------------
11089
// meta functions for internal use
11090
11091
namespace detail
11092
{
11093
11094
// ----------------------------------------------------------------------------
11095
// check if type T has all the needed member types
11096
11097
template<typename T, typename U = void>
11098
struct has_comment_type: std::false_type{};
11099
template<typename T>
11100
struct has_comment_type<T, cxx::void_t<typename T::comment_type>>: std::true_type{};
11101
11102
template<typename T, typename U = void>
11103
struct has_integer_type: std::false_type{};
11104
template<typename T>
11105
struct has_integer_type<T, cxx::void_t<typename T::integer_type>>: std::true_type{};
11106
11107
template<typename T, typename U = void>
11108
struct has_floating_type: std::false_type{};
11109
template<typename T>
11110
struct has_floating_type<T, cxx::void_t<typename T::floating_type>>: std::true_type{};
11111
11112
template<typename T, typename U = void>
11113
struct has_string_type: std::false_type{};
11114
template<typename T>
11115
struct has_string_type<T, cxx::void_t<typename T::string_type>>: std::true_type{};
11116
11117
template<typename T, typename U = void>
11118
struct has_array_type: std::false_type{};
11119
template<typename T>
11120
struct has_array_type<T, cxx::void_t<typename T::template array_type<int>>>: std::true_type{};
11121
11122
template<typename T, typename U = void>
11123
struct has_table_type: std::false_type{};
11124
template<typename T>
11125
struct has_table_type<T, cxx::void_t<typename T::template table_type<int, int>>>: std::true_type{};
11126
11127
template<typename T, typename U = void>
11128
struct has_parse_int: std::false_type{};
11129
template<typename T>
11130
struct has_parse_int<T, cxx::void_t<decltype(std::declval<T>().parse_int(
11131
std::declval<std::string const&>(),
11132
std::declval<::toml::source_location const&>(),
11133
std::declval<std::uint8_t>()
11134
))>>: std::true_type{};
11135
11136
template<typename T, typename U = void>
11137
struct has_parse_float: std::false_type{};
11138
template<typename T>
11139
struct has_parse_float<T, cxx::void_t<decltype(std::declval<T>().parse_float(
11140
std::declval<std::string const&>(),
11141
std::declval<::toml::source_location const&>(),
11142
std::declval<bool>()
11143
))>>: std::true_type{};
11144
11145
template<typename T>
11146
using is_type_config = cxx::conjunction<
11147
has_comment_type<T>,
11148
has_integer_type<T>,
11149
has_floating_type<T>,
11150
has_string_type<T>,
11151
has_array_type<T>,
11152
has_table_type<T>,
11153
has_parse_int<T>,
11154
has_parse_float<T>
11155
>;
11156
11157
} // namespace detail
11158
} // TOML11_INLINE_VERSION_NAMESPACE
11159
} // namespace toml
11160
11161
#if defined(TOML11_COMPILE_SOURCES)
11162
namespace toml
11163
{
11164
inline namespace TOML11_INLINE_VERSION_NAMESPACE
11165
{
11166
extern template class basic_value<type_config>;
11167
extern template class basic_value<ordered_type_config>;
11168
} // TOML11_INLINE_VERSION_NAMESPACE
11169
} // toml
11170
#endif // TOML11_COMPILE_SOURCES
11171
11172
#endif // TOML11_TYPES_HPP
11173
#ifndef TOML11_SERIALIZER_HPP
11174
#define TOML11_SERIALIZER_HPP
11175
11176
11177
#include <iomanip>
11178
#include <iterator>
11179
#include <sstream>
11180
11181
#include <cmath>
11182
#include <cstdio>
11183
11184
namespace toml
11185
{
11186
inline namespace TOML11_INLINE_VERSION_NAMESPACE
11187
{
11188
11189
struct serialization_error final : public ::toml::exception
11190
{
11191
public:
11192
explicit serialization_error(std::string what_arg, source_location loc)
11193
: what_(std::move(what_arg)), loc_(std::move(loc))
11194
{}
11195
~serialization_error() noexcept override = default;
11196
11197
const char* what() const noexcept override {return what_.c_str();}
11198
source_location const& location() const noexcept {return loc_;}
11199
11200
private:
11201
std::string what_;
11202
source_location loc_;
11203
};
11204
11205
namespace detail
11206
{
11207
template<typename TC>
11208
class serializer
11209
{
11210
public:
11211
11212
using value_type = basic_value<TC>;
11213
11214
using key_type = typename value_type::key_type ;
11215
using comment_type = typename value_type::comment_type ;
11216
using boolean_type = typename value_type::boolean_type ;
11217
using integer_type = typename value_type::integer_type ;
11218
using floating_type = typename value_type::floating_type ;
11219
using string_type = typename value_type::string_type ;
11220
using local_time_type = typename value_type::local_time_type ;
11221
using local_date_type = typename value_type::local_date_type ;
11222
using local_datetime_type = typename value_type::local_datetime_type ;
11223
using offset_datetime_type = typename value_type::offset_datetime_type;
11224
using array_type = typename value_type::array_type ;
11225
using table_type = typename value_type::table_type ;
11226
11227
using char_type = typename string_type::value_type;
11228
11229
public:
11230
11231
explicit serializer(const spec& sp)
11232
: spec_(sp), force_inline_(false), current_indent_(0)
11233
{}
11234
11235
string_type operator()(const std::vector<key_type>& ks, const value_type& v)
11236
{
11237
for(const auto& k : ks)
11238
{
11239
this->keys_.push_back(k);
11240
}
11241
return (*this)(v);
11242
}
11243
11244
string_type operator()(const key_type& k, const value_type& v)
11245
{
11246
this->keys_.push_back(k);
11247
return (*this)(v);
11248
}
11249
11250
string_type operator()(const value_type& v)
11251
{
11252
switch(v.type())
11253
{
11254
case value_t::boolean : {return (*this)(v.as_boolean (), v.as_boolean_fmt (), v.location());}
11255
case value_t::integer : {return (*this)(v.as_integer (), v.as_integer_fmt (), v.location());}
11256
case value_t::floating : {return (*this)(v.as_floating (), v.as_floating_fmt (), v.location());}
11257
case value_t::string : {return (*this)(v.as_string (), v.as_string_fmt (), v.location());}
11258
case value_t::offset_datetime: {return (*this)(v.as_offset_datetime(), v.as_offset_datetime_fmt(), v.location());}
11259
case value_t::local_datetime : {return (*this)(v.as_local_datetime (), v.as_local_datetime_fmt (), v.location());}
11260
case value_t::local_date : {return (*this)(v.as_local_date (), v.as_local_date_fmt (), v.location());}
11261
case value_t::local_time : {return (*this)(v.as_local_time (), v.as_local_time_fmt (), v.location());}
11262
case value_t::array :
11263
{
11264
return (*this)(v.as_array(), v.as_array_fmt(), v.comments(), v.location());
11265
}
11266
case value_t::table :
11267
{
11268
string_type retval;
11269
if(this->keys_.empty()) // it might be the root table. emit comments here.
11270
{
11271
retval += format_comments(v.comments(), v.as_table_fmt().indent_type);
11272
}
11273
if( ! retval.empty()) // we have comment.
11274
{
11275
retval += char_type('\n');
11276
}
11277
11278
retval += (*this)(v.as_table(), v.as_table_fmt(), v.comments(), v.location());
11279
return retval;
11280
}
11281
case value_t::empty:
11282
{
11283
if(this->spec_.ext_null_value)
11284
{
11285
return string_conv<string_type>("null");
11286
}
11287
break;
11288
}
11289
default:
11290
{
11291
break;
11292
}
11293
}
11294
throw serialization_error(format_error(
11295
"[error] toml::serializer: toml::basic_value "
11296
"does not have any valid type.", v.location(), "here"), v.location());
11297
}
11298
11299
private:
11300
11301
string_type operator()(const boolean_type& b, const boolean_format_info&, const source_location&) // {{{
11302
{
11303
if(b)
11304
{
11305
return string_conv<string_type>("true");
11306
}
11307
else
11308
{
11309
return string_conv<string_type>("false");
11310
}
11311
} // }}}
11312
11313
string_type operator()(const integer_type i, const integer_format_info& fmt, const source_location& loc) // {{{
11314
{
11315
std::ostringstream oss;
11316
this->set_locale(oss);
11317
11318
const auto insert_spacer = [&fmt](std::string s) -> std::string {
11319
if(fmt.spacer == 0) {return s;}
11320
11321
std::string sign;
11322
if( ! s.empty() && (s.at(0) == '+' || s.at(0) == '-'))
11323
{
11324
sign += s.at(0);
11325
s.erase(s.begin());
11326
}
11327
11328
std::string spaced;
11329
std::size_t counter = 0;
11330
for(auto iter = s.rbegin(); iter != s.rend(); ++iter)
11331
{
11332
if(counter != 0 && counter % fmt.spacer == 0)
11333
{
11334
spaced += '_';
11335
}
11336
spaced += *iter;
11337
counter += 1;
11338
}
11339
if(!spaced.empty() && spaced.back() == '_') {spaced.pop_back();}
11340
11341
s.clear();
11342
std::copy(spaced.rbegin(), spaced.rend(), std::back_inserter(s));
11343
return sign + s;
11344
};
11345
11346
std::string retval;
11347
if(fmt.fmt == integer_format::dec)
11348
{
11349
oss << std::setw(static_cast<int>(fmt.width)) << std::dec << i;
11350
retval = insert_spacer(oss.str());
11351
11352
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11353
{
11354
retval += '_';
11355
retval += fmt.suffix;
11356
}
11357
}
11358
else
11359
{
11360
if(i < 0)
11361
{
11362
throw serialization_error(format_error("binary, octal, hexadecimal "
11363
"integer does not allow negative value", loc, "here"), loc);
11364
}
11365
switch(fmt.fmt)
11366
{
11367
case integer_format::hex:
11368
{
11369
oss << std::noshowbase
11370
<< std::setw(static_cast<int>(fmt.width))
11371
<< std::setfill('0')
11372
<< std::hex;
11373
if(fmt.uppercase)
11374
{
11375
oss << std::uppercase;
11376
}
11377
else
11378
{
11379
oss << std::nouppercase;
11380
}
11381
oss << i;
11382
retval = std::string("0x") + insert_spacer(oss.str());
11383
break;
11384
}
11385
case integer_format::oct:
11386
{
11387
oss << std::setw(static_cast<int>(fmt.width)) << std::setfill('0') << std::oct << i;
11388
retval = std::string("0o") + insert_spacer(oss.str());
11389
break;
11390
}
11391
case integer_format::bin:
11392
{
11393
integer_type x{i};
11394
std::string tmp;
11395
std::size_t bits(0);
11396
while(x != 0)
11397
{
11398
if(fmt.spacer != 0)
11399
{
11400
if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';}
11401
}
11402
if(x % 2 == 1) { tmp += '1'; } else { tmp += '0'; }
11403
x >>= 1;
11404
bits += 1;
11405
}
11406
for(; bits < fmt.width; ++bits)
11407
{
11408
if(fmt.spacer != 0)
11409
{
11410
if(bits != 0 && (bits % fmt.spacer) == 0) {tmp += '_';}
11411
}
11412
tmp += '0';
11413
}
11414
for(auto iter = tmp.rbegin(); iter != tmp.rend(); ++iter)
11415
{
11416
oss << *iter;
11417
}
11418
retval = std::string("0b") + oss.str();
11419
break;
11420
}
11421
default:
11422
{
11423
throw serialization_error(format_error(
11424
"none of dec, hex, oct, bin: " + to_string(fmt.fmt),
11425
loc, "here"), loc);
11426
}
11427
}
11428
}
11429
return string_conv<string_type>(retval);
11430
} // }}}
11431
11432
string_type operator()(const floating_type f, const floating_format_info& fmt, const source_location&) // {{{
11433
{
11434
using std::isnan;
11435
using std::isinf;
11436
using std::signbit;
11437
11438
std::ostringstream oss;
11439
this->set_locale(oss);
11440
11441
if(isnan(f))
11442
{
11443
if(signbit(f))
11444
{
11445
oss << '-';
11446
}
11447
oss << "nan";
11448
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11449
{
11450
oss << '_';
11451
oss << fmt.suffix;
11452
}
11453
return string_conv<string_type>(oss.str());
11454
}
11455
11456
if(isinf(f))
11457
{
11458
if(signbit(f))
11459
{
11460
oss << '-';
11461
}
11462
oss << "inf";
11463
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11464
{
11465
oss << '_';
11466
oss << fmt.suffix;
11467
}
11468
return string_conv<string_type>(oss.str());
11469
}
11470
11471
switch(fmt.fmt)
11472
{
11473
case floating_format::defaultfloat:
11474
{
11475
if(fmt.prec != 0)
11476
{
11477
oss << std::setprecision(static_cast<int>(fmt.prec));
11478
}
11479
oss << f;
11480
// since defaultfloat may omit point, we need to add it
11481
std::string s = oss.str();
11482
if (s.find('.') == std::string::npos &&
11483
s.find('e') == std::string::npos &&
11484
s.find('E') == std::string::npos )
11485
{
11486
s += ".0";
11487
}
11488
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11489
{
11490
s += '_';
11491
s += fmt.suffix;
11492
}
11493
return string_conv<string_type>(s);
11494
}
11495
case floating_format::fixed:
11496
{
11497
if(fmt.prec != 0)
11498
{
11499
oss << std::setprecision(static_cast<int>(fmt.prec));
11500
}
11501
oss << std::fixed << f;
11502
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11503
{
11504
oss << '_' << fmt.suffix;
11505
}
11506
return string_conv<string_type>(oss.str());
11507
}
11508
case floating_format::scientific:
11509
{
11510
if(fmt.prec != 0)
11511
{
11512
oss << std::setprecision(static_cast<int>(fmt.prec));
11513
}
11514
oss << std::scientific << f;
11515
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11516
{
11517
oss << '_' << fmt.suffix;
11518
}
11519
return string_conv<string_type>(oss.str());
11520
}
11521
case floating_format::hex:
11522
{
11523
if(this->spec_.ext_hex_float)
11524
{
11525
oss << std::hexfloat << f;
11526
// suffix is only for decimal numbers.
11527
return string_conv<string_type>(oss.str());
11528
}
11529
else // no hex allowed. output with max precision.
11530
{
11531
oss << std::setprecision(std::numeric_limits<floating_type>::max_digits10)
11532
<< std::scientific << f;
11533
// suffix is only for decimal numbers.
11534
return string_conv<string_type>(oss.str());
11535
}
11536
}
11537
default:
11538
{
11539
if(this->spec_.ext_num_suffix && ! fmt.suffix.empty())
11540
{
11541
oss << '_' << fmt.suffix;
11542
}
11543
return string_conv<string_type>(oss.str());
11544
}
11545
}
11546
} // }}}
11547
11548
string_type operator()(string_type s, const string_format_info& fmt, const source_location& loc) // {{{
11549
{
11550
string_type retval;
11551
switch(fmt.fmt)
11552
{
11553
case string_format::basic:
11554
{
11555
retval += char_type('"');
11556
retval += this->escape_basic_string(s);
11557
retval += char_type('"');
11558
return retval;
11559
}
11560
case string_format::literal:
11561
{
11562
if(std::find(s.begin(), s.end(), char_type('\n')) != s.end())
11563
{
11564
throw serialization_error(format_error("toml::serializer: "
11565
"(non-multiline) literal string cannot have a newline",
11566
loc, "here"), loc);
11567
}
11568
retval += char_type('\'');
11569
retval += s;
11570
retval += char_type('\'');
11571
return retval;
11572
}
11573
case string_format::multiline_basic:
11574
{
11575
retval += string_conv<string_type>("\"\"\"");
11576
if(fmt.start_with_newline)
11577
{
11578
retval += char_type('\n');
11579
}
11580
11581
retval += this->escape_ml_basic_string(s);
11582
11583
retval += string_conv<string_type>("\"\"\"");
11584
return retval;
11585
}
11586
case string_format::multiline_literal:
11587
{
11588
retval += string_conv<string_type>("'''");
11589
if(fmt.start_with_newline)
11590
{
11591
retval += char_type('\n');
11592
}
11593
retval += s;
11594
retval += string_conv<string_type>("'''");
11595
return retval;
11596
}
11597
default:
11598
{
11599
throw serialization_error(format_error(
11600
"[error] toml::serializer::operator()(string): "
11601
"invalid string_format value", loc, "here"), loc);
11602
}
11603
}
11604
} // }}}
11605
11606
string_type operator()(const local_date_type& d, const local_date_format_info&, const source_location&) // {{{
11607
{
11608
std::ostringstream oss;
11609
oss << d;
11610
return string_conv<string_type>(oss.str());
11611
} // }}}
11612
11613
string_type operator()(const local_time_type& t, const local_time_format_info& fmt, const source_location&) // {{{
11614
{
11615
return this->format_local_time(t, fmt.has_seconds, fmt.subsecond_precision);
11616
} // }}}
11617
11618
string_type operator()(const local_datetime_type& dt, const local_datetime_format_info& fmt, const source_location&) // {{{
11619
{
11620
std::ostringstream oss;
11621
oss << dt.date;
11622
switch(fmt.delimiter)
11623
{
11624
case datetime_delimiter_kind::upper_T: { oss << 'T'; break; }
11625
case datetime_delimiter_kind::lower_t: { oss << 't'; break; }
11626
case datetime_delimiter_kind::space: { oss << ' '; break; }
11627
default: { oss << 'T'; break; }
11628
}
11629
return string_conv<string_type>(oss.str()) +
11630
this->format_local_time(dt.time, fmt.has_seconds, fmt.subsecond_precision);
11631
} // }}}
11632
11633
string_type operator()(const offset_datetime_type& odt, const offset_datetime_format_info& fmt, const source_location&) // {{{
11634
{
11635
std::ostringstream oss;
11636
oss << odt.date;
11637
switch(fmt.delimiter)
11638
{
11639
case datetime_delimiter_kind::upper_T: { oss << 'T'; break; }
11640
case datetime_delimiter_kind::lower_t: { oss << 't'; break; }
11641
case datetime_delimiter_kind::space: { oss << ' '; break; }
11642
default: { oss << 'T'; break; }
11643
}
11644
oss << string_conv<std::string>(this->format_local_time(odt.time, fmt.has_seconds, fmt.subsecond_precision));
11645
oss << odt.offset;
11646
return string_conv<string_type>(oss.str());
11647
} // }}}
11648
11649
string_type operator()(const array_type& a, const array_format_info& fmt, const comment_type& com, const source_location& loc) // {{{
11650
{
11651
array_format f = fmt.fmt;
11652
if(fmt.fmt == array_format::default_format)
11653
{
11654
// [[in.this.form]], you cannot add a comment to the array itself
11655
// (but you can add a comment to each table).
11656
// To keep comments, we need to avoid multiline array-of-tables
11657
// if array itself has a comment.
11658
if( ! this->keys_.empty() &&
11659
! a.empty() &&
11660
com.empty() &&
11661
std::all_of(a.begin(), a.end(), [](const value_type& e) {return e.is_table();}))
11662
{
11663
f = array_format::array_of_tables;
11664
}
11665
else
11666
{
11667
f = array_format::oneline;
11668
11669
// check if it becomes long
11670
std::size_t approx_len = 0;
11671
for(const auto& e : a)
11672
{
11673
// have a comment. cannot be inlined
11674
if( ! e.comments().empty())
11675
{
11676
f = array_format::multiline;
11677
break;
11678
}
11679
// possibly long types ...
11680
if(e.is_array() || e.is_table() || e.is_offset_datetime() || e.is_local_datetime())
11681
{
11682
f = array_format::multiline;
11683
break;
11684
}
11685
else if(e.is_boolean())
11686
{
11687
approx_len += (*this)(e.as_boolean(), e.as_boolean_fmt(), e.location()).size();
11688
}
11689
else if(e.is_integer())
11690
{
11691
approx_len += (*this)(e.as_integer(), e.as_integer_fmt(), e.location()).size();
11692
}
11693
else if(e.is_floating())
11694
{
11695
approx_len += (*this)(e.as_floating(), e.as_floating_fmt(), e.location()).size();
11696
}
11697
else if(e.is_string())
11698
{
11699
if(e.as_string_fmt().fmt == string_format::multiline_basic ||
11700
e.as_string_fmt().fmt == string_format::multiline_literal)
11701
{
11702
f = array_format::multiline;
11703
break;
11704
}
11705
approx_len += 2 + (*this)(e.as_string(), e.as_string_fmt(), e.location()).size();
11706
}
11707
else if(e.is_local_date())
11708
{
11709
approx_len += 10; // 1234-56-78
11710
}
11711
else if(e.is_local_time())
11712
{
11713
approx_len += 15; // 12:34:56.789012
11714
}
11715
11716
if(approx_len > 60) // key, ` = `, `[...]` < 80
11717
{
11718
f = array_format::multiline;
11719
break;
11720
}
11721
approx_len += 2; // `, `
11722
}
11723
}
11724
}
11725
if(this->force_inline_ && f == array_format::array_of_tables)
11726
{
11727
f = array_format::multiline;
11728
}
11729
if(a.empty() && f == array_format::array_of_tables)
11730
{
11731
f = array_format::oneline;
11732
}
11733
11734
// --------------------------------------------------------------------
11735
11736
if(f == array_format::array_of_tables)
11737
{
11738
if(this->keys_.empty())
11739
{
11740
throw serialization_error("array of table must have its key. "
11741
"use format(key, v)", loc);
11742
}
11743
string_type retval;
11744
for(const auto& e : a)
11745
{
11746
assert(e.is_table());
11747
11748
this->current_indent_ += e.as_table_fmt().name_indent;
11749
retval += this->format_comments(e.comments(), e.as_table_fmt().indent_type);
11750
retval += this->format_indent(e.as_table_fmt().indent_type);
11751
this->current_indent_ -= e.as_table_fmt().name_indent;
11752
11753
retval += string_conv<string_type>("[[");
11754
retval += this->format_keys(this->keys_).value();
11755
retval += string_conv<string_type>("]]\n");
11756
11757
retval += this->format_ml_table(e.as_table(), e.as_table_fmt());
11758
}
11759
return retval;
11760
}
11761
else if(f == array_format::oneline)
11762
{
11763
// ignore comments. we cannot emit comments
11764
string_type retval;
11765
retval += char_type('[');
11766
for(const auto& e : a)
11767
{
11768
this->force_inline_ = true;
11769
retval += (*this)(e);
11770
retval += string_conv<string_type>(", ");
11771
}
11772
if( ! a.empty())
11773
{
11774
retval.pop_back(); // ` `
11775
retval.pop_back(); // `,`
11776
}
11777
retval += char_type(']');
11778
this->force_inline_ = false;
11779
return retval;
11780
}
11781
else
11782
{
11783
assert(f == array_format::multiline);
11784
11785
string_type retval;
11786
retval += string_conv<string_type>("[\n");
11787
11788
for(const auto& e : a)
11789
{
11790
this->current_indent_ += fmt.body_indent;
11791
retval += this->format_comments(e.comments(), fmt.indent_type);
11792
retval += this->format_indent(fmt.indent_type);
11793
this->current_indent_ -= fmt.body_indent;
11794
11795
this->force_inline_ = true;
11796
retval += (*this)(e);
11797
retval += string_conv<string_type>(",\n");
11798
}
11799
this->force_inline_ = false;
11800
11801
this->current_indent_ += fmt.closing_indent;
11802
retval += this->format_indent(fmt.indent_type);
11803
this->current_indent_ -= fmt.closing_indent;
11804
11805
retval += char_type(']');
11806
return retval;
11807
}
11808
} // }}}
11809
11810
string_type operator()(const table_type& t, const table_format_info& fmt, const comment_type& com, const source_location& loc) // {{{
11811
{
11812
if(this->force_inline_)
11813
{
11814
if(fmt.fmt == table_format::multiline_oneline)
11815
{
11816
return this->format_ml_inline_table(t, fmt);
11817
}
11818
else
11819
{
11820
return this->format_inline_table(t, fmt);
11821
}
11822
}
11823
else
11824
{
11825
if(fmt.fmt == table_format::multiline)
11826
{
11827
string_type retval;
11828
// comment is emitted inside format_ml_table
11829
if(auto k = this->format_keys(this->keys_))
11830
{
11831
this->current_indent_ += fmt.name_indent;
11832
retval += this->format_comments(com, fmt.indent_type);
11833
retval += this->format_indent(fmt.indent_type);
11834
this->current_indent_ -= fmt.name_indent;
11835
retval += char_type('[');
11836
retval += k.value();
11837
retval += string_conv<string_type>("]\n");
11838
}
11839
// otherwise, its the root.
11840
11841
retval += this->format_ml_table(t, fmt);
11842
return retval;
11843
}
11844
else if(fmt.fmt == table_format::oneline)
11845
{
11846
return this->format_inline_table(t, fmt);
11847
}
11848
else if(fmt.fmt == table_format::multiline_oneline)
11849
{
11850
return this->format_ml_inline_table(t, fmt);
11851
}
11852
else if(fmt.fmt == table_format::dotted)
11853
{
11854
std::vector<string_type> keys;
11855
if(this->keys_.empty())
11856
{
11857
throw serialization_error(format_error("toml::serializer: "
11858
"dotted table must have its key. use format(key, v)",
11859
loc, "here"), loc);
11860
}
11861
keys.push_back(this->keys_.back());
11862
11863
const auto retval = this->format_dotted_table(t, fmt, loc, keys);
11864
keys.pop_back();
11865
return retval;
11866
}
11867
else
11868
{
11869
assert(fmt.fmt == table_format::implicit);
11870
11871
string_type retval;
11872
for(const auto& kv : t)
11873
{
11874
const auto& k = kv.first;
11875
const auto& v = kv.second;
11876
11877
if( ! v.is_table() && ! v.is_array_of_tables())
11878
{
11879
throw serialization_error(format_error("toml::serializer: "
11880
"an implicit table cannot have non-table value.",
11881
v.location(), "here"), v.location());
11882
}
11883
if(v.is_table())
11884
{
11885
if(v.as_table_fmt().fmt != table_format::multiline &&
11886
v.as_table_fmt().fmt != table_format::implicit)
11887
{
11888
throw serialization_error(format_error("toml::serializer: "
11889
"an implicit table cannot have non-multiline table",
11890
v.location(), "here"), v.location());
11891
}
11892
}
11893
else
11894
{
11895
assert(v.is_array());
11896
for(const auto& e : v.as_array())
11897
{
11898
if(e.as_table_fmt().fmt != table_format::multiline &&
11899
v.as_table_fmt().fmt != table_format::implicit)
11900
{
11901
throw serialization_error(format_error("toml::serializer: "
11902
"an implicit table cannot have non-multiline table",
11903
e.location(), "here"), e.location());
11904
}
11905
}
11906
}
11907
11908
keys_.push_back(k);
11909
retval += (*this)(v);
11910
keys_.pop_back();
11911
}
11912
return retval;
11913
}
11914
}
11915
} // }}}
11916
11917
private:
11918
11919
string_type escape_basic_string(const string_type& s) const // {{{
11920
{
11921
string_type retval;
11922
for(const char_type c : s)
11923
{
11924
switch(c)
11925
{
11926
case char_type('\\'): {retval += string_conv<string_type>("\\\\"); break;}
11927
case char_type('\"'): {retval += string_conv<string_type>("\\\""); break;}
11928
case char_type('\b'): {retval += string_conv<string_type>("\\b" ); break;}
11929
case char_type('\t'): {retval += string_conv<string_type>("\\t" ); break;}
11930
case char_type('\f'): {retval += string_conv<string_type>("\\f" ); break;}
11931
case char_type('\n'): {retval += string_conv<string_type>("\\n" ); break;}
11932
case char_type('\r'): {retval += string_conv<string_type>("\\r" ); break;}
11933
default :
11934
{
11935
if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e)
11936
{
11937
retval += string_conv<string_type>("\\e");
11938
}
11939
else if((char_type(0x00) <= c && c <= char_type(0x08)) ||
11940
(char_type(0x0A) <= c && c <= char_type(0x1F)) ||
11941
c == char_type(0x7F))
11942
{
11943
if(spec_.v1_1_0_add_escape_sequence_x)
11944
{
11945
retval += string_conv<string_type>("\\x");
11946
}
11947
else
11948
{
11949
retval += string_conv<string_type>("\\u00");
11950
}
11951
const auto c1 = c / 16;
11952
const auto c2 = c % 16;
11953
retval += static_cast<char_type>('0' + c1);
11954
if(c2 < 10)
11955
{
11956
retval += static_cast<char_type>('0' + c2);
11957
}
11958
else // 10 <= c2
11959
{
11960
retval += static_cast<char_type>('A' + (c2 - 10));
11961
}
11962
}
11963
else
11964
{
11965
retval += c;
11966
}
11967
}
11968
}
11969
}
11970
return retval;
11971
} // }}}
11972
11973
string_type escape_ml_basic_string(const string_type& s) // {{{
11974
{
11975
string_type retval;
11976
for(const char_type c : s)
11977
{
11978
switch(c)
11979
{
11980
case char_type('\\'): {retval += string_conv<string_type>("\\\\"); break;}
11981
case char_type('\b'): {retval += string_conv<string_type>("\\b" ); break;}
11982
case char_type('\t'): {retval += string_conv<string_type>("\\t" ); break;}
11983
case char_type('\f'): {retval += string_conv<string_type>("\\f" ); break;}
11984
case char_type('\n'): {retval += string_conv<string_type>("\n" ); break;}
11985
case char_type('\r'): {retval += string_conv<string_type>("\\r" ); break;}
11986
default :
11987
{
11988
if(c == char_type(0x1B) && spec_.v1_1_0_add_escape_sequence_e)
11989
{
11990
retval += string_conv<string_type>("\\e");
11991
}
11992
else if((char_type(0x00) <= c && c <= char_type(0x08)) ||
11993
(char_type(0x0A) <= c && c <= char_type(0x1F)) ||
11994
c == char_type(0x7F))
11995
{
11996
if(spec_.v1_1_0_add_escape_sequence_x)
11997
{
11998
retval += string_conv<string_type>("\\x");
11999
}
12000
else
12001
{
12002
retval += string_conv<string_type>("\\u00");
12003
}
12004
const auto c1 = c / 16;
12005
const auto c2 = c % 16;
12006
retval += static_cast<char_type>('0' + c1);
12007
if(c2 < 10)
12008
{
12009
retval += static_cast<char_type>('0' + c2);
12010
}
12011
else // 10 <= c2
12012
{
12013
retval += static_cast<char_type>('A' + (c2 - 10));
12014
}
12015
}
12016
else
12017
{
12018
retval += c;
12019
}
12020
}
12021
}
12022
}
12023
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
12024
// 3 consecutive `"`s are considered as a closing delimiter.
12025
// We need to check if there are 3 or more consecutive `"`s and insert
12026
// backslash to break them down into several short `"`s like the `str6`
12027
// in the following example.
12028
// ```toml
12029
// str4 = """Here are two quotation marks: "". Simple enough."""
12030
// # str5 = """Here are three quotation marks: """.""" # INVALID
12031
// str5 = """Here are three quotation marks: ""\"."""
12032
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
12033
// ```
12034
auto found_3_quotes = retval.find(string_conv<string_type>("\"\"\""));
12035
while(found_3_quotes != string_type::npos)
12036
{
12037
retval.replace(found_3_quotes, 3, string_conv<string_type>("\"\"\\\""));
12038
found_3_quotes = retval.find(string_conv<string_type>("\"\"\""));
12039
}
12040
return retval;
12041
} // }}}
12042
12043
string_type format_local_time(const local_time_type& t, const bool has_seconds, const std::size_t subsec_prec) // {{{
12044
{
12045
std::ostringstream oss;
12046
oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.hour);
12047
oss << ':';
12048
oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.minute);
12049
if(has_seconds)
12050
{
12051
oss << ':';
12052
oss << std::setfill('0') << std::setw(2) << static_cast<int>(t.second);
12053
if(subsec_prec != 0)
12054
{
12055
std::ostringstream subsec;
12056
subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.millisecond);
12057
subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.microsecond);
12058
subsec << std::setfill('0') << std::setw(3) << static_cast<int>(t.nanosecond);
12059
std::string subsec_str = subsec.str();
12060
oss << '.' << subsec_str.substr(0, subsec_prec);
12061
}
12062
}
12063
return string_conv<string_type>(oss.str());
12064
} // }}}
12065
12066
string_type format_ml_table(const table_type& t, const table_format_info& fmt) // {{{
12067
{
12068
const auto format_later = [](const value_type& v) -> bool {
12069
12070
const bool is_ml_table = v.is_table() &&
12071
v.as_table_fmt().fmt != table_format::oneline &&
12072
v.as_table_fmt().fmt != table_format::multiline_oneline &&
12073
v.as_table_fmt().fmt != table_format::dotted ;
12074
12075
const bool is_ml_array_table = v.is_array_of_tables() &&
12076
v.as_array_fmt().fmt != array_format::oneline &&
12077
v.as_array_fmt().fmt != array_format::multiline;
12078
12079
return is_ml_table || is_ml_array_table;
12080
};
12081
12082
string_type retval;
12083
this->current_indent_ += fmt.body_indent;
12084
for(const auto& kv : t)
12085
{
12086
const auto& key = kv.first;
12087
const auto& val = kv.second;
12088
if(format_later(val))
12089
{
12090
continue;
12091
}
12092
this->keys_.push_back(key);
12093
12094
retval += format_comments(val.comments(), fmt.indent_type);
12095
retval += format_indent(fmt.indent_type);
12096
if(val.is_table() && val.as_table_fmt().fmt == table_format::dotted)
12097
{
12098
retval += (*this)(val);
12099
}
12100
else
12101
{
12102
retval += format_key(key);
12103
retval += string_conv<string_type>(" = ");
12104
retval += (*this)(val);
12105
retval += char_type('\n');
12106
}
12107
this->keys_.pop_back();
12108
}
12109
this->current_indent_ -= fmt.body_indent;
12110
12111
if( ! retval.empty())
12112
{
12113
retval += char_type('\n'); // for readability, add empty line between tables
12114
}
12115
for(const auto& kv : t)
12116
{
12117
if( ! format_later(kv.second))
12118
{
12119
continue;
12120
}
12121
// must be a [multiline.table] or [[multiline.array.of.tables]].
12122
// comments will be generated inside it.
12123
this->keys_.push_back(kv.first);
12124
retval += (*this)(kv.second);
12125
this->keys_.pop_back();
12126
}
12127
return retval;
12128
} // }}}
12129
12130
string_type format_inline_table(const table_type& t, const table_format_info&) // {{{
12131
{
12132
// comments are ignored because we cannot write without newline
12133
string_type retval;
12134
retval += char_type('{');
12135
for(const auto& kv : t)
12136
{
12137
this->force_inline_ = true;
12138
retval += this->format_key(kv.first);
12139
retval += string_conv<string_type>(" = ");
12140
retval += (*this)(kv.second);
12141
retval += string_conv<string_type>(", ");
12142
}
12143
if( ! t.empty())
12144
{
12145
retval.pop_back(); // ' '
12146
retval.pop_back(); // ','
12147
}
12148
retval += char_type('}');
12149
this->force_inline_ = false;
12150
return retval;
12151
} // }}}
12152
12153
string_type format_ml_inline_table(const table_type& t, const table_format_info& fmt) // {{{
12154
{
12155
string_type retval;
12156
retval += string_conv<string_type>("{\n");
12157
this->current_indent_ += fmt.body_indent;
12158
for(const auto& kv : t)
12159
{
12160
this->force_inline_ = true;
12161
retval += format_comments(kv.second.comments(), fmt.indent_type);
12162
retval += format_indent(fmt.indent_type);
12163
retval += kv.first;
12164
retval += string_conv<string_type>(" = ");
12165
12166
this->force_inline_ = true;
12167
retval += (*this)(kv.second);
12168
12169
retval += string_conv<string_type>(",\n");
12170
}
12171
if( ! t.empty())
12172
{
12173
retval.pop_back(); // '\n'
12174
retval.pop_back(); // ','
12175
}
12176
this->current_indent_ -= fmt.body_indent;
12177
this->force_inline_ = false;
12178
12179
this->current_indent_ += fmt.closing_indent;
12180
retval += format_indent(fmt.indent_type);
12181
this->current_indent_ -= fmt.closing_indent;
12182
12183
retval += char_type('}');
12184
return retval;
12185
} // }}}
12186
12187
string_type format_dotted_table(const table_type& t, const table_format_info& fmt, // {{{
12188
const source_location&, std::vector<string_type>& keys)
12189
{
12190
// lets say we have: `{"a": {"b": {"c": {"d": "foo", "e": "bar"} } }`
12191
// and `a` and `b` are `dotted`.
12192
//
12193
// - in case if `c` is `oneline`:
12194
// ```toml
12195
// a.b.c = {d = "foo", e = "bar"}
12196
// ```
12197
//
12198
// - in case if and `c` is `dotted`:
12199
// ```toml
12200
// a.b.c.d = "foo"
12201
// a.b.c.e = "bar"
12202
// ```
12203
12204
string_type retval;
12205
12206
for(const auto& kv : t)
12207
{
12208
const auto& key = kv.first;
12209
const auto& val = kv.second;
12210
12211
keys.push_back(key);
12212
12213
// format recursive dotted table?
12214
if (val.is_table() &&
12215
val.as_table_fmt().fmt != table_format::oneline &&
12216
val.as_table_fmt().fmt != table_format::multiline_oneline)
12217
{
12218
retval += this->format_dotted_table(val.as_table(), val.as_table_fmt(), val.location(), keys);
12219
}
12220
else // non-table or inline tables. format normally
12221
{
12222
retval += format_comments(val.comments(), fmt.indent_type);
12223
retval += format_indent(fmt.indent_type);
12224
retval += format_keys(keys).value();
12225
retval += string_conv<string_type>(" = ");
12226
this->force_inline_ = true; // sub-table must be inlined
12227
retval += (*this)(val);
12228
retval += char_type('\n');
12229
this->force_inline_ = false;
12230
}
12231
keys.pop_back();
12232
}
12233
return retval;
12234
} // }}}
12235
12236
string_type format_key(const key_type& key) // {{{
12237
{
12238
if(key.empty())
12239
{
12240
return string_conv<string_type>("\"\"");
12241
}
12242
12243
// check the key can be a bare (unquoted) key
12244
auto loc = detail::make_temporary_location(string_conv<std::string>(key));
12245
auto reg = detail::syntax::unquoted_key(this->spec_).scan(loc);
12246
if(reg.is_ok() && loc.eof())
12247
{
12248
return key;
12249
}
12250
12251
//if it includes special characters, then format it in a "quoted" key.
12252
string_type formatted = string_conv<string_type>("\"");
12253
for(const char_type c : key)
12254
{
12255
switch(c)
12256
{
12257
case char_type('\\'): {formatted += string_conv<string_type>("\\\\"); break;}
12258
case char_type('\"'): {formatted += string_conv<string_type>("\\\""); break;}
12259
case char_type('\b'): {formatted += string_conv<string_type>("\\b" ); break;}
12260
case char_type('\t'): {formatted += string_conv<string_type>("\\t" ); break;}
12261
case char_type('\f'): {formatted += string_conv<string_type>("\\f" ); break;}
12262
case char_type('\n'): {formatted += string_conv<string_type>("\\n" ); break;}
12263
case char_type('\r'): {formatted += string_conv<string_type>("\\r" ); break;}
12264
default :
12265
{
12266
// ASCII ctrl char
12267
if( (char_type(0x00) <= c && c <= char_type(0x08)) ||
12268
(char_type(0x0A) <= c && c <= char_type(0x1F)) ||
12269
c == char_type(0x7F))
12270
{
12271
if(spec_.v1_1_0_add_escape_sequence_x)
12272
{
12273
formatted += string_conv<string_type>("\\x");
12274
}
12275
else
12276
{
12277
formatted += string_conv<string_type>("\\u00");
12278
}
12279
const auto c1 = c / 16;
12280
const auto c2 = c % 16;
12281
formatted += static_cast<char_type>('0' + c1);
12282
if(c2 < 10)
12283
{
12284
formatted += static_cast<char_type>('0' + c2);
12285
}
12286
else // 10 <= c2
12287
{
12288
formatted += static_cast<char_type>('A' + (c2 - 10));
12289
}
12290
}
12291
else
12292
{
12293
formatted += c;
12294
}
12295
break;
12296
}
12297
}
12298
}
12299
formatted += string_conv<string_type>("\"");
12300
return formatted;
12301
} // }}}
12302
cxx::optional<string_type> format_keys(const std::vector<key_type>& keys) // {{{
12303
{
12304
if(keys.empty())
12305
{
12306
return cxx::make_nullopt();
12307
}
12308
12309
string_type formatted;
12310
for(const auto& ky : keys)
12311
{
12312
formatted += format_key(ky);
12313
formatted += char_type('.');
12314
}
12315
formatted.pop_back(); // remove the last dot '.'
12316
return formatted;
12317
} // }}}
12318
12319
string_type format_comments(const discard_comments&, const indent_char) const // {{{
12320
{
12321
return string_conv<string_type>("");
12322
} // }}}
12323
string_type format_comments(const preserve_comments& comments, const indent_char indent_type) const // {{{
12324
{
12325
string_type retval;
12326
for(const auto& c : comments)
12327
{
12328
if(c.empty()) {continue;}
12329
retval += format_indent(indent_type);
12330
if(c.front() != '#') {retval += char_type('#');}
12331
retval += string_conv<string_type>(c);
12332
if(c.back() != '\n') {retval += char_type('\n');}
12333
}
12334
return retval;
12335
} // }}}
12336
12337
string_type format_indent(const indent_char indent_type) const // {{{
12338
{
12339
const auto indent = static_cast<std::size_t>((std::max)(0, this->current_indent_));
12340
if(indent_type == indent_char::space)
12341
{
12342
return string_conv<string_type>(make_string(indent, ' '));
12343
}
12344
else if(indent_type == indent_char::tab)
12345
{
12346
return string_conv<string_type>(make_string(indent, '\t'));
12347
}
12348
else
12349
{
12350
return string_type{};
12351
}
12352
} // }}}
12353
12354
std::locale set_locale(std::ostream& os) const
12355
{
12356
return os.imbue(std::locale::classic());
12357
}
12358
12359
private:
12360
12361
spec spec_;
12362
bool force_inline_; // table inside an array without fmt specification
12363
std::int32_t current_indent_;
12364
std::vector<key_type> keys_;
12365
};
12366
} // detail
12367
12368
template<typename TC>
12369
typename basic_value<TC>::string_type
12370
format(const basic_value<TC>& v, const spec s = spec::default_version())
12371
{
12372
detail::serializer<TC> ser(s);
12373
return ser(v);
12374
}
12375
template<typename TC>
12376
typename basic_value<TC>::string_type
12377
format(const typename basic_value<TC>::key_type& k,
12378
const basic_value<TC>& v,
12379
const spec s = spec::default_version())
12380
{
12381
detail::serializer<TC> ser(s);
12382
return ser(k, v);
12383
}
12384
template<typename TC>
12385
typename basic_value<TC>::string_type
12386
format(const std::vector<typename basic_value<TC>::key_type>& ks,
12387
const basic_value<TC>& v,
12388
const spec s = spec::default_version())
12389
{
12390
detail::serializer<TC> ser(s);
12391
return ser(ks, v);
12392
}
12393
12394
template<typename TC>
12395
std::ostream& operator<<(std::ostream& os, const basic_value<TC>& v)
12396
{
12397
os << format(v);
12398
return os;
12399
}
12400
12401
} // TOML11_INLINE_VERSION_NAMESPACE
12402
} // toml
12403
12404
#if defined(TOML11_COMPILE_SOURCES)
12405
namespace toml
12406
{
12407
inline namespace TOML11_INLINE_VERSION_NAMESPACE
12408
{
12409
struct type_config;
12410
struct ordered_type_config;
12411
12412
extern template typename basic_value<type_config>::string_type
12413
format<type_config>(const basic_value<type_config>&, const spec);
12414
12415
extern template typename basic_value<type_config>::string_type
12416
format<type_config>(const typename basic_value<type_config>::key_type& k,
12417
const basic_value<type_config>& v, const spec);
12418
12419
extern template typename basic_value<type_config>::string_type
12420
format<type_config>(const std::vector<typename basic_value<type_config>::key_type>& ks,
12421
const basic_value<type_config>& v, const spec s);
12422
12423
extern template typename basic_value<type_config>::string_type
12424
format<ordered_type_config>(const basic_value<ordered_type_config>&, const spec);
12425
12426
extern template typename basic_value<type_config>::string_type
12427
format<ordered_type_config>(const typename basic_value<ordered_type_config>::key_type& k,
12428
const basic_value<ordered_type_config>& v, const spec);
12429
12430
extern template typename basic_value<type_config>::string_type
12431
format<ordered_type_config>(const std::vector<typename basic_value<ordered_type_config>::key_type>& ks,
12432
const basic_value<ordered_type_config>& v, const spec s);
12433
12434
namespace detail
12435
{
12436
extern template class serializer<::toml::type_config>;
12437
extern template class serializer<::toml::ordered_type_config>;
12438
} // detail
12439
} // TOML11_INLINE_VERSION_NAMESPACE
12440
} // toml
12441
#endif // TOML11_COMPILE_SOURCES
12442
12443
12444
#endif // TOML11_SERIALIZER_HPP
12445
#ifndef TOML11_GET_HPP
12446
#define TOML11_GET_HPP
12447
12448
#include <algorithm>
12449
12450
12451
#if defined(TOML11_HAS_STRING_VIEW)
12452
#include <string_view>
12453
#endif // string_view
12454
12455
namespace toml
12456
{
12457
inline namespace TOML11_INLINE_VERSION_NAMESPACE
12458
{
12459
12460
// ============================================================================
12461
// T is toml::value; identity transformation.
12462
12463
template<typename T, typename TC>
12464
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>&
12465
get(basic_value<TC>& v)
12466
{
12467
return v;
12468
}
12469
12470
template<typename T, typename TC>
12471
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T> const&
12472
get(const basic_value<TC>& v)
12473
{
12474
return v;
12475
}
12476
12477
template<typename T, typename TC>
12478
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>
12479
get(basic_value<TC>&& v)
12480
{
12481
return basic_value<TC>(std::move(v));
12482
}
12483
12484
// ============================================================================
12485
// exact toml::* type
12486
12487
template<typename T, typename TC>
12488
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> &
12489
get(basic_value<TC>& v)
12490
{
12491
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
12492
return detail::getter<TC, ty>::get(v);
12493
}
12494
12495
template<typename T, typename TC>
12496
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const&
12497
get(const basic_value<TC>& v)
12498
{
12499
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
12500
return detail::getter<TC, ty>::get(v);
12501
}
12502
12503
template<typename T, typename TC>
12504
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T>
12505
get(basic_value<TC>&& v)
12506
{
12507
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
12508
return detail::getter<TC, ty>::get(std::move(v));
12509
}
12510
12511
// ============================================================================
12512
// T is toml::basic_value<U>
12513
12514
template<typename T, typename TC>
12515
cxx::enable_if_t<cxx::conjunction<
12516
detail::is_basic_value<T>,
12517
cxx::negation<std::is_same<T, basic_value<TC>>>
12518
>::value, T>
12519
get(basic_value<TC> v)
12520
{
12521
return T(std::move(v));
12522
}
12523
12524
// ============================================================================
12525
// integer convertible from toml::value::integer_type
12526
12527
template<typename T, typename TC>
12528
cxx::enable_if_t<cxx::conjunction<
12529
std::is_integral<T>,
12530
cxx::negation<std::is_same<T, bool>>,
12531
detail::is_not_toml_type<T, basic_value<TC>>,
12532
cxx::negation<detail::has_from_toml_method<T, TC>>,
12533
cxx::negation<detail::has_specialized_from<T>>
12534
>::value, T>
12535
get(const basic_value<TC>& v)
12536
{
12537
return static_cast<T>(v.as_integer());
12538
}
12539
12540
// ============================================================================
12541
// floating point convertible from toml::value::floating_type
12542
12543
template<typename T, typename TC>
12544
cxx::enable_if_t<cxx::conjunction<
12545
std::is_floating_point<T>,
12546
detail::is_not_toml_type<T, basic_value<TC>>,
12547
cxx::negation<detail::has_from_toml_method<T, TC>>,
12548
cxx::negation<detail::has_specialized_from<T>>
12549
>::value, T>
12550
get(const basic_value<TC>& v)
12551
{
12552
return static_cast<T>(v.as_floating());
12553
}
12554
12555
// ============================================================================
12556
// std::string with different char/trait/allocator
12557
12558
template<typename T, typename TC>
12559
cxx::enable_if_t<cxx::conjunction<
12560
detail::is_not_toml_type<T, basic_value<TC>>,
12561
detail::is_1byte_std_basic_string<T>
12562
>::value, T>
12563
get(const basic_value<TC>& v)
12564
{
12565
return detail::string_conv<cxx::remove_cvref_t<T>>(v.as_string());
12566
}
12567
12568
// ============================================================================
12569
// std::string_view
12570
12571
#if defined(TOML11_HAS_STRING_VIEW)
12572
12573
template<typename T, typename TC>
12574
cxx::enable_if_t<detail::is_string_view_of<T, typename basic_value<TC>::string_type>::value, T>
12575
get(const basic_value<TC>& v)
12576
{
12577
return T(v.as_string());
12578
}
12579
12580
#endif // string_view
12581
12582
// ============================================================================
12583
// std::chrono::duration from toml::local_time
12584
12585
template<typename T, typename TC>
12586
cxx::enable_if_t<detail::is_chrono_duration<T>::value, T>
12587
get(const basic_value<TC>& v)
12588
{
12589
return std::chrono::duration_cast<T>(
12590
std::chrono::nanoseconds(v.as_local_time()));
12591
}
12592
12593
// ============================================================================
12594
// std::chrono::system_clock::time_point from toml::datetime variants
12595
12596
template<typename T, typename TC>
12597
cxx::enable_if_t<
12598
std::is_same<std::chrono::system_clock::time_point, T>::value, T>
12599
get(const basic_value<TC>& v)
12600
{
12601
switch(v.type())
12602
{
12603
case value_t::local_date:
12604
{
12605
return std::chrono::system_clock::time_point(v.as_local_date());
12606
}
12607
case value_t::local_datetime:
12608
{
12609
return std::chrono::system_clock::time_point(v.as_local_datetime());
12610
}
12611
case value_t::offset_datetime:
12612
{
12613
return std::chrono::system_clock::time_point(v.as_offset_datetime());
12614
}
12615
default:
12616
{
12617
const auto loc = v.location();
12618
throw type_error(format_error("toml::get: "
12619
"bad_cast to std::chrono::system_clock::time_point", loc,
12620
"the actual type is " + to_string(v.type())), loc);
12621
}
12622
}
12623
}
12624
12625
// ============================================================================
12626
// forward declaration to use this recursively. ignore this and go ahead.
12627
12628
// array-like (w/ push_back)
12629
template<typename T, typename TC>
12630
cxx::enable_if_t<cxx::conjunction<
12631
detail::is_container<T>, // T is a container
12632
detail::has_push_back_method<T>, // .push_back() works
12633
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
12634
cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT>
12635
#if defined(TOML11_HAS_STRING_VIEW)
12636
cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT>
12637
#endif
12638
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
12639
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12640
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
12641
>::value, T>
12642
get(const basic_value<TC>&);
12643
12644
// std::array
12645
template<typename T, typename TC>
12646
cxx::enable_if_t<detail::is_std_array<T>::value, T>
12647
get(const basic_value<TC>&);
12648
12649
// std::forward_list
12650
template<typename T, typename TC>
12651
cxx::enable_if_t<detail::is_std_forward_list<T>::value, T>
12652
get(const basic_value<TC>&);
12653
12654
// std::pair<T1, T2>
12655
template<typename T, typename TC>
12656
cxx::enable_if_t<detail::is_std_pair<T>::value, T>
12657
get(const basic_value<TC>&);
12658
12659
// std::tuple<T1, T2, ...>
12660
template<typename T, typename TC>
12661
cxx::enable_if_t<detail::is_std_tuple<T>::value, T>
12662
get(const basic_value<TC>&);
12663
12664
// std::map<key, value> (key is convertible from toml::value::key_type)
12665
template<typename T, typename TC>
12666
cxx::enable_if_t<cxx::conjunction<
12667
detail::is_map<T>, // T is map
12668
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
12669
std::is_convertible<typename basic_value<TC>::key_type,
12670
typename T::key_type>, // keys are convertible
12671
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
12672
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12673
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
12674
>::value, T>
12675
get(const basic_value<TC>& v);
12676
12677
// std::map<key, value> (key is not convertible from toml::value::key_type, but
12678
// is a std::basic_string)
12679
template<typename T, typename TC>
12680
cxx::enable_if_t<cxx::conjunction<
12681
detail::is_map<T>, // T is map
12682
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
12683
cxx::negation<std::is_convertible<typename basic_value<TC>::key_type,
12684
typename T::key_type>>, // keys are NOT convertible
12685
detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string
12686
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
12687
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12688
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
12689
>::value, T>
12690
get(const basic_value<TC>& v);
12691
12692
// toml::from<T>::from_toml(v)
12693
template<typename T, typename TC>
12694
cxx::enable_if_t<detail::has_specialized_from<T>::value, T>
12695
get(const basic_value<TC>&);
12696
12697
// has T.from_toml(v) but no from<T>
12698
template<typename T, typename TC>
12699
cxx::enable_if_t<cxx::conjunction<
12700
detail::has_from_toml_method<T, TC>, // has T.from_toml()
12701
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12702
std::is_default_constructible<T> // T{} works
12703
>::value, T>
12704
get(const basic_value<TC>&);
12705
12706
// T(const toml::value&) and T is not toml::basic_value,
12707
// and it does not have `from<T>` nor `from_toml`.
12708
template<typename T, typename TC>
12709
cxx::enable_if_t<cxx::conjunction<
12710
std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&)
12711
cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself
12712
cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml()
12713
cxx::negation<detail::has_specialized_from<T>> // no toml::from<T>
12714
>::value, T>
12715
get(const basic_value<TC>&);
12716
12717
// ============================================================================
12718
// array-like types; most likely STL container, like std::vector, etc.
12719
12720
template<typename T, typename TC>
12721
cxx::enable_if_t<cxx::conjunction<
12722
detail::is_container<T>, // T is a container
12723
detail::has_push_back_method<T>, // .push_back() works
12724
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
12725
cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT>
12726
#if defined(TOML11_HAS_STRING_VIEW)
12727
cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT>
12728
#endif
12729
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
12730
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12731
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
12732
>::value, T>
12733
get(const basic_value<TC>& v)
12734
{
12735
using value_type = typename T::value_type;
12736
const auto& a = v.as_array();
12737
12738
T container;
12739
detail::try_reserve(container, a.size()); // if T has .reserve(), call it
12740
12741
for(const auto& elem : a)
12742
{
12743
container.push_back(get<value_type>(elem));
12744
}
12745
return container;
12746
}
12747
12748
// ============================================================================
12749
// std::array
12750
12751
template<typename T, typename TC>
12752
cxx::enable_if_t<detail::is_std_array<T>::value, T>
12753
get(const basic_value<TC>& v)
12754
{
12755
using value_type = typename T::value_type;
12756
const auto& a = v.as_array();
12757
12758
T container;
12759
if(a.size() != container.size())
12760
{
12761
const auto loc = v.location();
12762
throw std::out_of_range(format_error("toml::get: while converting to an array: "
12763
" array size is " + std::to_string(container.size()) +
12764
" but there are " + std::to_string(a.size()) + " elements in toml array.",
12765
loc, "here"));
12766
}
12767
for(std::size_t i=0; i<a.size(); ++i)
12768
{
12769
container.at(i) = ::toml::get<value_type>(a.at(i));
12770
}
12771
return container;
12772
}
12773
12774
// ============================================================================
12775
// std::forward_list
12776
12777
template<typename T, typename TC>
12778
cxx::enable_if_t<detail::is_std_forward_list<T>::value, T>
12779
get(const basic_value<TC>& v)
12780
{
12781
using value_type = typename T::value_type;
12782
12783
T container;
12784
for(const auto& elem : v.as_array())
12785
{
12786
container.push_front(get<value_type>(elem));
12787
}
12788
container.reverse();
12789
return container;
12790
}
12791
12792
// ============================================================================
12793
// std::pair
12794
12795
template<typename T, typename TC>
12796
cxx::enable_if_t<detail::is_std_pair<T>::value, T>
12797
get(const basic_value<TC>& v)
12798
{
12799
using first_type = typename T::first_type;
12800
using second_type = typename T::second_type;
12801
12802
const auto& ar = v.as_array();
12803
if(ar.size() != 2)
12804
{
12805
const auto loc = v.location();
12806
throw std::out_of_range(format_error("toml::get: while converting std::pair: "
12807
" but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.",
12808
loc, "here"));
12809
}
12810
return std::make_pair(::toml::get<first_type >(ar.at(0)),
12811
::toml::get<second_type>(ar.at(1)));
12812
}
12813
12814
// ============================================================================
12815
// std::tuple.
12816
12817
namespace detail
12818
{
12819
template<typename T, typename Array, std::size_t ... I>
12820
T get_tuple_impl(const Array& a, cxx::index_sequence<I...>)
12821
{
12822
return std::make_tuple(
12823
::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...);
12824
}
12825
} // detail
12826
12827
template<typename T, typename TC>
12828
cxx::enable_if_t<detail::is_std_tuple<T>::value, T>
12829
get(const basic_value<TC>& v)
12830
{
12831
const auto& ar = v.as_array();
12832
if(ar.size() != std::tuple_size<T>::value)
12833
{
12834
const auto loc = v.location();
12835
throw std::out_of_range(format_error("toml::get: while converting std::tuple: "
12836
" there are " + std::to_string(ar.size()) + " > " +
12837
std::to_string(std::tuple_size<T>::value) + " elements in toml array.",
12838
loc, "here"));
12839
}
12840
return detail::get_tuple_impl<T>(ar,
12841
cxx::make_index_sequence<std::tuple_size<T>::value>{});
12842
}
12843
12844
// ============================================================================
12845
// std::unordered_set
12846
12847
template<typename T, typename TC>
12848
cxx::enable_if_t<toml::detail::is_unordered_set<T>::value, T>
12849
get(const basic_value<TC>& v)
12850
{
12851
using value_type = typename T::value_type;
12852
const auto& a = v.as_array();
12853
12854
T container;
12855
for (const auto& elem : a)
12856
{
12857
container.insert(get<value_type>(elem));
12858
}
12859
return container;
12860
}
12861
12862
12863
// ============================================================================
12864
// map-like types; most likely STL map, like std::map or std::unordered_map.
12865
12866
// key is convertible from toml::value::key_type
12867
template<typename T, typename TC>
12868
cxx::enable_if_t<cxx::conjunction<
12869
detail::is_map<T>, // T is map
12870
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
12871
std::is_convertible<typename basic_value<TC>::key_type,
12872
typename T::key_type>, // keys are convertible
12873
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
12874
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12875
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
12876
>::value, T>
12877
get(const basic_value<TC>& v)
12878
{
12879
using key_type = typename T::key_type;
12880
using mapped_type = typename T::mapped_type;
12881
static_assert(
12882
std::is_convertible<typename basic_value<TC>::key_type, key_type>::value,
12883
"toml::get only supports map type of which key_type is "
12884
"convertible from toml::basic_value::key_type.");
12885
12886
T m;
12887
for(const auto& kv : v.as_table())
12888
{
12889
m.emplace(key_type(kv.first), get<mapped_type>(kv.second));
12890
}
12891
return m;
12892
}
12893
12894
// key is NOT convertible from toml::value::key_type but std::basic_string
12895
template<typename T, typename TC>
12896
cxx::enable_if_t<cxx::conjunction<
12897
detail::is_map<T>, // T is map
12898
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
12899
cxx::negation<std::is_convertible<typename basic_value<TC>::key_type,
12900
typename T::key_type>>, // keys are NOT convertible
12901
detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string
12902
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
12903
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12904
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
12905
>::value, T>
12906
get(const basic_value<TC>& v)
12907
{
12908
using key_type = typename T::key_type;
12909
using mapped_type = typename T::mapped_type;
12910
12911
T m;
12912
for(const auto& kv : v.as_table())
12913
{
12914
m.emplace(detail::string_conv<key_type>(kv.first), get<mapped_type>(kv.second));
12915
}
12916
return m;
12917
}
12918
12919
// ============================================================================
12920
// user-defined, but convertible types.
12921
12922
// toml::from<T>
12923
template<typename T, typename TC>
12924
cxx::enable_if_t<detail::has_specialized_from<T>::value, T>
12925
get(const basic_value<TC>& v)
12926
{
12927
return ::toml::from<T>::from_toml(v);
12928
}
12929
12930
// has T.from_toml(v) but no from<T>
12931
template<typename T, typename TC>
12932
cxx::enable_if_t<cxx::conjunction<
12933
detail::has_from_toml_method<T, TC>, // has T.from_toml()
12934
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
12935
std::is_default_constructible<T> // T{} works
12936
>::value, T>
12937
get(const basic_value<TC>& v)
12938
{
12939
T ud;
12940
ud.from_toml(v);
12941
return ud;
12942
}
12943
12944
// T(const toml::value&) and T is not toml::basic_value,
12945
// and it does not have `from<T>` nor `from_toml`.
12946
template<typename T, typename TC>
12947
cxx::enable_if_t<cxx::conjunction<
12948
std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&)
12949
cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself
12950
cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml()
12951
cxx::negation<detail::has_specialized_from<T>> // no toml::from<T>
12952
>::value, T>
12953
get(const basic_value<TC>& v)
12954
{
12955
return T(v);
12956
}
12957
12958
// ============================================================================
12959
// get_or(value, fallback)
12960
12961
template<typename TC>
12962
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
12963
get_or(const basic_value<TC>& v, const basic_value<TC>&)
12964
{
12965
return v;
12966
}
12967
12968
template<typename TC>
12969
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
12970
get_or(basic_value<TC>& v, basic_value<TC>&)
12971
{
12972
return v;
12973
}
12974
12975
template<typename TC>
12976
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
12977
get_or(basic_value<TC>&& v, basic_value<TC>&&)
12978
{
12979
return v;
12980
}
12981
12982
// ----------------------------------------------------------------------------
12983
// specialization for the exact toml types (return type becomes lvalue ref)
12984
12985
template<typename T, typename TC>
12986
cxx::enable_if_t<
12987
detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const&
12988
get_or(const basic_value<TC>& v, const T& opt) noexcept
12989
{
12990
try
12991
{
12992
return get<cxx::remove_cvref_t<T>>(v);
12993
}
12994
catch(...)
12995
{
12996
return opt;
12997
}
12998
}
12999
template<typename T, typename TC>
13000
cxx::enable_if_t<cxx::conjunction<
13001
cxx::negation<std::is_const<T>>,
13002
detail::is_exact_toml_type<T, basic_value<TC>>
13003
>::value, T>&
13004
get_or(basic_value<TC>& v, T& opt) noexcept
13005
{
13006
try
13007
{
13008
return get<cxx::remove_cvref_t<T>>(v);
13009
}
13010
catch(...)
13011
{
13012
return opt;
13013
}
13014
}
13015
template<typename T, typename TC>
13016
cxx::enable_if_t<detail::is_exact_toml_type<cxx::remove_cvref_t<T>,
13017
basic_value<TC>>::value, cxx::remove_cvref_t<T>>
13018
get_or(basic_value<TC>&& v, T&& opt) noexcept
13019
{
13020
try
13021
{
13022
return get<cxx::remove_cvref_t<T>>(std::move(v));
13023
}
13024
catch(...)
13025
{
13026
return cxx::remove_cvref_t<T>(std::forward<T>(opt));
13027
}
13028
}
13029
13030
// ----------------------------------------------------------------------------
13031
// specialization for string literal
13032
13033
// template<std::size_t N, typename TC>
13034
// typename basic_value<TC>::string_type
13035
// get_or(const basic_value<TC>& v,
13036
// const typename basic_value<TC>::string_type::value_type (&opt)[N])
13037
// {
13038
// try
13039
// {
13040
// return v.as_string();
13041
// }
13042
// catch(...)
13043
// {
13044
// return typename basic_value<TC>::string_type(opt);
13045
// }
13046
// }
13047
//
13048
// The above only matches to the literal, like `get_or(v, "foo");` but not
13049
// ```cpp
13050
// const auto opt = "foo";
13051
// const auto str = get_or(v, opt);
13052
// ```
13053
// . And the latter causes an error.
13054
// To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to
13055
// a character here.
13056
13057
template<typename TC>
13058
typename basic_value<TC>::string_type
13059
get_or(const basic_value<TC>& v,
13060
const typename basic_value<TC>::string_type::value_type* opt)
13061
{
13062
try
13063
{
13064
return v.as_string();
13065
}
13066
catch(...)
13067
{
13068
return typename basic_value<TC>::string_type(opt);
13069
}
13070
}
13071
13072
// ----------------------------------------------------------------------------
13073
// others (require type conversion and return type cannot be lvalue reference)
13074
13075
template<typename T, typename TC>
13076
cxx::enable_if_t<cxx::conjunction<
13077
cxx::negation<detail::is_basic_value<T>>,
13078
cxx::negation<detail::is_exact_toml_type<T, basic_value<TC>>>,
13079
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, typename basic_value<TC>::string_type::value_type const*>>
13080
>::value, cxx::remove_cvref_t<T>>
13081
get_or(const basic_value<TC>& v, T&& opt)
13082
{
13083
try
13084
{
13085
return get<cxx::remove_cvref_t<T>>(v);
13086
}
13087
catch(...)
13088
{
13089
return cxx::remove_cvref_t<T>(std::forward<T>(opt));
13090
}
13091
}
13092
13093
} // TOML11_INLINE_VERSION_NAMESPACE
13094
} // toml
13095
#endif // TOML11_GET_HPP
13096
#ifndef TOML11_FIND_HPP
13097
#define TOML11_FIND_HPP
13098
13099
#include <algorithm>
13100
13101
13102
#if defined(TOML11_HAS_STRING_VIEW)
13103
#include <string_view>
13104
#endif
13105
13106
namespace toml
13107
{
13108
inline namespace TOML11_INLINE_VERSION_NAMESPACE
13109
{
13110
13111
// ----------------------------------------------------------------------------
13112
// find<T>(value, key);
13113
13114
template<typename T, typename TC>
13115
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13116
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))>
13117
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
13118
{
13119
return ::toml::get<T>(v.at(ky));
13120
}
13121
13122
template<typename T, typename TC>
13123
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13124
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))>
13125
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
13126
{
13127
return ::toml::get<T>(v.at(ky));
13128
}
13129
13130
template<typename T, typename TC>
13131
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13132
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))>
13133
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
13134
{
13135
return ::toml::get<T>(std::move(v.at(ky)));
13136
}
13137
13138
// ----------------------------------------------------------------------------
13139
// find<T>(value, idx)
13140
13141
template<typename T, typename TC>
13142
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13143
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))>
13144
find(const basic_value<TC>& v, const std::size_t idx)
13145
{
13146
return ::toml::get<T>(v.at(idx));
13147
}
13148
template<typename T, typename TC>
13149
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13150
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))>
13151
find(basic_value<TC>& v, const std::size_t idx)
13152
{
13153
return ::toml::get<T>(v.at(idx));
13154
}
13155
template<typename T, typename TC>
13156
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13157
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))>
13158
find(basic_value<TC>&& v, const std::size_t idx)
13159
{
13160
return ::toml::get<T>(std::move(v.at(idx)));
13161
}
13162
13163
// ----------------------------------------------------------------------------
13164
// find(value, key/idx), w/o conversion
13165
13166
template<typename TC>
13167
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
13168
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
13169
{
13170
return v.at(ky);
13171
}
13172
template<typename TC>
13173
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
13174
find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky)
13175
{
13176
return v.at(ky);
13177
}
13178
template<typename TC>
13179
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
13180
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
13181
{
13182
return basic_value<TC>(std::move(v.at(ky)));
13183
}
13184
13185
template<typename TC>
13186
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
13187
find(basic_value<TC>& v, const std::size_t idx)
13188
{
13189
return v.at(idx);
13190
}
13191
template<typename TC>
13192
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
13193
find(basic_value<TC> const& v, const std::size_t idx)
13194
{
13195
return v.at(idx);
13196
}
13197
template<typename TC>
13198
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
13199
find(basic_value<TC>&& v, const std::size_t idx)
13200
{
13201
return basic_value<TC>(std::move(v.at(idx)));
13202
}
13203
13204
// --------------------------------------------------------------------------
13205
// find<optional<T>>
13206
13207
#if defined(TOML11_HAS_OPTIONAL)
13208
template<typename T, typename TC>
13209
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
13210
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
13211
{
13212
if(v.contains(ky))
13213
{
13214
return ::toml::get<typename T::value_type>(v.at(ky));
13215
}
13216
else
13217
{
13218
return std::nullopt;
13219
}
13220
}
13221
13222
template<typename T, typename TC>
13223
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
13224
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
13225
{
13226
if(v.contains(ky))
13227
{
13228
return ::toml::get<typename T::value_type>(v.at(ky));
13229
}
13230
else
13231
{
13232
return std::nullopt;
13233
}
13234
}
13235
13236
template<typename T, typename TC>
13237
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
13238
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
13239
{
13240
if(v.contains(ky))
13241
{
13242
return ::toml::get<typename T::value_type>(std::move(v.at(ky)));
13243
}
13244
else
13245
{
13246
return std::nullopt;
13247
}
13248
}
13249
13250
template<typename T, typename K, typename TC>
13251
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K>::value, T>
13252
find(const basic_value<TC>& v, const K& k)
13253
{
13254
if(static_cast<std::size_t>(k) < v.size())
13255
{
13256
return ::toml::get<typename T::value_type>(v.at(static_cast<std::size_t>(k)));
13257
}
13258
else
13259
{
13260
return std::nullopt;
13261
}
13262
}
13263
13264
template<typename T, typename K, typename TC>
13265
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K>::value, T>
13266
find(basic_value<TC>& v, const K& k)
13267
{
13268
if(static_cast<std::size_t>(k) < v.size())
13269
{
13270
return ::toml::get<typename T::value_type>(v.at(static_cast<std::size_t>(k)));
13271
}
13272
else
13273
{
13274
return std::nullopt;
13275
}
13276
}
13277
13278
template<typename T, typename K, typename TC>
13279
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K>::value, T>
13280
find(basic_value<TC>&& v, const K& k)
13281
{
13282
if(static_cast<std::size_t>(k) < v.size())
13283
{
13284
return ::toml::get<typename T::value_type>(std::move(v.at(static_cast<std::size_t>(k))));
13285
}
13286
else
13287
{
13288
return std::nullopt;
13289
}
13290
}
13291
#endif // optional
13292
13293
// --------------------------------------------------------------------------
13294
// toml::find(toml::value, toml::key, Ts&& ... keys)
13295
13296
namespace detail
13297
{
13298
13299
// It suppresses warnings by -Wsign-conversion when we pass integer literal
13300
// to toml::find. integer literal `0` is deduced as an int, and will be
13301
// converted to std::size_t. This causes sign-conversion.
13302
13303
template<typename TC>
13304
std::size_t key_cast(const std::size_t& v) noexcept
13305
{
13306
return v;
13307
}
13308
template<typename TC, typename T>
13309
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
13310
key_cast(const T& v) noexcept
13311
{
13312
return static_cast<std::size_t>(v);
13313
}
13314
13315
// for string-like (string, string literal, string_view)
13316
13317
template<typename TC>
13318
typename basic_value<TC>::key_type const&
13319
key_cast(const typename basic_value<TC>::key_type& v) noexcept
13320
{
13321
return v;
13322
}
13323
template<typename TC>
13324
typename basic_value<TC>::key_type
13325
key_cast(const typename basic_value<TC>::key_type::value_type* v)
13326
{
13327
return typename basic_value<TC>::key_type(v);
13328
}
13329
#if defined(TOML11_HAS_STRING_VIEW)
13330
template<typename TC>
13331
typename basic_value<TC>::key_type
13332
key_cast(const std::string_view v)
13333
{
13334
return typename basic_value<TC>::key_type(v);
13335
}
13336
#endif // string_view
13337
13338
} // detail
13339
13340
// ----------------------------------------------------------------------------
13341
// find(v, keys...)
13342
13343
template<typename TC, typename K1, typename K2, typename ... Ks>
13344
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
13345
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
13346
{
13347
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
13348
}
13349
template<typename TC, typename K1, typename K2, typename ... Ks>
13350
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
13351
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
13352
{
13353
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
13354
}
13355
template<typename TC, typename K1, typename K2, typename ... Ks>
13356
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
13357
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
13358
{
13359
return find(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
13360
}
13361
13362
// ----------------------------------------------------------------------------
13363
// find<T>(v, keys...)
13364
13365
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
13366
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13367
decltype(::toml::get<T>(std::declval<const basic_value<TC>&>()))>
13368
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
13369
{
13370
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
13371
}
13372
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
13373
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13374
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))>
13375
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
13376
{
13377
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
13378
}
13379
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
13380
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
13381
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))>
13382
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
13383
{
13384
return find<T>(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
13385
}
13386
13387
#if defined(TOML11_HAS_OPTIONAL)
13388
template<typename T, typename TC, typename K2, typename ... Ks>
13389
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
13390
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& k1, const K2& k2, const Ks& ... ks)
13391
{
13392
if(v.contains(k1))
13393
{
13394
return find<T>(v.at(k1), detail::key_cast<TC>(k2), ks...);
13395
}
13396
else
13397
{
13398
return std::nullopt;
13399
}
13400
}
13401
template<typename T, typename TC, typename K2, typename ... Ks>
13402
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
13403
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& k1, const K2& k2, const Ks& ... ks)
13404
{
13405
if(v.contains(k1))
13406
{
13407
return find<T>(v.at(k1), detail::key_cast<TC>(k2), ks...);
13408
}
13409
else
13410
{
13411
return std::nullopt;
13412
}
13413
}
13414
template<typename T, typename TC, typename K2, typename ... Ks>
13415
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
13416
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& k1, const K2& k2, const Ks& ... ks)
13417
{
13418
if(v.contains(k1))
13419
{
13420
return find<T>(v.at(k1), detail::key_cast<TC>(k2), ks...);
13421
}
13422
else
13423
{
13424
return std::nullopt;
13425
}
13426
}
13427
13428
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
13429
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K1>::value, T>
13430
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
13431
{
13432
if(static_cast<std::size_t>(k1) < v.size())
13433
{
13434
return find<T>(v.at(static_cast<std::size_t>(k1)), detail::key_cast<TC>(k2), ks...);
13435
}
13436
else
13437
{
13438
return std::nullopt;
13439
}
13440
}
13441
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
13442
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K1>::value, T>
13443
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
13444
{
13445
if(static_cast<std::size_t>(k1) < v.size())
13446
{
13447
return find<T>(v.at(static_cast<std::size_t>(k1)), detail::key_cast<TC>(k2), ks...);
13448
}
13449
else
13450
{
13451
return std::nullopt;
13452
}
13453
}
13454
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
13455
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K1>::value, T>
13456
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
13457
{
13458
if(static_cast<std::size_t>(k1) < v.size())
13459
{
13460
return find<T>(v.at(static_cast<std::size_t>(k1)), detail::key_cast<TC>(k2), ks...);
13461
}
13462
else
13463
{
13464
return std::nullopt;
13465
}
13466
}
13467
#endif // optional
13468
13469
// ===========================================================================
13470
// find_or<T>(value, key, fallback)
13471
13472
// ---------------------------------------------------------------------------
13473
// find_or(v, key, other_v)
13474
13475
template<typename TC, typename K>
13476
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
13477
find_or(basic_value<TC>& v, const K& k, basic_value<TC>& opt) noexcept
13478
{
13479
try
13480
{
13481
return ::toml::find(v, detail::key_cast<TC>(k));
13482
}
13483
catch(...)
13484
{
13485
return opt;
13486
}
13487
}
13488
template<typename TC, typename K>
13489
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
13490
find_or(const basic_value<TC>& v, const K& k, const basic_value<TC>& opt) noexcept
13491
{
13492
try
13493
{
13494
return ::toml::find(v, detail::key_cast<TC>(k));
13495
}
13496
catch(...)
13497
{
13498
return opt;
13499
}
13500
}
13501
template<typename TC, typename K>
13502
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
13503
find_or(basic_value<TC>&& v, const K& k, basic_value<TC>&& opt) noexcept
13504
{
13505
try
13506
{
13507
return ::toml::find(v, detail::key_cast<TC>(k));
13508
}
13509
catch(...)
13510
{
13511
return opt;
13512
}
13513
}
13514
13515
// ---------------------------------------------------------------------------
13516
// toml types (return type can be a reference)
13517
13518
template<typename T, typename TC, typename K>
13519
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
13520
cxx::remove_cvref_t<T> const&>
13521
find_or(const basic_value<TC>& v, const K& k, const T& opt)
13522
{
13523
try
13524
{
13525
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
13526
}
13527
catch(...)
13528
{
13529
return opt;
13530
}
13531
}
13532
13533
template<typename T, typename TC, typename K>
13534
cxx::enable_if_t<cxx::conjunction<
13535
cxx::negation<std::is_const<T>>,
13536
detail::is_exact_toml_type<T, basic_value<TC>>
13537
>::value, cxx::remove_cvref_t<T>&>
13538
find_or(basic_value<TC>& v, const K& k, T& opt)
13539
{
13540
try
13541
{
13542
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
13543
}
13544
catch(...)
13545
{
13546
return opt;
13547
}
13548
}
13549
13550
template<typename T, typename TC, typename K>
13551
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
13552
cxx::remove_cvref_t<T>>
13553
find_or(basic_value<TC>&& v, const K& k, T opt)
13554
{
13555
try
13556
{
13557
return ::toml::get<T>(std::move(v.at(detail::key_cast<TC>(k))));
13558
}
13559
catch(...)
13560
{
13561
return T(std::move(opt));
13562
}
13563
}
13564
13565
// ---------------------------------------------------------------------------
13566
// string literal (deduced as std::string)
13567
13568
// XXX to avoid confusion when T is explicitly specified in find_or<T>(),
13569
// we restrict the string type as std::string.
13570
template<typename TC, typename K>
13571
cxx::enable_if_t<detail::is_type_config<TC>::value, std::string>
13572
find_or(const basic_value<TC>& v, const K& k, const char* opt)
13573
{
13574
try
13575
{
13576
return ::toml::get<std::string>(v.at(detail::key_cast<TC>(k)));
13577
}
13578
catch(...)
13579
{
13580
return std::string(opt);
13581
}
13582
}
13583
13584
// ---------------------------------------------------------------------------
13585
// other types (requires type conversion and return type cannot be a reference)
13586
13587
template<typename T, typename TC, typename K>
13588
cxx::enable_if_t<cxx::conjunction<
13589
cxx::negation<detail::is_basic_value<cxx::remove_cvref_t<T>>>,
13590
detail::is_not_toml_type<cxx::remove_cvref_t<T>, basic_value<TC>>,
13591
cxx::negation<std::is_same<cxx::remove_cvref_t<T>,
13592
const typename basic_value<TC>::string_type::value_type*>>
13593
>::value, cxx::remove_cvref_t<T>>
13594
find_or(const basic_value<TC>& v, const K& ky, T opt)
13595
{
13596
try
13597
{
13598
return ::toml::get<cxx::remove_cvref_t<T>>(v.at(detail::key_cast<TC>(ky)));
13599
}
13600
catch(...)
13601
{
13602
return cxx::remove_cvref_t<T>(std::move(opt));
13603
}
13604
}
13605
13606
// ----------------------------------------------------------------------------
13607
// recursive
13608
13609
namespace detail
13610
{
13611
13612
template<typename ...Ts>
13613
auto last_one(Ts&&... args)
13614
-> decltype(std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...)))
13615
{
13616
return std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...));
13617
}
13618
13619
} // detail
13620
13621
template<typename Value, typename K1, typename K2, typename K3, typename ... Ks>
13622
auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept
13623
-> cxx::enable_if_t<
13624
detail::is_basic_value<cxx::remove_cvref_t<Value>>::value,
13625
decltype(find_or(v, k2, std::forward<K3>(k3), std::forward<Ks>(keys)...))
13626
>
13627
{
13628
try
13629
{
13630
return find_or(v.at(k1), k2, std::forward<K3>(k3), std::forward<Ks>(keys)...);
13631
}
13632
catch(...)
13633
{
13634
return detail::last_one(k3, keys...);
13635
}
13636
}
13637
13638
template<typename T, typename TC, typename K1, typename K2, typename K3, typename ... Ks>
13639
T find_or(const basic_value<TC>& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept
13640
{
13641
try
13642
{
13643
return find_or<T>(v.at(k1), k2, k3, keys...);
13644
}
13645
catch(...)
13646
{
13647
return static_cast<T>(detail::last_one(k3, keys...));
13648
}
13649
}
13650
13651
// ===========================================================================
13652
// find_or_default<T>(value, key)
13653
13654
template<typename T, typename TC, typename K>
13655
cxx::enable_if_t<std::is_default_constructible<T>::value, T>
13656
find_or_default(const basic_value<TC>& v, K&& k) noexcept(std::is_nothrow_default_constructible<T>::value)
13657
{
13658
try
13659
{
13660
return ::toml::get<T>(v.at(detail::key_cast<TC>(std::forward<K>(k))));
13661
}
13662
catch(...)
13663
{
13664
return T();
13665
}
13666
}
13667
13668
template<typename T, typename TC, typename K1, typename ... Ks>
13669
cxx::enable_if_t<std::is_default_constructible<T>::value, T>
13670
find_or_default(const basic_value<TC>& v, K1&& k1, Ks&& ... keys) noexcept(std::is_nothrow_default_constructible<T>::value)
13671
{
13672
try
13673
{
13674
return find_or_default<T>(v.at(std::forward<K1>(k1)), std::forward<Ks>(keys)...);
13675
}
13676
catch(...)
13677
{
13678
return T();
13679
}
13680
}
13681
13682
} // TOML11_INLINE_VERSION_NAMESPACE
13683
} // toml
13684
#endif // TOML11_FIND_HPP
13685
#ifndef TOML11_CONVERSION_HPP
13686
#define TOML11_CONVERSION_HPP
13687
13688
13689
#if defined(TOML11_HAS_OPTIONAL)
13690
13691
#include <optional>
13692
13693
namespace toml
13694
{
13695
inline namespace TOML11_INLINE_VERSION_NAMESPACE
13696
{
13697
namespace detail
13698
{
13699
13700
template<typename T>
13701
inline constexpr bool is_optional_v = false;
13702
13703
template<typename T>
13704
inline constexpr bool is_optional_v<std::optional<T>> = true;
13705
13706
template<typename T, typename TC>
13707
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
13708
{
13709
if constexpr(is_optional_v<T>)
13710
{
13711
if(v.contains(var_name))
13712
{
13713
obj = toml::find<typename T::value_type>(v, var_name);
13714
}
13715
else
13716
{
13717
obj = std::nullopt;
13718
}
13719
}
13720
else
13721
{
13722
obj = toml::find<T>(v, var_name);
13723
}
13724
}
13725
13726
template<typename T, typename TC>
13727
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
13728
{
13729
if constexpr(is_optional_v<T>)
13730
{
13731
if(obj.has_value())
13732
{
13733
v[var_name] = obj.value();
13734
}
13735
}
13736
else
13737
{
13738
v[var_name] = obj;
13739
}
13740
}
13741
13742
} // detail
13743
} // TOML11_INLINE_VERSION_NAMESPACE
13744
} // toml
13745
13746
#else
13747
13748
namespace toml
13749
{
13750
inline namespace TOML11_INLINE_VERSION_NAMESPACE
13751
{
13752
namespace detail
13753
{
13754
13755
template<typename T, typename TC>
13756
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
13757
{
13758
obj = toml::find<T>(v, var_name);
13759
}
13760
13761
template<typename T, typename TC>
13762
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
13763
{
13764
v[var_name] = obj;
13765
}
13766
13767
} // detail
13768
} // TOML11_INLINE_VERSION_NAMESPACE
13769
} // toml
13770
13771
#endif // optional
13772
13773
// use it in the following way.
13774
// ```cpp
13775
// namespace foo
13776
// {
13777
// struct Foo
13778
// {
13779
// std::string s;
13780
// double d;
13781
// int i;
13782
// };
13783
// } // foo
13784
//
13785
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
13786
// ```
13787
//
13788
// And then you can use `toml::get<foo::Foo>(v)` and `toml::find<foo::Foo>(file, "foo");`
13789
//
13790
13791
#define TOML11_STRINGIZE_AUX(x) #x
13792
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
13793
13794
#define TOML11_CONCATENATE_AUX(x, y) x##y
13795
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
13796
13797
// ============================================================================
13798
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
13799
13800
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
13801
13802
// ----------------------------------------------------------------------------
13803
// TOML11_ARGS_SIZE
13804
13805
#define TOML11_INDEX_RSEQ() \
13806
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
13807
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
13808
#define TOML11_ARGS_SIZE_IMPL(\
13809
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \
13810
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \
13811
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \
13812
ARG31, ARG32, N, ...) N
13813
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
13814
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
13815
13816
// ----------------------------------------------------------------------------
13817
// TOML11_FOR_EACH_VA_ARGS
13818
13819
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
13820
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
13821
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
13822
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
13823
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
13824
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
13825
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
13826
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
13827
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
13828
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
13829
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
13830
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
13831
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
13832
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
13833
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
13834
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
13835
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
13836
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
13837
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
13838
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
13839
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
13840
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
13841
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
13842
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
13843
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
13844
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
13845
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
13846
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
13847
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
13848
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
13849
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
13850
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
13851
13852
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
13853
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
13854
13855
13856
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
13857
toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME));
13858
13859
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
13860
toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME));
13861
13862
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
13863
namespace toml { \
13864
inline namespace TOML11_INLINE_VERSION_NAMESPACE { \
13865
template<> \
13866
struct from<NAME> \
13867
{ \
13868
template<typename TC> \
13869
static NAME from_toml(const basic_value<TC>& v) \
13870
{ \
13871
NAME obj; \
13872
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
13873
return obj; \
13874
} \
13875
}; \
13876
template<> \
13877
struct into<NAME> \
13878
{ \
13879
template<typename TC> \
13880
static basic_value<TC> into_toml(const NAME& obj) \
13881
{ \
13882
::toml::basic_value<TC> v = typename ::toml::basic_value<TC>::table_type{}; \
13883
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
13884
return v; \
13885
} \
13886
}; \
13887
} /* TOML11_INLINE_VERSION_NAMESPACE */ \
13888
} /* toml */
13889
13890
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
13891
13892
#endif // TOML11_CONVERSION_HPP
13893
#ifndef TOML11_CONTEXT_HPP
13894
#define TOML11_CONTEXT_HPP
13895
13896
13897
#include <vector>
13898
13899
namespace toml
13900
{
13901
inline namespace TOML11_INLINE_VERSION_NAMESPACE
13902
{
13903
namespace detail
13904
{
13905
13906
template<typename TypeConfig>
13907
class context
13908
{
13909
public:
13910
13911
explicit context(const spec& toml_spec)
13912
: toml_spec_(toml_spec), errors_{}
13913
{}
13914
13915
bool has_error() const noexcept {return !errors_.empty();}
13916
13917
std::vector<error_info> const& errors() const noexcept {return errors_;}
13918
13919
semantic_version& toml_version() noexcept {return toml_spec_.version;}
13920
semantic_version const& toml_version() const noexcept {return toml_spec_.version;}
13921
13922
spec& toml_spec() noexcept {return toml_spec_;}
13923
spec const& toml_spec() const noexcept {return toml_spec_;}
13924
13925
void report_error(error_info err)
13926
{
13927
this->errors_.push_back(std::move(err));
13928
}
13929
13930
error_info pop_last_error()
13931
{
13932
assert( ! errors_.empty());
13933
auto e = std::move(errors_.back());
13934
errors_.pop_back();
13935
return e;
13936
}
13937
13938
private:
13939
13940
spec toml_spec_;
13941
std::vector<error_info> errors_;
13942
};
13943
13944
} // detail
13945
} // TOML11_INLINE_VERSION_NAMESPACE
13946
} // toml
13947
13948
#if defined(TOML11_COMPILE_SOURCES)
13949
namespace toml
13950
{
13951
inline namespace TOML11_INLINE_VERSION_NAMESPACE
13952
{
13953
struct type_config;
13954
struct ordered_type_config;
13955
namespace detail
13956
{
13957
extern template class context<::toml::type_config>;
13958
extern template class context<::toml::ordered_type_config>;
13959
} // detail
13960
} // TOML11_INLINE_VERSION_NAMESPACE
13961
} // toml
13962
#endif // TOML11_COMPILE_SOURCES
13963
13964
#endif // TOML11_CONTEXT_HPP
13965
#ifndef TOML11_SKIP_HPP
13966
#define TOML11_SKIP_HPP
13967
13968
13969
#include <cassert>
13970
13971
namespace toml
13972
{
13973
inline namespace TOML11_INLINE_VERSION_NAMESPACE
13974
{
13975
namespace detail
13976
{
13977
13978
template<typename TC>
13979
bool skip_whitespace(location& loc, const context<TC>& ctx)
13980
{
13981
return syntax::ws(ctx.toml_spec()).scan(loc).is_ok();
13982
}
13983
13984
template<typename TC>
13985
bool skip_empty_lines(location& loc, const context<TC>& ctx)
13986
{
13987
return repeat_at_least(1, sequence(
13988
syntax::ws(ctx.toml_spec()),
13989
syntax::newline(ctx.toml_spec())
13990
)).scan(loc).is_ok();
13991
}
13992
13993
// For error recovery.
13994
//
13995
// In case if a comment line contains an invalid character, we need to skip it
13996
// to advance parsing.
13997
template<typename TC>
13998
void skip_comment_block(location& loc, const context<TC>& ctx)
13999
{
14000
while( ! loc.eof())
14001
{
14002
skip_whitespace(loc, ctx);
14003
if(loc.current() == '#')
14004
{
14005
while( ! loc.eof())
14006
{
14007
// both CRLF and LF ends with LF.
14008
if(loc.current() == '\n')
14009
{
14010
loc.advance();
14011
break;
14012
}
14013
}
14014
}
14015
else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok())
14016
{
14017
; // an empty line. skip this also
14018
}
14019
else
14020
{
14021
// the next token is neither a comment nor empty line.
14022
return ;
14023
}
14024
}
14025
return ;
14026
}
14027
14028
template<typename TC>
14029
void skip_empty_or_comment_lines(location& loc, const context<TC>& ctx)
14030
{
14031
const auto& spec = ctx.toml_spec();
14032
repeat_at_least(0, sequence(
14033
syntax::ws(spec),
14034
maybe(syntax::comment(spec)),
14035
syntax::newline(spec))
14036
).scan(loc);
14037
return ;
14038
}
14039
14040
// For error recovery.
14041
//
14042
// Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`.
14043
// To find delimiter, we need to skip delimiters in a string.
14044
// Since we are skipping invalid value while error recovery, we don't need
14045
// to check the syntax. Here we just skip string-like region until closing quote
14046
// is found.
14047
template<typename TC>
14048
void skip_string_like(location& loc, const context<TC>&)
14049
{
14050
// if """ is found, skip until the closing """ is found.
14051
if(literal("\"\"\"").scan(loc).is_ok())
14052
{
14053
while( ! loc.eof())
14054
{
14055
if(literal("\"\"\"").scan(loc).is_ok())
14056
{
14057
return;
14058
}
14059
loc.advance();
14060
}
14061
}
14062
else if(literal("'''").scan(loc).is_ok())
14063
{
14064
while( ! loc.eof())
14065
{
14066
if(literal("'''").scan(loc).is_ok())
14067
{
14068
return;
14069
}
14070
loc.advance();
14071
}
14072
}
14073
// if " is found, skip until the closing " or newline is found.
14074
else if(loc.current() == '"')
14075
{
14076
while( ! loc.eof())
14077
{
14078
loc.advance();
14079
if(loc.current() == '"' || loc.current() == '\n')
14080
{
14081
loc.advance();
14082
return;
14083
}
14084
}
14085
}
14086
else if(loc.current() == '\'')
14087
{
14088
while( ! loc.eof())
14089
{
14090
loc.advance();
14091
if(loc.current() == '\'' || loc.current() == '\n')
14092
{
14093
loc.advance();
14094
return ;
14095
}
14096
}
14097
}
14098
return;
14099
}
14100
14101
template<typename TC>
14102
void skip_value(location& loc, const context<TC>& ctx);
14103
template<typename TC>
14104
void skip_array_like(location& loc, const context<TC>& ctx);
14105
template<typename TC>
14106
void skip_inline_table_like(location& loc, const context<TC>& ctx);
14107
template<typename TC>
14108
void skip_key_value_pair(location& loc, const context<TC>& ctx);
14109
14110
template<typename TC>
14111
result<value_t, error_info>
14112
guess_value_type(const location& loc, const context<TC>& ctx);
14113
14114
template<typename TC>
14115
void skip_array_like(location& loc, const context<TC>& ctx)
14116
{
14117
const auto& spec = ctx.toml_spec();
14118
assert(loc.current() == '[');
14119
loc.advance();
14120
14121
while( ! loc.eof())
14122
{
14123
if(loc.current() == '\"' || loc.current() == '\'')
14124
{
14125
skip_string_like(loc, ctx);
14126
}
14127
else if(loc.current() == '#')
14128
{
14129
skip_comment_block(loc, ctx);
14130
}
14131
else if(loc.current() == '{')
14132
{
14133
skip_inline_table_like(loc, ctx);
14134
}
14135
else if(loc.current() == '[')
14136
{
14137
const auto checkpoint = loc;
14138
if(syntax::std_table(spec).scan(loc).is_ok() ||
14139
syntax::array_table(spec).scan(loc).is_ok())
14140
{
14141
loc = checkpoint;
14142
break;
14143
}
14144
// if it is not a table-definition, then it is an array.
14145
skip_array_like(loc, ctx);
14146
}
14147
else if(loc.current() == '=')
14148
{
14149
// key-value pair cannot be inside the array.
14150
// guessing the error is "missing closing bracket `]`".
14151
// find the previous key just before `=`.
14152
while(loc.get_location() != 0)
14153
{
14154
loc.retrace();
14155
if(loc.current() == '\n')
14156
{
14157
loc.advance();
14158
break;
14159
}
14160
}
14161
break;
14162
}
14163
else if(loc.current() == ']')
14164
{
14165
break; // found closing bracket
14166
}
14167
else
14168
{
14169
loc.advance();
14170
}
14171
}
14172
return ;
14173
}
14174
14175
template<typename TC>
14176
void skip_inline_table_like(location& loc, const context<TC>& ctx)
14177
{
14178
assert(loc.current() == '{');
14179
loc.advance();
14180
14181
const auto& spec = ctx.toml_spec();
14182
14183
while( ! loc.eof())
14184
{
14185
if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables)
14186
{
14187
break; // missing closing `}`.
14188
}
14189
else if(loc.current() == '\"' || loc.current() == '\'')
14190
{
14191
skip_string_like(loc, ctx);
14192
}
14193
else if(loc.current() == '#')
14194
{
14195
skip_comment_block(loc, ctx);
14196
if( ! spec.v1_1_0_allow_newlines_in_inline_tables)
14197
{
14198
// comment must end with newline.
14199
break; // missing closing `}`.
14200
}
14201
}
14202
else if(loc.current() == '[')
14203
{
14204
const auto checkpoint = loc;
14205
if(syntax::std_table(spec).scan(loc).is_ok() ||
14206
syntax::array_table(spec).scan(loc).is_ok())
14207
{
14208
loc = checkpoint;
14209
break; // missing closing `}`.
14210
}
14211
// if it is not a table-definition, then it is an array.
14212
skip_array_like(loc, ctx);
14213
}
14214
else if(loc.current() == '{')
14215
{
14216
skip_inline_table_like(loc, ctx);
14217
}
14218
else if(loc.current() == '}')
14219
{
14220
// closing brace found. guessing the error is inside the table.
14221
break;
14222
}
14223
else
14224
{
14225
// skip otherwise.
14226
loc.advance();
14227
}
14228
}
14229
return ;
14230
}
14231
14232
template<typename TC>
14233
void skip_value(location& loc, const context<TC>& ctx)
14234
{
14235
value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty);
14236
if(ty == value_t::string)
14237
{
14238
skip_string_like(loc, ctx);
14239
}
14240
else if(ty == value_t::array)
14241
{
14242
skip_array_like(loc, ctx);
14243
}
14244
else if(ty == value_t::table)
14245
{
14246
// In case of multiline tables, it may skip key-value pair but not the
14247
// whole table.
14248
skip_inline_table_like(loc, ctx);
14249
}
14250
else // others are an "in-line" values. skip until the next line
14251
{
14252
while( ! loc.eof())
14253
{
14254
if(loc.current() == '\n')
14255
{
14256
break;
14257
}
14258
else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}')
14259
{
14260
break;
14261
}
14262
loc.advance();
14263
}
14264
}
14265
return;
14266
}
14267
14268
template<typename TC>
14269
void skip_key_value_pair(location& loc, const context<TC>& ctx)
14270
{
14271
while( ! loc.eof())
14272
{
14273
if(loc.current() == '=')
14274
{
14275
skip_whitespace(loc, ctx);
14276
skip_value(loc, ctx);
14277
return;
14278
}
14279
else if(loc.current() == '\n')
14280
{
14281
// newline is found before finding `=`. assuming "missing `=`".
14282
return;
14283
}
14284
loc.advance();
14285
}
14286
return ;
14287
}
14288
14289
template<typename TC>
14290
void skip_until_next_table(location& loc, const context<TC>& ctx)
14291
{
14292
const auto& spec = ctx.toml_spec();
14293
while( ! loc.eof())
14294
{
14295
if(loc.current() == '\n')
14296
{
14297
loc.advance();
14298
const auto line_begin = loc;
14299
14300
skip_whitespace(loc, ctx);
14301
if(syntax::std_table(spec).scan(loc).is_ok())
14302
{
14303
loc = line_begin;
14304
return ;
14305
}
14306
if(syntax::array_table(spec).scan(loc).is_ok())
14307
{
14308
loc = line_begin;
14309
return ;
14310
}
14311
}
14312
loc.advance();
14313
}
14314
}
14315
14316
} // namespace detail
14317
} // TOML11_INLINE_VERSION_NAMESPACE
14318
} // namespace toml
14319
14320
#if defined(TOML11_COMPILE_SOURCES)
14321
namespace toml
14322
{
14323
inline namespace TOML11_INLINE_VERSION_NAMESPACE
14324
{
14325
struct type_config;
14326
struct ordered_type_config;
14327
14328
namespace detail
14329
{
14330
extern template bool skip_whitespace <type_config>(location& loc, const context<type_config>&);
14331
extern template bool skip_empty_lines <type_config>(location& loc, const context<type_config>&);
14332
extern template void skip_comment_block <type_config>(location& loc, const context<type_config>&);
14333
extern template void skip_empty_or_comment_lines<type_config>(location& loc, const context<type_config>&);
14334
extern template void skip_string_like <type_config>(location& loc, const context<type_config>&);
14335
extern template void skip_array_like <type_config>(location& loc, const context<type_config>&);
14336
extern template void skip_inline_table_like <type_config>(location& loc, const context<type_config>&);
14337
extern template void skip_value <type_config>(location& loc, const context<type_config>&);
14338
extern template void skip_key_value_pair <type_config>(location& loc, const context<type_config>&);
14339
extern template void skip_until_next_table <type_config>(location& loc, const context<type_config>&);
14340
14341
extern template bool skip_whitespace <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14342
extern template bool skip_empty_lines <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14343
extern template void skip_comment_block <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14344
extern template void skip_empty_or_comment_lines<ordered_type_config>(location& loc, const context<ordered_type_config>&);
14345
extern template void skip_string_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14346
extern template void skip_array_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14347
extern template void skip_inline_table_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14348
extern template void skip_value <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14349
extern template void skip_key_value_pair <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14350
extern template void skip_until_next_table <ordered_type_config>(location& loc, const context<ordered_type_config>&);
14351
14352
} // detail
14353
} // TOML11_INLINE_VERSION_NAMESPACE
14354
} // toml
14355
#endif // TOML11_COMPILE_SOURCES
14356
14357
#endif // TOML11_SKIP_HPP
14358
#ifndef TOML11_PARSER_HPP
14359
#define TOML11_PARSER_HPP
14360
14361
14362
#include <fstream>
14363
#include <sstream>
14364
14365
#include <cassert>
14366
#include <cmath>
14367
14368
#if defined(TOML11_HAS_FILESYSTEM) && TOML11_HAS_FILESYSTEM
14369
#include <filesystem>
14370
#endif
14371
14372
namespace toml
14373
{
14374
inline namespace TOML11_INLINE_VERSION_NAMESPACE
14375
{
14376
14377
struct syntax_error final : public ::toml::exception
14378
{
14379
public:
14380
syntax_error(std::string what_arg, std::vector<error_info> err)
14381
: what_(std::move(what_arg)), err_(std::move(err))
14382
{}
14383
~syntax_error() noexcept override = default;
14384
14385
const char* what() const noexcept override {return what_.c_str();}
14386
14387
std::vector<error_info> const& errors() const noexcept
14388
{
14389
return err_;
14390
}
14391
14392
private:
14393
std::string what_;
14394
std::vector<error_info> err_;
14395
};
14396
14397
struct file_io_error final : public ::toml::exception
14398
{
14399
public:
14400
14401
file_io_error(const std::string& msg, const std::string& fname)
14402
: errno_(cxx::make_nullopt()),
14403
what_(msg + " \"" + fname + "\"")
14404
{}
14405
file_io_error(int errnum, const std::string& msg, const std::string& fname)
14406
: errno_(errnum),
14407
what_(msg + " \"" + fname + "\": errno=" + std::to_string(errnum))
14408
{}
14409
~file_io_error() noexcept override = default;
14410
14411
const char* what() const noexcept override {return what_.c_str();}
14412
14413
bool has_errno() const noexcept {return errno_.has_value();}
14414
int get_errno() const noexcept {return errno_.value_or(0);}
14415
14416
private:
14417
14418
cxx::optional<int> errno_;
14419
std::string what_;
14420
};
14421
14422
namespace detail
14423
{
14424
14425
/* ============================================================================
14426
* __ ___ _ __ _ __ ___ _ _
14427
* / _/ _ \ ' \| ' \/ _ \ ' \
14428
* \__\___/_|_|_|_|_|_\___/_||_|
14429
*/
14430
14431
template<typename S>
14432
error_info make_syntax_error(std::string title,
14433
const S& scanner, location loc, std::string suffix = "")
14434
{
14435
auto msg = std::string("expected ") + scanner.expected_chars(loc);
14436
auto src = source_location(region(loc));
14437
return make_error_info(
14438
std::move(title), std::move(src), std::move(msg), std::move(suffix));
14439
}
14440
14441
14442
/* ============================================================================
14443
* _
14444
* __ ___ _ __ _ __ ___ _ _| |_
14445
* / _/ _ \ ' \| ' \/ -_) ' \ _|
14446
* \__\___/_|_|_|_|_|_\___|_||_\__|
14447
*/
14448
14449
template<typename TC>
14450
result<cxx::optional<std::string>, error_info>
14451
parse_comment_line(location& loc, context<TC>& ctx)
14452
{
14453
const auto& spec = ctx.toml_spec();
14454
const auto first = loc;
14455
14456
skip_whitespace(loc, ctx);
14457
14458
const auto com_reg = syntax::comment(spec).scan(loc);
14459
if(com_reg.is_ok())
14460
{
14461
// once comment started, newline must follow (or reach EOF).
14462
if( ! loc.eof() && ! syntax::newline(spec).scan(loc).is_ok())
14463
{
14464
while( ! loc.eof()) // skip until newline to continue parsing
14465
{
14466
loc.advance();
14467
if(loc.current() == '\n') { /*skip LF*/ loc.advance(); break; }
14468
}
14469
return err(make_error_info("toml::parse_comment_line: "
14470
"newline (LF / CRLF) or EOF is expected",
14471
source_location(region(loc)), "but got this",
14472
"Hint: most of the control characters are not allowed in comments"));
14473
}
14474
return ok(cxx::optional<std::string>(com_reg.as_string()));
14475
}
14476
else
14477
{
14478
loc = first; // rollback whitespace to parse indent
14479
return ok(cxx::optional<std::string>(cxx::make_nullopt()));
14480
}
14481
}
14482
14483
/* ============================================================================
14484
* ___ _
14485
* | _ ) ___ ___| |___ __ _ _ _
14486
* | _ \/ _ \/ _ \ / -_) _` | ' \
14487
* |___/\___/\___/_\___\__,_|_||_|
14488
*/
14489
14490
template<typename TC>
14491
result<basic_value<TC>, error_info>
14492
parse_boolean(location& loc, const context<TC>& ctx)
14493
{
14494
const auto& spec = ctx.toml_spec();
14495
14496
// ----------------------------------------------------------------------
14497
// check syntax
14498
auto reg = syntax::boolean(spec).scan(loc);
14499
if( ! reg.is_ok())
14500
{
14501
return err(make_syntax_error("toml::parse_boolean: "
14502
"invalid boolean: boolean must be `true` or `false`, in lowercase. "
14503
"string must be surrounded by `\"`", syntax::boolean(spec), loc));
14504
}
14505
14506
// ----------------------------------------------------------------------
14507
// it matches. gen value
14508
const auto str = reg.as_string();
14509
const auto val = [&str]() {
14510
if(str == "true")
14511
{
14512
return true;
14513
}
14514
else
14515
{
14516
assert(str == "false");
14517
return false;
14518
}
14519
}();
14520
14521
// ----------------------------------------------------------------------
14522
// no format info for boolean
14523
boolean_format_info fmt;
14524
14525
return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg)));
14526
}
14527
14528
/* ============================================================================
14529
* ___ _
14530
* |_ _|_ _| |_ ___ __ _ ___ _ _
14531
* | || ' \ _/ -_) _` / -_) '_|
14532
* |___|_||_\__\___\__, \___|_|
14533
* |___/
14534
*/
14535
14536
template<typename TC>
14537
result<basic_value<TC>, error_info>
14538
parse_bin_integer(location& loc, const context<TC>& ctx)
14539
{
14540
const auto first = loc;
14541
const auto& spec = ctx.toml_spec();
14542
auto reg = syntax::bin_int(spec).scan(loc);
14543
if( ! reg.is_ok())
14544
{
14545
return err(make_syntax_error("toml::parse_bin_integer: "
14546
"invalid integer: bin_int must be like: 0b0101, 0b1111_0000",
14547
syntax::bin_int(spec), loc));
14548
}
14549
14550
auto str = reg.as_string();
14551
14552
integer_format_info fmt;
14553
fmt.fmt = integer_format::bin;
14554
fmt.width = str.size() - 2 - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_'));
14555
14556
const auto first_underscore = std::find(str.rbegin(), str.rend(), '_');
14557
if(first_underscore != str.rend())
14558
{
14559
fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore));
14560
}
14561
14562
// skip prefix `0b` and zeros and underscores at the MSB
14563
str.erase(str.begin(), std::find(std::next(str.begin(), 2), str.end(), '1'));
14564
14565
// remove all `_` before calling TC::parse_int
14566
str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
14567
14568
// 0b0000_0000 becomes empty.
14569
if(str.empty()) { str = "0"; }
14570
14571
const auto val = TC::parse_int(str, source_location(region(loc)), 2);
14572
if(val.is_ok())
14573
{
14574
return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
14575
}
14576
else
14577
{
14578
loc = first;
14579
return err(val.as_err());
14580
}
14581
}
14582
14583
// ----------------------------------------------------------------------------
14584
14585
template<typename TC>
14586
result<basic_value<TC>, error_info>
14587
parse_oct_integer(location& loc, const context<TC>& ctx)
14588
{
14589
const auto first = loc;
14590
const auto& spec = ctx.toml_spec();
14591
auto reg = syntax::oct_int(spec).scan(loc);
14592
if( ! reg.is_ok())
14593
{
14594
return err(make_syntax_error("toml::parse_oct_integer: "
14595
"invalid integer: oct_int must be like: 0o775, 0o04_44",
14596
syntax::oct_int(spec), loc));
14597
}
14598
14599
auto str = reg.as_string();
14600
14601
integer_format_info fmt;
14602
fmt.fmt = integer_format::oct;
14603
fmt.width = str.size() - 2 - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_'));
14604
14605
const auto first_underscore = std::find(str.rbegin(), str.rend(), '_');
14606
if(first_underscore != str.rend())
14607
{
14608
fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore));
14609
}
14610
14611
// skip prefix `0o` and zeros and underscores at the MSB
14612
str.erase(str.begin(), std::find_if(
14613
std::next(str.begin(), 2), str.end(), [](const char c) {
14614
return c != '0' && c != '_';
14615
}));
14616
14617
// remove all `_` before calling TC::parse_int
14618
str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
14619
14620
// 0o0000_0000 becomes empty.
14621
if(str.empty()) { str = "0"; }
14622
14623
const auto val = TC::parse_int(str, source_location(region(loc)), 8);
14624
if(val.is_ok())
14625
{
14626
return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
14627
}
14628
else
14629
{
14630
loc = first;
14631
return err(val.as_err());
14632
}
14633
}
14634
14635
template<typename TC>
14636
result<basic_value<TC>, error_info>
14637
parse_hex_integer(location& loc, const context<TC>& ctx)
14638
{
14639
const auto first = loc;
14640
const auto& spec = ctx.toml_spec();
14641
auto reg = syntax::hex_int(spec).scan(loc);
14642
if( ! reg.is_ok())
14643
{
14644
return err(make_syntax_error("toml::parse_hex_integer: "
14645
"invalid integer: hex_int must be like: 0xC0FFEE, 0xdead_beef",
14646
syntax::hex_int(spec), loc));
14647
}
14648
14649
auto str = reg.as_string();
14650
14651
integer_format_info fmt;
14652
fmt.fmt = integer_format::hex;
14653
fmt.width = str.size() - 2 - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_'));
14654
14655
const auto first_underscore = std::find(str.rbegin(), str.rend(), '_');
14656
if(first_underscore != str.rend())
14657
{
14658
fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore));
14659
}
14660
14661
// skip prefix `0x` and zeros and underscores at the MSB
14662
str.erase(str.begin(), std::find_if(
14663
std::next(str.begin(), 2), str.end(), [](const char c) {
14664
return c != '0' && c != '_';
14665
}));
14666
14667
// remove all `_` before calling TC::parse_int
14668
str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
14669
14670
// 0x0000_0000 becomes empty.
14671
if(str.empty()) { str = "0"; }
14672
14673
// prefix zero and _ is removed. check if it uses upper/lower case.
14674
// if both upper and lower case letters are found, set upper=true.
14675
const auto lower_not_found = std::find_if(str.begin(), str.end(),
14676
[](const char c) { return std::islower(static_cast<int>(c)) != 0; }) == str.end();
14677
const auto upper_found = std::find_if(str.begin(), str.end(),
14678
[](const char c) { return std::isupper(static_cast<int>(c)) != 0; }) != str.end();
14679
fmt.uppercase = lower_not_found || upper_found;
14680
14681
const auto val = TC::parse_int(str, source_location(region(loc)), 16);
14682
if(val.is_ok())
14683
{
14684
return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
14685
}
14686
else
14687
{
14688
loc = first;
14689
return err(val.as_err());
14690
}
14691
}
14692
14693
template<typename TC>
14694
result<basic_value<TC>, error_info>
14695
parse_dec_integer(location& loc, const context<TC>& ctx)
14696
{
14697
const auto first = loc;
14698
const auto& spec = ctx.toml_spec();
14699
14700
// ----------------------------------------------------------------------
14701
// check syntax
14702
auto reg = syntax::dec_int(spec).scan(loc);
14703
if( ! reg.is_ok())
14704
{
14705
return err(make_syntax_error("toml::parse_dec_integer: "
14706
"invalid integer: dec_int must be like: 42, 123_456_789",
14707
syntax::dec_int(spec), loc));
14708
}
14709
14710
// ----------------------------------------------------------------------
14711
// it matches. gen value
14712
auto str = reg.as_string();
14713
14714
integer_format_info fmt;
14715
fmt.fmt = integer_format::dec;
14716
fmt.width = str.size() - static_cast<std::size_t>(std::count(str.begin(), str.end(), '_'));
14717
14718
const auto first_underscore = std::find(str.rbegin(), str.rend(), '_');
14719
if(first_underscore != str.rend())
14720
{
14721
fmt.spacer = static_cast<std::size_t>(std::distance(str.rbegin(), first_underscore));
14722
}
14723
14724
// remove all `_` before calling TC::parse_int
14725
str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
14726
14727
auto src = source_location(region(loc));
14728
const auto val = TC::parse_int(str, src, 10);
14729
if(val.is_err())
14730
{
14731
loc = first;
14732
return err(val.as_err());
14733
}
14734
14735
// ----------------------------------------------------------------------
14736
// parse suffix (extension)
14737
14738
if(spec.ext_num_suffix && loc.current() == '_')
14739
{
14740
const auto sfx_reg = syntax::num_suffix(spec).scan(loc);
14741
if( ! sfx_reg.is_ok())
14742
{
14743
loc = first;
14744
return err(make_error_info("toml::parse_dec_integer: "
14745
"invalid suffix: should be `_ non-digit-graph (graph | _graph)`",
14746
source_location(region(loc)), "here"));
14747
}
14748
auto sfx = sfx_reg.as_string();
14749
assert( ! sfx.empty() && sfx.front() == '_');
14750
sfx.erase(sfx.begin()); // remove the first `_`
14751
14752
fmt.suffix = sfx;
14753
}
14754
14755
return ok(basic_value<TC>(val.as_ok(), std::move(fmt), {}, std::move(reg)));
14756
}
14757
14758
template<typename TC>
14759
result<basic_value<TC>, error_info>
14760
parse_integer(location& loc, const context<TC>& ctx)
14761
{
14762
const auto first = loc;
14763
14764
if( ! loc.eof() && (loc.current() == '+' || loc.current() == '-'))
14765
{
14766
// skip +/- to diagnose +0xDEADBEEF or -0b0011 (invalid).
14767
// without this, +0xDEAD_BEEF will be parsed as a decimal int and
14768
// unexpected "xDEAD_BEEF" will appear after integer "+0".
14769
loc.advance();
14770
}
14771
14772
if( ! loc.eof() && loc.current() == '0')
14773
{
14774
loc.advance();
14775
if(loc.eof())
14776
{
14777
// `[+-]?0`. parse as an decimal integer.
14778
loc = first;
14779
return parse_dec_integer(loc, ctx);
14780
}
14781
14782
const auto prefix = loc.current();
14783
auto prefix_src = source_location(region(loc));
14784
14785
loc = first;
14786
14787
if(prefix == 'b') {return parse_bin_integer(loc, ctx);}
14788
if(prefix == 'o') {return parse_oct_integer(loc, ctx);}
14789
if(prefix == 'x') {return parse_hex_integer(loc, ctx);}
14790
14791
if(std::isdigit(prefix))
14792
{
14793
auto src = source_location(region(loc));
14794
return err(make_error_info("toml::parse_integer: "
14795
"leading zero in an decimal integer is not allowed",
14796
std::move(src), "leading zero"));
14797
}
14798
}
14799
14800
loc = first;
14801
return parse_dec_integer(loc, ctx);
14802
}
14803
14804
/* ============================================================================
14805
* ___ _ _ _
14806
* | __| |___ __ _| |_(_)_ _ __ _
14807
* | _|| / _ \/ _` | _| | ' \/ _` |
14808
* |_| |_\___/\__,_|\__|_|_||_\__, |
14809
* |___/
14810
*/
14811
14812
template<typename TC>
14813
result<basic_value<TC>, error_info>
14814
parse_floating(location& loc, const context<TC>& ctx)
14815
{
14816
using floating_type = typename basic_value<TC>::floating_type;
14817
14818
const auto first = loc;
14819
const auto& spec = ctx.toml_spec();
14820
14821
// ----------------------------------------------------------------------
14822
// check syntax
14823
bool is_hex = false;
14824
std::string str;
14825
region reg;
14826
if(spec.ext_hex_float && literal("0x").scan(loc).is_ok())
14827
{
14828
loc = first;
14829
is_hex = true;
14830
14831
reg = syntax::hex_floating(spec).scan(loc);
14832
if( ! reg.is_ok())
14833
{
14834
return err(make_syntax_error("toml::parse_floating: "
14835
"invalid hex floating: float must be like: 0xABCp-3f",
14836
syntax::floating(spec), loc));
14837
}
14838
str = reg.as_string();
14839
}
14840
else
14841
{
14842
reg = syntax::floating(spec).scan(loc);
14843
if( ! reg.is_ok())
14844
{
14845
return err(make_syntax_error("toml::parse_floating: "
14846
"invalid floating: float must be like: -3.14159_26535, 6.022e+23, "
14847
"inf, or nan (lowercase).", syntax::floating(spec), loc));
14848
}
14849
str = reg.as_string();
14850
}
14851
14852
// ----------------------------------------------------------------------
14853
// it matches. gen value
14854
14855
floating_format_info fmt;
14856
14857
if(is_hex)
14858
{
14859
fmt.fmt = floating_format::hex;
14860
}
14861
else
14862
{
14863
// since we already checked that the string conforms the TOML standard.
14864
if(std::find(str.begin(), str.end(), 'e') != str.end() ||
14865
std::find(str.begin(), str.end(), 'E') != str.end())
14866
{
14867
fmt.fmt = floating_format::scientific; // use exponent part
14868
}
14869
else
14870
{
14871
fmt.fmt = floating_format::fixed; // do not use exponent part
14872
}
14873
}
14874
14875
str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
14876
14877
floating_type val{0};
14878
14879
if(str == "inf" || str == "+inf")
14880
{
14881
TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_infinity)
14882
{
14883
val = std::numeric_limits<floating_type>::infinity();
14884
}
14885
else
14886
{
14887
return err(make_error_info("toml::parse_floating: inf value found"
14888
" but the current environment does not support inf. Please"
14889
" make sure that the floating-point implementation conforms"
14890
" IEEE 754/ISO 60559 international standard.",
14891
source_location(region(loc)),
14892
"floating_type: inf is not supported"));
14893
}
14894
}
14895
else if(str == "-inf")
14896
{
14897
TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_infinity)
14898
{
14899
val = -std::numeric_limits<floating_type>::infinity();
14900
}
14901
else
14902
{
14903
return err(make_error_info("toml::parse_floating: inf value found"
14904
" but the current environment does not support inf. Please"
14905
" make sure that the floating-point implementation conforms"
14906
" IEEE 754/ISO 60559 international standard.",
14907
source_location(region(loc)),
14908
"floating_type: inf is not supported"));
14909
}
14910
}
14911
else if(str == "nan" || str == "+nan")
14912
{
14913
TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_quiet_NaN)
14914
{
14915
val = std::numeric_limits<floating_type>::quiet_NaN();
14916
}
14917
else TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_signaling_NaN)
14918
{
14919
val = std::numeric_limits<floating_type>::signaling_NaN();
14920
}
14921
else
14922
{
14923
return err(make_error_info("toml::parse_floating: NaN value found"
14924
" but the current environment does not support NaN. Please"
14925
" make sure that the floating-point implementation conforms"
14926
" IEEE 754/ISO 60559 international standard.",
14927
source_location(region(loc)),
14928
"floating_type: NaN is not supported"));
14929
}
14930
}
14931
else if(str == "-nan")
14932
{
14933
using std::copysign;
14934
TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_quiet_NaN)
14935
{
14936
val = copysign(std::numeric_limits<floating_type>::quiet_NaN(), floating_type(-1));
14937
}
14938
else TOML11_CONSTEXPR_IF(std::numeric_limits<floating_type>::has_signaling_NaN)
14939
{
14940
val = copysign(std::numeric_limits<floating_type>::signaling_NaN(), floating_type(-1));
14941
}
14942
else
14943
{
14944
return err(make_error_info("toml::parse_floating: NaN value found"
14945
" but the current environment does not support NaN. Please"
14946
" make sure that the floating-point implementation conforms"
14947
" IEEE 754/ISO 60559 international standard.",
14948
source_location(region(loc)),
14949
"floating_type: NaN is not supported"));
14950
}
14951
}
14952
else
14953
{
14954
// set precision
14955
const auto has_sign = ! str.empty() && (str.front() == '+' || str.front() == '-');
14956
const auto decpoint = std::find(str.begin(), str.end(), '.');
14957
const auto exponent = std::find_if(str.begin(), str.end(),
14958
[](const char c) { return c == 'e' || c == 'E'; });
14959
if(decpoint != str.end() && exponent != str.end())
14960
{
14961
assert(decpoint < exponent);
14962
}
14963
14964
if(fmt.fmt == floating_format::scientific)
14965
{
14966
// total width
14967
fmt.prec = static_cast<std::size_t>(std::distance(str.begin(), exponent));
14968
if(has_sign)
14969
{
14970
fmt.prec -= 1;
14971
}
14972
if(decpoint != str.end())
14973
{
14974
fmt.prec -= 1;
14975
}
14976
}
14977
else if(fmt.fmt == floating_format::hex)
14978
{
14979
fmt.prec = std::numeric_limits<floating_type>::max_digits10;
14980
}
14981
else
14982
{
14983
// width after decimal point
14984
fmt.prec = static_cast<std::size_t>(std::distance(std::next(decpoint), exponent));
14985
}
14986
14987
auto src = source_location(region(loc));
14988
const auto res = TC::parse_float(str, src, is_hex);
14989
if(res.is_ok())
14990
{
14991
val = res.as_ok();
14992
}
14993
else
14994
{
14995
return err(res.as_err());
14996
}
14997
}
14998
14999
// ----------------------------------------------------------------------
15000
// parse suffix (extension)
15001
15002
if(spec.ext_num_suffix && loc.current() == '_')
15003
{
15004
const auto sfx_reg = syntax::num_suffix(spec).scan(loc);
15005
if( ! sfx_reg.is_ok())
15006
{
15007
auto src = source_location(region(loc));
15008
loc = first;
15009
return err(make_error_info("toml::parse_floating: "
15010
"invalid suffix: should be `_ non-digit-graph (graph | _graph)`",
15011
std::move(src), "here"));
15012
}
15013
auto sfx = sfx_reg.as_string();
15014
assert( ! sfx.empty() && sfx.front() == '_');
15015
sfx.erase(sfx.begin()); // remove the first `_`
15016
15017
fmt.suffix = sfx;
15018
}
15019
15020
return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg)));
15021
}
15022
15023
/* ============================================================================
15024
* ___ _ _ _
15025
* | \ __ _| |_ ___| |_(_)_ __ ___
15026
* | |) / _` | _/ -_) _| | ' \/ -_)
15027
* |___/\__,_|\__\___|\__|_|_|_|_\___|
15028
*/
15029
15030
// all the offset_datetime, local_datetime, local_date parses date part.
15031
template<typename TC>
15032
result<std::tuple<local_date, local_date_format_info, region>, error_info>
15033
parse_local_date_only(location& loc, const context<TC>& ctx)
15034
{
15035
const auto first = loc;
15036
const auto& spec = ctx.toml_spec();
15037
15038
local_date_format_info fmt;
15039
15040
// ----------------------------------------------------------------------
15041
// check syntax
15042
auto reg = syntax::local_date(spec).scan(loc);
15043
if( ! reg.is_ok())
15044
{
15045
return err(make_syntax_error("toml::parse_local_date: "
15046
"invalid date: date must be like: 1234-05-06, yyyy-mm-dd.",
15047
syntax::local_date(spec), loc));
15048
}
15049
15050
// ----------------------------------------------------------------------
15051
// it matches. gen value
15052
const auto str = reg.as_string();
15053
15054
// 0123456789
15055
// yyyy-mm-dd
15056
const auto year_r = from_string<int>(str.substr(0, 4));
15057
const auto month_r = from_string<int>(str.substr(5, 2));
15058
const auto day_r = from_string<int>(str.substr(8, 2));
15059
15060
if(year_r.is_err())
15061
{
15062
auto src = source_location(region(first));
15063
return err(make_error_info("toml::parse_local_date: "
15064
"failed to read year `" + str.substr(0, 4) + "`",
15065
std::move(src), "here"));
15066
}
15067
if(month_r.is_err())
15068
{
15069
auto src = source_location(region(first));
15070
return err(make_error_info("toml::parse_local_date: "
15071
"failed to read month `" + str.substr(5, 2) + "`",
15072
std::move(src), "here"));
15073
}
15074
if(day_r.is_err())
15075
{
15076
auto src = source_location(region(first));
15077
return err(make_error_info("toml::parse_local_date: "
15078
"failed to read day `" + str.substr(8, 2) + "`",
15079
std::move(src), "here"));
15080
}
15081
15082
const auto year = year_r.unwrap();
15083
const auto month = month_r.unwrap();
15084
const auto day = day_r.unwrap();
15085
15086
{
15087
// We briefly check whether the input date is valid or not.
15088
// Actually, because of the historical reasons, there are several
15089
// edge cases, such as 1582/10/5-1582/10/14 (only in several countries).
15090
// But here, we do not care about it.
15091
// It makes the code complicated and there is only low probability
15092
// that such a specific date is needed in practice. If someone need to
15093
// validate date accurately, that means that the one need a specialized
15094
// library for their purpose in another layer.
15095
15096
const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
15097
const auto max_day = [month, is_leap]() {
15098
if(month == 2)
15099
{
15100
return is_leap ? 29 : 28;
15101
}
15102
if(month == 4 || month == 6 || month == 9 || month == 11)
15103
{
15104
return 30;
15105
}
15106
return 31;
15107
}();
15108
15109
if((month < 1 || 12 < month) || (day < 1 || max_day < day))
15110
{
15111
auto src = source_location(region(first));
15112
return err(make_error_info("toml::parse_local_date: invalid date.",
15113
std::move(src), "month must be 01-12, day must be any of "
15114
"01-28,29,30,31 depending on the month/year."));
15115
}
15116
}
15117
15118
return ok(std::make_tuple(
15119
local_date(year, static_cast<month_t>(month - 1), day),
15120
std::move(fmt), std::move(reg)
15121
));
15122
}
15123
15124
template<typename TC>
15125
result<basic_value<TC>, error_info>
15126
parse_local_date(location& loc, const context<TC>& ctx)
15127
{
15128
auto val_fmt_reg = parse_local_date_only(loc, ctx);
15129
if(val_fmt_reg.is_err())
15130
{
15131
return err(val_fmt_reg.unwrap_err());
15132
}
15133
15134
auto val = std::move(std::get<0>(val_fmt_reg.unwrap()));
15135
auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap()));
15136
auto reg = std::move(std::get<2>(val_fmt_reg.unwrap()));
15137
15138
return ok(basic_value<TC>(std::move(val), std::move(fmt), {}, std::move(reg)));
15139
}
15140
15141
// all the offset_datetime, local_datetime, local_time parses date part.
15142
template<typename TC>
15143
result<std::tuple<local_time, local_time_format_info, region>, error_info>
15144
parse_local_time_only(location& loc, const context<TC>& ctx)
15145
{
15146
const auto first = loc;
15147
const auto& spec = ctx.toml_spec();
15148
15149
local_time_format_info fmt;
15150
15151
// ----------------------------------------------------------------------
15152
// check syntax
15153
auto reg = syntax::local_time(spec).scan(loc);
15154
if( ! reg.is_ok())
15155
{
15156
if(spec.v1_1_0_make_seconds_optional)
15157
{
15158
return err(make_syntax_error("toml::parse_local_time: "
15159
"invalid time: time must be HH:MM(:SS.sss) (seconds are optional)",
15160
syntax::local_time(spec), loc));
15161
}
15162
else
15163
{
15164
return err(make_syntax_error("toml::parse_local_time: "
15165
"invalid time: time must be HH:MM:SS(.sss) (subseconds are optional)",
15166
syntax::local_time(spec), loc));
15167
}
15168
}
15169
15170
// ----------------------------------------------------------------------
15171
// it matches. gen value
15172
const auto str = reg.as_string();
15173
15174
// at least we have HH:MM.
15175
// 01234
15176
// HH:MM
15177
const auto hour_r = from_string<int>(str.substr(0, 2));
15178
const auto minute_r = from_string<int>(str.substr(3, 2));
15179
15180
if(hour_r.is_err())
15181
{
15182
auto src = source_location(region(first));
15183
return err(make_error_info("toml::parse_local_time: "
15184
"failed to read hour `" + str.substr(0, 2) + "`",
15185
std::move(src), "here"));
15186
}
15187
if(minute_r.is_err())
15188
{
15189
auto src = source_location(region(first));
15190
return err(make_error_info("toml::parse_local_time: "
15191
"failed to read minute `" + str.substr(3, 2) + "`",
15192
std::move(src), "here"));
15193
}
15194
15195
const auto hour = hour_r.unwrap();
15196
const auto minute = minute_r.unwrap();
15197
15198
if((hour < 0 || 24 <= hour) || (minute < 0 || 60 <= minute))
15199
{
15200
auto src = source_location(region(first));
15201
return err(make_error_info("toml::parse_local_time: invalid time.",
15202
std::move(src), "hour must be 00-23, minute must be 00-59."));
15203
}
15204
15205
// -----------------------------------------------------------------------
15206
// we have hour and minute.
15207
// Since toml v1.1.0, second and subsecond part becomes optional.
15208
// Check the version and return if second does not exist.
15209
15210
if(str.size() == 5 && spec.v1_1_0_make_seconds_optional)
15211
{
15212
fmt.has_seconds = false;
15213
fmt.subsecond_precision = 0;
15214
return ok(std::make_tuple(local_time(hour, minute, 0), std::move(fmt), std::move(reg)));
15215
}
15216
assert(str.at(5) == ':');
15217
15218
// we have at least `:SS` part. `.subseconds` are optional.
15219
15220
// 0 1
15221
// 012345678901234
15222
// HH:MM:SS.subsec
15223
const auto sec_r = from_string<int>(str.substr(6, 2));
15224
if(sec_r.is_err())
15225
{
15226
auto src = source_location(region(first));
15227
return err(make_error_info("toml::parse_local_time: "
15228
"failed to read second `" + str.substr(6, 2) + "`",
15229
std::move(src), "here"));
15230
}
15231
const auto sec = sec_r.unwrap();
15232
15233
if(sec < 0 || 60 < sec) // :60 is allowed
15234
{
15235
auto src = source_location(region(first));
15236
return err(make_error_info("toml::parse_local_time: invalid time.",
15237
std::move(src), "second must be 00-60."));
15238
}
15239
15240
if(str.size() == 8)
15241
{
15242
fmt.has_seconds = true;
15243
fmt.subsecond_precision = 0;
15244
return ok(std::make_tuple(local_time(hour, minute, sec), std::move(fmt), std::move(reg)));
15245
}
15246
15247
assert(str.at(8) == '.');
15248
15249
auto secfrac = str.substr(9, str.size() - 9);
15250
15251
fmt.has_seconds = true;
15252
fmt.subsecond_precision = secfrac.size();
15253
15254
while(secfrac.size() < 9)
15255
{
15256
secfrac += '0';
15257
}
15258
assert(9 <= secfrac.size());
15259
const auto ms_r = from_string<int>(secfrac.substr(0, 3));
15260
const auto us_r = from_string<int>(secfrac.substr(3, 3));
15261
const auto ns_r = from_string<int>(secfrac.substr(6, 3));
15262
15263
if(ms_r.is_err())
15264
{
15265
auto src = source_location(region(first));
15266
return err(make_error_info("toml::parse_local_time: "
15267
"failed to read milliseconds `" + secfrac.substr(0, 3) + "`",
15268
std::move(src), "here"));
15269
}
15270
if(us_r.is_err())
15271
{
15272
auto src = source_location(region(first));
15273
return err(make_error_info("toml::parse_local_time: "
15274
"failed to read microseconds`" + str.substr(3, 3) + "`",
15275
std::move(src), "here"));
15276
}
15277
if(ns_r.is_err())
15278
{
15279
auto src = source_location(region(first));
15280
return err(make_error_info("toml::parse_local_time: "
15281
"failed to read nanoseconds`" + str.substr(6, 3) + "`",
15282
std::move(src), "here"));
15283
}
15284
const auto ms = ms_r.unwrap();
15285
const auto us = us_r.unwrap();
15286
const auto ns = ns_r.unwrap();
15287
15288
return ok(std::make_tuple(local_time(hour, minute, sec, ms, us, ns), std::move(fmt), std::move(reg)));
15289
}
15290
15291
template<typename TC>
15292
result<basic_value<TC>, error_info>
15293
parse_local_time(location& loc, const context<TC>& ctx)
15294
{
15295
const auto first = loc;
15296
15297
auto val_fmt_reg = parse_local_time_only(loc, ctx);
15298
if(val_fmt_reg.is_err())
15299
{
15300
return err(val_fmt_reg.unwrap_err());
15301
}
15302
15303
auto val = std::move(std::get<0>(val_fmt_reg.unwrap()));
15304
auto fmt = std::move(std::get<1>(val_fmt_reg.unwrap()));
15305
auto reg = std::move(std::get<2>(val_fmt_reg.unwrap()));
15306
15307
return ok(basic_value<TC>(std::move(val), std::move(fmt), {}, std::move(reg)));
15308
}
15309
15310
template<typename TC>
15311
result<basic_value<TC>, error_info>
15312
parse_local_datetime(location& loc, const context<TC>& ctx)
15313
{
15314
using char_type = location::char_type;
15315
15316
const auto first = loc;
15317
15318
local_datetime_format_info fmt;
15319
15320
// ----------------------------------------------------------------------
15321
15322
auto date_fmt_reg = parse_local_date_only(loc, ctx);
15323
if(date_fmt_reg.is_err())
15324
{
15325
return err(date_fmt_reg.unwrap_err());
15326
}
15327
15328
if(loc.current() == char_type('T'))
15329
{
15330
loc.advance();
15331
fmt.delimiter = datetime_delimiter_kind::upper_T;
15332
}
15333
else if(loc.current() == char_type('t'))
15334
{
15335
loc.advance();
15336
fmt.delimiter = datetime_delimiter_kind::lower_t;
15337
}
15338
else if(loc.current() == char_type(' '))
15339
{
15340
loc.advance();
15341
fmt.delimiter = datetime_delimiter_kind::space;
15342
}
15343
else
15344
{
15345
auto src = source_location(region(loc));
15346
return err(make_error_info("toml::parse_local_datetime: "
15347
"expect date-time delimiter `T`, `t` or ` `(space).",
15348
std::move(src), "here"));
15349
}
15350
15351
auto time_fmt_reg = parse_local_time_only(loc, ctx);
15352
if(time_fmt_reg.is_err())
15353
{
15354
return err(time_fmt_reg.unwrap_err());
15355
}
15356
15357
fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds;
15358
fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision;
15359
15360
// ----------------------------------------------------------------------
15361
15362
region reg(first, loc);
15363
local_datetime val(std::get<0>(date_fmt_reg.unwrap()),
15364
std::get<0>(time_fmt_reg.unwrap()));
15365
15366
return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg)));
15367
}
15368
15369
template<typename TC>
15370
result<basic_value<TC>, error_info>
15371
parse_offset_datetime(location& loc, const context<TC>& ctx)
15372
{
15373
using char_type = location::char_type;
15374
15375
const auto first = loc;
15376
const auto& spec = ctx.toml_spec();
15377
15378
offset_datetime_format_info fmt;
15379
15380
// ----------------------------------------------------------------------
15381
// date part
15382
15383
auto date_fmt_reg = parse_local_date_only(loc, ctx);
15384
if(date_fmt_reg.is_err())
15385
{
15386
return err(date_fmt_reg.unwrap_err());
15387
}
15388
15389
// ----------------------------------------------------------------------
15390
// delimiter
15391
15392
if(loc.current() == char_type('T'))
15393
{
15394
loc.advance();
15395
fmt.delimiter = datetime_delimiter_kind::upper_T;
15396
}
15397
else if(loc.current() == char_type('t'))
15398
{
15399
loc.advance();
15400
fmt.delimiter = datetime_delimiter_kind::lower_t;
15401
}
15402
else if(loc.current() == char_type(' '))
15403
{
15404
loc.advance();
15405
fmt.delimiter = datetime_delimiter_kind::space;
15406
}
15407
else
15408
{
15409
auto src = source_location(region(loc));
15410
return err(make_error_info("toml::parse_offset_datetime: "
15411
"expect date-time delimiter `T` or ` `(space).", std::move(src), "here"
15412
));
15413
}
15414
15415
// ----------------------------------------------------------------------
15416
// time part
15417
15418
auto time_fmt_reg = parse_local_time_only(loc, ctx);
15419
if(time_fmt_reg.is_err())
15420
{
15421
return err(time_fmt_reg.unwrap_err());
15422
}
15423
15424
fmt.has_seconds = std::get<1>(time_fmt_reg.unwrap()).has_seconds;
15425
fmt.subsecond_precision = std::get<1>(time_fmt_reg.unwrap()).subsecond_precision;
15426
15427
// ----------------------------------------------------------------------
15428
// offset part
15429
15430
const auto ofs_reg = syntax::time_offset(spec).scan(loc);
15431
if( ! ofs_reg.is_ok())
15432
{
15433
return err(make_syntax_error("toml::parse_offset_datetime: "
15434
"invalid offset: offset must be like: Z, +01:00, or -10:00.",
15435
syntax::time_offset(spec), loc));
15436
}
15437
15438
const auto ofs_str = ofs_reg.as_string();
15439
15440
time_offset offset(0, 0);
15441
15442
assert(ofs_str.size() != 0);
15443
15444
if(ofs_str.at(0) == char_type('+') || ofs_str.at(0) == char_type('-'))
15445
{
15446
const auto hour_r = from_string<int>(ofs_str.substr(1, 2));
15447
const auto minute_r = from_string<int>(ofs_str.substr(4, 2));
15448
if(hour_r.is_err())
15449
{
15450
auto src = source_location(region(loc));
15451
return err(make_error_info("toml::parse_offset_datetime: "
15452
"Failed to read offset hour part", std::move(src), "here"));
15453
}
15454
if(minute_r.is_err())
15455
{
15456
auto src = source_location(region(loc));
15457
return err(make_error_info("toml::parse_offset_datetime: "
15458
"Failed to read offset minute part", std::move(src), "here"));
15459
}
15460
const auto hour = hour_r.unwrap();
15461
const auto minute = minute_r.unwrap();
15462
15463
if(ofs_str.at(0) == '+')
15464
{
15465
offset = time_offset(hour, minute);
15466
}
15467
else
15468
{
15469
offset = time_offset(-hour, -minute);
15470
}
15471
}
15472
else
15473
{
15474
assert(ofs_str.at(0) == char_type('Z') || ofs_str.at(0) == char_type('z'));
15475
}
15476
15477
if (offset.hour < -24 || 24 < offset.hour ||
15478
offset.minute < -60 || 60 < offset.minute)
15479
{
15480
return err(make_error_info("toml::parse_offset_datetime: "
15481
"too large offset: |hour| <= 24, |minute| <= 60",
15482
source_location(region(first, loc)), "here"));
15483
}
15484
15485
15486
// ----------------------------------------------------------------------
15487
15488
region reg(first, loc);
15489
offset_datetime val(local_datetime(std::get<0>(date_fmt_reg.unwrap()),
15490
std::get<0>(time_fmt_reg.unwrap())),
15491
offset);
15492
15493
return ok(basic_value<TC>(val, std::move(fmt), {}, std::move(reg)));
15494
}
15495
15496
/* ============================================================================
15497
* ___ _ _
15498
* / __| |_ _ _(_)_ _ __ _
15499
* \__ \ _| '_| | ' \/ _` |
15500
* |___/\__|_| |_|_||_\__, |
15501
* |___/
15502
*/
15503
15504
template<typename TC>
15505
result<typename basic_value<TC>::string_type, error_info>
15506
parse_utf8_codepoint(const region& reg)
15507
{
15508
using string_type = typename basic_value<TC>::string_type;
15509
using char_type = typename string_type::value_type;
15510
15511
// assert(reg.as_lines().size() == 1); // XXX heavy check
15512
15513
const auto str = reg.as_string();
15514
assert( ! str.empty());
15515
assert(str.front() == 'u' || str.front() == 'U' || str.front() == 'x');
15516
15517
std::uint_least32_t codepoint;
15518
std::istringstream iss(str.substr(1));
15519
iss >> std::hex >> codepoint;
15520
15521
const auto to_char = [](const std::uint_least32_t i) noexcept -> char_type {
15522
const auto uc = static_cast<unsigned char>(i & 0xFF);
15523
return cxx::bit_cast<char_type>(uc);
15524
};
15525
15526
string_type character;
15527
if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII.
15528
{
15529
character += static_cast<char>(codepoint);
15530
}
15531
else if(codepoint < 0x800) //U+0080 ... U+07FF
15532
{
15533
// 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111
15534
character += to_char(0xC0|(codepoint >> 6 ));
15535
character += to_char(0x80|(codepoint & 0x3F));
15536
}
15537
else if(codepoint < 0x10000) // U+0800...U+FFFF
15538
{
15539
if(0xD800 <= codepoint && codepoint <= 0xDFFF)
15540
{
15541
auto src = source_location(reg);
15542
return err(make_error_info("toml::parse_utf8_codepoint: "
15543
"[0xD800, 0xDFFF] is not a valid UTF-8",
15544
std::move(src), "here"));
15545
}
15546
assert(codepoint < 0xD800 || 0xDFFF < codepoint);
15547
// 1110yyyy 10yxxxxx 10xxxxxx
15548
character += to_char(0xE0| (codepoint >> 12));
15549
character += to_char(0x80|((codepoint >> 6 ) & 0x3F));
15550
character += to_char(0x80|((codepoint ) & 0x3F));
15551
}
15552
else if(codepoint < 0x110000) // U+010000 ... U+10FFFF
15553
{
15554
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
15555
character += to_char(0xF0| (codepoint >> 18));
15556
character += to_char(0x80|((codepoint >> 12) & 0x3F));
15557
character += to_char(0x80|((codepoint >> 6 ) & 0x3F));
15558
character += to_char(0x80|((codepoint ) & 0x3F));
15559
}
15560
else // out of UTF-8 region
15561
{
15562
auto src = source_location(reg);
15563
return err(make_error_info("toml::parse_utf8_codepoint: "
15564
"input codepoint is too large.",
15565
std::move(src), "must be in range [0x00, 0x10FFFF]"));
15566
}
15567
return ok(character);
15568
}
15569
15570
template<typename TC>
15571
result<typename basic_value<TC>::string_type, error_info>
15572
parse_escape_sequence(location& loc, const context<TC>& ctx)
15573
{
15574
using string_type = typename basic_value<TC>::string_type;
15575
using char_type = typename string_type::value_type;
15576
15577
const auto& spec = ctx.toml_spec();
15578
15579
assert( ! loc.eof());
15580
assert(loc.current() == '\\');
15581
loc.advance(); // consume the first backslash
15582
15583
string_type retval;
15584
15585
if (loc.current() == '\\') { retval += char_type('\\'); loc.advance(); }
15586
else if(loc.current() == '"') { retval += char_type('\"'); loc.advance(); }
15587
else if(loc.current() == 'b') { retval += char_type('\b'); loc.advance(); }
15588
else if(loc.current() == 'f') { retval += char_type('\f'); loc.advance(); }
15589
else if(loc.current() == 'n') { retval += char_type('\n'); loc.advance(); }
15590
else if(loc.current() == 'r') { retval += char_type('\r'); loc.advance(); }
15591
else if(loc.current() == 't') { retval += char_type('\t'); loc.advance(); }
15592
else if(spec.v1_1_0_add_escape_sequence_e && loc.current() == 'e')
15593
{
15594
retval += char_type('\x1b');
15595
loc.advance();
15596
}
15597
else if(spec.v1_1_0_add_escape_sequence_x && loc.current() == 'x')
15598
{
15599
const auto reg = syntax::escaped_x2(spec).scan(loc);
15600
if( ! reg.is_ok())
15601
{
15602
auto src = source_location(region(loc));
15603
return err(make_error_info("toml::parse_escape_sequence: "
15604
"invalid token found in UTF-8 codepoint \\xhh",
15605
std::move(src), "here"));
15606
}
15607
const auto utf8 = parse_utf8_codepoint<TC>(reg);
15608
if(utf8.is_err())
15609
{
15610
return err(utf8.as_err());
15611
}
15612
retval += utf8.unwrap();
15613
}
15614
else if(loc.current() == 'u')
15615
{
15616
const auto reg = syntax::escaped_u4(spec).scan(loc);
15617
if( ! reg.is_ok())
15618
{
15619
auto src = source_location(region(loc));
15620
return err(make_error_info("toml::parse_escape_sequence: "
15621
"invalid token found in UTF-8 codepoint \\uhhhh",
15622
std::move(src), "here"));
15623
}
15624
const auto utf8 = parse_utf8_codepoint<TC>(reg);
15625
if(utf8.is_err())
15626
{
15627
return err(utf8.as_err());
15628
}
15629
retval += utf8.unwrap();
15630
}
15631
else if(loc.current() == 'U')
15632
{
15633
const auto reg = syntax::escaped_U8(spec).scan(loc);
15634
if( ! reg.is_ok())
15635
{
15636
auto src = source_location(region(loc));
15637
return err(make_error_info("toml::parse_escape_sequence: "
15638
"invalid token found in UTF-8 codepoint \\Uhhhhhhhh",
15639
std::move(src), "here"));
15640
}
15641
const auto utf8 = parse_utf8_codepoint<TC>(reg);
15642
if(utf8.is_err())
15643
{
15644
return err(utf8.as_err());
15645
}
15646
retval += utf8.unwrap();
15647
}
15648
else
15649
{
15650
auto src = source_location(region(loc));
15651
std::string escape_seqs = "allowed escape seqs: \\\\, \\\", \\b, \\f, \\n, \\r, \\t";
15652
if(spec.v1_1_0_add_escape_sequence_e)
15653
{
15654
escape_seqs += ", \\e";
15655
}
15656
if(spec.v1_1_0_add_escape_sequence_x)
15657
{
15658
escape_seqs += ", \\xhh";
15659
}
15660
escape_seqs += ", \\uhhhh, or \\Uhhhhhhhh";
15661
15662
return err(make_error_info("toml::parse_escape_sequence: "
15663
"unknown escape sequence.", std::move(src), escape_seqs));
15664
}
15665
return ok(retval);
15666
}
15667
15668
template<typename TC>
15669
result<basic_value<TC>, error_info>
15670
parse_ml_basic_string(location& loc, const context<TC>& ctx)
15671
{
15672
const auto first = loc;
15673
const auto& spec = ctx.toml_spec();
15674
15675
string_format_info fmt;
15676
fmt.fmt = string_format::multiline_basic;
15677
15678
auto reg = syntax::ml_basic_string(spec).scan(loc);
15679
if( ! reg.is_ok())
15680
{
15681
return err(make_syntax_error("toml::parse_ml_basic_string: "
15682
"invalid string format",
15683
syntax::ml_basic_string(spec), loc));
15684
}
15685
15686
// ----------------------------------------------------------------------
15687
// it matches. gen value
15688
15689
auto str = reg.as_string();
15690
15691
// we already checked that it starts with """ and ends with """.
15692
assert(str.substr(0, 3) == "\"\"\"");
15693
str.erase(0, 3);
15694
15695
assert(str.size() >= 3);
15696
assert(str.substr(str.size()-3, 3) == "\"\"\"");
15697
str.erase(str.size()-3, 3);
15698
15699
// the first newline just after """ is trimmed
15700
if(str.size() >= 1 && str.at(0) == '\n')
15701
{
15702
str.erase(0, 1);
15703
fmt.start_with_newline = true;
15704
}
15705
else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n')
15706
{
15707
str.erase(0, 2);
15708
fmt.start_with_newline = true;
15709
}
15710
15711
using string_type = typename basic_value<TC>::string_type;
15712
string_type val;
15713
{
15714
auto iter = str.cbegin();
15715
while(iter != str.cend())
15716
{
15717
if(*iter == '\\') // remove whitespaces around escaped-newline
15718
{
15719
// we assume that the string is not too long to copy
15720
auto loc2 = make_temporary_location(make_string(iter, str.cend()));
15721
if(syntax::escaped_newline(spec).scan(loc2).is_ok())
15722
{
15723
std::advance(iter, loc2.get_location()); // skip escaped newline and indent
15724
// now iter points non-WS char
15725
assert(iter == str.end() || (*iter != ' ' && *iter != '\t'));
15726
}
15727
else // normal escape seq.
15728
{
15729
auto esc = parse_escape_sequence(loc2, ctx);
15730
15731
// syntax does not check its value. the unicode codepoint may be
15732
// invalid, e.g. out-of-bound, [0xD800, 0xDFFF]
15733
if(esc.is_err())
15734
{
15735
return err(esc.unwrap_err());
15736
}
15737
15738
val += esc.unwrap();
15739
std::advance(iter, loc2.get_location());
15740
}
15741
}
15742
else // we already checked the syntax. we don't need to check it again.
15743
{
15744
val += static_cast<typename string_type::value_type>(*iter);
15745
++iter;
15746
}
15747
}
15748
}
15749
15750
return ok(basic_value<TC>(
15751
std::move(val), std::move(fmt), {}, std::move(reg)
15752
));
15753
}
15754
15755
template<typename TC>
15756
result<std::pair<typename basic_value<TC>::string_type, region>, error_info>
15757
parse_basic_string_only(location& loc, const context<TC>& ctx)
15758
{
15759
const auto first = loc;
15760
const auto& spec = ctx.toml_spec();
15761
15762
auto reg = syntax::basic_string(spec).scan(loc);
15763
if( ! reg.is_ok())
15764
{
15765
return err(make_syntax_error("toml::parse_basic_string: "
15766
"invalid string format",
15767
syntax::basic_string(spec), loc));
15768
}
15769
15770
// ----------------------------------------------------------------------
15771
// it matches. gen value
15772
15773
auto str = reg.as_string();
15774
15775
assert(str.back() == '\"');
15776
str.pop_back();
15777
assert(str.at(0) == '\"');
15778
str.erase(0, 1);
15779
15780
using string_type = typename basic_value<TC>::string_type;
15781
using char_type = typename string_type::value_type;
15782
string_type val;
15783
15784
{
15785
auto iter = str.begin();
15786
while(iter != str.end())
15787
{
15788
if(*iter == '\\')
15789
{
15790
auto loc2 = make_temporary_location(make_string(iter, str.end()));
15791
15792
auto esc = parse_escape_sequence(loc2, ctx);
15793
15794
// syntax does not check its value. the unicode codepoint may be
15795
// invalid, e.g. out-of-bound, [0xD800, 0xDFFF]
15796
if(esc.is_err())
15797
{
15798
return err(esc.unwrap_err());
15799
}
15800
15801
val += esc.unwrap();
15802
std::advance(iter, loc2.get_location());
15803
}
15804
else
15805
{
15806
val += char_type(*iter); // we already checked the syntax.
15807
++iter;
15808
}
15809
}
15810
}
15811
return ok(std::make_pair(val, reg));
15812
}
15813
15814
template<typename TC>
15815
result<basic_value<TC>, error_info>
15816
parse_basic_string(location& loc, const context<TC>& ctx)
15817
{
15818
const auto first = loc;
15819
15820
string_format_info fmt;
15821
fmt.fmt = string_format::basic;
15822
15823
auto val_res = parse_basic_string_only(loc, ctx);
15824
if(val_res.is_err())
15825
{
15826
return err(std::move(val_res.unwrap_err()));
15827
}
15828
auto val = std::move(val_res.unwrap().first );
15829
auto reg = std::move(val_res.unwrap().second);
15830
15831
return ok(basic_value<TC>(std::move(val), std::move(fmt), {}, std::move(reg)));
15832
}
15833
15834
template<typename TC>
15835
result<basic_value<TC>, error_info>
15836
parse_ml_literal_string(location& loc, const context<TC>& ctx)
15837
{
15838
const auto first = loc;
15839
const auto& spec = ctx.toml_spec();
15840
15841
string_format_info fmt;
15842
fmt.fmt = string_format::multiline_literal;
15843
15844
auto reg = syntax::ml_literal_string(spec).scan(loc);
15845
if( ! reg.is_ok())
15846
{
15847
return err(make_syntax_error("toml::parse_ml_literal_string: "
15848
"invalid string format",
15849
syntax::ml_literal_string(spec), loc));
15850
}
15851
15852
// ----------------------------------------------------------------------
15853
// it matches. gen value
15854
15855
auto str = reg.as_string();
15856
15857
assert(str.substr(0, 3) == "'''");
15858
assert(str.substr(str.size()-3, 3) == "'''");
15859
str.erase(0, 3);
15860
str.erase(str.size()-3, 3);
15861
15862
// the first newline just after """ is trimmed
15863
if(str.size() >= 1 && str.at(0) == '\n')
15864
{
15865
str.erase(0, 1);
15866
fmt.start_with_newline = true;
15867
}
15868
else if(str.size() >= 2 && str.at(0) == '\r' && str.at(1) == '\n')
15869
{
15870
str.erase(0, 2);
15871
fmt.start_with_newline = true;
15872
}
15873
15874
using string_type = typename basic_value<TC>::string_type;
15875
string_type val(str.begin(), str.end());
15876
15877
return ok(basic_value<TC>(
15878
std::move(val), std::move(fmt), {}, std::move(reg)
15879
));
15880
}
15881
15882
template<typename TC>
15883
result<std::pair<typename basic_value<TC>::string_type, region>, error_info>
15884
parse_literal_string_only(location& loc, const context<TC>& ctx)
15885
{
15886
const auto first = loc;
15887
const auto& spec = ctx.toml_spec();
15888
15889
auto reg = syntax::literal_string(spec).scan(loc);
15890
if( ! reg.is_ok())
15891
{
15892
return err(make_syntax_error("toml::parse_literal_string: "
15893
"invalid string format",
15894
syntax::literal_string(spec), loc));
15895
}
15896
15897
// ----------------------------------------------------------------------
15898
// it matches. gen value
15899
15900
auto str = reg.as_string();
15901
15902
assert(str.back() == '\'');
15903
str.pop_back();
15904
assert(str.at(0) == '\'');
15905
str.erase(0, 1);
15906
15907
using string_type = typename basic_value<TC>::string_type;
15908
string_type val(str.begin(), str.end());
15909
15910
return ok(std::make_pair(std::move(val), std::move(reg)));
15911
}
15912
15913
template<typename TC>
15914
result<basic_value<TC>, error_info>
15915
parse_literal_string(location& loc, const context<TC>& ctx)
15916
{
15917
const auto first = loc;
15918
15919
string_format_info fmt;
15920
fmt.fmt = string_format::literal;
15921
15922
auto val_res = parse_literal_string_only(loc, ctx);
15923
if(val_res.is_err())
15924
{
15925
return err(std::move(val_res.unwrap_err()));
15926
}
15927
auto val = std::move(val_res.unwrap().first );
15928
auto reg = std::move(val_res.unwrap().second);
15929
15930
return ok(basic_value<TC>(
15931
std::move(val), std::move(fmt), {}, std::move(reg)
15932
));
15933
}
15934
15935
template<typename TC>
15936
result<basic_value<TC>, error_info>
15937
parse_string(location& loc, const context<TC>& ctx)
15938
{
15939
const auto first = loc;
15940
15941
if( ! loc.eof() && loc.current() == '"')
15942
{
15943
if(literal("\"\"\"").scan(loc).is_ok())
15944
{
15945
loc = first;
15946
return parse_ml_basic_string(loc, ctx);
15947
}
15948
else
15949
{
15950
loc = first;
15951
return parse_basic_string(loc, ctx);
15952
}
15953
}
15954
else if( ! loc.eof() && loc.current() == '\'')
15955
{
15956
if(literal("'''").scan(loc).is_ok())
15957
{
15958
loc = first;
15959
return parse_ml_literal_string(loc, ctx);
15960
}
15961
else
15962
{
15963
loc = first;
15964
return parse_literal_string(loc, ctx);
15965
}
15966
}
15967
else
15968
{
15969
auto src = source_location(region(loc));
15970
return err(make_error_info("toml::parse_string: "
15971
"not a string", std::move(src), "here"));
15972
}
15973
}
15974
15975
template<typename TC>
15976
result<basic_value<TC>, error_info>
15977
parse_null(location& loc, const context<TC>& ctx)
15978
{
15979
const auto& spec = ctx.toml_spec();
15980
if( ! spec.ext_null_value)
15981
{
15982
return err(make_error_info("toml::parse_null: "
15983
"invalid spec: spec.ext_null_value must be true.",
15984
source_location(region(loc)), "here"));
15985
}
15986
15987
// ----------------------------------------------------------------------
15988
// check syntax
15989
auto reg = syntax::null_value(spec).scan(loc);
15990
if( ! reg.is_ok())
15991
{
15992
return err(make_syntax_error("toml::parse_null: "
15993
"invalid null: null must be lowercase. ",
15994
syntax::null_value(spec), loc));
15995
}
15996
15997
// ----------------------------------------------------------------------
15998
// it matches. gen value
15999
16000
// ----------------------------------------------------------------------
16001
// no format info for boolean
16002
16003
return ok(basic_value<TC>(detail::none_t{}, std::move(reg)));
16004
}
16005
16006
/* ============================================================================
16007
* _ __
16008
* | |/ /___ _ _
16009
* | ' </ -_) || |
16010
* |_|\_\___|\_, |
16011
* |__/
16012
*/
16013
16014
// non-dotted key.
16015
template<typename TC>
16016
result<typename basic_value<TC>::key_type, error_info>
16017
parse_simple_key(location& loc, const context<TC>& ctx)
16018
{
16019
using key_type = typename basic_value<TC>::key_type;
16020
const auto& spec = ctx.toml_spec();
16021
16022
if(loc.current() == '\"')
16023
{
16024
auto str_res = parse_basic_string_only(loc, ctx);
16025
if(str_res.is_ok())
16026
{
16027
return ok(std::move(str_res.unwrap().first));
16028
}
16029
else
16030
{
16031
return err(std::move(str_res.unwrap_err()));
16032
}
16033
}
16034
else if(loc.current() == '\'')
16035
{
16036
auto str_res = parse_literal_string_only(loc, ctx);
16037
if(str_res.is_ok())
16038
{
16039
return ok(std::move(str_res.unwrap().first));
16040
}
16041
else
16042
{
16043
return err(std::move(str_res.unwrap_err()));
16044
}
16045
}
16046
16047
// bare key.
16048
16049
if(const auto bare = syntax::unquoted_key(spec).scan(loc))
16050
{
16051
return ok(string_conv<key_type>(bare.as_string()));
16052
}
16053
else
16054
{
16055
std::string postfix;
16056
if(spec.ext_allow_non_english_in_bare_keys)
16057
{
16058
postfix = "Hint: Not all Unicode characters are allowed as bare key.\n";
16059
}
16060
else
16061
{
16062
postfix = "Hint: non-ASCII scripts are allowed in toml v1.1.0, but not in v1.0.0.\n";
16063
}
16064
return err(make_syntax_error("toml::parse_simple_key: "
16065
"invalid key: key must be \"quoted\", 'quoted-literal', or bare key.",
16066
syntax::unquoted_key(spec), loc, postfix));
16067
}
16068
}
16069
16070
// dotted key become vector of keys
16071
template<typename TC>
16072
result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info>
16073
parse_key(location& loc, const context<TC>& ctx)
16074
{
16075
const auto first = loc;
16076
const auto& spec = ctx.toml_spec();
16077
16078
using key_type = typename basic_value<TC>::key_type;
16079
std::vector<key_type> keys;
16080
while( ! loc.eof())
16081
{
16082
auto key = parse_simple_key(loc, ctx);
16083
if( ! key.is_ok())
16084
{
16085
return err(key.unwrap_err());
16086
}
16087
keys.push_back(std::move(key.unwrap()));
16088
16089
auto reg = syntax::dot_sep(spec).scan(loc);
16090
if( ! reg.is_ok())
16091
{
16092
break;
16093
}
16094
}
16095
if(keys.empty())
16096
{
16097
auto src = source_location(region(first));
16098
return err(make_error_info("toml::parse_key: expected a new key, "
16099
"but got nothing", std::move(src), "reached EOF"));
16100
}
16101
16102
return ok(std::make_pair(std::move(keys), region(first, loc)));
16103
}
16104
16105
// ============================================================================
16106
16107
// forward-decl to implement parse_array and parse_table
16108
template<typename TC>
16109
result<basic_value<TC>, error_info>
16110
parse_value(location&, context<TC>& ctx);
16111
16112
template<typename TC>
16113
result<std::pair<
16114
std::pair<std::vector<typename basic_value<TC>::key_type>, region>,
16115
basic_value<TC>
16116
>, error_info>
16117
parse_key_value_pair(location& loc, context<TC>& ctx)
16118
{
16119
const auto first = loc;
16120
const auto& spec = ctx.toml_spec();
16121
16122
auto key_res = parse_key(loc, ctx);
16123
if(key_res.is_err())
16124
{
16125
loc = first;
16126
return err(key_res.unwrap_err());
16127
}
16128
16129
if( ! syntax::keyval_sep(spec).scan(loc).is_ok())
16130
{
16131
auto e = make_syntax_error("toml::parse_key_value_pair: "
16132
"invalid key value separator `=`", syntax::keyval_sep(spec), loc);
16133
loc = first;
16134
return err(std::move(e));
16135
}
16136
16137
auto v_res = parse_value(loc, ctx);
16138
if(v_res.is_err())
16139
{
16140
// loc = first;
16141
return err(v_res.unwrap_err());
16142
}
16143
return ok(std::make_pair(std::move(key_res.unwrap()), std::move(v_res.unwrap())));
16144
}
16145
16146
/* ============================================================================
16147
* __ _ _ _ _ _ __ _ _ _
16148
* / _` | '_| '_/ _` | || |
16149
* \__,_|_| |_| \__,_|\_, |
16150
* |__/
16151
*/
16152
16153
// array(and multiline inline table with `{` and `}`) has the following format.
16154
// `[`
16155
// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,`
16156
// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? `,`
16157
// ...
16158
// (ws|newline|comment-line)? (value) (ws|newline|comment-line)? (`,`)?
16159
// (ws|newline|comment-line)? `]`
16160
// it skips (ws|newline|comment-line) and returns the token.
16161
template<typename TC>
16162
struct multiline_spacer
16163
{
16164
using comment_type = typename TC::comment_type;
16165
bool newline_found;
16166
indent_char indent_type;
16167
std::int32_t indent;
16168
comment_type comments;
16169
};
16170
template<typename T>
16171
std::ostream& operator<<(std::ostream& os, const multiline_spacer<T>& sp)
16172
{
16173
os << "{newline=" << sp.newline_found << ", ";
16174
os << "indent_type=" << sp.indent_type << ", ";
16175
os << "indent=" << sp.indent << ", ";
16176
os << "comments=" << sp.comments.size() << "}";
16177
return os;
16178
}
16179
16180
template<typename TC>
16181
cxx::optional<multiline_spacer<TC>>
16182
skip_multiline_spacer(location& loc, context<TC>& ctx, const bool newline_found = false)
16183
{
16184
const auto& spec = ctx.toml_spec();
16185
16186
multiline_spacer<TC> spacer;
16187
spacer.newline_found = newline_found;
16188
spacer.indent_type = indent_char::none;
16189
spacer.indent = 0;
16190
spacer.comments.clear();
16191
16192
bool spacer_found = false;
16193
while( ! loc.eof())
16194
{
16195
if(auto comm = sequence(syntax::comment(spec), syntax::newline(spec)).scan(loc))
16196
{
16197
spacer.newline_found = true;
16198
auto comment = comm.as_string();
16199
if( ! comment.empty() && comment.back() == '\n')
16200
{
16201
comment.pop_back();
16202
if (!comment.empty() && comment.back() == '\r')
16203
{
16204
comment.pop_back();
16205
}
16206
}
16207
16208
spacer.comments.push_back(std::move(comment));
16209
spacer.indent_type = indent_char::none;
16210
spacer.indent = 0;
16211
spacer_found = true;
16212
}
16213
else if(auto nl = syntax::newline(spec).scan(loc))
16214
{
16215
spacer.newline_found = true;
16216
spacer.comments.clear();
16217
spacer.indent_type = indent_char::none;
16218
spacer.indent = 0;
16219
spacer_found = true;
16220
}
16221
else if(auto sp = repeat_at_least(1, character(cxx::bit_cast<location::char_type>(' '))).scan(loc))
16222
{
16223
spacer.indent_type = indent_char::space;
16224
spacer.indent = static_cast<std::int32_t>(sp.length());
16225
spacer_found = true;
16226
}
16227
else if(auto tabs = repeat_at_least(1, character(cxx::bit_cast<location::char_type>('\t'))).scan(loc))
16228
{
16229
spacer.indent_type = indent_char::tab;
16230
spacer.indent = static_cast<std::int32_t>(tabs.length());
16231
spacer_found = true;
16232
}
16233
else
16234
{
16235
break; // done
16236
}
16237
}
16238
if( ! spacer_found)
16239
{
16240
return cxx::make_nullopt();
16241
}
16242
return spacer;
16243
}
16244
16245
// not an [[array.of.tables]]. It parses ["this", "type"]
16246
template<typename TC>
16247
result<basic_value<TC>, error_info>
16248
parse_array(location& loc, context<TC>& ctx)
16249
{
16250
const auto num_errors = ctx.errors().size();
16251
16252
const auto first = loc;
16253
16254
if(loc.eof() || loc.current() != '[')
16255
{
16256
auto src = source_location(region(loc));
16257
return err(make_error_info("toml::parse_array: "
16258
"The next token is not an array", std::move(src), "here"));
16259
}
16260
loc.advance();
16261
16262
typename basic_value<TC>::array_type val;
16263
16264
array_format_info fmt;
16265
fmt.fmt = array_format::oneline;
16266
fmt.indent_type = indent_char::none;
16267
16268
auto spacer = skip_multiline_spacer(loc, ctx);
16269
if(spacer.has_value() && spacer.value().newline_found)
16270
{
16271
fmt.fmt = array_format::multiline;
16272
}
16273
16274
bool comma_found = true;
16275
while( ! loc.eof())
16276
{
16277
if(loc.current() == location::char_type(']'))
16278
{
16279
if(spacer.has_value() && spacer.value().newline_found &&
16280
spacer.value().indent_type != indent_char::none)
16281
{
16282
fmt.indent_type = spacer.value().indent_type;
16283
fmt.closing_indent = spacer.value().indent;
16284
}
16285
break;
16286
}
16287
16288
if( ! comma_found)
16289
{
16290
auto src = source_location(region(loc));
16291
return err(make_error_info("toml::parse_array: "
16292
"expected value-separator `,` or closing `]`",
16293
std::move(src), "here"));
16294
}
16295
16296
if(spacer.has_value() && spacer.value().newline_found &&
16297
spacer.value().indent_type != indent_char::none)
16298
{
16299
fmt.indent_type = spacer.value().indent_type;
16300
fmt.body_indent = spacer.value().indent;
16301
}
16302
16303
if(auto elem_res = parse_value(loc, ctx))
16304
{
16305
auto elem = std::move(elem_res.unwrap());
16306
16307
if(spacer.has_value()) // copy previous comments to value
16308
{
16309
elem.comments() = std::move(spacer.value().comments);
16310
}
16311
16312
// parse spaces between a value and a comma
16313
// array = [
16314
// 42 , # the answer
16315
// ^^^^
16316
// 3.14 # pi
16317
// , 2.71 ^^^^
16318
// ^^
16319
spacer = skip_multiline_spacer(loc, ctx);
16320
if(spacer.has_value())
16321
{
16322
for(std::size_t i=0; i<spacer.value().comments.size(); ++i)
16323
{
16324
elem.comments().push_back(std::move(spacer.value().comments.at(i)));
16325
}
16326
if(spacer.value().newline_found)
16327
{
16328
fmt.fmt = array_format::multiline;
16329
}
16330
}
16331
16332
comma_found = character(',').scan(loc).is_ok();
16333
16334
// parse comment after a comma
16335
// array = [
16336
// 42 , # the answer
16337
// ^^^^^^^^^^^^
16338
// 3.14 # pi
16339
// ^^^^
16340
// ]
16341
auto com_res = parse_comment_line(loc, ctx);
16342
if(com_res.is_err())
16343
{
16344
ctx.report_error(com_res.unwrap_err());
16345
}
16346
16347
const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value();
16348
if(comment_found)
16349
{
16350
fmt.fmt = array_format::multiline;
16351
elem.comments().push_back(com_res.unwrap().value());
16352
}
16353
if(comma_found)
16354
{
16355
spacer = skip_multiline_spacer(loc, ctx, comment_found);
16356
if(spacer.has_value() && spacer.value().newline_found)
16357
{
16358
fmt.fmt = array_format::multiline;
16359
}
16360
}
16361
val.push_back(std::move(elem));
16362
}
16363
else
16364
{
16365
// if err, push error to ctx and try recovery.
16366
ctx.report_error(std::move(elem_res.unwrap_err()));
16367
16368
// if it looks like some value, then skip the value.
16369
// otherwise, it may be a new key-value pair or a new table and
16370
// the error is "missing closing ]". stop parsing.
16371
16372
const auto before_skip = loc.get_location();
16373
skip_value(loc, ctx);
16374
if(before_skip == loc.get_location()) // cannot skip! break...
16375
{
16376
break;
16377
}
16378
}
16379
}
16380
16381
if(loc.current() != ']')
16382
{
16383
auto src = source_location(region(loc));
16384
return err(make_error_info("toml::parse_array: missing closing bracket `]`",
16385
std::move(src), "expected `]`, reached EOF"));
16386
}
16387
else
16388
{
16389
loc.advance();
16390
}
16391
// any error reported from this function
16392
if(num_errors != ctx.errors().size())
16393
{
16394
assert(ctx.has_error()); // already reported
16395
return err(ctx.errors().back());
16396
}
16397
16398
return ok(basic_value<TC>(
16399
std::move(val), std::move(fmt), {}, region(first, loc)
16400
));
16401
}
16402
16403
/* ============================================================================
16404
* _ _ _ _ _ _
16405
* (_)_ _ | (_)_ _ ___ | |_ __ _| |__| |___
16406
* | | ' \| | | ' \/ -_) | _/ _` | '_ \ / -_)
16407
* |_|_||_|_|_|_||_\___| \__\__,_|_.__/_\___|
16408
*/
16409
16410
// ----------------------------------------------------------------------------
16411
// insert_value is the most complicated part of the toml spec.
16412
//
16413
// To parse a toml file correctly, we sometimes need to check an exising value
16414
// is appendable or not.
16415
//
16416
// For example while parsing an inline array of tables,
16417
//
16418
// ```toml
16419
// aot = [
16420
// {a = "foo"},
16421
// {a = "bar", b = "baz"},
16422
// ]
16423
// ```
16424
//
16425
// this `aot` is appendable until parser reaches to `]`. After that, it becomes
16426
// non-appendable.
16427
//
16428
// On the other hand, a normal array of tables, such as
16429
//
16430
// ```toml
16431
// [[aot]]
16432
// a = "foo"
16433
//
16434
// [[aot]]
16435
// a = "bar"
16436
// b = "baz"
16437
// ```
16438
// This `[[aot]]` is appendable until the parser reaches to the EOF.
16439
//
16440
//
16441
// It becomes a bit more difficult in case of dotted keys.
16442
// In TOML, it is allowed to append a key-value pair to a table that is
16443
// *implicitly* defined by a subtable definitino.
16444
//
16445
// ```toml
16446
// [x.y.z]
16447
// w = 123
16448
//
16449
// [x]
16450
// a = "foo" # OK. x is defined implicitly by `[x.y.z]`.
16451
// ```
16452
//
16453
// But if the table is defined by a dotted keys, it is not appendable.
16454
//
16455
// ```toml
16456
// [x]
16457
// y.z.w = 123
16458
//
16459
// [x.y]
16460
// # ERROR. x.y is already defined by a dotted table in the previous table.
16461
// ```
16462
//
16463
// Also, reopening a table using dotted keys is invalid.
16464
//
16465
// ```toml
16466
// [x.y.z]
16467
// w = 123
16468
//
16469
// [x]
16470
// y.z.v = 42 # ERROR. [x.y.z] is already defined.
16471
// ```
16472
//
16473
//
16474
// ```toml
16475
// [a]
16476
// b.c = "foo"
16477
// b.d = "bar"
16478
// ```
16479
//
16480
//
16481
// ```toml
16482
// a.b = "foo"
16483
// [a]
16484
// c = "bar" # ERROR
16485
// ```
16486
//
16487
// In summary,
16488
// - a table must be defined only once.
16489
// - assignment to an exising table is possible only when:
16490
// - defining a subtable [x.y] to an existing table [x].
16491
// - defining supertable [x] explicitly after [x.y].
16492
// - adding dotted keys in the same table.
16493
16494
enum class inserting_value_kind : std::uint8_t
16495
{
16496
std_table, // insert [standard.table]
16497
array_table, // insert [[array.of.tables]]
16498
dotted_keys // insert a.b.c = "this"
16499
};
16500
16501
template<typename TC>
16502
result<basic_value<TC>*, error_info>
16503
insert_value(const inserting_value_kind kind,
16504
typename basic_value<TC>::table_type* current_table_ptr,
16505
const std::vector<typename basic_value<TC>::key_type>& keys, region key_reg,
16506
basic_value<TC> val)
16507
{
16508
using value_type = basic_value<TC>;
16509
using array_type = typename basic_value<TC>::array_type;
16510
using table_type = typename basic_value<TC>::table_type;
16511
16512
auto key_loc = source_location(key_reg);
16513
16514
assert( ! keys.empty());
16515
16516
// dotted key can insert to dotted key tables defined at the same level.
16517
// dotted key can NOT reopen a table even if it is implcitly-defined one.
16518
//
16519
// [x.y.z] # define x and x.y implicitly.
16520
// a = 42
16521
//
16522
// [x] # reopening implcitly defined table
16523
// r.s.t = 3.14 # VALID r and r.s are new tables.
16524
// r.s.u = 2.71 # VALID r and r.s are dotted-key tables. valid.
16525
//
16526
// y.z.b = "foo" # INVALID x.y.z are multiline table, not a dotted key.
16527
// y.c = "bar" # INVALID x.y is implicit multiline table, not a dotted key.
16528
16529
// a table cannot reopen dotted-key tables.
16530
//
16531
// [t1]
16532
// t2.t3.v = 0
16533
// [t1.t2] # INVALID t1.t2 is defined as a dotted-key table.
16534
16535
for(std::size_t i=0; i<keys.size(); ++i)
16536
{
16537
const auto& key = keys.at(i);
16538
table_type& current_table = *current_table_ptr;
16539
16540
if(i+1 < keys.size()) // there are more keys. go down recursively...
16541
{
16542
const auto found = current_table.find(key);
16543
if(found == current_table.end()) // not found. add new table
16544
{
16545
table_format_info fmt;
16546
fmt.indent_type = indent_char::none;
16547
if(kind == inserting_value_kind::dotted_keys)
16548
{
16549
fmt.fmt = table_format::dotted;
16550
}
16551
else // table / array of tables
16552
{
16553
fmt.fmt = table_format::implicit;
16554
}
16555
current_table.emplace(key, value_type(
16556
table_type{}, fmt, std::vector<std::string>{}, key_reg));
16557
16558
assert(current_table.at(key).is_table());
16559
current_table_ptr = std::addressof(current_table.at(key).as_table());
16560
}
16561
else if (found->second.is_table())
16562
{
16563
const auto fmt = found->second.as_table_fmt().fmt;
16564
if(fmt == table_format::oneline || fmt == table_format::multiline_oneline)
16565
{
16566
// foo = {bar = "baz"} or foo = { \n bar = "baz" \n }
16567
return err(make_error_info("toml::insert_value: "
16568
"failed to insert a value: inline table is immutable",
16569
key_loc, "inserting this",
16570
found->second.location(), "to this table"));
16571
}
16572
// dotted key cannot reopen a table.
16573
if(kind ==inserting_value_kind::dotted_keys && fmt != table_format::dotted)
16574
{
16575
return err(make_error_info("toml::insert_value: "
16576
"reopening a table using dotted keys",
16577
key_loc, "dotted key cannot reopen a table",
16578
found->second.location(), "this table is already closed"));
16579
}
16580
assert(found->second.is_table());
16581
current_table_ptr = std::addressof(found->second.as_table());
16582
}
16583
else if(found->second.is_array_of_tables())
16584
{
16585
// aot = [{this = "type", of = "aot"}] # cannot be reopened
16586
if(found->second.as_array_fmt().fmt != array_format::array_of_tables)
16587
{
16588
return err(make_error_info("toml::insert_value:"
16589
"inline array of tables are immutable",
16590
key_loc, "inserting this",
16591
found->second.location(), "inline array of tables"));
16592
}
16593
// appending to [[aot]]
16594
16595
if(kind == inserting_value_kind::dotted_keys)
16596
{
16597
// [[array.of.tables]]
16598
// [array.of] # reopening supertable is okay
16599
// tables.x = "foo" # appending `x` to the first table
16600
return err(make_error_info("toml::insert_value:"
16601
"dotted key cannot reopen an array-of-tables",
16602
key_loc, "inserting this",
16603
found->second.location(), "to this array-of-tables."));
16604
}
16605
16606
// insert_value_by_dotkeys::std_table
16607
// [[array.of.tables]]
16608
// [array.of.tables.subtable] # appending to the last aot
16609
//
16610
// insert_value_by_dotkeys::array_table
16611
// [[array.of.tables]]
16612
// [[array.of.tables.subtable]] # appending to the last aot
16613
auto& current_array_table = found->second.as_array().back();
16614
16615
assert(current_array_table.is_table());
16616
current_table_ptr = std::addressof(current_array_table.as_table());
16617
}
16618
else
16619
{
16620
return err(make_error_info("toml::insert_value: "
16621
"failed to insert a value, value already exists",
16622
key_loc, "while inserting this",
16623
found->second.location(), "non-table value already exists"));
16624
}
16625
}
16626
else // this is the last key. insert a new value.
16627
{
16628
switch(kind)
16629
{
16630
case inserting_value_kind::dotted_keys:
16631
{
16632
if(current_table.find(key) != current_table.end())
16633
{
16634
return err(make_error_info("toml::insert_value: "
16635
"failed to insert a value, value already exists",
16636
key_loc, "inserting this",
16637
current_table.at(key).location(), "but value already exists"));
16638
}
16639
current_table.emplace(key, std::move(val));
16640
return ok(std::addressof(current_table.at(key)));
16641
}
16642
case inserting_value_kind::std_table:
16643
{
16644
// defining a new table or reopening supertable
16645
auto found = current_table.find(key);
16646
if(found == current_table.end()) // define a new aot
16647
{
16648
current_table.emplace(key, std::move(val));
16649
return ok(std::addressof(current_table.at(key)));
16650
}
16651
else // the table is already defined, reopen it
16652
{
16653
// assigning a [std.table]. it must be an implicit table.
16654
auto& target = found->second;
16655
if( ! target.is_table() || // could be an array-of-tables
16656
target.as_table_fmt().fmt != table_format::implicit)
16657
{
16658
return err(make_error_info("toml::insert_value: "
16659
"failed to insert a table, table already defined",
16660
key_loc, "inserting this",
16661
target.location(), "this table is explicitly defined"));
16662
}
16663
16664
// merge table
16665
for(const auto& kv : val.as_table())
16666
{
16667
if(target.contains(kv.first))
16668
{
16669
// [x.y.z]
16670
// w = "foo"
16671
// [x]
16672
// y = "bar"
16673
return err(make_error_info("toml::insert_value: "
16674
"failed to insert a table, table keys conflict to each other",
16675
key_loc, "inserting this table",
16676
kv.second.location(), "having this value",
16677
target.at(kv.first).location(), "already defined here"));
16678
}
16679
else
16680
{
16681
target[kv.first] = kv.second;
16682
}
16683
}
16684
// change implicit -> explicit
16685
target.as_table_fmt().fmt = table_format::multiline;
16686
// change definition region
16687
change_region_of_value(target, val);
16688
16689
return ok(std::addressof(current_table.at(key)));
16690
}
16691
}
16692
case inserting_value_kind::array_table:
16693
{
16694
auto found = current_table.find(key);
16695
if(found == current_table.end()) // define a new aot
16696
{
16697
array_format_info fmt;
16698
fmt.fmt = array_format::array_of_tables;
16699
fmt.indent_type = indent_char::none;
16700
16701
current_table.emplace(key, value_type(
16702
array_type{ std::move(val) }, std::move(fmt),
16703
std::vector<std::string>{}, std::move(key_reg)
16704
));
16705
16706
assert( ! current_table.at(key).as_array().empty());
16707
return ok(std::addressof(current_table.at(key).as_array().back()));
16708
}
16709
else // the array is already defined, append to it
16710
{
16711
if( ! found->second.is_array_of_tables())
16712
{
16713
return err(make_error_info("toml::insert_value: "
16714
"failed to insert an array of tables, value already exists",
16715
key_loc, "while inserting this",
16716
found->second.location(), "non-table value already exists"));
16717
}
16718
if(found->second.as_array_fmt().fmt != array_format::array_of_tables)
16719
{
16720
return err(make_error_info("toml::insert_value: "
16721
"failed to insert a table, inline array of tables is immutable",
16722
key_loc, "while inserting this",
16723
found->second.location(), "this is inline array-of-tables"));
16724
}
16725
found->second.as_array().push_back(std::move(val));
16726
assert( ! current_table.at(key).as_array().empty());
16727
return ok(std::addressof(current_table.at(key).as_array().back()));
16728
}
16729
}
16730
default: {assert(false);}
16731
}
16732
}
16733
}
16734
return err(make_error_info("toml::insert_key: no keys found",
16735
std::move(key_loc), "here"));
16736
}
16737
16738
// ----------------------------------------------------------------------------
16739
16740
template<typename TC>
16741
result<basic_value<TC>, error_info>
16742
parse_inline_table(location& loc, context<TC>& ctx)
16743
{
16744
using table_type = typename basic_value<TC>::table_type;
16745
16746
const auto num_errors = ctx.errors().size();
16747
16748
const auto first = loc;
16749
const auto& spec = ctx.toml_spec();
16750
16751
if(loc.eof() || loc.current() != '{')
16752
{
16753
auto src = source_location(region(loc));
16754
return err(make_error_info("toml::parse_inline_table: "
16755
"The next token is not an inline table", std::move(src), "here"));
16756
}
16757
loc.advance();
16758
16759
table_type table;
16760
table_format_info fmt;
16761
fmt.fmt = table_format::oneline;
16762
fmt.indent_type = indent_char::none;
16763
16764
cxx::optional<multiline_spacer<TC>> spacer(cxx::make_nullopt());
16765
16766
if(spec.v1_1_0_allow_newlines_in_inline_tables)
16767
{
16768
spacer = skip_multiline_spacer(loc, ctx);
16769
if(spacer.has_value() && spacer.value().newline_found)
16770
{
16771
fmt.fmt = table_format::multiline_oneline;
16772
}
16773
}
16774
else
16775
{
16776
skip_whitespace(loc, ctx);
16777
}
16778
16779
bool still_empty = true;
16780
bool comma_found = false;
16781
while( ! loc.eof())
16782
{
16783
// closing!
16784
if(loc.current() == '}')
16785
{
16786
if(comma_found && ! spec.v1_1_0_allow_trailing_comma_in_inline_tables)
16787
{
16788
auto src = source_location(region(loc));
16789
return err(make_error_info("toml::parse_inline_table: trailing "
16790
"comma is not allowed in TOML-v1.0.0)", std::move(src), "here"));
16791
}
16792
16793
if(spec.v1_1_0_allow_newlines_in_inline_tables)
16794
{
16795
if(spacer.has_value() && spacer.value().newline_found &&
16796
spacer.value().indent_type != indent_char::none)
16797
{
16798
fmt.indent_type = spacer.value().indent_type;
16799
fmt.closing_indent = spacer.value().indent;
16800
}
16801
}
16802
break;
16803
}
16804
16805
// if we already found a value and didn't found `,` nor `}`, error.
16806
if( ! comma_found && ! still_empty)
16807
{
16808
auto src = source_location(region(loc));
16809
return err(make_error_info("toml::parse_inline_table: "
16810
"expected value-separator `,` or closing `}`",
16811
std::move(src), "here"));
16812
}
16813
16814
// parse indent.
16815
if(spacer.has_value() && spacer.value().newline_found &&
16816
spacer.value().indent_type != indent_char::none)
16817
{
16818
fmt.indent_type = spacer.value().indent_type;
16819
fmt.body_indent = spacer.value().indent;
16820
}
16821
16822
still_empty = false; // parsing a value...
16823
if(auto kv_res = parse_key_value_pair<TC>(loc, ctx))
16824
{
16825
auto keys = std::move(kv_res.unwrap().first.first);
16826
auto key_reg = std::move(kv_res.unwrap().first.second);
16827
auto val = std::move(kv_res.unwrap().second);
16828
16829
auto ins_res = insert_value(inserting_value_kind::dotted_keys,
16830
std::addressof(table), keys, std::move(key_reg), std::move(val));
16831
if(ins_res.is_err())
16832
{
16833
ctx.report_error(std::move(ins_res.unwrap_err()));
16834
// we need to skip until the next value (or end of the table)
16835
// because we don't have valid kv pair.
16836
while( ! loc.eof())
16837
{
16838
const auto c = loc.current();
16839
if(c == ',' || c == '\n' || c == '}')
16840
{
16841
comma_found = (c == ',');
16842
break;
16843
}
16844
loc.advance();
16845
}
16846
continue;
16847
}
16848
16849
// if comment line follows immediately(without newline) after `,`, then
16850
// the comment is for the elem. we need to check if comment follows `,`.
16851
//
16852
// (key) = (val) (ws|newline|comment-line)? `,` (ws)? (comment)?
16853
16854
if(spec.v1_1_0_allow_newlines_in_inline_tables)
16855
{
16856
if(spacer.has_value()) // copy previous comments to value
16857
{
16858
for(std::size_t i=0; i<spacer.value().comments.size(); ++i)
16859
{
16860
ins_res.unwrap()->comments().push_back(spacer.value().comments.at(i));
16861
}
16862
}
16863
spacer = skip_multiline_spacer(loc, ctx);
16864
if(spacer.has_value())
16865
{
16866
for(std::size_t i=0; i<spacer.value().comments.size(); ++i)
16867
{
16868
ins_res.unwrap()->comments().push_back(spacer.value().comments.at(i));
16869
}
16870
if(spacer.value().newline_found)
16871
{
16872
fmt.fmt = table_format::multiline_oneline;
16873
if(spacer.value().indent_type != indent_char::none)
16874
{
16875
fmt.indent_type = spacer.value().indent_type;
16876
fmt.body_indent = spacer.value().indent;
16877
}
16878
}
16879
}
16880
}
16881
else
16882
{
16883
skip_whitespace(loc, ctx);
16884
}
16885
16886
comma_found = character(',').scan(loc).is_ok();
16887
16888
if(spec.v1_1_0_allow_newlines_in_inline_tables)
16889
{
16890
auto com_res = parse_comment_line(loc, ctx);
16891
if(com_res.is_err())
16892
{
16893
ctx.report_error(com_res.unwrap_err());
16894
}
16895
const bool comment_found = com_res.is_ok() && com_res.unwrap().has_value();
16896
if(comment_found)
16897
{
16898
fmt.fmt = table_format::multiline_oneline;
16899
ins_res.unwrap()->comments().push_back(com_res.unwrap().value());
16900
}
16901
if(comma_found)
16902
{
16903
spacer = skip_multiline_spacer(loc, ctx, comment_found);
16904
if(spacer.has_value() && spacer.value().newline_found)
16905
{
16906
fmt.fmt = table_format::multiline_oneline;
16907
}
16908
}
16909
}
16910
else
16911
{
16912
skip_whitespace(loc, ctx);
16913
}
16914
}
16915
else
16916
{
16917
ctx.report_error(std::move(kv_res.unwrap_err()));
16918
while( ! loc.eof())
16919
{
16920
if(loc.current() == '}')
16921
{
16922
break;
16923
}
16924
if( ! spec.v1_1_0_allow_newlines_in_inline_tables && loc.current() == '\n')
16925
{
16926
break;
16927
}
16928
loc.advance();
16929
}
16930
break;
16931
}
16932
}
16933
16934
if(loc.current() != '}')
16935
{
16936
auto src = source_location(region(loc));
16937
return err(make_error_info("toml::parse_inline_table: "
16938
"missing closing bracket `}`",
16939
std::move(src), "expected `}`, reached line end"));
16940
}
16941
else
16942
{
16943
loc.advance(); // skip }
16944
}
16945
16946
// any error reported from this function
16947
if(num_errors < ctx.errors().size())
16948
{
16949
assert(ctx.has_error()); // already reported
16950
return err(ctx.pop_last_error());
16951
}
16952
16953
basic_value<TC> retval(
16954
std::move(table), std::move(fmt), {}, region(first, loc));
16955
16956
return ok(std::move(retval));
16957
}
16958
16959
/* ============================================================================
16960
* _
16961
* __ ____ _| |_ _ ___
16962
* \ V / _` | | || / -_)
16963
* \_/\__,_|_|\_,_\___|
16964
*/
16965
16966
template<typename TC>
16967
result<value_t, error_info>
16968
guess_number_type(const location& first, const context<TC>& ctx)
16969
{
16970
const auto& spec = ctx.toml_spec();
16971
location loc = first;
16972
16973
if(syntax::offset_datetime(spec).scan(loc).is_ok())
16974
{
16975
return ok(value_t::offset_datetime);
16976
}
16977
loc = first;
16978
16979
if(syntax::local_datetime(spec).scan(loc).is_ok())
16980
{
16981
const auto curr = loc.current();
16982
// if offset_datetime contains bad offset, it syntax::offset_datetime
16983
// fails to scan it.
16984
if(curr == '+' || curr == '-')
16985
{
16986
return err(make_syntax_error("bad offset: must be [+-]HH:MM or Z",
16987
syntax::time_offset(spec), loc, std::string(
16988
"Hint: valid : +09:00, -05:30\n"
16989
"Hint: invalid: +9:00, -5:30\n")));
16990
}
16991
return ok(value_t::local_datetime);
16992
}
16993
loc = first;
16994
16995
if(syntax::local_date(spec).scan(loc).is_ok())
16996
{
16997
// bad time may appear after this.
16998
16999
if( ! loc.eof())
17000
{
17001
const auto c = loc.current();
17002
if(c == 'T' || c == 't')
17003
{
17004
loc.advance();
17005
17006
return err(make_syntax_error("bad time: must be HH:MM:SS.subsec",
17007
syntax::local_time(spec), loc, std::string(
17008
"Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n"
17009
"Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n")));
17010
}
17011
if(c == ' ')
17012
{
17013
// A space is allowed as a delimiter between local time.
17014
// But there is a case where bad time follows a space.
17015
// - invalid: 2019-06-16 7:00:00
17016
// - valid : 2019-06-16 07:00:00
17017
loc.advance();
17018
if( ! loc.eof() && ('0' <= loc.current() && loc.current() <= '9'))
17019
{
17020
return err(make_syntax_error("bad time: must be HH:MM:SS.subsec",
17021
syntax::local_time(spec), loc, std::string(
17022
"Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n"
17023
"Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n")));
17024
}
17025
}
17026
if('0' <= c && c <= '9')
17027
{
17028
return err(make_syntax_error("bad datetime: missing T or space",
17029
character_either("Tt "), loc, std::string(
17030
"Hint: valid : 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999\n"
17031
"Hint: invalid: 1979-05-27T7:32:00, 1979-05-27 17:32\n")));
17032
}
17033
}
17034
return ok(value_t::local_date);
17035
}
17036
loc = first;
17037
17038
if(syntax::local_time(spec).scan(loc).is_ok())
17039
{
17040
return ok(value_t::local_time);
17041
}
17042
loc = first;
17043
17044
if(syntax::floating(spec).scan(loc).is_ok())
17045
{
17046
if( ! loc.eof() && loc.current() == '_')
17047
{
17048
if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok())
17049
{
17050
return ok(value_t::floating);
17051
}
17052
auto src = source_location(region(loc));
17053
return err(make_error_info(
17054
"bad float: `_` must be surrounded by digits",
17055
std::move(src), "invalid underscore",
17056
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
17057
"Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"));
17058
}
17059
return ok(value_t::floating);
17060
}
17061
loc = first;
17062
17063
if(spec.ext_hex_float)
17064
{
17065
if(syntax::hex_floating(spec).scan(loc).is_ok())
17066
{
17067
if( ! loc.eof() && loc.current() == '_')
17068
{
17069
if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok())
17070
{
17071
return ok(value_t::floating);
17072
}
17073
auto src = source_location(region(loc));
17074
return err(make_error_info(
17075
"bad float: `_` must be surrounded by digits",
17076
std::move(src), "invalid underscore",
17077
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
17078
"Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n"));
17079
}
17080
return ok(value_t::floating);
17081
}
17082
loc = first;
17083
}
17084
17085
if(auto int_reg = syntax::integer(spec).scan(loc))
17086
{
17087
if( ! loc.eof())
17088
{
17089
const auto c = loc.current();
17090
if(c == '_')
17091
{
17092
if(spec.ext_num_suffix && syntax::num_suffix(spec).scan(loc).is_ok())
17093
{
17094
return ok(value_t::integer);
17095
}
17096
17097
if(int_reg.length() <= 2 && (int_reg.as_string() == "0" ||
17098
int_reg.as_string() == "-0" || int_reg.as_string() == "+0"))
17099
{
17100
auto src = source_location(region(loc));
17101
return err(make_error_info(
17102
"bad integer: leading zero is not allowed in decimal int",
17103
std::move(src), "leading zero",
17104
"Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
17105
"Hint: invalid: _42, 1__000, 0123\n"));
17106
}
17107
else
17108
{
17109
auto src = source_location(region(loc));
17110
return err(make_error_info(
17111
"bad integer: `_` must be surrounded by digits",
17112
std::move(src), "invalid underscore",
17113
"Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
17114
"Hint: invalid: _42, 1__000, 0123\n"));
17115
}
17116
}
17117
if('0' <= c && c <= '9')
17118
{
17119
if(loc.current() == '0')
17120
{
17121
loc.retrace();
17122
return err(make_error_info(
17123
"bad integer: leading zero",
17124
source_location(region(loc)), "leading zero is not allowed",
17125
std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
17126
"Hint: invalid: _42, 1__000, 0123\n")
17127
));
17128
}
17129
else // invalid digits, especially in oct/bin ints.
17130
{
17131
return err(make_error_info(
17132
"bad integer: invalid digit after an integer",
17133
source_location(region(loc)), "this digit is not allowed",
17134
std::string("Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
17135
"Hint: invalid: _42, 1__000, 0123\n")
17136
));
17137
}
17138
}
17139
if(c == ':' || c == '-')
17140
{
17141
auto src = source_location(region(loc));
17142
return err(make_error_info("bad datetime: invalid format",
17143
std::move(src), "here",
17144
std::string("Hint: valid : 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z\n"
17145
"Hint: invalid: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30")
17146
));
17147
}
17148
if(c == '.' || c == 'e' || c == 'E')
17149
{
17150
auto src = source_location(region(loc));
17151
return err(make_error_info("bad float: invalid format",
17152
std::move(src), "here", std::string(
17153
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
17154
"Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")));
17155
}
17156
}
17157
return ok(value_t::integer);
17158
}
17159
if( ! loc.eof() && loc.current() == '.')
17160
{
17161
auto src = source_location(region(loc));
17162
return err(make_error_info("bad float: integer part is required before decimal point",
17163
std::move(src), "missing integer part", std::string(
17164
"Hint: valid : +1.0, -2e-2, 3.141_592_653_589, inf, nan\n"
17165
"Hint: invalid: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0\n")
17166
));
17167
}
17168
if( ! loc.eof() && loc.current() == '_')
17169
{
17170
auto src = source_location(region(loc));
17171
return err(make_error_info("bad number: `_` must be surrounded by digits",
17172
std::move(src), "digits required before `_`", std::string(
17173
"Hint: valid : -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755\n"
17174
"Hint: invalid: _42, 1__000, 0123\n")
17175
));
17176
}
17177
17178
auto src = source_location(region(loc));
17179
return err(make_error_info("bad format: unknown value appeared",
17180
std::move(src), "here"));
17181
}
17182
17183
template<typename TC>
17184
result<value_t, error_info>
17185
guess_value_type(const location& loc, const context<TC>& ctx)
17186
{
17187
const auto& sp = ctx.toml_spec();
17188
location inner(loc);
17189
17190
switch(loc.current())
17191
{
17192
case '"' : {return ok(value_t::string); }
17193
case '\'': {return ok(value_t::string); }
17194
case '[' : {return ok(value_t::array); }
17195
case '{' : {return ok(value_t::table); }
17196
case 't' :
17197
{
17198
return ok(value_t::boolean);
17199
}
17200
case 'f' :
17201
{
17202
return ok(value_t::boolean);
17203
}
17204
case 'T' : // invalid boolean.
17205
{
17206
return err(make_syntax_error("toml::parse_value: "
17207
"`true` must be in lowercase. "
17208
"A string must be surrounded by quotes.",
17209
syntax::boolean(sp), inner));
17210
}
17211
case 'F' :
17212
{
17213
return err(make_syntax_error("toml::parse_value: "
17214
"`false` must be in lowercase. "
17215
"A string must be surrounded by quotes.",
17216
syntax::boolean(sp), inner));
17217
}
17218
case 'i' : // inf or string without quotes(syntax error).
17219
{
17220
if(literal("inf").scan(inner).is_ok())
17221
{
17222
return ok(value_t::floating);
17223
}
17224
else
17225
{
17226
return err(make_syntax_error("toml::parse_value: "
17227
"`inf` must be in lowercase. "
17228
"A string must be surrounded by quotes.",
17229
syntax::floating(sp), inner));
17230
}
17231
}
17232
case 'I' : // Inf or string without quotes(syntax error).
17233
{
17234
return err(make_syntax_error("toml::parse_value: "
17235
"`inf` must be in lowercase. "
17236
"A string must be surrounded by quotes.",
17237
syntax::floating(sp), inner));
17238
}
17239
case 'n' : // nan or null-extension
17240
{
17241
if(sp.ext_null_value)
17242
{
17243
if(literal("nan").scan(inner).is_ok())
17244
{
17245
return ok(value_t::floating);
17246
}
17247
else if(literal("null").scan(inner).is_ok())
17248
{
17249
return ok(value_t::empty);
17250
}
17251
else
17252
{
17253
return err(make_syntax_error("toml::parse_value: "
17254
"Both `nan` and `null` must be in lowercase. "
17255
"A string must be surrounded by quotes.",
17256
syntax::floating(sp), inner));
17257
}
17258
}
17259
else // must be nan.
17260
{
17261
if(literal("nan").scan(inner).is_ok())
17262
{
17263
return ok(value_t::floating);
17264
}
17265
else
17266
{
17267
return err(make_syntax_error("toml::parse_value: "
17268
"`nan` must be in lowercase. "
17269
"A string must be surrounded by quotes.",
17270
syntax::floating(sp), inner));
17271
}
17272
}
17273
}
17274
case 'N' : // nan or null-extension
17275
{
17276
if(sp.ext_null_value)
17277
{
17278
return err(make_syntax_error("toml::parse_value: "
17279
"Both `nan` and `null` must be in lowercase. "
17280
"A string must be surrounded by quotes.",
17281
syntax::floating(sp), inner));
17282
}
17283
else
17284
{
17285
return err(make_syntax_error("toml::parse_value: "
17286
"`nan` must be in lowercase. "
17287
"A string must be surrounded by quotes.",
17288
syntax::floating(sp), inner));
17289
}
17290
}
17291
default :
17292
{
17293
return guess_number_type(loc, ctx);
17294
}
17295
}
17296
}
17297
17298
template<typename TC>
17299
result<basic_value<TC>, error_info>
17300
parse_value(location& loc, context<TC>& ctx)
17301
{
17302
const auto ty_res = guess_value_type(loc, ctx);
17303
if(ty_res.is_err())
17304
{
17305
return err(ty_res.unwrap_err());
17306
}
17307
17308
switch(ty_res.unwrap())
17309
{
17310
case value_t::empty:
17311
{
17312
if(ctx.toml_spec().ext_null_value)
17313
{
17314
return parse_null(loc, ctx);
17315
}
17316
else
17317
{
17318
auto src = source_location(region(loc));
17319
return err(make_error_info("toml::parse_value: unknown value appeared",
17320
std::move(src), "here"));
17321
}
17322
}
17323
case value_t::boolean : {return parse_boolean (loc, ctx);}
17324
case value_t::integer : {return parse_integer (loc, ctx);}
17325
case value_t::floating : {return parse_floating (loc, ctx);}
17326
case value_t::string : {return parse_string (loc, ctx);}
17327
case value_t::offset_datetime: {return parse_offset_datetime(loc, ctx);}
17328
case value_t::local_datetime : {return parse_local_datetime (loc, ctx);}
17329
case value_t::local_date : {return parse_local_date (loc, ctx);}
17330
case value_t::local_time : {return parse_local_time (loc, ctx);}
17331
case value_t::array : {return parse_array (loc, ctx);}
17332
case value_t::table : {return parse_inline_table (loc, ctx);}
17333
default:
17334
{
17335
auto src = source_location(region(loc));
17336
return err(make_error_info("toml::parse_value: unknown value appeared",
17337
std::move(src), "here"));
17338
}
17339
}
17340
}
17341
17342
/* ============================================================================
17343
* _____ _ _
17344
* |_ _|_ _| |__| |___
17345
* | |/ _` | '_ \ / -_)
17346
* |_|\__,_|_.__/_\___|
17347
*/
17348
17349
template<typename TC>
17350
result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info>
17351
parse_table_key(location& loc, context<TC>& ctx)
17352
{
17353
const auto first = loc;
17354
const auto& spec = ctx.toml_spec();
17355
17356
auto reg = syntax::std_table(spec).scan(loc);
17357
if(!reg.is_ok())
17358
{
17359
return err(make_syntax_error("toml::parse_table_key: invalid table key",
17360
syntax::std_table(spec), loc));
17361
}
17362
17363
loc = first;
17364
loc.advance(); // skip [
17365
skip_whitespace(loc, ctx);
17366
17367
auto keys_res = parse_key(loc, ctx);
17368
if(keys_res.is_err())
17369
{
17370
return err(std::move(keys_res.unwrap_err()));
17371
}
17372
17373
skip_whitespace(loc, ctx);
17374
loc.advance(); // ]
17375
17376
return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg)));
17377
}
17378
17379
template<typename TC>
17380
result<std::pair<std::vector<typename basic_value<TC>::key_type>, region>, error_info>
17381
parse_array_table_key(location& loc, context<TC>& ctx)
17382
{
17383
const auto first = loc;
17384
const auto& spec = ctx.toml_spec();
17385
17386
auto reg = syntax::array_table(spec).scan(loc);
17387
if(!reg.is_ok())
17388
{
17389
return err(make_syntax_error("toml::parse_array_table_key: invalid array-of-tables key",
17390
syntax::array_table(spec), loc));
17391
}
17392
17393
loc = first;
17394
loc.advance(); // [
17395
loc.advance(); // [
17396
skip_whitespace(loc, ctx);
17397
17398
auto keys_res = parse_key(loc, ctx);
17399
if(keys_res.is_err())
17400
{
17401
return err(std::move(keys_res.unwrap_err()));
17402
}
17403
17404
skip_whitespace(loc, ctx);
17405
loc.advance(); // ]
17406
loc.advance(); // ]
17407
17408
return ok(std::make_pair(std::move(keys_res.unwrap().first), std::move(reg)));
17409
}
17410
17411
// called after reading [table.keys] and comments around it.
17412
// Since table may already contain a subtable ([x.y.z] can be defined before [x]),
17413
// the table that is being parsed is passed as an argument.
17414
template<typename TC>
17415
result<none_t, error_info>
17416
parse_table(location& loc, context<TC>& ctx, basic_value<TC>& table)
17417
{
17418
assert(table.is_table());
17419
17420
const auto num_errors = ctx.errors().size();
17421
const auto& spec = ctx.toml_spec();
17422
17423
// clear indent info
17424
table.as_table_fmt().indent_type = indent_char::none;
17425
17426
bool newline_found = true;
17427
while( ! loc.eof())
17428
{
17429
const auto start = loc;
17430
17431
auto sp = skip_multiline_spacer(loc, ctx, newline_found);
17432
17433
// if reached to EOF, the table ends here. return.
17434
if(loc.eof())
17435
{
17436
break;
17437
}
17438
// if next table is comming, return.
17439
if(sequence(syntax::ws(spec), character('[')).scan(loc).is_ok())
17440
{
17441
loc = start;
17442
break;
17443
}
17444
// otherwise, it should be a key-value pair.
17445
newline_found = newline_found || (sp.has_value() && sp.value().newline_found);
17446
if( ! newline_found)
17447
{
17448
return err(make_error_info("toml::parse_table: "
17449
"newline (LF / CRLF) or EOF is expected",
17450
source_location(region(loc)), "here"));
17451
}
17452
if(sp.has_value() && sp.value().indent_type != indent_char::none)
17453
{
17454
table.as_table_fmt().indent_type = sp.value().indent_type;
17455
table.as_table_fmt().body_indent = sp.value().indent;
17456
}
17457
17458
newline_found = false; // reset
17459
if(auto kv_res = parse_key_value_pair(loc, ctx))
17460
{
17461
auto keys = std::move(kv_res.unwrap().first.first);
17462
auto key_reg = std::move(kv_res.unwrap().first.second);
17463
auto val = std::move(kv_res.unwrap().second);
17464
17465
if(sp.has_value())
17466
{
17467
for(const auto& com : sp.value().comments)
17468
{
17469
val.comments().push_back(com);
17470
}
17471
}
17472
17473
if(auto com_res = parse_comment_line(loc, ctx))
17474
{
17475
if(auto com_opt = com_res.unwrap())
17476
{
17477
val.comments().push_back(com_opt.value());
17478
newline_found = true; // comment includes newline at the end
17479
}
17480
}
17481
else
17482
{
17483
ctx.report_error(std::move(com_res.unwrap_err()));
17484
}
17485
17486
auto ins_res = insert_value(inserting_value_kind::dotted_keys,
17487
std::addressof(table.as_table()),
17488
keys, std::move(key_reg), std::move(val));
17489
if(ins_res.is_err())
17490
{
17491
ctx.report_error(std::move(ins_res.unwrap_err()));
17492
}
17493
}
17494
else
17495
{
17496
ctx.report_error(std::move(kv_res.unwrap_err()));
17497
skip_key_value_pair(loc, ctx);
17498
}
17499
}
17500
17501
if(num_errors < ctx.errors().size())
17502
{
17503
assert(ctx.has_error()); // already reported
17504
return err(ctx.pop_last_error());
17505
}
17506
return ok();
17507
}
17508
17509
template<typename TC>
17510
result<basic_value<TC>, std::vector<error_info>>
17511
parse_file(location& loc, context<TC>& ctx)
17512
{
17513
using value_type = basic_value<TC>;
17514
using table_type = typename value_type::table_type;
17515
17516
const auto first = loc;
17517
const auto& spec = ctx.toml_spec();
17518
17519
if(loc.eof())
17520
{
17521
return ok(value_type(table_type(), table_format_info{}, {}, region(loc)));
17522
}
17523
17524
value_type root(table_type(), table_format_info{}, {}, region(loc));
17525
root.as_table_fmt().fmt = table_format::multiline;
17526
root.as_table_fmt().indent_type = indent_char::none;
17527
17528
// parse top comment.
17529
//
17530
// ```toml
17531
// # this is a comment for the top-level table.
17532
//
17533
// key = "the first value"
17534
// ```
17535
//
17536
// ```toml
17537
// # this is a comment for "the first value".
17538
// key = "the first value"
17539
// ```
17540
while( ! loc.eof())
17541
{
17542
if(auto com_res = parse_comment_line(loc, ctx))
17543
{
17544
if(auto com_opt = com_res.unwrap())
17545
{
17546
root.comments().push_back(std::move(com_opt.value()));
17547
}
17548
else // no comment found.
17549
{
17550
// if it is not an empty line, clear the root comment.
17551
if( ! sequence(syntax::ws(spec), syntax::newline(spec)).scan(loc).is_ok())
17552
{
17553
loc = first;
17554
root.comments().clear();
17555
}
17556
break;
17557
}
17558
}
17559
else
17560
{
17561
ctx.report_error(std::move(com_res.unwrap_err()));
17562
skip_comment_block(loc, ctx);
17563
}
17564
}
17565
17566
// parse root table
17567
{
17568
const auto res = parse_table(loc, ctx, root);
17569
if(res.is_err())
17570
{
17571
ctx.report_error(std::move(res.unwrap_err()));
17572
skip_until_next_table(loc, ctx);
17573
}
17574
}
17575
17576
// parse tables
17577
17578
while( ! loc.eof())
17579
{
17580
auto sp = skip_multiline_spacer(loc, ctx, /*newline_found=*/true);
17581
17582
if(auto key_res = parse_array_table_key(loc, ctx))
17583
{
17584
auto key = std::move(std::get<0>(key_res.unwrap()));
17585
auto reg = std::move(std::get<1>(key_res.unwrap()));
17586
17587
std::vector<std::string> com;
17588
if(sp.has_value())
17589
{
17590
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
17591
{
17592
com.push_back(std::move(sp.value().comments.at(i)));
17593
}
17594
}
17595
17596
// [table.def] must be followed by one of
17597
// - a comment line
17598
// - whitespace + newline
17599
// - EOF
17600
if(auto com_res = parse_comment_line(loc, ctx))
17601
{
17602
if(auto com_opt = com_res.unwrap())
17603
{
17604
com.push_back(com_opt.value());
17605
}
17606
else // if there is no comment, ws+newline must exist (or EOF)
17607
{
17608
skip_whitespace(loc, ctx);
17609
if( ! loc.eof() && ! syntax::newline(ctx.toml_spec()).scan(loc).is_ok())
17610
{
17611
ctx.report_error(make_syntax_error("toml::parse_file: "
17612
"newline (or EOF) expected",
17613
syntax::newline(ctx.toml_spec()), loc));
17614
skip_until_next_table(loc, ctx);
17615
continue;
17616
}
17617
}
17618
}
17619
else // comment syntax error (rare)
17620
{
17621
ctx.report_error(com_res.unwrap_err());
17622
skip_until_next_table(loc, ctx);
17623
continue;
17624
}
17625
17626
table_format_info fmt;
17627
fmt.fmt = table_format::multiline;
17628
fmt.indent_type = indent_char::none;
17629
auto tab = value_type(table_type{}, std::move(fmt), std::move(com), reg);
17630
17631
auto inserted = insert_value(inserting_value_kind::array_table,
17632
std::addressof(root.as_table()),
17633
key, std::move(reg), std::move(tab));
17634
17635
if(inserted.is_err())
17636
{
17637
ctx.report_error(inserted.unwrap_err());
17638
17639
// check errors in the table
17640
auto tmp = basic_value<TC>(table_type());
17641
auto res = parse_table(loc, ctx, tmp);
17642
if(res.is_err())
17643
{
17644
ctx.report_error(res.unwrap_err());
17645
skip_until_next_table(loc, ctx);
17646
}
17647
continue;
17648
}
17649
17650
auto tab_ptr = inserted.unwrap();
17651
assert(tab_ptr);
17652
17653
const auto tab_res = parse_table(loc, ctx, *tab_ptr);
17654
if(tab_res.is_err())
17655
{
17656
ctx.report_error(tab_res.unwrap_err());
17657
skip_until_next_table(loc, ctx);
17658
}
17659
17660
// parse_table first clears `indent_type`.
17661
// to keep header indent info, we must store it later.
17662
if(sp.has_value() && sp.value().indent_type != indent_char::none)
17663
{
17664
tab_ptr->as_table_fmt().indent_type = sp.value().indent_type;
17665
tab_ptr->as_table_fmt().name_indent = sp.value().indent;
17666
}
17667
continue;
17668
}
17669
if(auto key_res = parse_table_key(loc, ctx))
17670
{
17671
auto key = std::move(std::get<0>(key_res.unwrap()));
17672
auto reg = std::move(std::get<1>(key_res.unwrap()));
17673
17674
std::vector<std::string> com;
17675
if(sp.has_value())
17676
{
17677
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
17678
{
17679
com.push_back(std::move(sp.value().comments.at(i)));
17680
}
17681
}
17682
17683
// [table.def] must be followed by one of
17684
// - a comment line
17685
// - whitespace + newline
17686
// - EOF
17687
if(auto com_res = parse_comment_line(loc, ctx))
17688
{
17689
if(auto com_opt = com_res.unwrap())
17690
{
17691
com.push_back(com_opt.value());
17692
}
17693
else // if there is no comment, ws+newline must exist (or EOF)
17694
{
17695
skip_whitespace(loc, ctx);
17696
if( ! loc.eof() && ! syntax::newline(ctx.toml_spec()).scan(loc).is_ok())
17697
{
17698
ctx.report_error(make_syntax_error("toml::parse_file: "
17699
"newline (or EOF) expected",
17700
syntax::newline(ctx.toml_spec()), loc));
17701
skip_until_next_table(loc, ctx);
17702
continue;
17703
}
17704
}
17705
}
17706
else // comment syntax error (rare)
17707
{
17708
ctx.report_error(com_res.unwrap_err());
17709
skip_until_next_table(loc, ctx);
17710
continue;
17711
}
17712
17713
table_format_info fmt;
17714
fmt.fmt = table_format::multiline;
17715
fmt.indent_type = indent_char::none;
17716
auto tab = value_type(table_type{}, std::move(fmt), std::move(com), reg);
17717
17718
auto inserted = insert_value(inserting_value_kind::std_table,
17719
std::addressof(root.as_table()),
17720
key, std::move(reg), std::move(tab));
17721
17722
if(inserted.is_err())
17723
{
17724
ctx.report_error(inserted.unwrap_err());
17725
17726
// check errors in the table
17727
auto tmp = basic_value<TC>(table_type());
17728
auto res = parse_table(loc, ctx, tmp);
17729
if(res.is_err())
17730
{
17731
ctx.report_error(res.unwrap_err());
17732
skip_until_next_table(loc, ctx);
17733
}
17734
continue;
17735
}
17736
17737
auto tab_ptr = inserted.unwrap();
17738
assert(tab_ptr);
17739
17740
const auto tab_res = parse_table(loc, ctx, *tab_ptr);
17741
if(tab_res.is_err())
17742
{
17743
ctx.report_error(tab_res.unwrap_err());
17744
skip_until_next_table(loc, ctx);
17745
}
17746
if(sp.has_value() && sp.value().indent_type != indent_char::none)
17747
{
17748
tab_ptr->as_table_fmt().indent_type = sp.value().indent_type;
17749
tab_ptr->as_table_fmt().name_indent = sp.value().indent;
17750
}
17751
continue;
17752
}
17753
17754
// does not match array_table nor std_table. report an error.
17755
const auto keytop = loc;
17756
const auto maybe_array_of_tables = literal("[[").scan(loc).is_ok();
17757
loc = keytop;
17758
17759
if(maybe_array_of_tables)
17760
{
17761
ctx.report_error(make_syntax_error("toml::parse_file: invalid array-table key",
17762
syntax::array_table(spec), loc));
17763
}
17764
else
17765
{
17766
ctx.report_error(make_syntax_error("toml::parse_file: invalid table key",
17767
syntax::std_table(spec), loc));
17768
}
17769
skip_until_next_table(loc, ctx);
17770
}
17771
17772
if( ! ctx.errors().empty())
17773
{
17774
return err(std::move(ctx.errors()));
17775
}
17776
17777
#ifdef TOML11_ENABLE_ACCESS_CHECK
17778
detail::unset_access_flag_recursively(root);
17779
#endif
17780
17781
return ok(std::move(root));
17782
}
17783
17784
template<typename TC>
17785
result<basic_value<TC>, std::vector<error_info>>
17786
parse_impl(std::vector<location::char_type> cs, std::string fname, const spec& s)
17787
{
17788
using value_type = basic_value<TC>;
17789
using table_type = typename value_type::table_type;
17790
17791
// an empty file is a valid toml file.
17792
if(cs.empty())
17793
{
17794
auto src = std::make_shared<std::vector<location::char_type>>(std::move(cs));
17795
location loc(std::move(src), std::move(fname));
17796
return ok(value_type(table_type(), table_format_info{}, std::vector<std::string>{}, region(loc)));
17797
}
17798
17799
// to simplify parser, add newline at the end if there is no LF.
17800
// But, if it has raw CR, the file is invalid (in TOML, CR is not a valid
17801
// newline char). if it ends with CR, do not add LF and report it.
17802
if(cs.back() != '\n' && cs.back() != '\r')
17803
{
17804
cs.push_back('\n');
17805
}
17806
17807
auto src = std::make_shared<std::vector<location::char_type>>(std::move(cs));
17808
17809
location loc(std::move(src), std::move(fname));
17810
17811
// skip BOM if found
17812
if(loc.source()->size() >= 3)
17813
{
17814
auto first = loc;
17815
17816
const auto c0 = loc.current(); loc.advance();
17817
const auto c1 = loc.current(); loc.advance();
17818
const auto c2 = loc.current(); loc.advance();
17819
17820
const auto bom_found = (c0 == 0xEF) && (c1 == 0xBB) && (c2 == 0xBF);
17821
if( ! bom_found)
17822
{
17823
loc = first;
17824
}
17825
}
17826
17827
context<TC> ctx(s);
17828
17829
return parse_file(loc, ctx);
17830
}
17831
17832
} // detail
17833
17834
// -----------------------------------------------------------------------------
17835
// parse(byte array)
17836
17837
template<typename TC = type_config>
17838
result<basic_value<TC>, std::vector<error_info>>
17839
try_parse(std::vector<unsigned char> content, std::string filename,
17840
spec s = spec::default_version())
17841
{
17842
return detail::parse_impl<TC>(std::move(content), std::move(filename), std::move(s));
17843
}
17844
template<typename TC = type_config>
17845
basic_value<TC>
17846
parse(std::vector<unsigned char> content, std::string filename,
17847
spec s = spec::default_version())
17848
{
17849
auto res = try_parse<TC>(std::move(content), std::move(filename), std::move(s));
17850
if(res.is_ok())
17851
{
17852
return res.unwrap();
17853
}
17854
else
17855
{
17856
std::string msg;
17857
for(const auto& err : res.unwrap_err())
17858
{
17859
msg += format_error(err);
17860
}
17861
throw syntax_error(std::move(msg), std::move(res.unwrap_err()));
17862
}
17863
}
17864
17865
// -----------------------------------------------------------------------------
17866
// parse(istream)
17867
17868
template<typename TC = type_config>
17869
result<basic_value<TC>, std::vector<error_info>>
17870
try_parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version())
17871
{
17872
const auto beg = is.tellg();
17873
is.seekg(0, std::ios::end);
17874
const auto end = is.tellg();
17875
const auto fsize = end - beg;
17876
is.seekg(beg);
17877
17878
// read whole file as a sequence of char
17879
assert(fsize >= 0);
17880
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize), '\0');
17881
is.read(reinterpret_cast<char*>(letters.data()), static_cast<std::streamsize>(fsize));
17882
17883
return detail::parse_impl<TC>(std::move(letters), std::move(fname), std::move(s));
17884
}
17885
17886
template<typename TC = type_config>
17887
basic_value<TC> parse(std::istream& is, std::string fname = "unknown file", spec s = spec::default_version())
17888
{
17889
auto res = try_parse<TC>(is, std::move(fname), std::move(s));
17890
if(res.is_ok())
17891
{
17892
return res.unwrap();
17893
}
17894
else
17895
{
17896
std::string msg;
17897
for(const auto& err : res.unwrap_err())
17898
{
17899
msg += format_error(err);
17900
}
17901
throw syntax_error(std::move(msg), std::move(res.unwrap_err()));
17902
}
17903
}
17904
17905
// -----------------------------------------------------------------------------
17906
// parse(filename)
17907
17908
template<typename TC = type_config>
17909
result<basic_value<TC>, std::vector<error_info>>
17910
try_parse(std::string fname, spec s = spec::default_version())
17911
{
17912
std::ifstream ifs(fname, std::ios_base::binary);
17913
if(!ifs.good())
17914
{
17915
std::vector<error_info> e;
17916
e.push_back(error_info("toml::parse: Error opening file \"" + fname + "\"", {}));
17917
return err(std::move(e));
17918
}
17919
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
17920
17921
return try_parse<TC>(ifs, std::move(fname), std::move(s));
17922
}
17923
17924
template<typename TC = type_config>
17925
basic_value<TC> parse(std::string fname, spec s = spec::default_version())
17926
{
17927
std::ifstream ifs(fname, std::ios_base::binary);
17928
if(!ifs.good())
17929
{
17930
throw file_io_error("toml::parse: error opening file", fname);
17931
}
17932
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
17933
17934
return parse<TC>(ifs, std::move(fname), std::move(s));
17935
}
17936
17937
template<typename TC = type_config, std::size_t N>
17938
result<basic_value<TC>, std::vector<error_info>>
17939
try_parse(const char (&fname)[N], spec s = spec::default_version())
17940
{
17941
return try_parse<TC>(std::string(fname), std::move(s));
17942
}
17943
17944
template<typename TC = type_config, std::size_t N>
17945
basic_value<TC> parse(const char (&fname)[N], spec s = spec::default_version())
17946
{
17947
return parse<TC>(std::string(fname), std::move(s));
17948
}
17949
17950
// ----------------------------------------------------------------------------
17951
// parse_str
17952
17953
template<typename TC = type_config>
17954
result<basic_value<TC>, std::vector<error_info>>
17955
try_parse_str(std::string content, spec s = spec::default_version(),
17956
cxx::source_location loc = cxx::source_location::current())
17957
{
17958
std::istringstream iss(std::move(content));
17959
std::string name("internal string" + cxx::to_string(loc));
17960
return try_parse<TC>(iss, std::move(name), std::move(s));
17961
}
17962
17963
template<typename TC = type_config>
17964
basic_value<TC> parse_str(std::string content, spec s = spec::default_version(),
17965
cxx::source_location loc = cxx::source_location::current())
17966
{
17967
auto res = try_parse_str<TC>(std::move(content), std::move(s), std::move(loc));
17968
if(res.is_ok())
17969
{
17970
return res.unwrap();
17971
}
17972
else
17973
{
17974
std::string msg;
17975
for(const auto& err : res.unwrap_err())
17976
{
17977
msg += format_error(err);
17978
}
17979
throw syntax_error(std::move(msg), std::move(res.unwrap_err()));
17980
}
17981
}
17982
17983
// ----------------------------------------------------------------------------
17984
// filesystem
17985
17986
#if defined(TOML11_HAS_FILESYSTEM)
17987
17988
template<typename TC = type_config, typename FSPATH>
17989
cxx::enable_if_t<std::is_same<FSPATH, std::filesystem::path>::value,
17990
result<basic_value<TC>, std::vector<error_info>>>
17991
try_parse(const FSPATH& fpath, spec s = spec::default_version())
17992
{
17993
std::ifstream ifs(fpath, std::ios_base::binary);
17994
if(!ifs.good())
17995
{
17996
std::vector<error_info> e;
17997
e.push_back(error_info("toml::parse: Error opening file \"" + fpath.string() + "\"", {}));
17998
return err(std::move(e));
17999
}
18000
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
18001
18002
return try_parse<TC>(ifs, fpath.string(), std::move(s));
18003
}
18004
18005
template<typename TC = type_config, typename FSPATH>
18006
cxx::enable_if_t<std::is_same<FSPATH, std::filesystem::path>::value,
18007
basic_value<TC>>
18008
parse(const FSPATH& fpath, spec s = spec::default_version())
18009
{
18010
std::ifstream ifs(fpath, std::ios_base::binary);
18011
if(!ifs.good())
18012
{
18013
throw file_io_error("toml::parse: error opening file", fpath.string());
18014
}
18015
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
18016
18017
return parse<TC>(ifs, fpath.string(), std::move(s));
18018
}
18019
#endif
18020
18021
// -----------------------------------------------------------------------------
18022
// FILE*
18023
18024
template<typename TC = type_config>
18025
result<basic_value<TC>, std::vector<error_info>>
18026
try_parse(FILE* fp, std::string filename, spec s = spec::default_version())
18027
{
18028
const long beg = std::ftell(fp);
18029
if (beg == -1L)
18030
{
18031
return err(std::vector<error_info>{error_info(
18032
std::string("Failed to access: \"") + filename +
18033
"\", errno = " + std::to_string(errno), {}
18034
)});
18035
}
18036
18037
const int res_seekend = std::fseek(fp, 0, SEEK_END);
18038
if (res_seekend != 0)
18039
{
18040
return err(std::vector<error_info>{error_info(
18041
std::string("Failed to seek: \"") + filename +
18042
"\", errno = " + std::to_string(errno), {}
18043
)});
18044
}
18045
18046
const long end = std::ftell(fp);
18047
if (end == -1L)
18048
{
18049
return err(std::vector<error_info>{error_info(
18050
std::string("Failed to access: \"") + filename +
18051
"\", errno = " + std::to_string(errno), {}
18052
)});
18053
}
18054
18055
const auto fsize = end - beg;
18056
18057
const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET);
18058
if (res_seekbeg != 0)
18059
{
18060
return err(std::vector<error_info>{error_info(
18061
std::string("Failed to seek: \"") + filename +
18062
"\", errno = " + std::to_string(errno), {}
18063
)});
18064
18065
}
18066
18067
// read whole file as a sequence of char
18068
assert(fsize >= 0);
18069
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
18070
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
18071
if(actual != static_cast<std::size_t>(fsize))
18072
{
18073
return err(std::vector<error_info>{error_info(
18074
std::string("File size changed: \"") + filename +
18075
std::string("\" make sure that FILE* is in binary mode "
18076
"to avoid LF <-> CRLF conversion"), {}
18077
)});
18078
}
18079
18080
return detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
18081
}
18082
18083
template<typename TC = type_config>
18084
basic_value<TC>
18085
parse(FILE* fp, std::string filename, spec s = spec::default_version())
18086
{
18087
const long beg = std::ftell(fp);
18088
if (beg == -1L)
18089
{
18090
throw file_io_error(errno, "Failed to access", filename);
18091
}
18092
18093
const int res_seekend = std::fseek(fp, 0, SEEK_END);
18094
if (res_seekend != 0)
18095
{
18096
throw file_io_error(errno, "Failed to seek", filename);
18097
}
18098
18099
const long end = std::ftell(fp);
18100
if (end == -1L)
18101
{
18102
throw file_io_error(errno, "Failed to access", filename);
18103
}
18104
18105
const auto fsize = end - beg;
18106
18107
const auto res_seekbeg = std::fseek(fp, beg, SEEK_SET);
18108
if (res_seekbeg != 0)
18109
{
18110
throw file_io_error(errno, "Failed to seek", filename);
18111
}
18112
18113
// read whole file as a sequence of char
18114
assert(fsize >= 0);
18115
std::vector<detail::location::char_type> letters(static_cast<std::size_t>(fsize));
18116
const auto actual = std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), fp);
18117
if(actual != static_cast<std::size_t>(fsize))
18118
{
18119
throw file_io_error(errno, "File size changed; make sure that "
18120
"FILE* is in binary mode to avoid LF <-> CRLF conversion", filename);
18121
}
18122
18123
auto res = detail::parse_impl<TC>(std::move(letters), std::move(filename), std::move(s));
18124
if(res.is_ok())
18125
{
18126
return res.unwrap();
18127
}
18128
else
18129
{
18130
std::string msg;
18131
for(const auto& err : res.unwrap_err())
18132
{
18133
msg += format_error(err);
18134
}
18135
throw syntax_error(std::move(msg), std::move(res.unwrap_err()));
18136
}
18137
}
18138
18139
} // TOML11_INLINE_VERSION_NAMESPACE
18140
} // namespace toml
18141
18142
#if defined(TOML11_COMPILE_SOURCES)
18143
namespace toml
18144
{
18145
inline namespace TOML11_INLINE_VERSION_NAMESPACE
18146
{
18147
struct type_config;
18148
struct ordered_type_config;
18149
18150
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::vector<unsigned char>, std::string, spec);
18151
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::istream&, std::string, spec);
18152
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(std::string, spec);
18153
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse<type_config>(FILE*, std::string, spec);
18154
extern template result<basic_value<type_config>, std::vector<error_info>> try_parse_str<type_config>(std::string, spec, cxx::source_location);
18155
18156
extern template basic_value<type_config> parse<type_config>(std::vector<unsigned char>, std::string, spec);
18157
extern template basic_value<type_config> parse<type_config>(std::istream&, std::string, spec);
18158
extern template basic_value<type_config> parse<type_config>(std::string, spec);
18159
extern template basic_value<type_config> parse<type_config>(FILE*, std::string, spec);
18160
extern template basic_value<type_config> parse_str<type_config>(std::string, spec, cxx::source_location);
18161
18162
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::vector<unsigned char>, std::string, spec);
18163
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::istream&, std::string, spec);
18164
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(std::string, spec);
18165
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse<ordered_type_config>(FILE*, std::string, spec);
18166
extern template result<basic_value<ordered_type_config>, std::vector<error_info>> try_parse_str<ordered_type_config>(std::string, spec, cxx::source_location);
18167
18168
extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::vector<unsigned char>, std::string, spec);
18169
extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::istream&, std::string, spec);
18170
extern template basic_value<ordered_type_config> parse<ordered_type_config>(std::string, spec);
18171
extern template basic_value<ordered_type_config> parse<ordered_type_config>(FILE*, std::string, spec);
18172
extern template basic_value<ordered_type_config> parse_str<ordered_type_config>(std::string, spec, cxx::source_location);
18173
18174
#if defined(TOML11_HAS_FILESYSTEM)
18175
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, result<basic_value<type_config>, std::vector<error_info>>> try_parse<type_config, std::filesystem::path>(const std::filesystem::path&, spec);
18176
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, result<basic_value<ordered_type_config>, std::vector<error_info>>> try_parse<ordered_type_config, std::filesystem::path>(const std::filesystem::path&, spec);
18177
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, basic_value<type_config> > parse <type_config, std::filesystem::path>(const std::filesystem::path&, spec);
18178
extern template cxx::enable_if_t<std::is_same<std::filesystem::path, std::filesystem::path>::value, basic_value<ordered_type_config> > parse <ordered_type_config, std::filesystem::path>(const std::filesystem::path&, spec);
18179
#endif // filesystem
18180
18181
} // TOML11_INLINE_VERSION_NAMESPACE
18182
} // toml
18183
#endif // TOML11_COMPILE_SOURCES
18184
18185
#endif // TOML11_PARSER_HPP
18186
#ifndef TOML11_LITERAL_HPP
18187
#define TOML11_LITERAL_HPP
18188
18189
#ifndef TOML11_LITERAL_FWD_HPP
18190
#define TOML11_LITERAL_FWD_HPP
18191
18192
18193
namespace toml
18194
{
18195
inline namespace TOML11_INLINE_VERSION_NAMESPACE
18196
{
18197
18198
namespace detail
18199
{
18200
// implementation
18201
::toml::value literal_internal_impl(location loc);
18202
} // detail
18203
18204
inline namespace literals
18205
{
18206
inline namespace toml_literals
18207
{
18208
18209
::toml::value operator""_toml(const char* str, std::size_t len);
18210
18211
#if defined(TOML11_HAS_CHAR8_T)
18212
// value of u8"" literal has been changed from char to char8_t and char8_t is
18213
// NOT compatible to char
18214
::toml::value operator"" _toml(const char8_t* str, std::size_t len);
18215
#endif
18216
18217
} // toml_literals
18218
} // literals
18219
} // TOML11_INLINE_VERSION_NAMESPACE
18220
} // toml
18221
#endif // TOML11_LITERAL_FWD_HPP
18222
18223
#if ! defined(TOML11_COMPILE_SOURCES)
18224
#ifndef TOML11_LITERAL_IMPL_HPP
18225
#define TOML11_LITERAL_IMPL_HPP
18226
18227
18228
namespace toml
18229
{
18230
inline namespace TOML11_INLINE_VERSION_NAMESPACE
18231
{
18232
18233
namespace detail
18234
{
18235
// implementation
18236
TOML11_INLINE ::toml::value literal_internal_impl(location loc)
18237
{
18238
const auto s = ::toml::spec::default_version();
18239
context<type_config> ctx(s);
18240
18241
const auto front = loc;
18242
18243
// ------------------------------------------------------------------------
18244
// check if it is a raw value.
18245
18246
// skip empty lines and comment lines
18247
auto sp = skip_multiline_spacer(loc, ctx);
18248
if(loc.eof())
18249
{
18250
::toml::value val;
18251
if(sp.has_value())
18252
{
18253
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
18254
{
18255
val.comments().push_back(std::move(sp.value().comments.at(i)));
18256
}
18257
}
18258
return val;
18259
}
18260
18261
// to distinguish arrays and tables, first check it is a table or not.
18262
//
18263
// "[1,2,3]"_toml; // json: [1, 2, 3]
18264
// "[table]"_toml; // json: {"table": {}}
18265
// "[[1,2,3]]"_toml; // json: [[1, 2, 3]]
18266
// "[[table]]"_toml; // json: {"table": [{}]}
18267
//
18268
// "[[1]]"_toml; // json: {"1": [{}]}
18269
// "1 = [{}]"_toml; // json: {"1": [{}]}
18270
// "[[1,]]"_toml; // json: [[1]]
18271
// "[[1],]"_toml; // json: [[1]]
18272
const auto val_start = loc;
18273
18274
const bool is_table_key = syntax::std_table(s).scan(loc).is_ok();
18275
loc = val_start;
18276
const bool is_aots_key = syntax::array_table(s).scan(loc).is_ok();
18277
loc = val_start;
18278
18279
// If it is neither a table-key or a array-of-table-key, it may be a value.
18280
if(!is_table_key && !is_aots_key)
18281
{
18282
auto data = parse_value(loc, ctx);
18283
if(data.is_ok())
18284
{
18285
auto val = std::move(data.unwrap());
18286
if(sp.has_value())
18287
{
18288
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
18289
{
18290
val.comments().push_back(std::move(sp.value().comments.at(i)));
18291
}
18292
}
18293
auto com_res = parse_comment_line(loc, ctx);
18294
if(com_res.is_ok() && com_res.unwrap().has_value())
18295
{
18296
val.comments().push_back(com_res.unwrap().value());
18297
}
18298
return val;
18299
}
18300
}
18301
18302
// -------------------------------------------------------------------------
18303
// Note that still it can be a table, because the literal might be something
18304
// like the following.
18305
// ```cpp
18306
// // c++11 raw-string literal
18307
// const auto val = R"(
18308
// key = "value"
18309
// int = 42
18310
// )"_toml;
18311
// ```
18312
// It is a valid toml file.
18313
// It should be parsed as if we parse a file with this content.
18314
18315
loc = front;
18316
auto data = parse_file(loc, ctx);
18317
if(data.is_ok())
18318
{
18319
return data.unwrap();
18320
}
18321
else // not a value && not a file. error.
18322
{
18323
std::string msg;
18324
for(const auto& err : data.unwrap_err())
18325
{
18326
msg += format_error(err);
18327
}
18328
throw ::toml::syntax_error(std::move(msg), std::move(data.unwrap_err()));
18329
}
18330
}
18331
18332
} // detail
18333
18334
inline namespace literals
18335
{
18336
inline namespace toml_literals
18337
{
18338
18339
TOML11_INLINE ::toml::value
18340
operator""_toml(const char* str, std::size_t len)
18341
{
18342
if(len == 0)
18343
{
18344
return ::toml::value{};
18345
}
18346
18347
::toml::detail::location::container_type c(len);
18348
std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str),
18349
reinterpret_cast<const ::toml::detail::location::char_type*>(str + len),
18350
c.begin());
18351
if( ! c.empty() && c.back())
18352
{
18353
c.push_back('\n'); // to make it easy to parse comment, we add newline
18354
}
18355
18356
return literal_internal_impl(::toml::detail::location(
18357
std::make_shared<const toml::detail::location::container_type>(std::move(c)),
18358
"TOML literal encoded in a C++ code"));
18359
}
18360
18361
#if defined(__cpp_char8_t)
18362
# if __cpp_char8_t >= 201811L
18363
# define TOML11_HAS_CHAR8_T 1
18364
# endif
18365
#endif
18366
18367
#if defined(TOML11_HAS_CHAR8_T)
18368
// value of u8"" literal has been changed from char to char8_t and char8_t is
18369
// NOT compatible to char
18370
TOML11_INLINE ::toml::value
18371
operator"" _toml(const char8_t* str, std::size_t len)
18372
{
18373
if(len == 0)
18374
{
18375
return ::toml::value{};
18376
}
18377
18378
::toml::detail::location::container_type c(len);
18379
std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str),
18380
reinterpret_cast<const ::toml::detail::location::char_type*>(str + len),
18381
c.begin());
18382
if( ! c.empty() && c.back())
18383
{
18384
c.push_back('\n'); // to make it easy to parse comment, we add newline
18385
}
18386
18387
return literal_internal_impl(::toml::detail::location(
18388
std::make_shared<const toml::detail::location::container_type>(std::move(c)),
18389
"TOML literal encoded in a C++ code"));
18390
}
18391
#endif
18392
18393
} // toml_literals
18394
} // literals
18395
} // TOML11_INLINE_VERSION_NAMESPACE
18396
} // toml
18397
#endif // TOML11_LITERAL_IMPL_HPP
18398
#endif
18399
18400
#endif // TOML11_LITERAL_HPP
18401
#ifndef TOML11_TOML_HPP
18402
#define TOML11_TOML_HPP
18403
18404
// The MIT License (MIT)
18405
//
18406
// Copyright (c) 2017-now Toru Niina
18407
//
18408
// Permission is hereby granted, free of charge, to any person obtaining a copy
18409
// of this software and associated documentation files (the "Software"), to deal
18410
// in the Software without restriction, including without limitation the rights
18411
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18412
// copies of the Software, and to permit persons to whom the Software is
18413
// furnished to do so, subject to the following conditions:
18414
//
18415
// The above copyright notice and this permission notice shall be included in
18416
// all copies or substantial portions of the Software.
18417
//
18418
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18419
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18420
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18421
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18422
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18423
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18424
// THE SOFTWARE.
18425
18426
// IWYU pragma: begin_exports
18427
// IWYU pragma: end_exports
18428
18429
#endif// TOML11_TOML_HPP
18430
18431