diff --git a/docs/examples/templates.md b/docs/examples/templates.md index 398f061..ab9f593 100644 --- a/docs/examples/templates.md +++ b/docs/examples/templates.md @@ -20,11 +20,11 @@ description = "Target templates" type = "executable" sources = ["src/templates.cpp"] compile-definitions = ["IS_APP"] - # Unlike interface targets you can also inherit properties -[template.app.properties] -CXX_STANDARD = "11" -CXX_STANDARD_REQUIRED = true +properties = { + CXX_STANDARD = "11", + CXX_STANDARD_REQUIRED = true, +} [target.app-a] type = "app" diff --git a/tests/templates/cmake.toml b/tests/templates/cmake.toml index 31bb2c6..625d27a 100644 --- a/tests/templates/cmake.toml +++ b/tests/templates/cmake.toml @@ -8,11 +8,11 @@ description = "Target templates" type = "executable" sources = ["src/templates.cpp"] compile-definitions = ["IS_APP"] - # Unlike interface targets you can also inherit properties -[template.app.properties] -CXX_STANDARD = "11" -CXX_STANDARD_REQUIRED = true +properties = { + CXX_STANDARD = "11", + CXX_STANDARD_REQUIRED = true, +} [target.app-a] type = "app" diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 3afac4c..c2a275e 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -9,9 +9,10 @@ add_library(ordered_map INTERFACE) target_include_directories(ordered_map INTERFACE ordered-map-1.0.0/include) target_compile_definitions(ordered_map INTERFACE NOMINMAX) -# https://github.com/ToruNiina/toml11 (MIT) -add_library(toml11 INTERFACE) -target_include_directories(toml11 INTERFACE toml11-3.6.0) +# https://github.com/ToruNiina/toml11 (MIT) +add_library(toml11 INTERFACE) +target_include_directories(toml11 INTERFACE toml11-3.8.1) +target_compile_definitions(toml11 INTERFACE TOML11_USE_UNRELEASED_TOML_FEATURES) # https://github.com/mpark/variant (BSL-1.0) add_library(mpark_variant INTERFACE) diff --git a/third_party/toml11-3.6.0/LICENSE b/third_party/toml11-3.8.1/LICENSE similarity index 100% rename from third_party/toml11-3.6.0/LICENSE rename to third_party/toml11-3.8.1/LICENSE diff --git a/third_party/toml11-3.6.0/toml.hpp b/third_party/toml11-3.8.1/toml.hpp similarity index 85% rename from third_party/toml11-3.6.0/toml.hpp rename to third_party/toml11-3.8.1/toml.hpp index ce578b6..e975f4e 100644 --- a/third_party/toml11-3.6.0/toml.hpp +++ b/third_party/toml11-3.8.1/toml.hpp @@ -25,21 +25,14 @@ #ifndef TOML_FOR_MODERN_CPP #define TOML_FOR_MODERN_CPP -#ifndef __cplusplus -# error "__cplusplus is not defined" -#endif - -#if __cplusplus < 201103L && _MSC_VER < 1900 -# error "toml11 requires C++11 or later." -#endif - #define TOML11_VERSION_MAJOR 3 -#define TOML11_VERSION_MINOR 5 -#define TOML11_VERSION_PATCH 0 +#define TOML11_VERSION_MINOR 8 +#define TOML11_VERSION_PATCH 1 #include "toml/parser.hpp" #include "toml/literal.hpp" #include "toml/serializer.hpp" #include "toml/get.hpp" +#include "toml/macros.hpp" #endif// TOML_FOR_MODERN_CPP diff --git a/third_party/toml11-3.6.0/toml/color.hpp b/third_party/toml11-3.8.1/toml/color.hpp similarity index 75% rename from third_party/toml11-3.6.0/toml/color.hpp rename to third_party/toml11-3.8.1/toml/color.hpp index 4cb572c..a3fd890 100644 --- a/third_party/toml11-3.6.0/toml/color.hpp +++ b/third_party/toml11-3.8.1/toml/color.hpp @@ -17,11 +17,41 @@ namespace color_ansi { namespace detail { + inline int colorize_index() { static const int index = std::ios_base::xalloc(); return index; } + +// Control color mode globally +class color_mode +{ + public: + inline void enable() + { + should_color_ = true; + } + inline void disable() + { + should_color_ = false; + } + + inline bool should_color() const + { + return should_color_; + } + + static color_mode& status() + { + static color_mode status_; + return status_; + } + + private: + bool should_color_ = false; +}; + } // detail inline std::ostream& colorize(std::ostream& os) @@ -55,6 +85,21 @@ inline std::ostream& cyan (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} inline std::ostream& white (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} + +inline void enable() +{ + return detail::color_mode::status().enable(); +} +inline void disable() +{ + return detail::color_mode::status().disable(); +} + +inline bool should_color() +{ + return detail::color_mode::status().should_color(); +} + } // color_ansi // ANSI escape sequence is the only and default colorization method currently diff --git a/third_party/toml11-3.6.0/toml/combinator.hpp b/third_party/toml11-3.8.1/toml/combinator.hpp similarity index 98% rename from third_party/toml11-3.6.0/toml/combinator.hpp rename to third_party/toml11-3.8.1/toml/combinator.hpp index e250188..33ecca1 100644 --- a/third_party/toml11-3.6.0/toml/combinator.hpp +++ b/third_party/toml11-3.8.1/toml/combinator.hpp @@ -29,7 +29,7 @@ namespace detail // to output character as an error message. inline std::string show_char(const char c) { - // It supress an error that occurs only in Debug mode of MSVC++ on Windows. + // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. // I'm not completely sure but they check the value of char to be in the // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes // has negative value (if char has sign). So here it re-interprets c as @@ -154,7 +154,7 @@ struct sequence invoke(location& loc) { const auto first = loc.iter(); - const auto rslt = Head::invoke(loc); + auto rslt = Head::invoke(loc); if(rslt.is_err()) { loc.reset(first); diff --git a/third_party/toml11-3.6.0/toml/comments.hpp b/third_party/toml11-3.8.1/toml/comments.hpp similarity index 95% rename from third_party/toml11-3.6.0/toml/comments.hpp rename to third_party/toml11-3.8.1/toml/comments.hpp index 1e17544..c0f66f3 100644 --- a/third_party/toml11-3.6.0/toml/comments.hpp +++ b/third_party/toml11-3.8.1/toml/comments.hpp @@ -4,11 +4,18 @@ #define TOML11_COMMENTS_HPP #include #include +#include #include #include #include #include +#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT +# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments +#else +# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments +#endif + // This file provides mainly two classes, `preserve_comments` and `discard_comments`. // Those two are a container that have the same interface as `std::vector` // but bahaves in the opposite way. `preserve_comments` is just the same as @@ -339,7 +346,7 @@ operator+(const empty_iterator& lhs, typename empty_iterator::differ // // Why this is chose as the default type is because the last version (2.x.y) // does not contain any comments in a value. To minimize the impact on the -// efficiency, this is choosed as a default. +// efficiency, this is chosen as a default. // // To reduce the memory footprint, later we can try empty base optimization (EBO). struct discard_comments @@ -418,14 +425,14 @@ struct discard_comments // empty, so accessing through operator[], front/back, data causes address // error. - reference operator[](const size_type) noexcept {return *data();} - const_reference operator[](const size_type) const noexcept {return *data();} + reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} + const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} - reference front() noexcept {return *data();} - const_reference front() const noexcept {return *data();} - reference back() noexcept {return *data();} - const_reference back() const noexcept {return *data();} + reference front() noexcept {never_call("toml::discard_comment::front");} + const_reference front() const noexcept {never_call("toml::discard_comment::front");} + reference back() noexcept {never_call("toml::discard_comment::back");} + const_reference back() const noexcept {never_call("toml::discard_comment::back");} pointer data() noexcept {return nullptr;} const_pointer data() const noexcept {return nullptr;} @@ -443,6 +450,18 @@ struct discard_comments const_reverse_iterator rend() const noexcept {return const_iterator{};} const_reverse_iterator crbegin() const noexcept {return const_iterator{};} const_reverse_iterator crend() const noexcept {return const_iterator{};} + + private: + + [[noreturn]] static void never_call(const char *const this_function) + { +#ifdef __has_builtin +# if __has_builtin(__builtin_unreachable) + __builtin_unreachable(); +# endif +#endif + throw std::logic_error{this_function}; + } }; inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} diff --git a/third_party/toml11-3.6.0/toml/datetime.hpp b/third_party/toml11-3.8.1/toml/datetime.hpp similarity index 96% rename from third_party/toml11-3.6.0/toml/datetime.hpp rename to third_party/toml11-3.8.1/toml/datetime.hpp index d8127c1..83d04ba 100644 --- a/third_party/toml11-3.6.0/toml/datetime.hpp +++ b/third_party/toml11-3.8.1/toml/datetime.hpp @@ -21,34 +21,34 @@ namespace toml namespace detail { // TODO: find more sophisticated way to handle this -#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) +#if defined(_MSC_VER) inline std::tm localtime_s(const std::time_t* src) { std::tm dst; - const auto result = ::localtime_r(src, &dst); - if (!result) { throw std::runtime_error("localtime_r failed."); } + const auto result = ::localtime_s(&dst, src); + if (result) { throw std::runtime_error("localtime_s failed."); } return dst; } inline std::tm gmtime_s(const std::time_t* src) { std::tm dst; - const auto result = ::gmtime_r(src, &dst); - if (!result) { throw std::runtime_error("gmtime_r failed."); } + const auto result = ::gmtime_s(&dst, src); + if (result) { throw std::runtime_error("gmtime_s failed."); } return dst; } -#elif defined(_MSC_VER) +#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) inline std::tm localtime_s(const std::time_t* src) { std::tm dst; - const auto result = ::localtime_s(&dst, src); - if (result) { throw std::runtime_error("localtime_s failed."); } + const auto result = ::localtime_r(src, &dst); + if (!result) { throw std::runtime_error("localtime_r failed."); } return dst; } inline std::tm gmtime_s(const std::time_t* src) { std::tm dst; - const auto result = ::gmtime_s(&dst, src); - if (result) { throw std::runtime_error("gmtime_s failed."); } + const auto result = ::gmtime_r(src, &dst); + if (!result) { throw std::runtime_error("gmtime_r failed."); } return dst; } #else // fallback. not threadsafe @@ -85,9 +85,9 @@ enum class month_t : std::uint8_t struct local_date { - std::int16_t year; // A.D. (like, 2018) - std::uint8_t month; // [0, 11] - std::uint8_t day; // [1, 31] + std::int16_t year{}; // A.D. (like, 2018) + std::uint8_t month{}; // [0, 11] + std::uint8_t day{}; // [1, 31] local_date(int y, month_t m, int d) : year (static_cast(y)), @@ -181,12 +181,12 @@ operator<<(std::basic_ostream& os, const local_date& date) struct local_time { - std::uint8_t hour; // [0, 23] - std::uint8_t minute; // [0, 59] - std::uint8_t second; // [0, 60] - std::uint16_t millisecond; // [0, 999] - std::uint16_t microsecond; // [0, 999] - std::uint16_t nanosecond; // [0, 999] + std::uint8_t hour{}; // [0, 23] + std::uint8_t minute{}; // [0, 59] + std::uint8_t second{}; // [0, 60] + std::uint16_t millisecond{}; // [0, 999] + std::uint16_t microsecond{}; // [0, 999] + std::uint16_t nanosecond{}; // [0, 999] local_time(int h, int m, int s, int ms = 0, int us = 0, int ns = 0) @@ -297,8 +297,8 @@ operator<<(std::basic_ostream& os, const local_time& time) struct time_offset { - std::int8_t hour; // [-12, 12] - std::int8_t minute; // [-59, 59] + std::int8_t hour{}; // [-12, 12] + std::int8_t minute{}; // [-59, 59] time_offset(int h, int m) : hour (static_cast(h)), @@ -364,8 +364,8 @@ operator<<(std::basic_ostream& os, const time_offset& offset) struct local_datetime { - local_date date; - local_time time; + local_date date{}; + local_time time{}; local_datetime(local_date d, local_time t): date(d), time(t) {} @@ -478,9 +478,9 @@ operator<<(std::basic_ostream& os, const local_datetime& dt) struct offset_datetime { - local_date date; - local_time time; - time_offset offset; + local_date date{}; + local_time time{}; + time_offset offset{}; offset_datetime(local_date d, local_time t, time_offset o) : date(d), time(t), offset(o) diff --git a/third_party/toml11-3.6.0/toml/exception.hpp b/third_party/toml11-3.8.1/toml/exception.hpp similarity index 81% rename from third_party/toml11-3.6.0/toml/exception.hpp rename to third_party/toml11-3.8.1/toml/exception.hpp index c64651d..06bfe6e 100644 --- a/third_party/toml11-3.6.0/toml/exception.hpp +++ b/third_party/toml11-3.8.1/toml/exception.hpp @@ -2,14 +2,32 @@ // Distributed under the MIT License. #ifndef TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP -#include + +#include #include +#include + +#include #include "source_location.hpp" namespace toml { +struct file_io_error : public std::runtime_error +{ + public: + file_io_error(int errnum, const std::string& msg, const std::string& fname) + : std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)), + errno_(errnum) + {} + + int get_errno() const noexcept {return errno_;} + + private: + int errno_; +}; + struct exception : public std::exception { public: diff --git a/third_party/toml11-3.6.0/toml/from.hpp b/third_party/toml11-3.8.1/toml/from.hpp similarity index 93% rename from third_party/toml11-3.6.0/toml/from.hpp rename to third_party/toml11-3.8.1/toml/from.hpp index 8251973..10815ca 100644 --- a/third_party/toml11-3.6.0/toml/from.hpp +++ b/third_party/toml11-3.8.1/toml/from.hpp @@ -2,7 +2,6 @@ // Distributed under the MIT License. #ifndef TOML11_FROM_HPP #define TOML11_FROM_HPP -#include "traits.hpp" namespace toml { diff --git a/third_party/toml11-3.6.0/toml/get.hpp b/third_party/toml11-3.8.1/toml/get.hpp similarity index 89% rename from third_party/toml11-3.6.0/toml/get.hpp rename to third_party/toml11-3.8.1/toml/get.hpp index 3be82bf..ac1b354 100644 --- a/third_party/toml11-3.6.0/toml/get.hpp +++ b/third_party/toml11-3.8.1/toml/get.hpp @@ -140,7 +140,7 @@ get(basic_value&& v) // ============================================================================ // std::string_view -#if __cplusplus >= 201703L +#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 template class M, template class V> inline detail::enable_if_t::value, std::string_view> @@ -215,6 +215,7 @@ template, // T is a container detail::negation>, // w/o push_back(...) + detail::negation>, // T does not have special conversion detail::negation< // not toml::array detail::is_exact_toml_type>> >::value, T> @@ -255,19 +256,37 @@ get(const basic_value&); // toml::from::from_toml(v) template class M, template class V, - std::size_t S = sizeof(::toml::from)> -T get(const basic_value&); + template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value&); -// T(const toml::value&) and T is not toml::basic_value +template class M, template class V> +detail::enable_if_t::value, T> +get(basic_value&); + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. template class M, template class V> detail::enable_if_t>, - std::is_constructible&> + std::is_constructible&>, + detail::negation>, + detail::negation> >::value, T> get(const basic_value&); +template class M, template class V> +detail::enable_if_t>, + std::is_constructible&>, + detail::negation>, + detail::negation> + >::value, T> +get(basic_value&); + // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. @@ -321,6 +340,7 @@ template, // T is a container detail::negation>, // w/o push_back + detail::negation>, // T does not have special conversion detail::negation< // T is not toml::array detail::is_exact_toml_type>> >::value, T> @@ -338,8 +358,10 @@ get(const basic_value& v) {v.location(), "here"} })); } - std::transform(ar.cbegin(), ar.cend(), container.begin(), - [](const value& x){return ::toml::get(x);}); + for(std::size_t i=0; i(ar[i]); + } return container; } @@ -438,9 +460,16 @@ get(const basic_value& v) return ud; } template class M, template class V, - std::size_t> -T get(const basic_value& v) + template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value& v) +{ + return ::toml::from::from_toml(v); +} +template class M, template class V> +detail::enable_if_t::value, T> +get(basic_value& v) { return ::toml::from::from_toml(v); } @@ -448,14 +477,29 @@ T get(const basic_value& v) template class M, template class V> detail::enable_if_t>, - std::is_constructible&> + detail::negation>, // T is not a toml::value + std::is_constructible&>, // T is constructible from toml::value + detail::negation>, // and T does not have T.from_toml(v); + detail::negation> // and T does not have toml::from{}; >::value, T> get(const basic_value& v) { return T(v); } +template class M, template class V> +detail::enable_if_t>, // T is not a toml::value + std::is_constructible&>, // T is constructible from toml::value + detail::negation>, // and T does not have T.from_toml(v); + detail::negation> // and T does not have toml::from{}; + >::value, T> +get(basic_value& v) +{ + return T(v); +} + // ============================================================================ // find @@ -1031,6 +1075,50 @@ find_or(const basic_value& v, const toml::key& ky, T&& opt) return get_or(tab.at(ky), std::forward(opt)); } +// --------------------------------------------------------------------------- +// recursive find-or with type deduction (find_or(value, keys, opt)) + +template 1), std::nullptr_t> = nullptr> + // here we need to add SFINAE in the template parameter to avoid + // infinite recursion in type deduction on gcc +auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) + -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) +{ + if(!v.is_table()) + { + return detail::last_one(std::forward(keys)...); + } + auto&& tab = std::forward(v).as_table(); + if(tab.count(ky) == 0) + { + return detail::last_one(std::forward(keys)...); + } + return find_or(std::forward(tab).at(ky), std::forward(keys)...); +} + +// --------------------------------------------------------------------------- +// recursive find_or with explicit type specialization, find_or(value, keys...) + +template 1), std::nullptr_t> = nullptr> + // here we need to add SFINAE in the template parameter to avoid + // infinite recursion in type deduction on gcc +auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) + -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) +{ + if(!v.is_table()) + { + return detail::last_one(std::forward(keys)...); + } + auto&& tab = std::forward(v).as_table(); + if(tab.count(ky) == 0) + { + return detail::last_one(std::forward(keys)...); + } + return find_or(std::forward(tab).at(ky), std::forward(keys)...); +} + // ============================================================================ // expect diff --git a/third_party/toml11-3.6.0/toml/into.hpp b/third_party/toml11-3.8.1/toml/into.hpp similarity index 94% rename from third_party/toml11-3.6.0/toml/into.hpp rename to third_party/toml11-3.8.1/toml/into.hpp index 17f2248..7449556 100644 --- a/third_party/toml11-3.6.0/toml/into.hpp +++ b/third_party/toml11-3.8.1/toml/into.hpp @@ -2,7 +2,6 @@ // Distributed under the MIT License. #ifndef TOML11_INTO_HPP #define TOML11_INTO_HPP -#include "traits.hpp" namespace toml { diff --git a/third_party/toml11-3.6.0/toml/lexer.hpp b/third_party/toml11-3.8.1/toml/lexer.hpp similarity index 87% rename from third_party/toml11-3.6.0/toml/lexer.hpp rename to third_party/toml11-3.8.1/toml/lexer.hpp index 2eea996..2a1ff2d 100644 --- a/third_party/toml11-3.6.0/toml/lexer.hpp +++ b/third_party/toml11-3.8.1/toml/lexer.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include "combinator.hpp" @@ -119,8 +118,8 @@ using lex_local_time = lex_partial_time; // =========================================================================== using lex_quotation_mark = character<'"'>; -using lex_basic_unescaped = exclude, // 0x09 (tab) - in_range<0x0a, 0x1F>, // is allowed +using lex_basic_unescaped = exclude, // 0x09 (tab) is allowed + in_range<0x0A, 0x1F>, character<0x22>, character<0x5C>, character<0x7F>>>; @@ -133,6 +132,9 @@ using lex_escape_seq_char = either, character<'\\'>, character<'b'>, character<'f'>, character<'n'>, character<'r'>, character<'t'>, +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + character<'e'>, // ESC (0x1B) +#endif lex_escape_unicode_short, lex_escape_unicode_long >; @@ -166,7 +168,7 @@ using lex_basic_string = sequence, maybe >; -using lex_ml_basic_unescaped = exclude, // 0x09 - in_range<0x0a, 0x1F>, // is tab +using lex_ml_basic_unescaped = exclude, // 0x09 is tab + in_range<0x0A, 0x1F>, character<0x5C>, // backslash character<0x7F>, // DEL lex_ml_basic_string_delim>>; @@ -196,8 +198,8 @@ using lex_ml_basic_string = sequence; -using lex_literal_char = exclude, - in_range<0x10, 0x19>, character<0x27>>>; +using lex_literal_char = exclude, in_range<0x0A, 0x1F>, + character<0x7F>, character<0x27>>>; using lex_apostrophe = character<'\''>; using lex_literal_string = sequence, @@ -212,7 +214,7 @@ using lex_ml_literal_string_close = sequence< >; using lex_ml_literal_char = exclude, - in_range<0x10, 0x1F>, + in_range<0x0A, 0x1F>, character<0x7F>, lex_ml_literal_string_delim>>; using lex_ml_literal_body = repeat, @@ -225,12 +227,6 @@ using lex_string = either; // =========================================================================== - -using lex_comment_start_symbol = character<'#'>; -using lex_non_eol = either, exclude>>; -using lex_comment = sequence>; - using lex_dot_sep = sequence, character<'.'>, maybe>; using lex_unquoted_key = repeat, lex_array_table_close>; +using lex_utf8_1byte = in_range<0x00, 0x7F>; +using lex_utf8_2byte = sequence< + in_range<'\xC2', '\xDF'>, + in_range<'\x80', '\xBF'> + >; +using lex_utf8_3byte = sequence, in_range<'\xA0', '\xBF'>>, + sequence, in_range<'\x80', '\xBF'>>, + sequence, in_range<'\x80', '\x9F'>>, + sequence, in_range<'\x80', '\xBF'>> + >, in_range<'\x80', '\xBF'>>; +using lex_utf8_4byte = sequence, in_range<'\x90', '\xBF'>>, + sequence, in_range<'\x80', '\xBF'>>, + sequence, in_range<'\x80', '\x8F'>> + >, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>; +using lex_utf8_code = either< + lex_utf8_1byte, + lex_utf8_2byte, + lex_utf8_3byte, + lex_utf8_4byte + >; + +using lex_comment_start_symbol = character<'#'>; +using lex_non_eol_ascii = either, in_range<0x20, 0x7E>>; +using lex_comment = sequence, unlimited>>; + } // detail } // toml #endif // TOML_LEXER_HPP diff --git a/third_party/toml11-3.6.0/toml/literal.hpp b/third_party/toml11-3.8.1/toml/literal.hpp similarity index 80% rename from third_party/toml11-3.6.0/toml/literal.hpp rename to third_party/toml11-3.8.1/toml/literal.hpp index 2c3d18c..5086a76 100644 --- a/third_party/toml11-3.6.0/toml/literal.hpp +++ b/third_party/toml11-3.8.1/toml/literal.hpp @@ -12,8 +12,11 @@ inline namespace toml_literals { // implementation -inline ::toml::value literal_internal_impl(::toml::detail::location loc) +inline ::toml::basic_value +literal_internal_impl(::toml::detail::location loc) { + using value_type = ::toml::basic_value< + TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; // if there are some comments or empty lines, skip them. using skip_line = ::toml::detail::repeat, @@ -50,7 +53,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc) // If it is neither a table-key or a array-of-table-key, it may be a value. if(!is_table_key && !is_aots_key) { - if(auto data = ::toml::detail::parse_value<::toml::value>(loc)) + if(auto data = ::toml::detail::parse_value(loc, 0)) { return data.unwrap(); } @@ -67,7 +70,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc) // It is a valid toml file. // It should be parsed as if we parse a file with this content. - if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc)) + if(auto data = ::toml::detail::parse_toml_file(loc)) { return data.unwrap(); } @@ -78,11 +81,13 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc) } -inline ::toml::value operator"" _toml(const char* str, std::size_t len) +inline ::toml::basic_value +operator"" _toml(const char* str, std::size_t len) { ::toml::detail::location loc( std::string("TOML literal encoded in a C++ code"), std::vector(str, str + len)); + // literal length does not include the null character at the end. return literal_internal_impl(std::move(loc)); } @@ -91,7 +96,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len) #if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L // value of u8"" literal has been changed from char to char8_t and char8_t is // NOT compatible to char -inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len) +inline ::toml::basic_value +operator"" _toml(const char8_t* str, std::size_t len) { ::toml::detail::location loc( std::string("TOML literal encoded in a C++ code"), diff --git a/third_party/toml11-3.8.1/toml/macros.hpp b/third_party/toml11-3.8.1/toml/macros.hpp new file mode 100644 index 0000000..e8f91ae --- /dev/null +++ b/third_party/toml11-3.8.1/toml/macros.hpp @@ -0,0 +1,121 @@ +#ifndef TOML11_MACROS_HPP +#define TOML11_MACROS_HPP + +#define TOML11_STRINGIZE_AUX(x) #x +#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) + +#define TOML11_CONCATENATE_AUX(x, y) x##y +#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) + +// ============================================================================ +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE + +#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +// ---------------------------------------------------------------------------- +// TOML11_ARGS_SIZE + +#define TOML11_INDEX_RSEQ() \ + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#define TOML11_ARGS_SIZE_IMPL(\ + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ + ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ + ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ + ARG31, ARG32, N, ...) N +#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) +#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) + +// ---------------------------------------------------------------------------- +// TOML11_FOR_EACH_VA_ARGS + +#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) +#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) + +#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ + TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) + +// ---------------------------------------------------------------------------- +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE + +// use it in the following way. +// ```cpp +// namespace foo +// { +// struct Foo +// { +// std::string s; +// double d; +// int i; +// }; +// } // foo +// +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) +// ``` +// And then you can use `toml::find(file, "foo");` +// +#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ + obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); + +#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ + v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; + +#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ + namespace toml { \ + template<> \ + struct from \ + { \ + template class T, \ + template class A> \ + static NAME from_toml(const basic_value& v) \ + { \ + NAME obj; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ + return obj; \ + } \ + }; \ + template<> \ + struct into \ + { \ + static value into_toml(const NAME& obj) \ + { \ + ::toml::value v = ::toml::table{}; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ + return v; \ + } \ + }; \ + } /* toml */ + +#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +#endif// TOML11_MACROS_HPP diff --git a/third_party/toml11-3.6.0/toml/parser.hpp b/third_party/toml11-3.8.1/toml/parser.hpp similarity index 74% rename from third_party/toml11-3.6.0/toml/parser.hpp rename to third_party/toml11-3.8.1/toml/parser.hpp index a52d8c9..3f73ef9 100644 --- a/third_party/toml11-3.6.0/toml/parser.hpp +++ b/third_party/toml11-3.8.1/toml/parser.hpp @@ -8,17 +8,25 @@ #include "combinator.hpp" #include "lexer.hpp" +#include "macros.hpp" #include "region.hpp" #include "result.hpp" #include "types.hpp" #include "value.hpp" -#if __cplusplus >= 201703L +#ifndef TOML11_DISABLE_STD_FILESYSTEM +#ifdef __cpp_lib_filesystem #if __has_include() #define TOML11_HAS_STD_FILESYSTEM #include #endif // has_include() -#endif // cplusplus >= C++17 +#endif // __cpp_lib_filesystem +#endif // TOML11_DISABLE_STD_FILESYSTEM + +// the previous commit works with 500+ recursions. so it may be too small. +// but in most cases, i think we don't need such a deep recursion of +// arrays or inline-tables. +#define TOML11_VALUE_RECURSION_LIMIT 64 namespace toml { @@ -55,16 +63,55 @@ parse_binary_integer(location& loc) { auto str = token.unwrap().str(); assert(str.size() > 2); // minimum -> 0b1 + assert(str.at(0) == '0' && str.at(1) == 'b'); + + // skip all the zeros and `_` locating at the MSB + str.erase(str.begin(), std::find_if( + str.begin() + 2, // to skip prefix `0b` + str.end(), + [](const char c) { return c == '1'; }) + ); + assert(str.empty() || str.front() == '1'); + + // since toml11 uses int64_t, 64bit (unsigned) input cannot be read. + const auto max_length = 63 + std::count(str.begin(), str.end(), '_'); + if(static_cast(max_length) < str.size()) + { + loc.reset(first); + return err(format_underline("toml::parse_binary_integer: " + "only signed 64bit integer is available", + {{source_location(loc), "too large input (> int64_t)"}})); + } + integer retval(0), base(1); - for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) + for(auto i(str.rbegin()), e(str.rend()); i!=e; ++i) { - if (*i == '1'){retval += base; base *= 2;} - else if(*i == '0'){base *= 2;} - else if(*i == '_'){/* do nothing. */} - else // internal error. + assert(base != 0); // means overflow, checked in the above code + if(*i == '1') + { + retval += base; + if( (std::numeric_limits::max)() / 2 < base ) + { + base = 0; + } + base *= 2; + } + else if(*i == '0') + { + if( (std::numeric_limits::max)() / 2 < base ) + { + base = 0; + } + base *= 2; + } + else if(*i == '_') + { + // do nothing. + } + else // should be detected by lex_bin_int. [[unlikely]] { throw internal_error(format_underline( - "toml::parse_integer: internal error", + "toml::parse_binary_integer: internal error", {{source_location(token.unwrap()), "invalid token"}}), source_location(loc)); } @@ -89,6 +136,21 @@ parse_octal_integer(location& loc) std::istringstream iss(str); integer retval(0); iss >> std::oct >> retval; + if(iss.fail()) + { + // `istream` sets `failbit` if internally-called `std::num_get::get` + // fails. + // `std::num_get::get` calls `std::strtoll` if the argument type is + // signed. + // `std::strtoll` fails if + // - the value is out_of_range or + // - no conversion is possible. + // since we already checked that the string is valid octal integer, + // so the error reason is out_of_range. + loc.reset(first); + return err(format_underline("toml::parse_octal_integer:", + {{source_location(loc), "out of range"}})); + } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); @@ -109,6 +171,13 @@ parse_hexadecimal_integer(location& loc) std::istringstream iss(str); integer retval(0); iss >> std::hex >> retval; + if(iss.fail()) + { + // see parse_octal_integer for detail of this error message. + loc.reset(first); + return err(format_underline("toml::parse_hexadecimal_integer:", + {{source_location(loc), "out of range"}})); + } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); @@ -155,6 +224,13 @@ parse_integer(location& loc) std::istringstream iss(str); integer retval(0); iss >> retval; + if(iss.fail()) + { + // see parse_octal_integer for detail of this error message. + loc.reset(first); + return err(format_underline("toml::parse_integer:", + {{source_location(loc), "out of range"}})); + } return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); @@ -243,6 +319,13 @@ parse_floating(location& loc) std::istringstream iss(str); floating v(0.0); iss >> v; + if(iss.fail()) + { + // see parse_octal_integer for detail of this error message. + loc.reset(first); + return err(format_underline("toml::parse_floating:", + {{source_location(loc), "out of range"}})); + } return ok(std::make_pair(v, token.unwrap())); } loc.reset(first); @@ -325,6 +408,9 @@ inline result parse_escape_sequence(location& loc) case 'n' :{loc.advance(); return ok(std::string("\n"));} case 'f' :{loc.advance(); return ok(std::string("\f"));} case 'r' :{loc.advance(); return ok(std::string("\r"));} +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + case 'e' :{loc.advance(); return ok(std::string("\x1b"));} // ESC +#endif case 'u' : { if(const auto token = lex_escape_unicode_short::invoke(loc)) @@ -362,6 +448,19 @@ inline result parse_escape_sequence(location& loc) return err(msg); } +inline std::ptrdiff_t check_utf8_validity(const std::string& reg) +{ + location loc("tmp", reg); + const auto u8 = repeat::invoke(loc); + if(!u8 || loc.iter() != loc.end()) + { + const auto error_location = std::distance(loc.begin(), loc.iter()); + assert(0 <= error_location); + return error_location; + } + return -1; +} + inline result, std::string> parse_ml_basic_string(location& loc) { @@ -430,7 +529,21 @@ parse_ml_basic_string(location& loc) source_location(inner_loc)); } } - return ok(std::make_pair(toml::string(retval), token.unwrap())); + + const auto err_loc = check_utf8_validity(token.unwrap().str()); + if(err_loc == -1) + { + return ok(std::make_pair(toml::string(retval), token.unwrap())); + } + else + { + inner_loc.reset(first); + inner_loc.advance(err_loc); + throw syntax_error(format_underline( + "parse_ml_basic_string: invalid utf8 sequence found", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } } else { @@ -482,7 +595,21 @@ parse_basic_string(location& loc) } quot = lex_quotation_mark::invoke(inner_loc); } - return ok(std::make_pair(toml::string(retval), token.unwrap())); + + const auto err_loc = check_utf8_validity(token.unwrap().str()); + if(err_loc == -1) + { + return ok(std::make_pair(toml::string(retval), token.unwrap())); + } + else + { + inner_loc.reset(first); + inner_loc.advance(err_loc); + throw syntax_error(format_underline( + "parse_basic_string: invalid utf8 sequence found", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } } else { @@ -499,7 +626,8 @@ parse_ml_literal_string(location& loc) const auto first = loc.iter(); if(const auto token = lex_ml_literal_string::invoke(loc)) { - location inner_loc(loc.name(), token.unwrap().str()); + auto inner_loc = loc; + inner_loc.reset(first); const auto open = lex_ml_literal_string_open::invoke(inner_loc); if(!open) @@ -543,8 +671,22 @@ parse_ml_literal_string(location& loc) source_location(inner_loc)); } } - return ok(std::make_pair(toml::string(retval, toml::string_t::literal), - token.unwrap())); + + const auto err_loc = check_utf8_validity(token.unwrap().str()); + if(err_loc == -1) + { + return ok(std::make_pair(toml::string(retval, toml::string_t::literal), + token.unwrap())); + } + else + { + inner_loc.reset(first); + inner_loc.advance(err_loc); + throw syntax_error(format_underline( + "parse_ml_literal_string: invalid utf8 sequence found", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } } else { @@ -561,7 +703,8 @@ parse_literal_string(location& loc) const auto first = loc.iter(); if(const auto token = lex_literal_string::invoke(loc)) { - location inner_loc(loc.name(), token.unwrap().str()); + auto inner_loc = loc; + inner_loc.reset(first); const auto open = lex_apostrophe::invoke(inner_loc); if(!open) @@ -582,9 +725,23 @@ parse_literal_string(location& loc) {{source_location(inner_loc), "should be '"}}), source_location(inner_loc)); } - return ok(std::make_pair( - toml::string(body.unwrap().str(), toml::string_t::literal), - token.unwrap())); + + const auto err_loc = check_utf8_validity(token.unwrap().str()); + if(err_loc == -1) + { + return ok(std::make_pair( + toml::string(body.unwrap().str(), toml::string_t::literal), + token.unwrap())); + } + else + { + inner_loc.reset(first); + inner_loc.advance(err_loc); + throw syntax_error(format_underline( + "parse_literal_string: invalid utf8 sequence found", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } } else { @@ -638,7 +795,7 @@ parse_local_date(location& loc) if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { throw internal_error(format_underline( - "toml::parse_inner_local_date: invalid year format", + "toml::parse_local_date: invalid year format", {{source_location(inner_loc), "should be `-`"}}), source_location(inner_loc)); } @@ -660,12 +817,36 @@ parse_local_date(location& loc) {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } - return ok(std::make_pair(local_date( - static_cast(from_string(y.unwrap().str(), 0)), - static_cast( - static_cast(from_string(m.unwrap().str(), 0)-1)), - static_cast(from_string(d.unwrap().str(), 0))), - token.unwrap())); + + const auto year = static_cast(from_string(y.unwrap().str(), 0)); + const auto month = static_cast(from_string(m.unwrap().str(), 0)); + const auto day = static_cast(from_string(d.unwrap().str(), 0)); + + // We briefly check whether the input date is valid or not. But here, we + // only check if the RFC3339 compliance. + // Actually there are several special date that does not exist, + // because of historical reasons, such as 1582/10/5-1582/10/14 (only in + // several countries). But here, we do not care about such a complicated + // rule. It makes the code complicated and there is only low probability + // that such a specific date is needed in practice. If someone need to + // validate date accurately, that means that the one need a specialized + // library for their purpose in a different layer. + { + const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + const auto max_day = (month == 2) ? (is_leap ? 29 : 28) : + ((month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31); + + if((month < 1 || 12 < month) || (day < 1 || max_day < day)) + { + throw syntax_error(format_underline("toml::parse_date: " + "invalid date: it does not conform RFC3339.", {{ + source_location(loc), "month should be 01-12, day should be" + " 01-28,29,30,31, depending on month/year." + }}), source_location(inner_loc)); + } + } + return ok(std::make_pair(local_date(year, static_cast(month - 1), day), + token.unwrap())); } else { @@ -709,10 +890,22 @@ parse_local_time(location& loc) {{source_location(inner_loc), "here"}}), source_location(inner_loc)); } - local_time time( - from_string(h.unwrap().str(), 0), - from_string(m.unwrap().str(), 0), - from_string(s.unwrap().str(), 0), 0, 0); + + const int hour = from_string(h.unwrap().str(), 0); + const int minute = from_string(m.unwrap().str(), 0); + const int second = from_string(s.unwrap().str(), 0); + + if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute) || + (second < 0 || 60 < second)) // it may be leap second + { + throw syntax_error(format_underline("toml::parse_local_time: " + "invalid time: it does not conform RFC3339.", {{ + source_location(loc), "hour should be 00-23, minute should be" + " 00-59, second should be 00-60 (depending on the leap" + " second rules.)"}}), source_location(inner_loc)); + } + + local_time time(hour, minute, second, 0, 0); const auto before_secfrac = inner_loc.iter(); if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) @@ -792,7 +985,7 @@ parse_local_datetime(location& loc) { throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "invalid time fomrat"}}), + {{source_location(inner_loc), "invalid time format"}}), source_location(inner_loc)); } return ok(std::make_pair( @@ -826,15 +1019,26 @@ parse_offset_datetime(location& loc) if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) { const auto str = ofs.unwrap().str(); + + const auto hour = from_string(str.substr(1,2), 0); + const auto minute = from_string(str.substr(4,2), 0); + + if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute)) + { + throw syntax_error(format_underline("toml::parse_offset_datetime: " + "invalid offset: it does not conform RFC3339.", {{ + source_location(loc), "month should be 01-12, day should be" + " 01-28,29,30,31, depending on month/year." + }}), source_location(inner_loc)); + } + if(str.front() == '+') { - offset = time_offset(from_string(str.substr(1,2), 0), - from_string(str.substr(4,2), 0)); + offset = time_offset(hour, minute); } else { - offset = time_offset(-from_string(str.substr(1,2), 0), - -from_string(str.substr(4,2), 0)); + offset = time_offset(-hour, -minute); } } else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') @@ -898,7 +1102,7 @@ parse_key(location& loc) else { throw internal_error(format_underline( - "toml::detail::parse_key: dotted key contains invalid key", + "toml::parse_key: dotted key contains invalid key", {{source_location(inner_loc), k.unwrap_err()}}), source_location(inner_loc)); } @@ -930,7 +1134,7 @@ parse_key(location& loc) return ok(std::make_pair(std::vector(1, smpl.unwrap().first), smpl.unwrap().second)); } - return err(format_underline("toml::parse_key: an invalid key appeaed.", + return err(format_underline("toml::parse_key: an invalid key appeared.", {{source_location(loc), "is not a valid key"}}, { "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", "quoted keys: same as \"basic strings\" or 'literal strings'.", @@ -940,15 +1144,23 @@ parse_key(location& loc) // forward-decl to implement parse_array and parse_table template -result parse_value(location&); +result parse_value(location&, const std::size_t n_rec); template result, std::string> -parse_array(location& loc) +parse_array(location& loc, const std::size_t n_rec) { using value_type = Value; using array_type = typename value_type::array_type; + if(n_rec > TOML11_VALUE_RECURSION_LIMIT) + { + // parse_array does not have any way to handle recursive error currently... + throw syntax_error(std::string("toml::parse_array: recursion limit (" + TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), + source_location(loc)); + } + const auto first = loc.iter(); if(loc.iter() == loc.end()) { @@ -975,7 +1187,7 @@ parse_array(location& loc) region(loc, first, loc.iter()))); } - if(auto val = parse_value(loc)) + if(auto val = parse_value(loc, n_rec+1)) { // After TOML v1.0.0-rc.1, array becomes to be able to have values // with different types. So here we will omit this by default. @@ -1049,7 +1261,7 @@ parse_array(location& loc) template result, region>, Value>, std::string> -parse_key_value_pair(location& loc) +parse_key_value_pair(location& loc, const std::size_t n_rec) { using value_type = Value; @@ -1096,7 +1308,7 @@ parse_key_value_pair(location& loc) } const auto after_kvsp = loc.iter(); // err msg - auto val = parse_value(loc); + auto val = parse_value(loc, n_rec); if(!val) { std::string msg; @@ -1139,6 +1351,11 @@ std::string format_dotted_keys(InputIterator first, const InputIterator last) // forward decl for is_valid_forward_table_definition result, region>, std::string> parse_table_key(location& loc); +result, region>, std::string> +parse_array_table_key(location& loc); +template +result, std::string> +parse_inline_table(location& loc, const std::size_t n_rec); // The following toml file is allowed. // ```toml @@ -1164,16 +1381,81 @@ parse_table_key(location& loc); // of the key. If the key region points deeper node, it would be allowed. // Otherwise, the key points the same node. It would be rejected. template -bool is_valid_forward_table_definition(const Value& fwd, +bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, Iterator key_first, Iterator key_curr, Iterator key_last) { + // ------------------------------------------------------------------------ + // check type of the value to be inserted/merged + + std::string inserting_reg = ""; + if(const auto ptr = detail::get_region(inserting)) + { + inserting_reg = ptr->str(); + } + location inserting_def("internal", std::move(inserting_reg)); + if(const auto inlinetable = parse_inline_table(inserting_def, 0)) + { + // check if we are overwriting existing table. + // ```toml + // # NG + // a.b = 42 + // a = {d = 3.14} + // ``` + // Inserting an inline table to a existing super-table is not allowed in + // any case. If we found it, we can reject it without further checking. + return false; + } + + // Valid and invalid cases when inserting to the [a.b] table: + // + // ## Invalid + // + // ```toml + // # invalid + // [a] + // b.c.d = "foo" + // [a.b] # a.b is already defined and closed + // d = "bar" + // ``` + // ```toml + // # invalid + // a = {b.c.d = "foo"} + // [a.b] # a is already defined and inline table is closed + // d = "bar" + // ``` + // ```toml + // # invalid + // a.b.c.d = "foo" + // [a.b] # a.b is already defined and dotted-key table is closed + // d = "bar" + // ``` + // + // ## Valid + // + // ```toml + // # OK. a.b is defined, but is *overwritable* + // [a.b.c] + // d = "foo" + // [a.b] + // d = "bar" + // ``` + // ```toml + // # OK. a.b is defined, but is *overwritable* + // [a] + // b.c.d = "foo" + // b.e = "bar" + // ``` + + // ------------------------------------------------------------------------ + // check table defined before + std::string internal = ""; if(const auto ptr = detail::get_region(fwd)) { internal = ptr->str(); } location def("internal", std::move(internal)); - if(const auto tabkeys = parse_table_key(def)) + if(const auto tabkeys = parse_table_key(def)) // [table.key] { // table keys always contains all the nodes from the root. const auto& tks = tabkeys.unwrap().first; @@ -1186,7 +1468,22 @@ bool is_valid_forward_table_definition(const Value& fwd, // the keys are not equivalent. it is allowed. return true; } - if(const auto dotkeys = parse_key(def)) + // nested array-of-table definition implicitly defines tables. + // those tables can be reopened. + if(const auto atabkeys = parse_array_table_key(def)) + { + // table keys always contains all the nodes from the root. + const auto& tks = atabkeys.unwrap().first; + if(std::size_t(std::distance(key_first, key_last)) == tks.size() && + std::equal(tks.begin(), tks.end(), key_first)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + if(const auto dotkeys = parse_key(def)) // a.b.c = "foo" { // consider the following case. // [a] @@ -1194,6 +1491,18 @@ bool is_valid_forward_table_definition(const Value& fwd, // [a.b.c] // e = 2.71 // this defines the table [a.b.c] twice. no? + if(const auto reopening_dotkey_by_table = parse_table_key(inserting_def)) + { + // re-opening a dotkey-defined table by a table is invalid. + // only dotkey can append a key-val. Like: + // ```toml + // a.b.c = "foo" + // a.b.d = "bar" # OK. reopen `a.b` by dotkey + // [a.b] + // e = "bar" # Invalid. re-opening `a.b` by [a.b] is not allowed. + // ``` + return false; + } // a dotted key starts from the node representing a table in which the // dotted key belongs to. @@ -1266,7 +1575,10 @@ insert_nested_key(typename Value::table_type& root, const Value& v, } // the above if-else-if checks tab->at(k) is an array auto& a = tab->at(k).as_array(); - if(!(a.front().is_table())) + // If table element is defined as [[array_of_tables]], it + // cannot be an empty array. If an array of tables is + // defined as `aot = []`, it cannot be appended. + if(a.empty() || !(a.front().is_table())) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: array of table (\"", @@ -1286,7 +1598,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, // b = 54 // ``` // Here, from the type information, these cannot be detected - // bacause inline table is also a table. + // because inline table is also a table. // But toml v0.5.0 explicitly says it is invalid. The above // array-of-tables has a static size and appending to the // array is invalid. @@ -1315,7 +1627,41 @@ insert_nested_key(typename Value::table_type& root, const Value& v, } else // if not, we need to create the array of table { - value_type aot(array_type(1, v), key_reg); + // XXX: Consider the following array of tables. + // ```toml + // # This is a comment. + // [[aot]] + // foo = "bar" + // ``` + // Here, the comment is for `aot`. But here, actually two + // values are defined. An array that contains tables, named + // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. + // Those two are different from each other. But both of them + // points to the same portion of the TOML file, `[[aot]]`, + // so `key_reg.comments()` returns `# This is a comment`. + // If it is assigned as a comment of `aot` defined here, the + // comment will be duplicated. Both the `aot` itself and + // the 0-th element will have the same comment. This causes + // "duplication of the same comments" bug when the data is + // serialized. + // Next, consider the following. + // ```toml + // # comment 1 + // aot = [ + // # comment 2 + // {foo = "bar"}, + // ] + // ``` + // In this case, we can distinguish those two comments. So + // here we need to add "comment 1" to the `aot` and + // "comment 2" to the 0th element of that. + // To distinguish those two, we check the key region. + std::vector comments{/* empty by default */}; + if(key_reg.str().substr(0, 2) != "[[") + { + comments = key_reg.comments(); + } + value_type aot(array_type(1, v), key_reg, std::move(comments)); tab->insert(std::make_pair(k, aot)); return ok(true); } @@ -1326,7 +1672,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, if(tab->at(k).is_table() && v.is_table()) { if(!is_valid_forward_table_definition( - tab->at(k), first, iter, last)) + tab->at(k), v, first, iter, last)) { throw syntax_error(format_underline(concat_to_string( "toml::insert_value: table (\"", @@ -1344,6 +1690,16 @@ insert_nested_key(typename Value::table_type& root, const Value& v, auto& t = tab->at(k).as_table(); for(const auto& kv : v.as_table()) { + if(tab->at(k).contains(kv.first)) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: value (\"", + format_dotted_keys(first, last), + "\") already exists."), { + {t.at(kv.first).location(), "already exists here"}, + {v.location(), "this defined twice"} + }), v.location()); + } t[kv.first] = kv.second; } detail::change_region(tab->at(k), key_reg); @@ -1384,7 +1740,8 @@ insert_nested_key(typename Value::table_type& root, const Value& v, // [x.y.z] if(tab->count(k) == 0) { - (*tab)[k] = value_type(table_type{}, key_reg); + // a table that is defined implicitly doesn't have any comments. + (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); } // type checking... @@ -1424,6 +1781,29 @@ insert_nested_key(typename Value::table_type& root, const Value& v, {v.location(), "inserting this"} }), v.location()); } + if(a.empty()) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: table (\"", + format_dotted_keys(first, last), "\") conflicts with" + " existing value"), { + {tab->at(k).location(), std::string("this array is not insertable")}, + {v.location(), std::string("appending it to the statically sized array")} + }), v.location()); + } + if(const auto ptr = detail::get_region(a.at(0))) + { + if(ptr->str().substr(0,2) != "[[") + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: a table (\"", + format_dotted_keys(first, last), "\") cannot be " + "inserted to an existing inline array-of-tables"), { + {tab->at(k).location(), std::string("this array of table has a static size")}, + {v.location(), std::string("appending it to the statically sized array")} + }), v.location()); + } + } tab = std::addressof(a.back().as_table()); } else @@ -1444,11 +1824,18 @@ insert_nested_key(typename Value::table_type& root, const Value& v, template result, std::string> -parse_inline_table(location& loc) +parse_inline_table(location& loc, const std::size_t n_rec) { using value_type = Value; using table_type = typename value_type::table_type; + if(n_rec > TOML11_VALUE_RECURSION_LIMIT) + { + throw syntax_error(std::string("toml::parse_inline_table: recursion limit (" + TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), + source_location(loc)); + } + const auto first = loc.iter(); table_type retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) @@ -1457,22 +1844,33 @@ parse_inline_table(location& loc) {{source_location(loc), "the next token is not an inline table"}})); } loc.advance(); + + // check if the inline table is an empty table = { } + maybe::invoke(loc); +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + // TOML 1.1.0: allow newlines in inline tables + while(loc.iter() != loc.end() && + (*loc.iter() == '\r' || *loc.iter() == '\n')) + { + loc.advance(); + } + maybe::invoke(loc); // skip whitespace after newlines +#endif + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + loc.advance(); // skip `}` + return ok(std::make_pair(retval, region(loc, first, loc.iter()))); + } + // it starts from "{". it should be formatted as inline-table while(loc.iter() != loc.end()) { - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - - const auto kv_r = parse_key_value_pair(loc); + const auto kv_r = parse_key_value_pair(loc, n_rec+1); if(!kv_r) { return err(kv_r.unwrap_err()); } + const auto& kvpair = kv_r.unwrap(); const std::vector& keys = kvpair.first.first; const auto& key_reg = kvpair.first.second; @@ -1489,10 +1887,19 @@ parse_inline_table(location& loc) using lex_table_separator = sequence, character<','>>; const auto sp = lex_table_separator::invoke(loc); + if(!sp) { maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') + + if(loc.iter() == loc.end()) + { + throw syntax_error(format_underline( + "toml::parse_inline_table: missing table separator `}` ", + {{source_location(loc), "should be `}`"}}), + source_location(loc)); + } + else if(*loc.iter() == '}') { loc.advance(); // skip `}` return ok(std::make_pair( @@ -1513,6 +1920,33 @@ parse_inline_table(location& loc) source_location(loc)); } } + else // `,` is found + { + maybe::invoke(loc); +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + // TOML 1.1.0: allow newlines after commas in inline tables + while(loc.iter() != loc.end() && + (*loc.iter() == '\r' || *loc.iter() == '\n')) + { + loc.advance(); + } + maybe::invoke(loc); // skip whitespace after newlines +#endif + if(loc.iter() != loc.end() && *loc.iter() == '}') + { +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + // TOML 1.1.0: trailing comma is allowed in inline tables + loc.advance(); // skip `}` + return ok(std::make_pair(retval, region(loc, first, loc.iter()))); +#else + throw syntax_error(format_underline( + "toml::parse_inline_table: trailing comma is not allowed in" + " an inline table", + {{source_location(loc), "should be `}`"}}), + source_location(loc)); +#endif + } + } } loc.reset(first); throw syntax_error(format_underline("toml::parse_inline_table: " @@ -1674,11 +2108,24 @@ inline result guess_value_type(const location& loc) } } -template -result parse_value(location& loc) +template +result +parse_value_helper(result, std::string> rslt) { - using value_type = Value; + if(rslt.is_ok()) + { + auto comments = rslt.as_ok().second.comments(); + return ok(Value(std::move(rslt.as_ok()), std::move(comments))); + } + else + { + return err(std::move(rslt.as_err())); + } +} +template +result parse_value(location& loc, const std::size_t n_rec) +{ const auto first = loc.iter(); if(first == loc.end()) { @@ -1691,18 +2138,19 @@ result parse_value(location& loc) { return err(type.unwrap_err()); } + switch(type.unwrap()) { - case value_t::boolean : {return parse_boolean(loc); } - case value_t::integer : {return parse_integer(loc); } - case value_t::floating : {return parse_floating(loc); } - case value_t::string : {return parse_string(loc); } - case value_t::offset_datetime: {return parse_offset_datetime(loc);} - case value_t::local_datetime : {return parse_local_datetime(loc); } - case value_t::local_date : {return parse_local_date(loc); } - case value_t::local_time : {return parse_local_time(loc); } - case value_t::array : {return parse_array(loc); } - case value_t::table : {return parse_inline_table(loc);} + case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} + case value_t::integer : {return parse_value_helper(parse_integer(loc) );} + case value_t::floating : {return parse_value_helper(parse_floating(loc) );} + case value_t::string : {return parse_value_helper(parse_string(loc) );} + case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} + case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} + case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} + case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} + case value_t::array : {return parse_value_helper(parse_array(loc, n_rec));} + case value_t::table : {return parse_value_helper(parse_inline_table(loc, n_rec));} default: { const auto msg = format_underline("toml::parse_value: " @@ -1751,7 +2199,7 @@ parse_table_key(location& loc) source_location(inner_loc)); } - // after [table.key], newline or EOF(empty table) requried. + // after [table.key], newline or EOF(empty table) required. if(loc.iter() != loc.end()) { using lex_newline_after_table_key = @@ -1803,12 +2251,12 @@ parse_array_table_key(location& loc) if(!close) { throw internal_error(format_underline( - "toml::parse_table_key: no `]]`", + "toml::parse_array_table_key: no `]]`", {{source_location(inner_loc), "should be `]]`"}}), source_location(inner_loc)); } - // after [[table.key]], newline or EOF(empty table) requried. + // after [[table.key]], newline or EOF(empty table) required. if(loc.iter() != loc.end()) { using lex_newline_after_table_key = @@ -1867,7 +2315,7 @@ parse_ml_table(location& loc) return ok(tab); } - if(const auto kv = parse_key_value_pair(loc)) + if(const auto kv = parse_key_value_pair(loc, 0)) { const auto& kvpair = kv.unwrap(); const std::vector& keys = kvpair.first.first; @@ -1926,7 +2374,9 @@ result parse_toml_file(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return ok(value_type(table_type{} /*, empty file has no region ...*/)); + // For empty files, return an empty table with an empty region (zero-length). + // Without the region, error messages would miss the filename. + return ok(value_type(table_type{}, region(loc, first, first), {})); } // put the first line as a region of a file @@ -1969,7 +2419,7 @@ result parse_toml_file(location& loc) table_type data; // root object is also a table, but without [tablename] - if(auto tab = parse_ml_table(loc)) + if(const auto tab = parse_ml_table(loc)) { data = std::move(tab.unwrap()); } @@ -1993,7 +2443,7 @@ result parse_toml_file(location& loc) const auto& reg = tk.second; const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg), + value_type(tab.unwrap(), reg, reg.comments()), keys.begin(), keys.end(), reg, /*is_array_of_table=*/ true); if(!inserted) {return err(inserted.unwrap_err());} @@ -2010,7 +2460,8 @@ result parse_toml_file(location& loc) const auto& reg = tk.second; const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg), keys.begin(), keys.end(), reg); + value_type(tab.unwrap(), reg, reg.comments()), + keys.begin(), keys.end(), reg); if(!inserted) {return err(inserted.unwrap_err());} continue; @@ -2019,38 +2470,27 @@ result parse_toml_file(location& loc) "unknown line appeared", {{source_location(loc), "unknown format"}})); } - Value v(std::move(data), file); - v.comments() = comments; - - return ok(std::move(v)); + return ok(Value(std::move(data), file, comments)); } -} // detail - -template class Table = std::unordered_map, template class Array = std::vector> basic_value -parse(std::istream& is, const std::string& fname = "unknown file") +parse(std::vector& letters, const std::string& fname) { using value_type = basic_value; - const auto beg = is.tellg(); - is.seekg(0, std::ios::end); - const auto end = is.tellg(); - const auto fsize = end - beg; - is.seekg(beg); - - // read whole file as a sequence of char - assert(fsize >= 0); - std::vector letters(static_cast(fsize)); - is.read(letters.data(), fsize); - - while(!letters.empty() && letters.back() == '\0') + // append LF. + // Although TOML does not require LF at the EOF, to make parsing logic + // simpler, we "normalize" the content by adding LF if it does not exist. + // It also checks if the last char is CR, to avoid changing the meaning. + // This is not the *best* way to deal with the last character, but is a + // simple and quick fix. + if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') { - letters.pop_back(); + letters.push_back('\n'); } - assert(letters.empty() || letters.back() != '\0'); detail::location loc(std::move(fname), std::move(letters)); @@ -2070,25 +2510,91 @@ parse(std::istream& is, const std::string& fname = "unknown file") } } - const auto data = detail::parse_toml_file(loc); - if(!data) + if (auto data = detail::parse_toml_file(loc)) + { + return std::move(data).unwrap(); + } + else + { + throw syntax_error(std::move(data).unwrap_err(), source_location(loc)); + } +} + +} // detail + +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value +parse(FILE * file, const std::string& fname) +{ + const long beg = std::ftell(file); + if (beg == -1l) { - throw syntax_error(data.unwrap_err(), source_location(loc)); + throw file_io_error(errno, "Failed to access", fname); } - return data.unwrap(); + + const int res_seekend = std::fseek(file, 0, SEEK_END); + if (res_seekend != 0) + { + throw file_io_error(errno, "Failed to seek", fname); + } + + const long end = std::ftell(file); + if (end == -1l) + { + throw file_io_error(errno, "Failed to access", fname); + } + + const auto fsize = end - beg; + + const auto res_seekbeg = std::fseek(file, beg, SEEK_SET); + if (res_seekbeg != 0) + { + throw file_io_error(errno, "Failed to seek", fname); + } + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + std::fread(letters.data(), sizeof(char), static_cast(fsize), file); + + return detail::parse(letters, fname); +} + +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value +parse(std::istream& is, std::string fname = "unknown file") +{ + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + is.read(letters.data(), fsize); + + return detail::parse(letters, fname); } -template class Table = std::unordered_map, template class Array = std::vector> -basic_value parse(const std::string& fname) +basic_value parse(std::string fname) { - std::ifstream ifs(fname.c_str(), std::ios_base::binary); + std::ifstream ifs(fname, std::ios_base::binary); if(!ifs.good()) { - throw std::runtime_error("toml::parse: file open error -> " + fname); + throw std::ios_base::failure( + "toml::parse: Error opening file \"" + fname + "\""); } - return parse(ifs, fname); + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + return parse(ifs, std::move(fname)); } #ifdef TOML11_HAS_STD_FILESYSTEM @@ -2099,9 +2605,9 @@ basic_value parse(const std::string& fname) // Without this, both parse(std::string) and parse(std::filesystem::path) // matches to parse("filename.toml"). This breaks the existing code. // -// This function exactly matches to the invokation with c-string. +// This function exactly matches to the invocation with c-string. // So this function is preferred than others and the ambiguity disappears. -template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(const char* fname) @@ -2109,7 +2615,7 @@ basic_value parse(const char* fname) return parse(std::string(fname)); } -template class Table = std::unordered_map, template class Array = std::vector> basic_value parse(const std::filesystem::path& fpath) @@ -2117,9 +2623,10 @@ basic_value parse(const std::filesystem::path& fpath) std::ifstream ifs(fpath, std::ios_base::binary); if(!ifs.good()) { - throw std::runtime_error("toml::parse: file open error -> " + - fpath.string()); + throw std::ios_base::failure( + "toml::parse: Error opening file \"" + fpath.string() + "\""); } + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); return parse(ifs, fpath.string()); } #endif // TOML11_HAS_STD_FILESYSTEM diff --git a/third_party/toml11-3.6.0/toml/region.hpp b/third_party/toml11-3.8.1/toml/region.hpp similarity index 93% rename from third_party/toml11-3.6.0/toml/region.hpp rename to third_party/toml11-3.8.1/toml/region.hpp index 6761aed..72a6f11 100644 --- a/third_party/toml11-3.6.0/toml/region.hpp +++ b/third_party/toml11-3.8.1/toml/region.hpp @@ -70,16 +70,16 @@ struct region_base struct location final : public region_base { using const_iterator = typename std::vector::const_iterator; - using difference_type = typename const_iterator::difference_type; + using difference_type = typename std::iterator_traits::difference_type; using source_ptr = std::shared_ptr>; - location(std::string name, std::vector cont) + location(std::string source_name, std::vector cont) : source_(std::make_shared>(std::move(cont))), - line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin()) + line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) {} - location(std::string name, const std::string& cont) + location(std::string source_name, const std::string& cont) : source_(std::make_shared>(cont.begin(), cont.end())), - line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin()) + line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) {} location(const location&) = default; @@ -92,7 +92,7 @@ struct location final : public region_base char front() const noexcept override {return *iter_;} // this const prohibits codes like `++(loc.iter())`. - const const_iterator iter() const noexcept {return iter_;} + std::add_const::type iter() const noexcept {return iter_;} const_iterator begin() const noexcept {return source_->cbegin();} const_iterator end() const noexcept {return source_->cend();} @@ -227,8 +227,7 @@ struct region final : public region_base region& operator+=(const region& other) { // different regions cannot be concatenated - assert(this->begin() == other.begin() && this->end() == other.end() && - this->last_ == other.first_); + assert(this->source_ == other.source_ && this->last_ == other.first_); this->last_ = other.last_; return *this; @@ -343,9 +342,9 @@ struct region final : public region_base })) { // unwrap the first '#' by std::next. - auto str = make_string(std::next(comment_found), iter); - if(str.back() == '\r') {str.pop_back();} - com.push_back(std::move(str)); + auto s = make_string(std::next(comment_found), iter); + if(!s.empty() && s.back() == '\r') {s.pop_back();} + com.push_back(std::move(s)); } else { @@ -396,9 +395,9 @@ struct region final : public region_base })) { // unwrap the first '#' by std::next. - auto str = make_string(std::next(comment_found), this->line_end()); - if(str.back() == '\r') {str.pop_back();} - com.push_back(std::move(str)); + auto s = make_string(std::next(comment_found), this->line_end()); + if(!s.empty() && s.back() == '\r') {s.pop_back();} + com.push_back(std::move(s)); } } } diff --git a/third_party/toml11-3.6.0/toml/result.hpp b/third_party/toml11-3.8.1/toml/result.hpp similarity index 100% rename from third_party/toml11-3.6.0/toml/result.hpp rename to third_party/toml11-3.8.1/toml/result.hpp diff --git a/third_party/toml11-3.6.0/toml/serializer.hpp b/third_party/toml11-3.8.1/toml/serializer.hpp similarity index 65% rename from third_party/toml11-3.6.0/toml/serializer.hpp rename to third_party/toml11-3.8.1/toml/serializer.hpp index ed07f46..23fb89a 100644 --- a/third_party/toml11-3.6.0/toml/serializer.hpp +++ b/third_party/toml11-3.8.1/toml/serializer.hpp @@ -2,10 +2,19 @@ // Distributed under the MIT License. #ifndef TOML11_SERIALIZER_HPP #define TOML11_SERIALIZER_HPP +#include #include #include +#if defined(_WIN32) +#include +#elif defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(__linux__) +#include +#endif + #include "lexer.hpp" #include "value.hpp" @@ -26,19 +35,24 @@ namespace toml // a `"` and escaping some special character is boring. template std::basic_string -format_key(const std::basic_string& key) +format_key(const std::basic_string& k) { + if(k.empty()) + { + return std::string("\"\""); + } + // check the key can be a bare (unquoted) key - detail::location loc(key, std::vector(key.begin(), key.end())); + detail::location loc(k, std::vector(k.begin(), k.end())); detail::lex_unquoted_key::invoke(loc); if(loc.iter() == loc.end()) { - return key; // all the tokens are consumed. the key is unquoted-key. + return k; // all the tokens are consumed. the key is unquoted-key. } //if it includes special characters, then format it in a "quoted" key. std::basic_string serialized("\""); - for(const char c : key) + for(const char c : k) { switch(c) { @@ -49,7 +63,19 @@ format_key(const std::basic_string& key) case '\f': {serialized += "\\f"; break;} case '\n': {serialized += "\\n"; break;} case '\r': {serialized += "\\r"; break;} - default : {serialized += c; break;} + default: { + if (c >= 0x00 && c < 0x20) + { + std::array buf; + std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast(c)); + serialized += buf.data(); + } + else + { + serialized += c; + } + break; + } } } serialized += "\""; @@ -60,9 +86,12 @@ template std::basic_string format_keys(const std::vector>& keys) { - std::basic_string serialized; - if(keys.empty()) {return serialized;} + if(keys.empty()) + { + return std::string("\"\""); + } + std::basic_string serialized; for(const auto& ky : keys) { serialized += format_key(ky); @@ -97,8 +126,10 @@ struct serializer const int float_prec = std::numeric_limits::max_digits10, const bool can_be_inlined = false, const bool no_comment = false, - std::vector ks = {}) + std::vector ks = {}, + const bool value_has_comment = false) : can_be_inlined_(can_be_inlined), no_comment_(no_comment), + value_has_comment_(value_has_comment && !no_comment), float_prec_(float_prec), width_(w), keys_(std::move(ks)) {} ~serializer() = default; @@ -109,18 +140,93 @@ struct serializer } std::string operator()(const integer_type i) const { - return std::to_string(i); +#if defined(_WIN32) + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); + setlocale(LC_NUMERIC, "C"); +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) + const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); + locale_t original_locale(0); + if(c_locale != locale_t(0)) + { + original_locale = uselocale(c_locale); + } +#endif + + const auto str = std::to_string(i); + +#if defined(_WIN32) + setlocale(LC_NUMERIC, original_locale.c_str()); + _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) + if(original_locale != locale_t(0)) + { + uselocale(original_locale); + } +#endif + return str; } std::string operator()(const floating_type f) const { + if(std::isnan(f)) + { + if(std::signbit(f)) + { + return std::string("-nan"); + } + else + { + return std::string("nan"); + } + } + else if(!std::isfinite(f)) + { + if(std::signbit(f)) + { + return std::string("-inf"); + } + else + { + return std::string("inf"); + } + } + + // set locale to "C". + // To make it thread-local, we use OS-specific features. + // If we set process-global locale, it can break other thread that also + // outputs something simultaneously. +#if defined(_WIN32) + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); + setlocale(LC_NUMERIC, "C"); +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) + const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); + locale_t original_locale(0); + if(c_locale != locale_t(0)) + { + original_locale = uselocale(c_locale); + } +#endif + const auto fmt = "%.*g"; const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); // +1 for null character(\0) std::vector buf(static_cast(bsz + 1), '\0'); std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); + // restore the original locale +#if defined(_WIN32) + setlocale(LC_NUMERIC, original_locale.c_str()); + _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) + if(original_locale != locale_t(0)) + { + uselocale(original_locale); + } +#endif + std::string token(buf.begin(), std::prev(buf.end())); - if(token.back() == '.') // 1. => 1.0 + if(!token.empty() && token.back() == '.') // 1. => 1.0 { token += '0'; } @@ -144,8 +250,9 @@ struct serializer { if(s.kind == string_t::basic) { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) + if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || + std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && + this->width_ != (std::numeric_limits::max)()) { // if linefeed or double-quote is contained, // make it multiline basic string. @@ -244,92 +351,18 @@ struct serializer std::string operator()(const array_type& v) const { - if(!v.empty() && v.front().is_table())// v is an array of tables - { - // if it's not inlined, we need to add `[[table.key]]`. - // but if it can be inlined, - // ``` - // table.key = [ - // {...}, - // # comment - // {...}, - // ] - // ``` - if(this->can_be_inlined_) - { - std::string token; - if(!keys_.empty()) - { - token += format_key(keys_.back()); - token += " = "; - } - bool failed = false; - token += "[\n"; - for(const auto& item : v) - { - // if an element of the table has a comment, the table - // cannot be inlined. - if(this->has_comment_inside(item.as_table())) - { - failed = true; - break; - } - if(!no_comment_) - { - for(const auto& c : item.comments()) - { - token += '#'; - token += c; - token += '\n'; - } - } - - const auto t = this->make_inline_table(item.as_table()); - - if(t.size() + 1 > width_ || // +1 for the last comma {...}, - std::find(t.cbegin(), t.cend(), '\n') != t.cend()) - { - failed = true; - break; - } - token += t; - token += ",\n"; - } - if(!failed) - { - token += "]\n"; - return token; - } - // if failed, serialize them as [[array.of.tables]]. - } - - std::string token; - for(const auto& item : v) - { - if(!no_comment_) - { - for(const auto& c : item.comments()) - { - token += '#'; - token += c; - token += '\n'; - } - } - token += "[["; - token += format_keys(keys_); - token += "]]\n"; - token += this->make_multiline_table(item.as_table()); - } - return token; - } if(v.empty()) { return std::string("[]"); } + if(this->is_array_of_tables(v)) + { + return make_array_of_tables(v); + } // not an array of tables. normal array. // first, try to make it inline if none of the elements have a comment. - if(!this->has_comment_inside(v)) + if( ! this->has_comment_inside(v)) { const auto inl = this->make_inline_array(v); if(inl.size() < this->width_ && @@ -350,7 +383,7 @@ struct serializer token += "[\n"; for(const auto& item : v) { - if(!item.comments().empty() && !no_comment_) + if( ! item.comments().empty() && !no_comment_) { // if comment exists, the element must be the only element in the line. // e.g. the following is not allowed. @@ -376,15 +409,25 @@ struct serializer token += '\n'; } token += toml::visit(*this, item); - if(token.back() == '\n') {token.pop_back();} + if(!token.empty() && token.back() == '\n') {token.pop_back();} token += ",\n"; continue; } std::string next_elem; - next_elem += toml::visit(*this, item); + if(item.is_table()) + { + serializer ser(*this); + ser.can_be_inlined_ = true; + ser.width_ = (std::numeric_limits::max)(); + next_elem += toml::visit(ser, item); + } + else + { + next_elem += toml::visit(*this, item); + } // comma before newline. - if(next_elem.back() == '\n') {next_elem.pop_back();} + if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} // if current line does not exceeds the width limit, continue. if(current_line.size() + next_elem.size() + 1 < this->width_) @@ -411,7 +454,10 @@ struct serializer } if(!current_line.empty()) { - if(current_line.back() != '\n') {current_line += '\n';} + if(!current_line.empty() && current_line.back() != '\n') + { + current_line += '\n'; + } token += current_line; } token += "]\n"; @@ -467,7 +513,19 @@ struct serializer case '\f': {retval += "\\f"; break;} case '\n': {retval += "\\n"; break;} case '\r': {retval += "\\r"; break;} - default : {retval += c; break;} + default : + { + if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) + { + retval += "\\u00"; + retval += char(48 + (c / 16)); + retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); + } + else + { + retval += c; + } + } } } return retval; @@ -501,7 +559,21 @@ struct serializer } break; } - default: {retval += *i; break;} + default : + { + const auto c = *i; + if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) + { + retval += "\\u00"; + retval += char(48 + (c / 16)); + retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); + } + else + { + retval += c; + } + } + } } // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. @@ -557,8 +629,10 @@ struct serializer for(const auto& item : v) { if(is_first) {is_first = false;} else {token += ',';} - token += visit(serializer((std::numeric_limits::max)(), - this->float_prec_, true), item); + token += visit(serializer( + (std::numeric_limits::max)(), this->float_prec_, + /* inlined */ true, /*no comment*/ false, /*keys*/ {}, + /*has_comment*/ !item.comments().empty()), item); } token += ']'; return token; @@ -577,8 +651,10 @@ struct serializer if(is_first) {is_first = false;} else {token += ',';} token += format_key(kv.first); token += '='; - token += visit(serializer((std::numeric_limits::max)(), - this->float_prec_, true), kv.second); + token += visit(serializer( + (std::numeric_limits::max)(), this->float_prec_, + /* inlined */ true, /*no comment*/ false, /*keys*/ {}, + /*has_comment*/ !kv.second.comments().empty()), kv.second); } token += '}'; return token; @@ -588,8 +664,16 @@ struct serializer { std::string token; - // print non-table stuff first. because after printing [foo.bar], the - // remaining non-table values will be assigned into [foo.bar], not [foo] + // print non-table elements first. + // ```toml + // [foo] # a table we're writing now here + // key = "value" # <- non-table element, "key" + // # ... + // [foo.bar] # <- table element, "bar" + // ``` + // because after printing [foo.bar], the remaining non-table values will + // be assigned into [foo.bar], not [foo]. Those values should be printed + // earlier. for(const auto& kv : v) { if(kv.second.is_table() || is_array_of_tables(kv.second)) @@ -597,21 +681,16 @@ struct serializer continue; } - if(!kv.second.comments().empty() && !no_comment_) - { - for(const auto& c : kv.second.comments()) - { - token += '#'; - token += c; - token += '\n'; - } - } + token += write_comments(kv.second); + const auto key_and_sep = format_key(kv.first) + " = "; const auto residual_width = (this->width_ > key_and_sep.size()) ? this->width_ - key_and_sep.size() : 0; token += key_and_sep; - token += visit(serializer(residual_width, this->float_prec_, true), - kv.second); + token += visit(serializer(residual_width, this->float_prec_, + /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, + /*has_comment*/ !kv.second.comments().empty()), kv.second); + if(token.back() != '\n') { token += '\n'; @@ -637,45 +716,172 @@ struct serializer ks.push_back(kv.first); auto tmp = visit(serializer(this->width_, this->float_prec_, - !multiline_table_printed, this->no_comment_, ks), - kv.second); - + !multiline_table_printed, this->no_comment_, ks, + /*has_comment*/ !kv.second.comments().empty()), kv.second); + + // If it is the first time to print a multi-line table, it would be + // helpful to separate normal key-value pair and subtables by a + // newline. + // (this checks if the current key-value pair contains newlines. + // but it is not perfect because multi-line string can also contain + // a newline. in such a case, an empty line will be written) TODO if((!multiline_table_printed) && std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) { multiline_table_printed = true; + token += '\n'; // separate key-value pairs and subtables + + token += write_comments(kv.second); + token += tmp; + + // care about recursive tables (all tables in each level prints + // newline and there will be a full of newlines) + if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && + tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) + { + token += '\n'; + } } else { - // still inline tables only. - tmp += '\n'; + token += write_comments(kv.second); + token += tmp; + token += '\n'; + } + } + return token; + } + + std::string make_array_of_tables(const array_type& v) const + { + // if it's not inlined, we need to add `[[table.key]]`. + // but if it can be inlined, we can format it as the following. + // ``` + // table.key = [ + // {...}, + // # comment + // {...}, + // ] + // ``` + // This function checks if inlinization is possible or not, and then + // format the array-of-tables in a proper way. + // + // Note about comments: + // + // If the array itself has a comment (value_has_comment_ == true), we + // should try to make it inline. + // ```toml + // # comment about array + // array = [ + // # comment about table element + // {of = "table"} + // ] + // ``` + // If it is formatted as a multiline table, the two comments becomes + // indistinguishable. + // ```toml + // # comment about array + // # comment about table element + // [[array]] + // of = "table" + // ``` + // So we need to try to make it inline, and it force-inlines regardless + // of the line width limit. + // It may fail if the element of a table has comment. In that case, + // the array-of-tables will be formatted as a multiline table. + if(this->can_be_inlined_ || this->value_has_comment_) + { + std::string token; + if(!keys_.empty()) + { + token += format_key(keys_.back()); + token += " = "; } - if(!kv.second.comments().empty() && !no_comment_) + bool failed = false; + token += "[\n"; + for(const auto& item : v) { - for(const auto& c : kv.second.comments()) + // if an element of the table has a comment, the table + // cannot be inlined. + if(this->has_comment_inside(item.as_table())) { - token += '#'; - token += c; - token += '\n'; + failed = true; + break; + } + // write comments for the table itself + token += write_comments(item); + + const auto t = this->make_inline_table(item.as_table()); + + if(t.size() + 1 > width_ || // +1 for the last comma {...}, + std::find(t.cbegin(), t.cend(), '\n') != t.cend()) + { + // if the value itself has a comment, ignore the line width limit + if( ! this->value_has_comment_) + { + failed = true; + break; + } } + token += t; + token += ",\n"; } - token += tmp; + + if( ! failed) + { + token += "]\n"; + return token; + } + // if failed, serialize them as [[array.of.tables]]. + } + + std::string token; + for(const auto& item : v) + { + token += write_comments(item); + token += "[["; + token += format_keys(keys_); + token += "]]\n"; + token += this->make_multiline_table(item.as_table()); } return token; } + std::string write_comments(const value_type& v) const + { + std::string retval; + if(this->no_comment_) {return retval;} + + for(const auto& c : v.comments()) + { + retval += '#'; + retval += c; + retval += '\n'; + } + return retval; + } + bool is_array_of_tables(const value_type& v) const { - if(!v.is_array()) {return false;} - const auto& a = v.as_array(); - return !a.empty() && a.front().is_table(); + if(!v.is_array() || v.as_array().empty()) {return false;} + return is_array_of_tables(v.as_array()); + } + bool is_array_of_tables(const array_type& v) const + { + // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to + // check all the element in an array to check if the array is an array + // of tables. + return std::all_of(v.begin(), v.end(), [](const value_type& elem) { + return elem.is_table(); + }); } private: bool can_be_inlined_; bool no_comment_; + bool value_has_comment_; int float_prec_; std::size_t width_; std::vector keys_; @@ -699,7 +905,7 @@ format(const basic_value& v, std::size_t w = 80u, oss << v.comments(); oss << '\n'; // to split the file comment from the first element } - const auto serialized = visit(serializer(w, fprec, no_comment, false), v); + const auto serialized = visit(serializer(w, fprec, false, no_comment), v); oss << serialized; return oss.str(); } @@ -720,7 +926,7 @@ template std::basic_ostream& nocomment(std::basic_ostream& os) { - // by default, it is zero. and by defalut, it shows comments. + // by default, it is zero. and by default, it shows comments. os.iword(detail::comment_index(os)) = 1; return os; } @@ -729,7 +935,7 @@ template std::basic_ostream& showcomment(std::basic_ostream& os) { - // by default, it is zero. and by defalut, it shows comments. + // by default, it is zero. and by default, it shows comments. os.iword(detail::comment_index(os)) = 0; return os; } @@ -746,7 +952,7 @@ operator<<(std::basic_ostream& os, const basic_value& v) const int fprec = static_cast(os.precision()); os.width(0); - // by defualt, iword is initialized byl 0. And by default, toml11 outputs + // by default, iword is initialized by 0. And by default, toml11 outputs // comments. So `0` means showcomment. 1 means nocommnet. const bool no_comment = (1 == os.iword(detail::comment_index(os))); diff --git a/third_party/toml11-3.6.0/toml/source_location.hpp b/third_party/toml11-3.8.1/toml/source_location.hpp similarity index 96% rename from third_party/toml11-3.6.0/toml/source_location.hpp rename to third_party/toml11-3.8.1/toml/source_location.hpp index a386710..135024f 100644 --- a/third_party/toml11-3.6.0/toml/source_location.hpp +++ b/third_party/toml11-3.8.1/toml/source_location.hpp @@ -3,6 +3,7 @@ #ifndef TOML11_SOURCE_LOCATION_HPP #define TOML11_SOURCE_LOCATION_HPP #include +#include #include "region.hpp" @@ -124,7 +125,7 @@ inline std::string format_underline(const std::string& message, std::ostringstream retval; - if(colorize) + if(color::should_color() || colorize) { retval << color::colorize; // turn on ANSI color } @@ -136,12 +137,18 @@ inline std::string format_underline(const std::string& message, // if it is "[error]", it removes that part from the message shown. if(message.size() > 7 && message.substr(0, 7) == "[error]") { - retval << color::bold << color::red << "[error]" << color::reset + retval +#ifndef TOML11_NO_ERROR_PREFIX + << color::bold << color::red << "[error]" << color::reset +#endif << color::bold << message.substr(7) << color::reset << '\n'; } else { - retval << color::bold << color::red << "[error] " << color::reset + retval +#ifndef TOML11_NO_ERROR_PREFIX + << color::bold << color::red << "[error] " << color::reset +#endif << color::bold << message << color::reset << '\n'; } diff --git a/third_party/toml11-3.6.0/toml/storage.hpp b/third_party/toml11-3.8.1/toml/storage.hpp similarity index 100% rename from third_party/toml11-3.6.0/toml/storage.hpp rename to third_party/toml11-3.8.1/toml/storage.hpp diff --git a/third_party/toml11-3.6.0/toml/string.hpp b/third_party/toml11-3.8.1/toml/string.hpp similarity index 97% rename from third_party/toml11-3.6.0/toml/string.hpp rename to third_party/toml11-3.8.1/toml/string.hpp index a6ed080..def3e57 100644 --- a/third_party/toml11-3.6.0/toml/string.hpp +++ b/third_party/toml11-3.8.1/toml/string.hpp @@ -2,13 +2,17 @@ // Distributed under the MIT License. #ifndef TOML11_STRING_HPP #define TOML11_STRING_HPP + +#include "version.hpp" + #include #include #include -#if __cplusplus >= 201703L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #if __has_include() +#define TOML11_USING_STRING_VIEW 1 #include #endif #endif @@ -53,7 +57,7 @@ struct string string& operator+=(const std::string& rhs) {str += rhs; return *this;} string& operator+=(const string& rhs) {str += rhs.str; return *this;} -#if __cplusplus >= 201703L +#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 explicit string(std::string_view s): kind(string_t::basic), str(s){} string(std::string_view s, string_t k): kind(k), str(s){} diff --git a/third_party/toml11-3.6.0/toml/traits.hpp b/third_party/toml11-3.8.1/toml/traits.hpp similarity index 88% rename from third_party/toml11-3.6.0/toml/traits.hpp rename to third_party/toml11-3.8.1/toml/traits.hpp index eafa6af..255d9e8 100644 --- a/third_party/toml11-3.6.0/toml/traits.hpp +++ b/third_party/toml11-3.8.1/toml/traits.hpp @@ -2,6 +2,11 @@ // Distributed under the MIT License. #ifndef TOML11_TRAITS_HPP #define TOML11_TRAITS_HPP + +#include "from.hpp" +#include "into.hpp" +#include "version.hpp" + #include #include #include @@ -9,7 +14,7 @@ #include #include -#if __cplusplus >= 201703L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L #if __has_include() #include #endif // has_include() @@ -84,6 +89,22 @@ struct has_into_toml_method_impl static std::false_type check(...); }; +struct has_specialized_from_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; +struct has_specialized_into_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; + + /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER @@ -114,6 +135,11 @@ template struct has_into_toml_method : decltype(has_into_toml_method_impl::check(nullptr)){}; +template +struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; +template +struct has_specialized_into : decltype(has_specialized_into_impl::check(nullptr)){}; + #ifdef __INTEL_COMPILER #undef decltype #endif @@ -121,7 +147,7 @@ struct has_into_toml_method // --------------------------------------------------------------------------- // C++17 and/or/not -#if __cplusplus >= 201703L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L using std::conjunction; using std::disjunction; @@ -183,8 +209,10 @@ template struct is_container : conjunction< negation>, // not a map negation>, // not a std::string -#if __cplusplus >= 201703L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L +#if __has_include() negation>, // not a std::string_view +#endif // has_include() #endif has_iterator, // has T::iterator has_value_type // has T::value_type @@ -206,7 +234,7 @@ struct is_basic_value<::toml::basic_value>: std::true_type{}; // --------------------------------------------------------------------------- // C++14 index_sequence -#if __cplusplus >= 201402L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L using std::index_sequence; using std::make_index_sequence; @@ -236,12 +264,12 @@ struct index_sequence_maker<0> template using make_index_sequence = typename index_sequence_maker::type; -#endif // __cplusplus >= 2014 +#endif // cplusplus >= 2014 // --------------------------------------------------------------------------- // C++14 enable_if_t -#if __cplusplus >= 201402L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L using std::enable_if_t; @@ -250,12 +278,12 @@ using std::enable_if_t; template using enable_if_t = typename std::enable_if::type; -#endif // __cplusplus >= 2014 +#endif // cplusplus >= 2014 // --------------------------------------------------------------------------- // return_type_of_t -#if __cplusplus >= 201703L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703 template using return_type_of_t = std::invoke_result_t; diff --git a/third_party/toml11-3.6.0/toml/types.hpp b/third_party/toml11-3.8.1/toml/types.hpp similarity index 91% rename from third_party/toml11-3.6.0/toml/types.hpp rename to third_party/toml11-3.8.1/toml/types.hpp index 0818ebd..1e420e7 100644 --- a/third_party/toml11-3.6.0/toml/types.hpp +++ b/third_party/toml11-3.8.1/toml/types.hpp @@ -21,9 +21,14 @@ class basic_value; using character = char; using key = std::string; +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wshadow" +#endif + using boolean = bool; using integer = std::int64_t; -using floating = double; // "float" is a keyward, cannot use it here. +using floating = double; // "float" is a keyword, cannot use it here. // the following stuffs are structs defined here, so aliases are not needed. // - string // - offset_datetime @@ -32,12 +37,26 @@ using floating = double; // "float" is a keyward, cannot use it here. // - local_date // - local_time +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + // default toml::value and default array/table. these are defined after defining // basic_value itself. // using value = basic_value; // using array = typename value::array_type; // using table = typename value::table_type; +// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in +// GCC -Wshadow=global. +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# if 7 <= __GNUC__ +# pragma GCC diagnostic ignored "-Wshadow=global" +# else // gcc-6 or older +# pragma GCC diagnostic ignored "-Wshadow" +# endif +#endif enum class value_t : std::uint8_t { empty = 0, @@ -52,6 +71,9 @@ enum class value_t : std::uint8_t array = 9, table = 10, }; +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif template inline std::basic_ostream& @@ -147,4 +169,5 @@ template struct is_exact_toml_type } // detail } // toml + #endif// TOML11_TYPES_H diff --git a/third_party/toml11-3.6.0/toml/utility.hpp b/third_party/toml11-3.8.1/toml/utility.hpp similarity index 55% rename from third_party/toml11-3.6.0/toml/utility.hpp rename to third_party/toml11-3.8.1/toml/utility.hpp index 113bec6..53a18b9 100644 --- a/third_party/toml11-3.6.0/toml/utility.hpp +++ b/third_party/toml11-3.8.1/toml/utility.hpp @@ -7,8 +7,9 @@ #include #include "traits.hpp" +#include "version.hpp" -#if __cplusplus >= 201402L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L # define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] #elif defined(__GNUC__) # define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) @@ -21,7 +22,7 @@ namespace toml { -#if __cplusplus >= 201402L +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L using std::make_unique; @@ -33,7 +34,7 @@ inline std::unique_ptr make_unique(Ts&& ... args) return std::unique_ptr(new T(std::forward(args)...)); } -#endif // __cplusplus >= 2014 +#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014 namespace detail { @@ -89,5 +90,61 @@ T from_string(const std::string& str, T opt) return v; } +namespace detail +{ +#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L +template +decltype(auto) last_one(T&& tail) noexcept +{ + return std::forward(tail); +} + +template +decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept +{ + return last_one(std::forward(tail)...); +} +#else // C++11 +// The following code +// ```cpp +// 1 | template +// 2 | auto last_one(T&& /*head*/, Ts&& ... tail) +// 3 | -> decltype(last_one(std::forward(tail)...)) +// 4 | { +// 5 | return last_one(std::forward(tail)...); +// 6 | } +// ``` +// does not work because the function `last_one(...)` is not yet defined at +// line #3, so `decltype()` cannot deduce the type returned from `last_one`. +// So we need to determine return type in a different way, like a meta func. + +template +struct last_one_in_pack +{ + using type = typename last_one_in_pack::type; +}; +template +struct last_one_in_pack +{ + using type = T; +}; +template +using last_one_in_pack_t = typename last_one_in_pack::type; + +template +T&& last_one(T&& tail) noexcept +{ + return std::forward(tail); +} +template +enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> +last_one(T&& /*head*/, Ts&& ... tail) +{ + return last_one(std::forward(tail)...); +} + +#endif +} // detail + }// toml #endif // TOML11_UTILITY diff --git a/third_party/toml11-3.6.0/toml/value.hpp b/third_party/toml11-3.8.1/toml/value.hpp similarity index 94% rename from third_party/toml11-3.6.0/toml/value.hpp rename to third_party/toml11-3.8.1/toml/value.hpp index 76eee0d..d57ab80 100644 --- a/third_party/toml11-3.6.0/toml/value.hpp +++ b/third_party/toml11-3.8.1/toml/value.hpp @@ -66,9 +66,20 @@ throw_key_not_found_error(const Value& v, const key& ky) // ``` // It actually points to the top-level table at the first character, // not `[table]`. But it is too confusing. To avoid the confusion, the error - // message should explicitly say "key not found in the top-level table". + // message should explicitly say "key not found in the top-level table", + // or "the parsed file is empty" if there is no content at all (0 bytes in file). const auto loc = v.location(); - if(loc.line() == 1 && loc.region() == 1) + if(loc.line() == 1 && loc.region() == 0) + { + // First line with a zero-length region means "empty file". + // The region will be generated at `parse_toml_file` function + // if the file contains no bytes. + throw std::out_of_range(format_underline(concat_to_string( + "key \"", ky, "\" not found in the top-level table"), { + {loc, "the parsed file is empty"} + })); + } + else if(loc.line() == 1 && loc.region() == 1) { // Here it assumes that top-level table starts at the first character. // The region corresponds to the top-level table will be generated at @@ -90,7 +101,7 @@ throw_key_not_found_error(const Value& v, const key& ky) // ```toml // a = {b = "c"} // ``` - // toml11 consideres the inline table body as the table region. Here, + // toml11 considers the inline table body as the table region. Here, // `{b = "c"}` is the region of the table "a". The size of the region // is 9, not 1. The shotest inline table still has two characters, `{` // and `}`. The size cannot be 1. @@ -99,7 +110,7 @@ throw_key_not_found_error(const Value& v, const key& ky) // ```toml // [a] // ``` - // toml11 consideres the whole table key as the table region. Here, + // toml11 considers the whole table key as the table region. Here, // `[a]` is the table region. The size is 3, not 1. // throw std::out_of_range(format_underline(concat_to_string( @@ -236,6 +247,7 @@ class basic_value } basic_value& operator=(const basic_value& v) { + if(this == std::addressof(v)) {return *this;} this->cleanup(); this->region_info_ = v.region_info_; this->comments_ = v.comments_; @@ -258,6 +270,7 @@ class basic_value } basic_value& operator=(basic_value&& v) { + if(this == std::addressof(v)) {return *this;} this->cleanup(); this->region_info_ = std::move(v.region_info_); this->comments_ = std::move(v.comments_); @@ -281,9 +294,9 @@ class basic_value // overwrite comments ---------------------------------------------------- - basic_value(const basic_value& v, std::vector comments) + basic_value(const basic_value& v, std::vector com) : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(comments)) + comments_(std::move(com)) { switch(v.type()) { @@ -301,9 +314,9 @@ class basic_value } } - basic_value(basic_value&& v, std::vector comments) + basic_value(basic_value&& v, std::vector com) : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(comments)) + comments_(std::move(com)) { switch(this->type_) // here this->type_ is already initialized { @@ -359,9 +372,9 @@ class basic_value template class T, template class A> - basic_value(const basic_value& v, std::vector comments) + basic_value(const basic_value& v, std::vector com) : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(comments)) + comments_(std::move(com)) { switch(v.type()) { @@ -443,10 +456,10 @@ class basic_value assigner(this->boolean_, b); return *this; } - basic_value(boolean b, std::vector comments) + basic_value(boolean b, std::vector com) : type_(value_t::boolean), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->boolean_, b); } @@ -478,10 +491,10 @@ class basic_value template, detail::negation>>::value, std::nullptr_t>::type = nullptr> - basic_value(T i, std::vector comments) + basic_value(T i, std::vector com) : type_(value_t::integer), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->integer_, static_cast(i)); } @@ -511,10 +524,10 @@ class basic_value template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, std::vector comments) + basic_value(T f, std::vector com) : type_(value_t::floating), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->floating_, f); } @@ -535,10 +548,10 @@ class basic_value assigner(this->string_, s); return *this; } - basic_value(toml::string s, std::vector comments) + basic_value(toml::string s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, std::move(s)); } @@ -563,17 +576,17 @@ class basic_value { assigner(this->string_, toml::string(std::move(s), kind)); } - basic_value(std::string s, std::vector comments) + basic_value(std::string s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, toml::string(std::move(s))); } - basic_value(std::string s, string_t kind, std::vector comments) + basic_value(std::string s, string_t kind, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, toml::string(std::move(s), kind)); } @@ -598,22 +611,22 @@ class basic_value { assigner(this->string_, toml::string(std::string(s), kind)); } - basic_value(const char* s, std::vector comments) + basic_value(const char* s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, toml::string(std::string(s))); } - basic_value(const char* s, string_t kind, std::vector comments) + basic_value(const char* s, string_t kind, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, toml::string(std::string(s), kind)); } -#if __cplusplus >= 201703L +#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 basic_value(std::string_view s) : type_(value_t::string), region_info_(std::make_shared(region_base{})) @@ -628,10 +641,10 @@ class basic_value assigner(this->string_, toml::string(s)); return *this; } - basic_value(std::string_view s, std::vector comments) + basic_value(std::string_view s, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, toml::string(s)); } @@ -641,10 +654,10 @@ class basic_value { assigner(this->string_, toml::string(s, kind)); } - basic_value(std::string_view s, string_t kind, std::vector comments) + basic_value(std::string_view s, string_t kind, std::vector com) : type_(value_t::string), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->string_, toml::string(s, kind)); } @@ -666,10 +679,10 @@ class basic_value assigner(this->local_date_, ld); return *this; } - basic_value(const local_date& ld, std::vector comments) + basic_value(const local_date& ld, std::vector com) : type_(value_t::local_date), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->local_date_, ld); } @@ -682,10 +695,10 @@ class basic_value { assigner(this->local_time_, lt); } - basic_value(const local_time& lt, std::vector comments) + basic_value(const local_time& lt, std::vector com) : type_(value_t::local_time), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->local_time_, lt); } @@ -707,10 +720,10 @@ class basic_value } template basic_value(const std::chrono::duration& dur, - std::vector comments) + std::vector com) : type_(value_t::local_time), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->local_time_, local_time(dur)); } @@ -732,10 +745,10 @@ class basic_value { assigner(this->local_datetime_, ldt); } - basic_value(const local_datetime& ldt, std::vector comments) + basic_value(const local_datetime& ldt, std::vector com) : type_(value_t::local_datetime), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->local_datetime_, ldt); } @@ -756,10 +769,10 @@ class basic_value { assigner(this->offset_datetime_, odt); } - basic_value(const offset_datetime& odt, std::vector comments) + basic_value(const offset_datetime& odt, std::vector com) : type_(value_t::offset_datetime), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->offset_datetime_, odt); } @@ -778,10 +791,10 @@ class basic_value assigner(this->offset_datetime_, offset_datetime(tp)); } basic_value(const std::chrono::system_clock::time_point& tp, - std::vector comments) + std::vector com) : type_(value_t::offset_datetime), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->offset_datetime_, offset_datetime(tp)); } @@ -802,10 +815,10 @@ class basic_value { assigner(this->array_, ary); } - basic_value(const array_type& ary, std::vector comments) + basic_value(const array_type& ary, std::vector com) : type_(value_t::array), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->array_, ary); } @@ -833,10 +846,10 @@ class basic_value template::value, std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list, std::vector comments) + basic_value(std::initializer_list list, std::vector com) : type_(value_t::array), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { array_type ary(list.begin(), list.end()); assigner(this->array_, std::move(ary)); @@ -876,10 +889,10 @@ class basic_value detail::negation>, detail::is_container >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list, std::vector comments) + basic_value(const T& list, std::vector com) : type_(value_t::array), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { static_assert(std::is_convertible::value, "elements of a container should be convertible to toml::value"); @@ -915,10 +928,10 @@ class basic_value { assigner(this->table_, tab); } - basic_value(const table_type& tab, std::vector comments) + basic_value(const table_type& tab, std::vector com) : type_(value_t::table), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { assigner(this->table_, tab); } @@ -943,10 +956,10 @@ class basic_value } basic_value(std::initializer_list> list, - std::vector comments) + std::vector com) : type_(value_t::table), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { table_type tab; for(const auto& elem : list) {tab[elem.first] = elem.second;} @@ -982,10 +995,10 @@ class basic_value detail::negation>, detail::is_map >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp, std::vector comments) + basic_value(const Map& mp, std::vector com) : type_(value_t::table), region_info_(std::make_shared(region_base{})), - comments_(std::move(comments)) + comments_(std::move(com)) { table_type tab; for(const auto& elem : mp) {tab[elem.first] = elem.second;} @@ -1017,8 +1030,8 @@ class basic_value template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud, std::vector comments) - : basic_value(ud.into_toml(), std::move(comments)) + basic_value(const T& ud, std::vector com) + : basic_value(ud.into_toml(), std::move(com)) {} template::value, std::nullptr_t>::type = nullptr> @@ -1033,8 +1046,8 @@ class basic_value template)> basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} template)> - basic_value(const T& ud, std::vector comments) - : basic_value(::toml::into::into_toml(ud), std::move(comments)) + basic_value(const T& ud, std::vector com) + : basic_value(::toml::into::into_toml(ud), std::move(com)) {} template)> basic_value& operator=(const T& ud) @@ -1047,10 +1060,10 @@ class basic_value // // Those constructors take detail::region that contains parse result. - basic_value(boolean b, detail::region reg) + basic_value(boolean b, detail::region reg, std::vector cm) : type_(value_t::boolean), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->boolean_, b); } @@ -1058,68 +1071,75 @@ class basic_value detail::conjunction< std::is_integral, detail::negation> >::value, std::nullptr_t>::type = nullptr> - basic_value(T i, detail::region reg) + basic_value(T i, detail::region reg, std::vector cm) : type_(value_t::integer), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->integer_, static_cast(i)); } template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, detail::region reg) + basic_value(T f, detail::region reg, std::vector cm) : type_(value_t::floating), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->floating_, static_cast(f)); } - basic_value(toml::string s, detail::region reg) + basic_value(toml::string s, detail::region reg, + std::vector cm) : type_(value_t::string), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->string_, std::move(s)); } - basic_value(const local_date& ld, detail::region reg) + basic_value(const local_date& ld, detail::region reg, + std::vector cm) : type_(value_t::local_date), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->local_date_, ld); } - basic_value(const local_time& lt, detail::region reg) + basic_value(const local_time& lt, detail::region reg, + std::vector cm) : type_(value_t::local_time), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->local_time_, lt); } - basic_value(const local_datetime& ldt, detail::region reg) + basic_value(const local_datetime& ldt, detail::region reg, + std::vector cm) : type_(value_t::local_datetime), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->local_datetime_, ldt); } - basic_value(const offset_datetime& odt, detail::region reg) + basic_value(const offset_datetime& odt, detail::region reg, + std::vector cm) : type_(value_t::offset_datetime), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->offset_datetime_, odt); } - basic_value(const array_type& ary, detail::region reg) + basic_value(const array_type& ary, detail::region reg, + std::vector cm) : type_(value_t::array), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->array_, ary); } - basic_value(const table_type& tab, detail::region reg) + basic_value(const table_type& tab, detail::region reg, + std::vector cm) : type_(value_t::table), region_info_(std::make_shared(std::move(reg))), - comments_(region_info_->comments()) + comments_(std::move(cm)) { assigner(this->table_, tab); } @@ -1127,8 +1147,10 @@ class basic_value template::value, std::nullptr_t>::type = nullptr> - basic_value(std::pair parse_result) - : basic_value(std::move(parse_result.first), std::move(parse_result.second)) + basic_value(std::pair parse_result, std::vector com) + : basic_value(std::move(parse_result.first), + std::move(parse_result.second), + std::move(com)) {} // type checking and casting ============================================ @@ -1692,9 +1714,9 @@ class basic_value { switch(this->type_) { - case value_t::string : {string_.~string(); return;} - case value_t::array : {array_.~array_storage(); return;} - case value_t::table : {table_.~table_storage(); return;} + case value_t::string : {string_.~string(); return;} + case value_t::array : {array_.~array_storage(); return;} + case value_t::table : {table_.~table_storage(); return;} default : return; } } @@ -1730,7 +1752,8 @@ class basic_value }; // default toml::value and default array/table. -using value = basic_value; +// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp +using value = basic_value; using array = typename value::array_type; using table = typename value::table_type; diff --git a/third_party/toml11-3.8.1/toml/version.hpp b/third_party/toml11-3.8.1/toml/version.hpp new file mode 100644 index 0000000..9cbfa39 --- /dev/null +++ b/third_party/toml11-3.8.1/toml/version.hpp @@ -0,0 +1,42 @@ +#ifndef TOML11_VERSION_HPP +#define TOML11_VERSION_HPP + +// This file checks C++ version. + +#ifndef __cplusplus +# error "__cplusplus is not defined" +#endif + +// Since MSVC does not define `__cplusplus` correctly unless you pass +// `/Zc:__cplusplus` when compiling, the workaround macros are added. +// Those enables you to define version manually or to use MSVC specific +// version macro automatically. +// +// The value of `__cplusplus` macro is defined in the C++ standard spec, but +// MSVC ignores the value, maybe because of backward compatibility. Instead, +// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in +// the C++ standard. First we check the manual version definition, and then +// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`. +// +// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170 +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 +// +#if defined(TOML11_ENFORCE_CXX11) +# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L +#elif defined(TOML11_ENFORCE_CXX14) +# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L +#elif defined(TOML11_ENFORCE_CXX17) +# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L +#elif defined(TOML11_ENFORCE_CXX20) +# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L +#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER +# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG +#else +# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus +#endif + +#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900 +# error "toml11 requires C++11 or later." +#endif + +#endif// TOML11_VERSION_HPP