47 #if defined(__clang__) 48 #pragma clang system_header 49 #elif defined(__GNUC__) 50 #pragma GCC system_header 53 #ifndef CONFIGURU_HEADER_HPP 54 #define CONFIGURU_HEADER_HPP 63 #include <initializer_list> 73 #ifndef CONFIGURU_ONERROR 74 #define CONFIGURU_ONERROR(message_str) \ 75 throw std::runtime_error(message_str) 76 #endif // CONFIGURU_ONERROR 78 #ifndef CONFIGURU_ASSERT 80 #define CONFIGURU_ASSERT(test) assert(test) 81 #endif // CONFIGURU_ASSERT 83 #ifndef CONFIGURU_ON_DANGLING 84 #define CONFIGURU_ON_DANGLING(message_str) \ 86 CONFIGURU_ONERROR(message_str) 87 #endif // CONFIGURU_ON_DANGLING 89 #define CONFIGURU_NORETURN __attribute__((noreturn)) 91 #ifndef CONFIGURU_IMPLICIT_CONVERSIONS 92 #define CONFIGURU_IMPLICIT_CONVERSIONS 0 96 #ifndef CONFIGURU_VALUE_SEMANTICS 97 #define CONFIGURU_VALUE_SEMANTICS 0 102 #define CONFIGURU_NORETURN __attribute__((noreturn)) 104 #undef Bool // Needed on Ubuntu 14.04 with GCC 4.8.5 105 #undef check // Needed on OSX 111 using DocInfo_SP = std::shared_ptr<DocInfo>;
113 using Index = unsigned;
114 const Index BAD_INDEX =
static_cast<Index
>(-1);
119 Index line = BAD_INDEX;
122 Include(DocInfo_SP d, Index l) : doc(d), line(l) {}
128 std::vector<Include> includers;
130 std::string filename;
132 DocInfo(
const std::string& fn) : filename(fn) { }
134 void append_include_info(std::string& ret,
const std::string& indent=
" ")
const;
137 struct BadLookupInfo;
140 template<
typename Config_T>
144 Index _nr = BAD_INDEX;
145 mutable bool _accessed =
false;
148 Config_Entry(Config_T value, Index nr) : _value(std::move(value)), _nr(nr) {}
151 using Comment = std::string;
152 using Comments = std::vector<Comment>;
161 Comments pre_end_brace;
201 Null, Bool, Int, Float, String, Array, Object
206 using ConfigArrayImpl = std::vector<Config>;
207 using ConfigObjectImpl = std::map<std::string, ObjectEntry>;
210 #if !CONFIGURU_VALUE_SEMANTICS 211 std::atomic<unsigned> _ref_count { 1 };
213 ConfigArrayImpl _impl;
222 Config(std::nullptr_t) : _type(Null) { }
223 Config(
float f) : _type(Float) { _u.f = f; }
224 Config(
double f) : _type(Float) { _u.f = f; }
225 Config(
bool b) : _type(Bool) { _u.b = b; }
226 Config(
int i) : _type(Int) { _u.i = i; }
227 Config(
unsigned int i) : _type(Int) { _u.i = i; }
228 Config(
long i) : _type(Int) { _u.i = i; }
229 Config(
unsigned long i) :
Config(static_cast<unsigned long long>(i)) {}
230 Config(
long long i) : _type(Int) { _u.i = i; }
231 Config(
unsigned long long i) : _type(Int)
233 if ((i & 0x8000000000000000ull) != 0) {
234 CONFIGURU_ONERROR(
"Integer too large to fit into 63 bits");
236 _u.i =
static_cast<int64_t
>(i);
261 Config(std::initializer_list<std::pair<std::string, Config>> values);
265 Config(
const std::vector<T>& values) : _type(Uninitialized)
268 _u.array->_impl.reserve(values.size());
269 for (
const auto& v : values) {
275 Config(
const std::vector<bool>& values) : _type(Uninitialized)
278 _u.array->_impl.reserve(values.size());
279 for (
const auto v : values) {
286 Config(
const std::map<std::string, T>& values) : _type(Uninitialized)
289 for (
const auto& p : values) {
290 (*this)[p.first] = p.second;
301 void tag(
const DocInfo_SP& doc, Index line, Index column);
307 static Config object(std::initializer_list<std::pair<std::string, Config>> values);
313 static Config array(std::initializer_list<Config> values);
316 template<
typename Container>
321 auto& impl = ret._u.array->_impl;
322 impl.reserve(container.size());
323 for (
auto&& v : container) {
324 impl.emplace_back(v);
341 void swap(
Config& o) noexcept;
343 #ifdef CONFIG_EXTENSION 350 Type type()
const {
return _type; }
352 bool is_uninitialized()
const {
return _type == Uninitialized; }
353 bool is_null()
const {
return _type == Null; }
354 bool is_bool()
const {
return _type == Bool; }
355 bool is_int()
const {
return _type == Int; }
356 bool is_float()
const {
return _type == Float; }
357 bool is_string()
const {
return _type == String; }
358 bool is_object()
const {
return _type == Object; }
359 bool is_array()
const {
return _type == Array; }
360 bool is_number()
const {
return is_int() || is_float(); }
363 std::string where()
const;
366 Index
line()
const {
return _line; }
369 const DocInfo_SP&
doc()
const {
return _doc; }
370 void set_doc(
const DocInfo_SP& doc) { _doc = doc; }
375 #if CONFIGURU_IMPLICIT_CONVERSIONS 378 explicit operator T()
const {
return as<T>(*this); }
380 inline operator bool()
const {
return as_bool(); }
381 inline operator signed char()
const {
return as_integer<signed char>(); }
382 inline operator unsigned char()
const {
return as_integer<unsigned char>(); }
383 inline operator signed short()
const {
return as_integer<signed short>(); }
384 inline operator unsigned short()
const {
return as_integer<unsigned short>(); }
385 inline operator signed int()
const {
return as_integer<signed int>(); }
386 inline operator unsigned int()
const {
return as_integer<unsigned int>(); }
387 inline operator signed long()
const {
return as_integer<signed long>(); }
388 inline operator unsigned long()
const {
return as_integer<unsigned long>(); }
389 inline operator signed long long()
const {
return as_integer<signed long long>(); }
390 inline operator unsigned long long()
const {
return as_integer<unsigned long long>(); }
391 inline operator float()
const {
return as_float(); }
392 inline operator double()
const {
return as_double(); }
393 inline operator std::string()
const {
return as_string(); }
394 inline operator Config::ConfigArrayImpl()
const {
return as_array(); }
398 operator std::vector<T>()
const 400 const auto& array = as_array();
402 ret.reserve(array.size());
403 for (
auto&& config : array) {
404 ret.push_back((T)config);
410 template<
typename T,
size_t N>
411 operator std::array<T, N>()
const 413 const auto& array = as_array();
414 check(array.size() == N,
"Array size mismatch.");
415 std::array<T, N> ret;
416 std::copy(array.begin(), array.end(), ret.begin());
422 template<
typename Left,
typename Right>
423 operator std::pair<Left, Right>()
const 425 const auto& array = as_array();
426 check(array.size() == 2u,
"Mismatched array length.");
427 return {(Left)array[0], (Right)array[1]};
432 explicit operator T()
const {
return as<T>(*this); }
436 explicit operator std::vector<T>()
const 438 const auto& array = as_array();
440 ret.reserve(array.size());
441 for (
auto&& config : array) {
442 ret.push_back(static_cast<T>(config));
448 template<
typename T,
size_t N>
449 explicit operator std::array<T, N>()
const 451 const auto& array = as_array();
452 check(array.size() == N,
"Array size mismatch.");
453 std::array<T, N> ret;
454 for (
size_t i =0; i < N; ++i) {
455 ret[i] =
static_cast<T
>(array[i]);
462 template<
typename Left,
typename Right>
463 explicit operator std::pair<Left, Right>()
const 465 const auto& array = as_array();
466 check(array.size() == 2u,
"Mismatched array length.");
467 return {
static_cast<Left
>(array[0]), static_cast<Right>(array[1])};
471 const std::string& as_string()
const { assert_type(String);
return *_u.str; }
472 const char* c_str()
const { assert_type(String);
return _u.str->c_str(); }
481 template<
typename IntT>
482 IntT as_integer()
const 484 static_assert(std::is_integral<IntT>::value,
"Not an integer.");
486 check(static_cast<int64_t>(static_cast<IntT>(_u.i)) == _u.i,
"Integer out of range");
487 return static_cast<IntT
>(_u.i);
490 float as_float()
const 496 return static_cast<float>(_u.f);
500 double as_double()
const 518 if (_type == BadLookupType) {
519 return default_value;
521 return static_cast<T
>(*this);
531 return as_array().size();
538 return _u.array->_impl;
545 return _u.array->_impl;
551 auto&& array = as_array();
552 check(ix < array.size(),
"Array index out of range");
559 auto&& array = as_array();
560 check(ix < array.size(),
"Array index out of range");
567 as_array().push_back(std::move(value));
574 size_t object_size()
const;
593 const Config& operator[](
const std::string& key)
const;
596 Config& operator[](
const std::string& key);
599 template<std::
size_t N>
601 template<std::
size_t N>
602 const Config& operator[](
const char (&key)[N])
const {
return operator[](std::string(key)); }
605 bool has_key(
const std::string& key)
const;
608 size_t count(
const std::string& key)
const {
return has_key(key) ? 1 : 0; }
611 bool emplace(std::string key,
Config value);
614 void insert_or_assign(
const std::string& key,
Config&& value);
617 bool erase(
const std::string& key);
621 T
get(
const std::string& key)
const 623 return as<T>((*this)[key]);
628 T get_or(
const std::string& key,
const T& default_value)
const;
631 std::string
get_or(
const std::string& key,
const char* default_value)
const 633 return get_or<std::string>(key, default_value);
638 T get_or(std::initializer_list<std::string> keys,
const T& default_value)
const;
641 std::string
get_or(std::initializer_list<std::string> keys,
const char* default_value)
const 643 return get_or<std::string>(keys, default_value);
651 #if !CONFIGURU_VALUE_SEMANTICS // No need for a deep_clone method when all copies are deep clones. 652 Config deep_clone()
const;
659 void visit_dangling(
const std::function<
void(
const std::string& key,
const Config& value)>& visitor)
const;
662 void check_dangling()
const;
665 void mark_accessed(
bool v)
const;
672 return _comments && !_comments->empty();
696 const char* debug_descr()
const;
699 static const char* type_str(
Type t);
704 inline void check(
bool b,
const char* msg)
const 711 void assert_type(
Type t)
const;
713 void on_error(
const std::string& msg)
const CONFIGURU_NORETURN;
718 using ConfigComments_UP = std::unique_ptr<ConfigComments>;
724 const std::string* str;
727 BadLookupInfo* bad_lookup;
731 ConfigComments_UP _comments;
732 Index _line = BAD_INDEX;
733 Type _type = Uninitialized;
740 #if !CONFIGURU_VALUE_SEMANTICS 741 std::atomic<unsigned> _ref_count { 1 };
743 ConfigObjectImpl _impl;
749 explicit iterator(ConfigObjectImpl::iterator it) : _it(std::move(it)) {}
752 _it->second._accessed =
true;
762 return a._it == b._it;
766 return a._it != b._it;
769 const std::string& key()
const {
return _it->first; }
770 Config& value()
const {
return _it->second._value; }
773 ConfigObjectImpl::iterator _it;
780 explicit const_iterator(ConfigObjectImpl::const_iterator it) : _it(std::move(it)) {}
783 _it->second._accessed =
true;
793 return a._it == b._it;
797 return a._it != b._it;
800 const std::string& key()
const {
return _it->first; }
801 const Config& value()
const {
return _it->second._value; }
804 ConfigObjectImpl::const_iterator _it;
817 inline bool operator==(
const Config& a,
const Config& b)
822 inline bool operator!=(
const Config& a,
const Config& b)
829 template<>
inline bool Config::get()
const {
return as_bool(); }
830 template<>
inline signed char Config::get()
const {
return as_integer<signed char>(); }
831 template<>
inline unsigned char Config::get()
const {
return as_integer<unsigned char>(); }
832 template<>
inline signed short Config::get()
const {
return as_integer<signed short>(); }
833 template<>
inline unsigned short Config::get()
const {
return as_integer<unsigned short>(); }
834 template<>
inline signed int Config::get()
const {
return as_integer<signed int>(); }
835 template<>
inline unsigned int Config::get()
const {
return as_integer<unsigned int>(); }
836 template<>
inline signed long Config::get()
const {
return as_integer<signed long>(); }
837 template<>
inline unsigned long Config::get()
const {
return as_integer<unsigned long>(); }
838 template<>
inline signed long long Config::get()
const {
return as_integer<signed long long>(); }
839 template<>
inline unsigned long long Config::get()
const {
return as_integer<unsigned long long>(); }
840 template<>
inline float Config::get()
const {
return as_float(); }
841 template<>
inline double Config::get()
const {
return as_double(); }
842 template<>
inline const std::string&
Config::get()
const {
return as_string(); }
843 template<>
inline std::string
Config::get()
const {
return as_string(); }
844 template<>
inline const Config::ConfigArrayImpl&
Config::get()
const {
return as_array(); }
852 return config.
get<T>();
858 auto&&
object = as_object()._impl;
859 auto it =
object.find(key);
860 if (it ==
object.end()) {
861 return default_value;
863 const auto& entry = it->second;
864 entry._accessed =
true;
865 return as<T>(entry._value);
870 T
Config::get_or(std::initializer_list<std::string> keys,
const T& default_value)
const 873 for (
const auto& key : keys)
878 return default_value;
892 template<
class Config,
class Visitor>
896 if (config.is_object()) {
900 }
else if (config.is_array()) {
901 for (
auto&& e : config.
as_array()) {
907 inline void clear_doc(
Config& root)
941 ParseError(
const DocInfo_SP& doc, Index line, Index column,
const std::string& msg)
942 : _line(line), _column(column)
944 _what = doc->filename +
":" + std::to_string(line) +
":" + std::to_string(column);
945 doc->append_include_info(_what);
950 const char*
what() const noexcept
override 952 return _what.c_str();
955 Index line()
const noexcept {
return _line; }
956 Index column()
const noexcept {
return _column; }
959 Index _line, _column;
971 std::string indentation =
"\t";
972 bool enforce_indentation =
true;
973 bool end_with_newline =
true;
976 bool empty_file =
false;
977 bool implicit_top_object =
true;
978 bool implicit_top_array =
true;
981 bool single_line_comments =
true;
982 bool block_comments =
true;
983 bool nesting_block_comments =
true;
988 bool hexadecimal_integers =
true;
989 bool binary_integers =
true;
990 bool unary_plus =
true;
991 bool distinct_floats =
true;
994 bool array_omit_comma =
true;
995 bool array_trailing_comma =
true;
998 bool identifiers_keys =
true;
999 bool object_separator_equal =
false;
1000 bool allow_space_before_colon =
false;
1001 bool omit_colon_before_object =
false;
1002 bool object_omit_comma =
true;
1003 bool object_trailing_comma =
true;
1004 bool object_duplicate_keys =
false;
1005 bool object_align_values =
true;
1008 bool str_csharp_verbatim =
true;
1009 bool str_python_multiline =
true;
1010 bool str_32bit_unicode =
true;
1011 bool str_allow_tab =
true;
1014 bool allow_macro =
true;
1017 bool write_comments =
true;
1020 bool sort_keys =
false;
1023 bool write_uninitialized =
false;
1026 bool mark_accessed =
true;
1028 bool compact()
const {
return indentation.empty(); }
1046 options.block_comments =
false;
1050 options.
inf =
false;
1051 options.
nan =
false;
1081 options.write_comments =
false;
1102 options.block_comments =
true;
1136 options.write_comments =
false;
1153 std::map<std::string, Config> parsed_files;
1178 #endif // CONFIGURU_HEADER_HPP 1193 #if defined(CONFIGURU_IMPLEMENTATION) && !defined(CONFIGURU_HAS_BEEN_IMPLEMENTED) 1194 #define CONFIGURU_HAS_BEEN_IMPLEMENTED 1196 #include <algorithm> 1203 void DocInfo::append_include_info(std::string& ret,
const std::string& indent)
const 1205 if (!includers.empty()) {
1206 ret +=
", included at:\n";
1207 for (
auto&& includer : includers) {
1208 ret += indent + includer.doc->filename +
":" + std::to_string(includer.line);
1209 includer.doc->append_include_info(ret, indent +
" ");
1216 struct BadLookupInfo
1218 const DocInfo_SP doc;
1219 const unsigned line;
1220 const std::string key;
1222 #if !CONFIGURU_VALUE_SEMANTICS 1223 std::atomic<unsigned> _ref_count { 1 };
1226 BadLookupInfo(DocInfo_SP doc_, Index line_, std::string key_)
1227 : doc(std::move(doc_)), line(line_), key(std::move(key_)) {}
1232 CONFIGURU_ASSERT(str !=
nullptr);
1233 _u.str =
new std::string(str);
1238 _u.str =
new std::string(move(str));
1241 Config::Config(std::initializer_list<std::pair<std::string, Config>> values) : _type(Uninitialized)
1244 for (
auto&& v : values) {
1245 (*this)[v.first] = std::move(v.second);
1251 assert_type(Uninitialized);
1253 _u.object =
new ConfigObject();
1258 assert_type(Uninitialized);
1260 _u.array =
new ConfigArray();
1274 for (
auto&& p : values) {
1275 ret[
static_cast<std::string
>(p.first)] = std::move(p.second);
1291 ret._u.array->_impl.reserve(values.size());
1292 for (
auto&& v : values) {
1298 void Config::tag(
const DocInfo_SP& doc, Index line, Index column)
1319 if (&o ==
this) {
return; }
1320 std::swap(_type, o._type);
1321 std::swap(_u, o._u);
1322 std::swap(_doc, o._doc);
1323 std::swap(_line, o._line);
1324 std::swap(_comments, o._comments);
1329 if (&o ==
this) {
return *
this; }
1331 std::swap(_type, o._type);
1332 std::swap(_u, o._u);
1335 if (o._doc || o._line != BAD_INDEX) {
1336 std::swap(_doc, o._doc);
1337 std::swap(_line, o._line);
1341 std::swap(_comments, o._comments);
1349 if (&o ==
this) {
return *
this; }
1355 #if CONFIGURU_VALUE_SEMANTICS 1356 if (_type == String) {
1357 _u.str =
new std::string(*o._u.str);
1358 }
else if (_type == BadLookupType) {
1359 _u.bad_lookup =
new BadLookupInfo(*o._u.bad_lookup);
1360 }
else if (_type == Object) {
1361 _u.object =
new ConfigObject(*o._u.object);
1362 }
else if (_type == Array) {
1363 _u.array =
new ConfigArray(*o._u.array);
1365 memcpy(&_u, &o._u,
sizeof(_u));
1367 #else // !CONFIGURU_VALUE_SEMANTICS: 1368 if (_type == String) {
1369 _u.str =
new std::string(*o._u.str);
1371 memcpy(&_u, &o._u,
sizeof(_u));
1372 if (_type == BadLookupType) { ++_u.bad_lookup->_ref_count; }
1373 if (_type == Array) { ++_u.array->_ref_count; }
1374 if (_type == Object) { ++_u.object->_ref_count; }
1376 #endif // !CONFIGURU_VALUE_SEMANTICS 1379 if (o._doc || o._line != BAD_INDEX) {
1388 #if CONFIGURU_VALUE_SEMANTICS 1402 #if CONFIGURU_VALUE_SEMANTICS 1403 if (_type == BadLookupType) {
1404 delete _u.bad_lookup;
1405 }
else if (_type == Object) {
1407 }
else if (_type == Array) {
1409 }
else if (_type == String) {
1412 #else // !CONFIGURU_VALUE_SEMANTICS: 1413 if (_type == BadLookupType) {
1414 if (--_u.bad_lookup->_ref_count == 0) {
1415 delete _u.bad_lookup;
1417 }
else if (_type == Object) {
1418 if (--_u.object->_ref_count == 0) {
1421 }
else if (_type == Array) {
1422 if (--_u.array->_ref_count == 0) {
1425 }
else if (_type == String) {
1428 #endif // !CONFIGURU_VALUE_SEMANTICS 1430 _type = Uninitialized;
1439 return as_object()._impl.size();
1444 auto&&
object = as_object()._impl;
1445 auto it =
object.find(key);
1446 if (it ==
object.end()) {
1447 on_error(
"Key '" + key +
"' not in object");
1449 const auto& entry = it->second;
1450 entry._accessed =
true;
1451 return entry._value;
1457 auto&&
object = as_object()._impl;
1458 auto&& entry =
object[key];
1459 if (entry._nr == BAD_INDEX) {
1461 entry._nr =
static_cast<Index
>(
object.size()) - 1;
1462 entry._value._type = BadLookupType;
1463 entry._value._u.bad_lookup =
new BadLookupInfo{_doc, _line, key};
1465 entry._accessed =
true;
1467 return entry._value;
1472 return as_object()._impl.count(key) != 0;
1477 auto&&
object = as_object()._impl;
1478 return object.emplace(
1485 auto&&
object = as_object()._impl;
1486 auto&& entry =
object[key];
1487 if (entry._nr == BAD_INDEX) {
1489 entry._nr =
static_cast<Index
>(
object.size()) - 1;
1491 entry._accessed =
true;
1493 entry._value = std::move(config);
1498 auto&
object = as_object()._impl;
1499 auto it =
object.find(key);
1500 if (it ==
object.end()) {
1510 if (a._type != b._type) {
return false; }
1511 if (a._type == Null) {
return true; }
1512 if (a._type == Bool) {
return a._u.b == b._u.b; }
1513 if (a._type == Int) {
return a._u.i == b._u.i; }
1514 if (a._type == Float) {
return a._u.f == b._u.f; }
1515 if (a._type == String) {
return *a._u.str == *b._u.str; }
1516 if (a._type == Object) {
1517 if (a._u.object == b._u.object) {
return true; }
1520 if (a_object.size() != b_object.size()) {
return false; }
1521 for (
auto&& p: a_object) {
1522 auto it = b_object.find(p.first);
1523 if (it == b_object.end()) {
return false; }
1524 if (!deep_eq(p.second._value, it->second._value)) {
return false; }
1528 if (a._type == Array) {
1529 if (a._u.array == b._u.array) {
return true; }
1532 if (a_array.size() != b_array.size()) {
return false; }
1533 for (
size_t i=0; i<a_array.size(); ++i) {
1534 if (!deep_eq(a_array[i], a_array[i])) {
1544 #if !CONFIGURU_VALUE_SEMANTICS 1548 if (ret._type == Object) {
1550 for (
auto&& p : this->as_object()._impl) {
1551 auto& dst = ret._u.object->_impl[p.first];
1552 dst._nr = p.second._nr;
1553 dst._value = p.second._value.deep_clone();
1556 if (ret._type == Array) {
1558 for (
auto&& value : this->as_array()) {
1569 for (
auto&& p : as_object()._impl) {
1570 auto&& entry = p.second;
1571 auto&& value = entry._value;
1572 if (entry._accessed) {
1575 visitor(p.first, value);
1578 }
else if (is_array()) {
1579 for (
auto&& e : as_array()) {
1587 std::string message =
"";
1589 visit_dangling([&](
const std::string& key,
const Config& value){
1590 message +=
"\n " + value.
where() +
"Key '" + key +
"' never accessed.";
1593 if (!message.empty()) {
1594 message =
"Dangling keys:" + message;
1595 CONFIGURU_ON_DANGLING(message);
1602 for (
auto&& p : as_object()._impl) {
1603 auto&& entry = p.second;
1604 entry._accessed = v;
1605 entry._value.mark_accessed(v);
1607 }
else if (is_array()) {
1608 for (
auto&& e : as_array()) {
1617 case Bool:
return _u.b ?
"true" :
"false";
1618 case String:
return _u.str->c_str();
1619 default:
return type_str(_type);
1626 case Uninitialized:
return "uninitialized";
1627 case BadLookupType:
return "undefined";
1628 case Null:
return "null";
1629 case Bool:
return "bool";
1630 case Int:
return "integer";
1631 case Float:
return "float";
1632 case String:
return "string";
1633 case Array:
return "array";
1634 case Object:
return "object";
1636 return "BROKEN Config";
1639 std::string where_is(
const DocInfo_SP& doc, Index line)
1642 std::string ret = doc->filename;
1643 if (line != BAD_INDEX) {
1644 ret +=
":" + std::to_string(line);
1646 doc->append_include_info(ret);
1649 }
else if (line != BAD_INDEX) {
1650 return "line " + std::to_string(line) +
": ";
1658 return where_is(_doc, _line);
1661 void Config::on_error(
const std::string& msg)
const 1663 CONFIGURU_ONERROR(where() + msg);
1667 void Config::assert_type(Type exepected)
const 1669 if (_type == BadLookupType) {
1670 auto where = where_is(_u.bad_lookup->doc, _u.bad_lookup->line);
1671 CONFIGURU_ONERROR(where +
"Failed to find key '" + _u.bad_lookup->key +
"'");
1672 }
else if (_type != exepected) {
1673 const auto message = where() +
"Expected " + type_str(exepected) +
", got " + type_str(_type);
1674 if (_type == Uninitialized && exepected == Object) {
1675 CONFIGURU_ONERROR(message +
". Did you forget to call Config::object()?");
1676 }
else if (_type == Uninitialized && exepected == Array) {
1677 CONFIGURU_ONERROR(message +
". Did you forget to call Config::array()?");
1679 CONFIGURU_ONERROR(message);
1690 format.write_uninitialized =
true;
1691 format.end_with_newline =
false;
1692 format.mark_accessed =
false;
1708 void append(Comments& a, Comments&& b)
1710 for (
auto&& entry : b) {
1711 a.emplace_back(std::move(entry));
1715 bool ConfigComments::empty()
const 1717 return prefix.empty()
1719 && pre_end_brace.empty();
1724 configuru::append(this->prefix, std::move(other.prefix));
1725 configuru::append(this->postfix, std::move(other.postfix));
1726 configuru::append(this->pre_end_brace, std::move(other.pre_end_brace));
1730 size_t encode_utf8(std::string& dst, uint64_t c)
1734 dst +=
static_cast<char>(c);
1737 else if (c <= 0x7FF)
1739 dst +=
static_cast<char>( 0xC0 | (c >> 6) );
1740 dst +=
static_cast<char>( 0x80 | (c & 0x3F) );
1743 else if (c <= 0xFFFF)
1745 dst +=
static_cast<char>(0xE0 | (c >> 12));
1746 dst +=
static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1747 dst +=
static_cast<char>(0x80 | (c & 0x3F));
1750 else if (c <= 0x1FFFFF)
1752 dst +=
static_cast<char>(0xF0 | (c >> 18));
1753 dst +=
static_cast<char>(0x80 | ((c >> 12) & 0x3F));
1754 dst +=
static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1755 dst +=
static_cast<char>(0x80 | (c & 0x3F));
1758 else if (c <= 0x3FFFFFF)
1760 dst +=
static_cast<char>(0xF8 | (c >> 24));
1761 dst +=
static_cast<char>(0x80 | (c >> 18));
1762 dst +=
static_cast<char>(0x80 | ((c >> 12) & 0x3F));
1763 dst +=
static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1764 dst +=
static_cast<char>(0x80 | (c & 0x3F));
1767 else if (c <= 0x7FFFFFFF)
1769 dst +=
static_cast<char>(0xFC | (c >> 30));
1770 dst +=
static_cast<char>(0x80 | ((c >> 24) & 0x3F));
1771 dst +=
static_cast<char>(0x80 | ((c >> 18) & 0x3F));
1772 dst +=
static_cast<char>(0x80 | ((c >> 12) & 0x3F));
1773 dst +=
static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1774 dst +=
static_cast<char>(0x80 | (c & 0x3F));
1782 std::string quote(
char c)
1784 if (c == 0) {
return "<eof>"; }
1785 if (c ==
' ') {
return "<space>"; }
1786 if (c ==
'\n') {
return "'\\n'"; }
1787 if (c ==
'\t') {
return "'\\t'"; }
1788 if (c ==
'\r') {
return "'\\r'"; }
1789 if (c ==
'\b') {
return "'\\b'"; }
1790 return std::string(
"'") + c +
"'";
1797 const char* line_start;
1804 bool skip_white(Comments* out_comments,
int& out_indentation,
bool break_on_newline);
1806 bool skip_white_ignore_comments()
1809 return skip_white(
nullptr, indentation,
false);
1812 bool skip_pre_white(
Config* config,
int& out_indentation)
1814 if (!MAYBE_WHITE[static_cast<uint8_t>(_ptr[0])]) {
1816 out_indentation = -1;
1821 bool did_skip = skip_white(&comments, out_indentation,
false);
1822 if (!comments.empty()) {
1828 bool skip_post_white(
Config* config)
1830 if (!MAYBE_WHITE[static_cast<uint8_t>(_ptr[0])]) {
1837 bool did_skip = skip_white(&comments, indentation,
true);
1838 if (!comments.empty()) {
1845 void parse_value(
Config& out,
bool* out_did_skip_postwhites);
1846 void parse_array(
Config& dst);
1847 void parse_array_contents(
Config& dst);
1848 void parse_object(
Config& dst);
1849 void parse_object_contents(
Config& dst);
1850 void parse_int(
Config& out);
1851 void parse_float(
Config& out);
1852 void parse_finite_number(
Config& dst);
1854 std::string parse_c_sharp_string();
1855 uint64_t parse_hex(
int count);
1856 void parse_macro(
Config& dst);
1860 var.
tag(_doc, _line_nr, column());
1863 State get_state()
const 1865 return { _ptr, _line_nr, _line_start };
1868 void set_state(State s) {
1870 _line_nr = s.line_nr;
1871 _line_start = s.line_start;
1874 Index column()
const 1876 return static_cast<unsigned>(_ptr - _line_start + 1);
1879 const char* start_of_line()
const 1884 const char* end_of_line()
const 1886 const char* p = _ptr;
1887 while (*p && *p !=
'\r' && *p !=
'\n') {
1893 void throw_error(
const std::string& desc) CONFIGURU_NORETURN {
1894 const char* sol = start_of_line();
1895 const char* eol = end_of_line();
1896 std::string orientation;
1897 for (
const char* p = sol; p != eol; ++p) {
1901 orientation.push_back(*p);
1905 orientation +=
"\n";
1906 for (
const char* p = sol; p != _ptr; ++p) {
1910 orientation.push_back(
' ');
1915 throw ParseError(_doc, _line_nr, column(), desc +
"\n" + orientation);
1918 void throw_indentation_error(
int found_tabs,
int expected_tabs) {
1919 if (_options.enforce_indentation) {
1921 snprintf(buff,
sizeof(buff),
"Bad indentation: expected %d tabs, found %d", found_tabs, expected_tabs);
1926 void parse_assert(
bool b,
const char* error_msg) {
1928 throw_error(error_msg);
1932 void parse_assert(
bool b,
const char* error_msg,
const State& error_state) {
1934 set_state(error_state);
1935 throw_error(error_msg);
1939 void swallow(
char c) {
1943 throw_error(
"Expected " + quote(c));
1947 bool try_swallow(
const char* str) {
1948 auto n = strlen(str);
1949 if (strncmp(str, _ptr, n) == 0) {
1957 void swallow(
const char* str,
const char* error_msg) {
1958 parse_assert(try_swallow(str), error_msg);
1961 bool is_reserved_identifier(
const char* ptr)
1963 if (strncmp(ptr,
"true", 4)==0 || strncmp(ptr,
"null", 4)==0) {
1964 return !IDENT_CHARS[
static_cast<uint8_t
>(ptr[4])];
1965 }
else if (strncmp(ptr,
"false", 5)==0) {
1966 return !IDENT_CHARS[
static_cast<uint8_t
>(ptr[5])];
1973 bool IDENT_STARTERS[256] = { 0 };
1974 bool IDENT_CHARS[256] = { 0 };
1975 bool MAYBE_WHITE[256] = { 0 };
1976 bool SPECIAL_CHARACTERS[256] = { 0 };
1985 const char* _line_start;
1986 int _indentation = 0;
1992 void set_range(
bool lookup[256],
char a,
char b)
1994 for (
char c=a; c<=b; ++c) {
1995 lookup[
static_cast<uint8_t
>(c)] =
true;
1999 Parser::Parser(
const char* str,
const FormatOptions& options, DocInfo_SP doc,
ParseInfo& info) : _doc(doc), _info(info)
2006 IDENT_STARTERS[
static_cast<uint8_t
>(
'_')] =
true;
2007 set_range(IDENT_STARTERS,
'a',
'z');
2008 set_range(IDENT_STARTERS,
'A',
'Z');
2010 IDENT_CHARS[
static_cast<uint8_t
>(
'_')] =
true;
2011 set_range(IDENT_CHARS,
'a',
'z');
2012 set_range(IDENT_CHARS,
'A',
'Z');
2013 set_range(IDENT_CHARS,
'0',
'9');
2015 MAYBE_WHITE[
static_cast<uint8_t
>(
'\n')] =
true;
2016 MAYBE_WHITE[
static_cast<uint8_t
>(
'\r')] =
true;
2017 MAYBE_WHITE[
static_cast<uint8_t
>(
'\t')] =
true;
2018 MAYBE_WHITE[
static_cast<uint8_t
>(
' ')] =
true;
2019 MAYBE_WHITE[
static_cast<uint8_t
>(
'/')] =
true;
2021 SPECIAL_CHARACTERS[
static_cast<uint8_t
>(
'\0')] =
true;
2022 SPECIAL_CHARACTERS[
static_cast<uint8_t
>(
'\\')] =
true;
2023 SPECIAL_CHARACTERS[
static_cast<uint8_t
>(
'\"')] =
true;
2024 SPECIAL_CHARACTERS[
static_cast<uint8_t
>(
'\n')] =
true;
2025 SPECIAL_CHARACTERS[
static_cast<uint8_t
>(
'\t')] =
true;
2027 CONFIGURU_ASSERT(_options.indentation !=
"" || !_options.enforce_indentation);
2033 bool Parser::skip_white(Comments* out_comments,
int& out_indentation,
bool break_on_newline)
2035 auto start_ptr = _ptr;
2036 out_indentation = 0;
2037 bool found_newline =
false;
2039 const std::string& indentation = _options.indentation;
2041 while (MAYBE_WHITE[static_cast<uint8_t>(_ptr[0])]) {
2042 if (_ptr[0] ==
'\n') {
2047 out_indentation = 0;
2048 if (break_on_newline) {
return true; }
2049 found_newline =
true;
2051 else if (_ptr[0] ==
'\r') {
2053 parse_assert(_ptr[1] ==
'\n',
"CR with no LF. \\r only allowed before \\n.");
2057 out_indentation = 0;
2058 if (break_on_newline) {
return true; }
2059 found_newline =
true;
2061 else if (!indentation.empty() &&
2062 strncmp(_ptr, indentation.c_str(), indentation.size()) == 0)
2064 _ptr += indentation.size();
2065 if (_options.enforce_indentation && indentation ==
"\t") {
2066 parse_assert(out_indentation != -1,
"Tabs should only occur on the start of a line!");
2070 else if (_ptr[0] ==
'\t') {
2072 if (_options.enforce_indentation) {
2073 parse_assert(out_indentation != -1,
"Tabs should only occur on the start of a line!");
2077 else if (_ptr[0] ==
' ') {
2078 if (found_newline && _options.enforce_indentation) {
2079 if (indentation ==
"\t") {
2080 throw_error(
"Found a space at beginning of a line. Indentation must be done using tabs!");
2082 throw_error(
"Indentation should be a multiple of " + std::to_string(indentation.size()) +
" spaces.");
2086 out_indentation = -1;
2088 else if (_ptr[0] ==
'/' && _ptr[1] ==
'/') {
2089 parse_assert(_options.single_line_comments,
"Single line comments forbidden.");
2093 while (_ptr[0] && _ptr[0] !=
'\n') {
2096 if (out_comments) { out_comments->emplace_back(start, _ptr - start); }
2097 out_indentation = 0;
2098 if (break_on_newline) {
return true; }
2100 else if (_ptr[0] ==
'/' && _ptr[1] ==
'*') {
2101 parse_assert(_options.block_comments,
"Block comments forbidden.");
2103 auto state = get_state();
2105 unsigned nesting = 1;
2110 throw_error(
"Non-ending /* comment");
2112 else if (_ptr[0]==
'/' && _ptr[1]==
'*') {
2114 parse_assert(_options.nesting_block_comments,
"Nesting comments (/* /* */ */) forbidden.");
2117 else if (_ptr[0]==
'*' && _ptr[1]==
'/') {
2121 else if (_ptr[0] ==
'\n') {
2128 }
while (nesting > 0);
2129 if (out_comments) { out_comments->emplace_back(state.ptr, _ptr - state.ptr); }
2130 out_indentation = -1;
2131 if (break_on_newline) {
return true; }
2138 if (start_ptr == _ptr) {
2139 out_indentation = -1;
2151 Config Parser::top_level()
2153 bool is_object =
false;
2155 if (_options.implicit_top_object)
2157 auto state = get_state();
2158 skip_white_ignore_comments();
2160 if (IDENT_STARTERS[static_cast<uint8_t>(_ptr[0])] && !is_reserved_identifier(_ptr)) {
2162 }
else if (_ptr[0] ==
'"' || _ptr[0] ==
'@') {
2164 skip_white_ignore_comments();
2165 is_object = (_ptr[0] ==
':' || _ptr[0] ==
'=');
2175 parse_object_contents(ret);
2177 parse_array_contents(ret);
2178 parse_assert(ret.
array_size() <= 1 || _options.implicit_top_array,
"Multiple values not allowed without enclosing []");
2181 skip_post_white(&ret);
2183 parse_assert(_ptr[0] == 0,
"Expected EoF");
2186 if (_options.empty_file) {
2189 empty_object.comments() = std::move(ret.
comments());
2191 return empty_object;
2193 throw_error(
"Empty file");
2199 Config first( std::move(ret[0]) );
2209 void Parser::parse_value(
Config& dst,
bool* out_did_skip_postwhites)
2211 int line_indentation;
2212 skip_pre_white(&dst, line_indentation);
2215 if (line_indentation >= 0 && _indentation - 1 != line_indentation) {
2216 throw_indentation_error(_indentation - 1, line_indentation);
2219 if (_ptr[0] ==
'"' || _ptr[0] ==
'@') {
2222 else if (_ptr[0] ==
'n') {
2223 parse_assert(_ptr[1]==
'u' && _ptr[2]==
'l' && _ptr[3]==
'l',
"Expected 'null'");
2224 parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])],
"Expected 'null'");
2228 else if (_ptr[0] ==
't') {
2229 parse_assert(_ptr[1]==
'r' && _ptr[2]==
'u' && _ptr[3]==
'e',
"Expected 'true'");
2230 parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])],
"Expected 'true'");
2234 else if (_ptr[0] ==
'f') {
2235 parse_assert(_ptr[1]==
'a' && _ptr[2]==
'l' && _ptr[3]==
's' && _ptr[4]==
'e',
"Expected 'false'");
2236 parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[5])],
"Expected 'false'");
2240 else if (_ptr[0] ==
'{') {
2243 else if (_ptr[0] ==
'[') {
2246 else if (_ptr[0] ==
'#') {
2249 else if (_ptr[0] ==
'+' || _ptr[0] ==
'-' || _ptr[0] ==
'.' || (
'0' <= _ptr[0] && _ptr[0] <=
'9')) {
2252 if (_ptr[0] ==
'-' && _ptr[1] ==
'i' && _ptr[2]==
'n' && _ptr[3]==
'f') {
2253 parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])],
"Expected -inf");
2254 parse_assert(_options.inf,
"infinity forbidden.");
2256 dst = -std::numeric_limits<double>::infinity();
2258 else if (_ptr[0] ==
'+' && _ptr[1] ==
'i' && _ptr[2]==
'n' && _ptr[3]==
'f') {
2259 parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])],
"Expected +inf");
2260 parse_assert(_options.inf,
"infinity forbidden.");
2262 dst = std::numeric_limits<double>::infinity();
2264 else if (_ptr[0] ==
'+' && _ptr[1] ==
'N' && _ptr[2]==
'a' && _ptr[3]==
'N') {
2265 parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])],
"Expected +NaN");
2266 parse_assert(_options.nan,
"NaN (Not a Number) forbidden.");
2268 dst = std::numeric_limits<double>::quiet_NaN();
2270 parse_finite_number(dst);
2273 throw_error(
"Expected value");
2276 *out_did_skip_postwhites = skip_post_white(&dst);
2279 void Parser::parse_array(
Config& array)
2281 auto state = get_state();
2286 parse_array_contents(array);
2289 if (_ptr[0] ==
']') {
2293 throw_error(
"Non-terminated array");
2297 void Parser::parse_array_contents(
Config& array_cfg)
2300 auto& array_impl = array_cfg.
as_array();
2302 Comments next_prefix_comments;
2307 if (!next_prefix_comments.empty()) {
2310 int line_indentation;
2311 skip_pre_white(&value, line_indentation);
2313 if (_ptr[0] ==
']') {
2314 if (line_indentation >= 0 && _indentation - 1 != line_indentation) {
2315 throw_indentation_error(_indentation - 1, line_indentation);
2330 if (line_indentation >= 0 && _indentation != line_indentation) {
2331 throw_indentation_error(_indentation, line_indentation);
2334 if (IDENT_STARTERS[static_cast<uint8_t>(_ptr[0])] && !is_reserved_identifier(_ptr)) {
2335 throw_error(
"Found identifier; expected value. Did you mean to use a {object} rather than a [array]?");
2339 parse_value(value, &has_separator);
2341 skip_white(&next_prefix_comments, ignore,
false);
2343 auto comma_state = get_state();
2344 bool has_comma = _ptr[0] ==
',';
2348 skip_post_white(&value);
2349 has_separator =
true;
2352 array_impl.emplace_back(std::move(value));
2354 bool is_last_element = !_ptr[0] || _ptr[0] ==
']';
2356 if (is_last_element) {
2357 parse_assert(!has_comma || _options.array_trailing_comma,
2358 "Trailing comma forbidden.", comma_state);
2360 if (_options.array_omit_comma) {
2361 parse_assert(has_separator,
"Expected a space, newline, comma or ]");
2363 parse_assert(has_comma,
"Expected a comma or ]");
2369 void Parser::parse_object(
Config&
object)
2371 auto state = get_state();
2376 parse_object_contents(
object);
2379 if (_ptr[0] ==
'}') {
2383 throw_error(
"Non-terminated object");
2387 void Parser::parse_object_contents(
Config&
object)
2389 object.make_object();
2391 Comments next_prefix_comments;
2396 if (!next_prefix_comments.empty()) {
2399 int line_indentation;
2400 skip_pre_white(&value, line_indentation);
2402 if (_ptr[0] ==
'}') {
2403 if (line_indentation >= 0 && _indentation - 1 != line_indentation) {
2404 throw_indentation_error(_indentation - 1, line_indentation);
2419 if (line_indentation >= 0 && _indentation != line_indentation) {
2420 throw_indentation_error(_indentation, line_indentation);
2423 auto pre_key_state = get_state();
2426 if (IDENT_STARTERS[static_cast<uint8_t>(_ptr[0])] && !is_reserved_identifier(_ptr)) {
2427 parse_assert(_options.identifiers_keys,
"You need to surround keys with quotes");
2428 while (IDENT_CHARS[static_cast<uint8_t>(_ptr[0])]) {
2433 else if (_ptr[0] ==
'"' || _ptr[0] ==
'@') {
2436 throw_error(
"Object key expected (either an identifier or a quoted string), got " + quote(_ptr[0]));
2439 if (!_options.object_duplicate_keys &&
object.has_key(key)) {
2440 set_state(pre_key_state);
2441 throw_error(
"Duplicate key: \"" + key +
"\". Already set at " +
object[key].where());
2444 bool space_after_key = skip_white_ignore_comments();
2446 if (_ptr[0] ==
':' || (_options.object_separator_equal && _ptr[0] ==
'=')) {
2447 parse_assert(_options.allow_space_before_colon || _ptr[0] !=
':' || !space_after_key,
"No space allowed before colon");
2449 skip_white_ignore_comments();
2450 }
else if (_options.omit_colon_before_object && (_ptr[0] ==
'{' || _ptr[0] ==
'#')) {
2453 if (_options.object_separator_equal && _options.omit_colon_before_object) {
2454 throw_error(
"Expected one of '=', ':', '{' or '#' after object key");
2456 throw_error(
"Expected : after object key");
2461 parse_value(value, &has_separator);
2463 skip_white(&next_prefix_comments, ignore,
false);
2465 auto comma_state = get_state();
2466 bool has_comma = _ptr[0] ==
',';
2470 skip_post_white(&value);
2471 has_separator =
true;
2474 object.emplace(std::move(key), std::move(value));
2476 bool is_last_element = !_ptr[0] || _ptr[0] ==
'}';
2478 if (is_last_element) {
2479 parse_assert(!has_comma || _options.object_trailing_comma,
2480 "Trailing comma forbidden.", comma_state);
2482 if (_options.object_omit_comma) {
2483 parse_assert(has_separator,
"Expected a space, newline, comma or }");
2485 parse_assert(has_comma,
"Expected a comma or }");
2491 void Parser::parse_int(
Config& out)
2493 const auto start = _ptr;
2494 const auto result = strtoll(start, const_cast<char**>(&_ptr), 10);
2495 parse_assert(start < _ptr,
"Invalid integer");
2496 parse_assert(start[0] !=
'0' || result == 0,
"Integer may not start with a zero");
2500 void Parser::parse_float(
Config& out)
2502 const auto start = _ptr;
2503 const double result = strtod(start, const_cast<char**>(&_ptr));
2504 parse_assert(start < _ptr,
"Invalid number");
2508 void Parser::parse_finite_number(
Config& out)
2510 const auto pre_sign = _ptr;
2513 if (_ptr[0] ==
'+') {
2514 parse_assert(_options.unary_plus,
"Prefixing numbers with + is forbidden.");
2517 if (_ptr[0] ==
'-') {
2522 parse_assert(_ptr[0] !=
'+' && _ptr[0] !=
'-',
"Duplicate sign");
2525 if (_ptr[0] ==
'0' && _ptr[1] ==
'x') {
2526 parse_assert(_options.hexadecimal_integers,
"Hexadecimal numbers forbidden.");
2529 out = sign *
static_cast<int64_t
>(strtoull(start, const_cast<char**>(&_ptr), 16));
2530 parse_assert(start < _ptr,
"Missing hexaxdecimal digits after 0x");
2534 if (_ptr[0] ==
'0' && _ptr[1] ==
'b') {
2535 parse_assert(_options.binary_integers,
"Binary numbers forbidden.");
2538 out = sign *
static_cast<int64_t
>(strtoull(start, const_cast<char**>(&_ptr), 2));
2539 parse_assert(start < _ptr,
"Missing binary digits after 0b");
2543 const char* p = _ptr;
2545 while (
'0' <= *p && *p <=
'9') {
2549 if (*p ==
'.' || *p ==
'e' || *p ==
'E') {
2551 return parse_float(out);
2555 const auto MAX_INT_STR = (sign == +1 ?
"9223372036854775807" :
"9223372036854775808");
2557 const auto length = p - _ptr;
2561 return parse_int(out);
2566 return parse_float(out);
2570 for (
int i = 0; i < 19; ++i)
2572 if (_ptr[i] > MAX_INT_STR[i]) {
2574 return parse_float(out);
2576 if (_ptr[i] < MAX_INT_STR[i]) {
2578 return parse_int(out);
2582 return parse_int(out);
2585 std::string Parser::parse_c_sharp_string()
2588 auto state = get_state();
2589 parse_assert(_options.str_csharp_verbatim,
"C# @-style verbatim strings forbidden.");
2598 throw_error(
"Unterminated verbatim string");
2600 else if (_ptr[0] ==
'\n') {
2601 throw_error(
"Newline in verbatim string");
2603 else if (_ptr[0] ==
'"' && _ptr[1] ==
'"') {
2608 else if (_ptr[0] ==
'"') {
2619 std::string Parser::parse_string()
2621 if (_ptr[0] ==
'@') {
2622 return parse_c_sharp_string();
2625 auto state = get_state();
2626 parse_assert(_ptr[0] ==
'"',
"Quote (\") expected");
2628 if (_ptr[1] ==
'"' && _ptr[2] ==
'"') {
2630 parse_assert(_options.str_python_multiline,
"Python \"\"\"-style multiline strings forbidden.");
2632 const char* start = _ptr;
2634 if (_ptr[0]==0 || _ptr[1]==0 || _ptr[2]==0) {
2636 throw_error(
"Unterminated multiline string");
2639 if (_ptr[0] ==
'"' && _ptr[1] ==
'"' && _ptr[2] ==
'"' && _ptr[3] !=
'"') {
2640 std::string str(start, _ptr);
2645 if (_ptr[0] ==
'\n') {
2661 auto safe_end = _ptr;
2662 while (!SPECIAL_CHARACTERS[static_cast<uint8_t>(*safe_end)]) {
2666 if (_ptr != safe_end) {
2667 str.append(_ptr, safe_end - _ptr);
2673 throw_error(
"Unterminated string");
2675 if (_ptr[0] ==
'"') {
2679 if (_ptr[0] ==
'\n') {
2680 throw_error(
"Newline in string");
2682 if (_ptr[0] ==
'\t') {
2683 parse_assert(_options.str_allow_tab,
"Un-escaped tab not allowed in string");
2686 if (_ptr[0] ==
'\\') {
2690 if (_ptr[0] ==
'"') {
2693 }
else if (_ptr[0] ==
'\\') {
2694 str.push_back(
'\\');
2696 }
else if (_ptr[0] ==
'/') {
2699 }
else if (_ptr[0] ==
'b') {
2700 str.push_back(
'\b');
2702 }
else if (_ptr[0] ==
'f') {
2703 str.push_back(
'\f');
2705 }
else if (_ptr[0] ==
'n') {
2706 str.push_back(
'\n');
2708 }
else if (_ptr[0] ==
'r') {
2709 str.push_back(
'\r');
2711 }
else if (_ptr[0] ==
't') {
2712 str.push_back(
'\t');
2714 }
else if (_ptr[0] ==
'u') {
2717 uint64_t codepoint = parse_hex(4);
2719 if (0xD800 <= codepoint and codepoint <= 0xDBFF)
2722 parse_assert(_ptr[0] ==
'\\' && _ptr[1] ==
'u',
2723 "Missing second unicode surrogate.");
2725 uint64_t codepoint2 = parse_hex(4);
2726 parse_assert(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF,
"Invalid second unicode surrogate");
2727 codepoint = (codepoint << 10) + codepoint2 - 0x35FDC00;
2730 auto num_bytes_written = encode_utf8(str, codepoint);
2731 parse_assert(num_bytes_written > 0,
"Bad unicode codepoint");
2732 }
else if (_ptr[0] ==
'U') {
2734 parse_assert(_options.str_32bit_unicode,
"\\U 32 bit unicodes forbidden.");
2736 uint64_t unicode = parse_hex(8);
2737 auto num_bytes_written = encode_utf8(str, unicode);
2738 parse_assert(num_bytes_written > 0,
"Bad unicode codepoint");
2740 throw_error(
"Unknown escape character " + quote(_ptr[0]));
2743 str.push_back(_ptr[0]);
2750 uint64_t Parser::parse_hex(
int count)
2753 for (
int i=0; i<count; ++i) {
2756 if (
'0' <= c && c <=
'9') {
2757 ret +=
static_cast<uint64_t
>(c -
'0');
2758 }
else if (
'a' <= c && c <=
'f') {
2759 ret +=
static_cast<uint64_t
>(10 + c -
'a');
2760 }
else if (
'A' <= c && c <=
'F') {
2761 ret +=
static_cast<uint64_t
>(10 + c -
'A');
2763 throw_error(
"Expected hexadecimal digit, got " + quote(_ptr[0]));
2770 void Parser::parse_macro(
Config& dst)
2772 parse_assert(_options.allow_macro,
"#macros forbidden.");
2774 swallow(
"#include",
"Expected '#include'");
2775 skip_white_ignore_comments();
2780 if (_ptr[0] ==
'"') {
2783 }
else if (_ptr[0] ==
'<') {
2787 throw_error(
"Expected \" or <");
2790 auto state = get_state();
2797 throw_error(
"Unterminated include path");
2798 }
else if (_ptr[0] == terminator) {
2799 path = std::string(start, static_cast<size_t>(_ptr - start));
2802 }
else if (_ptr[0] ==
'\n') {
2803 throw_error(
"Newline in string");
2810 auto my_path = _doc->filename;
2811 auto pos = my_path.find_last_of(
'/');
2812 if (pos != std::string::npos) {
2813 auto my_dir = my_path.substr(0, pos+1);
2814 path = my_dir + path;
2818 auto it = _info.parsed_files.find(path);
2819 if (it == _info.parsed_files.end()) {
2820 auto child_doc = std::make_shared<DocInfo>(path);
2821 child_doc->includers.emplace_back(_doc, _line_nr);
2822 dst = parse_file(path.c_str(), _options, child_doc, _info);
2823 _info.parsed_files[path] = dst;
2825 auto child_doc = it->second.
doc();
2826 child_doc->includers.emplace_back(_doc, _line_nr);
2835 Parser p(str, options, doc, info);
2836 return p.top_level();
2842 return parse_string(str, options, std::make_shared<DocInfo>(name), info);
2845 std::string read_text_file(
const char* path)
2847 FILE* fp = fopen(path,
"rb");
2848 if (fp ==
nullptr) {
2849 CONFIGURU_ONERROR(std::string(
"Failed to open '") + path +
"' for reading: " + strerror(errno));
2851 std::string contents;
2852 fseek(fp, 0, SEEK_END);
2853 auto size = ftell(fp);
2855 CONFIGURU_ONERROR(std::string(
"Failed to find out size of '") + path +
"': " + strerror(errno));
2857 contents.resize(static_cast<size_t>(size));
2859 auto num_read = fread(&contents[0], 1, contents.size(), fp);
2861 if (num_read != contents.size()) {
2862 CONFIGURU_ONERROR(std::string(
"Failed to read from '") + path +
"': " + strerror(errno));
2870 auto file = read_text_file(path.c_str());
2877 return parse_file(path, options, std::make_shared<DocInfo>(path), info);
2891 bool is_identifier(
const char* p)
2894 || (
'a' <= *p && *p <=
'z')
2895 || (
'A' <= *p && *p <=
'Z'))
2900 || (
'a' <= *p && *p <=
'z')
2901 || (
'A' <= *p && *p <=
'Z')
2902 || (
'0' <= *p && *p <=
'9'))
2915 bool has_pre_end_brace_comments(
const Config& cfg)
2925 bool SAFE_CHARACTERS[256];
2929 : _options(options), _doc(std::move(doc))
2931 _compact = _options.compact();
2933 for (
int i = 0; i < 256; ++i) {
2934 SAFE_CHARACTERS[i] = i >= 0x20;
2937 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\\')] =
false;
2938 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\"')] =
false;
2939 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\0')] =
false;
2940 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\b')] =
false;
2941 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\f')] =
false;
2942 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\n')] =
false;
2943 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\r')] =
false;
2944 SAFE_CHARACTERS[
static_cast<uint8_t
>(
'\t')] =
false;
2947 inline void write_indent(
unsigned indent)
2949 if (_compact) {
return; }
2950 for (
unsigned i=0; i<indent; ++i) {
2955 void write_prefix_comments(
unsigned indent,
const Comments& comments)
2957 if (!_options.write_comments) {
return; }
2958 if (!comments.empty()) {
2959 _out.push_back(
'\n');
2960 for (
auto&& c : comments) {
2961 write_indent(indent);
2963 _out.push_back(
'\n');
2968 void write_prefix_comments(
unsigned indent,
const Config& cfg)
2970 if (!_options.write_comments) {
return; }
2976 void write_postfix_comments(
unsigned indent,
const Comments& comments)
2978 if (!_options.write_comments) {
return; }
2980 for (
auto&& c : comments) {
2981 _out.push_back(
' ');;
2986 void write_pre_brace_comments(
unsigned indent,
const Comments& comments)
2988 write_prefix_comments(indent, comments);
2991 void write_value(
unsigned indent,
const Config& config,
2992 bool write_prefix,
bool write_postfix)
2996 _out +=
"#include <";
2997 _out += config.
doc()->filename;
2998 _out.push_back(
'>');
3003 write_prefix_comments(indent, config);
3006 if (config.is_null()) {
3008 }
else if (config.is_bool()) {
3009 _out += (config.
as_bool() ?
"true" :
"false");
3010 }
else if (config.is_int()) {
3012 snprintf(temp_buff,
sizeof(temp_buff),
"%lld", static_cast<long long>(config));
3014 }
else if (config.is_float()) {
3015 write_number( config.as_double() );
3016 }
else if (config.is_string()) {
3017 write_string(config.as_string());
3018 }
else if (config.is_array()) {
3019 if (config.
array_size() == 0 && !has_pre_end_brace_comments(config)) {
3025 }
else if (_compact || is_simple_array(config)) {
3026 _out.push_back(
'[');
3028 _out.push_back(
' ');
3031 for (
size_t i = 0; i < array.size(); ++i) {
3032 write_value(indent + 1, array[i],
false,
true);
3034 if (i + 1 < array.size()) {
3035 _out.push_back(
',');
3038 _out.push_back(
' ');
3043 write_pre_brace_comments(indent + 1, config.
comments().pre_end_brace);
3048 for (
size_t i = 0; i < array.size(); ++i) {
3049 write_prefix_comments(indent + 1, array[i]);
3050 write_indent(indent + 1);
3051 write_value(indent + 1, array[i],
false,
true);
3053 _out.push_back(
'\n');
3058 write_pre_brace_comments(indent + 1, config.
comments().pre_end_brace);
3059 write_indent(indent);
3062 }
else if (config.is_object()) {
3063 if (config.
object_size() == 0 && !has_pre_end_brace_comments(config)) {
3071 _out.push_back(
'{');
3075 write_object_contents(indent + 1, config);
3076 write_indent(indent);
3077 _out.push_back(
'}');
3081 _out +=
"UNINITIALIZED";
3083 CONFIGURU_ONERROR(
"Failed to serialize uninitialized Config");
3087 if (write_postfix) {
3092 void write_object_contents(
unsigned indent,
const Config& config)
3095 auto&&
object = config.
as_object()._impl;
3097 using ObjIterator = Config::ConfigObjectImpl::const_iterator;
3098 std::vector<ObjIterator> pairs;
3099 pairs.reserve(
object.size());
3101 size_t longest_key = 0;
3104 for (
auto it=
object.begin(); it!=
object.end(); ++it) {
3105 pairs.push_back(it);
3107 longest_key = std::max(longest_key, it->first.size());
3112 std::sort(begin(pairs), end(pairs), [](
const ObjIterator& a,
const ObjIterator& b) {
3113 return a->first < b->first;
3116 std::sort(begin(pairs), end(pairs), [](
const ObjIterator& a,
const ObjIterator& b) {
3117 return a->second._nr < b->second._nr;
3122 for (
auto&& it : pairs) {
3123 auto&& value = it->second._value;
3124 write_prefix_comments(indent, value);
3125 write_indent(indent);
3126 write_key(it->first);
3128 _out.push_back(
':');
3130 _out.push_back(
' ');
3134 for (
size_t j=it->first.size(); j<longest_key; ++j) {
3135 _out.push_back(
' ');
3139 write_value(indent, value,
false,
true);
3141 if (i + 1 < pairs.size()) {
3142 _out.push_back(
',');
3145 _out.push_back(
'\n');
3152 write_pre_brace_comments(indent, config.
comments().pre_end_brace);
3155 void write_key(
const std::string& str)
3164 void write_number(
double val)
3171 const auto as_int =
static_cast<long long>(val);
3172 if (static_cast<double>(as_int) == val) {
3174 snprintf(temp_buff,
sizeof(temp_buff),
"%lld", as_int);
3182 if (std::isfinite(val)) {
3185 const auto as_float =
static_cast<float>(val);
3186 if (static_cast<double>(as_float) == val) {
3188 snprintf(temp_buff,
sizeof(temp_buff),
"%g", as_float);
3189 if (std::strtof(temp_buff,
nullptr) == as_float) {
3193 snprintf(temp_buff,
sizeof(temp_buff),
"%.8g", as_float);
3200 snprintf(temp_buff,
sizeof(temp_buff),
"%.1g", val);
3201 if (std::strtod(temp_buff,
nullptr) == val) {
3207 snprintf(temp_buff,
sizeof(temp_buff),
"%g", val);
3208 if (std::strtod(temp_buff,
nullptr) == val) {
3214 snprintf(temp_buff,
sizeof(temp_buff),
"%.16g", val);
3215 if (std::strtod(temp_buff,
nullptr) == val) {
3221 snprintf(temp_buff,
sizeof(temp_buff),
"%.17g", val);
3223 }
else if (val == +std::numeric_limits<double>::infinity()) {
3224 if (!_options.
inf) {
3225 CONFIGURU_ONERROR(
"Can't encode infinity");
3228 }
else if (val == -std::numeric_limits<double>::infinity()) {
3229 if (!_options.
inf) {
3230 CONFIGURU_ONERROR(
"Can't encode negative infinity");
3234 if (!_options.
nan) {
3235 CONFIGURU_ONERROR(
"Can't encode NaN");
3241 void write_string(
const std::string& str)
3243 const size_t LONG_LINE = 240;
3246 str.find(
'\n') == std::string::npos ||
3247 str.length() < LONG_LINE ||
3248 str.find(
"\"\"\"") != std::string::npos)
3250 write_quoted_string(str);
3252 write_verbatim_string(str);
3256 void write_hex_digit(
unsigned num)
3258 CONFIGURU_ASSERT(num < 16u);
3259 if (num < 10u) { _out.push_back(
char(
'0' + num)); }
3260 else { _out.push_back(
char(
'a' + num - 10)); }
3263 void write_hex_16(uint16_t n)
3265 write_hex_digit((n >> 12) & 0x0f);
3266 write_hex_digit((n >> 8) & 0x0f);
3267 write_hex_digit((n >> 4) & 0x0f);
3268 write_hex_digit((n >> 0) & 0x0f);
3271 void write_unicode_16(uint16_t c)
3277 void write_quoted_string(
const std::string& str)
3279 _out.push_back(
'"');
3281 const char* ptr = str.c_str();
3282 const char* end = ptr + str.size();
3286 while (SAFE_CHARACTERS[static_cast<uint8_t>(*ptr)]) {
3290 _out.append(start, ptr - start);
3292 if (ptr == end) {
break; }
3296 if (c ==
'\\') { _out +=
"\\\\"; }
3297 else if (c ==
'\"') { _out +=
"\\\""; }
3299 else if (c ==
'\0') { _out +=
"\\0"; }
3300 else if (c ==
'\b') { _out +=
"\\b"; }
3301 else if (c ==
'\f') { _out +=
"\\f"; }
3302 else if (c ==
'\n') { _out +=
"\\n"; }
3303 else if (c ==
'\r') { _out +=
"\\r"; }
3304 else if (c ==
'\t') { _out +=
"\\t"; }
3305 else { write_unicode_16(static_cast<uint16_t>(c)); }
3308 _out.push_back(
'"');
3311 void write_verbatim_string(
const std::string& str)
3318 bool is_simple(
const Config& var)
3320 if (var.is_array() && var.
array_size() > 0) {
return false; }
3321 if (var.is_object() && var.
object_size() > 0) {
return false; }
3322 if (_options.write_comments && var.
has_comments()) {
return false; }
3326 bool is_all_numbers(
const Config& array)
3329 if (!v.is_number()) {
3336 bool is_simple_array(
const Config& array)
3338 if (array.
array_size() <= 16 && is_all_numbers(array)) {
3342 if (array.
array_size() > 4) {
return false; }
3343 size_t estimated_width = 0;
3345 if (!is_simple(v)) {
3348 if (v.is_string()) {
3349 estimated_width += 2 + v.as_string().size();
3351 estimated_width += 5;
3353 estimated_width += 2;
3355 return estimated_width < 60;
3361 Writer w(options, config.
doc());
3364 w.write_object_contents(0, config);
3366 w.write_value(0, config,
true,
true);
3370 w._out.push_back(
'\n');
3378 return std::move(w._out);
3381 static void write_text_file(
const char* path,
const std::string& data)
3383 auto fp = fopen(path,
"wb");
3384 if (fp ==
nullptr) {
3385 CONFIGURU_ONERROR(std::string(
"Failed to open '") + path +
"' for writing: " + strerror(errno));
3387 auto num_bytes_written = fwrite(data.data(), 1, data.size(), fp);
3389 if (num_bytes_written != data.size()) {
3390 CONFIGURU_ONERROR(std::string(
"Failed to write to '") + path +
"': " + strerror(errno));
3397 write_text_file(path.c_str(), str);
3403 #endif // CONFIGURU_IMPLEMENTATION Accessing a Config of this type is always an error.
Definition: configuru.hpp:199
void tag(const DocInfo_SP &doc, Index line, Index column)
Used by the parser - no need to use directly.
Config()
Creates an uninitialized Config.
Definition: configuru.hpp:221
void dump_file(const std::string &path, const Config &config, const FormatOptions &options)
Helper: value in an object.
Definition: configuru.hpp:141
size_t array_size() const
Length of an array.
Definition: configuru.hpp:529
const ConfigObject & as_object() const
Definition: configuru.hpp:586
Config(const std::map< std::string, T > &values)
Object constructor.
Definition: configuru.hpp:286
void swap(Config &o) noexcept
Swaps file/line too.
Config parse_string(const char *str, const FormatOptions &options, const char *name)
void mark_accessed(bool v) const
Set the 'access' flag recursively,.
Type
Definition: configuru.hpp:197
static bool deep_eq(const Config &a, const Config &b)
Compare Config values recursively.
const ConfigArrayImpl & as_array() const
Only use this for iterating over an array: for (Config& e : cfg.as_array()) { ... } ...
Definition: configuru.hpp:542
bool erase(const std::string &key)
Erase a key from an object.
Definition: configuru.hpp:776
ConfigComments & comments()
Read/write of comments.
Definition: configuru.hpp:676
void visit_dangling(const std::function< void(const std::string &key, const Config &value)> &visitor) const
Visit dangling (unaccessed) object keys recursively.
We are the result of a key-lookup in a Object with no hit. We are in effect write-only.
Definition: configuru.hpp:200
std::ostream & operator<<(std::ostream &os, const Config &cfg)
Prints in JSON but in a fail-safe mannor, allowing uninitalized keys and inf/nan. ...
Index line() const
BAD_INDEX if not set.
Definition: configuru.hpp:366
Definition: configuru.hpp:745
bool emplace(std::string key, Config value)
Returns true iff the value was inserted, false if they key was already there.
static Config array()
Preferred way to create an empty array.
ConfigObject & as_object()
Definition: configuru.hpp:578
Definition: configuru.hpp:208
Config(const std::vector< bool > &values)
Array constructor.
Definition: configuru.hpp:275
const DocInfo_SP & doc() const
Handle to document.
Definition: configuru.hpp:369
size_t count(const std::string &key) const
Like has_key, but STL compatible.
Definition: configuru.hpp:608
std::string dump_string(const Config &config, const FormatOptions &options)
T get() const
Extract the value of this Config.
static Config object()
Preferred way to create an empty object.
void check_dangling() const
Will check for dangling (unaccessed) object keys recursively and call CONFIGURU_ON_DANGLING on all fo...
void push_back(Config value)
Append a value to this array.
Definition: configuru.hpp:565
std::string where() const
Returns file:line iff available.
static Config array(const Container &container)
Preferred way to create an array from an STL container.
Definition: configuru.hpp:317
FormatOptions make_json_options()
Returns FormatOptions that are describe a JSON file format.
Definition: configuru.hpp:1032
const char * what() const noexceptoverride
Will name the file name, line number, column and description.
Definition: configuru.hpp:950
bool as_bool() const
The Config must be a boolean.
Definition: configuru.hpp:475
ConfigArrayImpl & as_array()
Only use this for iterating over an array: for (Config& e : cfg.as_array()) { ... } ...
Definition: configuru.hpp:535
Definition: configuru.hpp:738
Config & operator[](const char(&key)[N])
For indexing with string literals:
Definition: configuru.hpp:600
T as(const configuru::Config &config)
Definition: configuru.hpp:850
bool has_key(const std::string &key) const
Check if an object has a specific key.
Thrown on a syntax error.
Definition: configuru.hpp:938
Definition: configuru.hpp:1151
const char * debug_descr() const
Returns either "true", "false", the constained string, or the type name.
Config & operator[](size_t ix)
Array indexing.
Definition: configuru.hpp:549
The Configuru namespace.
Definition: configuru.hpp:108
static const char * type_str(Type t)
Human-readable version of the type ("integer", "bool", etc).
Definition: configuru.hpp:116
Definition: configuru.hpp:194
FormatOptions make_forgiving_options()
Returns format options that allow us parsing most files.
Definition: configuru.hpp:1088
const ConfigComments & comments() const
Read comments.
Definition: configuru.hpp:685
Config(const std::vector< T > &values)
Array constructor.
Definition: configuru.hpp:265
std::string get_or(const std::string &key, const char *default_value) const
Look for the given key in this object, and return default_value on failure.
Definition: configuru.hpp:631
Helper for describing a document.
Definition: configuru.hpp:126
void visit_configs(Config &&config, Visitor &&visitor)
Recursively visit all values in a config.
Definition: configuru.hpp:893
bool has_comments() const
Was there any comments about this value in the input?
Definition: configuru.hpp:670
const Config & operator[](size_t ix) const
Array indexing.
Definition: configuru.hpp:557
std::string get_or(std::initializer_list< std::string > keys, const char *default_value) const
obj.get_or({"a", "b". "c"}, 42) - like obj["a"]["b"]["c"], but returns 42 if any of the keys are miss...
Definition: configuru.hpp:641
void make_object()
Used by the parser - no need to use directly.
Config deep_clone() const
Copy this Config value recursively.
void insert_or_assign(const std::string &key, Config &&value)
Like foo[key] = value, but faster.
void make_array()
Used by the parser - no need to use directly.
size_t object_size() const
Number of elementsi n this object.
T get_or(const T &default_value) const
Returns the value or default_value if this is the result of a bad lookup.
Definition: configuru.hpp:516