aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/src/release-notes/rl-next.md3
-rw-r--r--src/cpptoml/LICENSE18
-rw-r--r--src/cpptoml/cpptoml.h3668
-rw-r--r--src/libexpr/eval.cc17
-rw-r--r--src/libexpr/eval.hh3
-rw-r--r--src/libexpr/primops/fromTOML.cc111
-rw-r--r--src/nix/why-depends.cc17
-rw-r--r--src/toml11/LICENSE21
-rw-r--r--src/toml11/README.md1966
-rw-r--r--src/toml11/toml.hpp46
-rw-r--r--src/toml11/toml/color.hpp64
-rw-r--r--src/toml11/toml/combinator.hpp306
-rw-r--r--src/toml11/toml/comments.hpp472
-rw-r--r--src/toml11/toml/datetime.hpp631
-rw-r--r--src/toml11/toml/exception.hpp65
-rw-r--r--src/toml11/toml/from.hpp19
-rw-r--r--src/toml11/toml/get.hpp1117
-rw-r--r--src/toml11/toml/into.hpp19
-rw-r--r--src/toml11/toml/lexer.hpp293
-rw-r--r--src/toml11/toml/literal.hpp113
-rw-r--r--src/toml11/toml/macros.hpp121
-rw-r--r--src/toml11/toml/parser.hpp2364
-rw-r--r--src/toml11/toml/region.hpp417
-rw-r--r--src/toml11/toml/result.hpp717
-rw-r--r--src/toml11/toml/serializer.hpp922
-rw-r--r--src/toml11/toml/source_location.hpp233
-rw-r--r--src/toml11/toml/storage.hpp43
-rw-r--r--src/toml11/toml/string.hpp225
-rw-r--r--src/toml11/toml/traits.hpp327
-rw-r--r--src/toml11/toml/types.hpp173
-rw-r--r--src/toml11/toml/utility.hpp149
-rw-r--r--src/toml11/toml/value.hpp2035
32 files changed, 12946 insertions, 3749 deletions
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index c869b5e2f..ac662c132 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1 +1,4 @@
# Release X.Y (202?-??-??)
+
+* The TOML parser used by `builtins.fromTOML` has been replaced by [a
+ more compliant one](https://github.com/ToruNiina/toml11).
diff --git a/src/cpptoml/LICENSE b/src/cpptoml/LICENSE
deleted file mode 100644
index 8802c4fa5..000000000
--- a/src/cpptoml/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-Copyright (c) 2014 Chase Geigle
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/cpptoml/cpptoml.h b/src/cpptoml/cpptoml.h
deleted file mode 100644
index 5a00da3b4..000000000
--- a/src/cpptoml/cpptoml.h
+++ /dev/null
@@ -1,3668 +0,0 @@
-/**
- * @file cpptoml.h
- * @author Chase Geigle
- * @date May 2013
- */
-
-#ifndef CPPTOML_H
-#define CPPTOML_H
-
-#include <algorithm>
-#include <cassert>
-#include <clocale>
-#include <cstdint>
-#include <cstring>
-#include <fstream>
-#include <iomanip>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#if __cplusplus > 201103L
-#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
-#elif defined(__clang__)
-#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
-#elif defined(__GNUG__)
-#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
-#elif defined(_MSC_VER)
-#if _MSC_VER < 1910
-#define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
-#else
-#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
-#endif
-#endif
-
-namespace cpptoml
-{
-class writer; // forward declaration
-class base; // forward declaration
-#if defined(CPPTOML_USE_MAP)
-// a std::map will ensure that entries a sorted, albeit at a slight
-// performance penalty relative to the (default) unordered_map
-using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
-#else
-// by default an unordered_map is used for best performance as the
-// toml specification does not require entries to be sorted
-using string_to_base_map
- = std::unordered_map<std::string, std::shared_ptr<base>>;
-#endif
-
-// if defined, `base` will retain type information in form of an enum class
-// such that static_cast can be used instead of dynamic_cast
-// #define CPPTOML_NO_RTTI
-
-template <class T>
-class option
-{
- public:
- option() : empty_{true}
- {
- // nothing
- }
-
- option(T value) : empty_{false}, value_(std::move(value))
- {
- // nothing
- }
-
- explicit operator bool() const
- {
- return !empty_;
- }
-
- const T& operator*() const
- {
- return value_;
- }
-
- const T* operator->() const
- {
- return &value_;
- }
-
- template <class U>
- T value_or(U&& alternative) const
- {
- if (!empty_)
- return value_;
- return static_cast<T>(std::forward<U>(alternative));
- }
-
- private:
- bool empty_;
- T value_;
-};
-
-struct local_date
-{
- int year = 0;
- int month = 0;
- int day = 0;
-};
-
-struct local_time
-{
- int hour = 0;
- int minute = 0;
- int second = 0;
- int microsecond = 0;
-};
-
-struct zone_offset
-{
- int hour_offset = 0;
- int minute_offset = 0;
-};
-
-struct local_datetime : local_date, local_time
-{
-};
-
-struct offset_datetime : local_datetime, zone_offset
-{
- static inline struct offset_datetime from_zoned(const struct tm& t)
- {
- offset_datetime dt;
- dt.year = t.tm_year + 1900;
- dt.month = t.tm_mon + 1;
- dt.day = t.tm_mday;
- dt.hour = t.tm_hour;
- dt.minute = t.tm_min;
- dt.second = t.tm_sec;
-
- char buf[16];
- strftime(buf, 16, "%z", &t);
-
- int offset = std::stoi(buf);
- dt.hour_offset = offset / 100;
- dt.minute_offset = offset % 100;
- return dt;
- }
-
- CPPTOML_DEPRECATED("from_local has been renamed to from_zoned")
- static inline struct offset_datetime from_local(const struct tm& t)
- {
- return from_zoned(t);
- }
-
- static inline struct offset_datetime from_utc(const struct tm& t)
- {
- offset_datetime dt;
- dt.year = t.tm_year + 1900;
- dt.month = t.tm_mon + 1;
- dt.day = t.tm_mday;
- dt.hour = t.tm_hour;
- dt.minute = t.tm_min;
- dt.second = t.tm_sec;
- return dt;
- }
-};
-
-CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime")
-typedef offset_datetime datetime;
-
-class fill_guard
-{
- public:
- fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}
- {
- // nothing
- }
-
- ~fill_guard()
- {
- os_.fill(fill_);
- }
-
- private:
- std::ostream& os_;
- std::ostream::char_type fill_;
-};
-
-inline std::ostream& operator<<(std::ostream& os, const local_date& dt)
-{
- fill_guard g{os};
- os.fill('0');
-
- using std::setw;
- os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2)
- << dt.day;
-
- return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const local_time& ltime)
-{
- fill_guard g{os};
- os.fill('0');
-
- using std::setw;
- os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":"
- << setw(2) << ltime.second;
-
- if (ltime.microsecond > 0)
- {
- os << ".";
- int power = 100000;
- for (int curr_us = ltime.microsecond; curr_us; power /= 10)
- {
- auto num = curr_us / power;
- os << num;
- curr_us -= num * power;
- }
- }
-
- return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo)
-{
- fill_guard g{os};
- os.fill('0');
-
- using std::setw;
-
- if (zo.hour_offset != 0 || zo.minute_offset != 0)
- {
- if (zo.hour_offset > 0)
- {
- os << "+";
- }
- else
- {
- os << "-";
- }
- os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2)
- << std::abs(zo.minute_offset);
- }
- else
- {
- os << "Z";
- }
-
- return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
-{
- return os << static_cast<const local_date&>(dt) << "T"
- << static_cast<const local_time&>(dt);
-}
-
-inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
-{
- return os << static_cast<const local_datetime&>(dt)
- << static_cast<const zone_offset&>(dt);
-}
-
-template <class T, class... Ts>
-struct is_one_of;
-
-template <class T, class V>
-struct is_one_of<T, V> : std::is_same<T, V>
-{
-};
-
-template <class T, class V, class... Ts>
-struct is_one_of<T, V, Ts...>
-{
- const static bool value
- = std::is_same<T, V>::value || is_one_of<T, Ts...>::value;
-};
-
-template <class T>
-class value;
-
-template <class T>
-struct valid_value
- : is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,
- local_datetime, offset_datetime>
-{
-};
-
-template <class T, class Enable = void>
-struct value_traits;
-
-template <class T>
-struct valid_value_or_string_convertible
-{
-
- const static bool value = valid_value<typename std::decay<T>::type>::value
- || std::is_convertible<T, std::string>::value;
-};
-
-template <class T>
-struct value_traits<T, typename std::enable_if<
- valid_value_or_string_convertible<T>::value>::type>
-{
- using value_type = typename std::conditional<
- valid_value<typename std::decay<T>::type>::value,
- typename std::decay<T>::type, std::string>::type;
-
- using type = value<value_type>;
-
- static value_type construct(T&& val)
- {
- return value_type(val);
- }
-};
-
-template <class T>
-struct value_traits<
- T,
- typename std::enable_if<
- !valid_value_or_string_convertible<T>::value
- && std::is_floating_point<typename std::decay<T>::type>::value>::type>
-{
- using value_type = typename std::decay<T>::type;
-
- using type = value<double>;
-
- static value_type construct(T&& val)
- {
- return value_type(val);
- }
-};
-
-template <class T>
-struct value_traits<
- T, typename std::enable_if<
- !valid_value_or_string_convertible<T>::value
- && !std::is_floating_point<typename std::decay<T>::type>::value
- && std::is_signed<typename std::decay<T>::type>::value>::type>
-{
- using value_type = int64_t;
-
- using type = value<int64_t>;
-
- static value_type construct(T&& val)
- {
- if (val < (std::numeric_limits<int64_t>::min)())
- throw std::underflow_error{"constructed value cannot be "
- "represented by a 64-bit signed "
- "integer"};
-
- if (val > (std::numeric_limits<int64_t>::max)())
- throw std::overflow_error{"constructed value cannot be represented "
- "by a 64-bit signed integer"};
-
- return static_cast<int64_t>(val);
- }
-};
-
-template <class T>
-struct value_traits<
- T, typename std::enable_if<
- !valid_value_or_string_convertible<T>::value
- && std::is_unsigned<typename std::decay<T>::type>::value>::type>
-{
- using value_type = int64_t;
-
- using type = value<int64_t>;
-
- static value_type construct(T&& val)
- {
- if (val > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))
- throw std::overflow_error{"constructed value cannot be represented "
- "by a 64-bit signed integer"};
-
- return static_cast<int64_t>(val);
- }
-};
-
-class array;
-class table;
-class table_array;
-
-template <class T>
-struct array_of_trait
-{
- using return_type = option<std::vector<T>>;
-};
-
-template <>
-struct array_of_trait<array>
-{
- using return_type = option<std::vector<std::shared_ptr<array>>>;
-};
-
-template <class T>
-inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
-inline std::shared_ptr<array> make_array();
-
-namespace detail
-{
-template <class T>
-inline std::shared_ptr<T> make_element();
-}
-
-inline std::shared_ptr<table> make_table();
-inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);
-
-#if defined(CPPTOML_NO_RTTI)
-/// Base type used to store underlying data type explicitly if RTTI is disabled
-enum class base_type
-{
- NONE,
- STRING,
- LOCAL_TIME,
- LOCAL_DATE,
- LOCAL_DATETIME,
- OFFSET_DATETIME,
- INT,
- FLOAT,
- BOOL,
- TABLE,
- ARRAY,
- TABLE_ARRAY
-};
-
-/// Type traits class to convert C++ types to enum member
-template <class T>
-struct base_type_traits;
-
-template <>
-struct base_type_traits<std::string>
-{
- static const base_type type = base_type::STRING;
-};
-
-template <>
-struct base_type_traits<local_time>
-{
- static const base_type type = base_type::LOCAL_TIME;
-};
-
-template <>
-struct base_type_traits<local_date>
-{
- static const base_type type = base_type::LOCAL_DATE;
-};
-
-template <>
-struct base_type_traits<local_datetime>
-{
- static const base_type type = base_type::LOCAL_DATETIME;
-};
-
-template <>
-struct base_type_traits<offset_datetime>
-{
- static const base_type type = base_type::OFFSET_DATETIME;
-};
-
-template <>
-struct base_type_traits<int64_t>
-{
- static const base_type type = base_type::INT;
-};
-
-template <>
-struct base_type_traits<double>
-{
- static const base_type type = base_type::FLOAT;
-};
-
-template <>
-struct base_type_traits<bool>
-{
- static const base_type type = base_type::BOOL;
-};
-
-template <>
-struct base_type_traits<table>
-{
- static const base_type type = base_type::TABLE;
-};
-
-template <>
-struct base_type_traits<array>
-{
- static const base_type type = base_type::ARRAY;
-};
-
-template <>
-struct base_type_traits<table_array>
-{
- static const base_type type = base_type::TABLE_ARRAY;
-};
-#endif
-
-/**
- * A generic base TOML value used for type erasure.
- */
-class base : public std::enable_shared_from_this<base>
-{
- public:
- virtual ~base() = default;
-
- virtual std::shared_ptr<base> clone() const = 0;
-
- /**
- * Determines if the given TOML element is a value.
- */
- virtual bool is_value() const
- {
- return false;
- }
-
- /**
- * Determines if the given TOML element is a table.
- */
- virtual bool is_table() const
- {
- return false;
- }
-
- /**
- * Converts the TOML element into a table.
- */
- std::shared_ptr<table> as_table()
- {
- if (is_table())
- return std::static_pointer_cast<table>(shared_from_this());
- return nullptr;
- }
- /**
- * Determines if the TOML element is an array of "leaf" elements.
- */
- virtual bool is_array() const
- {
- return false;
- }
-
- /**
- * Converts the TOML element to an array.
- */
- std::shared_ptr<array> as_array()
- {
- if (is_array())
- return std::static_pointer_cast<array>(shared_from_this());
- return nullptr;
- }
-
- /**
- * Determines if the given TOML element is an array of tables.
- */
- virtual bool is_table_array() const
- {
- return false;
- }
-
- /**
- * Converts the TOML element into a table array.
- */
- std::shared_ptr<table_array> as_table_array()
- {
- if (is_table_array())
- return std::static_pointer_cast<table_array>(shared_from_this());
- return nullptr;
- }
-
- /**
- * Attempts to coerce the TOML element into a concrete TOML value
- * of type T.
- */
- template <class T>
- std::shared_ptr<value<T>> as();
-
- template <class T>
- std::shared_ptr<const value<T>> as() const;
-
- template <class Visitor, class... Args>
- void accept(Visitor&& visitor, Args&&... args) const;
-
-#if defined(CPPTOML_NO_RTTI)
- base_type type() const
- {
- return type_;
- }
-
- protected:
- base(const base_type t) : type_(t)
- {
- // nothing
- }
-
- private:
- const base_type type_ = base_type::NONE;
-
-#else
- protected:
- base()
- {
- // nothing
- }
-#endif
-};
-
-/**
- * A concrete TOML value representing the "leaves" of the "tree".
- */
-template <class T>
-class value : public base
-{
- struct make_shared_enabler
- {
- // nothing; this is a private key accessible only to friends
- };
-
- template <class U>
- friend std::shared_ptr<typename value_traits<U>::type>
- cpptoml::make_value(U&& val);
-
- public:
- static_assert(valid_value<T>::value, "invalid value type");
-
- std::shared_ptr<base> clone() const override;
-
- value(const make_shared_enabler&, const T& val) : value(val)
- {
- // nothing; note that users cannot actually invoke this function
- // because they lack access to the make_shared_enabler.
- }
-
- bool is_value() const override
- {
- return true;
- }
-
- /**
- * Gets the data associated with this value.
- */
- T& get()
- {
- return data_;
- }
-
- /**
- * Gets the data associated with this value. Const version.
- */
- const T& get() const
- {
- return data_;
- }
-
- private:
- T data_;
-
- /**
- * Constructs a value from the given data.
- */
-#if defined(CPPTOML_NO_RTTI)
- value(const T& val) : base(base_type_traits<T>::type), data_(val)
- {
- }
-#else
- value(const T& val) : data_(val)
- {
- }
-#endif
-
- value(const value& val) = delete;
- value& operator=(const value& val) = delete;
-};
-
-template <class T>
-std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
-{
- using value_type = typename value_traits<T>::type;
- using enabler = typename value_type::make_shared_enabler;
- return std::make_shared<value_type>(
- enabler{}, value_traits<T>::construct(std::forward<T>(val)));
-}
-
-template <class T>
-inline std::shared_ptr<value<T>> base::as()
-{
-#if defined(CPPTOML_NO_RTTI)
- if (type() == base_type_traits<T>::type)
- return std::static_pointer_cast<value<T>>(shared_from_this());
- else
- return nullptr;
-#else
- return std::dynamic_pointer_cast<value<T>>(shared_from_this());
-#endif
-}
-
-// special case value<double> to allow getting an integer parameter as a
-// double value
-template <>
-inline std::shared_ptr<value<double>> base::as()
-{
-#if defined(CPPTOML_NO_RTTI)
- if (type() == base_type::FLOAT)
- return std::static_pointer_cast<value<double>>(shared_from_this());
-
- if (type() == base_type::INT)
- {
- auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
- return make_value<double>(static_cast<double>(v->get()));
- }
-#else
- if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
- return v;
-
- if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))
- return make_value<double>(static_cast<double>(v->get()));
-#endif
-
- return nullptr;
-}
-
-template <class T>
-inline std::shared_ptr<const value<T>> base::as() const
-{
-#if defined(CPPTOML_NO_RTTI)
- if (type() == base_type_traits<T>::type)
- return std::static_pointer_cast<const value<T>>(shared_from_this());
- else
- return nullptr;
-#else
- return std::dynamic_pointer_cast<const value<T>>(shared_from_this());
-#endif
-}
-
-// special case value<double> to allow getting an integer parameter as a
-// double value
-template <>
-inline std::shared_ptr<const value<double>> base::as() const
-{
-#if defined(CPPTOML_NO_RTTI)
- if (type() == base_type::FLOAT)
- return std::static_pointer_cast<const value<double>>(
- shared_from_this());
-
- if (type() == base_type::INT)
- {
- auto v = as<int64_t>();
- // the below has to be a non-const value<double> due to a bug in
- // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
- return make_value<double>(static_cast<double>(v->get()));
- }
-#else
- if (auto v
- = std::dynamic_pointer_cast<const value<double>>(shared_from_this()))
- return v;
-
- if (auto v = as<int64_t>())
- {
- // the below has to be a non-const value<double> due to a bug in
- // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843
- return make_value<double>(static_cast<double>(v->get()));
- }
-#endif
-
- return nullptr;
-}
-
-/**
- * Exception class for array insertion errors.
- */
-class array_exception : public std::runtime_error
-{
- public:
- array_exception(const std::string& err) : std::runtime_error{err}
- {
- }
-};
-
-class array : public base
-{
- public:
- friend std::shared_ptr<array> make_array();
-
- std::shared_ptr<base> clone() const override;
-
- virtual bool is_array() const override
- {
- return true;
- }
-
- using size_type = std::size_t;
-
- /**
- * arrays can be iterated over
- */
- using iterator = std::vector<std::shared_ptr<base>>::iterator;
-
- /**
- * arrays can be iterated over. Const version.
- */
- using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator;
-
- iterator begin()
- {
- return values_.begin();
- }
-
- const_iterator begin() const
- {
- return values_.begin();
- }
-
- iterator end()
- {
- return values_.end();
- }
-
- const_iterator end() const
- {
- return values_.end();
- }
-
- /**
- * Obtains the array (vector) of base values.
- */
- std::vector<std::shared_ptr<base>>& get()
- {
- return values_;
- }
-
- /**
- * Obtains the array (vector) of base values. Const version.
- */
- const std::vector<std::shared_ptr<base>>& get() const
- {
- return values_;
- }
-
- std::shared_ptr<base> at(size_t idx) const
- {
- return values_.at(idx);
- }
-
- /**
- * Obtains an array of value<T>s. Note that elements may be
- * nullptr if they cannot be converted to a value<T>.
- */
- template <class T>
- std::vector<std::shared_ptr<value<T>>> array_of() const
- {
- std::vector<std::shared_ptr<value<T>>> result(values_.size());
-
- std::transform(values_.begin(), values_.end(), result.begin(),
- [&](std::shared_ptr<base> v) { return v->as<T>(); });
-
- return result;
- }
-
- /**
- * Obtains a option<vector<T>>. The option will be empty if the array
- * contains values that are not of type T.
- */
- template <class T>
- inline typename array_of_trait<T>::return_type get_array_of() const
- {
- std::vector<T> result;
- result.reserve(values_.size());
-
- for (const auto& val : values_)
- {
- if (auto v = val->as<T>())
- result.push_back(v->get());
- else
- return {};
- }
-
- return {std::move(result)};
- }
-
- /**
- * Obtains an array of arrays. Note that elements may be nullptr
- * if they cannot be converted to a array.
- */
- std::vector<std::shared_ptr<array>> nested_array() const
- {
- std::vector<std::shared_ptr<array>> result(values_.size());
-
- std::transform(values_.begin(), values_.end(), result.begin(),
- [&](std::shared_ptr<base> v) -> std::shared_ptr<array> {
- if (v->is_array())
- return std::static_pointer_cast<array>(v);
- return std::shared_ptr<array>{};
- });
-
- return result;
- }
-
- /**
- * Add a value to the end of the array
- */
- template <class T>
- void push_back(const std::shared_ptr<value<T>>& val)
- {
- if (values_.empty() || values_[0]->as<T>())
- {
- values_.push_back(val);
- }
- else
- {
- throw array_exception{"Arrays must be homogenous."};
- }
- }
-
- /**
- * Add an array to the end of the array
- */
- void push_back(const std::shared_ptr<array>& val)
- {
- if (values_.empty() || values_[0]->is_array())
- {
- values_.push_back(val);
- }
- else
- {
- throw array_exception{"Arrays must be homogenous."};
- }
- }
-
- /**
- * Convenience function for adding a simple element to the end
- * of the array.
- */
- template <class T>
- void push_back(T&& val, typename value_traits<T>::type* = 0)
- {
- push_back(make_value(std::forward<T>(val)));
- }
-
- /**
- * Insert a value into the array
- */
- template <class T>
- iterator insert(iterator position, const std::shared_ptr<value<T>>& value)
- {
- if (values_.empty() || values_[0]->as<T>())
- {
- return values_.insert(position, value);
- }
- else
- {
- throw array_exception{"Arrays must be homogenous."};
- }
- }
-
- /**
- * Insert an array into the array
- */
- iterator insert(iterator position, const std::shared_ptr<array>& value)
- {
- if (values_.empty() || values_[0]->is_array())
- {
- return values_.insert(position, value);
- }
- else
- {
- throw array_exception{"Arrays must be homogenous."};
- }
- }
-
- /**
- * Convenience function for inserting a simple element in the array
- */
- template <class T>
- iterator insert(iterator position, T&& val,
- typename value_traits<T>::type* = 0)
- {
- return insert(position, make_value(std::forward<T>(val)));
- }
-
- /**
- * Erase an element from the array
- */
- iterator erase(iterator position)
- {
- return values_.erase(position);
- }
-
- /**
- * Clear the array
- */
- void clear()
- {
- values_.clear();
- }
-
- /**
- * Reserve space for n values.
- */
- void reserve(size_type n)
- {
- values_.reserve(n);
- }
-
- private:
-#if defined(CPPTOML_NO_RTTI)
- array() : base(base_type::ARRAY)
- {
- // empty
- }
-#else
- array() = default;
-#endif
-
- template <class InputIterator>
- array(InputIterator begin, InputIterator end) : values_{begin, end}
- {
- // nothing
- }
-
- array(const array& obj) = delete;
- array& operator=(const array& obj) = delete;
-
- std::vector<std::shared_ptr<base>> values_;
-};
-
-inline std::shared_ptr<array> make_array()
-{
- struct make_shared_enabler : public array
- {
- make_shared_enabler()
- {
- // nothing
- }
- };
-
- return std::make_shared<make_shared_enabler>();
-}
-
-namespace detail
-{
-template <>
-inline std::shared_ptr<array> make_element<array>()
-{
- return make_array();
-}
-} // namespace detail
-
-/**
- * Obtains a option<vector<T>>. The option will be empty if the array
- * contains values that are not of type T.
- */
-template <>
-inline typename array_of_trait<array>::return_type
-array::get_array_of<array>() const
-{
- std::vector<std::shared_ptr<array>> result;
- result.reserve(values_.size());
-
- for (const auto& val : values_)
- {
- if (auto v = val->as_array())
- result.push_back(v);
- else
- return {};
- }
-
- return {std::move(result)};
-}
-
-class table;
-
-class table_array : public base
-{
- friend class table;
- friend std::shared_ptr<table_array> make_table_array(bool);
-
- public:
- std::shared_ptr<base> clone() const override;
-
- using size_type = std::size_t;
-
- /**
- * arrays can be iterated over
- */
- using iterator = std::vector<std::shared_ptr<table>>::iterator;
-
- /**
- * arrays can be iterated over. Const version.
- */
- using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator;
-
- iterator begin()
- {
- return array_.begin();
- }
-
- const_iterator begin() const
- {
- return array_.begin();
- }
-
- iterator end()
- {
- return array_.end();
- }
-
- const_iterator end() const
- {
- return array_.end();
- }
-
- virtual bool is_table_array() const override
- {
- return true;
- }
-
- std::vector<std::shared_ptr<table>>& get()
- {
- return array_;
- }
-
- const std::vector<std::shared_ptr<table>>& get() const
- {
- return array_;
- }
-
- /**
- * Add a table to the end of the array
- */
- void push_back(const std::shared_ptr<table>& val)
- {
- array_.push_back(val);
- }
-
- /**
- * Insert a table into the array
- */
- iterator insert(iterator position, const std::shared_ptr<table>& value)
- {
- return array_.insert(position, value);
- }
-
- /**
- * Erase an element from the array
- */
- iterator erase(iterator position)
- {
- return array_.erase(position);
- }
-
- /**
- * Clear the array
- */
- void clear()
- {
- array_.clear();
- }
-
- /**
- * Reserve space for n tables.
- */
- void reserve(size_type n)
- {
- array_.reserve(n);
- }
-
- /**
- * Whether or not the table array is declared inline. This mostly
- * matters for parsing, where statically defined arrays cannot be
- * appended to using the array-of-table syntax.
- */
- bool is_inline() const
- {
- return is_inline_;
- }
-
- private:
-#if defined(CPPTOML_NO_RTTI)
- table_array(bool is_inline = false)
- : base(base_type::TABLE_ARRAY), is_inline_(is_inline)
- {
- // nothing
- }
-#else
- table_array(bool is_inline = false) : is_inline_(is_inline)
- {
- // nothing
- }
-#endif
-
- table_array(const table_array& obj) = delete;
- table_array& operator=(const table_array& rhs) = delete;
-
- std::vector<std::shared_ptr<table>> array_;
- const bool is_inline_ = false;
-};
-
-inline std::shared_ptr<table_array> make_table_array(bool is_inline)
-{
- struct make_shared_enabler : public table_array
- {
- make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline)
- {
- // nothing
- }
- };
-
- return std::make_shared<make_shared_enabler>(is_inline);
-}
-
-namespace detail
-{
-template <>
-inline std::shared_ptr<table_array> make_element<table_array>()
-{
- return make_table_array(true);
-}
-} // namespace detail
-
-// The below are overloads for fetching specific value types out of a value
-// where special casting behavior (like bounds checking) is desired
-
-template <class T>
-typename std::enable_if<!std::is_floating_point<T>::value
- && std::is_signed<T>::value,
- option<T>>::type
-get_impl(const std::shared_ptr<base>& elem)
-{
- if (auto v = elem->as<int64_t>())
- {
- if (v->get() < (std::numeric_limits<T>::min)())
- throw std::underflow_error{
- "T cannot represent the value requested in get"};
-
- if (v->get() > (std::numeric_limits<T>::max)())
- throw std::overflow_error{
- "T cannot represent the value requested in get"};
-
- return {static_cast<T>(v->get())};
- }
- else
- {
- return {};
- }
-}
-
-template <class T>
-typename std::enable_if<!std::is_same<T, bool>::value
- && std::is_unsigned<T>::value,
- option<T>>::type
-get_impl(const std::shared_ptr<base>& elem)
-{
- if (auto v = elem->as<int64_t>())
- {
- if (v->get() < 0)
- throw std::underflow_error{"T cannot store negative value in get"};
-
- if (static_cast<uint64_t>(v->get()) > (std::numeric_limits<T>::max)())
- throw std::overflow_error{
- "T cannot represent the value requested in get"};
-
- return {static_cast<T>(v->get())};
- }
- else
- {
- return {};
- }
-}
-
-template <class T>
-typename std::enable_if<!std::is_integral<T>::value
- || std::is_same<T, bool>::value,
- option<T>>::type
-get_impl(const std::shared_ptr<base>& elem)
-{
- if (auto v = elem->as<T>())
- {
- return {v->get()};
- }
- else
- {
- return {};
- }
-}
-
-/**
- * Represents a TOML keytable.
- */
-class table : public base
-{
- public:
- friend class table_array;
- friend std::shared_ptr<table> make_table();
-
- std::shared_ptr<base> clone() const override;
-
- /**
- * tables can be iterated over.
- */
- using iterator = string_to_base_map::iterator;
-
- /**
- * tables can be iterated over. Const version.
- */
- using const_iterator = string_to_base_map::const_iterator;
-
- iterator begin()
- {
- return map_.begin();
- }
-
- const_iterator begin() const
- {
- return map_.begin();
- }
-
- iterator end()
- {
- return map_.end();
- }
-
- const_iterator end() const
- {
- return map_.end();
- }
-
- bool is_table() const override
- {
- return true;
- }
-
- bool empty() const
- {
- return map_.empty();
- }
-
- /**
- * Determines if this key table contains the given key.
- */
- bool contains(const std::string& key) const
- {
- return map_.find(key) != map_.end();
- }
-
- /**
- * Determines if this key table contains the given key. Will
- * resolve "qualified keys". Qualified keys are the full access
- * path separated with dots like "grandparent.parent.child".
- */
- bool contains_qualified(const std::string& key) const
- {
- return resolve_qualified(key);
- }
-
- /**
- * Obtains the base for a given key.
- * @throw std::out_of_range if the key does not exist
- */
- std::shared_ptr<base> get(const std::string& key) const
- {
- return map_.at(key);
- }
-
- /**
- * Obtains the base for a given key. Will resolve "qualified
- * keys". Qualified keys are the full access path separated with
- * dots like "grandparent.parent.child".
- *
- * @throw std::out_of_range if the key does not exist
- */
- std::shared_ptr<base> get_qualified(const std::string& key) const
- {
- std::shared_ptr<base> p;
- resolve_qualified(key, &p);
- return p;
- }
-
- /**
- * Obtains a table for a given key, if possible.
- */
- std::shared_ptr<table> get_table(const std::string& key) const
- {
- if (contains(key) && get(key)->is_table())
- return std::static_pointer_cast<table>(get(key));
- return nullptr;
- }
-
- /**
- * Obtains a table for a given key, if possible. Will resolve
- * "qualified keys".
- */
- std::shared_ptr<table> get_table_qualified(const std::string& key) const
- {
- if (contains_qualified(key) && get_qualified(key)->is_table())
- return std::static_pointer_cast<table>(get_qualified(key));
- return nullptr;
- }
-
- /**
- * Obtains an array for a given key.
- */
- std::shared_ptr<array> get_array(const std::string& key) const
- {
- if (!contains(key))
- return nullptr;
- return get(key)->as_array();
- }
-
- /**
- * Obtains an array for a given key. Will resolve "qualified keys".
- */
- std::shared_ptr<array> get_array_qualified(const std::string& key) const
- {
- if (!contains_qualified(key))
- return nullptr;
- return get_qualified(key)->as_array();
- }
-
- /**
- * Obtains a table_array for a given key, if possible.
- */
- std::shared_ptr<table_array> get_table_array(const std::string& key) const
- {
- if (!contains(key))
- return nullptr;
- return get(key)->as_table_array();
- }
-
- /**
- * Obtains a table_array for a given key, if possible. Will resolve
- * "qualified keys".
- */
- std::shared_ptr<table_array>
- get_table_array_qualified(const std::string& key) const
- {
- if (!contains_qualified(key))
- return nullptr;
- return get_qualified(key)->as_table_array();
- }
-
- /**
- * Helper function that attempts to get a value corresponding
- * to the template parameter from a given key.
- */
- template <class T>
- option<T> get_as(const std::string& key) const
- {
- try
- {
- return get_impl<T>(get(key));
- }
- catch (const std::out_of_range&)
- {
- return {};
- }
- }
-
- /**
- * Helper function that attempts to get a value corresponding
- * to the template parameter from a given key. Will resolve "qualified
- * keys".
- */
- template <class T>
- option<T> get_qualified_as(const std::string& key) const
- {
- try
- {
- return get_impl<T>(get_qualified(key));
- }
- catch (const std::out_of_range&)
- {
- return {};
- }
- }
-
- /**
- * Helper function that attempts to get an array of values of a given
- * type corresponding to the template parameter for a given key.
- *
- * If the key doesn't exist, doesn't exist as an array type, or one or
- * more keys inside the array type are not of type T, an empty option
- * is returned. Otherwise, an option containing a vector of the values
- * is returned.
- */
- template <class T>
- inline typename array_of_trait<T>::return_type
- get_array_of(const std::string& key) const
- {
- if (auto v = get_array(key))
- {
- std::vector<T> result;
- result.reserve(v->get().size());
-
- for (const auto& b : v->get())
- {
- if (auto val = b->as<T>())
- result.push_back(val->get());
- else
- return {};
- }
- return {std::move(result)};
- }
-
- return {};
- }
-
- /**
- * Helper function that attempts to get an array of values of a given
- * type corresponding to the template parameter for a given key. Will
- * resolve "qualified keys".
- *
- * If the key doesn't exist, doesn't exist as an array type, or one or
- * more keys inside the array type are not of type T, an empty option
- * is returned. Otherwise, an option containing a vector of the values
- * is returned.
- */
- template <class T>
- inline typename array_of_trait<T>::return_type
- get_qualified_array_of(const std::string& key) const
- {
- if (auto v = get_array_qualified(key))
- {
- std::vector<T> result;
- result.reserve(v->get().size());
-
- for (const auto& b : v->get())
- {
- if (auto val = b->as<T>())
- result.push_back(val->get());
- else
- return {};
- }
- return {std::move(result)};
- }
-
- return {};
- }
-
- /**
- * Adds an element to the keytable.
- */
- void insert(const std::string& key, const std::shared_ptr<base>& value)
- {
- map_[key] = value;
- }
-
- /**
- * Convenience shorthand for adding a simple element to the
- * keytable.
- */
- template <class T>
- void insert(const std::string& key, T&& val,
- typename value_traits<T>::type* = 0)
- {
- insert(key, make_value(std::forward<T>(val)));
- }
-
- /**
- * Removes an element from the table.
- */
- void erase(const std::string& key)
- {
- map_.erase(key);
- }
-
- private:
-#if defined(CPPTOML_NO_RTTI)
- table() : base(base_type::TABLE)
- {
- // nothing
- }
-#else
- table()
- {
- // nothing
- }
-#endif
-
- table(const table& obj) = delete;
- table& operator=(const table& rhs) = delete;
-
- std::vector<std::string> split(const std::string& value,
- char separator) const
- {
- std::vector<std::string> result;
- std::string::size_type p = 0;
- std::string::size_type q;
- while ((q = value.find(separator, p)) != std::string::npos)
- {
- result.emplace_back(value, p, q - p);
- p = q + 1;
- }
- result.emplace_back(value, p);
- return result;
- }
-
- // If output parameter p is specified, fill it with the pointer to the
- // specified entry and throw std::out_of_range if it couldn't be found.
- //
- // Otherwise, just return true if the entry could be found or false
- // otherwise and do not throw.
- bool resolve_qualified(const std::string& key,
- std::shared_ptr<base>* p = nullptr) const
- {
- auto parts = split(key, '.');
- auto last_key = parts.back();
- parts.pop_back();
-
- auto cur_table = this;
- for (const auto& part : parts)
- {
- cur_table = cur_table->get_table(part).get();
- if (!cur_table)
- {
- if (!p)
- return false;
-
- throw std::out_of_range{key + " is not a valid key"};
- }
- }
-
- if (!p)
- return cur_table->map_.count(last_key) != 0;
-
- *p = cur_table->map_.at(last_key);
- return true;
- }
-
- string_to_base_map map_;
-};
-
-/**
- * Helper function that attempts to get an array of arrays for a given
- * key.
- *
- * If the key doesn't exist, doesn't exist as an array type, or one or
- * more keys inside the array type are not of type T, an empty option
- * is returned. Otherwise, an option containing a vector of the values
- * is returned.
- */
-template <>
-inline typename array_of_trait<array>::return_type
-table::get_array_of<array>(const std::string& key) const
-{
- if (auto v = get_array(key))
- {
- std::vector<std::shared_ptr<array>> result;
- result.reserve(v->get().size());
-
- for (const auto& b : v->get())
- {
- if (auto val = b->as_array())
- result.push_back(val);
- else
- return {};
- }
-
- return {std::move(result)};
- }
-
- return {};
-}
-
-/**
- * Helper function that attempts to get an array of arrays for a given
- * key. Will resolve "qualified keys".
- *
- * If the key doesn't exist, doesn't exist as an array type, or one or
- * more keys inside the array type are not of type T, an empty option
- * is returned. Otherwise, an option containing a vector of the values
- * is returned.
- */
-template <>
-inline typename array_of_trait<array>::return_type
-table::get_qualified_array_of<array>(const std::string& key) const
-{
- if (auto v = get_array_qualified(key))
- {
- std::vector<std::shared_ptr<array>> result;
- result.reserve(v->get().size());
-
- for (const auto& b : v->get())
- {
- if (auto val = b->as_array())
- result.push_back(val);
- else
- return {};
- }
-
- return {std::move(result)};
- }
-
- return {};
-}
-
-std::shared_ptr<table> make_table()
-{
- struct make_shared_enabler : public table
- {
- make_shared_enabler()
- {
- // nothing
- }
- };
-
- return std::make_shared<make_shared_enabler>();
-}
-
-namespace detail
-{
-template <>
-inline std::shared_ptr<table> make_element<table>()
-{
- return make_table();
-}
-} // namespace detail
-
-template <class T>
-std::shared_ptr<base> value<T>::clone() const
-{
- return make_value(data_);
-}
-
-inline std::shared_ptr<base> array::clone() const
-{
- auto result = make_array();
- result->reserve(values_.size());
- for (const auto& ptr : values_)
- result->values_.push_back(ptr->clone());
- return result;
-}
-
-inline std::shared_ptr<base> table_array::clone() const
-{
- auto result = make_table_array(is_inline());
- result->reserve(array_.size());
- for (const auto& ptr : array_)
- result->array_.push_back(ptr->clone()->as_table());
- return result;
-}
-
-inline std::shared_ptr<base> table::clone() const
-{
- auto result = make_table();
- for (const auto& pr : map_)
- result->insert(pr.first, pr.second->clone());
- return result;
-}
-
-/**
- * Exception class for all TOML parsing errors.
- */
-class parse_exception : public std::runtime_error
-{
- public:
- parse_exception(const std::string& err) : std::runtime_error{err}
- {
- }
-
- parse_exception(const std::string& err, std::size_t line_number)
- : std::runtime_error{err + " at line " + std::to_string(line_number)}
- {
- }
-};
-
-inline bool is_number(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-inline bool is_hex(char c)
-{
- return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-/**
- * Helper object for consuming expected characters.
- */
-template <class OnError>
-class consumer
-{
- public:
- consumer(std::string::iterator& it, const std::string::iterator& end,
- OnError&& on_error)
- : it_(it), end_(end), on_error_(std::forward<OnError>(on_error))
- {
- // nothing
- }
-
- void operator()(char c)
- {
- if (it_ == end_ || *it_ != c)
- on_error_();
- ++it_;
- }
-
- template <std::size_t N>
- void operator()(const char (&str)[N])
- {
- std::for_each(std::begin(str), std::end(str) - 1,
- [&](char c) { (*this)(c); });
- }
-
- void eat_or(char a, char b)
- {
- if (it_ == end_ || (*it_ != a && *it_ != b))
- on_error_();
- ++it_;
- }
-
- int eat_digits(int len)
- {
- int val = 0;
- for (int i = 0; i < len; ++i)
- {
- if (!is_number(*it_) || it_ == end_)
- on_error_();
- val = 10 * val + (*it_++ - '0');
- }
- return val;
- }
-
- void error()
- {
- on_error_();
- }
-
- private:
- std::string::iterator& it_;
- const std::string::iterator& end_;
- OnError on_error_;
-};
-
-template <class OnError>
-consumer<OnError> make_consumer(std::string::iterator& it,
- const std::string::iterator& end,
- OnError&& on_error)
-{
- return consumer<OnError>(it, end, std::forward<OnError>(on_error));
-}
-
-// replacement for std::getline to handle incorrectly line-ended files
-// https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
-namespace detail
-{
-inline std::istream& getline(std::istream& input, std::string& line)
-{
- line.clear();
-
- std::istream::sentry sentry{input, true};
- auto sb = input.rdbuf();
-
- while (true)
- {
- auto c = sb->sbumpc();
- if (c == '\r')
- {
- if (sb->sgetc() == '\n')
- c = sb->sbumpc();
- }
-
- if (c == '\n')
- return input;
-
- if (c == std::istream::traits_type::eof())
- {
- if (line.empty())
- input.setstate(std::ios::eofbit);
- return input;
- }
-
- line.push_back(static_cast<char>(c));
- }
-}
-} // namespace detail
-
-/**
- * The parser class.
- */
-class parser
-{
- public:
- /**
- * Parsers are constructed from streams.
- */
- parser(std::istream& stream) : input_(stream)
- {
- // nothing
- }
-
- parser& operator=(const parser& parser) = delete;
-
- /**
- * Parses the stream this parser was created on until EOF.
- * @throw parse_exception if there are errors in parsing
- */
- std::shared_ptr<table> parse()
- {
- std::shared_ptr<table> root = make_table();
-
- table* curr_table = root.get();
-
- while (detail::getline(input_, line_))
- {
- line_number_++;
- auto it = line_.begin();
- auto end = line_.end();
- consume_whitespace(it, end);
- if (it == end || *it == '#')
- continue;
- if (*it == '[')
- {
- curr_table = root.get();
- parse_table(it, end, curr_table);
- }
- else
- {
- parse_key_value(it, end, curr_table);
- consume_whitespace(it, end);
- eol_or_comment(it, end);
- }
- }
- return root;
- }
-
- private:
-#if defined _MSC_VER
- __declspec(noreturn)
-#elif defined __GNUC__
- __attribute__((noreturn))
-#endif
- void throw_parse_exception(const std::string& err)
- {
- throw parse_exception{err, line_number_};
- }
-
- void parse_table(std::string::iterator& it,
- const std::string::iterator& end, table*& curr_table)
- {
- // remove the beginning keytable marker
- ++it;
- if (it == end)
- throw_parse_exception("Unexpected end of table");
- if (*it == '[')
- parse_table_array(it, end, curr_table);
- else
- parse_single_table(it, end, curr_table);
- }
-
- void parse_single_table(std::string::iterator& it,
- const std::string::iterator& end,
- table*& curr_table)
- {
- if (it == end || *it == ']')
- throw_parse_exception("Table name cannot be empty");
-
- std::string full_table_name;
- bool inserted = false;
-
- auto key_end = [](char c) { return c == ']'; };
-
- auto key_part_handler = [&](const std::string& part) {
- if (part.empty())
- throw_parse_exception("Empty component of table name");
-
- if (!full_table_name.empty())
- full_table_name += '.';
- full_table_name += part;
-
- if (curr_table->contains(part))
- {
-#if !defined(__PGI)
- auto b = curr_table->get(part);
-#else
- // Workaround for PGI compiler
- std::shared_ptr<base> b = curr_table->get(part);
-#endif
- if (b->is_table())
- curr_table = static_cast<table*>(b.get());
- else if (b->is_table_array())
- curr_table = std::static_pointer_cast<table_array>(b)
- ->get()
- .back()
- .get();
- else
- throw_parse_exception("Key " + full_table_name
- + "already exists as a value");
- }
- else
- {
- inserted = true;
- curr_table->insert(part, make_table());
- curr_table = static_cast<table*>(curr_table->get(part).get());
- }
- };
-
- key_part_handler(parse_key(it, end, key_end, key_part_handler));
-
- if (it == end)
- throw_parse_exception(
- "Unterminated table declaration; did you forget a ']'?");
-
- if (*it != ']')
- {
- std::string errmsg{"Unexpected character in table definition: "};
- errmsg += '"';
- errmsg += *it;
- errmsg += '"';
- throw_parse_exception(errmsg);
- }
-
- // table already existed
- if (!inserted)
- {
- auto is_value
- = [](const std::pair<const std::string&,
- const std::shared_ptr<base>&>& p) {
- return p.second->is_value();
- };
-
- // if there are any values, we can't add values to this table
- // since it has already been defined. If there aren't any
- // values, then it was implicitly created by something like
- // [a.b]
- if (curr_table->empty()
- || std::any_of(curr_table->begin(), curr_table->end(),
- is_value))
- {
- throw_parse_exception("Redefinition of table "
- + full_table_name);
- }
- }
-
- ++it;
- consume_whitespace(it, end);
- eol_or_comment(it, end);
- }
-
- void parse_table_array(std::string::iterator& it,
- const std::string::iterator& end, table*& curr_table)
- {
- ++it;
- if (it == end || *it == ']')
- throw_parse_exception("Table array name cannot be empty");
-
- auto key_end = [](char c) { return c == ']'; };
-
- std::string full_ta_name;
- auto key_part_handler = [&](const std::string& part) {
- if (part.empty())
- throw_parse_exception("Empty component of table array name");
-
- if (!full_ta_name.empty())
- full_ta_name += '.';
- full_ta_name += part;
-
- if (curr_table->contains(part))
- {
-#if !defined(__PGI)
- auto b = curr_table->get(part);
-#else
- // Workaround for PGI compiler
- std::shared_ptr<base> b = curr_table->get(part);
-#endif
-
- // if this is the end of the table array name, add an
- // element to the table array that we just looked up,
- // provided it was not declared inline
- if (it != end && *it == ']')
- {
- if (!b->is_table_array())
- {
- throw_parse_exception("Key " + full_ta_name
- + " is not a table array");
- }
-
- auto v = b->as_table_array();
-
- if (v->is_inline())
- {
- throw_parse_exception("Static array " + full_ta_name
- + " cannot be appended to");
- }
-
- v->get().push_back(make_table());
- curr_table = v->get().back().get();
- }
- // otherwise, just keep traversing down the key name
- else
- {
- if (b->is_table())
- curr_table = static_cast<table*>(b.get());
- else if (b->is_table_array())
- curr_table = std::static_pointer_cast<table_array>(b)
- ->get()
- .back()
- .get();
- else
- throw_parse_exception("Key " + full_ta_name
- + " already exists as a value");
- }
- }
- else
- {
- // if this is the end of the table array name, add a new
- // table array and a new table inside that array for us to
- // add keys to next
- if (it != end && *it == ']')
- {
- curr_table->insert(part, make_table_array());
- auto arr = std::static_pointer_cast<table_array>(
- curr_table->get(part));
- arr->get().push_back(make_table());
- curr_table = arr->get().back().get();
- }
- // otherwise, create the implicitly defined table and move
- // down to it
- else
- {
- curr_table->insert(part, make_table());
- curr_table
- = static_cast<table*>(curr_table->get(part).get());
- }
- }
- };
-
- key_part_handler(parse_key(it, end, key_end, key_part_handler));
-
- // consume the last "]]"
- auto eat = make_consumer(it, end, [this]() {
- throw_parse_exception("Unterminated table array name");
- });
- eat(']');
- eat(']');
-
- consume_whitespace(it, end);
- eol_or_comment(it, end);
- }
-
- void parse_key_value(std::string::iterator& it, std::string::iterator& end,
- table* curr_table)
- {
- auto key_end = [](char c) { return c == '='; };
-
- auto key_part_handler = [&](const std::string& part) {
- // two cases: this key part exists already, in which case it must
- // be a table, or it doesn't exist in which case we must create
- // an implicitly defined table
- if (curr_table->contains(part))
- {
- auto val = curr_table->get(part);
- if (val->is_table())
- {
- curr_table = static_cast<table*>(val.get());
- }
- else
- {
- throw_parse_exception("Key " + part
- + " already exists as a value");
- }
- }
- else
- {
- auto newtable = make_table();
- curr_table->insert(part, newtable);
- curr_table = newtable.get();
- }
- };
-
- auto key = parse_key(it, end, key_end, key_part_handler);
-
- if (curr_table->contains(key))
- throw_parse_exception("Key " + key + " already present");
- if (it == end || *it != '=')
- throw_parse_exception("Value must follow after a '='");
- ++it;
- consume_whitespace(it, end);
- curr_table->insert(key, parse_value(it, end));
- consume_whitespace(it, end);
- }
-
- template <class KeyEndFinder, class KeyPartHandler>
- std::string
- parse_key(std::string::iterator& it, const std::string::iterator& end,
- KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler)
- {
- // parse the key as a series of one or more simple-keys joined with '.'
- while (it != end && !key_end(*it))
- {
- auto part = parse_simple_key(it, end);
- consume_whitespace(it, end);
-
- if (it == end || key_end(*it))
- {
- return part;
- }
-
- if (*it != '.')
- {
- std::string errmsg{"Unexpected character in key: "};
- errmsg += '"';
- errmsg += *it;
- errmsg += '"';
- throw_parse_exception(errmsg);
- }
-
- key_part_handler(part);
-
- // consume the dot
- ++it;
- }
-
- throw_parse_exception("Unexpected end of key");
- }
-
- std::string parse_simple_key(std::string::iterator& it,
- const std::string::iterator& end)
- {
- consume_whitespace(it, end);
-
- if (it == end)
- throw_parse_exception("Unexpected end of key (blank key?)");
-
- if (*it == '"' || *it == '\'')
- {
- return string_literal(it, end, *it);
- }
- else
- {
- auto bke = std::find_if(it, end, [](char c) {
- return c == '.' || c == '=' || c == ']';
- });
- return parse_bare_key(it, bke);
- }
- }
-
- std::string parse_bare_key(std::string::iterator& it,
- const std::string::iterator& end)
- {
- if (it == end)
- {
- throw_parse_exception("Bare key missing name");
- }
-
- auto key_end = end;
- --key_end;
- consume_backwards_whitespace(key_end, it);
- ++key_end;
- std::string key{it, key_end};
-
- if (std::find(it, key_end, '#') != key_end)
- {
- throw_parse_exception("Bare key " + key + " cannot contain #");
- }
-
- if (std::find_if(it, key_end,
- [](char c) { return c == ' ' || c == '\t'; })
- != key_end)
- {
- throw_parse_exception("Bare key " + key
- + " cannot contain whitespace");
- }
-
- if (std::find_if(it, key_end,
- [](char c) { return c == '[' || c == ']'; })
- != key_end)
- {
- throw_parse_exception("Bare key " + key
- + " cannot contain '[' or ']'");
- }
-
- it = end;
- return key;
- }
-
- enum class parse_type
- {
- STRING = 1,
- LOCAL_TIME,
- LOCAL_DATE,
- LOCAL_DATETIME,
- OFFSET_DATETIME,
- INT,
- FLOAT,
- BOOL,
- ARRAY,
- INLINE_TABLE
- };
-
- std::shared_ptr<base> parse_value(std::string::iterator& it,
- std::string::iterator& end)
- {
- parse_type type = determine_value_type(it, end);
- switch (type)
- {
- case parse_type::STRING:
- return parse_string(it, end);
- case parse_type::LOCAL_TIME:
- return parse_time(it, end);
- case parse_type::LOCAL_DATE:
- case parse_type::LOCAL_DATETIME:
- case parse_type::OFFSET_DATETIME:
- return parse_date(it, end);
- case parse_type::INT:
- case parse_type::FLOAT:
- return parse_number(it, end);
- case parse_type::BOOL:
- return parse_bool(it, end);
- case parse_type::ARRAY:
- return parse_array(it, end);
- case parse_type::INLINE_TABLE:
- return parse_inline_table(it, end);
- default:
- throw_parse_exception("Failed to parse value");
- }
- }
-
- parse_type determine_value_type(const std::string::iterator& it,
- const std::string::iterator& end)
- {
- if (it == end)
- {
- throw_parse_exception("Failed to parse value type");
- }
- if (*it == '"' || *it == '\'')
- {
- return parse_type::STRING;
- }
- else if (is_time(it, end))
- {
- return parse_type::LOCAL_TIME;
- }
- else if (auto dtype = date_type(it, end))
- {
- return *dtype;
- }
- else if (is_number(*it) || *it == '-' || *it == '+'
- || (*it == 'i' && it + 1 != end && it[1] == 'n'
- && it + 2 != end && it[2] == 'f')
- || (*it == 'n' && it + 1 != end && it[1] == 'a'
- && it + 2 != end && it[2] == 'n'))
- {
- return determine_number_type(it, end);
- }
- else if (*it == 't' || *it == 'f')
- {
- return parse_type::BOOL;
- }
- else if (*it == '[')
- {
- return parse_type::ARRAY;
- }
- else if (*it == '{')
- {
- return parse_type::INLINE_TABLE;
- }
- throw_parse_exception("Failed to parse value type");
- }
-
- parse_type determine_number_type(const std::string::iterator& it,
- const std::string::iterator& end)
- {
- // determine if we are an integer or a float
- auto check_it = it;
- if (*check_it == '-' || *check_it == '+')
- ++check_it;
-
- if (check_it == end)
- throw_parse_exception("Malformed number");
-
- if (*check_it == 'i' || *check_it == 'n')
- return parse_type::FLOAT;
-
- while (check_it != end && is_number(*check_it))
- ++check_it;
- if (check_it != end && *check_it == '.')
- {
- ++check_it;
- while (check_it != end && is_number(*check_it))
- ++check_it;
- return parse_type::FLOAT;
- }
- else
- {
- return parse_type::INT;
- }
- }
-
- std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,
- std::string::iterator& end)
- {
- auto delim = *it;
- assert(delim == '"' || delim == '\'');
-
- // end is non-const here because we have to be able to potentially
- // parse multiple lines in a string, not just one
- auto check_it = it;
- ++check_it;
- if (check_it != end && *check_it == delim)
- {
- ++check_it;
- if (check_it != end && *check_it == delim)
- {
- it = ++check_it;
- return parse_multiline_string(it, end, delim);
- }
- }
- return make_value<std::string>(string_literal(it, end, delim));
- }
-
- std::shared_ptr<value<std::string>>
- parse_multiline_string(std::string::iterator& it,
- std::string::iterator& end, char delim)
- {
- std::stringstream ss;
-
- auto is_ws = [](char c) { return c == ' ' || c == '\t'; };
-
- bool consuming = false;
- std::shared_ptr<value<std::string>> ret;
-
- auto handle_line = [&](std::string::iterator& local_it,
- std::string::iterator& local_end) {
- if (consuming)
- {
- local_it = std::find_if_not(local_it, local_end, is_ws);
-
- // whole line is whitespace
- if (local_it == local_end)
- return;
- }
-
- consuming = false;
-
- while (local_it != local_end)
- {
- // handle escaped characters
- if (delim == '"' && *local_it == '\\')
- {
- auto check = local_it;
- // check if this is an actual escape sequence or a
- // whitespace escaping backslash
- ++check;
- consume_whitespace(check, local_end);
- if (check == local_end)
- {
- consuming = true;
- break;
- }
-
- ss << parse_escape_code(local_it, local_end);
- continue;
- }
-
- // if we can end the string
- if (std::distance(local_it, local_end) >= 3)
- {
- auto check = local_it;
- // check for """
- if (*check++ == delim && *check++ == delim
- && *check++ == delim)
- {
- local_it = check;
- ret = make_value<std::string>(ss.str());
- break;
- }
- }
-
- ss << *local_it++;
- }
- };
-
- // handle the remainder of the current line
- handle_line(it, end);
- if (ret)
- return ret;
-
- // start eating lines
- while (detail::getline(input_, line_))
- {
- ++line_number_;
-
- it = line_.begin();
- end = line_.end();
-
- handle_line(it, end);
-
- if (ret)
- return ret;
-
- if (!consuming)
- ss << std::endl;
- }
-
- throw_parse_exception("Unterminated multi-line basic string");
- }
-
- std::string string_literal(std::string::iterator& it,
- const std::string::iterator& end, char delim)
- {
- ++it;
- std::string val;
- while (it != end)
- {
- // handle escaped characters
- if (delim == '"' && *it == '\\')
- {
- val += parse_escape_code(it, end);
- }
- else if (*it == delim)
- {
- ++it;
- consume_whitespace(it, end);
- return val;
- }
- else
- {
- val += *it++;
- }
- }
- throw_parse_exception("Unterminated string literal");
- }
-
- std::string parse_escape_code(std::string::iterator& it,
- const std::string::iterator& end)
- {
- ++it;
- if (it == end)
- throw_parse_exception("Invalid escape sequence");
- char value;
- if (*it == 'b')
- {
- value = '\b';
- }
- else if (*it == 't')
- {
- value = '\t';
- }
- else if (*it == 'n')
- {
- value = '\n';
- }
- else if (*it == 'f')
- {
- value = '\f';
- }
- else if (*it == 'r')
- {
- value = '\r';
- }
- else if (*it == '"')
- {
- value = '"';
- }
- else if (*it == '\\')
- {
- value = '\\';
- }
- else if (*it == 'u' || *it == 'U')
- {
- return parse_unicode(it, end);
- }
- else
- {
- throw_parse_exception("Invalid escape sequence");
- }
- ++it;
- return std::string(1, value);
- }
-
- std::string parse_unicode(std::string::iterator& it,
- const std::string::iterator& end)
- {
- bool large = *it++ == 'U';
- auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);
-
- if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff)
- {
- throw_parse_exception(
- "Unicode escape sequence is not a Unicode scalar value");
- }
-
- std::string result;
- // See Table 3-6 of the Unicode standard
- if (codepoint <= 0x7f)
- {
- // 1-byte codepoints: 00000000 0xxxxxxx
- // repr: 0xxxxxxx
- result += static_cast<char>(codepoint & 0x7f);
- }
- else if (codepoint <= 0x7ff)
- {
- // 2-byte codepoints: 00000yyy yyxxxxxx
- // repr: 110yyyyy 10xxxxxx
- //
- // 0x1f = 00011111
- // 0xc0 = 11000000
- //
- result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));
- //
- // 0x80 = 10000000
- // 0x3f = 00111111
- //
- result += static_cast<char>(0x80 | (codepoint & 0x3f));
- }
- else if (codepoint <= 0xffff)
- {
- // 3-byte codepoints: zzzzyyyy yyxxxxxx
- // repr: 1110zzzz 10yyyyyy 10xxxxxx
- //
- // 0xe0 = 11100000
- // 0x0f = 00001111
- //
- result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));
- result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));
- result += static_cast<char>(0x80 | (codepoint & 0x3f));
- }
- else
- {
- // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx
- // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
- //
- // 0xf0 = 11110000
- // 0x07 = 00000111
- //
- result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));
- result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));
- result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));
- result += static_cast<char>(0x80 | (codepoint & 0x3f));
- }
- return result;
- }
-
- uint32_t parse_hex(std::string::iterator& it,
- const std::string::iterator& end, uint32_t place)
- {
- uint32_t value = 0;
- while (place > 0)
- {
- if (it == end)
- throw_parse_exception("Unexpected end of unicode sequence");
-
- if (!is_hex(*it))
- throw_parse_exception("Invalid unicode escape sequence");
-
- value += place * hex_to_digit(*it++);
- place /= 16;
- }
- return value;
- }
-
- uint32_t hex_to_digit(char c)
- {
- if (is_number(c))
- return static_cast<uint32_t>(c - '0');
- return 10
- + static_cast<uint32_t>(c
- - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
- }
-
- std::shared_ptr<base> parse_number(std::string::iterator& it,
- const std::string::iterator& end)
- {
- auto check_it = it;
- auto check_end = find_end_of_number(it, end);
-
- auto eat_sign = [&]() {
- if (check_it != end && (*check_it == '-' || *check_it == '+'))
- ++check_it;
- };
-
- auto check_no_leading_zero = [&]() {
- if (check_it != end && *check_it == '0' && check_it + 1 != check_end
- && check_it[1] != '.')
- {
- throw_parse_exception("Numbers may not have leading zeros");
- }
- };
-
- auto eat_digits = [&](bool (*check_char)(char)) {
- auto beg = check_it;
- while (check_it != end && check_char(*check_it))
- {
- ++check_it;
- if (check_it != end && *check_it == '_')
- {
- ++check_it;
- if (check_it == end || !check_char(*check_it))
- throw_parse_exception("Malformed number");
- }
- }
-
- if (check_it == beg)
- throw_parse_exception("Malformed number");
- };
-
- auto eat_hex = [&]() { eat_digits(&is_hex); };
-
- auto eat_numbers = [&]() { eat_digits(&is_number); };
-
- if (check_it != end && *check_it == '0' && check_it + 1 != check_end
- && (check_it[1] == 'x' || check_it[1] == 'o' || check_it[1] == 'b'))
- {
- ++check_it;
- char base = *check_it;
- ++check_it;
- if (base == 'x')
- {
- eat_hex();
- return parse_int(it, check_it, 16);
- }
- else if (base == 'o')
- {
- auto start = check_it;
- eat_numbers();
- auto val = parse_int(start, check_it, 8, "0");
- it = start;
- return val;
- }
- else // if (base == 'b')
- {
- auto start = check_it;
- eat_numbers();
- auto val = parse_int(start, check_it, 2);
- it = start;
- return val;
- }
- }
-
- eat_sign();
- check_no_leading_zero();
-
- if (check_it != end && check_it + 1 != end && check_it + 2 != end)
- {
- if (check_it[0] == 'i' && check_it[1] == 'n' && check_it[2] == 'f')
- {
- auto val = std::numeric_limits<double>::infinity();
- if (*it == '-')
- val = -val;
- it = check_it + 3;
- return make_value(val);
- }
- else if (check_it[0] == 'n' && check_it[1] == 'a'
- && check_it[2] == 'n')
- {
- auto val = std::numeric_limits<double>::quiet_NaN();
- if (*it == '-')
- val = -val;
- it = check_it + 3;
- return make_value(val);
- }
- }
-
- eat_numbers();
-
- if (check_it != end
- && (*check_it == '.' || *check_it == 'e' || *check_it == 'E'))
- {
- bool is_exp = *check_it == 'e' || *check_it == 'E';
-
- ++check_it;
- if (check_it == end)
- throw_parse_exception("Floats must have trailing digits");
-
- auto eat_exp = [&]() {
- eat_sign();
- check_no_leading_zero();
- eat_numbers();
- };
-
- if (is_exp)
- eat_exp();
- else
- eat_numbers();
-
- if (!is_exp && check_it != end
- && (*check_it == 'e' || *check_it == 'E'))
- {
- ++check_it;
- eat_exp();
- }
-
- return parse_float(it, check_it);
- }
- else
- {
- return parse_int(it, check_it);
- }
- }
-
- std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
- const std::string::iterator& end,
- int base = 10,
- const char* prefix = "")
- {
- std::string v{it, end};
- v = prefix + v;
- v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
- it = end;
- try
- {
- return make_value<int64_t>(std::stoll(v, nullptr, base));
- }
- catch (const std::invalid_argument& ex)
- {
- throw_parse_exception("Malformed number (invalid argument: "
- + std::string{ex.what()} + ")");
- }
- catch (const std::out_of_range& ex)
- {
- throw_parse_exception("Malformed number (out of range: "
- + std::string{ex.what()} + ")");
- }
- }
-
- std::shared_ptr<value<double>> parse_float(std::string::iterator& it,
- const std::string::iterator& end)
- {
- std::string v{it, end};
- v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
- it = end;
- char decimal_point = std::localeconv()->decimal_point[0];
- std::replace(v.begin(), v.end(), '.', decimal_point);
- try
- {
- return make_value<double>(std::stod(v));
- }
- catch (const std::invalid_argument& ex)
- {
- throw_parse_exception("Malformed number (invalid argument: "
- + std::string{ex.what()} + ")");
- }
- catch (const std::out_of_range& ex)
- {
- throw_parse_exception("Malformed number (out of range: "
- + std::string{ex.what()} + ")");
- }
- }
-
- std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,
- const std::string::iterator& end)
- {
- auto eat = make_consumer(it, end, [this]() {
- throw_parse_exception("Attempted to parse invalid boolean value");
- });
-
- if (*it == 't')
- {
- eat("true");
- return make_value<bool>(true);
- }
- else if (*it == 'f')
- {
- eat("false");
- return make_value<bool>(false);
- }
-
- eat.error();
- return nullptr;
- }
-
- std::string::iterator find_end_of_number(std::string::iterator it,
- std::string::iterator end)
- {
- auto ret = std::find_if(it, end, [](char c) {
- return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
- && c != '-' && c != '+' && c != 'x' && c != 'o' && c != 'b';
- });
- if (ret != end && ret + 1 != end && ret + 2 != end)
- {
- if ((ret[0] == 'i' && ret[1] == 'n' && ret[2] == 'f')
- || (ret[0] == 'n' && ret[1] == 'a' && ret[2] == 'n'))
- {
- ret = ret + 3;
- }
- }
- return ret;
- }
-
- std::string::iterator find_end_of_date(std::string::iterator it,
- std::string::iterator end)
- {
- auto end_of_date = std::find_if(it, end, [](char c) {
- return !is_number(c) && c != '-';
- });
- if (end_of_date != end && *end_of_date == ' ' && end_of_date + 1 != end
- && is_number(end_of_date[1]))
- end_of_date++;
- return std::find_if(end_of_date, end, [](char c) {
- return !is_number(c) && c != 'T' && c != 'Z' && c != ':'
- && c != '-' && c != '+' && c != '.';
- });
- }
-
- std::string::iterator find_end_of_time(std::string::iterator it,
- std::string::iterator end)
- {
- return std::find_if(it, end, [](char c) {
- return !is_number(c) && c != ':' && c != '.';
- });
- }
-
- local_time read_time(std::string::iterator& it,
- const std::string::iterator& end)
- {
- auto time_end = find_end_of_time(it, end);
-
- auto eat = make_consumer(
- it, time_end, [&]() { throw_parse_exception("Malformed time"); });
-
- local_time ltime;
-
- ltime.hour = eat.eat_digits(2);
- eat(':');
- ltime.minute = eat.eat_digits(2);
- eat(':');
- ltime.second = eat.eat_digits(2);
-
- int power = 100000;
- if (it != time_end && *it == '.')
- {
- ++it;
- while (it != time_end && is_number(*it))
- {
- ltime.microsecond += power * (*it++ - '0');
- power /= 10;
- }
- }
-
- if (it != time_end)
- throw_parse_exception("Malformed time");
-
- return ltime;
- }
-
- std::shared_ptr<value<local_time>>
- parse_time(std::string::iterator& it, const std::string::iterator& end)
- {
- return make_value(read_time(it, end));
- }
-
- std::shared_ptr<base> parse_date(std::string::iterator& it,
- const std::string::iterator& end)
- {
- auto date_end = find_end_of_date(it, end);
-
- auto eat = make_consumer(
- it, date_end, [&]() { throw_parse_exception("Malformed date"); });
-
- local_date ldate;
- ldate.year = eat.eat_digits(4);
- eat('-');
- ldate.month = eat.eat_digits(2);
- eat('-');
- ldate.day = eat.eat_digits(2);
-
- if (it == date_end)
- return make_value(ldate);
-
- eat.eat_or('T', ' ');
-
- local_datetime ldt;
- static_cast<local_date&>(ldt) = ldate;
- static_cast<local_time&>(ldt) = read_time(it, date_end);
-
- if (it == date_end)
- return make_value(ldt);
-
- offset_datetime dt;
- static_cast<local_datetime&>(dt) = ldt;
-
- int hoff = 0;
- int moff = 0;
- if (*it == '+' || *it == '-')
- {
- auto plus = *it == '+';
- ++it;
-
- hoff = eat.eat_digits(2);
- dt.hour_offset = (plus) ? hoff : -hoff;
- eat(':');
- moff = eat.eat_digits(2);
- dt.minute_offset = (plus) ? moff : -moff;
- }
- else if (*it == 'Z')
- {
- ++it;
- }
-
- if (it != date_end)
- throw_parse_exception("Malformed date");
-
- return make_value(dt);
- }
-
- std::shared_ptr<base> parse_array(std::string::iterator& it,
- std::string::iterator& end)
- {
- // this gets ugly because of the "homogeneity" restriction:
- // arrays can either be of only one type, or contain arrays
- // (each of those arrays could be of different types, though)
- //
- // because of the latter portion, we don't really have a choice
- // but to represent them as arrays of base values...
- ++it;
-
- // ugh---have to read the first value to determine array type...
- skip_whitespace_and_comments(it, end);
-
- // edge case---empty array
- if (*it == ']')
- {
- ++it;
- return make_array();
- }
-
- auto val_end = std::find_if(
- it, end, [](char c) { return c == ',' || c == ']' || c == '#'; });
- parse_type type = determine_value_type(it, val_end);
- switch (type)
- {
- case parse_type::STRING:
- return parse_value_array<std::string>(it, end);
- case parse_type::LOCAL_TIME:
- return parse_value_array<local_time>(it, end);
- case parse_type::LOCAL_DATE:
- return parse_value_array<local_date>(it, end);
- case parse_type::LOCAL_DATETIME:
- return parse_value_array<local_datetime>(it, end);
- case parse_type::OFFSET_DATETIME:
- return parse_value_array<offset_datetime>(it, end);
- case parse_type::INT:
- return parse_value_array<int64_t>(it, end);
- case parse_type::FLOAT:
- return parse_value_array<double>(it, end);
- case parse_type::BOOL:
- return parse_value_array<bool>(it, end);
- case parse_type::ARRAY:
- return parse_object_array<array>(&parser::parse_array, '[', it,
- end);
- case parse_type::INLINE_TABLE:
- return parse_object_array<table_array>(
- &parser::parse_inline_table, '{', it, end);
- default:
- throw_parse_exception("Unable to parse array");
- }
- }
-
- template <class Value>
- std::shared_ptr<array> parse_value_array(std::string::iterator& it,
- std::string::iterator& end)
- {
- auto arr = make_array();
- while (it != end && *it != ']')
- {
- auto val = parse_value(it, end);
- if (auto v = val->as<Value>())
- arr->get().push_back(val);
- else
- throw_parse_exception("Arrays must be homogeneous");
- skip_whitespace_and_comments(it, end);
- if (*it != ',')
- break;
- ++it;
- skip_whitespace_and_comments(it, end);
- }
- if (it != end)
- ++it;
- return arr;
- }
-
- template <class Object, class Function>
- std::shared_ptr<Object> parse_object_array(Function&& fun, char delim,
- std::string::iterator& it,
- std::string::iterator& end)
- {
- auto arr = detail::make_element<Object>();
-
- while (it != end && *it != ']')
- {
- if (*it != delim)
- throw_parse_exception("Unexpected character in array");
-
- arr->get().push_back(((*this).*fun)(it, end));
- skip_whitespace_and_comments(it, end);
-
- if (it == end || *it != ',')
- break;
-
- ++it;
- skip_whitespace_and_comments(it, end);
- }
-
- if (it == end || *it != ']')
- throw_parse_exception("Unterminated array");
-
- ++it;
- return arr;
- }
-
- std::shared_ptr<table> parse_inline_table(std::string::iterator& it,
- std::string::iterator& end)
- {
- auto tbl = make_table();
- do
- {
- ++it;
- if (it == end)
- throw_parse_exception("Unterminated inline table");
-
- consume_whitespace(it, end);
- if (it != end && *it != '}')
- {
- parse_key_value(it, end, tbl.get());
- consume_whitespace(it, end);
- }
- } while (*it == ',');
-
- if (it == end || *it != '}')
- throw_parse_exception("Unterminated inline table");
-
- ++it;
- consume_whitespace(it, end);
-
- return tbl;
- }
-
- void skip_whitespace_and_comments(std::string::iterator& start,
- std::string::iterator& end)
- {
- consume_whitespace(start, end);
- while (start == end || *start == '#')
- {
- if (!detail::getline(input_, line_))
- throw_parse_exception("Unclosed array");
- line_number_++;
- start = line_.begin();
- end = line_.end();
- consume_whitespace(start, end);
- }
- }
-
- void consume_whitespace(std::string::iterator& it,
- const std::string::iterator& end)
- {
- while (it != end && (*it == ' ' || *it == '\t'))
- ++it;
- }
-
- void consume_backwards_whitespace(std::string::iterator& back,
- const std::string::iterator& front)
- {
- while (back != front && (*back == ' ' || *back == '\t'))
- --back;
- }
-
- void eol_or_comment(const std::string::iterator& it,
- const std::string::iterator& end)
- {
- if (it != end && *it != '#')
- throw_parse_exception("Unidentified trailing character '"
- + std::string{*it}
- + "'---did you forget a '#'?");
- }
-
- bool is_time(const std::string::iterator& it,
- const std::string::iterator& end)
- {
- auto time_end = find_end_of_time(it, end);
- auto len = std::distance(it, time_end);
-
- if (len < 8)
- return false;
-
- if (it[2] != ':' || it[5] != ':')
- return false;
-
- if (len > 8)
- return it[8] == '.' && len > 9;
-
- return true;
- }
-
- option<parse_type> date_type(const std::string::iterator& it,
- const std::string::iterator& end)
- {
- auto date_end = find_end_of_date(it, end);
- auto len = std::distance(it, date_end);
-
- if (len < 10)
- return {};
-
- if (it[4] != '-' || it[7] != '-')
- return {};
-
- if (len >= 19 && (it[10] == 'T' || it[10] == ' ')
- && is_time(it + 11, date_end))
- {
- // datetime type
- auto time_end = find_end_of_time(it + 11, date_end);
- if (time_end == date_end)
- return {parse_type::LOCAL_DATETIME};
- else
- return {parse_type::OFFSET_DATETIME};
- }
- else if (len == 10)
- {
- // just a regular date
- return {parse_type::LOCAL_DATE};
- }
-
- return {};
- }
-
- std::istream& input_;
- std::string line_;
- std::size_t line_number_ = 0;
-};
-
-/**
- * Utility function to parse a file as a TOML file. Returns the root table.
- * Throws a parse_exception if the file cannot be opened.
- */
-inline std::shared_ptr<table> parse_file(const std::string& filename)
-{
-#if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)
- boost::nowide::ifstream file{filename.c_str()};
-#elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)
- nowide::ifstream file{filename.c_str()};
-#else
- std::ifstream file{filename};
-#endif
- if (!file.is_open())
- throw parse_exception{filename + " could not be opened for parsing"};
- parser p{file};
- return p.parse();
-}
-
-template <class... Ts>
-struct value_accept;
-
-template <>
-struct value_accept<>
-{
- template <class Visitor, class... Args>
- static void accept(const base&, Visitor&&, Args&&...)
- {
- // nothing
- }
-};
-
-template <class T, class... Ts>
-struct value_accept<T, Ts...>
-{
- template <class Visitor, class... Args>
- static void accept(const base& b, Visitor&& visitor, Args&&... args)
- {
- if (auto v = b.as<T>())
- {
- visitor.visit(*v, std::forward<Args>(args)...);
- }
- else
- {
- value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor),
- std::forward<Args>(args)...);
- }
- }
-};
-
-/**
- * base implementation of accept() that calls visitor.visit() on the concrete
- * class.
- */
-template <class Visitor, class... Args>
-void base::accept(Visitor&& visitor, Args&&... args) const
-{
- if (is_value())
- {
- using value_acceptor
- = value_accept<std::string, int64_t, double, bool, local_date,
- local_time, local_datetime, offset_datetime>;
- value_acceptor::accept(*this, std::forward<Visitor>(visitor),
- std::forward<Args>(args)...);
- }
- else if (is_table())
- {
- visitor.visit(static_cast<const table&>(*this),
- std::forward<Args>(args)...);
- }
- else if (is_array())
- {
- visitor.visit(static_cast<const array&>(*this),
- std::forward<Args>(args)...);
- }
- else if (is_table_array())
- {
- visitor.visit(static_cast<const table_array&>(*this),
- std::forward<Args>(args)...);
- }
-}
-
-/**
- * Writer that can be passed to accept() functions of cpptoml objects and
- * will output valid TOML to a stream.
- */
-class toml_writer
-{
- public:
- /**
- * Construct a toml_writer that will write to the given stream
- */
- toml_writer(std::ostream& s, const std::string& indent_space = "\t")
- : stream_(s), indent_(indent_space), has_naked_endline_(false)
- {
- // nothing
- }
-
- public:
- /**
- * Output a base value of the TOML tree.
- */
- template <class T>
- void visit(const value<T>& v, bool = false)
- {
- write(v);
- }
-
- /**
- * Output a table element of the TOML tree
- */
- void visit(const table& t, bool in_array = false)
- {
- write_table_header(in_array);
- std::vector<std::string> values;
- std::vector<std::string> tables;
-
- for (const auto& i : t)
- {
- if (i.second->is_table() || i.second->is_table_array())
- {
- tables.push_back(i.first);
- }
- else
- {
- values.push_back(i.first);
- }
- }
-
- for (unsigned int i = 0; i < values.size(); ++i)
- {
- path_.push_back(values[i]);
-
- if (i > 0)
- endline();
-
- write_table_item_header(*t.get(values[i]));
- t.get(values[i])->accept(*this, false);
- path_.pop_back();
- }
-
- for (unsigned int i = 0; i < tables.size(); ++i)
- {
- path_.push_back(tables[i]);
-
- if (values.size() > 0 || i > 0)
- endline();
-
- write_table_item_header(*t.get(tables[i]));
- t.get(tables[i])->accept(*this, false);
- path_.pop_back();
- }
-
- endline();
- }
-
- /**
- * Output an array element of the TOML tree
- */
- void visit(const array& a, bool = false)
- {
- write("[");
-
- for (unsigned int i = 0; i < a.get().size(); ++i)
- {
- if (i > 0)
- write(", ");
-
- if (a.get()[i]->is_array())
- {
- a.get()[i]->as_array()->accept(*this, true);
- }
- else
- {
- a.get()[i]->accept(*this, true);
- }
- }
-
- write("]");
- }
-
- /**
- * Output a table_array element of the TOML tree
- */
- void visit(const table_array& t, bool = false)
- {
- for (unsigned int j = 0; j < t.get().size(); ++j)
- {
- if (j > 0)
- endline();
-
- t.get()[j]->accept(*this, true);
- }
-
- endline();
- }
-
- /**
- * Escape a string for output.
- */
- static std::string escape_string(const std::string& str)
- {
- std::string res;
- for (auto it = str.begin(); it != str.end(); ++it)
- {
- if (*it == '\b')
- {
- res += "\\b";
- }
- else if (*it == '\t')
- {
- res += "\\t";
- }
- else if (*it == '\n')
- {
- res += "\\n";
- }
- else if (*it == '\f')
- {
- res += "\\f";
- }
- else if (*it == '\r')
- {
- res += "\\r";
- }
- else if (*it == '"')
- {
- res += "\\\"";
- }
- else if (*it == '\\')
- {
- res += "\\\\";
- }
- else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
- {
- res += "\\u";
- std::stringstream ss;
- ss << std::hex << static_cast<uint32_t>(*it);
- res += ss.str();
- }
- else
- {
- res += *it;
- }
- }
- return res;
- }
-
- protected:
- /**
- * Write out a string.
- */
- void write(const value<std::string>& v)
- {
- write("\"");
- write(escape_string(v.get()));
- write("\"");
- }
-
- /**
- * Write out a double.
- */
- void write(const value<double>& v)
- {
- std::stringstream ss;
- ss << std::showpoint
- << std::setprecision(std::numeric_limits<double>::max_digits10)
- << v.get();
-
- auto double_str = ss.str();
- auto pos = double_str.find("e0");
- if (pos != std::string::npos)
- double_str.replace(pos, 2, "e");
- pos = double_str.find("e-0");
- if (pos != std::string::npos)
- double_str.replace(pos, 3, "e-");
-
- stream_ << double_str;
- has_naked_endline_ = false;
- }
-
- /**
- * Write out an integer, local_date, local_time, local_datetime, or
- * offset_datetime.
- */
- template <class T>
- typename std::enable_if<
- is_one_of<T, int64_t, local_date, local_time, local_datetime,
- offset_datetime>::value>::type
- write(const value<T>& v)
- {
- write(v.get());
- }
-
- /**
- * Write out a boolean.
- */
- void write(const value<bool>& v)
- {
- write((v.get() ? "true" : "false"));
- }
-
- /**
- * Write out the header of a table.
- */
- void write_table_header(bool in_array = false)
- {
- if (!path_.empty())
- {
- indent();
-
- write("[");
-
- if (in_array)
- {
- write("[");
- }
-
- for (unsigned int i = 0; i < path_.size(); ++i)
- {
- if (i > 0)
- {
- write(".");
- }
-
- if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
- "fghijklmnopqrstuvwxyz0123456789"
- "_-")
- == std::string::npos)
- {
- write(path_[i]);
- }
- else
- {
- write("\"");
- write(escape_string(path_[i]));
- write("\"");
- }
- }
-
- if (in_array)
- {
- write("]");
- }
-
- write("]");
- endline();
- }
- }
-
- /**
- * Write out the identifier for an item in a table.
- */
- void write_table_item_header(const base& b)
- {
- if (!b.is_table() && !b.is_table_array())
- {
- indent();
-
- if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde"
- "fghijklmnopqrstuvwxyz0123456789"
- "_-")
- == std::string::npos)
- {
- write(path_.back());
- }
- else
- {
- write("\"");
- write(escape_string(path_.back()));
- write("\"");
- }
-
- write(" = ");
- }
- }
-
- private:
- /**
- * Indent the proper number of tabs given the size of
- * the path.
- */
- void indent()
- {
- for (std::size_t i = 1; i < path_.size(); ++i)
- write(indent_);
- }
-
- /**
- * Write a value out to the stream.
- */
- template <class T>
- void write(const T& v)
- {
- stream_ << v;
- has_naked_endline_ = false;
- }
-
- /**
- * Write an endline out to the stream
- */
- void endline()
- {
- if (!has_naked_endline_)
- {
- stream_ << "\n";
- has_naked_endline_ = true;
- }
- }
-
- private:
- std::ostream& stream_;
- const std::string indent_;
- std::vector<std::string> path_;
- bool has_naked_endline_;
-};
-
-inline std::ostream& operator<<(std::ostream& stream, const base& b)
-{
- toml_writer writer{stream};
- b.accept(writer);
- return stream;
-}
-
-template <class T>
-std::ostream& operator<<(std::ostream& stream, const value<T>& v)
-{
- toml_writer writer{stream};
- v.accept(writer);
- return stream;
-}
-
-inline std::ostream& operator<<(std::ostream& stream, const table& t)
-{
- toml_writer writer{stream};
- t.accept(writer);
- return stream;
-}
-
-inline std::ostream& operator<<(std::ostream& stream, const table_array& t)
-{
- toml_writer writer{stream};
- t.accept(writer);
- return stream;
-}
-
-inline std::ostream& operator<<(std::ostream& stream, const array& a)
-{
- toml_writer writer{stream};
- a.accept(writer);
- return stream;
-}
-} // namespace cpptoml
-#endif // CPPTOML_H
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 8cf4d9549..a95726f5f 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -821,8 +821,23 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
Value * EvalState::allocValue()
{
+ /* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
+ GC_malloc_many returns a linked list of objects of the given size, where the first word
+ of each object is also the pointer to the next object in the list. This also means that we
+ have to explicitly clear the first word of every object we take. */
+ if (!valueAllocCache) {
+ valueAllocCache = GC_malloc_many(sizeof(Value));
+ if (!valueAllocCache) throw std::bad_alloc();
+ }
+
+ /* GC_NEXT is a convenience macro for accessing the first word of an object.
+ Take the first list item, advance the list to the next item, and clear the next pointer. */
+ void * p = valueAllocCache;
+ GC_PTR_STORE_AND_DIRTY(&valueAllocCache, GC_NEXT(p));
+ GC_NEXT(p) = nullptr;
+
nrValues++;
- auto v = (Value *) allocBytes(sizeof(Value));
+ auto v = (Value *) p;
return v;
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 0ba570434..d7ef7b88a 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -133,6 +133,9 @@ private:
/* Cache used by prim_match(). */
std::shared_ptr<RegexCache> regexCache;
+ /* Allocation cache for GC'd Value objects. */
+ void * valueAllocCache = nullptr;
+
public:
EvalState(
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index 4c6682dfd..bfea1ce34 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -1,86 +1,77 @@
#include "primops.hh"
#include "eval-inline.hh"
-#include "../../cpptoml/cpptoml.h"
+#include "../../toml11/toml.hpp"
namespace nix {
-static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & v)
+static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
{
- using namespace cpptoml;
-
auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(toml);
- std::function<void(Value &, std::shared_ptr<base>)> visit;
+ std::function<void(Value &, toml::value)> visit;
- visit = [&](Value & v, std::shared_ptr<base> t) {
+ visit = [&](Value & v, toml::value t) {
- if (auto t2 = t->as_table()) {
+ switch(t.type())
+ {
+ case toml::value_t::table:
+ {
+ auto table = toml::get<toml::table>(t);
- size_t size = 0;
- for (auto & i : *t2) { (void) i; size++; }
+ size_t size = 0;
+ for (auto & i : table) { (void) i; size++; }
- state.mkAttrs(v, size);
+ state.mkAttrs(v, size);
- for (auto & i : *t2) {
- auto & v2 = *state.allocAttr(v, state.symbols.create(i.first));
+ for(auto & elem: table) {
- if (auto i2 = i.second->as_table_array()) {
- size_t size2 = i2->get().size();
- state.mkList(v2, size2);
- for (size_t j = 0; j < size2; ++j)
- visit(*(v2.listElems()[j] = state.allocValue()), i2->get()[j]);
+ auto & v2 = *state.allocAttr(v, state.symbols.create(elem.first));
+ visit(v2, elem.second);
+ }
}
- else
- visit(v2, i.second);
- }
-
- v.attrs->sort();
- }
-
- else if (auto t2 = t->as_array()) {
- size_t size = t2->get().size();
-
- state.mkList(v, size);
-
- for (size_t i = 0; i < size; ++i)
- visit(*(v.listElems()[i] = state.allocValue()), t2->get()[i]);
- }
-
- // Handle cases like 'a = [[{ a = true }]]', which IMHO should be
- // parsed as a array containing an array containing a table,
- // but instead are parsed as an array containing a table array
- // containing a table.
- else if (auto t2 = t->as_table_array()) {
- size_t size = t2->get().size();
-
- state.mkList(v, size);
-
- for (size_t j = 0; j < size; ++j)
- visit(*(v.listElems()[j] = state.allocValue()), t2->get()[j]);
- }
+ break;;
+ case toml::value_t::array:
+ {
+ auto array = toml::get<std::vector<toml::value>>(t);
+
+ size_t size = array.size();
+ state.mkList(v, size);
+ for (size_t i = 0; i < size; ++i)
+ visit(*(v.listElems()[i] = state.allocValue()), array[i]);
+ }
+ break;;
+ case toml::value_t::boolean:
+ mkBool(v, toml::get<bool>(t));
+ break;;
+ case toml::value_t::integer:
+ mkInt(v, toml::get<long>(t));
+ break;;
+ case toml::value_t::floating:
+ mkFloat(v, toml::get<double>(t));
+ break;;
+ case toml::value_t::string:
+ mkString(v, toml::get<std::string>(t));
+ break;;
+ case toml::value_t::local_datetime:
+ case toml::value_t::offset_datetime:
+ case toml::value_t::local_date:
+ case toml::value_t::local_time:
+ // We fail since Nix doesn't have date and time types
+ throw std::runtime_error("Dates and times are not supported");
+ break;;
+ case toml::value_t::empty:
+ mkNull(v);
+ break;;
- else if (t->is_value()) {
- if (auto val = t->as<int64_t>())
- mkInt(v, val->get());
- else if (auto val = t->as<NixFloat>())
- mkFloat(v, val->get());
- else if (auto val = t->as<bool>())
- mkBool(v, val->get());
- else if (auto val = t->as<std::string>())
- mkString(v, val->get());
- else
- throw EvalError("unsupported value type in TOML");
}
-
- else abort();
};
try {
- visit(v, parser(tomlStream).parse());
- } catch (std::runtime_error & e) {
+ visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
+ } catch (std::exception & e) { // TODO: toml::syntax_error
throw EvalError({
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
.errPos = pos
diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc
index 2f6b361bb..e27f56ef5 100644
--- a/src/nix/why-depends.cc
+++ b/src/nix/why-depends.cc
@@ -34,8 +34,21 @@ struct CmdWhyDepends : SourceExprCommand
CmdWhyDepends()
{
- expectArg("package", &_package);
- expectArg("dependency", &_dependency);
+ expectArgs({
+ .label = "package",
+ .handler = {&_package},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeInstallable(prefix);
+ }}
+ });
+
+ expectArgs({
+ .label = "dependency",
+ .handler = {&_dependency},
+ .completer = {[&](size_t, std::string_view prefix) {
+ completeInstallable(prefix);
+ }}
+ });
addFlag({
.longName = "all",
diff --git a/src/toml11/LICENSE b/src/toml11/LICENSE
new file mode 100644
index 000000000..f55c511d6
--- /dev/null
+++ b/src/toml11/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Toru Niina
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/toml11/README.md b/src/toml11/README.md
new file mode 100644
index 000000000..62b586305
--- /dev/null
+++ b/src/toml11/README.md
@@ -0,0 +1,1966 @@
+toml11
+======
+
+[![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions)
+[![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11)
+[![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master)
+[![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master)
+[![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases)
+[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE)
+[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136)
+
+toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
+
+- It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0).
+- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
+- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
+- It has configurable container. You can use any random-access containers and key-value maps as backend containers.
+- It optionally preserves comments without any overhead.
+- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings.
+- It supports user-defined type conversion from/into toml values.
+- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows.
+
+## Example
+
+```cpp
+#include <toml.hpp>
+#include <iostream>
+
+int main()
+{
+ // ```toml
+ // title = "an example toml file"
+ // nums = [3, 1, 4, 1, 5]
+ // ```
+ auto data = toml::parse("example.toml");
+
+ // find a value with the specified type from a table
+ std::string title = toml::find<std::string>(data, "title");
+
+ // convert the whole array into any container automatically
+ std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
+
+ // access with STL-like manner
+ if(!data.contains("foo"))
+ {
+ data["foo"] = "bar";
+ }
+
+ // pass a fallback
+ std::string name = toml::find_or<std::string>(data, "name", "not found");
+
+ // width-dependent formatting
+ std::cout << std::setw(80) << data << std::endl;
+
+ return 0;
+}
+```
+
+## Table of Contents
+
+- [Integration](#integration)
+- [Decoding a toml file](#decoding-a-toml-file)
+ - [In the case of syntax error](#in-the-case-of-syntax-error)
+ - [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints)
+- [Finding a toml value](#finding-a-toml-value)
+ - [Finding a value in a table](#finding-a-value-in-a-table)
+ - [In case of error](#in-case-of-error)
+ - [Dotted keys](#dotted-keys)
+- [Casting a toml value](#casting-a-toml-value)
+- [Checking value type](#checking-value-type)
+- [More about conversion](#more-about-conversion)
+ - [Converting an array](#converting-an-array)
+ - [Converting a table](#converting-a-table)
+ - [Getting an array of tables](#getting-an-array-of-tables)
+ - [Cost of conversion](#cost-of-conversion)
+ - [Converting datetime and its variants](#converting-datetime-and-its-variants)
+- [Getting with a fallback](#getting-with-a-fallback)
+- [Expecting conversion](#expecting-conversion)
+- [Visiting a toml::value](#visiting-a-tomlvalue)
+- [Constructing a toml::value](#constructing-a-tomlvalue)
+- [Preserving Comments](#preserving-comments)
+- [Customizing containers](#customizing-containers)
+- [TOML literal](#toml-literal)
+- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types)
+- [Formatting user-defined error messages](#formatting-user-defined-error-messages)
+- [Obtaining location information](#obtaining-location-information)
+- [Exceptions](#exceptions)
+- [Colorize Error Messages](#colorize-error-messages)
+- [Serializing TOML data](#serializing-toml-data)
+- [Underlying types](#underlying-types)
+- [Unreleased TOML features](#unreleased-toml-features)
+- [Breaking Changes from v2](#breaking-changes-from-v2)
+- [Running Tests](#running-tests)
+- [Contributors](#contributors)
+- [Licensing Terms](#licensing-terms)
+
+## Integration
+
+Just include the file after adding it to the include path.
+
+```cpp
+#include <toml.hpp> // that's all! now you can use it.
+#include <iostream>
+
+int main()
+{
+ const auto data = toml::parse("example.toml");
+ const auto title = toml::find<std::string>(data, "title");
+ std::cout << "the title is " << title << std::endl;
+ return 0;
+}
+```
+
+The convenient way is to add this repository as a git-submodule or to install
+it in your system by CMake.
+
+Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly.
+
+## Decoding a toml file
+
+To parse a toml file, the only thing you have to do is
+to pass a filename to the `toml::parse` function.
+
+```cpp
+const std::string fname("sample.toml");
+const toml::value data = toml::parse(fname);
+```
+
+As required by the TOML specification, the top-level value is always a table.
+You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`.
+
+If it encounters an error while opening a file, it will throw `std::runtime_error`.
+
+You can also pass a `std::istream` to the `toml::parse` function.
+To show a filename in an error message, however, it is recommended to pass the
+filename with the stream.
+
+```cpp
+std::ifstream ifs("sample.toml", std::ios_base::binary);
+assert(ifs.good());
+const auto data = toml::parse(ifs, /*optional -> */ "sample.toml");
+```
+
+**Note**: When you are **on Windows, open a file in binary mode**.
+If a file is opened in text-mode, CRLF ("\r\n") will automatically be
+converted to LF ("\n") and this causes inconsistency between file size
+and the contents that would be read. This causes weird error.
+
+### In the case of syntax error
+
+If there is a syntax error in a toml file, `toml::parse` will throw
+`toml::syntax_error` that inherits `std::exception`.
+
+toml11 has clean and informative error messages inspired by Rust and
+it looks like the following.
+
+```console
+terminate called after throwing an instance of 'toml::syntax_error'
+ what(): [error] toml::parse_table: invalid line format # error description
+ --> example.toml # file name
+ 3 | a = 42 = true # line num and content
+ | ^------ expected newline, but got '='. # error reason
+```
+
+If you (mistakenly) duplicate tables and got an error, it is helpful to see
+where they are. toml11 shows both at the same time like the following.
+
+```console
+terminate called after throwing an instance of 'toml::syntax_error'
+ what(): [error] toml::insert_value: table ("table") already exists.
+ --> duplicate-table.toml
+ 1 | [table]
+ | ~~~~~~~ table already exists here
+ ...
+ 3 | [table]
+ | ~~~~~~~ table defined twice
+```
+
+When toml11 encounters a malformed value, it tries to detect what type it is.
+Then it shows hints to fix the format. An error message while reading one of
+the malformed files in [the language agnostic test suite](https://github.com/BurntSushi/toml-test).
+is shown below.
+
+```console
+what(): [error] bad time: should be HH:MM:SS.subsec
+ --> ./datetime-malformed-no-secs.toml
+ 1 | no-secs = 1987-07-05T17:45Z
+ | ^------- HH:MM:SS.subsec
+ |
+Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999
+Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32
+```
+
+You can find other examples in a job named `output_result` on
+[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
+
+Since the error message generation is generally a difficult task, the current
+status is not ideal. If you encounter a weird error message, please let us know
+and contribute to improve the quality!
+
+### Invalid UTF-8 codepoints
+
+It throws `syntax_error` if a value of an escape sequence
+representing unicode character is not a valid UTF-8 codepoint.
+
+```console
+ what(): [error] toml::read_utf8_codepoint: input codepoint is too large.
+ --> utf8.toml
+ 1 | exceeds_unicode = "\U0011FFFF example"
+ | ^--------- should be in [0x00..0x10FFFF]
+```
+
+## Finding a toml value
+
+After parsing successfully, you can obtain the values from the result of
+`toml::parse` using `toml::find` function.
+
+```toml
+# sample.toml
+answer = 42
+pi = 3.14
+numbers = [1,2,3]
+time = 1979-05-27T07:32:00Z
+```
+
+``` cpp
+const auto data = toml::parse("sample.toml");
+const auto answer = toml::find<std::int64_t >(data, "answer");
+const auto pi = toml::find<double >(data, "pi");
+const auto numbers = toml::find<std::vector<int>>(data, "numbers");
+const auto timepoint = toml::find<std::chrono::system_clock::time_point>(data, "time");
+```
+
+By default, `toml::find` returns a `toml::value`.
+
+```cpp
+const toml::value& answer = toml::find(data, "answer");
+```
+
+When you pass an exact TOML type that does not require type conversion,
+`toml::find` returns a reference without copying the value.
+
+```cpp
+const auto data = toml::parse("sample.toml");
+const auto& answer = toml::find<toml::integer>(data, "answer");
+```
+
+If the specified type requires conversion, you can't take a reference to the value.
+See also [underlying types](#underlying-types).
+
+**NOTE**: For some technical reason, automatic conversion between `integer` and
+`floating` is not supported. If you want to get a floating value even if a value
+has integer value, you need to convert it manually after obtaining a value,
+like the following.
+
+```cpp
+const auto vx = toml::find(data, "x");
+double x = vx.is_floating() ? vx.as_floating(std::nothrow) :
+ static_cast<double>(vx.as_integer()); // it throws if vx is neither
+ // floating nor integer.
+```
+
+### Finding a value in a table
+
+There are several way to get a value defined in a table.
+First, you can get a table as a normal value and find a value from the table.
+
+```toml
+[fruit]
+name = "apple"
+[fruit.physical]
+color = "red"
+shape = "round"
+```
+
+``` cpp
+const auto data = toml::parse("fruit.toml");
+const auto& fruit = toml::find(data, "fruit");
+const auto name = toml::find<std::string>(fruit, "name");
+
+const auto& physical = toml::find(fruit, "physical");
+const auto color = toml::find<std::string>(physical, "color");
+const auto shape = toml::find<std::string>(physical, "shape");
+```
+
+Here, variable `fruit` is a `toml::value` and can be used as the first argument
+of `toml::find`.
+
+Second, you can pass as many arguments as the number of subtables to `toml::find`.
+
+```cpp
+const auto data = toml::parse("fruit.toml");
+const auto color = toml::find<std::string>(data, "fruit", "physical", "color");
+const auto shape = toml::find<std::string>(data, "fruit", "physical", "shape");
+```
+
+### Finding a value in an array
+
+You can find n-th value in an array by `toml::find`.
+
+```toml
+values = ["foo", "bar", "baz"]
+```
+
+``` cpp
+const auto data = toml::parse("sample.toml");
+const auto values = toml::find(data, "values");
+const auto bar = toml::find<std::string>(values, 1);
+```
+
+`toml::find` can also search array recursively.
+
+```cpp
+const auto data = toml::parse("fruit.toml");
+const auto bar = toml::find<std::string>(data, "values", 1);
+```
+
+Before calling `toml::find`, you can check if a value corresponding to a key
+exists. You can use both `bool toml::value::contains(const key&) const` and
+`std::size_t toml::value::count(const key&) const`. Those behaves like the
+`std::map::contains` and `std::map::count`.
+
+```cpp
+const auto data = toml::parse("fruit.toml");
+if(data.contains("fruit") && data.at("fruit").count("physical") != 0)
+{
+ // ...
+}
+```
+
+### In case of error
+
+If the value does not exist, `toml::find` throws `std::out_of_range` with the
+location of the table.
+
+```console
+terminate called after throwing an instance of 'std::out_of_range'
+ what(): [error] key "answer" not found
+ --> example.toml
+ 6 | [tab]
+ | ~~~~~ in this table
+```
+
+----
+
+If the specified type differs from the actual value contained, it throws
+`toml::type_error` that inherits `std::exception`.
+
+Similar to the case of syntax error, toml11 also displays clean error messages.
+The error message when you choose `int` to get `string` value would be like this.
+
+```console
+terminate called after throwing an instance of 'toml::type_error'
+ what(): [error] toml::value bad_cast to integer
+ --> example.toml
+ 3 | title = "TOML Example"
+ | ~~~~~~~~~~~~~~ the actual type is string
+```
+
+**NOTE**: In order to show this kind of error message, all the toml values have
+a pointer to represent its range in a file. The entire contents of a file is
+shared by `toml::value`s and remains on the heap memory. It is recommended to
+destruct all the `toml::value` classes after configuring your application
+if you have a large TOML file compared to the memory resource.
+
+### Dotted keys
+
+TOML v0.5.0 has a new feature named "dotted keys".
+You can chain keys to represent the structure of the data.
+
+```toml
+physical.color = "orange"
+physical.shape = "round"
+```
+
+This is equivalent to the following.
+
+```toml
+[physical]
+color = "orange"
+shape = "round"
+```
+
+You can get both of the above tables with the same c++ code.
+
+```cpp
+const auto physical = toml::find(data, "physical");
+const auto color = toml::find<std::string>(physical, "color");
+```
+
+The following code does not work for the above toml file.
+
+```cpp
+// XXX this does not work!
+const auto color = toml::find<std::string>(data, "physical.color");
+```
+
+The above code works with the following toml file.
+
+```toml
+"physical.color" = "orange"
+# equivalent to {"physical.color": "orange"},
+# NOT {"physical": {"color": "orange"}}.
+```
+
+
+## Casting a toml value
+
+### `toml::get`
+
+`toml::parse` returns `toml::value`. `toml::value` is a union type that can
+contain one of the following types.
+
+- `toml::boolean` (`bool`)
+- `toml::integer` (`std::int64_t`)
+- `toml::floating` (`double`)
+- `toml::string` (a type convertible to std::string)
+- `toml::local_date`
+- `toml::local_time`
+- `toml::local_datetime`
+- `toml::offset_datetime`
+- `toml::array` (by default, `std::vector<toml::value>`)
+ - It depends. See [customizing containers](#customizing-containers) for detail.
+- `toml::table` (by default, `std::unordered_map<toml::key, toml::value>`)
+ - It depends. See [customizing containers](#customizing-containers) for detail.
+
+To get a value inside, you can use `toml::get<T>()`. The usage is the same as
+`toml::find<T>` (actually, `toml::find` internally uses `toml::get` after casting
+a value to `toml::table`).
+
+``` cpp
+const toml::value data = toml::parse("sample.toml");
+const toml::value answer_ = toml::get<toml::table >(data).at("answer");
+const std::int64_t answer = toml::get<std::int64_t>(answer_);
+```
+
+When you pass an exact TOML type that does not require type conversion,
+`toml::get` returns a reference through which you can modify the content
+(if the `toml::value` is `const`, it returns `const` reference).
+
+```cpp
+toml::value data = toml::parse("sample.toml");
+toml::value answer_ = toml::get<toml::table >(data).at("answer");
+toml::integer& answer = toml::get<toml::integer>(answer_);
+answer = 6 * 9; // write to data.answer. now `answer_` contains 54.
+```
+
+If the specified type requires conversion, you can't take a reference to the value.
+See also [underlying types](#underlying-types).
+
+It also throws a `toml::type_error` if the type differs.
+
+### `as_xxx`
+
+You can also use a member function to cast a value.
+
+```cpp
+const std::int64_t answer = data.as_table().at("answer").as_integer();
+```
+
+It also throws a `toml::type_error` if the type differs. If you are sure that
+the value `v` contains a value of the specified type, you can suppress checking
+by passing `std::nothrow`.
+
+```cpp
+const auto& answer = data.as_table().at("answer");
+if(answer.is_integer() && answer.as_integer(std::nothrow) == 42)
+{
+ std::cout << "value is 42" << std::endl;
+}
+```
+
+If `std::nothrow` is passed, the functions are marked as noexcept.
+
+By casting a `toml::value` into an array or a table, you can iterate over the
+elements.
+
+```cpp
+const auto data = toml::parse("example.toml");
+std::cout << "keys in the top-level table are the following: \n";
+for(const auto& [k, v] : data.as_table())
+{
+ std::cout << k << '\n';
+}
+
+const auto& fruits = toml::find(data, "fruits");
+for(const auto& v : fruits.as_array())
+{
+ std::cout << toml::find<std::string>(v, "name") << '\n';
+}
+```
+
+The full list of the functions is below.
+
+```cpp
+namespace toml {
+class value {
+ // ...
+ const boolean& as_boolean() const&;
+ const integer& as_integer() const&;
+ const floating& as_floating() const&;
+ const string& as_string() const&;
+ const offset_datetime& as_offset_datetime() const&;
+ const local_datetime& as_local_datetime() const&;
+ const local_date& as_local_date() const&;
+ const local_time& as_local_time() const&;
+ const array& as_array() const&;
+ const table& as_table() const&;
+ // --------------------------------------------------------
+ // non-const version
+ boolean& as_boolean() &;
+ // ditto...
+ // --------------------------------------------------------
+ // rvalue version
+ boolean&& as_boolean() &&;
+ // ditto...
+
+ // --------------------------------------------------------
+ // noexcept versions ...
+ const boolean& as_boolean(const std::nothrow_t&) const& noexcept;
+ boolean& as_boolean(const std::nothrow_t&) & noexcept;
+ boolean&& as_boolean(const std::nothrow_t&) && noexcept;
+ // ditto...
+};
+} // toml
+```
+
+### `at()`
+
+You can access to the element of a table and an array by `toml::basic_value::at`.
+
+```cpp
+const toml::value v{1,2,3,4,5};
+std::cout << v.at(2).as_integer() << std::endl; // 3
+
+const toml::value v{{"foo", 42}, {"bar", 3.14}};
+std::cout << v.at("foo").as_integer() << std::endl; // 42
+```
+
+If an invalid key (integer for a table, string for an array), it throws
+`toml::type_error` for the conversion. If the provided key is out-of-range,
+it throws `std::out_of_range`.
+
+Note that, although `std::string` has `at()` member function, `toml::value::at`
+throws if the contained type is a string. Because `std::string` does not
+contain `toml::value`.
+
+### `operator[]`
+
+You can also access to the element of a table and an array by
+`toml::basic_value::operator[]`.
+
+```cpp
+const toml::value v{1,2,3,4,5};
+std::cout << v[2].as_integer() << std::endl; // 3
+
+const toml::value v{{"foo", 42}, {"bar", 3.14}};
+std::cout << v["foo"].as_integer() << std::endl; // 42
+```
+
+When you access to a `toml::value` that is not initialized yet via
+`operator[](const std::string&)`, the `toml::value` will be a table,
+just like the `std::map`.
+
+```cpp
+toml::value v; // not initialized as a table.
+v["foo"] = 42; // OK. `v` will be a table.
+```
+
+Contrary, if you access to a `toml::value` that contains an array via `operator[]`,
+it does not check anything. It converts `toml::value` without type check and then
+access to the n-th element without boundary check, just like the `std::vector::operator[]`.
+
+```cpp
+toml::value v; // not initialized as an array
+v[2] = 42; // error! UB
+```
+
+Please make sure that the `toml::value` has an array inside when you access to
+its element via `operator[]`.
+
+## Checking value type
+
+You can check the type of a value by `is_xxx` function.
+
+```cpp
+const toml::value v = /* ... */;
+if(v.is_integer())
+{
+ std::cout << "value is an integer" << std::endl;
+}
+```
+
+The complete list of the functions is below.
+
+```cpp
+namespace toml {
+class value {
+ // ...
+ bool is_boolean() const noexcept;
+ bool is_integer() const noexcept;
+ bool is_floating() const noexcept;
+ bool is_string() const noexcept;
+ bool is_offset_datetime() const noexcept;
+ bool is_local_datetime() const noexcept;
+ bool is_local_date() const noexcept;
+ bool is_local_time() const noexcept;
+ bool is_array() const noexcept;
+ bool is_table() const noexcept;
+ bool is_uninitialized() const noexcept;
+ // ...
+};
+} // toml
+```
+
+Also, you can get `enum class value_t` from `toml::value::type()`.
+
+```cpp
+switch(data.at("something").type())
+{
+ case toml::value_t::integer: /*do some stuff*/ ; break;
+ case toml::value_t::floating: /*do some stuff*/ ; break;
+ case toml::value_t::string : /*do some stuff*/ ; break;
+ default : throw std::runtime_error(
+ "unexpected type : " + toml::stringize(data.at("something").type()));
+}
+```
+
+The complete list of the `enum`s can be found in the section
+[underlying types](#underlying-types).
+
+The `enum`s can be used as a parameter of `toml::value::is` function like the following.
+
+```cpp
+toml::value v = /* ... */;
+if(v.is(toml::value_t::boolean)) // ...
+```
+
+## More about conversion
+
+Since `toml::find` internally uses `toml::get`, all the following examples work
+with both `toml::get` and `toml::find`.
+
+### Converting an array
+
+You can get any kind of `container` class from a `toml::array`
+except for `map`-like classes.
+
+``` cpp
+// # sample.toml
+// numbers = [1,2,3]
+
+const auto numbers = toml::find(data, "numbers");
+
+const auto vc = toml::get<std::vector<int> >(numbers);
+const auto ls = toml::get<std::list<int> >(numbers);
+const auto dq = toml::get<std::deque<int> >(numbers);
+const auto ar = toml::get<std::array<int, 3>>(numbers);
+// if the size of data.at("numbers") is larger than that of std::array,
+// it will throw toml::type_error because std::array is not resizable.
+```
+
+Surprisingly, you can convert `toml::array` into `std::pair` and `std::tuple`.
+
+```cpp
+// numbers = [1,2,3]
+const auto tp = toml::get<std::tuple<short, int, unsigned int>>(numbers);
+```
+
+This functionality is helpful when you have a toml file like the following.
+
+```toml
+array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this
+```
+
+What is the corresponding C++ type?
+Obviously, it is a `std::pair` of `std::vector`s.
+
+```cpp
+const auto array_of_arrays = toml::find(data, "array_of_arrays");
+const auto aofa = toml::get<
+ std::pair<std::vector<int>, std::vector<std::string>>
+ >(array_of_arrays);
+```
+
+If you don't know the type of the elements, you can use `toml::array`,
+which is a `std::vector` of `toml::value`, instead.
+
+```cpp
+const auto a_of_a = toml::get<toml::array>(array_of_arrays);
+const auto first = toml::get<std::vector<int>>(a_of_a.at(0));
+```
+
+You can change the implementation of `toml::array` with `std::deque` or some
+other array-like container. See [Customizing containers](#customizing-containers)
+for detail.
+
+### Converting a table
+
+When all the values of the table have the same type, toml11 allows you to
+convert a `toml::table` to a `map` that contains the convertible type.
+
+```toml
+[tab]
+key1 = "foo" # all the values are
+key2 = "bar" # toml String
+```
+
+```cpp
+const auto data = toml::parse("sample.toml");
+const auto tab = toml::find<std::map<std::string, std::string>>(data, "tab");
+std::cout << tab["key1"] << std::endl; // foo
+std::cout << tab["key2"] << std::endl; // bar
+```
+
+But since `toml::table` is just an alias of `std::unordered_map<toml::key, toml::value>`,
+normally you don't need to convert it because it has all the functionalities that
+`std::unordered_map` has (e.g. `operator[]`, `count`, and `find`). In most cases
+`toml::table` is sufficient.
+
+```cpp
+toml::table tab = toml::get<toml::table>(data);
+if(data.count("title") != 0)
+{
+ data["title"] = std::string("TOML example");
+}
+```
+
+You can change the implementation of `toml::table` with `std::map` or some
+other map-like container. See [Customizing containers](#customizing-containers)
+for detail.
+
+### Getting an array of tables
+
+An array of tables is just an array of tables.
+You can get it in completely the same way as the other arrays and tables.
+
+```toml
+# sample.toml
+array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}]
+
+[[array_of_tables]]
+key = "value4"
+[[array_of_tables]]
+key = "value5"
+[[array_of_tables]]
+key = "value6"
+```
+
+```cpp
+const auto data = toml::parse("sample.toml");
+const auto aot1 = toml::find<std::vector<toml::table>>(data, "array_of_inline_tables");
+const auto aot2 = toml::find<std::vector<toml::table>>(data, "array_of_tables");
+```
+
+### Cost of conversion
+
+Although conversion through `toml::(get|find)` is convenient, it has additional
+copy-cost because it copies data contained in `toml::value` to the
+user-specified type. Of course in some cases this overhead is not ignorable.
+
+```cpp
+// the following code constructs a std::vector.
+// it requires heap allocation for vector and element conversion.
+const auto array = toml::find<std::vector<int>>(data, "foo");
+```
+
+By passing the exact types, `toml::get` returns reference that has no overhead.
+
+``` cpp
+const auto& tab = toml::find<toml::table>(data, "tab");
+const auto& numbers = toml::find<toml::array>(data, "numbers");
+```
+
+Also, `as_xxx` are zero-overhead because they always return a reference.
+
+``` cpp
+const auto& tab = toml::find(data, "tab" ).as_table();
+const auto& numbers = toml::find(data, "numbers").as_array();
+```
+
+In this case you need to call `toml::get` each time you access to
+the element of `toml::array` because `toml::array` is an array of `toml::value`.
+
+```cpp
+const auto& num0 = toml::get<toml::integer>(numbers.at(0));
+const auto& num1 = toml::get<toml::integer>(numbers.at(1));
+const auto& num2 = toml::get<toml::integer>(numbers.at(2));
+```
+
+### Converting datetime and its variants
+
+TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`,
+`local_datetime`, and `offset_datetime`.
+
+Since `local_date`, `local_datetime`, and `offset_datetime` represent a time
+point, you can convert them to `std::chrono::system_clock::time_point`.
+
+Contrary, `local_time` does not represents a time point because they lack a
+date information, but it can be converted to `std::chrono::duration` that
+represents a duration from the beginning of the day, `00:00:00.000`.
+
+```toml
+# sample.toml
+date = 2018-12-23
+time = 12:30:00
+l_dt = 2018-12-23T12:30:00
+o_dt = 2018-12-23T12:30:00+09:30
+```
+
+```cpp
+const auto data = toml::parse("sample.toml");
+
+const auto date = toml::get<std::chrono::system_clock::time_point>(data.at("date"));
+const auto l_dt = toml::get<std::chrono::system_clock::time_point>(data.at("l_dt"));
+const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt"));
+
+const auto time = toml::get<std::chrono::minutes>(data.at("time")); // 12 * 60 + 30 min
+```
+
+`local_date` and `local_datetime` are assumed to be in the local timezone when
+they are converted into `time_point`. On the other hand, `offset_datetime` only
+uses the offset part of the data and it does not take local timezone into account.
+
+To contain datetime data, toml11 defines its own datetime types.
+For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp).
+
+## Getting with a fallback
+
+`toml::find_or` returns a default value if the value is not found or has a
+different type.
+
+```cpp
+const auto data = toml::parse("example.toml");
+const auto num = toml::find_or(data, "num", 42);
+```
+
+It works recursively if you pass several keys for subtables.
+In that case, the last argument is considered to be the optional value.
+All other arguments between `toml::value` and the optinoal value are considered as keys.
+
+```cpp
+// [fruit.physical]
+// color = "red"
+auto data = toml::parse("fruit.toml");
+auto color = toml::find_or(data, "fruit", "physical", "color", "red");
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
+// arguments optional value
+```
+
+Also, `toml::get_or` returns a default value if `toml::get<T>` failed.
+
+```cpp
+toml::value v("foo"); // v contains String
+const int value = toml::get_or(v, 42); // conversion fails. it returns 42.
+```
+
+These functions automatically deduce what type you want to get
+from the default value you passed.
+
+To get a reference through this function, take care about the default value.
+
+```cpp
+toml::value v("foo"); // v contains String
+toml::integer& i = toml::get_or(v, 42); // does not work because binding `42`
+ // to `integer&` is invalid
+toml::integer opt = 42;
+toml::integer& i = toml::get_or(v, opt); // this works.
+```
+
+## Expecting conversion
+
+By using `toml::expect`, you will get your expected value or an error message
+without throwing `toml::type_error`.
+
+```cpp
+const auto value = toml::expect<std::string>(data.at("title"));
+if(value.is_ok()) {
+ std::cout << value.unwrap() << std::endl;
+} else {
+ std::cout << value.unwrap_err() << std::endl;
+}
+```
+
+Also, you can pass a function object to modify the expected value.
+
+```cpp
+const auto value = toml::expect<int>(data.at("number"))
+ .map(// function that receives expected type (here, int)
+ [](const int number) -> double {
+ return number * 1.5 + 1.0;
+ }).unwrap_or(/*default value =*/ 3.14);
+```
+
+## Visiting a toml::value
+
+toml11 provides `toml::visit` to apply a function to `toml::value` in the
+same way as `std::variant`.
+
+```cpp
+const toml::value v(3.14);
+toml::visit([](const auto& val) -> void {
+ std::cout << val << std::endl;
+ }, v);
+```
+
+The function object that would be passed to `toml::visit` must be able to
+receive all the possible TOML types. Also, the result types should be the same
+each other.
+
+## Constructing a toml::value
+
+`toml::value` can be constructed in various ways.
+
+```cpp
+toml::value v(true); // boolean
+toml::value v(42); // integer
+toml::value v(3.14); // floating
+toml::value v("foobar"); // string
+toml::value v(toml::local_date(2019, toml::month_t::Apr, 1)); // date
+toml::value v{1, 2, 3, 4, 5}; // array
+toml::value v{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; // table
+```
+
+When constructing a string, you can choose to use either literal or basic string.
+By default, it will be a basic string.
+
+```cpp
+toml::value v("foobar", toml::string_t::basic );
+toml::value v("foobar", toml::string_t::literal);
+```
+
+Datetime objects can be constructed from `std::tm` and
+`std::chrono::system_clock::time_point`. But you need to specify what type
+you use to avoid ambiguity.
+
+```cpp
+const auto now = std::chrono::system_clock::now();
+toml::value v(toml::local_date(now));
+toml::value v(toml::local_datetime(now));
+toml::value v(toml::offset_datetime(now));
+```
+
+Since local time is not equivalent to a time point, because it lacks date
+information, it will be constructed from `std::chrono::duration`.
+
+```cpp
+toml::value v(toml::local_time(std::chrono::hours(10)));
+```
+
+You can construct an array object not only from `initializer_list`, but also
+from STL containers. In that case, the element type must be convertible to
+`toml::value`.
+
+```cpp
+std::vector<int> vec{1,2,3,4,5};
+toml::value v(vec);
+```
+
+When you construct an array value, all the elements of `initializer_list`
+must be convertible into `toml::value`.
+
+If a `toml::value` has an array, you can `push_back` an element in it.
+
+```cpp
+toml::value v{1,2,3,4,5};
+v.push_back(6);
+```
+
+`emplace_back` also works.
+
+## Preserving comments
+
+toml11 v3 or later allows you yo choose whether comments are preserved or not via template parameter
+
+```cpp
+const auto data1 = toml::parse<toml::discard_comments >("example.toml");
+const auto data2 = toml::parse<toml::preserve_comments>("example.toml");
+```
+
+or macro definition.
+
+```cpp
+#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
+#include <toml11/toml.hpp>
+```
+
+This feature is controlled by template parameter in `toml::basic_value<...>`.
+`toml::value` is an alias of `toml::basic_value<...>`.
+
+If template parameter is explicitly specified, the return value of `toml::parse`
+will be `toml::basic_value<toml::preserve_comments>`.
+If the macro is defined, the alias `toml::value` will be
+`toml::basic_value<toml::preserve_comments>`.
+
+Comments related to a value can be obtained by `toml::value::comments()`.
+The return value has the same interface as `std::vector<std::string>`.
+
+```cpp
+const auto& com = v.comments();
+for(const auto& c : com)
+{
+ std::cout << c << std::endl;
+}
+```
+
+Comments just before and just after (within the same line) a value are kept in a value.
+
+```toml
+# this is a comment for v1.
+v1 = "foo"
+
+v2 = "bar" # this is a comment for v2.
+# Note that this comment is NOT a comment for v2.
+
+# this comment is not related to any value
+# because there are empty lines between v3.
+# this comment will be ignored even if you set `preserve_comments`.
+
+# this is a comment for v3
+# this is also a comment for v3.
+v3 = "baz" # ditto.
+```
+
+Each comment line becomes one element of a `std::vector`.
+
+Hash signs will be removed, but spaces after hash sign will not be removed.
+
+```cpp
+v1.comments().at(0) == " this is a comment for v1."s;
+
+v2.comments().at(1) == " this is a comment for v1."s;
+
+v3.comments().at(0) == " this is a comment for v3."s;
+v3.comments().at(1) == " this is also a comment for v3."s;
+v3.comments().at(2) == " ditto."s;
+```
+
+Note that a comment just after an opening brace of an array will not be a
+comment for the array.
+
+```toml
+# this is a comment for a.
+a = [ # this is not a comment for a. this will be ignored.
+ 1, 2, 3,
+ # this is a comment for `42`.
+ 42, # this is also a comment for `42`.
+ 5
+] # this is a comment for a.
+```
+
+You can also append and modify comments.
+The interfaces are the same as `std::vector<std::string>`.
+
+```cpp
+toml::basic_value<toml::preserve_comments> v(42);
+v.comments().push_back(" add this comment.");
+// # add this comment.
+// i = 42
+```
+
+Also, you can pass a `std::vector<std::string>` when constructing a
+`toml::basic_value<toml::preserve_comments>`.
+
+```cpp
+std::vector<std::string> comments{"comment 1", "comment 2"};
+const toml::basic_value<toml::preserve_comments> v1(42, std::move(comments));
+const toml::basic_value<toml::preserve_comments> v2(42, {"comment 1", "comment 2"});
+```
+
+When `toml::discard_comments` is chosen, comments will not be contained in a value.
+`value::comments()` will always be kept empty.
+All the modification on comments would be ignored.
+All the element access in a `discard_comments` causes the same error as accessing
+an element of an empty `std::vector`.
+
+The comments will also be serialized. If comments exist, those comments will be
+added just before the values.
+
+__NOTE__: Result types from `toml::parse(...)` and
+`toml::parse<toml::preserve_comments>(...)` are different.
+
+## Customizing containers
+
+Actually, `toml::basic_value` has 3 template arguments.
+
+```cpp
+template<typename Comment, // discard/preserve_comment
+ template<typename ...> class Table = std::unordered_map,
+ template<typename ...> class Array = std::vector>
+class basic_value;
+```
+
+This enables you to change the containers used inside. E.g. you can use
+`std::map` to contain a table object instead of `std::unordered_map`.
+And also can use `std::deque` as a array object instead of `std::vector`.
+
+You can set these parameters while calling `toml::parse` function.
+
+```cpp
+const auto data = toml::parse<
+ toml::preserve_comments, std::map, std::deque
+ >("example.toml");
+```
+
+Needless to say, the result types from `toml::parse(...)` and
+`toml::parse<Com, Map, Cont>(...)` are different (unless you specify the same
+types as default).
+
+Note that, since `toml::table` and `toml::array` is an alias for a table and an
+array of a default `toml::value`, so it is different from the types actually
+contained in a `toml::basic_value` when you customize containers.
+To get the actual type in a generic way, use
+`typename toml::basic_type<C, T, A>::table_type` and
+`typename toml::basic_type<C, T, A>::array_type`.
+
+## TOML literal
+
+toml11 supports `"..."_toml` literal.
+It accept both a bare value and a file content.
+
+```cpp
+using namespace toml::literals::toml_literals;
+
+// `_toml` can convert a bare value without key
+const toml::value v = u8"0xDEADBEEF"_toml;
+// v is an Integer value containing 0xDEADBEEF.
+
+// raw string literal (`R"(...)"` is useful for this purpose)
+const toml::value t = u8R"(
+ title = "this is TOML literal"
+ [table]
+ key = "value"
+)"_toml;
+// the literal will be parsed and the result will be contained in t
+```
+
+The literal function is defined in the same way as the standard library literals
+such as `std::literals::string_literals::operator""s`.
+
+```cpp
+namespace toml
+{
+inline namespace literals
+{
+inline namespace toml_literals
+{
+toml::value operator"" _toml(const char* str, std::size_t len);
+} // toml_literals
+} // literals
+} // toml
+```
+
+Access to the operator can be gained with `using namespace toml::literals;`,
+`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`.
+
+Note that a key that is composed only of digits is allowed in TOML.
+And, unlike the file parser, toml-literal allows a bare value without a key.
+Thus it is difficult to distinguish arrays having integers and definitions of
+tables that are named as digits.
+Currently, literal `[1]` becomes a table named "1".
+To ensure a literal to be considered as an array with one element, you need to
+add a comma after the first element (like `[1,]`).
+
+```cpp
+"[1,2,3]"_toml; // This is an array
+"[table]"_toml; // This is a table that has an empty table named "table" inside.
+"[[1,2,3]]"_toml; // This is an array of arrays
+"[[table]]"_toml; // This is a table that has an array of tables inside.
+
+"[[1]]"_toml; // This literal is ambiguous.
+ // Currently, it becomes a table that has array of table "1".
+"1 = [{}]"_toml; // This is a table that has an array of table named 1.
+"[[1,]]"_toml; // This is an array of arrays.
+"[[1],]"_toml; // ditto.
+```
+
+NOTE: `_toml` literal returns a `toml::value` that does not have comments.
+
+## Conversion between toml value and arbitrary types
+
+You can also use `toml::get` and other related functions with the types
+you defined after you implement a way to convert it.
+
+```cpp
+namespace ext
+{
+struct foo
+{
+ int a;
+ double b;
+ std::string c;
+};
+} // ext
+
+const auto data = toml::parse("example.toml");
+
+// to do this
+const foo f = toml::find<ext::foo>(data, "foo");
+```
+
+There are 3 ways to use `toml::get` with the types that you defined.
+
+The first one is to implement `from_toml(const toml::value&)` member function.
+
+```cpp
+namespace ext
+{
+struct foo
+{
+ int a;
+ double b;
+ std::string c;
+
+ void from_toml(const toml::value& v)
+ {
+ this->a = toml::find<int >(v, "a");
+ this->b = toml::find<double >(v, "b");
+ this->c = toml::find<std::string>(v, "c");
+ return;
+ }
+};
+} // ext
+```
+
+In this way, because `toml::get` first constructs `foo` without arguments,
+the type should be default-constructible.
+
+The second is to implement `constructor(const toml::value&)`.
+
+```cpp
+namespace ext
+{
+struct foo
+{
+ explicit foo(const toml::value& v)
+ : a(toml::find<int>(v, "a")), b(toml::find<double>(v, "b")),
+ c(toml::find<std::string>(v, "c"))
+ {}
+
+ int a;
+ double b;
+ std::string c;
+};
+} // ext
+```
+
+Note that implicit default constructor declaration will be suppressed
+when a constructor is defined. If you want to use the struct (here, `foo`)
+in a container (e.g. `std::vector<foo>`), you may need to define default
+constructor explicitly.
+
+The third is to implement specialization of `toml::from` for your type.
+
+```cpp
+namespace ext
+{
+struct foo
+{
+ int a;
+ double b;
+ std::string c;
+};
+} // ext
+
+namespace toml
+{
+template<>
+struct from<ext::foo>
+{
+ static ext::foo from_toml(const value& v)
+ {
+ ext::foo f;
+ f.a = find<int >(v, "a");
+ f.b = find<double >(v, "b");
+ f.c = find<std::string>(v, "c");
+ return f;
+ }
+};
+} // toml
+```
+
+In this way, since the conversion function is defined outside of the class,
+you can add conversion between `toml::value` and classes defined in another library.
+
+In some cases, a class has a templatized constructor that takes a template, `T`.
+It confuses `toml::get/find<T>` because it makes the class "constructible" from
+`toml::value`. To avoid this problem, `toml::from` and `from_toml` always
+precede constructor. It makes easier to implement conversion between
+`toml::value` and types defined in other libraries because it skips constructor.
+
+But, importantly, you cannot define `toml::from<T>` and `T.from_toml` at the same
+time because it causes ambiguity in the overload resolution of `toml::get<T>` and `toml::find<T>`.
+
+So the precedence is `toml::from<T>` == `T.from_toml()` > `T(toml::value)`.
+
+If you want to convert any versions of `toml::basic_value`,
+you need to templatize the conversion function as follows.
+
+```cpp
+struct foo
+{
+ template<typename C, template<typename ...> class M, template<typename ...> class A>
+ void from_toml(const toml::basic_value<C, M, A>& v)
+ {
+ this->a = toml::find<int >(v, "a");
+ this->b = toml::find<double >(v, "b");
+ this->c = toml::find<std::string>(v, "c");
+ return;
+ }
+};
+// or
+namespace toml
+{
+template<>
+struct from<ext::foo>
+{
+ template<typename C, template<typename ...> class M, template<typename ...> class A>
+ static ext::foo from_toml(const basic_value<C, M, A>& v)
+ {
+ ext::foo f;
+ f.a = find<int >(v, "a");
+ f.b = find<double >(v, "b");
+ f.c = find<std::string>(v, "c");
+ return f;
+ }
+};
+} // toml
+```
+
+----
+
+The opposite direction is also supported in a similar way. You can directly
+pass your type to `toml::value`'s constructor by introducing `into_toml` or
+`toml::into<T>`.
+
+```cpp
+namespace ext
+{
+struct foo
+{
+ int a;
+ double b;
+ std::string c;
+
+ toml::value into_toml() const // you need to mark it const.
+ {
+ return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}};
+ }
+};
+} // ext
+
+ext::foo f{42, 3.14, "foobar"};
+toml::value v(f);
+```
+
+The definition of `toml::into<T>` is similar to `toml::from<T>`.
+
+```cpp
+namespace ext
+{
+struct foo
+{
+ int a;
+ double b;
+ std::string c;
+};
+} // ext
+
+namespace toml
+{
+template<>
+struct into<ext::foo>
+{
+ static toml::value into_toml(const ext::foo& f)
+ {
+ return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}};
+ }
+};
+} // toml
+
+ext::foo f{42, 3.14, "foobar"};
+toml::value v(f);
+```
+
+Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
+`toml::array` are okay to return from `into_toml`.
+
+You can also return a custom `toml::basic_value` from `toml::into`.
+
+```cpp
+namespace toml
+{
+template<>
+struct into<ext::foo>
+{
+ static toml::basic_value<toml::preserve_comments> into_toml(const ext::foo& f)
+ {
+ toml::basic_value<toml::preserve_comments> v{{"a", f.a}, {"b", f.b}, {"c", f.c}};
+ v.comments().push_back(" comment");
+ return v;
+ }
+};
+} // toml
+```
+
+But note that, if this `basic_value` would be assigned into other `toml::value`
+that discards `comments`, the comments would be dropped.
+
+### Macro to automatically define conversion functions
+
+There is a helper macro that automatically generates conversion functions `from` and `into` for a simple struct.
+
+```cpp
+namespace foo
+{
+struct Foo
+{
+ std::string s;
+ double d;
+ int i;
+};
+} // foo
+
+TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
+
+int main()
+{
+ const auto file = toml::parse("example.toml");
+ auto f = toml::find<foo::Foo>(file, "foo");
+}
+```
+
+And then you can use `toml::find<foo::Foo>(file, "foo");`
+
+**Note** that, because of a slight difference in implementation of preprocessor between gcc/clang and MSVC, [you need to define `/Zc:preprocessor`](https://github.com/ToruNiina/toml11/issues/139#issuecomment-803683682) to use it in MSVC (Thank you @glebm !).
+
+## Formatting user-defined error messages
+
+When you encounter an error after you read the toml value, you may want to
+show the error with the value.
+
+toml11 provides you a function that formats user-defined error message with
+related values. With a code like the following,
+
+```cpp
+const auto value = toml::find<int>(data, "num");
+if(value < 0)
+{
+ std::cerr << toml::format_error("[error] value should be positive",
+ data.at("num"), "positive number required")
+ << std::endl;
+}
+```
+
+you will get an error message like this.
+
+```console
+[error] value should be positive
+ --> example.toml
+ 3 | num = -42
+ | ~~~ positive number required
+```
+
+When you pass two values to `toml::format_error`,
+
+```cpp
+const auto min = toml::find<int>(range, "min");
+const auto max = toml::find<int>(range, "max");
+if(max < min)
+{
+ std::cerr << toml::format_error("[error] max should be larger than min",
+ data.at("min"), "minimum number here",
+ data.at("max"), "maximum number here");
+ << std::endl;
+}
+```
+
+you will get an error message like this.
+
+```console
+[error] max should be larger than min
+ --> example.toml
+ 3 | min = 54
+ | ~~ minimum number here
+ ...
+ 4 | max = 42
+ | ~~ maximum number here
+```
+
+You can print hints at the end of the message.
+
+```cpp
+std::vector<std::string> hints;
+hints.push_back("positive number means n >= 0.");
+hints.push_back("negative number is not positive.");
+std::cerr << toml::format_error("[error] value should be positive",
+ data.at("num"), "positive number required", hints)
+ << std::endl;
+```
+
+```console
+[error] value should be positive
+ --> example.toml
+ 2 | num = 42
+ | ~~ positive number required
+ |
+Hint: positive number means n >= 0.
+Hint: negative number is not positive.
+```
+
+## Obtaining location information
+
+You can also format error messages in your own way by using `source_location`.
+
+```cpp
+struct source_location
+{
+ std::uint_least32_t line() const noexcept;
+ std::uint_least32_t column() const noexcept;
+ std::uint_least32_t region() const noexcept;
+ std::string const& file_name() const noexcept;
+ std::string const& line_str() const noexcept;
+};
+// +-- line() +--- length of the region (here, region() == 9)
+// v .---+---.
+// 12 | value = "foo bar" <- line_str() returns the line itself.
+// ^-------- column() points here
+```
+
+You can get this by
+```cpp
+const toml::value v = /*...*/;
+const toml::source_location loc = v.location();
+```
+
+## Exceptions
+
+The following `exception` classes inherits `toml::exception` that inherits
+`std::exception`.
+
+```cpp
+namespace toml {
+struct exception : public std::exception {/**/};
+struct syntax_error : public toml::exception {/**/};
+struct type_error : public toml::exception {/**/};
+struct internal_error : public toml::exception {/**/};
+} // toml
+```
+
+`toml::exception` has `toml::exception::location()` member function that returns
+`toml::source_location`, in addition to `what()`.
+
+```cpp
+namespace toml {
+struct exception : public std::exception
+{
+ // ...
+ source_location const& location() const noexcept;
+};
+} // toml
+```
+
+It represents where the error occurs.
+
+`syntax_error` will be thrown from `toml::parse` and `_toml` literal.
+`type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and
+other functions that takes a content inside of `toml::value`.
+
+Note that, currently, from `toml::value::at()` and `toml::find(value, key)`
+may throw an `std::out_of_range` that does not inherits `toml::exception`.
+
+Also, in some cases, most likely in the file open error, it will throw an
+`std::runtime_error`.
+
+## Colorize Error Messages
+
+By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from
+`toml::parse` and `toml::find|get` will be colorized. By default, this feature
+is turned off.
+
+With the following toml file taken from `toml-lang/toml/tests/hard_example.toml`,
+
+```toml
+[error]
+array = [
+ "This might most likely happen in multiline arrays",
+ Like here,
+ "or here,
+ and here"
+ ] End of array comment, forgot the #
+```
+
+the error message would be like this.
+
+![error-message-1](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-1.png)
+
+With the following,
+
+```toml
+[error]
+# array = [
+# "This might most likely happen in multiline arrays",
+# Like here,
+# "or here,
+# and here"
+# ] End of array comment, forgot the #
+number = 3.14 pi <--again forgot the #
+```
+
+the error message would be like this.
+
+![error-message-2](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-2.png)
+
+The message would be messy when it is written to a file, not a terminal because
+it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code).
+
+Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined
+error message by passing `true` to the `toml::format_error` function.
+If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default.
+If not, the default value would be `false`.
+
+```cpp
+std::cerr << toml::format_error("[error] value should be positive",
+ data.at("num"), "positive number required",
+ hints, /*colorize = */ true) << std::endl;
+```
+
+Note: It colorize `[error]` in red. That means that it detects `[error]` prefix
+at the front of the error message. If there is no `[error]` prefix,
+`format_error` adds it to the error message.
+
+## Serializing TOML data
+
+toml11 enables you to serialize data into toml format.
+
+```cpp
+const toml::value data{{"foo", 42}, {"bar", "baz"}};
+std::cout << data << std::endl;
+// bar = "baz"
+// foo = 42
+```
+
+toml11 automatically makes a small table and small array inline.
+You can specify the width to make them inline by `std::setw` for streams.
+
+```cpp
+const toml::value data{
+ {"qux", {{"foo", 42}, {"bar", "baz"}}},
+ {"quux", {"small", "array", "of", "strings"}},
+ {"foobar", {"this", "array", "of", "strings", "is", "too", "long",
+ "to", "print", "into", "single", "line", "isn't", "it?"}},
+};
+
+// the threshold becomes 80.
+std::cout << std::setw(80) << data << std::endl;
+// foobar = [
+// "this","array","of","strings","is","too","long","to","print","into",
+// "single","line","isn't","it?",
+// ]
+// quux = ["small","array","of","strings"]
+// qux = {bar="baz",foo=42}
+
+
+// the width is 0. nothing become inline.
+std::cout << std::setw(0) << data << std::endl;
+// foobar = [
+// "this",
+// ... (snip)
+// "it?",
+// ]
+// quux = [
+// "small",
+// "array",
+// "of",
+// "strings",
+// ]
+// [qux]
+// bar = "baz"
+// foo = 42
+```
+
+It is recommended to set width before printing data. Some I/O functions changes
+width to 0, and it makes all the stuff (including `toml::array`) multiline.
+The resulting files becomes too long.
+
+To control the precision of floating point numbers, you need to pass
+`std::setprecision` to stream.
+
+```cpp
+const toml::value data{
+ {"pi", 3.141592653589793},
+ {"e", 2.718281828459045}
+};
+std::cout << std::setprecision(17) << data << std::endl;
+// e = 2.7182818284590451
+// pi = 3.1415926535897931
+std::cout << std::setprecision( 7) << data << std::endl;
+// e = 2.718282
+// pi = 3.141593
+```
+
+There is another way to format toml values, `toml::format()`.
+It returns `std::string` that represents a value.
+
+```cpp
+const toml::value v{{"a", 42}};
+const std::string fmt = toml::format(v);
+// a = 42
+```
+
+Note that since `toml::format` formats a value, the resulting string may lack
+the key value.
+
+```cpp
+const toml::value v{3.14};
+const std::string fmt = toml::format(v);
+// 3.14
+```
+
+To control the width and precision, `toml::format` receives optional second and
+third arguments to set them. By default, the width is 80 and the precision is
+`std::numeric_limits<double>::max_digit10`.
+
+```cpp
+const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17);
+```
+
+When you pass a comment-preserving-value, the comment will also be serialized.
+An array or a table containing a value that has a comment would not be inlined.
+
+## Underlying types
+
+The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below.
+
+| TOML type | underlying c++ type | enum class |
+| -------------- | ---------------------------------- | -------------------------------- |
+| Boolean | `bool` | `toml::value_t::boolean` |
+| Integer | `std::int64_t` | `toml::value_t::integer` |
+| Float | `double` | `toml::value_t::floating` |
+| String | `toml::string` | `toml::value_t::string` |
+| LocalDate | `toml::local_date` | `toml::value_t::local_date` |
+| LocalTime | `toml::local_time` | `toml::value_t::local_time` |
+| LocalDatetime | `toml::local_datetime` | `toml::value_t::local_datetime` |
+| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offset_datetime` |
+| Array | `array-like<toml::value>` | `toml::value_t::array` |
+| Table | `map-like<toml::key, toml::value>` | `toml::value_t::table` |
+
+`array-like` and `map-like` are the STL containers that works like a `std::vector` and
+`std::unordered_map`, respectively. By default, `std::vector` and `std::unordered_map`
+are used. See [Customizing containers](#customizing-containers) for detail.
+
+`toml::string` is effectively the same as `std::string` but has an additional
+flag that represents a kind of a string, `string_t::basic` and `string_t::literal`.
+Although `std::string` is not an exact toml type, still you can get a reference
+that points to internal `std::string` by using `toml::get<std::string>()` for convenience.
+The most important difference between `std::string` and `toml::string` is that
+`toml::string` will be formatted as a TOML string when outputted with `ostream`.
+This feature is introduced to make it easy to write a custom serializer.
+
+`Datetime` variants are `struct` that are defined in this library.
+Because `std::chrono::system_clock::time_point` is a __time point__,
+not capable of representing a Local Time independent from a specific day.
+
+## Unreleased TOML features
+
+Since TOML v1.0.0-rc.1 has been released, those features are now activated by
+default. We no longer need to define `TOML11_USE_UNRELEASED_FEATURES`.
+
+- Leading zeroes in exponent parts of floats are permitted.
+ - e.g. `1.0e+01`, `5e+05`
+ - [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656)
+- Allow raw tab characters in basic strings and multi-line basic strings.
+ - [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627)
+- Allow heterogeneous arrays
+ - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676)
+
+## Note about heterogeneous arrays
+
+Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value`
+does not. Here the reason is explained.
+
+```cpp
+// this won't be compiled
+toml::value v{
+ "foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}}
+}
+```
+
+There is a workaround for this. By explicitly converting values into
+`toml::value`, you can initialize `toml::value` with a heterogeneous array.
+Also, you can first initialize a `toml::value` with an array and then
+`push_back` into it.
+
+```cpp
+// OK!
+toml::value v{
+ toml::value("foo"), toml::value(3.14), toml::value(42),
+ toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
+}
+
+// OK!
+toml::value v(toml::array{});
+v.push_back("foo");
+v.push_back(3.14);
+
+// OK!
+toml::array a;
+a.push_back("foo");
+a.push_back(3.14);
+toml::value v(std::move(a));
+```
+
+The reason why the first example is not allowed is the following.
+Let's assume that you are initializing a `toml::value` with a table.
+
+```cpp
+ // # expecting TOML table.
+toml::value v{ // [v]
+ {"answer", 42}, // answer = 42
+ {"pi", 3.14}, // pi = 3.14
+ {"foo", "bar"} // foo = "bar"
+};
+```
+
+This is indistinguishable from a (heterogeneous) TOML array definition.
+
+```toml
+v = [
+ ["answer", 42],
+ ["pi", 3.14],
+ ["foo", "bar"],
+]
+```
+
+This means that the above C++ code makes constructor's overload resolution
+ambiguous. So a constructor that allows both "table as an initializer-list" and
+"heterogeneous array as an initializer-list" cannot be implemented.
+
+Thus, although it is painful, we need to explicitly cast values into
+`toml::value` when you initialize heterogeneous array in a C++ code.
+
+```cpp
+toml::value v{
+ toml::value("foo"), toml::value(3.14), toml::value(42),
+ toml::value{1,2,3,4,5}, toml::value{{"key", "value"}}
+};
+```
+
+## Breaking Changes from v2
+
+Although toml11 is relatively new library (it's three years old now), it had
+some confusing and inconvenient user-interfaces because of historical reasons.
+
+Between v2 and v3, those interfaces are rearranged.
+
+- `toml::parse` now returns a `toml::value`, not `toml::table`.
+- `toml::value` is now an alias of `toml::basic_value<discard_comment, std::vector, std::unordered_map>`.
+ - See [Customizing containers](#customizing-containers) for detail.
+- The elements of `toml::value_t` are renamed as `snake_case`.
+ - See [Underlying types](#underlying-types) for detail.
+- Supports for the CamelCaseNames are dropped.
+ - See [Underlying types](#underlying-types) for detail.
+- `(is|as)_float` has been removed to make the function names consistent with others.
+ - Since `float` is a keyword, toml11 named a float type as `toml::floating`.
+ - Also a `value_t` corresponds to `toml::floating` is named `value_t::floating`.
+ - So `(is|as)_floating` is introduced and `is_float` has been removed.
+ - See [Casting a toml::value](#casting-a-tomlvalue) and [Checking value type](#checking-value-type) for detail.
+- An overload of `toml::find` for `toml::table` has been dropped. Use `toml::value` version instead.
+ - Because type conversion between a table and a value causes ambiguity while overload resolution
+ - Since `toml::parse` now returns a `toml::value`, this feature becomes less important.
+ - Also because `toml::table` is a normal STL container, implementing utility function is easy.
+ - See [Finding a toml::value](#finding-a-toml-value) for detail.
+- An overload of `operator<<` and `toml::format` for `toml::table`s are dropped.
+ - Use `toml::value` instead.
+ - See [Serializing TOML data](#serializing-toml-data) for detail.
+- Interface around comments.
+ - See [Preserving Comments](#preserving-comments) for detail.
+- An ancient `from_toml/into_toml` has been removed. Use arbitrary type conversion support.
+ - See [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) for detail.
+
+Such a big change will not happen in the coming years.
+
+## Running Tests
+
+After cloning this repository, run the following command (thank you @jwillikers
+for automating test set fetching!).
+
+```sh
+$ mkdir build
+$ cd build
+$ cmake .. -Dtoml11_BUILD_TEST=ON
+$ make
+$ make test
+```
+
+To run the language agnostic test suite, you need to compile
+`tests/check_toml_test.cpp` and pass it to the tester.
+
+## Contributors
+
+I appreciate the help of the contributors who introduced the great feature to this library.
+
+- Guillaume Fraux (@Luthaf)
+ - Windows support and CI on Appvayor
+ - Intel Compiler support
+- Quentin Khan (@xaxousis)
+ - Found & Fixed a bug around ODR
+ - Improved error messages for invalid keys to show the location where the parser fails
+- Petr Beneš (@wbenny)
+ - Fixed warnings on MSVC
+- Ivan Shynkarenka (@chronoxor)
+ - Fixed Visual Studio 2019 warnings
+- @khoitd1997
+ - Fixed warnings while type conversion
+- @KerstinKeller
+ - Added installation script to CMake
+- J.C. Moyer (@jcmoyer)
+ - Fixed an example code in the documentation
+- Jt Freeman (@blockparty-sh)
+ - Fixed feature test macro around `localtime_s`
+ - Suppress warnings in Debug mode
+- OGAWA Kenichi (@kenichiice)
+ - Suppress warnings on intel compiler
+- Jordan Williams (@jwillikers)
+ - Fixed clang range-loop-analysis warnings
+ - Fixed feature test macro to suppress -Wundef
+ - Use cache variables in CMakeLists.txt
+ - Automate test set fetching, update and refactor CMakeLists.txt
+- Scott McCaskill
+ - Parse 9 digits (nanoseconds) of fractional seconds in a `local_time`
+- Shu Wang (@halfelf)
+ - fix "Finding a value in an array" example in README
+- @maass-tv and @SeverinLeonhardt
+ - Fix MSVC warning C4866
+- OGAWA KenIchi (@kenichiice)
+ - Fix include path in README
+- Mohammed Alyousef (@MoAlyousef)
+ - Made testing optional in CMake
+- Ivan Shynkarenka (@chronoxor)
+ - Fix compilation error in `<filesystem>` with MinGW
+- Alex Merry (@amerry)
+ - Add missing include files
+- sneakypete81 (@sneakypete81)
+ - Fix typo in error message
+- Oliver Kahrmann (@founderio)
+ - Fix missing filename in error message if parsed file is empty
+- Karl Nilsson (@karl-nilsson)
+ - Fix many spelling errors
+- ohdarling88 (@ohdarling)
+ - Fix a bug in a constructor of serializer
+- estshorter (@estshorter)
+ - Fix MSVC warning C26478
+- Philip Top (@phlptp)
+ - Improve checking standard library feature availability check
+- Louis Marascio (@marascio)
+ - Fix free-nonheap-object warning
+
+
+## Licensing terms
+
+This product is licensed under the terms of the [MIT License](LICENSE).
+
+- Copyright (c) 2017-2021 Toru Niina
+
+All rights reserved.
diff --git a/src/toml11/toml.hpp b/src/toml11/toml.hpp
new file mode 100644
index 000000000..f34cfccca
--- /dev/null
+++ b/src/toml11/toml.hpp
@@ -0,0 +1,46 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Toru Niina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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 7
+#define TOML11_VERSION_PATCH 0
+
+#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/src/toml11/toml/color.hpp b/src/toml11/toml/color.hpp
new file mode 100644
index 000000000..4cb572cb0
--- /dev/null
+++ b/src/toml11/toml/color.hpp
@@ -0,0 +1,64 @@
+#ifndef TOML11_COLOR_HPP
+#define TOML11_COLOR_HPP
+#include <cstdint>
+#include <ostream>
+
+#ifdef TOML11_COLORIZE_ERROR_MESSAGE
+#define TOML11_ERROR_MESSAGE_COLORIZED true
+#else
+#define TOML11_ERROR_MESSAGE_COLORIZED false
+#endif
+
+namespace toml
+{
+
+// put ANSI escape sequence to ostream
+namespace color_ansi
+{
+namespace detail
+{
+inline int colorize_index()
+{
+ static const int index = std::ios_base::xalloc();
+ return index;
+}
+} // detail
+
+inline std::ostream& colorize(std::ostream& os)
+{
+ // by default, it is zero.
+ os.iword(detail::colorize_index()) = 1;
+ return os;
+}
+inline std::ostream& nocolorize(std::ostream& os)
+{
+ os.iword(detail::colorize_index()) = 0;
+ return os;
+}
+inline std::ostream& reset (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;}
+inline std::ostream& bold (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;}
+inline std::ostream& grey (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;}
+inline std::ostream& red (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;}
+inline std::ostream& green (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;}
+inline std::ostream& yellow (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;}
+inline std::ostream& blue (std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;}
+inline std::ostream& magenta(std::ostream& os)
+{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;}
+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;}
+} // color_ansi
+
+// ANSI escape sequence is the only and default colorization method currently
+namespace color = color_ansi;
+
+} // toml
+#endif// TOML11_COLOR_HPP
diff --git a/src/toml11/toml/combinator.hpp b/src/toml11/toml/combinator.hpp
new file mode 100644
index 000000000..33ecca1eb
--- /dev/null
+++ b/src/toml11/toml/combinator.hpp
@@ -0,0 +1,306 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_COMBINATOR_HPP
+#define TOML11_COMBINATOR_HPP
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+
+#include <array>
+#include <iomanip>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+
+#include "region.hpp"
+#include "result.hpp"
+#include "traits.hpp"
+#include "utility.hpp"
+
+// they scans characters and returns region if it matches to the condition.
+// when they fail, it does not change the location.
+// in lexer.hpp, these are used.
+
+namespace toml
+{
+namespace detail
+{
+
+// to output character as an error message.
+inline std::string show_char(const char c)
+{
+ // 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
+ // unsigned char through pointer. In general, converting pointer to a
+ // pointer that has different type cause UB, but `(signed|unsigned)?char`
+ // are one of the exceptions. Converting pointer only to char and std::byte
+ // (c++17) are valid.
+ if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c))))
+ {
+ return std::string(1, c);
+ }
+ else
+ {
+ std::array<char, 5> buf;
+ buf.fill('\0');
+ const auto r = std::snprintf(
+ buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
+ (void) r; // Unused variable warning
+ assert(r == static_cast<int>(buf.size()) - 1);
+ return std::string(buf.data());
+ }
+}
+
+template<char C>
+struct character
+{
+ static constexpr char target = C;
+
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ if(loc.iter() == loc.end()) {return none();}
+ const auto first = loc.iter();
+
+ const char c = *(loc.iter());
+ if(c != target)
+ {
+ return none();
+ }
+ loc.advance(); // update location
+
+ return ok(region(loc, first, loc.iter()));
+ }
+};
+template<char C>
+constexpr char character<C>::target;
+
+// closed interval [Low, Up]. both Low and Up are included.
+template<char Low, char Up>
+struct in_range
+{
+ // assuming ascii part of UTF-8...
+ static_assert(Low <= Up, "lower bound should be less than upper bound.");
+
+ static constexpr char upper = Up;
+ static constexpr char lower = Low;
+
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ if(loc.iter() == loc.end()) {return none();}
+ const auto first = loc.iter();
+
+ const char c = *(loc.iter());
+ if(c < lower || upper < c)
+ {
+ return none();
+ }
+
+ loc.advance();
+ return ok(region(loc, first, loc.iter()));
+ }
+};
+template<char L, char U> constexpr char in_range<L, U>::upper;
+template<char L, char U> constexpr char in_range<L, U>::lower;
+
+// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
+// for detecting invalid characters, like control sequences in toml string.
+template<typename Combinator>
+struct exclude
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ if(loc.iter() == loc.end()) {return none();}
+ auto first = loc.iter();
+
+ auto rslt = Combinator::invoke(loc);
+ if(rslt.is_ok())
+ {
+ loc.reset(first);
+ return none();
+ }
+ loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
+ return ok(region(loc, first, loc.iter()));
+ }
+};
+
+// increment `iter`, if matches. otherwise, just return empty string.
+template<typename Combinator>
+struct maybe
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ const auto rslt = Combinator::invoke(loc);
+ if(rslt.is_ok())
+ {
+ return rslt;
+ }
+ return ok(region(loc));
+ }
+};
+
+template<typename ... Ts>
+struct sequence;
+
+template<typename Head, typename ... Tail>
+struct sequence<Head, Tail...>
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ const auto first = loc.iter();
+ auto rslt = Head::invoke(loc);
+ if(rslt.is_err())
+ {
+ loc.reset(first);
+ return none();
+ }
+ return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first);
+ }
+
+ // called from the above function only, recursively.
+ template<typename Iterator>
+ static result<region, none_t>
+ invoke(location& loc, region reg, Iterator first)
+ {
+ const auto rslt = Head::invoke(loc);
+ if(rslt.is_err())
+ {
+ loc.reset(first);
+ return none();
+ }
+ reg += rslt.unwrap(); // concat regions
+ return sequence<Tail...>::invoke(loc, std::move(reg), first);
+ }
+};
+
+template<typename Head>
+struct sequence<Head>
+{
+ // would be called from sequence<T ...>::invoke only.
+ template<typename Iterator>
+ static result<region, none_t>
+ invoke(location& loc, region reg, Iterator first)
+ {
+ const auto rslt = Head::invoke(loc);
+ if(rslt.is_err())
+ {
+ loc.reset(first);
+ return none();
+ }
+ reg += rslt.unwrap(); // concat regions
+ return ok(reg);
+ }
+};
+
+template<typename ... Ts>
+struct either;
+
+template<typename Head, typename ... Tail>
+struct either<Head, Tail...>
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ const auto rslt = Head::invoke(loc);
+ if(rslt.is_ok()) {return rslt;}
+ return either<Tail...>::invoke(loc);
+ }
+};
+template<typename Head>
+struct either<Head>
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ return Head::invoke(loc);
+ }
+};
+
+template<typename T, typename N>
+struct repeat;
+
+template<std::size_t N> struct exactly{};
+template<std::size_t N> struct at_least{};
+struct unlimited{};
+
+template<typename T, std::size_t N>
+struct repeat<T, exactly<N>>
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ region retval(loc);
+ const auto first = loc.iter();
+ for(std::size_t i=0; i<N; ++i)
+ {
+ auto rslt = T::invoke(loc);
+ if(rslt.is_err())
+ {
+ loc.reset(first);
+ return none();
+ }
+ retval += rslt.unwrap();
+ }
+ return ok(std::move(retval));
+ }
+};
+
+template<typename T, std::size_t N>
+struct repeat<T, at_least<N>>
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ region retval(loc);
+
+ const auto first = loc.iter();
+ for(std::size_t i=0; i<N; ++i)
+ {
+ auto rslt = T::invoke(loc);
+ if(rslt.is_err())
+ {
+ loc.reset(first);
+ return none();
+ }
+ retval += rslt.unwrap();
+ }
+ while(true)
+ {
+ auto rslt = T::invoke(loc);
+ if(rslt.is_err())
+ {
+ return ok(std::move(retval));
+ }
+ retval += rslt.unwrap();
+ }
+ }
+};
+
+template<typename T>
+struct repeat<T, unlimited>
+{
+ static result<region, none_t>
+ invoke(location& loc)
+ {
+ region retval(loc);
+ while(true)
+ {
+ auto rslt = T::invoke(loc);
+ if(rslt.is_err())
+ {
+ return ok(std::move(retval));
+ }
+ retval += rslt.unwrap();
+ }
+ }
+};
+
+} // detail
+} // toml
+#endif// TOML11_COMBINATOR_HPP
diff --git a/src/toml11/toml/comments.hpp b/src/toml11/toml/comments.hpp
new file mode 100644
index 000000000..ec2504117
--- /dev/null
+++ b/src/toml11/toml/comments.hpp
@@ -0,0 +1,472 @@
+// Copyright Toru Niina 2019.
+// Distributed under the MIT License.
+#ifndef TOML11_COMMENTS_HPP
+#define TOML11_COMMENTS_HPP
+#include <initializer_list>
+#include <iterator>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#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<std::string>`
+// but bahaves in the opposite way. `preserve_comments` is just the same as
+// `std::vector<std::string>` and each `std::string` corresponds to a comment line.
+// Conversely, `discard_comments` discards all the strings and ignores everything
+// assigned in it. `discard_comments` is always empty and you will encounter an
+// error whenever you access to the element.
+namespace toml
+{
+struct discard_comments; // forward decl
+
+// use it in the following way
+//
+// const toml::basic_value<toml::preserve_comments> data =
+// toml::parse<toml::preserve_comments>("example.toml");
+//
+// the interface is almost the same as std::vector<std::string>.
+struct preserve_comments
+{
+ // `container_type` is not provided in discard_comments.
+ // do not use this inner-type in a generic code.
+ using container_type = std::vector<std::string>;
+
+ using size_type = container_type::size_type;
+ using difference_type = container_type::difference_type;
+ using value_type = container_type::value_type;
+ using reference = container_type::reference;
+ using const_reference = container_type::const_reference;
+ using pointer = container_type::pointer;
+ using const_pointer = container_type::const_pointer;
+ using iterator = container_type::iterator;
+ using const_iterator = container_type::const_iterator;
+ using reverse_iterator = container_type::reverse_iterator;
+ using const_reverse_iterator = container_type::const_reverse_iterator;
+
+ preserve_comments() = default;
+ ~preserve_comments() = default;
+ preserve_comments(preserve_comments const&) = default;
+ preserve_comments(preserve_comments &&) = default;
+ preserve_comments& operator=(preserve_comments const&) = default;
+ preserve_comments& operator=(preserve_comments &&) = default;
+
+ explicit preserve_comments(const std::vector<std::string>& c): comments(c){}
+ explicit preserve_comments(std::vector<std::string>&& c)
+ : comments(std::move(c))
+ {}
+ preserve_comments& operator=(const std::vector<std::string>& c)
+ {
+ comments = c;
+ return *this;
+ }
+ preserve_comments& operator=(std::vector<std::string>&& c)
+ {
+ comments = std::move(c);
+ return *this;
+ }
+
+ explicit preserve_comments(const discard_comments&) {}
+
+ explicit preserve_comments(size_type n): comments(n) {}
+ preserve_comments(size_type n, const std::string& x): comments(n, x) {}
+ preserve_comments(std::initializer_list<std::string> x): comments(x) {}
+ template<typename InputIterator>
+ preserve_comments(InputIterator first, InputIterator last)
+ : comments(first, last)
+ {}
+
+ template<typename InputIterator>
+ void assign(InputIterator first, InputIterator last) {comments.assign(first, last);}
+ void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
+ void assign(size_type n, const std::string& val) {comments.assign(n, val);}
+
+ // Related to the issue #97.
+ //
+ // It is known that `std::vector::insert` and `std::vector::erase` in
+ // the standard library implementation included in GCC 4.8.5 takes
+ // `std::vector::iterator` instead of `std::vector::const_iterator`.
+ // Because of the const-correctness, we cannot convert a `const_iterator` to
+ // an `iterator`. It causes compilation error in GCC 4.8.5.
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
+# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
+# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
+# endif
+#endif
+
+#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
+ iterator insert(iterator p, const std::string& x)
+ {
+ return comments.insert(p, x);
+ }
+ iterator insert(iterator p, std::string&& x)
+ {
+ return comments.insert(p, std::move(x));
+ }
+ void insert(iterator p, size_type n, const std::string& x)
+ {
+ return comments.insert(p, n, x);
+ }
+ template<typename InputIterator>
+ void insert(iterator p, InputIterator first, InputIterator last)
+ {
+ return comments.insert(p, first, last);
+ }
+ void insert(iterator p, std::initializer_list<std::string> ini)
+ {
+ return comments.insert(p, ini);
+ }
+
+ template<typename ... Ts>
+ iterator emplace(iterator p, Ts&& ... args)
+ {
+ return comments.emplace(p, std::forward<Ts>(args)...);
+ }
+
+ iterator erase(iterator pos) {return comments.erase(pos);}
+ iterator erase(iterator first, iterator last)
+ {
+ return comments.erase(first, last);
+ }
+#else
+ iterator insert(const_iterator p, const std::string& x)
+ {
+ return comments.insert(p, x);
+ }
+ iterator insert(const_iterator p, std::string&& x)
+ {
+ return comments.insert(p, std::move(x));
+ }
+ iterator insert(const_iterator p, size_type n, const std::string& x)
+ {
+ return comments.insert(p, n, x);
+ }
+ template<typename InputIterator>
+ iterator insert(const_iterator p, InputIterator first, InputIterator last)
+ {
+ return comments.insert(p, first, last);
+ }
+ iterator insert(const_iterator p, std::initializer_list<std::string> ini)
+ {
+ return comments.insert(p, ini);
+ }
+
+ template<typename ... Ts>
+ iterator emplace(const_iterator p, Ts&& ... args)
+ {
+ return comments.emplace(p, std::forward<Ts>(args)...);
+ }
+
+ iterator erase(const_iterator pos) {return comments.erase(pos);}
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ return comments.erase(first, last);
+ }
+#endif
+
+ void swap(preserve_comments& other) {comments.swap(other.comments);}
+
+ void push_back(const std::string& v) {comments.push_back(v);}
+ void push_back(std::string&& v) {comments.push_back(std::move(v));}
+ void pop_back() {comments.pop_back();}
+
+ template<typename ... Ts>
+ void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);}
+
+ void clear() {comments.clear();}
+
+ size_type size() const noexcept {return comments.size();}
+ size_type max_size() const noexcept {return comments.max_size();}
+ size_type capacity() const noexcept {return comments.capacity();}
+ bool empty() const noexcept {return comments.empty();}
+
+ void reserve(size_type n) {comments.reserve(n);}
+ void resize(size_type n) {comments.resize(n);}
+ void resize(size_type n, const std::string& c) {comments.resize(n, c);}
+ void shrink_to_fit() {comments.shrink_to_fit();}
+
+ reference operator[](const size_type n) noexcept {return comments[n];}
+ const_reference operator[](const size_type n) const noexcept {return comments[n];}
+ reference at(const size_type n) {return comments.at(n);}
+ const_reference at(const size_type n) const {return comments.at(n);}
+ reference front() noexcept {return comments.front();}
+ const_reference front() const noexcept {return comments.front();}
+ reference back() noexcept {return comments.back();}
+ const_reference back() const noexcept {return comments.back();}
+
+ pointer data() noexcept {return comments.data();}
+ const_pointer data() const noexcept {return comments.data();}
+
+ iterator begin() noexcept {return comments.begin();}
+ iterator end() noexcept {return comments.end();}
+ const_iterator begin() const noexcept {return comments.begin();}
+ const_iterator end() const noexcept {return comments.end();}
+ const_iterator cbegin() const noexcept {return comments.cbegin();}
+ const_iterator cend() const noexcept {return comments.cend();}
+
+ reverse_iterator rbegin() noexcept {return comments.rbegin();}
+ reverse_iterator rend() noexcept {return comments.rend();}
+ const_reverse_iterator rbegin() const noexcept {return comments.rbegin();}
+ const_reverse_iterator rend() const noexcept {return comments.rend();}
+ const_reverse_iterator crbegin() const noexcept {return comments.crbegin();}
+ const_reverse_iterator crend() const noexcept {return comments.crend();}
+
+ friend bool operator==(const preserve_comments&, const preserve_comments&);
+ friend bool operator!=(const preserve_comments&, const preserve_comments&);
+ friend bool operator< (const preserve_comments&, const preserve_comments&);
+ friend bool operator<=(const preserve_comments&, const preserve_comments&);
+ friend bool operator> (const preserve_comments&, const preserve_comments&);
+ friend bool operator>=(const preserve_comments&, const preserve_comments&);
+
+ friend void swap(preserve_comments&, std::vector<std::string>&);
+ friend void swap(std::vector<std::string>&, preserve_comments&);
+
+ private:
+
+ container_type comments;
+};
+
+inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;}
+inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;}
+inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;}
+inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;}
+inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;}
+inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;}
+
+inline void swap(preserve_comments& lhs, preserve_comments& rhs)
+{
+ lhs.swap(rhs);
+ return;
+}
+inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
+{
+ lhs.comments.swap(rhs);
+ return;
+}
+inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
+{
+ lhs.swap(rhs.comments);
+ return;
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com)
+{
+ for(const auto& c : com)
+ {
+ os << '#' << c << '\n';
+ }
+ return os;
+}
+
+namespace detail
+{
+
+// To provide the same interface with `preserve_comments`, `discard_comments`
+// should have an iterator. But it does not contain anything, so we need to
+// add an iterator that points nothing.
+//
+// It always points null, so DO NOT unwrap this iterator. It always crashes
+// your program.
+template<typename T, bool is_const>
+struct empty_iterator
+{
+ using value_type = T;
+ using reference_type = typename std::conditional<is_const, T const&, T&>::type;
+ using pointer_type = typename std::conditional<is_const, T const*, T*>::type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::random_access_iterator_tag;
+
+ empty_iterator() = default;
+ ~empty_iterator() = default;
+ empty_iterator(empty_iterator const&) = default;
+ empty_iterator(empty_iterator &&) = default;
+ empty_iterator& operator=(empty_iterator const&) = default;
+ empty_iterator& operator=(empty_iterator &&) = default;
+
+ // DO NOT call these operators.
+ reference_type operator*() const noexcept {std::terminate();}
+ pointer_type operator->() const noexcept {return nullptr;}
+ reference_type operator[](difference_type) const noexcept {return this->operator*();}
+
+ // These operators do nothing.
+ empty_iterator& operator++() noexcept {return *this;}
+ empty_iterator operator++(int) noexcept {return *this;}
+ empty_iterator& operator--() noexcept {return *this;}
+ empty_iterator operator--(int) noexcept {return *this;}
+
+ empty_iterator& operator+=(difference_type) noexcept {return *this;}
+ empty_iterator& operator-=(difference_type) noexcept {return *this;}
+
+ empty_iterator operator+(difference_type) const noexcept {return *this;}
+ empty_iterator operator-(difference_type) const noexcept {return *this;}
+};
+
+template<typename T, bool C>
+bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
+template<typename T, bool C>
+bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
+template<typename T, bool C>
+bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
+template<typename T, bool C>
+bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
+template<typename T, bool C>
+bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
+template<typename T, bool C>
+bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
+
+template<typename T, bool C>
+typename empty_iterator<T, C>::difference_type
+operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;}
+
+template<typename T, bool C>
+empty_iterator<T, C>
+operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;}
+template<typename T, bool C>
+empty_iterator<T, C>
+operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;}
+
+} // detail
+
+// The default comment type. It discards all the comments. It requires only one
+// byte to contain, so the memory footprint is smaller than preserve_comments.
+//
+// It just ignores `push_back`, `insert`, `erase`, and any other modifications.
+// IT always returns size() == 0, the iterator taken by `begin()` is always the
+// same as that of `end()`, and accessing through `operator[]` or iterators
+// always causes a segmentation fault. DO NOT access to the element of this.
+//
+// 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 chosen as a default.
+//
+// To reduce the memory footprint, later we can try empty base optimization (EBO).
+struct discard_comments
+{
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::string;
+ using reference = std::string&;
+ using const_reference = std::string const&;
+ using pointer = std::string*;
+ using const_pointer = std::string const*;
+ using iterator = detail::empty_iterator<std::string, false>;
+ using const_iterator = detail::empty_iterator<std::string, true>;
+ using reverse_iterator = detail::empty_iterator<std::string, false>;
+ using const_reverse_iterator = detail::empty_iterator<std::string, true>;
+
+ discard_comments() = default;
+ ~discard_comments() = default;
+ discard_comments(discard_comments const&) = default;
+ discard_comments(discard_comments &&) = default;
+ discard_comments& operator=(discard_comments const&) = default;
+ discard_comments& operator=(discard_comments &&) = default;
+
+ explicit discard_comments(const std::vector<std::string>&) noexcept {}
+ explicit discard_comments(std::vector<std::string>&&) noexcept {}
+ discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
+ discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;}
+
+ explicit discard_comments(const preserve_comments&) noexcept {}
+
+ explicit discard_comments(size_type) noexcept {}
+ discard_comments(size_type, const std::string&) noexcept {}
+ discard_comments(std::initializer_list<std::string>) noexcept {}
+ template<typename InputIterator>
+ discard_comments(InputIterator, InputIterator) noexcept {}
+
+ template<typename InputIterator>
+ void assign(InputIterator, InputIterator) noexcept {}
+ void assign(std::initializer_list<std::string>) noexcept {}
+ void assign(size_type, const std::string&) noexcept {}
+
+ iterator insert(const_iterator, const std::string&) {return iterator{};}
+ iterator insert(const_iterator, std::string&&) {return iterator{};}
+ iterator insert(const_iterator, size_type, const std::string&) {return iterator{};}
+ template<typename InputIterator>
+ iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};}
+ iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};}
+
+ template<typename ... Ts>
+ iterator emplace(const_iterator, Ts&& ...) {return iterator{};}
+ iterator erase(const_iterator) {return iterator{};}
+ iterator erase(const_iterator, const_iterator) {return iterator{};}
+
+ void swap(discard_comments&) {return;}
+
+ void push_back(const std::string&) {return;}
+ void push_back(std::string&& ) {return;}
+ void pop_back() {return;}
+
+ template<typename ... Ts>
+ void emplace_back(Ts&& ...) {return;}
+
+ void clear() {return;}
+
+ size_type size() const noexcept {return 0;}
+ size_type max_size() const noexcept {return 0;}
+ size_type capacity() const noexcept {return 0;}
+ bool empty() const noexcept {return true;}
+
+ void reserve(size_type) {return;}
+ void resize(size_type) {return;}
+ void resize(size_type, const std::string&) {return;}
+ void shrink_to_fit() {return;}
+
+ // DO NOT access to the element of this container. This container is always
+ // 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 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();}
+
+ pointer data() noexcept {return nullptr;}
+ const_pointer data() const noexcept {return nullptr;}
+
+ iterator begin() noexcept {return iterator{};}
+ iterator end() noexcept {return iterator{};}
+ const_iterator begin() const noexcept {return const_iterator{};}
+ const_iterator end() const noexcept {return const_iterator{};}
+ const_iterator cbegin() const noexcept {return const_iterator{};}
+ const_iterator cend() const noexcept {return const_iterator{};}
+
+ reverse_iterator rbegin() noexcept {return iterator{};}
+ reverse_iterator rend() noexcept {return iterator{};}
+ const_reverse_iterator rbegin() const noexcept {return const_iterator{};}
+ 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{};}
+};
+
+inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
+inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;}
+inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;}
+inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;}
+inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;}
+inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;}
+
+inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&)
+{
+ return os;
+}
+
+} // toml11
+#endif// TOML11_COMMENTS_HPP
diff --git a/src/toml11/toml/datetime.hpp b/src/toml11/toml/datetime.hpp
new file mode 100644
index 000000000..d8127c150
--- /dev/null
+++ b/src/toml11/toml/datetime.hpp
@@ -0,0 +1,631 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_DATETIME_HPP
+#define TOML11_DATETIME_HPP
+#include <cstdint>
+#include <cstdlib>
+#include <ctime>
+
+#include <array>
+#include <chrono>
+#include <iomanip>
+#include <ostream>
+#include <tuple>
+
+namespace toml
+{
+
+// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
+// provided in the absolutely same purpose, but C++11 is actually not compatible
+// with C11. We need to dispatch the function depending on the OS.
+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)
+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."); }
+ 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."); }
+ return dst;
+}
+#elif defined(_MSC_VER)
+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."); }
+ 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."); }
+ return dst;
+}
+#else // fallback. not threadsafe
+inline std::tm localtime_s(const std::time_t* src)
+{
+ const auto result = std::localtime(src);
+ if (!result) { throw std::runtime_error("localtime failed."); }
+ return *result;
+}
+inline std::tm gmtime_s(const std::time_t* src)
+{
+ const auto result = std::gmtime(src);
+ if (!result) { throw std::runtime_error("gmtime failed."); }
+ return *result;
+}
+#endif
+} // detail
+
+enum class month_t : std::uint8_t
+{
+ Jan = 0,
+ Feb = 1,
+ Mar = 2,
+ Apr = 3,
+ May = 4,
+ Jun = 5,
+ Jul = 6,
+ Aug = 7,
+ Sep = 8,
+ Oct = 9,
+ Nov = 10,
+ Dec = 11
+};
+
+struct local_date
+{
+ 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<std::int16_t>(y)),
+ month(static_cast<std::uint8_t>(m)),
+ day (static_cast<std::uint8_t>(d))
+ {}
+
+ explicit local_date(const std::tm& t)
+ : year (static_cast<std::int16_t>(t.tm_year + 1900)),
+ month(static_cast<std::uint8_t>(t.tm_mon)),
+ day (static_cast<std::uint8_t>(t.tm_mday))
+ {}
+
+ explicit local_date(const std::chrono::system_clock::time_point& tp)
+ {
+ const auto t = std::chrono::system_clock::to_time_t(tp);
+ const auto time = detail::localtime_s(&t);
+ *this = local_date(time);
+ }
+
+ explicit local_date(const std::time_t t)
+ : local_date(std::chrono::system_clock::from_time_t(t))
+ {}
+
+ operator std::chrono::system_clock::time_point() const
+ {
+ // std::mktime returns date as local time zone. no conversion needed
+ std::tm t;
+ t.tm_sec = 0;
+ t.tm_min = 0;
+ t.tm_hour = 0;
+ t.tm_mday = static_cast<int>(this->day);
+ t.tm_mon = static_cast<int>(this->month);
+ t.tm_year = static_cast<int>(this->year) - 1900;
+ t.tm_wday = 0; // the value will be ignored
+ t.tm_yday = 0; // the value will be ignored
+ t.tm_isdst = -1;
+ return std::chrono::system_clock::from_time_t(std::mktime(&t));
+ }
+
+ operator std::time_t() const
+ {
+ return std::chrono::system_clock::to_time_t(
+ std::chrono::system_clock::time_point(*this));
+ }
+
+ local_date() = default;
+ ~local_date() = default;
+ local_date(local_date const&) = default;
+ local_date(local_date&&) = default;
+ local_date& operator=(local_date const&) = default;
+ local_date& operator=(local_date&&) = default;
+};
+
+inline bool operator==(const local_date& lhs, const local_date& rhs)
+{
+ return std::make_tuple(lhs.year, lhs.month, lhs.day) ==
+ std::make_tuple(rhs.year, rhs.month, rhs.day);
+}
+inline bool operator!=(const local_date& lhs, const local_date& rhs)
+{
+ return !(lhs == rhs);
+}
+inline bool operator< (const local_date& lhs, const local_date& rhs)
+{
+ return std::make_tuple(lhs.year, lhs.month, lhs.day) <
+ std::make_tuple(rhs.year, rhs.month, rhs.day);
+}
+inline bool operator<=(const local_date& lhs, const local_date& rhs)
+{
+ return (lhs < rhs) || (lhs == rhs);
+}
+inline bool operator> (const local_date& lhs, const local_date& rhs)
+{
+ return !(lhs <= rhs);
+}
+inline bool operator>=(const local_date& lhs, const local_date& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
+{
+ os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
+ os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
+ os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
+ return os;
+}
+
+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]
+
+ local_time(int h, int m, int s,
+ int ms = 0, int us = 0, int ns = 0)
+ : hour (static_cast<std::uint8_t>(h)),
+ minute(static_cast<std::uint8_t>(m)),
+ second(static_cast<std::uint8_t>(s)),
+ millisecond(static_cast<std::uint16_t>(ms)),
+ microsecond(static_cast<std::uint16_t>(us)),
+ nanosecond (static_cast<std::uint16_t>(ns))
+ {}
+
+ explicit local_time(const std::tm& t)
+ : hour (static_cast<std::uint8_t>(t.tm_hour)),
+ minute(static_cast<std::uint8_t>(t.tm_min)),
+ second(static_cast<std::uint8_t>(t.tm_sec)),
+ millisecond(0), microsecond(0), nanosecond(0)
+ {}
+
+ template<typename Rep, typename Period>
+ explicit local_time(const std::chrono::duration<Rep, Period>& t)
+ {
+ const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
+ this->hour = static_cast<std::uint8_t>(h.count());
+ const auto t2 = t - h;
+ const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
+ this->minute = static_cast<std::uint8_t>(m.count());
+ const auto t3 = t2 - m;
+ const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
+ this->second = static_cast<std::uint8_t>(s.count());
+ const auto t4 = t3 - s;
+ const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
+ this->millisecond = static_cast<std::uint16_t>(ms.count());
+ const auto t5 = t4 - ms;
+ const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
+ this->microsecond = static_cast<std::uint16_t>(us.count());
+ const auto t6 = t5 - us;
+ const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
+ this->nanosecond = static_cast<std::uint16_t>(ns.count());
+ }
+
+ operator std::chrono::nanoseconds() const
+ {
+ return std::chrono::nanoseconds (this->nanosecond) +
+ std::chrono::microseconds(this->microsecond) +
+ std::chrono::milliseconds(this->millisecond) +
+ std::chrono::seconds(this->second) +
+ std::chrono::minutes(this->minute) +
+ std::chrono::hours(this->hour);
+ }
+
+ local_time() = default;
+ ~local_time() = default;
+ local_time(local_time const&) = default;
+ local_time(local_time&&) = default;
+ local_time& operator=(local_time const&) = default;
+ local_time& operator=(local_time&&) = default;
+};
+
+inline bool operator==(const local_time& lhs, const local_time& rhs)
+{
+ return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) ==
+ std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
+}
+inline bool operator!=(const local_time& lhs, const local_time& rhs)
+{
+ return !(lhs == rhs);
+}
+inline bool operator< (const local_time& lhs, const local_time& rhs)
+{
+ return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) <
+ std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
+}
+inline bool operator<=(const local_time& lhs, const local_time& rhs)
+{
+ return (lhs < rhs) || (lhs == rhs);
+}
+inline bool operator> (const local_time& lhs, const local_time& rhs)
+{
+ return !(lhs <= rhs);
+}
+inline bool operator>=(const local_time& lhs, const local_time& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
+{
+ os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':';
+ os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':';
+ os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second);
+ if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0)
+ {
+ os << '.';
+ os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond);
+ if(time.microsecond != 0 || time.nanosecond != 0)
+ {
+ os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond);
+ if(time.nanosecond != 0)
+ {
+ os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond);
+ }
+ }
+ }
+ return os;
+}
+
+struct time_offset
+{
+ std::int8_t hour; // [-12, 12]
+ std::int8_t minute; // [-59, 59]
+
+ time_offset(int h, int m)
+ : hour (static_cast<std::int8_t>(h)),
+ minute(static_cast<std::int8_t>(m))
+ {}
+
+ operator std::chrono::minutes() const
+ {
+ return std::chrono::minutes(this->minute) +
+ std::chrono::hours(this->hour);
+ }
+
+ time_offset() = default;
+ ~time_offset() = default;
+ time_offset(time_offset const&) = default;
+ time_offset(time_offset&&) = default;
+ time_offset& operator=(time_offset const&) = default;
+ time_offset& operator=(time_offset&&) = default;
+};
+
+inline bool operator==(const time_offset& lhs, const time_offset& rhs)
+{
+ return std::make_tuple(lhs.hour, lhs.minute) ==
+ std::make_tuple(rhs.hour, rhs.minute);
+}
+inline bool operator!=(const time_offset& lhs, const time_offset& rhs)
+{
+ return !(lhs == rhs);
+}
+inline bool operator< (const time_offset& lhs, const time_offset& rhs)
+{
+ return std::make_tuple(lhs.hour, lhs.minute) <
+ std::make_tuple(rhs.hour, rhs.minute);
+}
+inline bool operator<=(const time_offset& lhs, const time_offset& rhs)
+{
+ return (lhs < rhs) || (lhs == rhs);
+}
+inline bool operator> (const time_offset& lhs, const time_offset& rhs)
+{
+ return !(lhs <= rhs);
+}
+inline bool operator>=(const time_offset& lhs, const time_offset& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
+{
+ if(offset.hour == 0 && offset.minute == 0)
+ {
+ os << 'Z';
+ return os;
+ }
+ int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
+ if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
+ os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
+ os << std::setfill('0') << std::setw(2) << minute % 60;
+ return os;
+}
+
+struct local_datetime
+{
+ local_date date;
+ local_time time;
+
+ local_datetime(local_date d, local_time t): date(d), time(t) {}
+
+ explicit local_datetime(const std::tm& t): date(t), time(t){}
+
+ explicit local_datetime(const std::chrono::system_clock::time_point& tp)
+ {
+ const auto t = std::chrono::system_clock::to_time_t(tp);
+ std::tm ltime = detail::localtime_s(&t);
+
+ this->date = local_date(ltime);
+ this->time = local_time(ltime);
+
+ // std::tm lacks subsecond information, so diff between tp and tm
+ // can be used to get millisecond & microsecond information.
+ const auto t_diff = tp -
+ std::chrono::system_clock::from_time_t(std::mktime(&ltime));
+ this->time.millisecond = static_cast<std::uint16_t>(
+ std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
+ this->time.microsecond = static_cast<std::uint16_t>(
+ std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
+ this->time.nanosecond = static_cast<std::uint16_t>(
+ std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
+ }
+
+ explicit local_datetime(const std::time_t t)
+ : local_datetime(std::chrono::system_clock::from_time_t(t))
+ {}
+
+ operator std::chrono::system_clock::time_point() const
+ {
+ using internal_duration =
+ typename std::chrono::system_clock::time_point::duration;
+
+ // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
+ // of local_date and local_time independently, the conversion fails if
+ // it is the day when DST begins or ends. Since local_date considers the
+ // time is 00:00 A.M. and local_time does not consider DST because it
+ // does not have any date information. We need to consider both date and
+ // time information at the same time to convert it correctly.
+
+ std::tm t;
+ t.tm_sec = static_cast<int>(this->time.second);
+ t.tm_min = static_cast<int>(this->time.minute);
+ t.tm_hour = static_cast<int>(this->time.hour);
+ t.tm_mday = static_cast<int>(this->date.day);
+ t.tm_mon = static_cast<int>(this->date.month);
+ t.tm_year = static_cast<int>(this->date.year) - 1900;
+ t.tm_wday = 0; // the value will be ignored
+ t.tm_yday = 0; // the value will be ignored
+ t.tm_isdst = -1;
+
+ // std::mktime returns date as local time zone. no conversion needed
+ auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
+ dt += std::chrono::duration_cast<internal_duration>(
+ std::chrono::milliseconds(this->time.millisecond) +
+ std::chrono::microseconds(this->time.microsecond) +
+ std::chrono::nanoseconds (this->time.nanosecond));
+ return dt;
+ }
+
+ operator std::time_t() const
+ {
+ return std::chrono::system_clock::to_time_t(
+ std::chrono::system_clock::time_point(*this));
+ }
+
+ local_datetime() = default;
+ ~local_datetime() = default;
+ local_datetime(local_datetime const&) = default;
+ local_datetime(local_datetime&&) = default;
+ local_datetime& operator=(local_datetime const&) = default;
+ local_datetime& operator=(local_datetime&&) = default;
+};
+
+inline bool operator==(const local_datetime& lhs, const local_datetime& rhs)
+{
+ return std::make_tuple(lhs.date, lhs.time) ==
+ std::make_tuple(rhs.date, rhs.time);
+}
+inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs)
+{
+ return !(lhs == rhs);
+}
+inline bool operator< (const local_datetime& lhs, const local_datetime& rhs)
+{
+ return std::make_tuple(lhs.date, lhs.time) <
+ std::make_tuple(rhs.date, rhs.time);
+}
+inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs)
+{
+ return (lhs < rhs) || (lhs == rhs);
+}
+inline bool operator> (const local_datetime& lhs, const local_datetime& rhs)
+{
+ return !(lhs <= rhs);
+}
+inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
+{
+ os << dt.date << 'T' << dt.time;
+ return os;
+}
+
+struct offset_datetime
+{
+ 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)
+ {}
+ offset_datetime(const local_datetime& dt, time_offset o)
+ : date(dt.date), time(dt.time), offset(o)
+ {}
+ explicit offset_datetime(const local_datetime& ld)
+ : date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
+ // use the current local timezone offset
+ {}
+ explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
+ : offset(0, 0) // use gmtime
+ {
+ const auto timet = std::chrono::system_clock::to_time_t(tp);
+ const auto tm = detail::gmtime_s(&timet);
+ this->date = local_date(tm);
+ this->time = local_time(tm);
+ }
+ explicit offset_datetime(const std::time_t& t)
+ : offset(0, 0) // use gmtime
+ {
+ const auto tm = detail::gmtime_s(&t);
+ this->date = local_date(tm);
+ this->time = local_time(tm);
+ }
+ explicit offset_datetime(const std::tm& t)
+ : offset(0, 0) // assume gmtime
+ {
+ this->date = local_date(t);
+ this->time = local_time(t);
+ }
+
+ operator std::chrono::system_clock::time_point() const
+ {
+ // get date-time
+ using internal_duration =
+ typename std::chrono::system_clock::time_point::duration;
+
+ // first, convert it to local date-time information in the same way as
+ // local_datetime does. later we will use time_t to adjust time offset.
+ std::tm t;
+ t.tm_sec = static_cast<int>(this->time.second);
+ t.tm_min = static_cast<int>(this->time.minute);
+ t.tm_hour = static_cast<int>(this->time.hour);
+ t.tm_mday = static_cast<int>(this->date.day);
+ t.tm_mon = static_cast<int>(this->date.month);
+ t.tm_year = static_cast<int>(this->date.year) - 1900;
+ t.tm_wday = 0; // the value will be ignored
+ t.tm_yday = 0; // the value will be ignored
+ t.tm_isdst = -1;
+ const std::time_t tp_loc = std::mktime(std::addressof(t));
+
+ auto tp = std::chrono::system_clock::from_time_t(tp_loc);
+ tp += std::chrono::duration_cast<internal_duration>(
+ std::chrono::milliseconds(this->time.millisecond) +
+ std::chrono::microseconds(this->time.microsecond) +
+ std::chrono::nanoseconds (this->time.nanosecond));
+
+ // Since mktime uses local time zone, it should be corrected.
+ // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
+ // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
+ // to add `+09:00` to `03:00:00Z`.
+ // Here, it uses the time_t converted from date-time info to handle
+ // daylight saving time.
+ const auto ofs = get_local_offset(std::addressof(tp_loc));
+ tp += std::chrono::hours (ofs.hour);
+ tp += std::chrono::minutes(ofs.minute);
+
+ // We got `12:00:00Z` by correcting local timezone applied by mktime.
+ // Then we will apply the offset. Let's say `12:00:00-08:00` is given.
+ // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
+ // So we need to subtract the offset.
+ tp -= std::chrono::minutes(this->offset);
+ return tp;
+ }
+
+ operator std::time_t() const
+ {
+ return std::chrono::system_clock::to_time_t(
+ std::chrono::system_clock::time_point(*this));
+ }
+
+ offset_datetime() = default;
+ ~offset_datetime() = default;
+ offset_datetime(offset_datetime const&) = default;
+ offset_datetime(offset_datetime&&) = default;
+ offset_datetime& operator=(offset_datetime const&) = default;
+ offset_datetime& operator=(offset_datetime&&) = default;
+
+ private:
+
+ static time_offset get_local_offset(const std::time_t* tp)
+ {
+ // get local timezone with the same date-time information as mktime
+ const auto t = detail::localtime_s(tp);
+
+ std::array<char, 6> buf;
+ const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
+ if(result != 5)
+ {
+ throw std::runtime_error("toml::offset_datetime: cannot obtain "
+ "timezone information of current env");
+ }
+ const int ofs = std::atoi(buf.data());
+ const int ofs_h = ofs / 100;
+ const int ofs_m = ofs - (ofs_h * 100);
+ return time_offset(ofs_h, ofs_m);
+ }
+};
+
+inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs)
+{
+ return std::make_tuple(lhs.date, lhs.time, lhs.offset) ==
+ std::make_tuple(rhs.date, rhs.time, rhs.offset);
+}
+inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs)
+{
+ return !(lhs == rhs);
+}
+inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs)
+{
+ return std::make_tuple(lhs.date, lhs.time, lhs.offset) <
+ std::make_tuple(rhs.date, rhs.time, rhs.offset);
+}
+inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs)
+{
+ return (lhs < rhs) || (lhs == rhs);
+}
+inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs)
+{
+ return !(lhs <= rhs);
+}
+inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const offset_datetime& dt)
+{
+ os << dt.date << 'T' << dt.time << dt.offset;
+ return os;
+}
+
+}//toml
+#endif// TOML11_DATETIME
diff --git a/src/toml11/toml/exception.hpp b/src/toml11/toml/exception.hpp
new file mode 100644
index 000000000..c64651d0a
--- /dev/null
+++ b/src/toml11/toml/exception.hpp
@@ -0,0 +1,65 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_EXCEPTION_HPP
+#define TOML11_EXCEPTION_HPP
+#include <stdexcept>
+#include <string>
+
+#include "source_location.hpp"
+
+namespace toml
+{
+
+struct exception : public std::exception
+{
+ public:
+ explicit exception(const source_location& loc): loc_(loc) {}
+ virtual ~exception() noexcept override = default;
+ virtual const char* what() const noexcept override {return "";}
+ virtual source_location const& location() const noexcept {return loc_;}
+
+ protected:
+ source_location loc_;
+};
+
+struct syntax_error : public toml::exception
+{
+ public:
+ explicit syntax_error(const std::string& what_arg, const source_location& loc)
+ : exception(loc), what_(what_arg)
+ {}
+ virtual ~syntax_error() noexcept override = default;
+ virtual const char* what() const noexcept override {return what_.c_str();}
+
+ protected:
+ std::string what_;
+};
+
+struct type_error : public toml::exception
+{
+ public:
+ explicit type_error(const std::string& what_arg, const source_location& loc)
+ : exception(loc), what_(what_arg)
+ {}
+ virtual ~type_error() noexcept override = default;
+ virtual const char* what() const noexcept override {return what_.c_str();}
+
+ protected:
+ std::string what_;
+};
+
+struct internal_error : public toml::exception
+{
+ public:
+ explicit internal_error(const std::string& what_arg, const source_location& loc)
+ : exception(loc), what_(what_arg)
+ {}
+ virtual ~internal_error() noexcept override = default;
+ virtual const char* what() const noexcept override {return what_.c_str();}
+
+ protected:
+ std::string what_;
+};
+
+} // toml
+#endif // TOML_EXCEPTION
diff --git a/src/toml11/toml/from.hpp b/src/toml11/toml/from.hpp
new file mode 100644
index 000000000..10815caf5
--- /dev/null
+++ b/src/toml11/toml/from.hpp
@@ -0,0 +1,19 @@
+// Copyright Toru Niina 2019.
+// Distributed under the MIT License.
+#ifndef TOML11_FROM_HPP
+#define TOML11_FROM_HPP
+
+namespace toml
+{
+
+template<typename T>
+struct from;
+// {
+// static T from_toml(const toml::value& v)
+// {
+// // User-defined conversions ...
+// }
+// };
+
+} // toml
+#endif // TOML11_FROM_HPP
diff --git a/src/toml11/toml/get.hpp b/src/toml11/toml/get.hpp
new file mode 100644
index 000000000..d7fdf553b
--- /dev/null
+++ b/src/toml11/toml/get.hpp
@@ -0,0 +1,1117 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_GET_HPP
+#define TOML11_GET_HPP
+#include <algorithm>
+
+#include "from.hpp"
+#include "result.hpp"
+#include "value.hpp"
+
+namespace toml
+{
+
+// ============================================================================
+// exact toml::* type
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> &
+get(basic_value<C, M, V>& v)
+{
+ return v.template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>();
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
+get(const basic_value<C, M, V>& v)
+{
+ return v.template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>();
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>
+get(basic_value<C, M, V>&& v)
+{
+ return T(std::move(v).template cast<detail::type_to_enum<T, basic_value<C, M, V>>::value>());
+}
+
+// ============================================================================
+// T == toml::value; identity transformation.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T>&
+get(basic_value<C, M, V>& v)
+{
+ return v;
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T> const&
+get(const basic_value<C, M, V>& v)
+{
+ return v;
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, basic_value<C, M, V>>::value, T>
+get(basic_value<C, M, V>&& v)
+{
+ return basic_value<C, M, V>(std::move(v));
+}
+
+// ============================================================================
+// T == toml::basic_value<C2, M2, V2>; basic_value -> basic_value
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<detail::conjunction<detail::is_basic_value<T>,
+ detail::negation<std::is_same<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ return T(v);
+}
+
+// ============================================================================
+// integer convertible from toml::Integer
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<detail::conjunction<
+ std::is_integral<T>, // T is integral
+ detail::negation<std::is_same<T, bool>>, // but not bool
+ detail::negation< // but not toml::integer
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ return static_cast<T>(v.as_integer());
+}
+
+// ============================================================================
+// floating point convertible from toml::Float
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<detail::conjunction<
+ std::is_floating_point<T>, // T is floating_point
+ detail::negation< // but not toml::floating
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ return static_cast<T>(v.as_floating());
+}
+
+// ============================================================================
+// std::string; toml uses its own toml::string, but it should be convertible to
+// std::string seamlessly
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
+get(basic_value<C, M, V>& v)
+{
+ return v.as_string().str;
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
+get(const basic_value<C, M, V>& v)
+{
+ return v.as_string().str;
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
+get(basic_value<C, M, V>&& v)
+{
+ return std::string(std::move(v.as_string().str));
+}
+
+// ============================================================================
+// std::string_view
+
+#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
+get(const basic_value<C, M, V>& v)
+{
+ return std::string_view(v.as_string().str);
+}
+#endif
+
+// ============================================================================
+// std::chrono::duration from toml::local_time.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<detail::is_chrono_duration<T>::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ return std::chrono::duration_cast<T>(
+ std::chrono::nanoseconds(v.as_local_time()));
+}
+
+// ============================================================================
+// std::chrono::system_clock::time_point from toml::datetime variants
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+inline detail::enable_if_t<
+ std::is_same<std::chrono::system_clock::time_point, T>::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ switch(v.type())
+ {
+ case value_t::local_date:
+ {
+ return std::chrono::system_clock::time_point(v.as_local_date());
+ }
+ case value_t::local_datetime:
+ {
+ return std::chrono::system_clock::time_point(v.as_local_datetime());
+ }
+ case value_t::offset_datetime:
+ {
+ return std::chrono::system_clock::time_point(v.as_offset_datetime());
+ }
+ default:
+ {
+ throw type_error(detail::format_underline("toml::value: "
+ "bad_cast to std::chrono::system_clock::time_point", {
+ {v.location(), concat_to_string("the actual type is ", v.type())}
+ }), v.location());
+ }
+ }
+}
+
+// ============================================================================
+// forward declaration to use this recursively. ignore this and go ahead.
+
+// array-like type with push_back(value) method
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::is_container<T>, // T is a container
+ detail::has_push_back_method<T>, // T::push_back(value) works
+ detail::negation< // but not toml::array
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>&);
+
+// array-like type without push_back(value) method
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::is_container<T>, // T is a container
+ detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...)
+ detail::negation< // not toml::array
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>&);
+
+// std::pair<T1, T2>
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_std_pair<T>::value, T>
+get(const basic_value<C, M, V>&);
+
+// std::tuple<T1, T2, ...>
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_std_tuple<T>::value, T>
+get(const basic_value<C, M, V>&);
+
+// map-like classes
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::is_map<T>, // T is map
+ detail::negation< // but not toml::table
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>&);
+
+// T.from_toml(v)
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::negation< // not a toml::* type
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>,
+ detail::has_from_toml_method<T, C, M, V>, // but has from_toml(toml::value)
+ std::is_default_constructible<T> // and default constructible
+ >::value, T>
+get(const basic_value<C, M, V>&);
+
+// toml::from<T>::from_toml(v)
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::has_specialized_from<T>::value, T>
+get(const basic_value<C, M, V>&);
+
+// T(const toml::value&) and T is not toml::basic_value,
+// and it does not have `from<T>` nor `from_toml`.
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::negation<detail::is_basic_value<T>>,
+ std::is_constructible<T, const basic_value<C, M, V>&>,
+ detail::negation<detail::has_from_toml_method<T, C, M, V>>,
+ detail::negation<detail::has_specialized_from<T>>
+ >::value, T>
+get(const basic_value<C, M, V>&);
+
+// ============================================================================
+// array-like types; most likely STL container, like std::vector, etc.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::is_container<T>, // T is a container
+ detail::has_push_back_method<T>, // container.push_back(elem) works
+ detail::negation< // but not toml::array
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ using value_type = typename T::value_type;
+ const auto& ary = v.as_array();
+
+ T container;
+ try_reserve(container, ary.size());
+
+ for(const auto& elem : ary)
+ {
+ container.push_back(get<value_type>(elem));
+ }
+ return container;
+}
+
+// ============================================================================
+// std::forward_list does not have push_back, insert, or emplace.
+// It has insert_after, emplace_after, push_front.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_std_forward_list<T>::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ using value_type = typename T::value_type;
+ T container;
+ for(const auto& elem : v.as_array())
+ {
+ container.push_front(get<value_type>(elem));
+ }
+ container.reverse();
+ return container;
+}
+
+// ============================================================================
+// array-like types, without push_back(). most likely [std|boost]::array.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::is_container<T>, // T is a container
+ detail::negation<detail::has_push_back_method<T>>, // w/o push_back
+ detail::negation< // T is not toml::array
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ using value_type = typename T::value_type;
+ const auto& ar = v.as_array();
+
+ T container;
+ if(ar.size() != container.size())
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "toml::get: specified container size is ", container.size(),
+ " but there are ", ar.size(), " elements in toml array."), {
+ {v.location(), "here"}
+ }));
+ }
+ for(std::size_t i=0; i<ar.size(); ++i)
+ {
+ container[i] = ::toml::get<value_type>(ar[i]);
+ }
+ return container;
+}
+
+// ============================================================================
+// std::pair.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_std_pair<T>::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ using first_type = typename T::first_type;
+ using second_type = typename T::second_type;
+
+ const auto& ar = v.as_array();
+ if(ar.size() != 2)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "toml::get: specified std::pair but there are ", ar.size(),
+ " elements in toml array."), {{v.location(), "here"}}));
+ }
+ return std::make_pair(::toml::get<first_type >(ar.at(0)),
+ ::toml::get<second_type>(ar.at(1)));
+}
+
+// ============================================================================
+// std::tuple.
+
+namespace detail
+{
+template<typename T, typename Array, std::size_t ... I>
+T get_tuple_impl(const Array& a, index_sequence<I...>)
+{
+ return std::make_tuple(
+ ::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...);
+}
+} // detail
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_std_tuple<T>::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ const auto& ar = v.as_array();
+ if(ar.size() != std::tuple_size<T>::value)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "toml::get: specified std::tuple with ",
+ std::tuple_size<T>::value, " elements, but there are ", ar.size(),
+ " elements in toml array."), {{v.location(), "here"}}));
+ }
+ return detail::get_tuple_impl<T>(ar,
+ detail::make_index_sequence<std::tuple_size<T>::value>{});
+}
+
+// ============================================================================
+// map-like types; most likely STL map, like std::map or std::unordered_map.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::is_map<T>, // T is map
+ detail::negation< // but not toml::array
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ using key_type = typename T::key_type;
+ using mapped_type = typename T::mapped_type;
+ static_assert(std::is_convertible<std::string, key_type>::value,
+ "toml::get only supports map type of which key_type is "
+ "convertible from std::string.");
+ T map;
+ for(const auto& kv : v.as_table())
+ {
+ map.emplace(key_type(kv.first), get<mapped_type>(kv.second));
+ }
+ return map;
+}
+
+// ============================================================================
+// user-defined, but compatible types.
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::negation< // not a toml::* type
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>>,
+ detail::has_from_toml_method<T, C, M, V>, // but has from_toml(toml::value) memfn
+ std::is_default_constructible<T> // and default constructible
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ T ud;
+ ud.from_toml(v);
+ return ud;
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::has_specialized_from<T>::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ return ::toml::from<T>::from_toml(v);
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
+ std::is_constructible<T, const basic_value<C, M, V>&>, // T is constructible from toml::value
+ detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
+ detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
+ >::value, T>
+get(const basic_value<C, M, V>& v)
+{
+ return T(v);
+}
+
+// ============================================================================
+// find
+
+// ----------------------------------------------------------------------------
+// these overloads do not require to set T. and returns value itself.
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V> const& find(const basic_value<C, M, V>& v, const key& ky)
+{
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0)
+ {
+ detail::throw_key_not_found_error(v, ky);
+ }
+ return tab.at(ky);
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V>& find(basic_value<C, M, V>& v, const key& ky)
+{
+ auto& tab = v.as_table();
+ if(tab.count(ky) == 0)
+ {
+ detail::throw_key_not_found_error(v, ky);
+ }
+ return tab.at(ky);
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V> find(basic_value<C, M, V>&& v, const key& ky)
+{
+ typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
+ if(tab.count(ky) == 0)
+ {
+ detail::throw_key_not_found_error(v, ky);
+ }
+ return basic_value<C, M, V>(std::move(tab.at(ky)));
+}
+
+// ----------------------------------------------------------------------------
+// find(value, idx)
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V> const&
+find(const basic_value<C, M, V>& v, const std::size_t idx)
+{
+ const auto& ary = v.as_array();
+ if(ary.size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "index ", idx, " is out of range"), {{v.location(), "in this array"}}));
+ }
+ return ary.at(idx);
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V>& find(basic_value<C, M, V>& v, const std::size_t idx)
+{
+ auto& ary = v.as_array();
+ if(ary.size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "index ", idx, " is out of range"), {{v.location(), "in this array"}}));
+ }
+ return ary.at(idx);
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V> find(basic_value<C, M, V>&& v, const std::size_t idx)
+{
+ auto& ary = v.as_array();
+ if(ary.size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "index ", idx, " is out of range"), {{v.location(), "in this array"}}));
+ }
+ return basic_value<C, M, V>(std::move(ary.at(idx)));
+}
+
+// ----------------------------------------------------------------------------
+// find<T>(value, key);
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
+find(const basic_value<C, M, V>& v, const key& ky)
+{
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0)
+ {
+ detail::throw_key_not_found_error(v, ky);
+ }
+ return ::toml::get<T>(tab.at(ky));
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
+find(basic_value<C, M, V>& v, const key& ky)
+{
+ auto& tab = v.as_table();
+ if(tab.count(ky) == 0)
+ {
+ detail::throw_key_not_found_error(v, ky);
+ }
+ return ::toml::get<T>(tab.at(ky));
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
+find(basic_value<C, M, V>&& v, const key& ky)
+{
+ typename basic_value<C, M, V>::table_type tab = std::move(v).as_table();
+ if(tab.count(ky) == 0)
+ {
+ detail::throw_key_not_found_error(v, ky);
+ }
+ return ::toml::get<T>(std::move(tab.at(ky)));
+}
+
+// ----------------------------------------------------------------------------
+// find<T>(value, idx)
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V> const&>()))
+find(const basic_value<C, M, V>& v, const std::size_t idx)
+{
+ const auto& ary = v.as_array();
+ if(ary.size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "index ", idx, " is out of range"), {{v.location(), "in this array"}}));
+ }
+ return ::toml::get<T>(ary.at(idx));
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
+find(basic_value<C, M, V>& v, const std::size_t idx)
+{
+ auto& ary = v.as_array();
+ if(ary.size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "index ", idx, " is out of range"), {{v.location(), "in this array"}}));
+ }
+ return ::toml::get<T>(ary.at(idx));
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
+find(basic_value<C, M, V>&& v, const std::size_t idx)
+{
+ typename basic_value<C, M, V>::array_type ary = std::move(v).as_array();
+ if(ary.size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(concat_to_string(
+ "index ", idx, " is out of range"), {{v.location(), "in this array"}}));
+ }
+ return ::toml::get<T>(std::move(ary.at(idx)));
+}
+
+// --------------------------------------------------------------------------
+// toml::find(toml::value, toml::key, Ts&& ... keys)
+
+namespace detail
+{
+// It suppresses warnings by -Wsign-conversion. Let's say we have the following
+// code.
+// ```cpp
+// const auto x = toml::find<std::string>(data, "array", 0);
+// ```
+// Here, the type of literal number `0` is `int`. `int` is a signed integer.
+// `toml::find` takes `std::size_t` as an index. So it causes implicit sign
+// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0`
+// suppresses the warning, but it makes user code messy.
+// To suppress this warning, we need to be aware of type conversion caused
+// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of
+// keys can be any combination of {string-like, size_t-like}. Of course we can't
+// write down all the combinations. Thus we need to use some function that
+// recognize the type of argument and cast it into `std::string` or
+// `std::size_t` depending on the context.
+// `key_cast` does the job. It has 2 overloads. One is invoked when the
+// argument type is an integer and cast the argument into `std::size_t`. The
+// other is invoked when the argument type is not an integer, possibly one of
+// std::string, const char[N] or const char*, and construct std::string from
+// the argument.
+// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to
+// `toml::find(v, k)` to suppress -Wsign-conversion.
+
+template<typename T>
+enable_if_t<conjunction<std::is_integral<remove_cvref_t<T>>,
+ negation<std::is_same<remove_cvref_t<T>, bool>>>::value, std::size_t>
+key_cast(T&& v) noexcept
+{
+ return std::size_t(v);
+}
+template<typename T>
+enable_if_t<negation<conjunction<std::is_integral<remove_cvref_t<T>>,
+ negation<std::is_same<remove_cvref_t<T>, bool>>>>::value, std::string>
+key_cast(T&& v) noexcept
+{
+ return std::string(std::forward<T>(v));
+}
+} // detail
+
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V,
+ typename Key1, typename Key2, typename ... Keys>
+const basic_value<C, M, V>&
+find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
+{
+ return ::toml::find(::toml::find(v, detail::key_cast(k1)),
+ detail::key_cast(k2), std::forward<Keys>(keys)...);
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V,
+ typename Key1, typename Key2, typename ... Keys>
+basic_value<C, M, V>&
+find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
+{
+ return ::toml::find(::toml::find(v, detail::key_cast(k1)),
+ detail::key_cast(k2), std::forward<Keys>(keys)...);
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V,
+ typename Key1, typename Key2, typename ... Keys>
+basic_value<C, M, V>
+find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
+{
+ return ::toml::find(::toml::find(std::move(v), std::forward<Key1>(k1)),
+ detail::key_cast(k2), std::forward<Keys>(keys)...);
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V,
+ typename Key1, typename Key2, typename ... Keys>
+decltype(::toml::get<T>(std::declval<const basic_value<C, M, V>&>()))
+find(const basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
+{
+ return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
+ detail::key_cast(k2), std::forward<Keys>(keys)...);
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V,
+ typename Key1, typename Key2, typename ... Keys>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&>()))
+find(basic_value<C, M, V>& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
+{
+ return ::toml::find<T>(::toml::find(v, detail::key_cast(k1)),
+ detail::key_cast(k2), std::forward<Keys>(keys)...);
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V,
+ typename Key1, typename Key2, typename ... Keys>
+decltype(::toml::get<T>(std::declval<basic_value<C, M, V>&&>()))
+find(basic_value<C, M, V>&& v, Key1&& k1, Key2&& k2, Keys&& ... keys)
+{
+ return ::toml::find<T>(::toml::find(std::move(v), detail::key_cast(k1)),
+ detail::key_cast(k2), std::forward<Keys>(keys)...);
+}
+
+// ============================================================================
+// get_or(value, fallback)
+
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V> const&
+get_or(const basic_value<C, M, V>& v, const basic_value<C, M, V>&)
+{
+ return v;
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V>&
+get_or(basic_value<C, M, V>& v, basic_value<C, M, V>&)
+{
+ return v;
+}
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V>
+get_or(basic_value<C, M, V>&& v, basic_value<C, M, V>&&)
+{
+ return v;
+}
+
+// ----------------------------------------------------------------------------
+// specialization for the exact toml types (return type becomes lvalue ref)
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
+get_or(const basic_value<C, M, V>& v, const T& opt)
+{
+ try
+ {
+ return get<detail::remove_cvref_t<T>>(v);
+ }
+ catch(...)
+ {
+ return opt;
+ }
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
+get_or(basic_value<C, M, V>& v, T& opt)
+{
+ try
+ {
+ return get<detail::remove_cvref_t<T>>(v);
+ }
+ catch(...)
+ {
+ return opt;
+ }
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_exact_toml_type<detail::remove_cvref_t<T>,
+ basic_value<C, M, V>>::value, detail::remove_cvref_t<T>>
+get_or(basic_value<C, M, V>&& v, T&& opt)
+{
+ try
+ {
+ return get<detail::remove_cvref_t<T>>(std::move(v));
+ }
+ catch(...)
+ {
+ return detail::remove_cvref_t<T>(std::forward<T>(opt));
+ }
+}
+
+// ----------------------------------------------------------------------------
+// specialization for std::string (return type becomes lvalue ref)
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<std::is_same<detail::remove_cvref_t<T>, std::string>::value,
+ std::string> const&
+get_or(const basic_value<C, M, V>& v, const T& opt)
+{
+ try
+ {
+ return v.as_string().str;
+ }
+ catch(...)
+ {
+ return opt;
+ }
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
+get_or(basic_value<C, M, V>& v, T& opt)
+{
+ try
+ {
+ return v.as_string().str;
+ }
+ catch(...)
+ {
+ return opt;
+ }
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ std::is_same<detail::remove_cvref_t<T>, std::string>::value, std::string>
+get_or(basic_value<C, M, V>&& v, T&& opt)
+{
+ try
+ {
+ return std::move(v.as_string().str);
+ }
+ catch(...)
+ {
+ return std::string(std::forward<T>(opt));
+ }
+}
+
+// ----------------------------------------------------------------------------
+// specialization for string literal
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::is_string_literal<
+ typename std::remove_reference<T>::type>::value, std::string>
+get_or(const basic_value<C, M, V>& v, T&& opt)
+{
+ try
+ {
+ return std::move(v.as_string().str);
+ }
+ catch(...)
+ {
+ return std::string(std::forward<T>(opt));
+ }
+}
+
+// ----------------------------------------------------------------------------
+// others (require type conversion and return type cannot be lvalue reference)
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ detail::negation<detail::is_exact_toml_type<detail::remove_cvref_t<T>,
+ basic_value<C, M, V>>>,
+ detail::negation<std::is_same<std::string, detail::remove_cvref_t<T>>>,
+ detail::negation<detail::is_string_literal<
+ typename std::remove_reference<T>::type>>
+ >::value, detail::remove_cvref_t<T>>
+get_or(const basic_value<C, M, V>& v, T&& opt)
+{
+ try
+ {
+ return get<detail::remove_cvref_t<T>>(v);
+ }
+ catch(...)
+ {
+ return detail::remove_cvref_t<T>(std::forward<T>(opt));
+ }
+}
+
+// ===========================================================================
+// find_or(value, key, fallback)
+
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V> const&
+find_or(const basic_value<C, M, V>& v, const key& ky,
+ const basic_value<C, M, V>& opt)
+{
+ if(!v.is_table()) {return opt;}
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return tab.at(ky);
+}
+
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V>&
+find_or(basic_value<C, M, V>& v, const toml::key& ky, basic_value<C, M, V>& opt)
+{
+ if(!v.is_table()) {return opt;}
+ auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return tab.at(ky);
+}
+
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+basic_value<C, M, V>
+find_or(basic_value<C, M, V>&& v, const toml::key& ky, basic_value<C, M, V>&& opt)
+{
+ if(!v.is_table()) {return opt;}
+ auto tab = std::move(v).as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return basic_value<C, M, V>(std::move(tab.at(ky)));
+}
+
+// ---------------------------------------------------------------------------
+// exact types (return type can be a reference)
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T> const&
+find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
+{
+ if(!v.is_table()) {return opt;}
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return get_or(tab.at(ky), opt);
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>::value, T>&
+find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
+{
+ if(!v.is_table()) {return opt;}
+ auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return get_or(tab.at(ky), opt);
+}
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ detail::is_exact_toml_type<T, basic_value<C, M, V>>::value,
+ detail::remove_cvref_t<T>>
+find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
+{
+ if(!v.is_table()) {return std::forward<T>(opt);}
+ auto tab = std::move(v).as_table();
+ if(tab.count(ky) == 0) {return std::forward<T>(opt);}
+ return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
+}
+
+// ---------------------------------------------------------------------------
+// std::string (return type can be a reference)
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<std::is_same<T, std::string>::value, std::string> const&
+find_or(const basic_value<C, M, V>& v, const key& ky, const T& opt)
+{
+ if(!v.is_table()) {return opt;}
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return get_or(tab.at(ky), opt);
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<std::is_same<T, std::string>::value, std::string>&
+find_or(basic_value<C, M, V>& v, const toml::key& ky, T& opt)
+{
+ if(!v.is_table()) {return opt;}
+ auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return opt;}
+ return get_or(tab.at(ky), opt);
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<std::is_same<T, std::string>::value, std::string>
+find_or(basic_value<C, M, V>&& v, const toml::key& ky, T&& opt)
+{
+ if(!v.is_table()) {return std::forward<T>(opt);}
+ auto tab = std::move(v).as_table();
+ if(tab.count(ky) == 0) {return std::forward<T>(opt);}
+ return get_or(std::move(tab.at(ky)), std::forward<T>(opt));
+}
+
+// ---------------------------------------------------------------------------
+// string literal (deduced as std::string)
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<
+ detail::is_string_literal<typename std::remove_reference<T>::type>::value,
+ std::string>
+find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
+{
+ if(!v.is_table()) {return std::string(opt);}
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return std::string(opt);}
+ return get_or(tab.at(ky), std::forward<T>(opt));
+}
+
+// ---------------------------------------------------------------------------
+// others (require type conversion and return type cannot be lvalue reference)
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+detail::enable_if_t<detail::conjunction<
+ // T is not an exact toml type
+ detail::negation<detail::is_exact_toml_type<
+ detail::remove_cvref_t<T>, basic_value<C, M, V>>>,
+ // T is not std::string
+ detail::negation<std::is_same<std::string, detail::remove_cvref_t<T>>>,
+ // T is not a string literal
+ detail::negation<detail::is_string_literal<
+ typename std::remove_reference<T>::type>>
+ >::value, detail::remove_cvref_t<T>>
+find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
+{
+ if(!v.is_table()) {return std::forward<T>(opt);}
+ const auto& tab = v.as_table();
+ if(tab.count(ky) == 0) {return std::forward<T>(opt);}
+ return get_or(tab.at(ky), std::forward<T>(opt));
+}
+
+// ---------------------------------------------------------------------------
+// recursive find-or with type deduction (find_or(value, keys, opt))
+
+template<typename Value, typename ... Ks,
+ typename detail::enable_if_t<(sizeof...(Ks) > 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<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
+{
+ if(!v.is_table())
+ {
+ return detail::last_one(std::forward<Ks>(keys)...);
+ }
+ auto&& tab = std::forward<Value>(v).as_table();
+ if(tab.count(ky) == 0)
+ {
+ return detail::last_one(std::forward<Ks>(keys)...);
+ }
+ return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
+}
+
+// ---------------------------------------------------------------------------
+// recursive find_or with explicit type specialization, find_or<int>(value, keys...)
+
+template<typename T, typename Value, typename ... Ks,
+ typename detail::enable_if_t<(sizeof...(Ks) > 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<T>(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
+{
+ if(!v.is_table())
+ {
+ return detail::last_one(std::forward<Ks>(keys)...);
+ }
+ auto&& tab = std::forward<Value>(v).as_table();
+ if(tab.count(ky) == 0)
+ {
+ return detail::last_one(std::forward<Ks>(keys)...);
+ }
+ return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
+}
+
+// ============================================================================
+// expect
+
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+result<T, std::string> expect(const basic_value<C, M, V>& v) noexcept
+{
+ try
+ {
+ return ok(get<T>(v));
+ }
+ catch(const std::exception& e)
+ {
+ return err(e.what());
+ }
+}
+template<typename T, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+result<T, std::string>
+expect(const basic_value<C, M, V>& v, const toml::key& k) noexcept
+{
+ try
+ {
+ return ok(find<T>(v, k));
+ }
+ catch(const std::exception& e)
+ {
+ return err(e.what());
+ }
+}
+
+} // toml
+#endif// TOML11_GET
diff --git a/src/toml11/toml/into.hpp b/src/toml11/toml/into.hpp
new file mode 100644
index 000000000..74495560e
--- /dev/null
+++ b/src/toml11/toml/into.hpp
@@ -0,0 +1,19 @@
+// Copyright Toru Niina 2019.
+// Distributed under the MIT License.
+#ifndef TOML11_INTO_HPP
+#define TOML11_INTO_HPP
+
+namespace toml
+{
+
+template<typename T>
+struct into;
+// {
+// static toml::value into_toml(const T& user_defined_type)
+// {
+// // User-defined conversions ...
+// }
+// };
+
+} // toml
+#endif // TOML11_INTO_HPP
diff --git a/src/toml11/toml/lexer.hpp b/src/toml11/toml/lexer.hpp
new file mode 100644
index 000000000..ea5050b8d
--- /dev/null
+++ b/src/toml11/toml/lexer.hpp
@@ -0,0 +1,293 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_LEXER_HPP
+#define TOML11_LEXER_HPP
+#include <istream>
+#include <sstream>
+#include <stdexcept>
+#include <fstream>
+
+#include "combinator.hpp"
+
+namespace toml
+{
+namespace detail
+{
+
+// these scans contents from current location in a container of char
+// and extract a region that matches their own pattern.
+// to see the implementation of each component, see combinator.hpp.
+
+using lex_wschar = either<character<' '>, character<'\t'>>;
+using lex_ws = repeat<lex_wschar, at_least<1>>;
+using lex_newline = either<character<'\n'>,
+ sequence<character<'\r'>, character<'\n'>>>;
+using lex_lower = in_range<'a', 'z'>;
+using lex_upper = in_range<'A', 'Z'>;
+using lex_alpha = either<lex_lower, lex_upper>;
+using lex_digit = in_range<'0', '9'>;
+using lex_nonzero = in_range<'1', '9'>;
+using lex_oct_dig = in_range<'0', '7'>;
+using lex_bin_dig = in_range<'0', '1'>;
+using lex_hex_dig = either<lex_digit, in_range<'A', 'F'>, in_range<'a', 'f'>>;
+
+using lex_hex_prefix = sequence<character<'0'>, character<'x'>>;
+using lex_oct_prefix = sequence<character<'0'>, character<'o'>>;
+using lex_bin_prefix = sequence<character<'0'>, character<'b'>>;
+using lex_underscore = character<'_'>;
+using lex_plus = character<'+'>;
+using lex_minus = character<'-'>;
+using lex_sign = either<lex_plus, lex_minus>;
+
+// digit | nonzero 1*(digit | _ digit)
+using lex_unsigned_dec_int = either<sequence<lex_nonzero, repeat<
+ either<lex_digit, sequence<lex_underscore, lex_digit>>, at_least<1>>>,
+ lex_digit>;
+// (+|-)? unsigned_dec_int
+using lex_dec_int = sequence<maybe<lex_sign>, lex_unsigned_dec_int>;
+
+// hex_prefix hex_dig *(hex_dig | _ hex_dig)
+using lex_hex_int = sequence<lex_hex_prefix, sequence<lex_hex_dig, repeat<
+ either<lex_hex_dig, sequence<lex_underscore, lex_hex_dig>>, unlimited>>>;
+// oct_prefix oct_dig *(oct_dig | _ oct_dig)
+using lex_oct_int = sequence<lex_oct_prefix, sequence<lex_oct_dig, repeat<
+ either<lex_oct_dig, sequence<lex_underscore, lex_oct_dig>>, unlimited>>>;
+// bin_prefix bin_dig *(bin_dig | _ bin_dig)
+using lex_bin_int = sequence<lex_bin_prefix, sequence<lex_bin_dig, repeat<
+ either<lex_bin_dig, sequence<lex_underscore, lex_bin_dig>>, unlimited>>>;
+
+// (dec_int | hex_int | oct_int | bin_int)
+using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>;
+
+// ===========================================================================
+
+using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>;
+using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>;
+using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>;
+
+using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
+ sequence<lex_underscore, lex_digit>>, unlimited>>;
+
+using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
+
+using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
+ maybe<lex_sign>, lex_zero_prefixable_int>;
+
+using lex_float = either<lex_special_float,
+ sequence<lex_dec_int, either<lex_exponent_part,
+ sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>;
+
+// ===========================================================================
+
+using lex_true = sequence<character<'t'>, character<'r'>,
+ character<'u'>, character<'e'>>;
+using lex_false = sequence<character<'f'>, character<'a'>, character<'l'>,
+ character<'s'>, character<'e'>>;
+using lex_boolean = either<lex_true, lex_false>;
+
+// ===========================================================================
+
+using lex_date_fullyear = repeat<lex_digit, exactly<4>>;
+using lex_date_month = repeat<lex_digit, exactly<2>>;
+using lex_date_mday = repeat<lex_digit, exactly<2>>;
+using lex_time_delim = either<character<'T'>, character<'t'>, character<' '>>;
+using lex_time_hour = repeat<lex_digit, exactly<2>>;
+using lex_time_minute = repeat<lex_digit, exactly<2>>;
+using lex_time_second = repeat<lex_digit, exactly<2>>;
+using lex_time_secfrac = sequence<character<'.'>,
+ repeat<lex_digit, at_least<1>>>;
+
+using lex_time_numoffset = sequence<either<character<'+'>, character<'-'>>,
+ sequence<lex_time_hour, character<':'>,
+ lex_time_minute>>;
+using lex_time_offset = either<character<'Z'>, character<'z'>,
+ lex_time_numoffset>;
+
+using lex_partial_time = sequence<lex_time_hour, character<':'>,
+ lex_time_minute, character<':'>,
+ lex_time_second, maybe<lex_time_secfrac>>;
+using lex_full_date = sequence<lex_date_fullyear, character<'-'>,
+ lex_date_month, character<'-'>,
+ lex_date_mday>;
+using lex_full_time = sequence<lex_partial_time, lex_time_offset>;
+
+using lex_offset_date_time = sequence<lex_full_date, lex_time_delim, lex_full_time>;
+using lex_local_date_time = sequence<lex_full_date, lex_time_delim, lex_partial_time>;
+using lex_local_date = lex_full_date;
+using lex_local_time = lex_partial_time;
+
+// ===========================================================================
+
+using lex_quotation_mark = character<'"'>;
+using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
+ in_range<0x0A, 0x1F>,
+ character<0x22>, character<0x5C>,
+ character<0x7F>>>;
+
+using lex_escape = character<'\\'>;
+using lex_escape_unicode_short = sequence<character<'u'>,
+ repeat<lex_hex_dig, exactly<4>>>;
+using lex_escape_unicode_long = sequence<character<'U'>,
+ repeat<lex_hex_dig, exactly<8>>>;
+using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
+ character<'b'>, character<'f'>,
+ character<'n'>, character<'r'>,
+ character<'t'>,
+ lex_escape_unicode_short,
+ lex_escape_unicode_long
+ >;
+using lex_escaped = sequence<lex_escape, lex_escape_seq_char>;
+using lex_basic_char = either<lex_basic_unescaped, lex_escaped>;
+using lex_basic_string = sequence<lex_quotation_mark,
+ repeat<lex_basic_char, unlimited>,
+ lex_quotation_mark>;
+
+// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
+// are allowed to be used.
+// After this, the following strings are *explicitly* allowed.
+// - One or two `"`s in a multi-line basic string is allowed wherever it is.
+// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
+// - One or two `"`s can appear just before or after the delimiter.
+// ```toml
+// str4 = """Here are two quotation marks: "". Simple enough."""
+// str5 = """Here are three quotation marks: ""\"."""
+// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
+// str7 = """"This," she said, "is just a pointless statement.""""
+// ```
+// In the current implementation (v3.3.0), it is difficult to parse `str7` in
+// the above example. It is difficult to recognize `"` at the end of string body
+// collectly. It will be misunderstood as a `"""` delimiter and an additional,
+// invalid `"`. Like this:
+// ```console
+// what(): [error] toml::parse_table: invalid line format
+// --> hoge.toml
+// |
+// 13 | str7 = """"This," she said, "is just a pointless statement.""""
+// | ^- expected newline, but got '"'.
+// ```
+// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
+// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
+// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
+// In parse_ml_basic_string() function, the trailing `"`s will be attached to
+// the string body.
+//
+using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
+using lex_ml_basic_string_open = lex_ml_basic_string_delim;
+using lex_ml_basic_string_close = sequence<
+ repeat<lex_quotation_mark, exactly<3>>,
+ maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
+ >;
+
+using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
+ in_range<0x0A, 0x1F>,
+ character<0x5C>, // backslash
+ character<0x7F>, // DEL
+ lex_ml_basic_string_delim>>;
+
+using lex_ml_basic_escaped_newline = sequence<
+ lex_escape, maybe<lex_ws>, lex_newline,
+ repeat<either<lex_ws, lex_newline>, unlimited>>;
+
+using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
+using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
+ lex_ml_basic_escaped_newline>,
+ unlimited>;
+using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
+ lex_ml_basic_body,
+ lex_ml_basic_string_close>;
+
+using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
+ character<0x7F>, character<0x27>>>;
+using lex_apostrophe = character<'\''>;
+using lex_literal_string = sequence<lex_apostrophe,
+ repeat<lex_literal_char, unlimited>,
+ lex_apostrophe>;
+
+// the same reason as above.
+using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
+using lex_ml_literal_string_open = lex_ml_literal_string_delim;
+using lex_ml_literal_string_close = sequence<
+ repeat<lex_apostrophe, exactly<3>>,
+ maybe<lex_apostrophe>, maybe<lex_apostrophe>
+ >;
+
+using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
+ in_range<0x0A, 0x1F>,
+ character<0x7F>,
+ lex_ml_literal_string_delim>>;
+using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
+ unlimited>;
+using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
+ lex_ml_literal_body,
+ lex_ml_literal_string_close>;
+
+using lex_string = either<lex_ml_basic_string, lex_basic_string,
+ lex_ml_literal_string, lex_literal_string>;
+
+// ===========================================================================
+using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
+
+using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
+ character<'-'>, character<'_'>>,
+ at_least<1>>;
+using lex_quoted_key = either<lex_basic_string, lex_literal_string>;
+using lex_simple_key = either<lex_unquoted_key, lex_quoted_key>;
+using lex_dotted_key = sequence<lex_simple_key,
+ repeat<sequence<lex_dot_sep, lex_simple_key>,
+ at_least<1>
+ >
+ >;
+using lex_key = either<lex_dotted_key, lex_simple_key>;
+
+using lex_keyval_sep = sequence<maybe<lex_ws>,
+ character<'='>,
+ maybe<lex_ws>>;
+
+using lex_std_table_open = character<'['>;
+using lex_std_table_close = character<']'>;
+using lex_std_table = sequence<lex_std_table_open,
+ maybe<lex_ws>,
+ lex_key,
+ maybe<lex_ws>,
+ lex_std_table_close>;
+
+using lex_array_table_open = sequence<lex_std_table_open, lex_std_table_open>;
+using lex_array_table_close = sequence<lex_std_table_close, lex_std_table_close>;
+using lex_array_table = sequence<lex_array_table_open,
+ maybe<lex_ws>,
+ lex_key,
+ maybe<lex_ws>,
+ lex_array_table_close>;
+
+using lex_utf8_1byte = in_range<0x00, 0x7F>;
+using lex_utf8_2byte = sequence<
+ in_range<static_cast<char>(0xC2), static_cast<char>(0xDF)>,
+ in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>
+ >;
+using lex_utf8_3byte = sequence<either<
+ sequence<character<static_cast<char>(0xE0)>, in_range<static_cast<char>(0xA0), static_cast<char>(0xBF)>>,
+ sequence<in_range <static_cast<char>(0xE1), static_cast<char>(0xEC)>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>,
+ sequence<character<static_cast<char>(0xED)>, in_range<static_cast<char>(0x80), static_cast<char>(0x9F)>>,
+ sequence<in_range <static_cast<char>(0xEE), static_cast<char>(0xEF)>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>
+ >, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>;
+using lex_utf8_4byte = sequence<either<
+ sequence<character<static_cast<char>(0xF0)>, in_range<static_cast<char>(0x90), static_cast<char>(0xBF)>>,
+ sequence<in_range <static_cast<char>(0xF1), static_cast<char>(0xF3)>, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>,
+ sequence<character<static_cast<char>(0xF4)>, in_range<static_cast<char>(0x80), static_cast<char>(0x8F)>>
+ >, in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>,
+ in_range<static_cast<char>(0x80), static_cast<char>(0xBF)>>;
+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<character<0x09>, in_range<0x20, 0x7E>>;
+using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
+ lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
+
+} // detail
+} // toml
+#endif // TOML_LEXER_HPP
diff --git a/src/toml11/toml/literal.hpp b/src/toml11/toml/literal.hpp
new file mode 100644
index 000000000..04fbbc13e
--- /dev/null
+++ b/src/toml11/toml/literal.hpp
@@ -0,0 +1,113 @@
+// Copyright Toru Niina 2019.
+// Distributed under the MIT License.
+#ifndef TOML11_LITERAL_HPP
+#define TOML11_LITERAL_HPP
+#include "parser.hpp"
+
+namespace toml
+{
+inline namespace literals
+{
+inline namespace toml_literals
+{
+
+// implementation
+inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
+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<toml::detail::sequence<
+ ::toml::detail::maybe<::toml::detail::lex_ws>,
+ ::toml::detail::maybe<::toml::detail::lex_comment>,
+ ::toml::detail::lex_newline
+ >, ::toml::detail::at_least<1>>;
+ skip_line::invoke(loc);
+
+ // if there are some whitespaces before a value, skip them.
+ using skip_ws = ::toml::detail::repeat<
+ ::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
+ skip_ws::invoke(loc);
+
+ // to distinguish arrays and tables, first check it is a table or not.
+ //
+ // "[1,2,3]"_toml; // this is an array
+ // "[table]"_toml; // a table that has an empty table named "table" inside.
+ // "[[1,2,3]]"_toml; // this is an array of arrays
+ // "[[table]]"_toml; // this is a table that has an array of tables inside.
+ //
+ // "[[1]]"_toml; // this can be both... (currently it becomes a table)
+ // "1 = [{}]"_toml; // this is a table that has an array of table named 1.
+ // "[[1,]]"_toml; // this is an array of arrays.
+ // "[[1],]"_toml; // this also.
+
+ const auto the_front = loc.iter();
+
+ const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc);
+ loc.reset(the_front);
+
+ const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc);
+ loc.reset(the_front);
+
+ // 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<value_type>(loc))
+ {
+ return data.unwrap();
+ }
+ }
+
+ // Note that still it can be a table, because the literal might be something
+ // like the following.
+ // ```cpp
+ // R"( // c++11 raw string literals
+ // key = "value"
+ // int = 42
+ // )"_toml;
+ // ```
+ // 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<value_type>(loc))
+ {
+ return data.unwrap();
+ }
+ else // none of them.
+ {
+ throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
+ }
+
+}
+
+inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
+operator"" _toml(const char* str, std::size_t len)
+{
+ ::toml::detail::location loc(
+ std::string("TOML literal encoded in a C++ code"),
+ std::vector<char>(str, str + len));
+ // literal length does not include the null character at the end.
+ return literal_internal_impl(std::move(loc));
+}
+
+// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
+// So here we use the feature test macro for `char8_t` itself.
+#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::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
+operator"" _toml(const char8_t* str, std::size_t len)
+{
+ ::toml::detail::location loc(
+ std::string("TOML literal encoded in a C++ code"),
+ std::vector<char>(reinterpret_cast<const char*>(str),
+ reinterpret_cast<const char*>(str) + len));
+ return literal_internal_impl(std::move(loc));
+}
+#endif
+
+} // toml_literals
+} // literals
+} // toml
+#endif//TOML11_LITERAL_HPP
diff --git a/src/toml11/toml/macros.hpp b/src/toml11/toml/macros.hpp
new file mode 100644
index 000000000..e8f91aecd
--- /dev/null
+++ b/src/toml11/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<foo::Foo>(file, "foo");`
+//
+#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
+ obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(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<NAME> \
+ { \
+ template<typename C, template<typename ...> class T, \
+ template<typename ...> class A> \
+ static NAME from_toml(const basic_value<C, T, A>& v) \
+ { \
+ NAME obj; \
+ TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
+ return obj; \
+ } \
+ }; \
+ template<> \
+ struct into<NAME> \
+ { \
+ 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/src/toml11/toml/parser.hpp b/src/toml11/toml/parser.hpp
new file mode 100644
index 000000000..e31179918
--- /dev/null
+++ b/src/toml11/toml/parser.hpp
@@ -0,0 +1,2364 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_PARSER_HPP
+#define TOML11_PARSER_HPP
+#include <cstring>
+#include <fstream>
+#include <sstream>
+
+#include "combinator.hpp"
+#include "lexer.hpp"
+#include "region.hpp"
+#include "result.hpp"
+#include "types.hpp"
+#include "value.hpp"
+
+#ifndef TOML11_DISABLE_STD_FILESYSTEM
+#ifdef __cpp_lib_filesystem
+#if __has_include(<filesystem>)
+#define TOML11_HAS_STD_FILESYSTEM
+#include <filesystem>
+#endif // has_include(<string_view>)
+#endif // __cpp_lib_filesystem
+#endif // TOML11_DISABLE_STD_FILESYSTEM
+
+namespace toml
+{
+namespace detail
+{
+
+inline result<std::pair<boolean, region>, std::string>
+parse_boolean(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_boolean::invoke(loc))
+ {
+ const auto reg = token.unwrap();
+ if (reg.str() == "true") {return ok(std::make_pair(true, reg));}
+ else if(reg.str() == "false") {return ok(std::make_pair(false, reg));}
+ else // internal error.
+ {
+ throw internal_error(format_underline(
+ "toml::parse_boolean: internal error",
+ {{source_location(reg), "invalid token"}}),
+ source_location(reg));
+ }
+ }
+ loc.reset(first); //rollback
+ return err(format_underline("toml::parse_boolean: ",
+ {{source_location(loc), "the next token is not a boolean"}}));
+}
+
+inline result<std::pair<integer, region>, std::string>
+parse_binary_integer(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_bin_int::invoke(loc))
+ {
+ auto str = token.unwrap().str();
+ assert(str.size() > 2); // minimum -> 0b1
+ integer retval(0), base(1);
+ for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i)
+ {
+ if (*i == '1'){retval += base; base *= 2;}
+ else if(*i == '0'){base *= 2;}
+ else if(*i == '_'){/* do nothing. */}
+ else // internal error.
+ {
+ throw internal_error(format_underline(
+ "toml::parse_integer: internal error",
+ {{source_location(token.unwrap()), "invalid token"}}),
+ source_location(loc));
+ }
+ }
+ return ok(std::make_pair(retval, token.unwrap()));
+ }
+ loc.reset(first);
+ return err(format_underline("toml::parse_binary_integer:",
+ {{source_location(loc), "the next token is not an integer"}}));
+}
+
+inline result<std::pair<integer, region>, std::string>
+parse_octal_integer(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_oct_int::invoke(loc))
+ {
+ auto str = token.unwrap().str();
+ str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
+ str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix
+
+ std::istringstream iss(str);
+ integer retval(0);
+ iss >> std::oct >> retval;
+ return ok(std::make_pair(retval, token.unwrap()));
+ }
+ loc.reset(first);
+ return err(format_underline("toml::parse_octal_integer:",
+ {{source_location(loc), "the next token is not an integer"}}));
+}
+
+inline result<std::pair<integer, region>, std::string>
+parse_hexadecimal_integer(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_hex_int::invoke(loc))
+ {
+ auto str = token.unwrap().str();
+ str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
+ str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix
+
+ std::istringstream iss(str);
+ integer retval(0);
+ iss >> std::hex >> retval;
+ return ok(std::make_pair(retval, token.unwrap()));
+ }
+ loc.reset(first);
+ return err(format_underline("toml::parse_hexadecimal_integer",
+ {{source_location(loc), "the next token is not an integer"}}));
+}
+
+inline result<std::pair<integer, region>, std::string>
+parse_integer(location& loc)
+{
+ const auto first = loc.iter();
+ if(first != loc.end() && *first == '0')
+ {
+ const auto second = std::next(first);
+ if(second == loc.end()) // the token is just zero.
+ {
+ loc.advance();
+ return ok(std::make_pair(0, region(loc, first, second)));
+ }
+
+ if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100
+ if(*second == 'o') {return parse_octal_integer (loc);} // 0o775
+ if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE
+
+ if(std::isdigit(*second))
+ {
+ return err(format_underline("toml::parse_integer: "
+ "leading zero in an Integer is not allowed.",
+ {{source_location(loc), "leading zero"}}));
+ }
+ else if(std::isalpha(*second))
+ {
+ return err(format_underline("toml::parse_integer: "
+ "unknown integer prefix appeared.",
+ {{source_location(loc), "none of 0x, 0o, 0b"}}));
+ }
+ }
+
+ if(const auto token = lex_dec_int::invoke(loc))
+ {
+ auto str = token.unwrap().str();
+ str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
+
+ std::istringstream iss(str);
+ integer retval(0);
+ iss >> retval;
+ return ok(std::make_pair(retval, token.unwrap()));
+ }
+ loc.reset(first);
+ return err(format_underline("toml::parse_integer: ",
+ {{source_location(loc), "the next token is not an integer"}}));
+}
+
+inline result<std::pair<floating, region>, std::string>
+parse_floating(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_float::invoke(loc))
+ {
+ auto str = token.unwrap().str();
+ if(str == "inf" || str == "+inf")
+ {
+ if(std::numeric_limits<floating>::has_infinity)
+ {
+ return ok(std::make_pair(
+ std::numeric_limits<floating>::infinity(), token.unwrap()));
+ }
+ else
+ {
+ throw std::domain_error("toml::parse_floating: inf value found"
+ " but the current environment does not support inf. Please"
+ " make sure that the floating-point implementation conforms"
+ " IEEE 754/ISO 60559 international standard.");
+ }
+ }
+ else if(str == "-inf")
+ {
+ if(std::numeric_limits<floating>::has_infinity)
+ {
+ return ok(std::make_pair(
+ -std::numeric_limits<floating>::infinity(), token.unwrap()));
+ }
+ else
+ {
+ throw std::domain_error("toml::parse_floating: inf value found"
+ " but the current environment does not support inf. Please"
+ " make sure that the floating-point implementation conforms"
+ " IEEE 754/ISO 60559 international standard.");
+ }
+ }
+ else if(str == "nan" || str == "+nan")
+ {
+ if(std::numeric_limits<floating>::has_quiet_NaN)
+ {
+ return ok(std::make_pair(
+ std::numeric_limits<floating>::quiet_NaN(), token.unwrap()));
+ }
+ else if(std::numeric_limits<floating>::has_signaling_NaN)
+ {
+ return ok(std::make_pair(
+ std::numeric_limits<floating>::signaling_NaN(), token.unwrap()));
+ }
+ else
+ {
+ throw std::domain_error("toml::parse_floating: NaN value found"
+ " but the current environment does not support NaN. Please"
+ " make sure that the floating-point implementation conforms"
+ " IEEE 754/ISO 60559 international standard.");
+ }
+ }
+ else if(str == "-nan")
+ {
+ if(std::numeric_limits<floating>::has_quiet_NaN)
+ {
+ return ok(std::make_pair(
+ -std::numeric_limits<floating>::quiet_NaN(), token.unwrap()));
+ }
+ else if(std::numeric_limits<floating>::has_signaling_NaN)
+ {
+ return ok(std::make_pair(
+ -std::numeric_limits<floating>::signaling_NaN(), token.unwrap()));
+ }
+ else
+ {
+ throw std::domain_error("toml::parse_floating: NaN value found"
+ " but the current environment does not support NaN. Please"
+ " make sure that the floating-point implementation conforms"
+ " IEEE 754/ISO 60559 international standard.");
+ }
+ }
+ str.erase(std::remove(str.begin(), str.end(), '_'), str.end());
+ std::istringstream iss(str);
+ floating v(0.0);
+ iss >> v;
+ return ok(std::make_pair(v, token.unwrap()));
+ }
+ loc.reset(first);
+ return err(format_underline("toml::parse_floating: ",
+ {{source_location(loc), "the next token is not a float"}}));
+}
+
+inline std::string read_utf8_codepoint(const region& reg, const location& loc)
+{
+ const auto str = reg.str().substr(1);
+ std::uint_least32_t codepoint;
+ std::istringstream iss(str);
+ iss >> std::hex >> codepoint;
+
+ const auto to_char = [](const std::uint_least32_t i) noexcept -> char {
+ const auto uc = static_cast<unsigned char>(i);
+ return *reinterpret_cast<const char*>(std::addressof(uc));
+ };
+
+ std::string character;
+ if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII.
+ {
+ character += static_cast<char>(codepoint);
+ }
+ else if(codepoint < 0x800) //U+0080 ... U+07FF
+ {
+ // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111
+ character += to_char(0xC0| codepoint >> 6);
+ character += to_char(0x80|(codepoint & 0x3F));
+ }
+ else if(codepoint < 0x10000) // U+0800...U+FFFF
+ {
+ if(0xD800 <= codepoint && codepoint <= 0xDFFF)
+ {
+ throw syntax_error(format_underline(
+ "toml::read_utf8_codepoint: codepoints in the range "
+ "[0xD800, 0xDFFF] are not valid UTF-8.", {{
+ source_location(loc), "not a valid UTF-8 codepoint"
+ }}), source_location(loc));
+ }
+ assert(codepoint < 0xD800 || 0xDFFF < codepoint);
+ // 1110yyyy 10yxxxxx 10xxxxxx
+ character += to_char(0xE0| codepoint >> 12);
+ character += to_char(0x80|(codepoint >> 6 & 0x3F));
+ character += to_char(0x80|(codepoint & 0x3F));
+ }
+ else if(codepoint < 0x110000) // U+010000 ... U+10FFFF
+ {
+ // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
+ character += to_char(0xF0| codepoint >> 18);
+ character += to_char(0x80|(codepoint >> 12 & 0x3F));
+ character += to_char(0x80|(codepoint >> 6 & 0x3F));
+ character += to_char(0x80|(codepoint & 0x3F));
+ }
+ else // out of UTF-8 region
+ {
+ throw syntax_error(format_underline("toml::read_utf8_codepoint:"
+ " input codepoint is too large.",
+ {{source_location(loc), "should be in [0x00..0x10FFFF]"}}),
+ source_location(loc));
+ }
+ return character;
+}
+
+inline result<std::string, std::string> parse_escape_sequence(location& loc)
+{
+ const auto first = loc.iter();
+ if(first == loc.end() || *first != '\\')
+ {
+ return err(format_underline("toml::parse_escape_sequence: ", {{
+ source_location(loc), "the next token is not a backslash \"\\\""}}));
+ }
+ loc.advance();
+ switch(*loc.iter())
+ {
+ case '\\':{loc.advance(); return ok(std::string("\\"));}
+ case '"' :{loc.advance(); return ok(std::string("\""));}
+ case 'b' :{loc.advance(); return ok(std::string("\b"));}
+ case 't' :{loc.advance(); return ok(std::string("\t"));}
+ 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"));}
+ case 'u' :
+ {
+ if(const auto token = lex_escape_unicode_short::invoke(loc))
+ {
+ return ok(read_utf8_codepoint(token.unwrap(), loc));
+ }
+ else
+ {
+ return err(format_underline("parse_escape_sequence: "
+ "invalid token found in UTF-8 codepoint uXXXX.",
+ {{source_location(loc), "here"}}));
+ }
+ }
+ case 'U':
+ {
+ if(const auto token = lex_escape_unicode_long::invoke(loc))
+ {
+ return ok(read_utf8_codepoint(token.unwrap(), loc));
+ }
+ else
+ {
+ return err(format_underline("parse_escape_sequence: "
+ "invalid token found in UTF-8 codepoint Uxxxxxxxx",
+ {{source_location(loc), "here"}}));
+ }
+ }
+ }
+
+ const auto msg = format_underline("parse_escape_sequence: "
+ "unknown escape sequence appeared.", {{source_location(loc),
+ "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}},
+ /* Hints = */{"if you want to write backslash as just one backslash, "
+ "use literal string like: regex = '<\\i\\c*\\s*>'"});
+ loc.reset(first);
+ return err(msg);
+}
+
+inline std::ptrdiff_t check_utf8_validity(const std::string& reg)
+{
+ location loc("tmp", reg);
+ const auto u8 = repeat<lex_utf8_code, unlimited>::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::pair<toml::string, region>, std::string>
+parse_ml_basic_string(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_ml_basic_string::invoke(loc))
+ {
+ auto inner_loc = loc;
+ inner_loc.reset(first);
+
+ std::string retval;
+ retval.reserve(token.unwrap().size());
+
+ auto delim = lex_ml_basic_string_open::invoke(inner_loc);
+ if(!delim)
+ {
+ throw internal_error(format_underline(
+ "parse_ml_basic_string: invalid token",
+ {{source_location(inner_loc), "should be \"\"\""}}),
+ source_location(inner_loc));
+ }
+ // immediate newline is ignored (if exists)
+ /* discard return value */ lex_newline::invoke(inner_loc);
+
+ delim = none();
+ while(!delim)
+ {
+ using lex_unescaped_seq = repeat<
+ either<lex_ml_basic_unescaped, lex_newline>, unlimited>;
+ if(auto unescaped = lex_unescaped_seq::invoke(inner_loc))
+ {
+ retval += unescaped.unwrap().str();
+ }
+ if(auto escaped = parse_escape_sequence(inner_loc))
+ {
+ retval += escaped.unwrap();
+ }
+ if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc))
+ {
+ // ignore newline after escape until next non-ws char
+ }
+ if(inner_loc.iter() == inner_loc.end())
+ {
+ throw internal_error(format_underline(
+ "parse_ml_basic_string: unexpected end of region",
+ {{source_location(inner_loc), "not sufficient token"}}),
+ source_location(inner_loc));
+ }
+ delim = lex_ml_basic_string_close::invoke(inner_loc);
+ }
+ // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s
+ // at just before the delimiter. Here, we need to attach `"`s at the
+ // end of the string body, if it exists.
+ // For detail, see the definition of `lex_ml_basic_string_close`.
+ assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(),
+ [](const char c) noexcept {return c == '\"';}));
+ switch(delim.unwrap().size())
+ {
+ case 3: {break;}
+ case 4: {retval += "\""; break;}
+ case 5: {retval += "\"\""; break;}
+ default:
+ {
+ throw internal_error(format_underline(
+ "parse_ml_basic_string: closing delimiter has invalid length",
+ {{source_location(inner_loc), "end of this"}}),
+ source_location(inner_loc));
+ }
+ }
+
+ 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
+ {
+ loc.reset(first);
+ return err(format_underline("toml::parse_ml_basic_string: "
+ "the next token is not a valid multiline string",
+ {{source_location(loc), "here"}}));
+ }
+}
+
+inline result<std::pair<toml::string, region>, std::string>
+parse_basic_string(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_basic_string::invoke(loc))
+ {
+ auto inner_loc = loc;
+ inner_loc.reset(first);
+
+ auto quot = lex_quotation_mark::invoke(inner_loc);
+ if(!quot)
+ {
+ throw internal_error(format_underline("parse_basic_string: "
+ "invalid token", {{source_location(inner_loc), "should be \""}}),
+ source_location(inner_loc));
+ }
+
+ std::string retval;
+ retval.reserve(token.unwrap().size());
+
+ quot = none();
+ while(!quot)
+ {
+ using lex_unescaped_seq = repeat<lex_basic_unescaped, unlimited>;
+ if(auto unescaped = lex_unescaped_seq::invoke(inner_loc))
+ {
+ retval += unescaped.unwrap().str();
+ }
+ if(auto escaped = parse_escape_sequence(inner_loc))
+ {
+ retval += escaped.unwrap();
+ }
+ if(inner_loc.iter() == inner_loc.end())
+ {
+ throw internal_error(format_underline(
+ "parse_basic_string: unexpected end of region",
+ {{source_location(inner_loc), "not sufficient token"}}),
+ source_location(inner_loc));
+ }
+ quot = lex_quotation_mark::invoke(inner_loc);
+ }
+
+ 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
+ {
+ loc.reset(first); // rollback
+ return err(format_underline("toml::parse_basic_string: "
+ "the next token is not a valid string",
+ {{source_location(loc), "here"}}));
+ }
+}
+
+inline result<std::pair<toml::string, region>, std::string>
+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());
+
+ const auto open = lex_ml_literal_string_open::invoke(inner_loc);
+ if(!open)
+ {
+ throw internal_error(format_underline(
+ "parse_ml_literal_string: invalid token",
+ {{source_location(inner_loc), "should be '''"}}),
+ source_location(inner_loc));
+ }
+ // immediate newline is ignored (if exists)
+ /* discard return value */ lex_newline::invoke(inner_loc);
+
+ const auto body = lex_ml_literal_body::invoke(inner_loc);
+
+ const auto close = lex_ml_literal_string_close::invoke(inner_loc);
+ if(!close)
+ {
+ throw internal_error(format_underline(
+ "parse_ml_literal_string: invalid token",
+ {{source_location(inner_loc), "should be '''"}}),
+ source_location(inner_loc));
+ }
+ // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s
+ // at just before the delimiter. Here, we need to attach `'`s at the
+ // end of the string body, if it exists.
+ // For detail, see the definition of `lex_ml_basic_string_close`.
+
+ std::string retval = body.unwrap().str();
+ assert(std::all_of(close.unwrap().first(), close.unwrap().last(),
+ [](const char c) noexcept {return c == '\'';}));
+ switch(close.unwrap().size())
+ {
+ case 3: {break;}
+ case 4: {retval += "'"; break;}
+ case 5: {retval += "''"; break;}
+ default:
+ {
+ throw internal_error(format_underline(
+ "parse_ml_literal_string: closing delimiter has invalid length",
+ {{source_location(inner_loc), "end of this"}}),
+ source_location(inner_loc));
+ }
+ }
+
+ 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_basic_string: invalid utf8 sequence found",
+ {{source_location(inner_loc), "here"}}),
+ source_location(inner_loc));
+ }
+ }
+ else
+ {
+ loc.reset(first); // rollback
+ return err(format_underline("toml::parse_ml_literal_string: "
+ "the next token is not a valid multiline literal string",
+ {{source_location(loc), "here"}}));
+ }
+}
+
+inline result<std::pair<toml::string, region>, std::string>
+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());
+
+ const auto open = lex_apostrophe::invoke(inner_loc);
+ if(!open)
+ {
+ throw internal_error(format_underline(
+ "parse_literal_string: invalid token",
+ {{source_location(inner_loc), "should be '"}}),
+ source_location(inner_loc));
+ }
+
+ const auto body = repeat<lex_literal_char, unlimited>::invoke(inner_loc);
+
+ const auto close = lex_apostrophe::invoke(inner_loc);
+ if(!close)
+ {
+ throw internal_error(format_underline(
+ "parse_literal_string: invalid token",
+ {{source_location(inner_loc), "should be '"}}),
+ source_location(inner_loc));
+ }
+
+ 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_ml_basic_string: invalid utf8 sequence found",
+ {{source_location(inner_loc), "here"}}),
+ source_location(inner_loc));
+ }
+ }
+ else
+ {
+ loc.reset(first); // rollback
+ return err(format_underline("toml::parse_literal_string: "
+ "the next token is not a valid literal string",
+ {{source_location(loc), "here"}}));
+ }
+}
+
+inline result<std::pair<toml::string, region>, std::string>
+parse_string(location& loc)
+{
+ if(loc.iter() != loc.end() && *(loc.iter()) == '"')
+ {
+ if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' &&
+ loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"')
+ {
+ return parse_ml_basic_string(loc);
+ }
+ else
+ {
+ return parse_basic_string(loc);
+ }
+ }
+ else if(loc.iter() != loc.end() && *(loc.iter()) == '\'')
+ {
+ if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' &&
+ loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'')
+ {
+ return parse_ml_literal_string(loc);
+ }
+ else
+ {
+ return parse_literal_string(loc);
+ }
+ }
+ return err(format_underline("toml::parse_string: ",
+ {{source_location(loc), "the next token is not a string"}}));
+}
+
+inline result<std::pair<local_date, region>, std::string>
+parse_local_date(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_local_date::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+
+ const auto y = lex_date_fullyear::invoke(inner_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",
+ {{source_location(inner_loc), "should be `-`"}}),
+ source_location(inner_loc));
+ }
+ inner_loc.advance();
+ const auto m = lex_date_month::invoke(inner_loc);
+ if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-')
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_date: invalid month format",
+ {{source_location(inner_loc), "should be `-`"}}),
+ source_location(inner_loc));
+ }
+ inner_loc.advance();
+ const auto d = lex_date_mday::invoke(inner_loc);
+ if(!d)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_date: invalid day format",
+ {{source_location(inner_loc), "here"}}),
+ source_location(inner_loc));
+ }
+
+ const auto year = static_cast<std::int16_t>(from_string<int>(y.unwrap().str(), 0));
+ const auto month = static_cast<std::int8_t >(from_string<int>(m.unwrap().str(), 0));
+ const auto day = static_cast<std::int8_t >(from_string<int>(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_t>(month - 1), day),
+ token.unwrap()));
+ }
+ else
+ {
+ loc.reset(first);
+ return err(format_underline("toml::parse_local_date: ",
+ {{source_location(loc), "the next token is not a local_date"}}));
+ }
+}
+
+inline result<std::pair<local_time, region>, std::string>
+parse_local_time(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_local_time::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+
+ const auto h = lex_time_hour::invoke(inner_loc);
+ if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':')
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_time: invalid year format",
+ {{source_location(inner_loc), "should be `:`"}}),
+ source_location(inner_loc));
+ }
+ inner_loc.advance();
+ const auto m = lex_time_minute::invoke(inner_loc);
+ if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':')
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_time: invalid month format",
+ {{source_location(inner_loc), "should be `:`"}}),
+ source_location(inner_loc));
+ }
+ inner_loc.advance();
+ const auto s = lex_time_second::invoke(inner_loc);
+ if(!s)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_time: invalid second format",
+ {{source_location(inner_loc), "here"}}),
+ source_location(inner_loc));
+ }
+
+ const int hour = from_string<int>(h.unwrap().str(), 0);
+ const int minute = from_string<int>(m.unwrap().str(), 0);
+ const int second = from_string<int>(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_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))
+ {
+ auto sf = secfrac.unwrap().str();
+ sf.erase(sf.begin()); // sf.front() == '.'
+ switch(sf.size() % 3)
+ {
+ case 2: sf += '0'; break;
+ case 1: sf += "00"; break;
+ case 0: break;
+ default: break;
+ }
+ if(sf.size() >= 9)
+ {
+ time.millisecond = from_string<std::uint16_t>(sf.substr(0, 3), 0u);
+ time.microsecond = from_string<std::uint16_t>(sf.substr(3, 3), 0u);
+ time.nanosecond = from_string<std::uint16_t>(sf.substr(6, 3), 0u);
+ }
+ else if(sf.size() >= 6)
+ {
+ time.millisecond = from_string<std::uint16_t>(sf.substr(0, 3), 0u);
+ time.microsecond = from_string<std::uint16_t>(sf.substr(3, 3), 0u);
+ }
+ else if(sf.size() >= 3)
+ {
+ time.millisecond = from_string<std::uint16_t>(sf, 0u);
+ time.microsecond = 0u;
+ }
+ }
+ else
+ {
+ if(before_secfrac != inner_loc.iter())
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_time: invalid subsecond format",
+ {{source_location(inner_loc), "here"}}),
+ source_location(inner_loc));
+ }
+ }
+ return ok(std::make_pair(time, token.unwrap()));
+ }
+ else
+ {
+ loc.reset(first);
+ return err(format_underline("toml::parse_local_time: ",
+ {{source_location(loc), "the next token is not a local_time"}}));
+ }
+}
+
+inline result<std::pair<local_datetime, region>, std::string>
+parse_local_datetime(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_local_date_time::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+ const auto date = parse_local_date(inner_loc);
+ if(!date || inner_loc.iter() == inner_loc.end())
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_datetime: invalid datetime format",
+ {{source_location(inner_loc), "date, not datetime"}}),
+ source_location(inner_loc));
+ }
+ const char delim = *(inner_loc.iter());
+ if(delim != 'T' && delim != 't' && delim != ' ')
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_datetime: invalid datetime format",
+ {{source_location(inner_loc), "should be `T` or ` ` (space)"}}),
+ source_location(inner_loc));
+ }
+ inner_loc.advance();
+ const auto time = parse_local_time(inner_loc);
+ if(!time)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_local_datetime: invalid datetime format",
+ {{source_location(inner_loc), "invalid time format"}}),
+ source_location(inner_loc));
+ }
+ return ok(std::make_pair(
+ local_datetime(date.unwrap().first, time.unwrap().first),
+ token.unwrap()));
+ }
+ else
+ {
+ loc.reset(first);
+ return err(format_underline("toml::parse_local_datetime: ",
+ {{source_location(loc), "the next token is not a local_datetime"}}));
+ }
+}
+
+inline result<std::pair<offset_datetime, region>, std::string>
+parse_offset_datetime(location& loc)
+{
+ const auto first = loc.iter();
+ if(const auto token = lex_offset_date_time::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+ const auto datetime = parse_local_datetime(inner_loc);
+ if(!datetime || inner_loc.iter() == inner_loc.end())
+ {
+ throw internal_error(format_underline(
+ "toml::parse_offset_datetime: invalid datetime format",
+ {{source_location(inner_loc), "date, not datetime"}}),
+ source_location(inner_loc));
+ }
+ time_offset offset(0, 0);
+ if(const auto ofs = lex_time_numoffset::invoke(inner_loc))
+ {
+ const auto str = ofs.unwrap().str();
+
+ const auto hour = from_string<int>(str.substr(1,2), 0);
+ const auto minute = from_string<int>(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(hour, minute);
+ }
+ else
+ {
+ offset = time_offset(-hour, -minute);
+ }
+ }
+ else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z')
+ {
+ throw internal_error(format_underline(
+ "toml::parse_offset_datetime: invalid datetime format",
+ {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}),
+ source_location(inner_loc));
+ }
+ return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset),
+ token.unwrap()));
+ }
+ else
+ {
+ loc.reset(first);
+ return err(format_underline("toml::parse_offset_datetime: ",
+ {{source_location(loc), "the next token is not a offset_datetime"}}));
+ }
+}
+
+inline result<std::pair<key, region>, std::string>
+parse_simple_key(location& loc)
+{
+ if(const auto bstr = parse_basic_string(loc))
+ {
+ return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second));
+ }
+ if(const auto lstr = parse_literal_string(loc))
+ {
+ return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second));
+ }
+ if(const auto bare = lex_unquoted_key::invoke(loc))
+ {
+ const auto reg = bare.unwrap();
+ return ok(std::make_pair(reg.str(), reg));
+ }
+ return err(format_underline("toml::parse_simple_key: ",
+ {{source_location(loc), "the next token is not a simple key"}}));
+}
+
+// dotted key become vector of keys
+inline result<std::pair<std::vector<key>, region>, std::string>
+parse_key(location& loc)
+{
+ const auto first = loc.iter();
+ // dotted key -> `foo.bar.baz` where several single keys are chained by
+ // dots. Whitespaces between keys and dots are allowed.
+ if(const auto token = lex_dotted_key::invoke(loc))
+ {
+ const auto reg = token.unwrap();
+ location inner_loc(loc.name(), reg.str());
+ std::vector<key> keys;
+
+ while(inner_loc.iter() != inner_loc.end())
+ {
+ lex_ws::invoke(inner_loc);
+ if(const auto k = parse_simple_key(inner_loc))
+ {
+ keys.push_back(k.unwrap().first);
+ }
+ else
+ {
+ throw internal_error(format_underline(
+ "toml::detail::parse_key: dotted key contains invalid key",
+ {{source_location(inner_loc), k.unwrap_err()}}),
+ source_location(inner_loc));
+ }
+
+ lex_ws::invoke(inner_loc);
+ if(inner_loc.iter() == inner_loc.end())
+ {
+ break;
+ }
+ else if(*inner_loc.iter() == '.')
+ {
+ inner_loc.advance(); // to skip `.`
+ }
+ else
+ {
+ throw internal_error(format_underline("toml::parse_key: "
+ "dotted key contains invalid key ",
+ {{source_location(inner_loc), "should be `.`"}}),
+ source_location(inner_loc));
+ }
+ }
+ return ok(std::make_pair(keys, reg));
+ }
+ loc.reset(first);
+
+ // simple_key: a single (basic_string|literal_string|bare key)
+ if(const auto smpl = parse_simple_key(loc))
+ {
+ return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first),
+ smpl.unwrap().second));
+ }
+ 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'.",
+ "dotted keys: sequence of bare or quoted keys joined with a dot."
+ }));
+}
+
+// forward-decl to implement parse_array and parse_table
+template<typename Value>
+result<Value, std::string> parse_value(location&);
+
+template<typename Value>
+result<std::pair<typename Value::array_type, region>, std::string>
+parse_array(location& loc)
+{
+ using value_type = Value;
+ using array_type = typename value_type::array_type;
+
+ const auto first = loc.iter();
+ if(loc.iter() == loc.end())
+ {
+ return err("toml::parse_array: input is empty");
+ }
+ if(*loc.iter() != '[')
+ {
+ return err("toml::parse_array: token is not an array");
+ }
+ loc.advance();
+
+ using lex_ws_comment_newline = repeat<
+ either<lex_wschar, lex_newline, lex_comment>, unlimited>;
+
+ array_type retval;
+ while(loc.iter() != loc.end())
+ {
+ lex_ws_comment_newline::invoke(loc); // skip
+
+ if(loc.iter() != loc.end() && *loc.iter() == ']')
+ {
+ loc.advance(); // skip ']'
+ return ok(std::make_pair(retval,
+ region(loc, first, loc.iter())));
+ }
+
+ if(auto val = parse_value<value_type>(loc))
+ {
+ // 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.
+ //
+ // But some of the test-suite checks if the parser accepts a hetero-
+ // geneous arrays, so we keep this for a while.
+#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS
+ if(!retval.empty() && retval.front().type() != val.as_ok().type())
+ {
+ auto array_start_loc = loc;
+ array_start_loc.reset(first);
+
+ throw syntax_error(format_underline("toml::parse_array: "
+ "type of elements should be the same each other.", {
+ {source_location(array_start_loc), "array starts here"},
+ {
+ retval.front().location(),
+ "value has type " + stringize(retval.front().type())
+ },
+ {
+ val.unwrap().location(),
+ "value has different type, " + stringize(val.unwrap().type())
+ }
+ }), source_location(loc));
+ }
+#endif
+ retval.push_back(std::move(val.unwrap()));
+ }
+ else
+ {
+ auto array_start_loc = loc;
+ array_start_loc.reset(first);
+
+ throw syntax_error(format_underline("toml::parse_array: "
+ "value having invalid format appeared in an array", {
+ {source_location(array_start_loc), "array starts here"},
+ {source_location(loc), "it is not a valid value."}
+ }), source_location(loc));
+ }
+
+ using lex_array_separator = sequence<maybe<lex_ws_comment_newline>, character<','>>;
+ const auto sp = lex_array_separator::invoke(loc);
+ if(!sp)
+ {
+ lex_ws_comment_newline::invoke(loc);
+ if(loc.iter() != loc.end() && *loc.iter() == ']')
+ {
+ loc.advance(); // skip ']'
+ return ok(std::make_pair(retval,
+ region(loc, first, loc.iter())));
+ }
+ else
+ {
+ auto array_start_loc = loc;
+ array_start_loc.reset(first);
+
+ throw syntax_error(format_underline("toml::parse_array:"
+ " missing array separator `,` after a value", {
+ {source_location(array_start_loc), "array starts here"},
+ {source_location(loc), "should be `,`"}
+ }), source_location(loc));
+ }
+ }
+ }
+ loc.reset(first);
+ throw syntax_error(format_underline("toml::parse_array: "
+ "array did not closed by `]`",
+ {{source_location(loc), "should be closed"}}),
+ source_location(loc));
+}
+
+template<typename Value>
+result<std::pair<std::pair<std::vector<key>, region>, Value>, std::string>
+parse_key_value_pair(location& loc)
+{
+ using value_type = Value;
+
+ const auto first = loc.iter();
+ auto key_reg = parse_key(loc);
+ if(!key_reg)
+ {
+ std::string msg = std::move(key_reg.unwrap_err());
+ // if the next token is keyvalue-separator, it means that there are no
+ // key. then we need to show error as "empty key is not allowed".
+ if(const auto keyval_sep = lex_keyval_sep::invoke(loc))
+ {
+ loc.reset(first);
+ msg = format_underline("toml::parse_key_value_pair: "
+ "empty key is not allowed.",
+ {{source_location(loc), "key expected before '='"}});
+ }
+ return err(std::move(msg));
+ }
+
+ const auto kvsp = lex_keyval_sep::invoke(loc);
+ if(!kvsp)
+ {
+ std::string msg;
+ // if the line contains '=' after the invalid sequence, possibly the
+ // error is in the key (like, invalid character in bare key).
+ const auto line_end = std::find(loc.iter(), loc.end(), '\n');
+ if(std::find(loc.iter(), line_end, '=') != line_end)
+ {
+ msg = format_underline("toml::parse_key_value_pair: "
+ "invalid format for key",
+ {{source_location(loc), "invalid character in key"}},
+ {"Did you forget '.' to separate dotted-key?",
+ "Allowed characters for bare key are [0-9a-zA-Z_-]."});
+ }
+ else // if not, the error is lack of key-value separator.
+ {
+ msg = format_underline("toml::parse_key_value_pair: "
+ "missing key-value separator `=`",
+ {{source_location(loc), "should be `=`"}});
+ }
+ loc.reset(first);
+ return err(std::move(msg));
+ }
+
+ const auto after_kvsp = loc.iter(); // err msg
+ auto val = parse_value<value_type>(loc);
+ if(!val)
+ {
+ std::string msg;
+ loc.reset(after_kvsp);
+ // check there is something not a comment/whitespace after `=`
+ if(sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>::invoke(loc))
+ {
+ loc.reset(after_kvsp);
+ msg = format_underline("toml::parse_key_value_pair: "
+ "missing value after key-value separator '='",
+ {{source_location(loc), "expected value, but got nothing"}});
+ }
+ else // there is something not a comment/whitespace, so invalid format.
+ {
+ msg = std::move(val.unwrap_err());
+ }
+ loc.reset(first);
+ return err(msg);
+ }
+ return ok(std::make_pair(std::move(key_reg.unwrap()),
+ std::move(val.unwrap())));
+}
+
+// for error messages.
+template<typename InputIterator>
+std::string format_dotted_keys(InputIterator first, const InputIterator last)
+{
+ static_assert(std::is_same<key,
+ typename std::iterator_traits<InputIterator>::value_type>::value,"");
+
+ std::string retval(*first++);
+ for(; first != last; ++first)
+ {
+ retval += '.';
+ retval += *first;
+ }
+ return retval;
+}
+
+// forward decl for is_valid_forward_table_definition
+result<std::pair<std::vector<key>, region>, std::string>
+parse_table_key(location& loc);
+template<typename Value>
+result<std::pair<typename Value::table_type, region>, std::string>
+parse_inline_table(location& loc);
+
+// The following toml file is allowed.
+// ```toml
+// [a.b.c] # here, table `a` has element `b`.
+// foo = "bar"
+// [a] # merge a = {baz = "qux"} to a = {b = {...}}
+// baz = "qux"
+// ```
+// But the following is not allowed.
+// ```toml
+// [a]
+// b.c.foo = "bar"
+// [a] # error! the same table [a] defined!
+// baz = "qux"
+// ```
+// The following is neither allowed.
+// ```toml
+// a = { b.c.foo = "bar"}
+// [a] # error! the same table [a] defined!
+// baz = "qux"
+// ```
+// Here, it parses region of `tab->at(k)` as a table key and check the depth
+// 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<typename Value, typename Iterator>
+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<Value>(inserting_def))
+ {
+ // 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;
+ }
+
+ // ------------------------------------------------------------------------
+ // 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)) // [table.key]
+ {
+ // table keys always contains all the nodes from the root.
+ const auto& tks = tabkeys.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))
+ {
+ // consider the following case.
+ // [a]
+ // b.c = {d = 42}
+ // [a.b.c]
+ // e = 2.71
+ // this defines the table [a.b.c] twice. no?
+
+ // a dotted key starts from the node representing a table in which the
+ // dotted key belongs to.
+ const auto& dks = dotkeys.unwrap().first;
+ if(std::size_t(std::distance(key_curr, key_last)) == dks.size() &&
+ std::equal(dks.begin(), dks.end(), key_curr))
+ {
+ // the keys are equivalent. it is not allowed.
+ return false;
+ }
+ // the keys are not equivalent. it is allowed.
+ return true;
+ }
+ return false;
+}
+
+template<typename Value, typename InputIterator>
+result<bool, std::string>
+insert_nested_key(typename Value::table_type& root, const Value& v,
+ InputIterator iter, const InputIterator last,
+ region key_reg,
+ const bool is_array_of_table = false)
+{
+ static_assert(std::is_same<key,
+ typename std::iterator_traits<InputIterator>::value_type>::value,"");
+
+ using value_type = Value;
+ using table_type = typename value_type::table_type;
+ using array_type = typename value_type::array_type;
+
+ const auto first = iter;
+ assert(iter != last);
+
+ table_type* tab = std::addressof(root);
+ for(; iter != last; ++iter) // search recursively
+ {
+ const key& k = *iter;
+ if(std::next(iter) == last) // k is the last key
+ {
+ // XXX if the value is array-of-tables, there can be several
+ // tables that are in the same array. in that case, we need to
+ // find the last element and insert it to there.
+ if(is_array_of_table)
+ {
+ if(tab->count(k) == 1) // there is already an array of table
+ {
+ if(tab->at(k).is_table())
+ {
+ // show special err msg for conflicting table
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: array of table (\"",
+ format_dotted_keys(first, last),
+ "\") cannot be defined"), {
+ {tab->at(k).location(), "table already defined"},
+ {v.location(), "this conflicts with the previous table"}
+ }), v.location());
+ }
+ else if(!(tab->at(k).is_array()))
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: array of table (\"",
+ format_dotted_keys(first, last), "\") collides with"
+ " existing value"), {
+ {tab->at(k).location(),
+ concat_to_string("this ", tab->at(k).type(),
+ " value already exists")},
+ {v.location(),
+ "while inserting this array-of-tables"}
+ }), v.location());
+ }
+ // the above if-else-if checks tab->at(k) is an array
+ auto& a = tab->at(k).as_array();
+ // 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 (\"",
+ format_dotted_keys(first, last), "\") collides with"
+ " existing value"), {
+ {tab->at(k).location(),
+ concat_to_string("this ", tab->at(k).type(),
+ " value already exists")},
+ {v.location(),
+ "while inserting this array-of-tables"}
+ }), v.location());
+ }
+ // avoid conflicting array of table like the following.
+ // ```toml
+ // a = [{b = 42}] # define a as an array of *inline* tables
+ // [[a]] # a is an array of *multi-line* tables
+ // b = 54
+ // ```
+ // Here, from the type information, these cannot be detected
+ // 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.
+ // In this library, multi-line table value has a region
+ // that points to the key of the table (e.g. [[a]]). By
+ // comparing the first two letters in key, we can detect
+ // the array-of-table is inline or multiline.
+ if(const auto ptr = detail::get_region(a.front()))
+ {
+ if(ptr->str().substr(0,2) != "[[")
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: array of table (\"",
+ format_dotted_keys(first, last), "\") collides "
+ "with existing array-of-tables"), {
+ {tab->at(k).location(),
+ concat_to_string("this ", tab->at(k).type(),
+ " value has static size")},
+ {v.location(),
+ "appending it to the statically sized array"}
+ }), v.location());
+ }
+ }
+ a.push_back(v);
+ return ok(true);
+ }
+ else // if not, we need to create the array of table
+ {
+ // 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<std::string> 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);
+ }
+ } // end if(array of table)
+
+ if(tab->count(k) == 1)
+ {
+ if(tab->at(k).is_table() && v.is_table())
+ {
+ if(!is_valid_forward_table_definition(
+ tab->at(k), v, first, iter, last))
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: table (\"",
+ format_dotted_keys(first, last),
+ "\") already exists."), {
+ {tab->at(k).location(), "table already exists here"},
+ {v.location(), "table defined twice"}
+ }), v.location());
+ }
+ // to allow the following toml file.
+ // [a.b.c]
+ // d = 42
+ // [a]
+ // e = 2.71
+ 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);
+ return ok(true);
+ }
+ else if(v.is_table() &&
+ tab->at(k).is_array() &&
+ tab->at(k).as_array().size() > 0 &&
+ tab->at(k).as_array().front().is_table())
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: array of tables (\"",
+ format_dotted_keys(first, last), "\") already exists."), {
+ {tab->at(k).location(), "array of tables defined here"},
+ {v.location(), "table conflicts with the previous array of table"}
+ }), v.location());
+ }
+ else
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: value (\"",
+ format_dotted_keys(first, last), "\") already exists."), {
+ {tab->at(k).location(), "value already exists here"},
+ {v.location(), "value defined twice"}
+ }), v.location());
+ }
+ }
+ tab->insert(std::make_pair(k, v));
+ return ok(true);
+ }
+ else // k is not the last one, we should insert recursively
+ {
+ // if there is no corresponding value, insert it first.
+ // related: you don't need to write
+ // # [x]
+ // # [x.y]
+ // to write
+ // [x.y.z]
+ if(tab->count(k) == 0)
+ {
+ // a table that is defined implicitly doesn't have any comments.
+ (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/});
+ }
+
+ // type checking...
+ if(tab->at(k).is_table())
+ {
+ // According to toml-lang/toml:36d3091b3 "Clarify that inline
+ // tables are immutable", check if it adds key-value pair to an
+ // inline table.
+ if(const auto* ptr = get_region(tab->at(k)))
+ {
+ // here, if the value is a (multi-line) table, the region
+ // should be something like `[table-name]`.
+ if(ptr->front() == '{')
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: inserting to an inline table (",
+ format_dotted_keys(first, std::next(iter)),
+ ") but inline tables are immutable"), {
+ {tab->at(k).location(), "inline tables are immutable"},
+ {v.location(), "inserting this"}
+ }), v.location());
+ }
+ }
+ tab = std::addressof((*tab)[k].as_table());
+ }
+ else if(tab->at(k).is_array()) // inserting to array-of-tables?
+ {
+ auto& a = (*tab)[k].as_array();
+ if(!a.back().is_table())
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: target (",
+ format_dotted_keys(first, std::next(iter)),
+ ") is neither table nor an array of tables"), {
+ {a.back().location(), concat_to_string(
+ "actual type is ", a.back().type())},
+ {v.location(), "inserting this"}
+ }), v.location());
+ }
+ tab = std::addressof(a.back().as_table());
+ }
+ else
+ {
+ throw syntax_error(format_underline(concat_to_string(
+ "toml::insert_value: target (",
+ format_dotted_keys(first, std::next(iter)),
+ ") is neither table nor an array of tables"), {
+ {tab->at(k).location(), concat_to_string(
+ "actual type is ", tab->at(k).type())},
+ {v.location(), "inserting this"}
+ }), v.location());
+ }
+ }
+ }
+ return err(std::string("toml::detail::insert_nested_key: never reach here"));
+}
+
+template<typename Value>
+result<std::pair<typename Value::table_type, region>, std::string>
+parse_inline_table(location& loc)
+{
+ using value_type = Value;
+ using table_type = typename value_type::table_type;
+
+ const auto first = loc.iter();
+ table_type retval;
+ if(!(loc.iter() != loc.end() && *loc.iter() == '{'))
+ {
+ return err(format_underline("toml::parse_inline_table: ",
+ {{source_location(loc), "the next token is not an inline table"}}));
+ }
+ loc.advance();
+
+ // check if the inline table is an empty table = { }
+ maybe<lex_ws>::invoke(loc);
+ 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())
+ {
+ const auto kv_r = parse_key_value_pair<value_type>(loc);
+ if(!kv_r)
+ {
+ return err(kv_r.unwrap_err());
+ }
+
+ const auto& kvpair = kv_r.unwrap();
+ const std::vector<key>& keys = kvpair.first.first;
+ const auto& key_reg = kvpair.first.second;
+ const value_type& val = kvpair.second;
+
+ const auto inserted =
+ insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg);
+ if(!inserted)
+ {
+ throw internal_error("toml::parse_inline_table: "
+ "failed to insert value into table: " + inserted.unwrap_err(),
+ source_location(loc));
+ }
+
+ using lex_table_separator = sequence<maybe<lex_ws>, character<','>>;
+ const auto sp = lex_table_separator::invoke(loc);
+
+ if(!sp)
+ {
+ maybe<lex_ws>::invoke(loc);
+
+ 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(
+ retval, region(loc, first, loc.iter())));
+ }
+ else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n')
+ {
+ throw syntax_error(format_underline(
+ "toml::parse_inline_table: missing curly brace `}`",
+ {{source_location(loc), "should be `}`"}}),
+ source_location(loc));
+ }
+ else
+ {
+ throw syntax_error(format_underline(
+ "toml::parse_inline_table: missing table separator `,` ",
+ {{source_location(loc), "should be `,`"}}),
+ source_location(loc));
+ }
+ }
+ else // `,` is found
+ {
+ maybe<lex_ws>::invoke(loc);
+ if(loc.iter() != loc.end() && *loc.iter() == '}')
+ {
+ 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));
+ }
+ }
+ }
+ loc.reset(first);
+ throw syntax_error(format_underline("toml::parse_inline_table: "
+ "inline table did not closed by `}`",
+ {{source_location(loc), "should be closed"}}),
+ source_location(loc));
+}
+
+inline result<value_t, std::string> guess_number_type(const location& l)
+{
+ // This function tries to find some (common) mistakes by checking characters
+ // that follows the last character of a value. But it is often difficult
+ // because some non-newline characters can appear after a value. E.g.
+ // spaces, tabs, commas (in an array or inline table), closing brackets
+ // (of an array or inline table), comment-sign (#). Since this function
+ // does not parse further, those characters are always allowed to be there.
+ location loc = l;
+
+ if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);}
+ loc.reset(l.iter());
+
+ if(lex_local_date_time::invoke(loc))
+ {
+ // bad offset may appear after this.
+ if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-'
+ || *loc.iter() == 'Z' || *loc.iter() == 'z'))
+ {
+ return err(format_underline("bad offset: should be [+-]HH:MM or Z",
+ {{source_location(loc), "[+-]HH:MM or Z"}},
+ {"pass: +09:00, -05:30", "fail: +9:00, -5:30"}));
+ }
+ return ok(value_t::local_datetime);
+ }
+ loc.reset(l.iter());
+
+ if(lex_local_date::invoke(loc))
+ {
+ // bad time may appear after this.
+ // A space is allowed as a delimiter between local time. But there are
+ // both cases in which a space becomes valid or invalid.
+ // - invalid: 2019-06-16 7:00:00
+ // - valid : 2019-06-16 07:00:00
+ if(loc.iter() != loc.end())
+ {
+ const auto c = *loc.iter();
+ if(c == 'T' || c == 't')
+ {
+ return err(format_underline("bad time: should be HH:MM:SS.subsec",
+ {{source_location(loc), "HH:MM:SS.subsec"}},
+ {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999",
+ "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"}));
+ }
+ if('0' <= c && c <= '9')
+ {
+ return err(format_underline("bad time: missing T",
+ {{source_location(loc), "T or space required here"}},
+ {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999",
+ "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"}));
+ }
+ if(c == ' ' && std::next(loc.iter()) != loc.end() &&
+ ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9'))
+ {
+ loc.advance();
+ return err(format_underline("bad time: should be HH:MM:SS.subsec",
+ {{source_location(loc), "HH:MM:SS.subsec"}},
+ {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999",
+ "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"}));
+ }
+ }
+ return ok(value_t::local_date);
+ }
+ loc.reset(l.iter());
+
+ if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);}
+ loc.reset(l.iter());
+
+ if(lex_float::invoke(loc))
+ {
+ if(loc.iter() != loc.end() && *loc.iter() == '_')
+ {
+ return err(format_underline("bad float: `_` should be surrounded by digits",
+ {{source_location(loc), "here"}},
+ {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan",
+ "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"}));
+ }
+ return ok(value_t::floating);
+ }
+ loc.reset(l.iter());
+
+ if(lex_integer::invoke(loc))
+ {
+ if(loc.iter() != loc.end())
+ {
+ const auto c = *loc.iter();
+ if(c == '_')
+ {
+ return err(format_underline("bad integer: `_` should be surrounded by digits",
+ {{source_location(loc), "here"}},
+ {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755",
+ "fail: 1__000, 0123"}));
+ }
+ if('0' <= c && c <= '9')
+ {
+ // leading zero. point '0'
+ loc.retrace();
+ return err(format_underline("bad integer: leading zero",
+ {{source_location(loc), "here"}},
+ {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755",
+ "fail: 1__000, 0123"}));
+ }
+ if(c == ':' || c == '-')
+ {
+ return err(format_underline("bad datetime: invalid format",
+ {{source_location(loc), "here"}},
+ {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z",
+ "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"}));
+ }
+ if(c == '.' || c == 'e' || c == 'E')
+ {
+ return err(format_underline("bad float: invalid format",
+ {{source_location(loc), "here"}},
+ {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan",
+ "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"}));
+ }
+ }
+ return ok(value_t::integer);
+ }
+ if(loc.iter() != loc.end() && *loc.iter() == '.')
+ {
+ return err(format_underline("bad float: invalid format",
+ {{source_location(loc), "integer part required before this"}},
+ {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan",
+ "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"}));
+ }
+ if(loc.iter() != loc.end() && *loc.iter() == '_')
+ {
+ return err(format_underline("bad number: `_` should be surrounded by digits",
+ {{source_location(loc), "`_` is not surrounded by digits"}},
+ {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755",
+ "fail: 1__000, 0123"}));
+ }
+ return err(format_underline("bad format: unknown value appeared",
+ {{source_location(loc), "here"}}));
+}
+
+inline result<value_t, std::string> guess_value_type(const location& loc)
+{
+ switch(*loc.iter())
+ {
+ case '"' : {return ok(value_t::string); }
+ case '\'': {return ok(value_t::string); }
+ case 't' : {return ok(value_t::boolean); }
+ case 'f' : {return ok(value_t::boolean); }
+ case '[' : {return ok(value_t::array); }
+ case '{' : {return ok(value_t::table); }
+ case 'i' : {return ok(value_t::floating);} // inf.
+ case 'n' : {return ok(value_t::floating);} // nan.
+ default : {return guess_number_type(loc);}
+ }
+}
+
+template<typename Value, typename T>
+result<Value, std::string>
+parse_value_helper(result<std::pair<T, region>, std::string> rslt)
+{
+ 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<typename Value>
+result<Value, std::string> parse_value(location& loc)
+{
+ const auto first = loc.iter();
+ if(first == loc.end())
+ {
+ return err(format_underline("toml::parse_value: input is empty",
+ {{source_location(loc), ""}}));
+ }
+
+ const auto type = guess_value_type(loc);
+ if(!type)
+ {
+ return err(type.unwrap_err());
+ }
+
+ switch(type.unwrap())
+ {
+ case value_t::boolean : {return parse_value_helper<Value>(parse_boolean(loc) );}
+ case value_t::integer : {return parse_value_helper<Value>(parse_integer(loc) );}
+ case value_t::floating : {return parse_value_helper<Value>(parse_floating(loc) );}
+ case value_t::string : {return parse_value_helper<Value>(parse_string(loc) );}
+ case value_t::offset_datetime: {return parse_value_helper<Value>(parse_offset_datetime(loc) );}
+ case value_t::local_datetime : {return parse_value_helper<Value>(parse_local_datetime(loc) );}
+ case value_t::local_date : {return parse_value_helper<Value>(parse_local_date(loc) );}
+ case value_t::local_time : {return parse_value_helper<Value>(parse_local_time(loc) );}
+ case value_t::array : {return parse_value_helper<Value>(parse_array<Value>(loc) );}
+ case value_t::table : {return parse_value_helper<Value>(parse_inline_table<Value>(loc));}
+ default:
+ {
+ const auto msg = format_underline("toml::parse_value: "
+ "unknown token appeared", {{source_location(loc), "unknown"}});
+ loc.reset(first);
+ return err(msg);
+ }
+ }
+}
+
+inline result<std::pair<std::vector<key>, region>, std::string>
+parse_table_key(location& loc)
+{
+ if(auto token = lex_std_table::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+
+ const auto open = lex_std_table_open::invoke(inner_loc);
+ if(!open || inner_loc.iter() == inner_loc.end())
+ {
+ throw internal_error(format_underline(
+ "toml::parse_table_key: no `[`",
+ {{source_location(inner_loc), "should be `[`"}}),
+ source_location(inner_loc));
+ }
+ // to skip [ a . b . c ]
+ // ^----------- this whitespace
+ lex_ws::invoke(inner_loc);
+ const auto keys = parse_key(inner_loc);
+ if(!keys)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_table_key: invalid key",
+ {{source_location(inner_loc), "not key"}}),
+ source_location(inner_loc));
+ }
+ // to skip [ a . b . c ]
+ // ^-- this whitespace
+ lex_ws::invoke(inner_loc);
+ const auto close = lex_std_table_close::invoke(inner_loc);
+ if(!close)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_table_key: no `]`",
+ {{source_location(inner_loc), "should be `]`"}}),
+ source_location(inner_loc));
+ }
+
+ // after [table.key], newline or EOF(empty table) required.
+ if(loc.iter() != loc.end())
+ {
+ using lex_newline_after_table_key =
+ sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>;
+ const auto nl = lex_newline_after_table_key::invoke(loc);
+ if(!nl)
+ {
+ throw syntax_error(format_underline(
+ "toml::parse_table_key: newline required after [table.key]",
+ {{source_location(loc), "expected newline"}}),
+ source_location(loc));
+ }
+ }
+ return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
+ }
+ else
+ {
+ return err(format_underline("toml::parse_table_key: "
+ "not a valid table key", {{source_location(loc), "here"}}));
+ }
+}
+
+inline result<std::pair<std::vector<key>, region>, std::string>
+parse_array_table_key(location& loc)
+{
+ if(auto token = lex_array_table::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+
+ const auto open = lex_array_table_open::invoke(inner_loc);
+ if(!open || inner_loc.iter() == inner_loc.end())
+ {
+ throw internal_error(format_underline(
+ "toml::parse_array_table_key: no `[[`",
+ {{source_location(inner_loc), "should be `[[`"}}),
+ source_location(inner_loc));
+ }
+ lex_ws::invoke(inner_loc);
+ const auto keys = parse_key(inner_loc);
+ if(!keys)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_array_table_key: invalid key",
+ {{source_location(inner_loc), "not a key"}}),
+ source_location(inner_loc));
+ }
+ lex_ws::invoke(inner_loc);
+ const auto close = lex_array_table_close::invoke(inner_loc);
+ if(!close)
+ {
+ throw internal_error(format_underline(
+ "toml::parse_table_key: no `]]`",
+ {{source_location(inner_loc), "should be `]]`"}}),
+ source_location(inner_loc));
+ }
+
+ // after [[table.key]], newline or EOF(empty table) required.
+ if(loc.iter() != loc.end())
+ {
+ using lex_newline_after_table_key =
+ sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>;
+ const auto nl = lex_newline_after_table_key::invoke(loc);
+ if(!nl)
+ {
+ throw syntax_error(format_underline("toml::"
+ "parse_array_table_key: newline required after [[table.key]]",
+ {{source_location(loc), "expected newline"}}),
+ source_location(loc));
+ }
+ }
+ return ok(std::make_pair(keys.unwrap().first, token.unwrap()));
+ }
+ else
+ {
+ return err(format_underline("toml::parse_array_table_key: "
+ "not a valid table key", {{source_location(loc), "here"}}));
+ }
+}
+
+// parse table body (key-value pairs until the iter hits the next [tablekey])
+template<typename Value>
+result<typename Value::table_type, std::string>
+parse_ml_table(location& loc)
+{
+ using value_type = Value;
+ using table_type = typename value_type::table_type;
+
+ const auto first = loc.iter();
+ if(first == loc.end())
+ {
+ return ok(table_type{});
+ }
+
+ // XXX at lest one newline is needed.
+ using skip_line = repeat<
+ sequence<maybe<lex_ws>, maybe<lex_comment>, lex_newline>, at_least<1>>;
+ skip_line::invoke(loc);
+ lex_ws::invoke(loc);
+
+ table_type tab;
+ while(loc.iter() != loc.end())
+ {
+ lex_ws::invoke(loc);
+ const auto before = loc.iter();
+ if(const auto tmp = parse_array_table_key(loc)) // next table found
+ {
+ loc.reset(before);
+ return ok(tab);
+ }
+ if(const auto tmp = parse_table_key(loc)) // next table found
+ {
+ loc.reset(before);
+ return ok(tab);
+ }
+
+ if(const auto kv = parse_key_value_pair<value_type>(loc))
+ {
+ const auto& kvpair = kv.unwrap();
+ const std::vector<key>& keys = kvpair.first.first;
+ const auto& key_reg = kvpair.first.second;
+ const value_type& val = kvpair.second;
+ const auto inserted =
+ insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg);
+ if(!inserted)
+ {
+ return err(inserted.unwrap_err());
+ }
+ }
+ else
+ {
+ return err(kv.unwrap_err());
+ }
+
+ // comment lines are skipped by the above function call.
+ // However, since the `skip_line` requires at least 1 newline, it fails
+ // if the file ends with ws and/or comment without newline.
+ // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment`
+ // itself. To skip the last ws and/or comment, call lexers.
+ // It does not matter if these fails, so the return value is discarded.
+ lex_ws::invoke(loc);
+ lex_comment::invoke(loc);
+
+ // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines
+ // and comments after the last key-value pairs are allowed.
+ const auto newline = skip_line::invoke(loc);
+ if(!newline && loc.iter() != loc.end())
+ {
+ const auto before2 = loc.iter();
+ lex_ws::invoke(loc); // skip whitespace
+ const auto msg = format_underline("toml::parse_table: "
+ "invalid line format", {{source_location(loc), concat_to_string(
+ "expected newline, but got '", show_char(*loc.iter()), "'.")}});
+ loc.reset(before2);
+ return err(msg);
+ }
+
+ // the skip_lines only matches with lines that includes newline.
+ // to skip the last line that includes comment and/or whitespace
+ // but no newline, call them one more time.
+ lex_ws::invoke(loc);
+ lex_comment::invoke(loc);
+ }
+ return ok(tab);
+}
+
+template<typename Value>
+result<Value, std::string> parse_toml_file(location& loc)
+{
+ using value_type = Value;
+ using table_type = typename value_type::table_type;
+
+ const auto first = loc.iter();
+ if(first == loc.end())
+ {
+ // 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
+ // Here first != loc.end(), so taking std::next is okay
+ const region file(loc, first, std::next(loc.iter()));
+
+ // The first successive comments that are separated from the first value
+ // by an empty line are for a file itself.
+ // ```toml
+ // # this is a comment for a file.
+ //
+ // key = "the first value"
+ // ```
+ // ```toml
+ // # this is a comment for "the first value".
+ // key = "the first value"
+ // ```
+ std::vector<std::string> comments;
+ using lex_first_comments = sequence<
+ repeat<sequence<maybe<lex_ws>, lex_comment, lex_newline>, at_least<1>>,
+ sequence<maybe<lex_ws>, lex_newline>
+ >;
+ if(const auto token = lex_first_comments::invoke(loc))
+ {
+ location inner_loc(loc.name(), token.unwrap().str());
+ while(inner_loc.iter() != inner_loc.end())
+ {
+ maybe<lex_ws>::invoke(inner_loc); // remove ws if exists
+ if(lex_newline::invoke(inner_loc))
+ {
+ assert(inner_loc.iter() == inner_loc.end());
+ break; // empty line found.
+ }
+ auto com = lex_comment::invoke(inner_loc).unwrap().str();
+ com.erase(com.begin()); // remove # sign
+ comments.push_back(std::move(com));
+ lex_newline::invoke(inner_loc);
+ }
+ }
+
+ table_type data;
+ // root object is also a table, but without [tablename]
+ if(const auto tab = parse_ml_table<value_type>(loc))
+ {
+ data = std::move(tab.unwrap());
+ }
+ else // failed (empty table is regarded as success in parse_ml_table)
+ {
+ return err(tab.unwrap_err());
+ }
+ while(loc.iter() != loc.end())
+ {
+ // here, the region of [table] is regarded as the table-key because
+ // the table body is normally too big and it is not so informative
+ // if the first key-value pair of the table is shown in the error
+ // message.
+ if(const auto tabkey = parse_array_table_key(loc))
+ {
+ const auto tab = parse_ml_table<value_type>(loc);
+ if(!tab){return err(tab.unwrap_err());}
+
+ const auto& tk = tabkey.unwrap();
+ const auto& keys = tk.first;
+ const auto& reg = tk.second;
+
+ const auto inserted = insert_nested_key(data,
+ value_type(tab.unwrap(), reg, reg.comments()),
+ keys.begin(), keys.end(), reg,
+ /*is_array_of_table=*/ true);
+ if(!inserted) {return err(inserted.unwrap_err());}
+
+ continue;
+ }
+ if(const auto tabkey = parse_table_key(loc))
+ {
+ const auto tab = parse_ml_table<value_type>(loc);
+ if(!tab){return err(tab.unwrap_err());}
+
+ const auto& tk = tabkey.unwrap();
+ const auto& keys = tk.first;
+ const auto& reg = tk.second;
+
+ const auto inserted = insert_nested_key(data,
+ value_type(tab.unwrap(), reg, reg.comments()),
+ keys.begin(), keys.end(), reg);
+ if(!inserted) {return err(inserted.unwrap_err());}
+
+ continue;
+ }
+ return err(format_underline("toml::parse_toml_file: "
+ "unknown line appeared", {{source_location(loc), "unknown format"}}));
+ }
+
+ return ok(Value(std::move(data), file, comments));
+}
+
+} // detail
+
+template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
+ template<typename ...> class Table = std::unordered_map,
+ template<typename ...> class Array = std::vector>
+basic_value<Comment, Table, Array>
+parse(std::istream& is, const std::string& fname = "unknown file")
+{
+ using value_type = basic_value<Comment, Table, Array>;
+
+ 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<char> letters(static_cast<std::size_t>(fsize));
+ is.read(letters.data(), fsize);
+
+ // 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.push_back('\n');
+ }
+
+ detail::location loc(std::move(fname), std::move(letters));
+
+ // skip BOM if exists.
+ // XXX component of BOM (like 0xEF) exceeds the representable range of
+ // signed char, so on some (actually, most) of the environment, these cannot
+ // be compared to char. However, since we are always out of luck, we need to
+ // check our chars are equivalent to BOM. To do this, first we need to
+ // convert char to unsigned char to guarantee the comparability.
+ if(loc.source()->size() >= 3)
+ {
+ std::array<unsigned char, 3> BOM;
+ std::memcpy(BOM.data(), loc.source()->data(), 3);
+ if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF)
+ {
+ loc.advance(3); // BOM found. skip.
+ }
+ }
+
+ const auto data = detail::parse_toml_file<value_type>(loc);
+ if(!data)
+ {
+ throw syntax_error(data.unwrap_err(), source_location(loc));
+ }
+ return data.unwrap();
+}
+
+template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
+ template<typename ...> class Table = std::unordered_map,
+ template<typename ...> class Array = std::vector>
+basic_value<Comment, Table, Array> parse(const std::string& fname)
+{
+ std::ifstream ifs(fname.c_str(), std::ios_base::binary);
+ if(!ifs.good())
+ {
+ throw std::runtime_error("toml::parse: file open error -> " + fname);
+ }
+ return parse<Comment, Table, Array>(ifs, fname);
+}
+
+#ifdef TOML11_HAS_STD_FILESYSTEM
+// This function just forwards `parse("filename.toml")` to std::string version
+// to avoid the ambiguity in overload resolution.
+//
+// Both std::string and std::filesystem::path are convertible from const char*.
+// 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 invocation with c-string.
+// So this function is preferred than others and the ambiguity disappears.
+template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
+ template<typename ...> class Table = std::unordered_map,
+ template<typename ...> class Array = std::vector>
+basic_value<Comment, Table, Array> parse(const char* fname)
+{
+ return parse<Comment, Table, Array>(std::string(fname));
+}
+
+template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
+ template<typename ...> class Table = std::unordered_map,
+ template<typename ...> class Array = std::vector>
+basic_value<Comment, Table, Array> 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());
+ }
+ return parse<Comment, Table, Array>(ifs, fpath.string());
+}
+#endif // TOML11_HAS_STD_FILESYSTEM
+
+} // toml
+#endif// TOML11_PARSER_HPP
diff --git a/src/toml11/toml/region.hpp b/src/toml11/toml/region.hpp
new file mode 100644
index 000000000..2e01e51d0
--- /dev/null
+++ b/src/toml11/toml/region.hpp
@@ -0,0 +1,417 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_REGION_HPP
+#define TOML11_REGION_HPP
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <iomanip>
+#include <cassert>
+#include "color.hpp"
+
+namespace toml
+{
+namespace detail
+{
+
+// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
+template<typename Iterator>
+std::string make_string(Iterator first, Iterator last)
+{
+ if(first == last) {return "";}
+ return std::string(first, last);
+}
+inline std::string make_string(std::size_t len, char c)
+{
+ if(len == 0) {return "";}
+ return std::string(len, c);
+}
+
+// region_base is a base class of location and region that are defined below.
+// it will be used to generate better error messages.
+struct region_base
+{
+ region_base() = default;
+ virtual ~region_base() = default;
+ region_base(const region_base&) = default;
+ region_base(region_base&& ) = default;
+ region_base& operator=(const region_base&) = default;
+ region_base& operator=(region_base&& ) = default;
+
+ virtual bool is_ok() const noexcept {return false;}
+ virtual char front() const noexcept {return '\0';}
+
+ virtual std::string str() const {return std::string("unknown region");}
+ virtual std::string name() const {return std::string("unknown file");}
+ virtual std::string line() const {return std::string("unknown line");}
+ virtual std::string line_num() const {return std::string("?");}
+
+ // length of the region
+ virtual std::size_t size() const noexcept {return 0;}
+ // number of characters in the line before the region
+ virtual std::size_t before() const noexcept {return 0;}
+ // number of characters in the line after the region
+ virtual std::size_t after() const noexcept {return 0;}
+
+ virtual std::vector<std::string> comments() const {return {};}
+ // ```toml
+ // # comment_before
+ // key = "value" # comment_inline
+ // ```
+};
+
+// location represents a position in a container, which contains a file content.
+// it can be considered as a region that contains only one character.
+//
+// it contains pointer to the file content and iterator that points the current
+// location.
+struct location final : public region_base
+{
+ using const_iterator = typename std::vector<char>::const_iterator;
+ using difference_type = typename const_iterator::difference_type;
+ using source_ptr = std::shared_ptr<const std::vector<char>>;
+
+ location(std::string source_name, std::vector<char> cont)
+ : source_(std::make_shared<std::vector<char>>(std::move(cont))),
+ line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
+ {}
+ location(std::string source_name, const std::string& cont)
+ : source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
+ line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
+ {}
+
+ location(const location&) = default;
+ location(location&&) = default;
+ location& operator=(const location&) = default;
+ location& operator=(location&&) = default;
+ ~location() = default;
+
+ bool is_ok() const noexcept override {return static_cast<bool>(source_);}
+ char front() const noexcept override {return *iter_;}
+
+ // this const prohibits codes like `++(loc.iter())`.
+ const const_iterator iter() const noexcept {return iter_;}
+
+ const_iterator begin() const noexcept {return source_->cbegin();}
+ const_iterator end() const noexcept {return source_->cend();}
+
+ // XXX `location::line_num()` used to be implemented using `std::count` to
+ // count a number of '\n'. But with a long toml file (typically, 10k lines),
+ // it becomes intolerably slow because each time it generates error messages,
+ // it counts '\n' from thousands of characters. To workaround it, I decided
+ // to introduce `location::line_number_` member variable and synchronize it
+ // to the location changes the point to look. So an overload of `iter()`
+ // which returns mutable reference is removed and `advance()`, `retrace()`
+ // and `reset()` is added.
+ void advance(difference_type n = 1) noexcept
+ {
+ this->line_number_ += static_cast<std::size_t>(
+ std::count(this->iter_, std::next(this->iter_, n), '\n'));
+ this->iter_ += n;
+ return;
+ }
+ void retrace(difference_type n = 1) noexcept
+ {
+ this->line_number_ -= static_cast<std::size_t>(
+ std::count(std::prev(this->iter_, n), this->iter_, '\n'));
+ this->iter_ -= n;
+ return;
+ }
+ void reset(const_iterator rollback) noexcept
+ {
+ // since c++11, std::distance works in both ways for random-access
+ // iterators and returns a negative value if `first > last`.
+ if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
+ {
+ this->line_number_ -= static_cast<std::size_t>(
+ std::count(rollback, this->iter_, '\n'));
+ }
+ else // iter < rollback [[unlikely]]
+ {
+ this->line_number_ += static_cast<std::size_t>(
+ std::count(this->iter_, rollback, '\n'));
+ }
+ this->iter_ = rollback;
+ return;
+ }
+
+ std::string str() const override {return make_string(1, *this->iter());}
+ std::string name() const override {return source_name_;}
+
+ std::string line_num() const override
+ {
+ return std::to_string(this->line_number_);
+ }
+
+ std::string line() const override
+ {
+ return make_string(this->line_begin(), this->line_end());
+ }
+
+ const_iterator line_begin() const noexcept
+ {
+ using reverse_iterator = std::reverse_iterator<const_iterator>;
+ return std::find(reverse_iterator(this->iter()),
+ reverse_iterator(this->begin()), '\n').base();
+ }
+ const_iterator line_end() const noexcept
+ {
+ return std::find(this->iter(), this->end(), '\n');
+ }
+
+ // location is always points a character. so the size is 1.
+ std::size_t size() const noexcept override
+ {
+ return 1u;
+ }
+ std::size_t before() const noexcept override
+ {
+ const auto sz = std::distance(this->line_begin(), this->iter());
+ assert(sz >= 0);
+ return static_cast<std::size_t>(sz);
+ }
+ std::size_t after() const noexcept override
+ {
+ const auto sz = std::distance(this->iter(), this->line_end());
+ assert(sz >= 0);
+ return static_cast<std::size_t>(sz);
+ }
+
+ source_ptr const& source() const& noexcept {return source_;}
+ source_ptr&& source() && noexcept {return std::move(source_);}
+
+ private:
+
+ source_ptr source_;
+ std::size_t line_number_;
+ std::string source_name_;
+ const_iterator iter_;
+};
+
+// region represents a range in a container, which contains a file content.
+//
+// it contains pointer to the file content and iterator that points the first
+// and last location.
+struct region final : public region_base
+{
+ using const_iterator = typename std::vector<char>::const_iterator;
+ using source_ptr = std::shared_ptr<const std::vector<char>>;
+
+ // delete default constructor. source_ never be null.
+ region() = delete;
+
+ explicit region(const location& loc)
+ : source_(loc.source()), source_name_(loc.name()),
+ first_(loc.iter()), last_(loc.iter())
+ {}
+ explicit region(location&& loc)
+ : source_(loc.source()), source_name_(loc.name()),
+ first_(loc.iter()), last_(loc.iter())
+ {}
+
+ region(const location& loc, const_iterator f, const_iterator l)
+ : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
+ {}
+ region(location&& loc, const_iterator f, const_iterator l)
+ : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
+ {}
+
+ region(const region&) = default;
+ region(region&&) = default;
+ region& operator=(const region&) = default;
+ region& operator=(region&&) = default;
+ ~region() = default;
+
+ region& operator+=(const region& other)
+ {
+ // different regions cannot be concatenated
+ assert(this->begin() == other.begin() && this->end() == other.end() &&
+ this->last_ == other.first_);
+
+ this->last_ = other.last_;
+ return *this;
+ }
+
+ bool is_ok() const noexcept override {return static_cast<bool>(source_);}
+ char front() const noexcept override {return *first_;}
+
+ std::string str() const override {return make_string(first_, last_);}
+ std::string line() const override
+ {
+ if(this->contain_newline())
+ {
+ return make_string(this->line_begin(),
+ std::find(this->line_begin(), this->last(), '\n'));
+ }
+ return make_string(this->line_begin(), this->line_end());
+ }
+ std::string line_num() const override
+ {
+ return std::to_string(1 + std::count(this->begin(), this->first(), '\n'));
+ }
+
+ std::size_t size() const noexcept override
+ {
+ const auto sz = std::distance(first_, last_);
+ assert(sz >= 0);
+ return static_cast<std::size_t>(sz);
+ }
+ std::size_t before() const noexcept override
+ {
+ const auto sz = std::distance(this->line_begin(), this->first());
+ assert(sz >= 0);
+ return static_cast<std::size_t>(sz);
+ }
+ std::size_t after() const noexcept override
+ {
+ const auto sz = std::distance(this->last(), this->line_end());
+ assert(sz >= 0);
+ return static_cast<std::size_t>(sz);
+ }
+
+ bool contain_newline() const noexcept
+ {
+ return std::find(this->first(), this->last(), '\n') != this->last();
+ }
+
+ const_iterator line_begin() const noexcept
+ {
+ using reverse_iterator = std::reverse_iterator<const_iterator>;
+ return std::find(reverse_iterator(this->first()),
+ reverse_iterator(this->begin()), '\n').base();
+ }
+ const_iterator line_end() const noexcept
+ {
+ return std::find(this->last(), this->end(), '\n');
+ }
+
+ const_iterator begin() const noexcept {return source_->cbegin();}
+ const_iterator end() const noexcept {return source_->cend();}
+ const_iterator first() const noexcept {return first_;}
+ const_iterator last() const noexcept {return last_;}
+
+ source_ptr const& source() const& noexcept {return source_;}
+ source_ptr&& source() && noexcept {return std::move(source_);}
+
+ std::string name() const override {return source_name_;}
+
+ std::vector<std::string> comments() const override
+ {
+ // assuming the current region (`*this`) points a value.
+ // ```toml
+ // a = "value"
+ // ^^^^^^^- this region
+ // ```
+ using rev_iter = std::reverse_iterator<const_iterator>;
+
+ std::vector<std::string> com{};
+ {
+ // find comments just before the current region.
+ // ```toml
+ // # this should be collected.
+ // # this also.
+ // a = value # not this.
+ // ```
+
+ // # this is a comment for `a`, not array elements.
+ // a = [1, 2, 3, 4, 5]
+ if(this->first() == std::find_if(this->line_begin(), this->first(),
+ [](const char c) noexcept -> bool {return c == '[' || c == '{';}))
+ {
+ auto iter = this->line_begin(); // points the first character
+ while(iter != this->begin())
+ {
+ iter = std::prev(iter);
+
+ // range [line_start, iter) represents the previous line
+ const auto line_start = std::find(
+ rev_iter(iter), rev_iter(this->begin()), '\n').base();
+ const auto comment_found = std::find(line_start, iter, '#');
+ if(comment_found == iter)
+ {
+ break; // comment not found.
+ }
+
+ // exclude the following case.
+ // > a = "foo" # comment // <-- this is not a comment for b but a.
+ // > b = "current value"
+ if(std::all_of(line_start, comment_found,
+ [](const char c) noexcept -> bool {
+ return c == ' ' || c == '\t';
+ }))
+ {
+ // unwrap the first '#' by std::next.
+ 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
+ {
+ break;
+ }
+ iter = line_start;
+ }
+ }
+ }
+
+ if(com.size() > 1)
+ {
+ std::reverse(com.begin(), com.end());
+ }
+
+ {
+ // find comments just after the current region.
+ // ```toml
+ // # not this.
+ // a = value # this one.
+ // a = [ # not this (technically difficult)
+ //
+ // ] # and this.
+ // ```
+ // The reason why it's difficult is that it requires parsing in the
+ // following case.
+ // ```toml
+ // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
+ // # ...
+ // ] # this is apparently a comment for a.
+ //
+ // b = [
+ // 3.14 ] # there is no way to add a comment to `3.14` currently.
+ //
+ // c = [
+ // 3.14 # do this if you need a comment here.
+ // ]
+ // ```
+ const auto comment_found =
+ std::find(this->last(), this->line_end(), '#');
+ if(comment_found != this->line_end()) // '#' found
+ {
+ // table = {key = "value"} # what is this for?
+ // the above comment is not for "value", but {key="value"}.
+ if(comment_found == std::find_if(this->last(), comment_found,
+ [](const char c) noexcept -> bool {
+ return !(c == ' ' || c == '\t' || c == ',');
+ }))
+ {
+ // unwrap the first '#' by std::next.
+ 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));
+ }
+ }
+ }
+ return com;
+ }
+
+ private:
+
+ source_ptr source_;
+ std::string source_name_;
+ const_iterator first_, last_;
+};
+
+} // detail
+} // toml
+#endif// TOML11_REGION_H
diff --git a/src/toml11/toml/result.hpp b/src/toml11/toml/result.hpp
new file mode 100644
index 000000000..77cd46c64
--- /dev/null
+++ b/src/toml11/toml/result.hpp
@@ -0,0 +1,717 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_RESULT_HPP
+#define TOML11_RESULT_HPP
+#include "traits.hpp"
+#include <type_traits>
+#include <stdexcept>
+#include <utility>
+#include <new>
+#include <string>
+#include <sstream>
+#include <cassert>
+
+namespace toml
+{
+
+template<typename T>
+struct success
+{
+ using value_type = T;
+ value_type value;
+
+ explicit success(const value_type& v)
+ noexcept(std::is_nothrow_copy_constructible<value_type>::value)
+ : value(v)
+ {}
+ explicit success(value_type&& v)
+ noexcept(std::is_nothrow_move_constructible<value_type>::value)
+ : value(std::move(v))
+ {}
+
+ template<typename U>
+ explicit success(U&& v): value(std::forward<U>(v)) {}
+
+ template<typename U>
+ explicit success(const success<U>& v): value(v.value) {}
+ template<typename U>
+ explicit success(success<U>&& v): value(std::move(v.value)) {}
+
+ ~success() = default;
+ success(const success&) = default;
+ success(success&&) = default;
+ success& operator=(const success&) = default;
+ success& operator=(success&&) = default;
+};
+
+template<typename T>
+struct failure
+{
+ using value_type = T;
+ value_type value;
+
+ explicit failure(const value_type& v)
+ noexcept(std::is_nothrow_copy_constructible<value_type>::value)
+ : value(v)
+ {}
+ explicit failure(value_type&& v)
+ noexcept(std::is_nothrow_move_constructible<value_type>::value)
+ : value(std::move(v))
+ {}
+
+ template<typename U>
+ explicit failure(U&& v): value(std::forward<U>(v)) {}
+
+ template<typename U>
+ explicit failure(const failure<U>& v): value(v.value) {}
+ template<typename U>
+ explicit failure(failure<U>&& v): value(std::move(v.value)) {}
+
+ ~failure() = default;
+ failure(const failure&) = default;
+ failure(failure&&) = default;
+ failure& operator=(const failure&) = default;
+ failure& operator=(failure&&) = default;
+};
+
+template<typename T>
+success<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
+ok(T&& v)
+{
+ return success<
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type
+ >(std::forward<T>(v));
+}
+template<typename T>
+failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
+err(T&& v)
+{
+ return failure<
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type
+ >(std::forward<T>(v));
+}
+
+inline success<std::string> ok(const char* literal)
+{
+ return success<std::string>(std::string(literal));
+}
+inline failure<std::string> err(const char* literal)
+{
+ return failure<std::string>(std::string(literal));
+}
+
+
+template<typename T, typename E>
+struct result
+{
+ using value_type = T;
+ using error_type = E;
+ using success_type = success<value_type>;
+ using failure_type = failure<error_type>;
+
+ result(const success_type& s): is_ok_(true)
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(s);
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ result(const failure_type& f): is_ok_(false)
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ result(success_type&& s): is_ok_(true)
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ result(failure_type&& f): is_ok_(false)
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+
+ template<typename U>
+ result(const success<U>& s): is_ok_(true)
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ template<typename U>
+ result(const failure<U>& f): is_ok_(false)
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ template<typename U>
+ result(success<U>&& s): is_ok_(true)
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ template<typename U>
+ result(failure<U>&& f): is_ok_(false)
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+
+ result& operator=(const success_type& s)
+ {
+ this->cleanup();
+ this->is_ok_ = true;
+ auto tmp = ::new(std::addressof(this->succ)) success_type(s);
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ return *this;
+ }
+ result& operator=(const failure_type& f)
+ {
+ this->cleanup();
+ this->is_ok_ = false;
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ return *this;
+ }
+ result& operator=(success_type&& s)
+ {
+ this->cleanup();
+ this->is_ok_ = true;
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ return *this;
+ }
+ result& operator=(failure_type&& f)
+ {
+ this->cleanup();
+ this->is_ok_ = false;
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ return *this;
+ }
+
+ template<typename U>
+ result& operator=(const success<U>& s)
+ {
+ this->cleanup();
+ this->is_ok_ = true;
+ auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ return *this;
+ }
+ template<typename U>
+ result& operator=(const failure<U>& f)
+ {
+ this->cleanup();
+ this->is_ok_ = false;
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ return *this;
+ }
+ template<typename U>
+ result& operator=(success<U>&& s)
+ {
+ this->cleanup();
+ this->is_ok_ = true;
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ return *this;
+ }
+ template<typename U>
+ result& operator=(failure<U>&& f)
+ {
+ this->cleanup();
+ this->is_ok_ = false;
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ return *this;
+ }
+
+ ~result() noexcept {this->cleanup();}
+
+ result(const result& other): is_ok_(other.is_ok())
+ {
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ }
+ result(result&& other): is_ok_(other.is_ok())
+ {
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ }
+
+ template<typename U, typename F>
+ result(const result<U, F>& other): is_ok_(other.is_ok())
+ {
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ }
+ template<typename U, typename F>
+ result(result<U, F>&& other): is_ok_(other.is_ok())
+ {
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ }
+
+ result& operator=(const result& other)
+ {
+ this->cleanup();
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ is_ok_ = other.is_ok();
+ return *this;
+ }
+ result& operator=(result&& other)
+ {
+ this->cleanup();
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ is_ok_ = other.is_ok();
+ return *this;
+ }
+
+ template<typename U, typename F>
+ result& operator=(const result<U, F>& other)
+ {
+ this->cleanup();
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ is_ok_ = other.is_ok();
+ return *this;
+ }
+ template<typename U, typename F>
+ result& operator=(result<U, F>&& other)
+ {
+ this->cleanup();
+ if(other.is_ok())
+ {
+ auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
+ assert(tmp == std::addressof(this->succ));
+ (void)tmp;
+ }
+ else
+ {
+ auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
+ assert(tmp == std::addressof(this->fail));
+ (void)tmp;
+ }
+ is_ok_ = other.is_ok();
+ return *this;
+ }
+
+ bool is_ok() const noexcept {return is_ok_;}
+ bool is_err() const noexcept {return !is_ok_;}
+
+ operator bool() const noexcept {return is_ok_;}
+
+ value_type& unwrap() &
+ {
+ if(is_err())
+ {
+ throw std::runtime_error("toml::result: bad unwrap: " +
+ format_error(this->as_err()));
+ }
+ return this->succ.value;
+ }
+ value_type const& unwrap() const&
+ {
+ if(is_err())
+ {
+ throw std::runtime_error("toml::result: bad unwrap: " +
+ format_error(this->as_err()));
+ }
+ return this->succ.value;
+ }
+ value_type&& unwrap() &&
+ {
+ if(is_err())
+ {
+ throw std::runtime_error("toml::result: bad unwrap: " +
+ format_error(this->as_err()));
+ }
+ return std::move(this->succ.value);
+ }
+
+ value_type& unwrap_or(value_type& opt) &
+ {
+ if(is_err()) {return opt;}
+ return this->succ.value;
+ }
+ value_type const& unwrap_or(value_type const& opt) const&
+ {
+ if(is_err()) {return opt;}
+ return this->succ.value;
+ }
+ value_type unwrap_or(value_type opt) &&
+ {
+ if(is_err()) {return opt;}
+ return this->succ.value;
+ }
+
+ error_type& unwrap_err() &
+ {
+ if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
+ return this->fail.value;
+ }
+ error_type const& unwrap_err() const&
+ {
+ if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
+ return this->fail.value;
+ }
+ error_type&& unwrap_err() &&
+ {
+ if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
+ return std::move(this->fail.value);
+ }
+
+ value_type& as_ok() & noexcept {return this->succ.value;}
+ value_type const& as_ok() const& noexcept {return this->succ.value;}
+ value_type&& as_ok() && noexcept {return std::move(this->succ.value);}
+
+ error_type& as_err() & noexcept {return this->fail.value;}
+ error_type const& as_err() const& noexcept {return this->fail.value;}
+ error_type&& as_err() && noexcept {return std::move(this->fail.value);}
+
+
+ // prerequisities
+ // F: T -> U
+ // retval: result<U, E>
+ template<typename F>
+ result<detail::return_type_of_t<F, value_type&>, error_type>
+ map(F&& f) &
+ {
+ if(this->is_ok()){return ok(f(this->as_ok()));}
+ return err(this->as_err());
+ }
+ template<typename F>
+ result<detail::return_type_of_t<F, value_type const&>, error_type>
+ map(F&& f) const&
+ {
+ if(this->is_ok()){return ok(f(this->as_ok()));}
+ return err(this->as_err());
+ }
+ template<typename F>
+ result<detail::return_type_of_t<F, value_type &&>, error_type>
+ map(F&& f) &&
+ {
+ if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
+ return err(std::move(this->as_err()));
+ }
+
+ // prerequisities
+ // F: E -> F
+ // retval: result<T, F>
+ template<typename F>
+ result<value_type, detail::return_type_of_t<F, error_type&>>
+ map_err(F&& f) &
+ {
+ if(this->is_err()){return err(f(this->as_err()));}
+ return ok(this->as_ok());
+ }
+ template<typename F>
+ result<value_type, detail::return_type_of_t<F, error_type const&>>
+ map_err(F&& f) const&
+ {
+ if(this->is_err()){return err(f(this->as_err()));}
+ return ok(this->as_ok());
+ }
+ template<typename F>
+ result<value_type, detail::return_type_of_t<F, error_type&&>>
+ map_err(F&& f) &&
+ {
+ if(this->is_err()){return err(f(std::move(this->as_err())));}
+ return ok(std::move(this->as_ok()));
+ }
+
+ // prerequisities
+ // F: T -> U
+ // retval: U
+ template<typename F, typename U>
+ detail::return_type_of_t<F, value_type&>
+ map_or_else(F&& f, U&& opt) &
+ {
+ if(this->is_err()){return std::forward<U>(opt);}
+ return f(this->as_ok());
+ }
+ template<typename F, typename U>
+ detail::return_type_of_t<F, value_type const&>
+ map_or_else(F&& f, U&& opt) const&
+ {
+ if(this->is_err()){return std::forward<U>(opt);}
+ return f(this->as_ok());
+ }
+ template<typename F, typename U>
+ detail::return_type_of_t<F, value_type&&>
+ map_or_else(F&& f, U&& opt) &&
+ {
+ if(this->is_err()){return std::forward<U>(opt);}
+ return f(std::move(this->as_ok()));
+ }
+
+ // prerequisities
+ // F: E -> U
+ // retval: U
+ template<typename F, typename U>
+ detail::return_type_of_t<F, error_type&>
+ map_err_or_else(F&& f, U&& opt) &
+ {
+ if(this->is_ok()){return std::forward<U>(opt);}
+ return f(this->as_err());
+ }
+ template<typename F, typename U>
+ detail::return_type_of_t<F, error_type const&>
+ map_err_or_else(F&& f, U&& opt) const&
+ {
+ if(this->is_ok()){return std::forward<U>(opt);}
+ return f(this->as_err());
+ }
+ template<typename F, typename U>
+ detail::return_type_of_t<F, error_type&&>
+ map_err_or_else(F&& f, U&& opt) &&
+ {
+ if(this->is_ok()){return std::forward<U>(opt);}
+ return f(std::move(this->as_err()));
+ }
+
+ // prerequisities:
+ // F: func T -> U
+ // toml::err(error_type) should be convertible to U.
+ // normally, type U is another result<S, F> and E is convertible to F
+ template<typename F>
+ detail::return_type_of_t<F, value_type&>
+ and_then(F&& f) &
+ {
+ if(this->is_ok()){return f(this->as_ok());}
+ return err(this->as_err());
+ }
+ template<typename F>
+ detail::return_type_of_t<F, value_type const&>
+ and_then(F&& f) const&
+ {
+ if(this->is_ok()){return f(this->as_ok());}
+ return err(this->as_err());
+ }
+ template<typename F>
+ detail::return_type_of_t<F, value_type&&>
+ and_then(F&& f) &&
+ {
+ if(this->is_ok()){return f(std::move(this->as_ok()));}
+ return err(std::move(this->as_err()));
+ }
+
+ // prerequisities:
+ // F: func E -> U
+ // toml::ok(value_type) should be convertible to U.
+ // normally, type U is another result<S, F> and T is convertible to S
+ template<typename F>
+ detail::return_type_of_t<F, error_type&>
+ or_else(F&& f) &
+ {
+ if(this->is_err()){return f(this->as_err());}
+ return ok(this->as_ok());
+ }
+ template<typename F>
+ detail::return_type_of_t<F, error_type const&>
+ or_else(F&& f) const&
+ {
+ if(this->is_err()){return f(this->as_err());}
+ return ok(this->as_ok());
+ }
+ template<typename F>
+ detail::return_type_of_t<F, error_type&&>
+ or_else(F&& f) &&
+ {
+ if(this->is_err()){return f(std::move(this->as_err()));}
+ return ok(std::move(this->as_ok()));
+ }
+
+ // if *this is error, returns *this. otherwise, returns other.
+ result and_other(const result& other) const&
+ {
+ return this->is_err() ? *this : other;
+ }
+ result and_other(result&& other) &&
+ {
+ return this->is_err() ? std::move(*this) : std::move(other);
+ }
+
+ // if *this is okay, returns *this. otherwise, returns other.
+ result or_other(const result& other) const&
+ {
+ return this->is_ok() ? *this : other;
+ }
+ result or_other(result&& other) &&
+ {
+ return this->is_ok() ? std::move(*this) : std::move(other);
+ }
+
+ void swap(result<T, E>& other)
+ {
+ result<T, E> tmp(std::move(*this));
+ *this = std::move(other);
+ other = std::move(tmp);
+ return ;
+ }
+
+ private:
+
+ static std::string format_error(std::exception const& excpt)
+ {
+ return std::string(excpt.what());
+ }
+ template<typename U, typename std::enable_if<!std::is_base_of<
+ std::exception, U>::value, std::nullptr_t>::type = nullptr>
+ static std::string format_error(U const& others)
+ {
+ std::ostringstream oss; oss << others;
+ return oss.str();
+ }
+
+ void cleanup() noexcept
+ {
+ if(this->is_ok_) {this->succ.~success_type();}
+ else {this->fail.~failure_type();}
+ return;
+ }
+
+ private:
+
+ bool is_ok_;
+ union
+ {
+ success_type succ;
+ failure_type fail;
+ };
+};
+
+template<typename T, typename E>
+void swap(result<T, E>& lhs, result<T, E>& rhs)
+{
+ lhs.swap(rhs);
+ return;
+}
+
+// this might be confusing because it eagerly evaluated, while in the other
+// cases operator && and || are short-circuited.
+//
+// template<typename T, typename E>
+// inline result<T, E>
+// operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
+// {
+// return lhs.is_ok() ? rhs : lhs;
+// }
+//
+// template<typename T, typename E>
+// inline result<T, E>
+// operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
+// {
+// return lhs.is_ok() ? lhs : rhs;
+// }
+
+// ----------------------------------------------------------------------------
+// re-use result<T, E> as a optional<T> with none_t
+
+namespace detail
+{
+struct none_t {};
+inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
+inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
+inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
+inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
+inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
+inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
+template<typename charT, typename traitsT>
+std::basic_ostream<charT, traitsT>&
+operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&)
+{
+ os << "none";
+ return os;
+}
+inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};}
+} // detail
+} // toml11
+#endif// TOML11_RESULT_H
diff --git a/src/toml11/toml/serializer.hpp b/src/toml11/toml/serializer.hpp
new file mode 100644
index 000000000..88ae775a8
--- /dev/null
+++ b/src/toml11/toml/serializer.hpp
@@ -0,0 +1,922 @@
+// Copyright Toru Niina 2019.
+// Distributed under the MIT License.
+#ifndef TOML11_SERIALIZER_HPP
+#define TOML11_SERIALIZER_HPP
+#include <cmath>
+#include <cstdio>
+
+#include <limits>
+
+#include "lexer.hpp"
+#include "value.hpp"
+
+namespace toml
+{
+
+// This function serialize a key. It checks a string is a bare key and
+// escapes special characters if the string is not compatible to a bare key.
+// ```cpp
+// std::string k("non.bare.key"); // the key itself includes `.`s.
+// std::string formatted = toml::format_key(k);
+// assert(formatted == "\"non.bare.key\"");
+// ```
+//
+// This function is exposed to make it easy to write a user-defined serializer.
+// Since toml restricts characters available in a bare key, generally a string
+// should be escaped. But checking whether a string needs to be surrounded by
+// a `"` and escaping some special character is boring.
+template<typename charT, typename traits, typename Alloc>
+std::basic_string<charT, traits, Alloc>
+format_key(const std::basic_string<charT, traits, Alloc>& k)
+{
+ if(k.empty())
+ {
+ return std::string("\"\"");
+ }
+
+ // check the key can be a bare (unquoted) key
+ detail::location loc(k, std::vector<char>(k.begin(), k.end()));
+ detail::lex_unquoted_key::invoke(loc);
+ if(loc.iter() == loc.end())
+ {
+ 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<charT, traits, Alloc> serialized("\"");
+ for(const char c : k)
+ {
+ switch(c)
+ {
+ case '\\': {serialized += "\\\\"; break;}
+ case '\"': {serialized += "\\\""; break;}
+ case '\b': {serialized += "\\b"; break;}
+ case '\t': {serialized += "\\t"; break;}
+ case '\f': {serialized += "\\f"; break;}
+ case '\n': {serialized += "\\n"; break;}
+ case '\r': {serialized += "\\r"; break;}
+ default : {serialized += c; break;}
+ }
+ }
+ serialized += "\"";
+ return serialized;
+}
+
+template<typename charT, typename traits, typename Alloc>
+std::basic_string<charT, traits, Alloc>
+format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
+{
+ if(keys.empty())
+ {
+ return std::string("\"\"");
+ }
+
+ std::basic_string<charT, traits, Alloc> serialized;
+ for(const auto& ky : keys)
+ {
+ serialized += format_key(ky);
+ serialized += charT('.');
+ }
+ serialized.pop_back(); // remove the last dot '.'
+ return serialized;
+}
+
+template<typename Value>
+struct serializer
+{
+ static_assert(detail::is_basic_value<Value>::value,
+ "toml::serializer is for toml::value and its variants, "
+ "toml::basic_value<...>.");
+
+ using value_type = Value;
+ using key_type = typename value_type::key_type ;
+ using comment_type = typename value_type::comment_type ;
+ using boolean_type = typename value_type::boolean_type ;
+ using integer_type = typename value_type::integer_type ;
+ using floating_type = typename value_type::floating_type ;
+ using string_type = typename value_type::string_type ;
+ using local_time_type = typename value_type::local_time_type ;
+ using local_date_type = typename value_type::local_date_type ;
+ using local_datetime_type = typename value_type::local_datetime_type ;
+ using offset_datetime_type = typename value_type::offset_datetime_type;
+ using array_type = typename value_type::array_type ;
+ using table_type = typename value_type::table_type ;
+
+ serializer(const std::size_t w = 80u,
+ const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
+ const bool can_be_inlined = false,
+ const bool no_comment = false,
+ std::vector<toml::key> 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;
+
+ std::string operator()(const boolean_type& b) const
+ {
+ return b ? "true" : "false";
+ }
+ std::string operator()(const integer_type i) const
+ {
+ return std::to_string(i);
+ }
+ 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");
+ }
+ }
+
+ const auto fmt = "%.*g";
+ const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
+ // +1 for null character(\0)
+ std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
+ std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
+
+ std::string token(buf.begin(), std::prev(buf.end()));
+ if(!token.empty() && token.back() == '.') // 1. => 1.0
+ {
+ token += '0';
+ }
+
+ const auto e = std::find_if(
+ token.cbegin(), token.cend(), [](const char c) noexcept -> bool {
+ return c == 'e' || c == 'E';
+ });
+ const auto has_exponent = (token.cend() != e);
+ const auto has_fraction = (token.cend() != std::find(
+ token.cbegin(), token.cend(), '.'));
+
+ if(!has_exponent && !has_fraction)
+ {
+ // the resulting value does not have any float specific part!
+ token += ".0";
+ }
+ return token;
+ }
+ std::string operator()(const string_type& s) const
+ {
+ 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()) &&
+ this->width_ != (std::numeric_limits<std::size_t>::max)())
+ {
+ // if linefeed or double-quote is contained,
+ // make it multiline basic string.
+ const auto escaped = this->escape_ml_basic_string(s.str);
+ std::string open("\"\"\"");
+ std::string close("\"\"\"");
+ if(escaped.find('\n') != std::string::npos ||
+ this->width_ < escaped.size() + 6)
+ {
+ // if the string body contains newline or is enough long,
+ // add newlines after and before delimiters.
+ open += "\n";
+ close = std::string("\\\n") + close;
+ }
+ return open + escaped + close;
+ }
+
+ // no linefeed. try to make it oneline-string.
+ std::string oneline = this->escape_basic_string(s.str);
+ if(oneline.size() + 2 < width_ || width_ < 2)
+ {
+ const std::string quote("\"");
+ return quote + oneline + quote;
+ }
+
+ // the line is too long compared to the specified width.
+ // split it into multiple lines.
+ std::string token("\"\"\"\n");
+ while(!oneline.empty())
+ {
+ if(oneline.size() < width_)
+ {
+ token += oneline;
+ oneline.clear();
+ }
+ else if(oneline.at(width_-2) == '\\')
+ {
+ token += oneline.substr(0, width_-2);
+ token += "\\\n";
+ oneline.erase(0, width_-2);
+ }
+ else
+ {
+ token += oneline.substr(0, width_-1);
+ token += "\\\n";
+ oneline.erase(0, width_-1);
+ }
+ }
+ return token + std::string("\\\n\"\"\"");
+ }
+ else // the string `s` is literal-string.
+ {
+ if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
+ std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
+ {
+ std::string open("'''");
+ if(this->width_ + 6 < s.str.size())
+ {
+ open += '\n'; // the first newline is ignored by TOML spec
+ }
+ const std::string close("'''");
+ return open + s.str + close;
+ }
+ else
+ {
+ const std::string quote("'");
+ return quote + s.str + quote;
+ }
+ }
+ }
+
+ std::string operator()(const local_date_type& d) const
+ {
+ std::ostringstream oss;
+ oss << d;
+ return oss.str();
+ }
+ std::string operator()(const local_time_type& t) const
+ {
+ std::ostringstream oss;
+ oss << t;
+ return oss.str();
+ }
+ std::string operator()(const local_datetime_type& dt) const
+ {
+ std::ostringstream oss;
+ oss << dt;
+ return oss.str();
+ }
+ std::string operator()(const offset_datetime_type& odt) const
+ {
+ std::ostringstream oss;
+ oss << odt;
+ return oss.str();
+ }
+
+ std::string operator()(const array_type& v) const
+ {
+ 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))
+ {
+ const auto inl = this->make_inline_array(v);
+ if(inl.size() < this->width_ &&
+ std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
+ {
+ return inl;
+ }
+ }
+
+ // if the length exceeds this->width_, print multiline array.
+ // key = [
+ // # ...
+ // 42,
+ // ...
+ // ]
+ std::string token;
+ std::string current_line;
+ token += "[\n";
+ for(const auto& item : v)
+ {
+ 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.
+ // ```toml
+ // array = [
+ // # comment for what?
+ // 1, 2, 3, 4, 5
+ // ]
+ // ```
+ if(!current_line.empty())
+ {
+ if(current_line.back() != '\n')
+ {
+ current_line += '\n';
+ }
+ token += current_line;
+ current_line.clear();
+ }
+ for(const auto& c : item.comments())
+ {
+ token += '#';
+ token += c;
+ token += '\n';
+ }
+ token += toml::visit(*this, item);
+ if(!token.empty() && token.back() == '\n') {token.pop_back();}
+ token += ",\n";
+ continue;
+ }
+ std::string next_elem;
+ if(item.is_table())
+ {
+ serializer ser(*this);
+ ser.can_be_inlined_ = true;
+ ser.width_ = (std::numeric_limits<std::size_t>::max)();
+ next_elem += toml::visit(ser, item);
+ }
+ else
+ {
+ next_elem += toml::visit(*this, item);
+ }
+
+ // comma before newline.
+ 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_)
+ {
+ current_line += next_elem;
+ current_line += ',';
+ }
+ else if(current_line.empty())
+ {
+ // if current line was empty, force put the next_elem because
+ // next_elem is not splittable
+ token += next_elem;
+ token += ",\n";
+ // current_line is kept empty
+ }
+ else // reset current_line
+ {
+ assert(current_line.back() == ',');
+ token += current_line;
+ token += '\n';
+ current_line = next_elem;
+ current_line += ',';
+ }
+ }
+ if(!current_line.empty())
+ {
+ if(!current_line.empty() && current_line.back() != '\n')
+ {
+ current_line += '\n';
+ }
+ token += current_line;
+ }
+ token += "]\n";
+ return token;
+ }
+
+ // templatize for any table-like container
+ std::string operator()(const table_type& v) const
+ {
+ // if an element has a comment, then it can't be inlined.
+ // table = {# how can we write a comment for this? key = "value"}
+ if(this->can_be_inlined_ && !(this->has_comment_inside(v)))
+ {
+ std::string token;
+ if(!this->keys_.empty())
+ {
+ token += format_key(this->keys_.back());
+ token += " = ";
+ }
+ token += this->make_inline_table(v);
+ if(token.size() < this->width_ &&
+ token.end() == std::find(token.begin(), token.end(), '\n'))
+ {
+ return token;
+ }
+ }
+
+ std::string token;
+ if(!keys_.empty())
+ {
+ token += '[';
+ token += format_keys(keys_);
+ token += "]\n";
+ }
+ token += this->make_multiline_table(v);
+ return token;
+ }
+
+ private:
+
+ std::string escape_basic_string(const std::string& s) const
+ {
+ //XXX assuming `s` is a valid utf-8 sequence.
+ std::string retval;
+ for(const char c : s)
+ {
+ switch(c)
+ {
+ case '\\': {retval += "\\\\"; break;}
+ case '\"': {retval += "\\\""; break;}
+ case '\b': {retval += "\\b"; break;}
+ case '\t': {retval += "\\t"; break;}
+ case '\f': {retval += "\\f"; break;}
+ case '\n': {retval += "\\n"; break;}
+ case '\r': {retval += "\\r"; 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;
+ }
+
+ std::string escape_ml_basic_string(const std::string& s) const
+ {
+ std::string retval;
+ for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
+ {
+ switch(*i)
+ {
+ case '\\': {retval += "\\\\"; break;}
+ // One or two consecutive "s are allowed.
+ // Later we will check there are no three consecutive "s.
+ // case '\"': {retval += "\\\""; break;}
+ case '\b': {retval += "\\b"; break;}
+ case '\t': {retval += "\\t"; break;}
+ case '\f': {retval += "\\f"; break;}
+ case '\n': {retval += "\n"; break;}
+ case '\r':
+ {
+ if(std::next(i) != e && *std::next(i) == '\n')
+ {
+ retval += "\r\n";
+ ++i;
+ }
+ else
+ {
+ retval += "\\r";
+ }
+ 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.
+ // 3 consecutive `"`s are considered as a closing delimiter.
+ // We need to check if there are 3 or more consecutive `"`s and insert
+ // backslash to break them down into several short `"`s like the `str6`
+ // in the following example.
+ // ```toml
+ // str4 = """Here are two quotation marks: "". Simple enough."""
+ // # str5 = """Here are three quotation marks: """.""" # INVALID
+ // str5 = """Here are three quotation marks: ""\"."""
+ // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
+ // ```
+ auto found_3_quotes = retval.find("\"\"\"");
+ while(found_3_quotes != std::string::npos)
+ {
+ retval.replace(found_3_quotes, 3, "\"\"\\\"");
+ found_3_quotes = retval.find("\"\"\"");
+ }
+ return retval;
+ }
+
+ // if an element of a table or an array has a comment, it cannot be inlined.
+ bool has_comment_inside(const array_type& a) const noexcept
+ {
+ // if no_comment is set, comments would not be written.
+ if(this->no_comment_) {return false;}
+
+ for(const auto& v : a)
+ {
+ if(!v.comments().empty()) {return true;}
+ }
+ return false;
+ }
+ bool has_comment_inside(const table_type& t) const noexcept
+ {
+ // if no_comment is set, comments would not be written.
+ if(this->no_comment_) {return false;}
+
+ for(const auto& kv : t)
+ {
+ if(!kv.second.comments().empty()) {return true;}
+ }
+ return false;
+ }
+
+ std::string make_inline_array(const array_type& v) const
+ {
+ assert(!has_comment_inside(v));
+ std::string token;
+ token += '[';
+ bool is_first = true;
+ for(const auto& item : v)
+ {
+ if(is_first) {is_first = false;} else {token += ',';}
+ token += visit(serializer(
+ (std::numeric_limits<std::size_t>::max)(), this->float_prec_,
+ /* inlined */ true, /*no comment*/ false, /*keys*/ {},
+ /*has_comment*/ !item.comments().empty()), item);
+ }
+ token += ']';
+ return token;
+ }
+
+ std::string make_inline_table(const table_type& v) const
+ {
+ assert(!has_comment_inside(v));
+ assert(this->can_be_inlined_);
+ std::string token;
+ token += '{';
+ bool is_first = true;
+ for(const auto& kv : v)
+ {
+ // in inline tables, trailing comma is not allowed (toml-lang #569).
+ if(is_first) {is_first = false;} else {token += ',';}
+ token += format_key(kv.first);
+ token += '=';
+ token += visit(serializer(
+ (std::numeric_limits<std::size_t>::max)(), this->float_prec_,
+ /* inlined */ true, /*no comment*/ false, /*keys*/ {},
+ /*has_comment*/ !kv.second.comments().empty()), kv.second);
+ }
+ token += '}';
+ return token;
+ }
+
+ std::string make_multiline_table(const table_type& v) const
+ {
+ std::string token;
+
+ // 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))
+ {
+ continue;
+ }
+
+ 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_,
+ /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
+ /*has_comment*/ !kv.second.comments().empty()), kv.second);
+
+ if(token.back() != '\n')
+ {
+ token += '\n';
+ }
+ }
+
+ // normal tables / array of tables
+
+ // after multiline table appeared, the other tables cannot be inline
+ // because the table would be assigned into the table.
+ // [foo]
+ // ...
+ // bar = {...} # <- bar will be a member of [foo].
+ bool multiline_table_printed = false;
+ for(const auto& kv : v)
+ {
+ if(!kv.second.is_table() && !is_array_of_tables(kv.second))
+ {
+ continue; // other stuff are already serialized. skip them.
+ }
+
+ std::vector<toml::key> ks(this->keys_);
+ ks.push_back(kv.first);
+
+ auto tmp = visit(serializer(this->width_, this->float_prec_,
+ !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
+ {
+ 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 += " = ";
+ }
+
+ 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;
+ }
+ // 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";
+ }
+
+ 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() || 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<toml::key> keys_;
+};
+
+template<typename C,
+ template<typename ...> class M, template<typename ...> class V>
+std::string
+format(const basic_value<C, M, V>& v, std::size_t w = 80u,
+ int fprec = std::numeric_limits<toml::floating>::max_digits10,
+ bool no_comment = false, bool force_inline = false)
+{
+ using value_type = basic_value<C, M, V>;
+ // if value is a table, it is considered to be a root object.
+ // the root object can't be an inline table.
+ if(v.is_table())
+ {
+ std::ostringstream oss;
+ if(!v.comments().empty())
+ {
+ oss << v.comments();
+ oss << '\n'; // to split the file comment from the first element
+ }
+ const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v);
+ oss << serialized;
+ return oss.str();
+ }
+ return visit(serializer<value_type>(w, fprec, force_inline), v);
+}
+
+namespace detail
+{
+template<typename charT, typename traits>
+int comment_index(std::basic_ostream<charT, traits>&)
+{
+ static const int index = std::ios_base::xalloc();
+ return index;
+}
+} // detail
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+nocomment(std::basic_ostream<charT, traits>& os)
+{
+ // by default, it is zero. and by default, it shows comments.
+ os.iword(detail::comment_index(os)) = 1;
+ return os;
+}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+showcomment(std::basic_ostream<charT, traits>& os)
+{
+ // by default, it is zero. and by default, it shows comments.
+ os.iword(detail::comment_index(os)) = 0;
+ return os;
+}
+
+template<typename charT, typename traits, typename C,
+ template<typename ...> class M, template<typename ...> class V>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
+{
+ using value_type = basic_value<C, M, V>;
+
+ // get status of std::setw().
+ const auto w = static_cast<std::size_t>(os.width());
+ const int fprec = static_cast<int>(os.precision());
+ os.width(0);
+
+ // 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)));
+
+ if(!no_comment && v.is_table() && !v.comments().empty())
+ {
+ os << v.comments();
+ os << '\n'; // to split the file comment from the first element
+ }
+ // the root object can't be an inline table. so pass `false`.
+ const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
+ os << serialized;
+
+ // if v is a non-table value, and has only one comment, then
+ // put a comment just after a value. in the following way.
+ //
+ // ```toml
+ // key = "value" # comment.
+ // ```
+ //
+ // Since the top-level toml object is a table, one who want to put a
+ // non-table toml value must use this in a following way.
+ //
+ // ```cpp
+ // toml::value v;
+ // std::cout << "user-defined-key = " << v << std::endl;
+ // ```
+ //
+ // In this case, it is impossible to put comments before key-value pair.
+ // The only way to preserve comments is to put all of them after a value.
+ if(!no_comment && !v.is_table() && !v.comments().empty())
+ {
+ os << " #";
+ for(const auto& c : v.comments()) {os << c;}
+ }
+ return os;
+}
+
+} // toml
+#endif// TOML11_SERIALIZER_HPP
diff --git a/src/toml11/toml/source_location.hpp b/src/toml11/toml/source_location.hpp
new file mode 100644
index 000000000..fa175b5b4
--- /dev/null
+++ b/src/toml11/toml/source_location.hpp
@@ -0,0 +1,233 @@
+// Copyright Toru Niina 2019.
+// Distributed under the MIT License.
+#ifndef TOML11_SOURCE_LOCATION_HPP
+#define TOML11_SOURCE_LOCATION_HPP
+#include <cstdint>
+#include <sstream>
+
+#include "region.hpp"
+
+namespace toml
+{
+
+// A struct to contain location in a toml file.
+// The interface imitates std::experimental::source_location,
+// but not completely the same.
+//
+// It would be constructed by toml::value. It can be used to generate
+// user-defined error messages.
+//
+// - std::uint_least32_t line() const noexcept
+// - returns the line number where the region is on.
+// - std::uint_least32_t column() const noexcept
+// - returns the column number where the region starts.
+// - std::uint_least32_t region() const noexcept
+// - returns the size of the region.
+//
+// +-- line() +-- region of interest (region() == 9)
+// v .---+---.
+// 12 | value = "foo bar"
+// ^
+// +-- column()
+//
+// - std::string const& file_name() const noexcept;
+// - name of the file.
+// - std::string const& line_str() const noexcept;
+// - the whole line that contains the region of interest.
+//
+struct source_location
+{
+ public:
+
+ source_location()
+ : line_num_(1), column_num_(1), region_size_(1),
+ file_name_("unknown file"), line_str_("")
+ {}
+
+ explicit source_location(const detail::region_base* reg)
+ : line_num_(1), column_num_(1), region_size_(1),
+ file_name_("unknown file"), line_str_("")
+ {
+ if(reg)
+ {
+ if(reg->line_num() != detail::region_base().line_num())
+ {
+ line_num_ = static_cast<std::uint_least32_t>(
+ std::stoul(reg->line_num()));
+ }
+ column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
+ region_size_ = static_cast<std::uint_least32_t>(reg->size());
+ file_name_ = reg->name();
+ line_str_ = reg->line();
+ }
+ }
+
+ explicit source_location(const detail::region& reg)
+ : line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
+ column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
+ region_size_(static_cast<std::uint_least32_t>(reg.size())),
+ file_name_(reg.name()),
+ line_str_ (reg.line())
+ {}
+ explicit source_location(const detail::location& loc)
+ : line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
+ column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
+ region_size_(static_cast<std::uint_least32_t>(loc.size())),
+ file_name_(loc.name()),
+ line_str_ (loc.line())
+ {}
+
+ ~source_location() = default;
+ source_location(source_location const&) = default;
+ source_location(source_location &&) = default;
+ source_location& operator=(source_location const&) = default;
+ source_location& operator=(source_location &&) = default;
+
+ std::uint_least32_t line() const noexcept {return line_num_;}
+ std::uint_least32_t column() const noexcept {return column_num_;}
+ std::uint_least32_t region() const noexcept {return region_size_;}
+
+ std::string const& file_name() const noexcept {return file_name_;}
+ std::string const& line_str() const noexcept {return line_str_;}
+
+ private:
+
+ std::uint_least32_t line_num_;
+ std::uint_least32_t column_num_;
+ std::uint_least32_t region_size_;
+ std::string file_name_;
+ std::string line_str_;
+};
+
+namespace detail
+{
+
+// internal error message generation.
+inline std::string format_underline(const std::string& message,
+ const std::vector<std::pair<source_location, std::string>>& loc_com,
+ const std::vector<std::string>& helps = {},
+ const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
+{
+ std::size_t line_num_width = 0;
+ for(const auto& lc : loc_com)
+ {
+ std::uint_least32_t line = lc.first.line();
+ std::size_t digit = 0;
+ while(line != 0)
+ {
+ line /= 10;
+ digit += 1;
+ }
+ line_num_width = (std::max)(line_num_width, digit);
+ }
+ // 1 is the minimum width
+ line_num_width = std::max<std::size_t>(line_num_width, 1);
+
+ std::ostringstream retval;
+
+ if(colorize)
+ {
+ retval << color::colorize; // turn on ANSI color
+ }
+
+ // XXX
+ // Here, before `colorize` support, it does not output `[error]` prefix
+ // automatically. So some user may output it manually and this change may
+ // duplicate the prefix. To avoid it, check the first 7 characters and
+ // 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
+ << color::bold << message.substr(7) << color::reset << '\n';
+ }
+ else
+ {
+ retval << color::bold << color::red << "[error] " << color::reset
+ << color::bold << message << color::reset << '\n';
+ }
+
+ const auto format_one_location = [line_num_width]
+ (std::ostringstream& oss,
+ const source_location& loc, const std::string& comment) -> void
+ {
+ oss << ' ' << color::bold << color::blue
+ << std::setw(static_cast<int>(line_num_width))
+ << std::right << loc.line() << " | " << color::reset
+ << loc.line_str() << '\n';
+
+ oss << make_string(line_num_width + 1, ' ')
+ << color::bold << color::blue << " | " << color::reset
+ << make_string(loc.column()-1 /*1-origin*/, ' ');
+
+ if(loc.region() == 1)
+ {
+ // invalid
+ // ^------
+ oss << color::bold << color::red << "^---" << color::reset;
+ }
+ else
+ {
+ // invalid
+ // ~~~~~~~
+ const auto underline_len = (std::min)(
+ static_cast<std::size_t>(loc.region()), loc.line_str().size());
+ oss << color::bold << color::red
+ << make_string(underline_len, '~') << color::reset;
+ }
+ oss << ' ';
+ oss << comment;
+ return;
+ };
+
+ assert(!loc_com.empty());
+
+ // --> example.toml
+ // |
+ retval << color::bold << color::blue << " --> " << color::reset
+ << loc_com.front().first.file_name() << '\n';
+ retval << make_string(line_num_width + 1, ' ')
+ << color::bold << color::blue << " |\n" << color::reset;
+ // 1 | key value
+ // | ^--- missing =
+ format_one_location(retval, loc_com.front().first, loc_com.front().second);
+
+ // process the rest of the locations
+ for(std::size_t i=1; i<loc_com.size(); ++i)
+ {
+ const auto& prev = loc_com.at(i-1);
+ const auto& curr = loc_com.at(i);
+
+ retval << '\n';
+ // if the filenames are the same, print "..."
+ if(prev.first.file_name() == curr.first.file_name())
+ {
+ retval << color::bold << color::blue << " ...\n" << color::reset;
+ }
+ else // if filename differs, print " --> filename.toml" again
+ {
+ retval << color::bold << color::blue << " --> " << color::reset
+ << curr.first.file_name() << '\n';
+ retval << make_string(line_num_width + 1, ' ')
+ << color::bold << color::blue << " |\n" << color::reset;
+ }
+
+ format_one_location(retval, curr.first, curr.second);
+ }
+
+ if(!helps.empty())
+ {
+ retval << '\n';
+ retval << make_string(line_num_width + 1, ' ');
+ retval << color::bold << color::blue << " |" << color::reset;
+ for(const auto& help : helps)
+ {
+ retval << color::bold << "\nHint: " << color::reset;
+ retval << help;
+ }
+ }
+ return retval.str();
+}
+
+} // detail
+} // toml
+#endif// TOML11_SOURCE_LOCATION_HPP
diff --git a/src/toml11/toml/storage.hpp b/src/toml11/toml/storage.hpp
new file mode 100644
index 000000000..202f9035f
--- /dev/null
+++ b/src/toml11/toml/storage.hpp
@@ -0,0 +1,43 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_STORAGE_HPP
+#define TOML11_STORAGE_HPP
+#include "utility.hpp"
+
+namespace toml
+{
+namespace detail
+{
+
+// this contains pointer and deep-copy the content if copied.
+// to avoid recursive pointer.
+template<typename T>
+struct storage
+{
+ using value_type = T;
+
+ explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
+ explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
+ ~storage() = default;
+ storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
+ storage& operator=(const storage& rhs)
+ {
+ this->ptr = toml::make_unique<T>(*rhs.ptr);
+ return *this;
+ }
+ storage(storage&&) = default;
+ storage& operator=(storage&&) = default;
+
+ bool is_ok() const noexcept {return static_cast<bool>(ptr);}
+
+ value_type& value() & noexcept {return *ptr;}
+ value_type const& value() const& noexcept {return *ptr;}
+ value_type&& value() && noexcept {return std::move(*ptr);}
+
+ private:
+ std::unique_ptr<value_type> ptr;
+};
+
+} // detail
+} // toml
+#endif// TOML11_STORAGE_HPP
diff --git a/src/toml11/toml/string.hpp b/src/toml11/toml/string.hpp
new file mode 100644
index 000000000..5136d8c56
--- /dev/null
+++ b/src/toml11/toml/string.hpp
@@ -0,0 +1,225 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_STRING_HPP
+#define TOML11_STRING_HPP
+#include <cstdint>
+
+#include <algorithm>
+#include <string>
+
+#if __cplusplus >= 201703L
+#if __has_include(<string_view>)
+#define TOML11_USING_STRING_VIEW 1
+#include <string_view>
+#endif
+#endif
+
+namespace toml
+{
+
+enum class string_t : std::uint8_t
+{
+ basic = 0,
+ literal = 1,
+};
+
+struct string
+{
+ string() = default;
+ ~string() = default;
+ string(const string& s) = default;
+ string(string&& s) = default;
+ string& operator=(const string& s) = default;
+ string& operator=(string&& s) = default;
+
+ string(const std::string& s): kind(string_t::basic), str(s){}
+ string(const std::string& s, string_t k): kind(k), str(s){}
+ string(const char* s): kind(string_t::basic), str(s){}
+ string(const char* s, string_t k): kind(k), str(s){}
+
+ string(std::string&& s): kind(string_t::basic), str(std::move(s)){}
+ string(std::string&& s, string_t k): kind(k), str(std::move(s)){}
+
+ string& operator=(const std::string& s)
+ {kind = string_t::basic; str = s; return *this;}
+ string& operator=(std::string&& s)
+ {kind = string_t::basic; str = std::move(s); return *this;}
+
+ operator std::string& () & noexcept {return str;}
+ operator std::string const& () const& noexcept {return str;}
+ operator std::string&& () && noexcept {return std::move(str);}
+
+ string& operator+=(const char* rhs) {str += rhs; return *this;}
+ string& operator+=(const char rhs) {str += rhs; return *this;}
+ string& operator+=(const std::string& rhs) {str += rhs; return *this;}
+ string& operator+=(const string& rhs) {str += rhs.str; return *this;}
+
+#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){}
+
+ string& operator=(std::string_view s)
+ {kind = string_t::basic; str = s; return *this;}
+
+ explicit operator std::string_view() const noexcept
+ {return std::string_view(str);}
+
+ string& operator+=(const std::string_view& rhs) {str += rhs; return *this;}
+#endif
+
+ string_t kind;
+ std::string str;
+};
+
+inline bool operator==(const string& lhs, const string& rhs)
+{
+ return lhs.kind == rhs.kind && lhs.str == rhs.str;
+}
+inline bool operator!=(const string& lhs, const string& rhs)
+{
+ return !(lhs == rhs);
+}
+inline bool operator<(const string& lhs, const string& rhs)
+{
+ return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind);
+}
+inline bool operator>(const string& lhs, const string& rhs)
+{
+ return rhs < lhs;
+}
+inline bool operator<=(const string& lhs, const string& rhs)
+{
+ return !(rhs < lhs);
+}
+inline bool operator>=(const string& lhs, const string& rhs)
+{
+ return !(lhs < rhs);
+}
+
+inline bool
+operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;}
+inline bool
+operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;}
+inline bool
+operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;}
+inline bool
+operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;}
+inline bool
+operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;}
+inline bool
+operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;}
+
+inline bool
+operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;}
+inline bool
+operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;}
+inline bool
+operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;}
+inline bool
+operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;}
+inline bool
+operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;}
+inline bool
+operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;}
+
+inline bool
+operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);}
+inline bool
+operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);}
+inline bool
+operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);}
+inline bool
+operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);}
+inline bool
+operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);}
+inline bool
+operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);}
+
+inline bool
+operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;}
+inline bool
+operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;}
+inline bool
+operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;}
+inline bool
+operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;}
+inline bool
+operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;}
+inline bool
+operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;}
+
+template<typename charT, typename traits>
+std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, const string& s)
+{
+ if(s.kind == string_t::basic)
+ {
+ if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
+ {
+ // it contains newline. make it multiline string.
+ os << "\"\"\"\n";
+ for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i)
+ {
+ switch(*i)
+ {
+ case '\\': {os << "\\\\"; break;}
+ case '\"': {os << "\\\""; break;}
+ case '\b': {os << "\\b"; break;}
+ case '\t': {os << "\\t"; break;}
+ case '\f': {os << "\\f"; break;}
+ case '\n': {os << '\n'; break;}
+ case '\r':
+ {
+ // since it is a multiline string,
+ // CRLF is not needed to be escaped.
+ if(std::next(i) != e && *std::next(i) == '\n')
+ {
+ os << "\r\n";
+ ++i;
+ }
+ else
+ {
+ os << "\\r";
+ }
+ break;
+ }
+ default: {os << *i; break;}
+ }
+ }
+ os << "\\\n\"\"\"";
+ return os;
+ }
+ // no newline. make it inline.
+ os << "\"";
+ for(const auto c : s.str)
+ {
+ switch(c)
+ {
+ case '\\': {os << "\\\\"; break;}
+ case '\"': {os << "\\\""; break;}
+ case '\b': {os << "\\b"; break;}
+ case '\t': {os << "\\t"; break;}
+ case '\f': {os << "\\f"; break;}
+ case '\n': {os << "\\n"; break;}
+ case '\r': {os << "\\r"; break;}
+ default : {os << c; break;}
+ }
+ }
+ os << "\"";
+ return os;
+ }
+ // the string `s` is literal-string.
+ if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
+ std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
+ {
+ // contains newline or single quote. make it multiline.
+ os << "'''\n" << s.str << "'''";
+ return os;
+ }
+ // normal literal string
+ os << '\'' << s.str << '\'';
+ return os;
+}
+
+} // toml
+#endif// TOML11_STRING_H
diff --git a/src/toml11/toml/traits.hpp b/src/toml11/toml/traits.hpp
new file mode 100644
index 000000000..5495c93b2
--- /dev/null
+++ b/src/toml11/toml/traits.hpp
@@ -0,0 +1,327 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_TRAITS_HPP
+#define TOML11_TRAITS_HPP
+
+#include "from.hpp"
+#include "into.hpp"
+
+#include <chrono>
+#include <forward_list>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#if __cplusplus >= 201703L
+#if __has_include(<string_view>)
+#include <string_view>
+#endif // has_include(<string_view>)
+#endif // cplusplus >= C++17
+
+namespace toml
+{
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+class basic_value;
+
+namespace detail
+{
+// ---------------------------------------------------------------------------
+// check whether type T is a kind of container/map class
+
+struct has_iterator_impl
+{
+ template<typename T> static std::true_type check(typename T::iterator*);
+ template<typename T> static std::false_type check(...);
+};
+struct has_value_type_impl
+{
+ template<typename T> static std::true_type check(typename T::value_type*);
+ template<typename T> static std::false_type check(...);
+};
+struct has_key_type_impl
+{
+ template<typename T> static std::true_type check(typename T::key_type*);
+ template<typename T> static std::false_type check(...);
+};
+struct has_mapped_type_impl
+{
+ template<typename T> static std::true_type check(typename T::mapped_type*);
+ template<typename T> static std::false_type check(...);
+};
+struct has_reserve_method_impl
+{
+ template<typename T> static std::false_type check(...);
+ template<typename T> static std::true_type check(
+ decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
+};
+struct has_push_back_method_impl
+{
+ template<typename T> static std::false_type check(...);
+ template<typename T> static std::true_type check(
+ decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
+};
+struct is_comparable_impl
+{
+ template<typename T> static std::false_type check(...);
+ template<typename T> static std::true_type check(
+ decltype(std::declval<T>() < std::declval<T>())*);
+};
+
+struct has_from_toml_method_impl
+{
+ template<typename T, typename C,
+ template<typename ...> class Tb, template<typename ...> class A>
+ static std::true_type check(
+ decltype(std::declval<T>().from_toml(
+ std::declval<::toml::basic_value<C, Tb, A>>()))*);
+
+ template<typename T, typename C,
+ template<typename ...> class Tb, template<typename ...> class A>
+ static std::false_type check(...);
+};
+struct has_into_toml_method_impl
+{
+ template<typename T>
+ static std::true_type check(decltype(std::declval<T>().into_toml())*);
+ template<typename T>
+ static std::false_type check(...);
+};
+
+struct has_specialized_from_impl
+{
+ template<typename T>
+ static std::false_type check(...);
+ template<typename T, std::size_t S = sizeof(::toml::from<T>)>
+ static std::true_type check(::toml::from<T>*);
+};
+struct has_specialized_into_impl
+{
+ template<typename T>
+ static std::false_type check(...);
+ template<typename T, std::size_t S = sizeof(::toml::into<T>)>
+ static std::true_type check(::toml::from<T>*);
+};
+
+
+/// 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
+#define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
+#endif
+
+template<typename T>
+struct has_iterator : decltype(has_iterator_impl::check<T>(nullptr)){};
+template<typename T>
+struct has_value_type : decltype(has_value_type_impl::check<T>(nullptr)){};
+template<typename T>
+struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
+template<typename T>
+struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
+template<typename T>
+struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){};
+template<typename T>
+struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
+template<typename T>
+struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
+
+template<typename T, typename C,
+ template<typename ...> class Tb, template<typename ...> class A>
+struct has_from_toml_method
+: decltype(has_from_toml_method_impl::check<T, C, Tb, A>(nullptr)){};
+
+template<typename T>
+struct has_into_toml_method
+: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
+
+template<typename T>
+struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){};
+template<typename T>
+struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){};
+
+#ifdef __INTEL_COMPILER
+#undef decltype
+#endif
+
+// ---------------------------------------------------------------------------
+// C++17 and/or/not
+
+#if __cplusplus >= 201703L
+
+using std::conjunction;
+using std::disjunction;
+using std::negation;
+
+#else
+
+template<typename ...> struct conjunction : std::true_type{};
+template<typename T> struct conjunction<T> : T{};
+template<typename T, typename ... Ts>
+struct conjunction<T, Ts...> :
+ std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
+{};
+
+template<typename ...> struct disjunction : std::false_type{};
+template<typename T> struct disjunction<T> : T {};
+template<typename T, typename ... Ts>
+struct disjunction<T, Ts...> :
+ std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
+{};
+
+template<typename T>
+struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
+
+#endif
+
+// ---------------------------------------------------------------------------
+// type checkers
+
+template<typename T> struct is_std_pair : std::false_type{};
+template<typename T1, typename T2>
+struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
+
+template<typename T> struct is_std_tuple : std::false_type{};
+template<typename ... Ts>
+struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
+
+template<typename T> struct is_std_forward_list : std::false_type{};
+template<typename T>
+struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
+
+template<typename T> struct is_chrono_duration: std::false_type{};
+template<typename Rep, typename Period>
+struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
+
+template<typename T>
+struct is_map : conjunction< // map satisfies all the following conditions
+ has_iterator<T>, // has T::iterator
+ has_value_type<T>, // has T::value_type
+ has_key_type<T>, // has T::key_type
+ has_mapped_type<T> // has T::mapped_type
+ >{};
+template<typename T> struct is_map<T&> : is_map<T>{};
+template<typename T> struct is_map<T const&> : is_map<T>{};
+template<typename T> struct is_map<T volatile&> : is_map<T>{};
+template<typename T> struct is_map<T const volatile&> : is_map<T>{};
+
+template<typename T>
+struct is_container : conjunction<
+ negation<is_map<T>>, // not a map
+ negation<std::is_same<T, std::string>>, // not a std::string
+#if __cplusplus >= 201703L
+#if __has_include(<string_view>)
+ negation<std::is_same<T, std::string_view>>, // not a std::string_view
+#endif // has_include(<string_view>)
+#endif
+ has_iterator<T>, // has T::iterator
+ has_value_type<T> // has T::value_type
+ >{};
+template<typename T> struct is_container<T&> : is_container<T>{};
+template<typename T> struct is_container<T const&> : is_container<T>{};
+template<typename T> struct is_container<T volatile&> : is_container<T>{};
+template<typename T> struct is_container<T const volatile&> : is_container<T>{};
+
+template<typename T>
+struct is_basic_value: std::false_type{};
+template<typename T> struct is_basic_value<T&> : is_basic_value<T>{};
+template<typename T> struct is_basic_value<T const&> : is_basic_value<T>{};
+template<typename T> struct is_basic_value<T volatile&> : is_basic_value<T>{};
+template<typename T> struct is_basic_value<T const volatile&> : is_basic_value<T>{};
+template<typename C, template<typename ...> class M, template<typename ...> class V>
+struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
+
+// ---------------------------------------------------------------------------
+// C++14 index_sequence
+
+#if __cplusplus >= 201402L
+
+using std::index_sequence;
+using std::make_index_sequence;
+
+#else
+
+template<std::size_t ... Ns> struct index_sequence{};
+
+template<typename IS, std::size_t N> struct push_back_index_sequence{};
+template<std::size_t N, std::size_t ... Ns>
+struct push_back_index_sequence<index_sequence<Ns...>, N>
+{
+ typedef index_sequence<Ns..., N> type;
+};
+
+template<std::size_t N>
+struct index_sequence_maker
+{
+ typedef typename push_back_index_sequence<
+ typename index_sequence_maker<N-1>::type, N>::type type;
+};
+template<>
+struct index_sequence_maker<0>
+{
+ typedef index_sequence<0> type;
+};
+template<std::size_t N>
+using make_index_sequence = typename index_sequence_maker<N-1>::type;
+
+#endif // __cplusplus >= 2014
+
+// ---------------------------------------------------------------------------
+// C++14 enable_if_t
+
+#if __cplusplus >= 201402L
+
+using std::enable_if_t;
+
+#else
+
+template<bool B, typename T>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+#endif // __cplusplus >= 2014
+
+// ---------------------------------------------------------------------------
+// return_type_of_t
+
+#if __cplusplus >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
+
+template<typename F, typename ... Args>
+using return_type_of_t = std::invoke_result_t<F, Args...>;
+
+#else
+// result_of is deprecated after C++17
+template<typename F, typename ... Args>
+using return_type_of_t = typename std::result_of<F(Args...)>::type;
+
+#endif
+
+// ---------------------------------------------------------------------------
+// is_string_literal
+//
+// to use this, pass `typename remove_reference<T>::type` to T.
+
+template<typename T>
+struct is_string_literal:
+disjunction<
+ std::is_same<const char*, T>,
+ conjunction<
+ std::is_array<T>,
+ std::is_same<const char, typename std::remove_extent<T>::type>
+ >
+ >{};
+
+// ---------------------------------------------------------------------------
+// C++20 remove_cvref_t
+
+template<typename T>
+struct remove_cvref
+{
+ using type = typename std::remove_cv<
+ typename std::remove_reference<T>::type>::type;
+};
+
+template<typename T>
+using remove_cvref_t = typename remove_cvref<T>::type;
+
+}// detail
+}//toml
+#endif // TOML_TRAITS
diff --git a/src/toml11/toml/types.hpp b/src/toml11/toml/types.hpp
new file mode 100644
index 000000000..1e420e7fd
--- /dev/null
+++ b/src/toml11/toml/types.hpp
@@ -0,0 +1,173 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_TYPES_HPP
+#define TOML11_TYPES_HPP
+#include <unordered_map>
+#include <vector>
+
+#include "comments.hpp"
+#include "datetime.hpp"
+#include "string.hpp"
+#include "traits.hpp"
+
+namespace toml
+{
+
+template<typename Comment, // discard/preserve_comment
+ template<typename ...> class Table, // map-like class
+ template<typename ...> class Array> // vector-like class
+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 keyword, cannot use it here.
+// the following stuffs are structs defined here, so aliases are not needed.
+// - string
+// - offset_datetime
+// - offset_datetime
+// - local_datetime
+// - 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<discard_comments, std::unordered_map, std::vector>;
+// 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,
+ boolean = 1,
+ integer = 2,
+ floating = 3,
+ string = 4,
+ offset_datetime = 5,
+ local_datetime = 6,
+ local_date = 7,
+ local_time = 8,
+ array = 9,
+ table = 10,
+};
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+
+template<typename charT, typename traits>
+inline std::basic_ostream<charT, traits>&
+operator<<(std::basic_ostream<charT, traits>& os, value_t t)
+{
+ switch(t)
+ {
+ case value_t::boolean : os << "boolean"; return os;
+ case value_t::integer : os << "integer"; return os;
+ case value_t::floating : os << "floating"; return os;
+ case value_t::string : os << "string"; return os;
+ case value_t::offset_datetime : os << "offset_datetime"; return os;
+ case value_t::local_datetime : os << "local_datetime"; return os;
+ case value_t::local_date : os << "local_date"; return os;
+ case value_t::local_time : os << "local_time"; return os;
+ case value_t::array : os << "array"; return os;
+ case value_t::table : os << "table"; return os;
+ case value_t::empty : os << "empty"; return os;
+ default : os << "unknown"; return os;
+ }
+}
+
+template<typename charT = char,
+ typename traits = std::char_traits<charT>,
+ typename alloc = std::allocator<charT>>
+inline std::basic_string<charT, traits, alloc> stringize(value_t t)
+{
+ std::basic_ostringstream<charT, traits, alloc> oss;
+ oss << t;
+ return oss.str();
+}
+
+namespace detail
+{
+
+// helper to define a type that represents a value_t value.
+template<value_t V>
+using value_t_constant = std::integral_constant<value_t, V>;
+
+// meta-function that convertes from value_t to the exact toml type that corresponds to.
+// It takes toml::basic_value type because array and table types depend on it.
+template<value_t t, typename Value> struct enum_to_type {using type = void ;};
+template<typename Value> struct enum_to_type<value_t::empty , Value>{using type = void ;};
+template<typename Value> struct enum_to_type<value_t::boolean , Value>{using type = boolean ;};
+template<typename Value> struct enum_to_type<value_t::integer , Value>{using type = integer ;};
+template<typename Value> struct enum_to_type<value_t::floating , Value>{using type = floating ;};
+template<typename Value> struct enum_to_type<value_t::string , Value>{using type = string ;};
+template<typename Value> struct enum_to_type<value_t::offset_datetime, Value>{using type = offset_datetime ;};
+template<typename Value> struct enum_to_type<value_t::local_datetime , Value>{using type = local_datetime ;};
+template<typename Value> struct enum_to_type<value_t::local_date , Value>{using type = local_date ;};
+template<typename Value> struct enum_to_type<value_t::local_time , Value>{using type = local_time ;};
+template<typename Value> struct enum_to_type<value_t::array , Value>{using type = typename Value::array_type;};
+template<typename Value> struct enum_to_type<value_t::table , Value>{using type = typename Value::table_type;};
+
+// meta-function that converts from an exact toml type to the enum that corresponds to.
+template<typename T, typename Value>
+struct type_to_enum : std::conditional<
+ std::is_same<T, typename Value::array_type>::value, // if T == array_type,
+ value_t_constant<value_t::array>, // then value_t::array
+ typename std::conditional< // else...
+ std::is_same<T, typename Value::table_type>::value, // if T == table_type
+ value_t_constant<value_t::table>, // then value_t::table
+ value_t_constant<value_t::empty> // else value_t::empty
+ >::type
+ >::type {};
+template<typename Value> struct type_to_enum<boolean , Value>: value_t_constant<value_t::boolean > {};
+template<typename Value> struct type_to_enum<integer , Value>: value_t_constant<value_t::integer > {};
+template<typename Value> struct type_to_enum<floating , Value>: value_t_constant<value_t::floating > {};
+template<typename Value> struct type_to_enum<string , Value>: value_t_constant<value_t::string > {};
+template<typename Value> struct type_to_enum<offset_datetime, Value>: value_t_constant<value_t::offset_datetime> {};
+template<typename Value> struct type_to_enum<local_datetime , Value>: value_t_constant<value_t::local_datetime > {};
+template<typename Value> struct type_to_enum<local_date , Value>: value_t_constant<value_t::local_date > {};
+template<typename Value> struct type_to_enum<local_time , Value>: value_t_constant<value_t::local_time > {};
+
+// meta-function that checks the type T is the same as one of the toml::* types.
+template<typename T, typename Value>
+struct is_exact_toml_type : disjunction<
+ std::is_same<T, boolean >,
+ std::is_same<T, integer >,
+ std::is_same<T, floating >,
+ std::is_same<T, string >,
+ std::is_same<T, offset_datetime>,
+ std::is_same<T, local_datetime >,
+ std::is_same<T, local_date >,
+ std::is_same<T, local_time >,
+ std::is_same<T, typename Value::array_type>,
+ std::is_same<T, typename Value::table_type>
+ >{};
+template<typename T, typename V> struct is_exact_toml_type<T&, V> : is_exact_toml_type<T, V>{};
+template<typename T, typename V> struct is_exact_toml_type<T const&, V> : is_exact_toml_type<T, V>{};
+template<typename T, typename V> struct is_exact_toml_type<T volatile&, V> : is_exact_toml_type<T, V>{};
+template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>: is_exact_toml_type<T, V>{};
+
+} // detail
+} // toml
+
+#endif// TOML11_TYPES_H
diff --git a/src/toml11/toml/utility.hpp b/src/toml11/toml/utility.hpp
new file mode 100644
index 000000000..4a6b4309d
--- /dev/null
+++ b/src/toml11/toml/utility.hpp
@@ -0,0 +1,149 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_UTILITY_HPP
+#define TOML11_UTILITY_HPP
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "traits.hpp"
+
+#if __cplusplus >= 201402L
+# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
+#elif defined(__GNUC__)
+# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
+#elif defined(_MSC_VER)
+# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
+#else
+# define TOML11_MARK_AS_DEPRECATED
+#endif
+
+namespace toml
+{
+
+#if __cplusplus >= 201402L
+
+using std::make_unique;
+
+#else
+
+template<typename T, typename ... Ts>
+inline std::unique_ptr<T> make_unique(Ts&& ... args)
+{
+ return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
+}
+
+#endif // __cplusplus >= 2014
+
+namespace detail
+{
+template<typename Container>
+void try_reserve_impl(Container& container, std::size_t N, std::true_type)
+{
+ container.reserve(N);
+ return;
+}
+template<typename Container>
+void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
+{
+ return;
+}
+} // detail
+
+template<typename Container>
+void try_reserve(Container& container, std::size_t N)
+{
+ if(N <= container.size()) {return;}
+ detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
+ return;
+}
+
+namespace detail
+{
+inline std::string concat_to_string_impl(std::ostringstream& oss)
+{
+ return oss.str();
+}
+template<typename T, typename ... Ts>
+std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail)
+{
+ oss << std::forward<T>(head);
+ return concat_to_string_impl(oss, std::forward<Ts>(tail) ... );
+}
+} // detail
+
+template<typename ... Ts>
+std::string concat_to_string(Ts&& ... args)
+{
+ std::ostringstream oss;
+ oss << std::boolalpha << std::fixed;
+ return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
+}
+
+template<typename T>
+T from_string(const std::string& str, T opt)
+{
+ T v(opt);
+ std::istringstream iss(str);
+ iss >> v;
+ return v;
+}
+
+namespace detail
+{
+#if __cplusplus >= 201402L
+template<typename T>
+decltype(auto) last_one(T&& tail) noexcept
+{
+ return std::forward<T>(tail);
+}
+
+template<typename T, typename ... Ts>
+decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept
+{
+ return last_one(std::forward<Ts>(tail)...);
+}
+#else // C++11
+// The following code
+// ```cpp
+// 1 | template<typename T, typename ... Ts>
+// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
+// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
+// 4 | {
+// 5 | return last_one(std::forward<Ts>(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<typename T, typename ... Ts>
+struct last_one_in_pack
+{
+ using type = typename last_one_in_pack<Ts...>::type;
+};
+template<typename T>
+struct last_one_in_pack<T>
+{
+ using type = T;
+};
+template<typename ... Ts>
+using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type;
+
+template<typename T>
+T&& last_one(T&& tail) noexcept
+{
+ return std::forward<T>(tail);
+}
+template<typename T, typename ... Ts>
+enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>>
+last_one(T&& /*head*/, Ts&& ... tail)
+{
+ return last_one(std::forward<Ts>(tail)...);
+}
+
+#endif
+} // detail
+
+}// toml
+#endif // TOML11_UTILITY
diff --git a/src/toml11/toml/value.hpp b/src/toml11/toml/value.hpp
new file mode 100644
index 000000000..1b43db8d4
--- /dev/null
+++ b/src/toml11/toml/value.hpp
@@ -0,0 +1,2035 @@
+// Copyright Toru Niina 2017.
+// Distributed under the MIT License.
+#ifndef TOML11_VALUE_HPP
+#define TOML11_VALUE_HPP
+#include <cassert>
+
+#include "comments.hpp"
+#include "exception.hpp"
+#include "into.hpp"
+#include "region.hpp"
+#include "source_location.hpp"
+#include "storage.hpp"
+#include "traits.hpp"
+#include "types.hpp"
+#include "utility.hpp"
+
+namespace toml
+{
+
+namespace detail
+{
+
+// to show error messages. not recommended for users.
+template<typename Value>
+inline region_base const* get_region(const Value& v)
+{
+ return v.region_info_.get();
+}
+
+template<typename Value>
+void change_region(Value& v, region reg)
+{
+ v.region_info_ = std::make_shared<region>(std::move(reg));
+ return;
+}
+
+template<value_t Expected, typename Value>
+[[noreturn]] inline void
+throw_bad_cast(const std::string& funcname, value_t actual, const Value& v)
+{
+ throw type_error(detail::format_underline(
+ concat_to_string(funcname, "bad_cast to ", Expected), {
+ {v.location(), concat_to_string("the actual type is ", actual)}
+ }), v.location());
+}
+
+// Throw `out_of_range` from `toml::value::at()` and `toml::find()`
+// after generating an error message.
+//
+// The implementation is a bit complicated and there are many edge-cases.
+// If you are not interested in the error message generation, just skip this.
+template<typename Value>
+[[noreturn]] void
+throw_key_not_found_error(const Value& v, const key& ky)
+{
+ // The top-level table has its region at the first character of the file.
+ // That means that, in the case when a key is not found in the top-level
+ // table, the error message points to the first character. If the file has
+ // its first table at the first line, the error message would be like this.
+ // ```console
+ // [error] key "a" not found
+ // --> example.toml
+ // |
+ // 1 | [table]
+ // | ^------ in this table
+ // ```
+ // 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",
+ // 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() == 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
+ // `parse_toml_file` function.
+ // It also assumes that the top-level table size is just one and
+ // the line number is `1`. It is always satisfied. And those conditions
+ // are satisfied only if the table is the top-level table.
+ //
+ // 1. one-character dot-key at the first line
+ // ```toml
+ // a.b = "c"
+ // ```
+ // toml11 counts whole key as the table key. Here, `a.b` is the region
+ // of the table "a". It could be counter intuitive, but it works.
+ // The size of the region is 3, not 1. The above example is the shortest
+ // dot-key example. The size cannot be 1.
+ //
+ // 2. one-character inline-table at the first line
+ // ```toml
+ // a = {b = "c"}
+ // ```
+ // 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.
+ //
+ // 3. one-character table declaration at the first line
+ // ```toml
+ // [a]
+ // ```
+ // 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(
+ "key \"", ky, "\" not found in the top-level table"), {
+ {loc, "the top-level table starts here"}
+ }));
+ }
+ else
+ {
+ // normal table.
+ throw std::out_of_range(format_underline(concat_to_string(
+ "key \"", ky, "\" not found"), { {loc, "in this table"} }));
+ }
+}
+
+// switch by `value_t` at the compile time.
+template<value_t T>
+struct switch_cast {};
+#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \
+ template<> \
+ struct switch_cast<value_t::TYPE> \
+ { \
+ template<typename Value> \
+ static typename Value::TYPE##_type& invoke(Value& v) \
+ { \
+ return v.as_##TYPE(); \
+ } \
+ template<typename Value> \
+ static typename Value::TYPE##_type const& invoke(const Value& v) \
+ { \
+ return v.as_##TYPE(); \
+ } \
+ template<typename Value> \
+ static typename Value::TYPE##_type&& invoke(Value&& v) \
+ { \
+ return std::move(v).as_##TYPE(); \
+ } \
+ }; \
+ /**/
+TOML11_GENERATE_SWITCH_CASTER(boolean)
+TOML11_GENERATE_SWITCH_CASTER(integer)
+TOML11_GENERATE_SWITCH_CASTER(floating)
+TOML11_GENERATE_SWITCH_CASTER(string)
+TOML11_GENERATE_SWITCH_CASTER(offset_datetime)
+TOML11_GENERATE_SWITCH_CASTER(local_datetime)
+TOML11_GENERATE_SWITCH_CASTER(local_date)
+TOML11_GENERATE_SWITCH_CASTER(local_time)
+TOML11_GENERATE_SWITCH_CASTER(array)
+TOML11_GENERATE_SWITCH_CASTER(table)
+
+#undef TOML11_GENERATE_SWITCH_CASTER
+
+}// detail
+
+template<typename Comment, // discard/preserve_comment
+ template<typename ...> class Table = std::unordered_map,
+ template<typename ...> class Array = std::vector>
+class basic_value
+{
+ template<typename T, typename U>
+ static void assigner(T& dst, U&& v)
+ {
+ const auto tmp = ::new(std::addressof(dst)) T(std::forward<U>(v));
+ assert(tmp == std::addressof(dst));
+ (void)tmp;
+ }
+
+ using region_base = detail::region_base;
+
+ template<typename C, template<typename ...> class T,
+ template<typename ...> class A>
+ friend class basic_value;
+
+ public:
+
+ using comment_type = Comment;
+ using key_type = ::toml::key;
+ using value_type = basic_value<comment_type, Table, Array>;
+ using boolean_type = ::toml::boolean;
+ using integer_type = ::toml::integer;
+ using floating_type = ::toml::floating;
+ using string_type = ::toml::string;
+ using local_time_type = ::toml::local_time;
+ using local_date_type = ::toml::local_date;
+ using local_datetime_type = ::toml::local_datetime;
+ using offset_datetime_type = ::toml::offset_datetime;
+ using array_type = Array<value_type>;
+ using table_type = Table<key_type, value_type>;
+
+ public:
+
+ basic_value() noexcept
+ : type_(value_t::empty),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {}
+ ~basic_value() noexcept {this->cleanup();}
+
+ basic_value(const basic_value& v)
+ : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_)
+ {
+ switch(v.type())
+ {
+ case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
+ case value_t::integer : assigner(integer_ , v.integer_ ); break;
+ case value_t::floating : assigner(floating_ , v.floating_ ); break;
+ case value_t::string : assigner(string_ , v.string_ ); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
+ case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
+ case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
+ case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
+ case value_t::array : assigner(array_ , v.array_ ); break;
+ case value_t::table : assigner(table_ , v.table_ ); break;
+ default: break;
+ }
+ }
+ basic_value(basic_value&& v)
+ : type_(v.type()), region_info_(std::move(v.region_info_)),
+ comments_(std::move(v.comments_))
+ {
+ switch(this->type_) // here this->type_ is already initialized
+ {
+ case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break;
+ case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break;
+ case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break;
+ case value_t::string : assigner(string_ , std::move(v.string_ )); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break;
+ case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break;
+ case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break;
+ case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break;
+ case value_t::array : assigner(array_ , std::move(v.array_ )); break;
+ case value_t::table : assigner(table_ , std::move(v.table_ )); break;
+ default: break;
+ }
+ }
+ basic_value& operator=(const basic_value& v)
+ {
+ this->cleanup();
+ this->region_info_ = v.region_info_;
+ this->comments_ = v.comments_;
+ this->type_ = v.type();
+ switch(this->type_)
+ {
+ case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
+ case value_t::integer : assigner(integer_ , v.integer_ ); break;
+ case value_t::floating : assigner(floating_ , v.floating_ ); break;
+ case value_t::string : assigner(string_ , v.string_ ); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
+ case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
+ case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
+ case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
+ case value_t::array : assigner(array_ , v.array_ ); break;
+ case value_t::table : assigner(table_ , v.table_ ); break;
+ default: break;
+ }
+ return *this;
+ }
+ basic_value& operator=(basic_value&& v)
+ {
+ this->cleanup();
+ this->region_info_ = std::move(v.region_info_);
+ this->comments_ = std::move(v.comments_);
+ this->type_ = v.type();
+ switch(this->type_)
+ {
+ case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break;
+ case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break;
+ case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break;
+ case value_t::string : assigner(string_ , std::move(v.string_ )); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break;
+ case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break;
+ case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break;
+ case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break;
+ case value_t::array : assigner(array_ , std::move(v.array_ )); break;
+ case value_t::table : assigner(table_ , std::move(v.table_ )); break;
+ default: break;
+ }
+ return *this;
+ }
+
+ // overwrite comments ----------------------------------------------------
+
+ basic_value(const basic_value& v, std::vector<std::string> com)
+ : type_(v.type()), region_info_(v.region_info_),
+ comments_(std::move(com))
+ {
+ switch(v.type())
+ {
+ case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
+ case value_t::integer : assigner(integer_ , v.integer_ ); break;
+ case value_t::floating : assigner(floating_ , v.floating_ ); break;
+ case value_t::string : assigner(string_ , v.string_ ); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
+ case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
+ case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
+ case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
+ case value_t::array : assigner(array_ , v.array_ ); break;
+ case value_t::table : assigner(table_ , v.table_ ); break;
+ default: break;
+ }
+ }
+
+ basic_value(basic_value&& v, std::vector<std::string> com)
+ : type_(v.type()), region_info_(std::move(v.region_info_)),
+ comments_(std::move(com))
+ {
+ switch(this->type_) // here this->type_ is already initialized
+ {
+ case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break;
+ case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break;
+ case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break;
+ case value_t::string : assigner(string_ , std::move(v.string_ )); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break;
+ case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break;
+ case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break;
+ case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break;
+ case value_t::array : assigner(array_ , std::move(v.array_ )); break;
+ case value_t::table : assigner(table_ , std::move(v.table_ )); break;
+ default: break;
+ }
+ }
+
+ // -----------------------------------------------------------------------
+ // conversion between different basic_values.
+ template<typename C,
+ template<typename ...> class T,
+ template<typename ...> class A>
+ basic_value(const basic_value<C, T, A>& v)
+ : type_(v.type()), region_info_(v.region_info_), comments_(v.comments())
+ {
+ switch(v.type())
+ {
+ case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
+ case value_t::integer : assigner(integer_ , v.integer_ ); break;
+ case value_t::floating : assigner(floating_ , v.floating_ ); break;
+ case value_t::string : assigner(string_ , v.string_ ); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
+ case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
+ case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
+ case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
+ case value_t::array :
+ {
+ array_type tmp(v.as_array(std::nothrow).begin(),
+ v.as_array(std::nothrow).end());
+ assigner(array_, std::move(tmp));
+ break;
+ }
+ case value_t::table :
+ {
+ table_type tmp(v.as_table(std::nothrow).begin(),
+ v.as_table(std::nothrow).end());
+ assigner(table_, std::move(tmp));
+ break;
+ }
+ default: break;
+ }
+ }
+ template<typename C,
+ template<typename ...> class T,
+ template<typename ...> class A>
+ basic_value(const basic_value<C, T, A>& v, std::vector<std::string> com)
+ : type_(v.type()), region_info_(v.region_info_),
+ comments_(std::move(com))
+ {
+ switch(v.type())
+ {
+ case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
+ case value_t::integer : assigner(integer_ , v.integer_ ); break;
+ case value_t::floating : assigner(floating_ , v.floating_ ); break;
+ case value_t::string : assigner(string_ , v.string_ ); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
+ case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
+ case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
+ case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
+ case value_t::array :
+ {
+ array_type tmp(v.as_array(std::nothrow).begin(),
+ v.as_array(std::nothrow).end());
+ assigner(array_, std::move(tmp));
+ break;
+ }
+ case value_t::table :
+ {
+ table_type tmp(v.as_table(std::nothrow).begin(),
+ v.as_table(std::nothrow).end());
+ assigner(table_, std::move(tmp));
+ break;
+ }
+ default: break;
+ }
+ }
+ template<typename C,
+ template<typename ...> class T,
+ template<typename ...> class A>
+ basic_value& operator=(const basic_value<C, T, A>& v)
+ {
+ this->region_info_ = v.region_info_;
+ this->comments_ = comment_type(v.comments());
+ this->type_ = v.type();
+ switch(v.type())
+ {
+ case value_t::boolean : assigner(boolean_ , v.boolean_ ); break;
+ case value_t::integer : assigner(integer_ , v.integer_ ); break;
+ case value_t::floating : assigner(floating_ , v.floating_ ); break;
+ case value_t::string : assigner(string_ , v.string_ ); break;
+ case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break;
+ case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break;
+ case value_t::local_date : assigner(local_date_ , v.local_date_ ); break;
+ case value_t::local_time : assigner(local_time_ , v.local_time_ ); break;
+ case value_t::array :
+ {
+ array_type tmp(v.as_array(std::nothrow).begin(),
+ v.as_array(std::nothrow).end());
+ assigner(array_, std::move(tmp));
+ break;
+ }
+ case value_t::table :
+ {
+ table_type tmp(v.as_table(std::nothrow).begin(),
+ v.as_table(std::nothrow).end());
+ assigner(table_, std::move(tmp));
+ break;
+ }
+ default: break;
+ }
+ return *this;
+ }
+
+ // boolean ==============================================================
+
+ basic_value(boolean b)
+ : type_(value_t::boolean),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->boolean_, b);
+ }
+ basic_value& operator=(boolean b)
+ {
+ this->cleanup();
+ this->type_ = value_t::boolean;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->boolean_, b);
+ return *this;
+ }
+ basic_value(boolean b, std::vector<std::string> com)
+ : type_(value_t::boolean),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->boolean_, b);
+ }
+
+ // integer ==============================================================
+
+ template<typename T, typename std::enable_if<detail::conjunction<
+ std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value(T i)
+ : type_(value_t::integer),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->integer_, static_cast<integer>(i));
+ }
+
+ template<typename T, typename std::enable_if<detail::conjunction<
+ std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value& operator=(T i)
+ {
+ this->cleanup();
+ this->type_ = value_t::integer;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->integer_, static_cast<integer>(i));
+ return *this;
+ }
+
+ template<typename T, typename std::enable_if<detail::conjunction<
+ std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value(T i, std::vector<std::string> com)
+ : type_(value_t::integer),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->integer_, static_cast<integer>(i));
+ }
+
+ // floating =============================================================
+
+ template<typename T, typename std::enable_if<
+ std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value(T f)
+ : type_(value_t::floating),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->floating_, static_cast<floating>(f));
+ }
+
+
+ template<typename T, typename std::enable_if<
+ std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value& operator=(T f)
+ {
+ this->cleanup();
+ this->type_ = value_t::floating;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->floating_, static_cast<floating>(f));
+ return *this;
+ }
+
+ template<typename T, typename std::enable_if<
+ std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value(T f, std::vector<std::string> com)
+ : type_(value_t::floating),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->floating_, f);
+ }
+
+ // string ===============================================================
+
+ basic_value(toml::string s)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->string_, std::move(s));
+ }
+ basic_value& operator=(toml::string s)
+ {
+ this->cleanup();
+ this->type_ = value_t::string ;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->string_, s);
+ return *this;
+ }
+ basic_value(toml::string s, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, std::move(s));
+ }
+
+ basic_value(std::string s)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->string_, toml::string(std::move(s)));
+ }
+ basic_value& operator=(std::string s)
+ {
+ this->cleanup();
+ this->type_ = value_t::string ;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->string_, toml::string(std::move(s)));
+ return *this;
+ }
+ basic_value(std::string s, string_t kind)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->string_, toml::string(std::move(s), kind));
+ }
+ basic_value(std::string s, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, toml::string(std::move(s)));
+ }
+ basic_value(std::string s, string_t kind, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, toml::string(std::move(s), kind));
+ }
+
+ basic_value(const char* s)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->string_, toml::string(std::string(s)));
+ }
+ basic_value& operator=(const char* s)
+ {
+ this->cleanup();
+ this->type_ = value_t::string ;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->string_, toml::string(std::string(s)));
+ return *this;
+ }
+ basic_value(const char* s, string_t kind)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->string_, toml::string(std::string(s), kind));
+ }
+ basic_value(const char* s, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, toml::string(std::string(s)));
+ }
+ basic_value(const char* s, string_t kind, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, toml::string(std::string(s), kind));
+ }
+
+#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>(region_base{}))
+ {
+ assigner(this->string_, toml::string(s));
+ }
+ basic_value& operator=(std::string_view s)
+ {
+ this->cleanup();
+ this->type_ = value_t::string ;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->string_, toml::string(s));
+ return *this;
+ }
+ basic_value(std::string_view s, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, toml::string(s));
+ }
+ basic_value(std::string_view s, string_t kind)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->string_, toml::string(s, kind));
+ }
+ basic_value(std::string_view s, string_t kind, std::vector<std::string> com)
+ : type_(value_t::string),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->string_, toml::string(s, kind));
+ }
+#endif
+
+ // local date ===========================================================
+
+ basic_value(const local_date& ld)
+ : type_(value_t::local_date),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->local_date_, ld);
+ }
+ basic_value& operator=(const local_date& ld)
+ {
+ this->cleanup();
+ this->type_ = value_t::local_date;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->local_date_, ld);
+ return *this;
+ }
+ basic_value(const local_date& ld, std::vector<std::string> com)
+ : type_(value_t::local_date),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->local_date_, ld);
+ }
+
+ // local time ===========================================================
+
+ basic_value(const local_time& lt)
+ : type_(value_t::local_time),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->local_time_, lt);
+ }
+ basic_value(const local_time& lt, std::vector<std::string> com)
+ : type_(value_t::local_time),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->local_time_, lt);
+ }
+ basic_value& operator=(const local_time& lt)
+ {
+ this->cleanup();
+ this->type_ = value_t::local_time;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->local_time_, lt);
+ return *this;
+ }
+
+ template<typename Rep, typename Period>
+ basic_value(const std::chrono::duration<Rep, Period>& dur)
+ : type_(value_t::local_time),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->local_time_, local_time(dur));
+ }
+ template<typename Rep, typename Period>
+ basic_value(const std::chrono::duration<Rep, Period>& dur,
+ std::vector<std::string> com)
+ : type_(value_t::local_time),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->local_time_, local_time(dur));
+ }
+ template<typename Rep, typename Period>
+ basic_value& operator=(const std::chrono::duration<Rep, Period>& dur)
+ {
+ this->cleanup();
+ this->type_ = value_t::local_time;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->local_time_, local_time(dur));
+ return *this;
+ }
+
+ // local datetime =======================================================
+
+ basic_value(const local_datetime& ldt)
+ : type_(value_t::local_datetime),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->local_datetime_, ldt);
+ }
+ basic_value(const local_datetime& ldt, std::vector<std::string> com)
+ : type_(value_t::local_datetime),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->local_datetime_, ldt);
+ }
+ basic_value& operator=(const local_datetime& ldt)
+ {
+ this->cleanup();
+ this->type_ = value_t::local_datetime;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->local_datetime_, ldt);
+ return *this;
+ }
+
+ // offset datetime ======================================================
+
+ basic_value(const offset_datetime& odt)
+ : type_(value_t::offset_datetime),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->offset_datetime_, odt);
+ }
+ basic_value(const offset_datetime& odt, std::vector<std::string> com)
+ : type_(value_t::offset_datetime),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->offset_datetime_, odt);
+ }
+ basic_value& operator=(const offset_datetime& odt)
+ {
+ this->cleanup();
+ this->type_ = value_t::offset_datetime;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->offset_datetime_, odt);
+ return *this;
+ }
+ basic_value(const std::chrono::system_clock::time_point& tp)
+ : type_(value_t::offset_datetime),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->offset_datetime_, offset_datetime(tp));
+ }
+ basic_value(const std::chrono::system_clock::time_point& tp,
+ std::vector<std::string> com)
+ : type_(value_t::offset_datetime),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->offset_datetime_, offset_datetime(tp));
+ }
+ basic_value& operator=(const std::chrono::system_clock::time_point& tp)
+ {
+ this->cleanup();
+ this->type_ = value_t::offset_datetime;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->offset_datetime_, offset_datetime(tp));
+ return *this;
+ }
+
+ // array ================================================================
+
+ basic_value(const array_type& ary)
+ : type_(value_t::array),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->array_, ary);
+ }
+ basic_value(const array_type& ary, std::vector<std::string> com)
+ : type_(value_t::array),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->array_, ary);
+ }
+ basic_value& operator=(const array_type& ary)
+ {
+ this->cleanup();
+ this->type_ = value_t::array ;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->array_, ary);
+ return *this;
+ }
+
+ // array (initializer_list) ----------------------------------------------
+
+ template<typename T, typename std::enable_if<
+ std::is_convertible<T, value_type>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value(std::initializer_list<T> list)
+ : type_(value_t::array),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ array_type ary(list.begin(), list.end());
+ assigner(this->array_, std::move(ary));
+ }
+ template<typename T, typename std::enable_if<
+ std::is_convertible<T, value_type>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value(std::initializer_list<T> list, std::vector<std::string> com)
+ : type_(value_t::array),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ array_type ary(list.begin(), list.end());
+ assigner(this->array_, std::move(ary));
+ }
+ template<typename T, typename std::enable_if<
+ std::is_convertible<T, value_type>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value& operator=(std::initializer_list<T> list)
+ {
+ this->cleanup();
+ this->type_ = value_t::array;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+
+ array_type ary(list.begin(), list.end());
+ assigner(this->array_, std::move(ary));
+ return *this;
+ }
+
+ // array (STL Containers) ------------------------------------------------
+
+ template<typename T, typename std::enable_if<detail::conjunction<
+ detail::negation<std::is_same<T, array_type>>,
+ detail::is_container<T>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value(const T& list)
+ : type_(value_t::array),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ static_assert(std::is_convertible<typename T::value_type, value_type>::value,
+ "elements of a container should be convertible to toml::value");
+
+ array_type ary(list.size());
+ std::copy(list.begin(), list.end(), ary.begin());
+ assigner(this->array_, std::move(ary));
+ }
+ template<typename T, typename std::enable_if<detail::conjunction<
+ detail::negation<std::is_same<T, array_type>>,
+ detail::is_container<T>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value(const T& list, std::vector<std::string> com)
+ : type_(value_t::array),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ static_assert(std::is_convertible<typename T::value_type, value_type>::value,
+ "elements of a container should be convertible to toml::value");
+
+ array_type ary(list.size());
+ std::copy(list.begin(), list.end(), ary.begin());
+ assigner(this->array_, std::move(ary));
+ }
+ template<typename T, typename std::enable_if<detail::conjunction<
+ detail::negation<std::is_same<T, array_type>>,
+ detail::is_container<T>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value& operator=(const T& list)
+ {
+ static_assert(std::is_convertible<typename T::value_type, value_type>::value,
+ "elements of a container should be convertible to toml::value");
+
+ this->cleanup();
+ this->type_ = value_t::array;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+
+ array_type ary(list.size());
+ std::copy(list.begin(), list.end(), ary.begin());
+ assigner(this->array_, std::move(ary));
+ return *this;
+ }
+
+ // table ================================================================
+
+ basic_value(const table_type& tab)
+ : type_(value_t::table),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ assigner(this->table_, tab);
+ }
+ basic_value(const table_type& tab, std::vector<std::string> com)
+ : type_(value_t::table),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ assigner(this->table_, tab);
+ }
+ basic_value& operator=(const table_type& tab)
+ {
+ this->cleanup();
+ this->type_ = value_t::table;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+ assigner(this->table_, tab);
+ return *this;
+ }
+
+ // initializer-list ------------------------------------------------------
+
+ basic_value(std::initializer_list<std::pair<key, basic_value>> list)
+ : type_(value_t::table),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ table_type tab;
+ for(const auto& elem : list) {tab[elem.first] = elem.second;}
+ assigner(this->table_, std::move(tab));
+ }
+
+ basic_value(std::initializer_list<std::pair<key, basic_value>> list,
+ std::vector<std::string> com)
+ : type_(value_t::table),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ table_type tab;
+ for(const auto& elem : list) {tab[elem.first] = elem.second;}
+ assigner(this->table_, std::move(tab));
+ }
+ basic_value& operator=(std::initializer_list<std::pair<key, basic_value>> list)
+ {
+ this->cleanup();
+ this->type_ = value_t::table;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+
+ table_type tab;
+ for(const auto& elem : list) {tab[elem.first] = elem.second;}
+ assigner(this->table_, std::move(tab));
+ return *this;
+ }
+
+ // other table-like -----------------------------------------------------
+
+ template<typename Map, typename std::enable_if<detail::conjunction<
+ detail::negation<std::is_same<Map, table_type>>,
+ detail::is_map<Map>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value(const Map& mp)
+ : type_(value_t::table),
+ region_info_(std::make_shared<region_base>(region_base{}))
+ {
+ table_type tab;
+ for(const auto& elem : mp) {tab[elem.first] = elem.second;}
+ assigner(this->table_, std::move(tab));
+ }
+ template<typename Map, typename std::enable_if<detail::conjunction<
+ detail::negation<std::is_same<Map, table_type>>,
+ detail::is_map<Map>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value(const Map& mp, std::vector<std::string> com)
+ : type_(value_t::table),
+ region_info_(std::make_shared<region_base>(region_base{})),
+ comments_(std::move(com))
+ {
+ table_type tab;
+ for(const auto& elem : mp) {tab[elem.first] = elem.second;}
+ assigner(this->table_, std::move(tab));
+ }
+ template<typename Map, typename std::enable_if<detail::conjunction<
+ detail::negation<std::is_same<Map, table_type>>,
+ detail::is_map<Map>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value& operator=(const Map& mp)
+ {
+ this->cleanup();
+ this->type_ = value_t::table;
+ this->region_info_ = std::make_shared<region_base>(region_base{});
+
+ table_type tab;
+ for(const auto& elem : mp) {tab[elem.first] = elem.second;}
+ assigner(this->table_, std::move(tab));
+ return *this;
+ }
+
+ // user-defined =========================================================
+
+ // convert using into_toml() method -------------------------------------
+
+ template<typename T, typename std::enable_if<
+ detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value(const T& ud): basic_value(ud.into_toml()) {}
+
+ template<typename T, typename std::enable_if<
+ detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value(const T& ud, std::vector<std::string> com)
+ : basic_value(ud.into_toml(), std::move(com))
+ {}
+ template<typename T, typename std::enable_if<
+ detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value& operator=(const T& ud)
+ {
+ *this = ud.into_toml();
+ return *this;
+ }
+
+ // convert using into<T> struct -----------------------------------------
+
+ template<typename T, std::size_t S = sizeof(::toml::into<T>)>
+ basic_value(const T& ud): basic_value(::toml::into<T>::into_toml(ud)) {}
+ template<typename T, std::size_t S = sizeof(::toml::into<T>)>
+ basic_value(const T& ud, std::vector<std::string> com)
+ : basic_value(::toml::into<T>::into_toml(ud), std::move(com))
+ {}
+ template<typename T, std::size_t S = sizeof(::toml::into<T>)>
+ basic_value& operator=(const T& ud)
+ {
+ *this = ::toml::into<T>::into_toml(ud);
+ return *this;
+ }
+
+ // for internal use ------------------------------------------------------
+ //
+ // Those constructors take detail::region that contains parse result.
+
+ basic_value(boolean b, detail::region reg, std::vector<std::string> cm)
+ : type_(value_t::boolean),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->boolean_, b);
+ }
+ template<typename T, typename std::enable_if<
+ detail::conjunction<
+ std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
+ >::value, std::nullptr_t>::type = nullptr>
+ basic_value(T i, detail::region reg, std::vector<std::string> cm)
+ : type_(value_t::integer),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->integer_, static_cast<integer>(i));
+ }
+ template<typename T, typename std::enable_if<
+ std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
+ basic_value(T f, detail::region reg, std::vector<std::string> cm)
+ : type_(value_t::floating),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->floating_, static_cast<floating>(f));
+ }
+ basic_value(toml::string s, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::string),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->string_, std::move(s));
+ }
+ basic_value(const local_date& ld, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::local_date),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->local_date_, ld);
+ }
+ basic_value(const local_time& lt, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::local_time),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->local_time_, lt);
+ }
+ basic_value(const local_datetime& ldt, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::local_datetime),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->local_datetime_, ldt);
+ }
+ basic_value(const offset_datetime& odt, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::offset_datetime),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->offset_datetime_, odt);
+ }
+ basic_value(const array_type& ary, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::array),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->array_, ary);
+ }
+ basic_value(const table_type& tab, detail::region reg,
+ std::vector<std::string> cm)
+ : type_(value_t::table),
+ region_info_(std::make_shared<detail::region>(std::move(reg))),
+ comments_(std::move(cm))
+ {
+ assigner(this->table_, tab);
+ }
+
+ template<typename T, typename std::enable_if<
+ detail::is_exact_toml_type<T, value_type>::value,
+ std::nullptr_t>::type = nullptr>
+ basic_value(std::pair<T, detail::region> parse_result, std::vector<std::string> com)
+ : basic_value(std::move(parse_result.first),
+ std::move(parse_result.second),
+ std::move(com))
+ {}
+
+ // type checking and casting ============================================
+
+ template<typename T, typename std::enable_if<
+ detail::is_exact_toml_type<T, value_type>::value,
+ std::nullptr_t>::type = nullptr>
+ bool is() const noexcept
+ {
+ return detail::type_to_enum<T, value_type>::value == this->type_;
+ }
+ bool is(value_t t) const noexcept {return t == this->type_;}
+
+ bool is_uninitialized() const noexcept {return this->is(value_t::empty );}
+ bool is_boolean() const noexcept {return this->is(value_t::boolean );}
+ bool is_integer() const noexcept {return this->is(value_t::integer );}
+ bool is_floating() const noexcept {return this->is(value_t::floating );}
+ bool is_string() const noexcept {return this->is(value_t::string );}
+ bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);}
+ bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );}
+ bool is_local_date() const noexcept {return this->is(value_t::local_date );}
+ bool is_local_time() const noexcept {return this->is(value_t::local_time );}
+ bool is_array() const noexcept {return this->is(value_t::array );}
+ bool is_table() const noexcept {return this->is(value_t::table );}
+
+ value_t type() const noexcept {return type_;}
+
+ template<value_t T>
+ typename detail::enum_to_type<T, value_type>::type& cast() &
+ {
+ if(this->type_ != T)
+ {
+ detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this);
+ }
+ return detail::switch_cast<T>::invoke(*this);
+ }
+ template<value_t T>
+ typename detail::enum_to_type<T, value_type>::type const& cast() const&
+ {
+ if(this->type_ != T)
+ {
+ detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this);
+ }
+ return detail::switch_cast<T>::invoke(*this);
+ }
+ template<value_t T>
+ typename detail::enum_to_type<T, value_type>::type&& cast() &&
+ {
+ if(this->type_ != T)
+ {
+ detail::throw_bad_cast<T>("toml::value::cast: ", this->type_, *this);
+ }
+ return detail::switch_cast<T>::invoke(std::move(*this));
+ }
+
+ // ------------------------------------------------------------------------
+ // nothrow version
+
+ boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;}
+ integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;}
+ floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;}
+ string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;}
+ offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;}
+ local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;}
+ local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;}
+ local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;}
+ array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();}
+ table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();}
+
+ boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;}
+ integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;}
+ floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;}
+ string & as_string (const std::nothrow_t&) & noexcept {return this->string_;}
+ offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;}
+ local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;}
+ local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;}
+ local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;}
+ array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();}
+ table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();}
+
+ boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);}
+ integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);}
+ floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);}
+ string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);}
+ offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);}
+ local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);}
+ local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);}
+ local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);}
+ array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());}
+ table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());}
+
+ // ========================================================================
+ // throw version
+ // ------------------------------------------------------------------------
+ // const reference {{{
+
+ boolean const& as_boolean() const&
+ {
+ if(this->type_ != value_t::boolean)
+ {
+ detail::throw_bad_cast<value_t::boolean>(
+ "toml::value::as_boolean(): ", this->type_, *this);
+ }
+ return this->boolean_;
+ }
+ integer const& as_integer() const&
+ {
+ if(this->type_ != value_t::integer)
+ {
+ detail::throw_bad_cast<value_t::integer>(
+ "toml::value::as_integer(): ", this->type_, *this);
+ }
+ return this->integer_;
+ }
+ floating const& as_floating() const&
+ {
+ if(this->type_ != value_t::floating)
+ {
+ detail::throw_bad_cast<value_t::floating>(
+ "toml::value::as_floating(): ", this->type_, *this);
+ }
+ return this->floating_;
+ }
+ string const& as_string() const&
+ {
+ if(this->type_ != value_t::string)
+ {
+ detail::throw_bad_cast<value_t::string>(
+ "toml::value::as_string(): ", this->type_, *this);
+ }
+ return this->string_;
+ }
+ offset_datetime const& as_offset_datetime() const&
+ {
+ if(this->type_ != value_t::offset_datetime)
+ {
+ detail::throw_bad_cast<value_t::offset_datetime>(
+ "toml::value::as_offset_datetime(): ", this->type_, *this);
+ }
+ return this->offset_datetime_;
+ }
+ local_datetime const& as_local_datetime() const&
+ {
+ if(this->type_ != value_t::local_datetime)
+ {
+ detail::throw_bad_cast<value_t::local_datetime>(
+ "toml::value::as_local_datetime(): ", this->type_, *this);
+ }
+ return this->local_datetime_;
+ }
+ local_date const& as_local_date() const&
+ {
+ if(this->type_ != value_t::local_date)
+ {
+ detail::throw_bad_cast<value_t::local_date>(
+ "toml::value::as_local_date(): ", this->type_, *this);
+ }
+ return this->local_date_;
+ }
+ local_time const& as_local_time() const&
+ {
+ if(this->type_ != value_t::local_time)
+ {
+ detail::throw_bad_cast<value_t::local_time>(
+ "toml::value::as_local_time(): ", this->type_, *this);
+ }
+ return this->local_time_;
+ }
+ array_type const& as_array() const&
+ {
+ if(this->type_ != value_t::array)
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::as_array(): ", this->type_, *this);
+ }
+ return this->array_.value();
+ }
+ table_type const& as_table() const&
+ {
+ if(this->type_ != value_t::table)
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::as_table(): ", this->type_, *this);
+ }
+ return this->table_.value();
+ }
+ // }}}
+ // ------------------------------------------------------------------------
+ // nonconst reference {{{
+
+ boolean & as_boolean() &
+ {
+ if(this->type_ != value_t::boolean)
+ {
+ detail::throw_bad_cast<value_t::boolean>(
+ "toml::value::as_boolean(): ", this->type_, *this);
+ }
+ return this->boolean_;
+ }
+ integer & as_integer() &
+ {
+ if(this->type_ != value_t::integer)
+ {
+ detail::throw_bad_cast<value_t::integer>(
+ "toml::value::as_integer(): ", this->type_, *this);
+ }
+ return this->integer_;
+ }
+ floating & as_floating() &
+ {
+ if(this->type_ != value_t::floating)
+ {
+ detail::throw_bad_cast<value_t::floating>(
+ "toml::value::as_floating(): ", this->type_, *this);
+ }
+ return this->floating_;
+ }
+ string & as_string() &
+ {
+ if(this->type_ != value_t::string)
+ {
+ detail::throw_bad_cast<value_t::string>(
+ "toml::value::as_string(): ", this->type_, *this);
+ }
+ return this->string_;
+ }
+ offset_datetime & as_offset_datetime() &
+ {
+ if(this->type_ != value_t::offset_datetime)
+ {
+ detail::throw_bad_cast<value_t::offset_datetime>(
+ "toml::value::as_offset_datetime(): ", this->type_, *this);
+ }
+ return this->offset_datetime_;
+ }
+ local_datetime & as_local_datetime() &
+ {
+ if(this->type_ != value_t::local_datetime)
+ {
+ detail::throw_bad_cast<value_t::local_datetime>(
+ "toml::value::as_local_datetime(): ", this->type_, *this);
+ }
+ return this->local_datetime_;
+ }
+ local_date & as_local_date() &
+ {
+ if(this->type_ != value_t::local_date)
+ {
+ detail::throw_bad_cast<value_t::local_date>(
+ "toml::value::as_local_date(): ", this->type_, *this);
+ }
+ return this->local_date_;
+ }
+ local_time & as_local_time() &
+ {
+ if(this->type_ != value_t::local_time)
+ {
+ detail::throw_bad_cast<value_t::local_time>(
+ "toml::value::as_local_time(): ", this->type_, *this);
+ }
+ return this->local_time_;
+ }
+ array_type & as_array() &
+ {
+ if(this->type_ != value_t::array)
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::as_array(): ", this->type_, *this);
+ }
+ return this->array_.value();
+ }
+ table_type & as_table() &
+ {
+ if(this->type_ != value_t::table)
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::as_table(): ", this->type_, *this);
+ }
+ return this->table_.value();
+ }
+
+ // }}}
+ // ------------------------------------------------------------------------
+ // rvalue reference {{{
+
+ boolean && as_boolean() &&
+ {
+ if(this->type_ != value_t::boolean)
+ {
+ detail::throw_bad_cast<value_t::boolean>(
+ "toml::value::as_boolean(): ", this->type_, *this);
+ }
+ return std::move(this->boolean_);
+ }
+ integer && as_integer() &&
+ {
+ if(this->type_ != value_t::integer)
+ {
+ detail::throw_bad_cast<value_t::integer>(
+ "toml::value::as_integer(): ", this->type_, *this);
+ }
+ return std::move(this->integer_);
+ }
+ floating && as_floating() &&
+ {
+ if(this->type_ != value_t::floating)
+ {
+ detail::throw_bad_cast<value_t::floating>(
+ "toml::value::as_floating(): ", this->type_, *this);
+ }
+ return std::move(this->floating_);
+ }
+ string && as_string() &&
+ {
+ if(this->type_ != value_t::string)
+ {
+ detail::throw_bad_cast<value_t::string>(
+ "toml::value::as_string(): ", this->type_, *this);
+ }
+ return std::move(this->string_);
+ }
+ offset_datetime && as_offset_datetime() &&
+ {
+ if(this->type_ != value_t::offset_datetime)
+ {
+ detail::throw_bad_cast<value_t::offset_datetime>(
+ "toml::value::as_offset_datetime(): ", this->type_, *this);
+ }
+ return std::move(this->offset_datetime_);
+ }
+ local_datetime && as_local_datetime() &&
+ {
+ if(this->type_ != value_t::local_datetime)
+ {
+ detail::throw_bad_cast<value_t::local_datetime>(
+ "toml::value::as_local_datetime(): ", this->type_, *this);
+ }
+ return std::move(this->local_datetime_);
+ }
+ local_date && as_local_date() &&
+ {
+ if(this->type_ != value_t::local_date)
+ {
+ detail::throw_bad_cast<value_t::local_date>(
+ "toml::value::as_local_date(): ", this->type_, *this);
+ }
+ return std::move(this->local_date_);
+ }
+ local_time && as_local_time() &&
+ {
+ if(this->type_ != value_t::local_time)
+ {
+ detail::throw_bad_cast<value_t::local_time>(
+ "toml::value::as_local_time(): ", this->type_, *this);
+ }
+ return std::move(this->local_time_);
+ }
+ array_type && as_array() &&
+ {
+ if(this->type_ != value_t::array)
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::as_array(): ", this->type_, *this);
+ }
+ return std::move(this->array_.value());
+ }
+ table_type && as_table() &&
+ {
+ if(this->type_ != value_t::table)
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::as_table(): ", this->type_, *this);
+ }
+ return std::move(this->table_.value());
+ }
+ // }}}
+
+ // accessors =============================================================
+ //
+ // may throw type_error or out_of_range
+ //
+ value_type& at(const key& k)
+ {
+ if(!this->is_table())
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::at(key): ", this->type_, *this);
+ }
+ if(this->as_table(std::nothrow).count(k) == 0)
+ {
+ detail::throw_key_not_found_error(*this, k);
+ }
+ return this->as_table(std::nothrow).at(k);
+ }
+ value_type const& at(const key& k) const
+ {
+ if(!this->is_table())
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::at(key): ", this->type_, *this);
+ }
+ if(this->as_table(std::nothrow).count(k) == 0)
+ {
+ detail::throw_key_not_found_error(*this, k);
+ }
+ return this->as_table(std::nothrow).at(k);
+ }
+ value_type& operator[](const key& k)
+ {
+ if(this->is_uninitialized())
+ {
+ *this = table_type{};
+ }
+ else if(!this->is_table()) // initialized, but not a table
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::operator[](key): ", this->type_, *this);
+ }
+ return this->as_table(std::nothrow)[k];
+ }
+
+ value_type& at(const std::size_t idx)
+ {
+ if(!this->is_array())
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::at(idx): ", this->type_, *this);
+ }
+ if(this->as_array(std::nothrow).size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(
+ "toml::value::at(idx): no element corresponding to the index", {
+ {this->location(), concat_to_string("the length is ",
+ this->as_array(std::nothrow).size(),
+ ", and the specified index is ", idx)}
+ }));
+ }
+ return this->as_array().at(idx);
+ }
+ value_type const& at(const std::size_t idx) const
+ {
+ if(!this->is_array())
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::at(idx): ", this->type_, *this);
+ }
+ if(this->as_array(std::nothrow).size() <= idx)
+ {
+ throw std::out_of_range(detail::format_underline(
+ "toml::value::at(idx): no element corresponding to the index", {
+ {this->location(), concat_to_string("the length is ",
+ this->as_array(std::nothrow).size(),
+ ", and the specified index is ", idx)}
+ }));
+ }
+ return this->as_array(std::nothrow).at(idx);
+ }
+
+ value_type& operator[](const std::size_t idx) noexcept
+ {
+ // no check...
+ return this->as_array(std::nothrow)[idx];
+ }
+ value_type const& operator[](const std::size_t idx) const noexcept
+ {
+ // no check...
+ return this->as_array(std::nothrow)[idx];
+ }
+
+ void push_back(const value_type& x)
+ {
+ if(!this->is_array())
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::push_back(value): ", this->type_, *this);
+ }
+ this->as_array(std::nothrow).push_back(x);
+ return;
+ }
+ void push_back(value_type&& x)
+ {
+ if(!this->is_array())
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::push_back(value): ", this->type_, *this);
+ }
+ this->as_array(std::nothrow).push_back(std::move(x));
+ return;
+ }
+
+ template<typename ... Ts>
+ value_type& emplace_back(Ts&& ... args)
+ {
+ if(!this->is_array())
+ {
+ detail::throw_bad_cast<value_t::array>(
+ "toml::value::emplace_back(...): ", this->type_, *this);
+ }
+ this->as_array(std::nothrow).emplace_back(std::forward<Ts>(args) ...);
+ return this->as_array(std::nothrow).back();
+ }
+
+ std::size_t size() const
+ {
+ switch(this->type_)
+ {
+ case value_t::array:
+ {
+ return this->as_array(std::nothrow).size();
+ }
+ case value_t::table:
+ {
+ return this->as_table(std::nothrow).size();
+ }
+ case value_t::string:
+ {
+ return this->as_string(std::nothrow).str.size();
+ }
+ default:
+ {
+ throw type_error(detail::format_underline(
+ "toml::value::size(): bad_cast to container types", {
+ {this->location(),
+ concat_to_string("the actual type is ", this->type_)}
+ }), this->location());
+ }
+ }
+ }
+
+ std::size_t count(const key_type& k) const
+ {
+ if(!this->is_table())
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::count(key): ", this->type_, *this);
+ }
+ return this->as_table(std::nothrow).count(k);
+ }
+
+ bool contains(const key_type& k) const
+ {
+ if(!this->is_table())
+ {
+ detail::throw_bad_cast<value_t::table>(
+ "toml::value::contains(key): ", this->type_, *this);
+ }
+ return (this->as_table(std::nothrow).count(k) != 0);
+ }
+
+ source_location location() const
+ {
+ return source_location(this->region_info_.get());
+ }
+
+ comment_type const& comments() const noexcept {return this->comments_;}
+ comment_type& comments() noexcept {return this->comments_;}
+
+ private:
+
+ void cleanup() noexcept
+ {
+ 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;}
+ default : return;
+ }
+ }
+
+ // for error messages
+ template<typename Value>
+ friend region_base const* detail::get_region(const Value& v);
+
+ template<typename Value>
+ friend void detail::change_region(Value& v, detail::region reg);
+
+ private:
+
+ using array_storage = detail::storage<array_type>;
+ using table_storage = detail::storage<table_type>;
+
+ value_t type_;
+ union
+ {
+ boolean boolean_;
+ integer integer_;
+ floating floating_;
+ string string_;
+ offset_datetime offset_datetime_;
+ local_datetime local_datetime_;
+ local_date local_date_;
+ local_time local_time_;
+ array_storage array_;
+ table_storage table_;
+ };
+ std::shared_ptr<region_base> region_info_;
+ comment_type comments_;
+};
+
+// default toml::value and default array/table.
+// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp
+using value = basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
+using array = typename value::array_type;
+using table = typename value::table_type;
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+inline bool
+operator==(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
+{
+ if(lhs.type() != rhs.type()) {return false;}
+ if(lhs.comments() != rhs.comments()) {return false;}
+
+ switch(lhs.type())
+ {
+ case value_t::boolean :
+ {
+ return lhs.as_boolean() == rhs.as_boolean();
+ }
+ case value_t::integer :
+ {
+ return lhs.as_integer() == rhs.as_integer();
+ }
+ case value_t::floating :
+ {
+ return lhs.as_floating() == rhs.as_floating();
+ }
+ case value_t::string :
+ {
+ return lhs.as_string() == rhs.as_string();
+ }
+ case value_t::offset_datetime:
+ {
+ return lhs.as_offset_datetime() == rhs.as_offset_datetime();
+ }
+ case value_t::local_datetime:
+ {
+ return lhs.as_local_datetime() == rhs.as_local_datetime();
+ }
+ case value_t::local_date:
+ {
+ return lhs.as_local_date() == rhs.as_local_date();
+ }
+ case value_t::local_time:
+ {
+ return lhs.as_local_time() == rhs.as_local_time();
+ }
+ case value_t::array :
+ {
+ return lhs.as_array() == rhs.as_array();
+ }
+ case value_t::table :
+ {
+ return lhs.as_table() == rhs.as_table();
+ }
+ case value_t::empty : {return true; }
+ default: {return false;}
+ }
+}
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+inline bool operator!=(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+typename std::enable_if<detail::conjunction<
+ detail::is_comparable<typename basic_value<C, T, A>::array_type>,
+ detail::is_comparable<typename basic_value<C, T, A>::table_type>
+ >::value, bool>::type
+operator<(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
+{
+ if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());}
+ switch(lhs.type())
+ {
+ case value_t::boolean :
+ {
+ return lhs.as_boolean() < rhs.as_boolean() ||
+ (lhs.as_boolean() == rhs.as_boolean() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::integer :
+ {
+ return lhs.as_integer() < rhs.as_integer() ||
+ (lhs.as_integer() == rhs.as_integer() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::floating :
+ {
+ return lhs.as_floating() < rhs.as_floating() ||
+ (lhs.as_floating() == rhs.as_floating() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::string :
+ {
+ return lhs.as_string() < rhs.as_string() ||
+ (lhs.as_string() == rhs.as_string() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::offset_datetime:
+ {
+ return lhs.as_offset_datetime() < rhs.as_offset_datetime() ||
+ (lhs.as_offset_datetime() == rhs.as_offset_datetime() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::local_datetime:
+ {
+ return lhs.as_local_datetime() < rhs.as_local_datetime() ||
+ (lhs.as_local_datetime() == rhs.as_local_datetime() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::local_date:
+ {
+ return lhs.as_local_date() < rhs.as_local_date() ||
+ (lhs.as_local_date() == rhs.as_local_date() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::local_time:
+ {
+ return lhs.as_local_time() < rhs.as_local_time() ||
+ (lhs.as_local_time() == rhs.as_local_time() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::array :
+ {
+ return lhs.as_array() < rhs.as_array() ||
+ (lhs.as_array() == rhs.as_array() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::table :
+ {
+ return lhs.as_table() < rhs.as_table() ||
+ (lhs.as_table() == rhs.as_table() &&
+ lhs.comments() < rhs.comments());
+ }
+ case value_t::empty :
+ {
+ return lhs.comments() < rhs.comments();
+ }
+ default:
+ {
+ return lhs.comments() < rhs.comments();
+ }
+ }
+}
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+typename std::enable_if<detail::conjunction<
+ detail::is_comparable<typename basic_value<C, T, A>::array_type>,
+ detail::is_comparable<typename basic_value<C, T, A>::table_type>
+ >::value, bool>::type
+operator<=(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
+{
+ return (lhs < rhs) || (lhs == rhs);
+}
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+typename std::enable_if<detail::conjunction<
+ detail::is_comparable<typename basic_value<C, T, A>::array_type>,
+ detail::is_comparable<typename basic_value<C, T, A>::table_type>
+ >::value, bool>::type
+operator>(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
+{
+ return !(lhs <= rhs);
+}
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+typename std::enable_if<detail::conjunction<
+ detail::is_comparable<typename basic_value<C, T, A>::array_type>,
+ detail::is_comparable<typename basic_value<C, T, A>::table_type>
+ >::value, bool>::type
+operator>=(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
+{
+ return !(lhs < rhs);
+}
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+inline std::string format_error(const std::string& err_msg,
+ const basic_value<C, T, A>& v, const std::string& comment,
+ std::vector<std::string> hints = {},
+ const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
+{
+ return detail::format_underline(err_msg, {{v.location(), comment}},
+ std::move(hints), colorize);
+}
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+inline std::string format_error(const std::string& err_msg,
+ const toml::basic_value<C, T, A>& v1, const std::string& comment1,
+ const toml::basic_value<C, T, A>& v2, const std::string& comment2,
+ std::vector<std::string> hints = {},
+ const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
+{
+ return detail::format_underline(err_msg, {
+ {v1.location(), comment1}, {v2.location(), comment2}
+ }, std::move(hints), colorize);
+}
+
+template<typename C, template<typename ...> class T, template<typename ...> class A>
+inline std::string format_error(const std::string& err_msg,
+ const toml::basic_value<C, T, A>& v1, const std::string& comment1,
+ const toml::basic_value<C, T, A>& v2, const std::string& comment2,
+ const toml::basic_value<C, T, A>& v3, const std::string& comment3,
+ std::vector<std::string> hints = {},
+ const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
+{
+ return detail::format_underline(err_msg, {{v1.location(), comment1},
+ {v2.location(), comment2}, {v3.location(), comment3}
+ }, std::move(hints), colorize);
+}
+
+template<typename Visitor, typename C,
+ template<typename ...> class T, template<typename ...> class A>
+detail::return_type_of_t<Visitor, const toml::boolean&>
+visit(Visitor&& visitor, const toml::basic_value<C, T, A>& v)
+{
+ switch(v.type())
+ {
+ case value_t::boolean : {return visitor(v.as_boolean ());}
+ case value_t::integer : {return visitor(v.as_integer ());}
+ case value_t::floating : {return visitor(v.as_floating ());}
+ case value_t::string : {return visitor(v.as_string ());}
+ case value_t::offset_datetime: {return visitor(v.as_offset_datetime());}
+ case value_t::local_datetime : {return visitor(v.as_local_datetime ());}
+ case value_t::local_date : {return visitor(v.as_local_date ());}
+ case value_t::local_time : {return visitor(v.as_local_time ());}
+ case value_t::array : {return visitor(v.as_array ());}
+ case value_t::table : {return visitor(v.as_table ());}
+ case value_t::empty : break;
+ default: break;
+ }
+ throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
+ "does not have any valid basic_value.", v, "here"));
+}
+
+template<typename Visitor, typename C,
+ template<typename ...> class T, template<typename ...> class A>
+detail::return_type_of_t<Visitor, toml::boolean&>
+visit(Visitor&& visitor, toml::basic_value<C, T, A>& v)
+{
+ switch(v.type())
+ {
+ case value_t::boolean : {return visitor(v.as_boolean ());}
+ case value_t::integer : {return visitor(v.as_integer ());}
+ case value_t::floating : {return visitor(v.as_floating ());}
+ case value_t::string : {return visitor(v.as_string ());}
+ case value_t::offset_datetime: {return visitor(v.as_offset_datetime());}
+ case value_t::local_datetime : {return visitor(v.as_local_datetime ());}
+ case value_t::local_date : {return visitor(v.as_local_date ());}
+ case value_t::local_time : {return visitor(v.as_local_time ());}
+ case value_t::array : {return visitor(v.as_array ());}
+ case value_t::table : {return visitor(v.as_table ());}
+ case value_t::empty : break;
+ default: break;
+ }
+ throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
+ "does not have any valid basic_value.", v, "here"));
+}
+
+template<typename Visitor, typename C,
+ template<typename ...> class T, template<typename ...> class A>
+detail::return_type_of_t<Visitor, toml::boolean&&>
+visit(Visitor&& visitor, toml::basic_value<C, T, A>&& v)
+{
+ switch(v.type())
+ {
+ case value_t::boolean : {return visitor(std::move(v.as_boolean ()));}
+ case value_t::integer : {return visitor(std::move(v.as_integer ()));}
+ case value_t::floating : {return visitor(std::move(v.as_floating ()));}
+ case value_t::string : {return visitor(std::move(v.as_string ()));}
+ case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));}
+ case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));}
+ case value_t::local_date : {return visitor(std::move(v.as_local_date ()));}
+ case value_t::local_time : {return visitor(std::move(v.as_local_time ()));}
+ case value_t::array : {return visitor(std::move(v.as_array ()));}
+ case value_t::table : {return visitor(std::move(v.as_table ()));}
+ case value_t::empty : break;
+ default: break;
+ }
+ throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
+ "does not have any valid basic_value.", v, "here"));
+}
+
+}// toml
+#endif// TOML11_VALUE