bump cpptoml to v0.1.1
This commit is contained in:
		
							parent
							
								
									22f2744afd
								
							
						
					
					
						commit
						abdedcdb38
					
				
					 1 changed files with 392 additions and 181 deletions
				
			
		| 
						 | 
					@ -4,8 +4,8 @@
 | 
				
			||||||
 * @date May 2013
 | 
					 * @date May 2013
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef _CPPTOML_H_
 | 
					#ifndef CPPTOML_H
 | 
				
			||||||
#define _CPPTOML_H_
 | 
					#define CPPTOML_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
#include <cassert>
 | 
					#include <cassert>
 | 
				
			||||||
| 
						 | 
					@ -84,11 +84,12 @@ class option
 | 
				
			||||||
        return &value_;
 | 
					        return &value_;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const T& value_or(const T& alternative) const
 | 
					    template <class U>
 | 
				
			||||||
 | 
					    T value_or(U&& alternative) const
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (!empty_)
 | 
					        if (!empty_)
 | 
				
			||||||
            return value_;
 | 
					            return value_;
 | 
				
			||||||
        return alternative;
 | 
					        return static_cast<T>(std::forward<U>(alternative));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private:
 | 
					  private:
 | 
				
			||||||
| 
						 | 
					@ -295,12 +296,11 @@ struct valid_value_or_string_convertible
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
struct value_traits<T, typename std::
 | 
					struct value_traits<T, typename std::enable_if<
 | 
				
			||||||
                           enable_if<valid_value_or_string_convertible<T>::
 | 
					                           valid_value_or_string_convertible<T>::value>::type>
 | 
				
			||||||
                                         value>::type>
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using value_type = typename std::
 | 
					    using value_type = typename std::conditional<
 | 
				
			||||||
        conditional<valid_value<typename std::decay<T>::type>::value,
 | 
					        valid_value<typename std::decay<T>::type>::value,
 | 
				
			||||||
        typename std::decay<T>::type, std::string>::type;
 | 
					        typename std::decay<T>::type, std::string>::type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    using type = value<value_type>;
 | 
					    using type = value<value_type>;
 | 
				
			||||||
| 
						 | 
					@ -312,12 +312,11 @@ struct value_traits<T, typename std::
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
struct value_traits<T,
 | 
					struct value_traits<
 | 
				
			||||||
                    typename std::
 | 
					    T,
 | 
				
			||||||
                        enable_if<!valid_value_or_string_convertible<T>::value
 | 
					    typename std::enable_if<
 | 
				
			||||||
                                  && std::is_floating_point<
 | 
					        !valid_value_or_string_convertible<T>::value
 | 
				
			||||||
                                         typename std::decay<T>::type>::value>::
 | 
					        && std::is_floating_point<typename std::decay<T>::type>::value>::type>
 | 
				
			||||||
                            type>
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using value_type = typename std::decay<T>::type;
 | 
					    using value_type = typename std::decay<T>::type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -330,11 +329,11 @@ struct value_traits<T,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
struct value_traits<T,
 | 
					struct value_traits<
 | 
				
			||||||
                    typename std::
 | 
					    T, typename std::enable_if<
 | 
				
			||||||
                        enable_if<!valid_value_or_string_convertible<T>::value
 | 
					           !valid_value_or_string_convertible<T>::value
 | 
				
			||||||
                                  && std::is_signed<typename std::decay<T>::
 | 
					           && !std::is_floating_point<typename std::decay<T>::type>::value
 | 
				
			||||||
                                                        type>::value>::type>
 | 
					           && std::is_signed<typename std::decay<T>::type>::value>::type>
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using value_type = int64_t;
 | 
					    using value_type = int64_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -356,11 +355,10 @@ struct value_traits<T,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
struct value_traits<T,
 | 
					struct value_traits<
 | 
				
			||||||
                    typename std::
 | 
					    T, typename std::enable_if<
 | 
				
			||||||
                        enable_if<!valid_value_or_string_convertible<T>::value
 | 
					           !valid_value_or_string_convertible<T>::value
 | 
				
			||||||
                                  && std::is_unsigned<typename std::decay<T>::
 | 
					           && std::is_unsigned<typename std::decay<T>::type>::value>::type>
 | 
				
			||||||
                                                          type>::value>::type>
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using value_type = int64_t;
 | 
					    using value_type = int64_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -395,10 +393,15 @@ struct array_of_trait<array>
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
 | 
					inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
 | 
				
			||||||
inline std::shared_ptr<array> make_array();
 | 
					inline std::shared_ptr<array> make_array();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace detail
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
inline std::shared_ptr<T> make_element();
 | 
					inline std::shared_ptr<T> make_element();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline std::shared_ptr<table> make_table();
 | 
					inline std::shared_ptr<table> make_table();
 | 
				
			||||||
inline std::shared_ptr<table_array> make_table_array();
 | 
					inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(CPPTOML_NO_RTTI)
 | 
					#if defined(CPPTOML_NO_RTTI)
 | 
				
			||||||
/// Base type used to store underlying data type explicitly if RTTI is disabled
 | 
					/// Base type used to store underlying data type explicitly if RTTI is disabled
 | 
				
			||||||
| 
						 | 
					@ -698,7 +701,7 @@ inline std::shared_ptr<value<double>> base::as()
 | 
				
			||||||
    if (type() == base_type::INT)
 | 
					    if (type() == base_type::INT)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
 | 
					        auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());
 | 
				
			||||||
        return make_value<double>(static_cast<double>(v->get()));;
 | 
					        return make_value<double>(static_cast<double>(v->get()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
 | 
					    if (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))
 | 
				
			||||||
| 
						 | 
					@ -731,7 +734,8 @@ inline std::shared_ptr<const value<double>> base::as() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#if defined(CPPTOML_NO_RTTI)
 | 
					#if defined(CPPTOML_NO_RTTI)
 | 
				
			||||||
    if (type() == base_type::FLOAT)
 | 
					    if (type() == base_type::FLOAT)
 | 
				
			||||||
        return std::static_pointer_cast<const value<double>>(shared_from_this());
 | 
					        return std::static_pointer_cast<const value<double>>(
 | 
				
			||||||
 | 
					            shared_from_this());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (type() == base_type::INT)
 | 
					    if (type() == base_type::INT)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -1027,11 +1031,14 @@ inline std::shared_ptr<array> make_array()
 | 
				
			||||||
    return std::make_shared<make_shared_enabler>();
 | 
					    return std::make_shared<make_shared_enabler>();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace detail
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
template <>
 | 
					template <>
 | 
				
			||||||
inline std::shared_ptr<array> make_element<array>()
 | 
					inline std::shared_ptr<array> make_element<array>()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return make_array();
 | 
					    return make_array();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					} // namespace detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Obtains a option<vector<T>>. The option will be empty if the array
 | 
					 * Obtains a option<vector<T>>. The option will be empty if the array
 | 
				
			||||||
| 
						 | 
					@ -1060,7 +1067,7 @@ class table;
 | 
				
			||||||
class table_array : public base
 | 
					class table_array : public base
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    friend class table;
 | 
					    friend class table;
 | 
				
			||||||
    friend std::shared_ptr<table_array> make_table_array();
 | 
					    friend std::shared_ptr<table_array> make_table_array(bool);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public:
 | 
					  public:
 | 
				
			||||||
    std::shared_ptr<base> clone() const override;
 | 
					    std::shared_ptr<base> clone() const override;
 | 
				
			||||||
| 
						 | 
					@ -1152,14 +1159,25 @@ class table_array : public base
 | 
				
			||||||
        array_.reserve(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:
 | 
					  private:
 | 
				
			||||||
#if defined(CPPTOML_NO_RTTI)
 | 
					#if defined(CPPTOML_NO_RTTI)
 | 
				
			||||||
    table_array() : base(base_type::TABLE_ARRAY)
 | 
					    table_array(bool is_inline = false)
 | 
				
			||||||
 | 
					        : base(base_type::TABLE_ARRAY), is_inline_(is_inline)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // nothing
 | 
					        // nothing
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    table_array()
 | 
					    table_array(bool is_inline = false) : is_inline_(is_inline)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // nothing
 | 
					        // nothing
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -1169,26 +1187,30 @@ class table_array : public base
 | 
				
			||||||
    table_array& operator=(const table_array& rhs) = delete;
 | 
					    table_array& operator=(const table_array& rhs) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<std::shared_ptr<table>> array_;
 | 
					    std::vector<std::shared_ptr<table>> array_;
 | 
				
			||||||
 | 
					    const bool is_inline_ = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline std::shared_ptr<table_array> make_table_array()
 | 
					inline std::shared_ptr<table_array> make_table_array(bool is_inline)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct make_shared_enabler : public table_array
 | 
					    struct make_shared_enabler : public table_array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        make_shared_enabler()
 | 
					        make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // nothing
 | 
					            // nothing
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return std::make_shared<make_shared_enabler>();
 | 
					    return std::make_shared<make_shared_enabler>(is_inline);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace detail
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
template <>
 | 
					template <>
 | 
				
			||||||
inline std::shared_ptr<table_array> make_element<table_array>()
 | 
					inline std::shared_ptr<table_array> make_element<table_array>()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return make_table_array();
 | 
					    return make_table_array(true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					} // namespace detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The below are overloads for fetching specific value types out of a value
 | 
					// The below are overloads for fetching specific value types out of a value
 | 
				
			||||||
// where special casting behavior (like bounds checking) is desired
 | 
					// where special casting behavior (like bounds checking) is desired
 | 
				
			||||||
| 
						 | 
					@ -1679,11 +1701,14 @@ std::shared_ptr<table> make_table()
 | 
				
			||||||
    return std::make_shared<make_shared_enabler>();
 | 
					    return std::make_shared<make_shared_enabler>();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace detail
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
template <>
 | 
					template <>
 | 
				
			||||||
inline std::shared_ptr<table> make_element<table>()
 | 
					inline std::shared_ptr<table> make_element<table>()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return make_table();
 | 
					    return make_table();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					} // namespace detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class T>
 | 
					template <class T>
 | 
				
			||||||
std::shared_ptr<base> value<T>::clone() const
 | 
					std::shared_ptr<base> value<T>::clone() const
 | 
				
			||||||
| 
						 | 
					@ -1702,7 +1727,7 @@ inline std::shared_ptr<base> array::clone() const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline std::shared_ptr<base> table_array::clone() const
 | 
					inline std::shared_ptr<base> table_array::clone() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto result = make_table_array();
 | 
					    auto result = make_table_array(is_inline());
 | 
				
			||||||
    result->reserve(array_.size());
 | 
					    result->reserve(array_.size());
 | 
				
			||||||
    for (const auto& ptr : array_)
 | 
					    for (const auto& ptr : array_)
 | 
				
			||||||
        result->array_.push_back(ptr->clone()->as_table());
 | 
					        result->array_.push_back(ptr->clone()->as_table());
 | 
				
			||||||
| 
						 | 
					@ -1738,6 +1763,11 @@ inline bool is_number(char c)
 | 
				
			||||||
    return c >= '0' && c <= '9';
 | 
					    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.
 | 
					 * Helper object for consuming expected characters.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -1766,6 +1796,13 @@ class consumer
 | 
				
			||||||
                      [&](char c) { (*this)(c); });
 | 
					                      [&](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 eat_digits(int len)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        int val = 0;
 | 
					        int val = 0;
 | 
				
			||||||
| 
						 | 
					@ -1830,7 +1867,7 @@ inline std::istream& getline(std::istream& input, std::string& line)
 | 
				
			||||||
        line.push_back(static_cast<char>(c));
 | 
					        line.push_back(static_cast<char>(c));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					} // namespace detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The parser class.
 | 
					 * The parser class.
 | 
				
			||||||
| 
						 | 
					@ -1914,21 +1951,25 @@ class parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string full_table_name;
 | 
					        std::string full_table_name;
 | 
				
			||||||
        bool inserted = false;
 | 
					        bool inserted = false;
 | 
				
			||||||
        while (it != end && *it != ']')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            auto part = parse_key(it, end,
 | 
					 | 
				
			||||||
                                  [](char c) { return c == '.' || c == ']'; });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto key_end = [](char c) { return c == ']'; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto key_part_handler = [&](const std::string& part) {
 | 
				
			||||||
            if (part.empty())
 | 
					            if (part.empty())
 | 
				
			||||||
                throw_parse_exception("Empty component of table name");
 | 
					                throw_parse_exception("Empty component of table name");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!full_table_name.empty())
 | 
					            if (!full_table_name.empty())
 | 
				
			||||||
                full_table_name += ".";
 | 
					                full_table_name += '.';
 | 
				
			||||||
            full_table_name += part;
 | 
					            full_table_name += part;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (curr_table->contains(part))
 | 
					            if (curr_table->contains(part))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					#if !defined(__PGI)
 | 
				
			||||||
                auto b = curr_table->get(part);
 | 
					                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())
 | 
					                if (b->is_table())
 | 
				
			||||||
                    curr_table = static_cast<table*>(b.get());
 | 
					                    curr_table = static_cast<table*>(b.get());
 | 
				
			||||||
                else if (b->is_table_array())
 | 
					                else if (b->is_table_array())
 | 
				
			||||||
| 
						 | 
					@ -1946,16 +1987,23 @@ class parser
 | 
				
			||||||
                curr_table->insert(part, make_table());
 | 
					                curr_table->insert(part, make_table());
 | 
				
			||||||
                curr_table = static_cast<table*>(curr_table->get(part).get());
 | 
					                curr_table = static_cast<table*>(curr_table->get(part).get());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            consume_whitespace(it, end);
 | 
					        };
 | 
				
			||||||
            if (it != end && *it == '.')
 | 
					
 | 
				
			||||||
                ++it;
 | 
					        key_part_handler(parse_key(it, end, key_end, key_part_handler));
 | 
				
			||||||
            consume_whitespace(it, end);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (it == end)
 | 
					        if (it == end)
 | 
				
			||||||
            throw_parse_exception(
 | 
					            throw_parse_exception(
 | 
				
			||||||
                "Unterminated table declaration; did you forget a ']'?");
 | 
					                "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
 | 
					        // table already existed
 | 
				
			||||||
        if (!inserted)
 | 
					        if (!inserted)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					@ -1969,8 +2017,9 @@ class parser
 | 
				
			||||||
            // since it has already been defined. If there aren't any
 | 
					            // since it has already been defined. If there aren't any
 | 
				
			||||||
            // values, then it was implicitly created by something like
 | 
					            // values, then it was implicitly created by something like
 | 
				
			||||||
            // [a.b]
 | 
					            // [a.b]
 | 
				
			||||||
            if (curr_table->empty() || std::any_of(curr_table->begin(),
 | 
					            if (curr_table->empty()
 | 
				
			||||||
                                                   curr_table->end(), is_value))
 | 
					                || std::any_of(curr_table->begin(), curr_table->end(),
 | 
				
			||||||
 | 
					                               is_value))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw_parse_exception("Redefinition of table "
 | 
					                throw_parse_exception("Redefinition of table "
 | 
				
			||||||
                                      + full_table_name);
 | 
					                                      + full_table_name);
 | 
				
			||||||
| 
						 | 
					@ -1989,36 +2038,45 @@ class parser
 | 
				
			||||||
        if (it == end || *it == ']')
 | 
					        if (it == end || *it == ']')
 | 
				
			||||||
            throw_parse_exception("Table array name cannot be empty");
 | 
					            throw_parse_exception("Table array name cannot be empty");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string full_ta_name;
 | 
					        auto key_end = [](char c) { return c == ']'; };
 | 
				
			||||||
        while (it != end && *it != ']')
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            auto part = parse_key(it, end,
 | 
					 | 
				
			||||||
                                  [](char c) { return c == '.' || c == ']'; });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string full_ta_name;
 | 
				
			||||||
 | 
					        auto key_part_handler = [&](const std::string& part) {
 | 
				
			||||||
            if (part.empty())
 | 
					            if (part.empty())
 | 
				
			||||||
                throw_parse_exception("Empty component of table array name");
 | 
					                throw_parse_exception("Empty component of table array name");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!full_ta_name.empty())
 | 
					            if (!full_ta_name.empty())
 | 
				
			||||||
                full_ta_name += ".";
 | 
					                full_ta_name += '.';
 | 
				
			||||||
            full_ta_name += part;
 | 
					            full_ta_name += part;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            consume_whitespace(it, end);
 | 
					 | 
				
			||||||
            if (it != end && *it == '.')
 | 
					 | 
				
			||||||
                ++it;
 | 
					 | 
				
			||||||
            consume_whitespace(it, end);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (curr_table->contains(part))
 | 
					            if (curr_table->contains(part))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					#if !defined(__PGI)
 | 
				
			||||||
                auto b = curr_table->get(part);
 | 
					                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
 | 
					                // if this is the end of the table array name, add an
 | 
				
			||||||
                // element to the table array that we just looked up
 | 
					                // element to the table array that we just looked up,
 | 
				
			||||||
 | 
					                // provided it was not declared inline
 | 
				
			||||||
                if (it != end && *it == ']')
 | 
					                if (it != end && *it == ']')
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (!b->is_table_array())
 | 
					                    if (!b->is_table_array())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
                        throw_parse_exception("Key " + full_ta_name
 | 
					                        throw_parse_exception("Key " + full_ta_name
 | 
				
			||||||
                                              + " is not a table array");
 | 
					                                              + " is not a table array");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    auto v = b->as_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());
 | 
					                    v->get().push_back(make_table());
 | 
				
			||||||
                    curr_table = v->get().back().get();
 | 
					                    curr_table = v->get().back().get();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -2059,15 +2117,16 @@ class parser
 | 
				
			||||||
                        = static_cast<table*>(curr_table->get(part).get());
 | 
					                        = static_cast<table*>(curr_table->get(part).get());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        key_part_handler(parse_key(it, end, key_end, key_part_handler));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // consume the last "]]"
 | 
					        // consume the last "]]"
 | 
				
			||||||
        if (it == end)
 | 
					        auto eat = make_consumer(it, end, [this]() {
 | 
				
			||||||
            throw_parse_exception("Unterminated table array name");
 | 
					            throw_parse_exception("Unterminated table array name");
 | 
				
			||||||
        ++it;
 | 
					        });
 | 
				
			||||||
        if (it == end)
 | 
					        eat(']');
 | 
				
			||||||
            throw_parse_exception("Unterminated table array name");
 | 
					        eat(']');
 | 
				
			||||||
        ++it;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        consume_whitespace(it, end);
 | 
					        consume_whitespace(it, end);
 | 
				
			||||||
        eol_or_comment(it, end);
 | 
					        eol_or_comment(it, end);
 | 
				
			||||||
| 
						 | 
					@ -2076,7 +2135,35 @@ class parser
 | 
				
			||||||
    void parse_key_value(std::string::iterator& it, std::string::iterator& end,
 | 
					    void parse_key_value(std::string::iterator& it, std::string::iterator& end,
 | 
				
			||||||
                         table* curr_table)
 | 
					                         table* curr_table)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto key = parse_key(it, end, [](char c) { return c == '='; });
 | 
					        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))
 | 
					        if (curr_table->contains(key))
 | 
				
			||||||
            throw_parse_exception("Key " + key + " already present");
 | 
					            throw_parse_exception("Key " + key + " already present");
 | 
				
			||||||
        if (it == end || *it != '=')
 | 
					        if (it == end || *it != '=')
 | 
				
			||||||
| 
						 | 
					@ -2087,18 +2174,57 @@ class parser
 | 
				
			||||||
        consume_whitespace(it, end);
 | 
					        consume_whitespace(it, end);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template <class Function>
 | 
					    template <class KeyEndFinder, class KeyPartHandler>
 | 
				
			||||||
    std::string parse_key(std::string::iterator& it,
 | 
					    std::string
 | 
				
			||||||
                          const std::string::iterator& end, Function&& fun)
 | 
					    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);
 | 
					        consume_whitespace(it, end);
 | 
				
			||||||
        if (*it == '"')
 | 
					
 | 
				
			||||||
 | 
					        if (it == end)
 | 
				
			||||||
 | 
					            throw_parse_exception("Unexpected end of key (blank key?)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (*it == '"' || *it == '\'')
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return parse_quoted_key(it, end);
 | 
					            return string_literal(it, end, *it);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            auto bke = std::find_if(it, end, std::forward<Function>(fun));
 | 
					            auto bke = std::find_if(it, end, [](char c) {
 | 
				
			||||||
 | 
					                return c == '.' || c == '=' || c == ']';
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
            return parse_bare_key(it, bke);
 | 
					            return parse_bare_key(it, bke);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -2142,12 +2268,6 @@ class parser
 | 
				
			||||||
        return key;
 | 
					        return key;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string parse_quoted_key(std::string::iterator& it,
 | 
					 | 
				
			||||||
                                 const std::string::iterator& end)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return string_literal(it, end, '"');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    enum class parse_type
 | 
					    enum class parse_type
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        STRING = 1,
 | 
					        STRING = 1,
 | 
				
			||||||
| 
						 | 
					@ -2193,7 +2313,7 @@ class parser
 | 
				
			||||||
    parse_type determine_value_type(const std::string::iterator& it,
 | 
					    parse_type determine_value_type(const std::string::iterator& it,
 | 
				
			||||||
                                    const std::string::iterator& end)
 | 
					                                    const std::string::iterator& end)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if(it == end)
 | 
					        if (it == end)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            throw_parse_exception("Failed to parse value type");
 | 
					            throw_parse_exception("Failed to parse value type");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -2209,7 +2329,11 @@ class parser
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return *dtype;
 | 
					            return *dtype;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else if (is_number(*it) || *it == '-' || *it == '+')
 | 
					        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);
 | 
					            return determine_number_type(it, end);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -2235,6 +2359,13 @@ class parser
 | 
				
			||||||
        auto check_it = it;
 | 
					        auto check_it = it;
 | 
				
			||||||
        if (*check_it == '-' || *check_it == '+')
 | 
					        if (*check_it == '-' || *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))
 | 
					        while (check_it != end && is_number(*check_it))
 | 
				
			||||||
            ++check_it;
 | 
					            ++check_it;
 | 
				
			||||||
        if (check_it != end && *check_it == '.')
 | 
					        if (check_it != end && *check_it == '.')
 | 
				
			||||||
| 
						 | 
					@ -2283,8 +2414,7 @@ class parser
 | 
				
			||||||
        bool consuming = false;
 | 
					        bool consuming = false;
 | 
				
			||||||
        std::shared_ptr<value<std::string>> ret;
 | 
					        std::shared_ptr<value<std::string>> ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto handle_line
 | 
					        auto handle_line = [&](std::string::iterator& local_it,
 | 
				
			||||||
            = [&](std::string::iterator& local_it,
 | 
					 | 
				
			||||||
                               std::string::iterator& local_end) {
 | 
					                               std::string::iterator& local_end) {
 | 
				
			||||||
            if (consuming)
 | 
					            if (consuming)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -2514,17 +2644,13 @@ class parser
 | 
				
			||||||
        return value;
 | 
					        return value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool is_hex(char c)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uint32_t hex_to_digit(char c)
 | 
					    uint32_t hex_to_digit(char c)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_number(c))
 | 
					        if (is_number(c))
 | 
				
			||||||
            return static_cast<uint32_t>(c - '0');
 | 
					            return static_cast<uint32_t>(c - '0');
 | 
				
			||||||
        return 10 + static_cast<uint32_t>(
 | 
					        return 10
 | 
				
			||||||
                        c - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
 | 
					               + static_cast<uint32_t>(c
 | 
				
			||||||
 | 
					                                       - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::shared_ptr<base> parse_number(std::string::iterator& it,
 | 
					    std::shared_ptr<base> parse_number(std::string::iterator& it,
 | 
				
			||||||
| 
						 | 
					@ -2538,25 +2664,6 @@ class parser
 | 
				
			||||||
                ++check_it;
 | 
					                ++check_it;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        eat_sign();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto eat_numbers = [&]() {
 | 
					 | 
				
			||||||
            auto beg = check_it;
 | 
					 | 
				
			||||||
            while (check_it != end && is_number(*check_it))
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                ++check_it;
 | 
					 | 
				
			||||||
                if (check_it != end && *check_it == '_')
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    ++check_it;
 | 
					 | 
				
			||||||
                    if (check_it == end || !is_number(*check_it))
 | 
					 | 
				
			||||||
                        throw_parse_exception("Malformed number");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (check_it == beg)
 | 
					 | 
				
			||||||
                throw_parse_exception("Malformed number");
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto check_no_leading_zero = [&]() {
 | 
					        auto check_no_leading_zero = [&]() {
 | 
				
			||||||
            if (check_it != end && *check_it == '0' && check_it + 1 != check_end
 | 
					            if (check_it != end && *check_it == '0' && check_it + 1 != check_end
 | 
				
			||||||
                && check_it[1] != '.')
 | 
					                && check_it[1] != '.')
 | 
				
			||||||
| 
						 | 
					@ -2565,7 +2672,80 @@ class parser
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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();
 | 
					        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();
 | 
					        eat_numbers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (check_it != end
 | 
					        if (check_it != end
 | 
				
			||||||
| 
						 | 
					@ -2604,14 +2784,17 @@ class parser
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
 | 
					    std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,
 | 
				
			||||||
                                              const std::string::iterator& end)
 | 
					                                              const std::string::iterator& end,
 | 
				
			||||||
 | 
					                                              int base = 10,
 | 
				
			||||||
 | 
					                                              const char* prefix = "")
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::string v{it, end};
 | 
					        std::string v{it, end};
 | 
				
			||||||
 | 
					        v = prefix + v;
 | 
				
			||||||
        v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
 | 
					        v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
 | 
				
			||||||
        it = end;
 | 
					        it = end;
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return make_value<int64_t>(std::stoll(v));
 | 
					            return make_value<int64_t>(std::stoll(v, nullptr, base));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (const std::invalid_argument& ex)
 | 
					        catch (const std::invalid_argument& ex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					@ -2674,18 +2857,33 @@ class parser
 | 
				
			||||||
    std::string::iterator find_end_of_number(std::string::iterator it,
 | 
					    std::string::iterator find_end_of_number(std::string::iterator it,
 | 
				
			||||||
                                             std::string::iterator end)
 | 
					                                             std::string::iterator end)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return std::find_if(it, end, [](char c) {
 | 
					        auto ret = std::find_if(it, end, [](char c) {
 | 
				
			||||||
            return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
 | 
					            return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'
 | 
				
			||||||
                   && c != '-' && c != '+';
 | 
					                   && 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 find_end_of_date(std::string::iterator it,
 | 
				
			||||||
                                           std::string::iterator end)
 | 
					                                           std::string::iterator end)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return std::find_if(it, end, [](char c) {
 | 
					        auto end_of_date = std::find_if(it, end, [](char c) {
 | 
				
			||||||
            return !is_number(c) && c != 'T' && c != 'Z' && c != ':' && c != '-'
 | 
					            return !is_number(c) && c != '-';
 | 
				
			||||||
                   && 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 != '.';
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2754,7 +2952,7 @@ class parser
 | 
				
			||||||
        if (it == date_end)
 | 
					        if (it == date_end)
 | 
				
			||||||
            return make_value(ldate);
 | 
					            return make_value(ldate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        eat('T');
 | 
					        eat.eat_or('T', ' ');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        local_datetime ldt;
 | 
					        local_datetime ldt;
 | 
				
			||||||
        static_cast<local_date&>(ldt) = ldate;
 | 
					        static_cast<local_date&>(ldt) = ldate;
 | 
				
			||||||
| 
						 | 
					@ -2850,9 +3048,9 @@ class parser
 | 
				
			||||||
        auto arr = make_array();
 | 
					        auto arr = make_array();
 | 
				
			||||||
        while (it != end && *it != ']')
 | 
					        while (it != end && *it != ']')
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            auto value = parse_value(it, end);
 | 
					            auto val = parse_value(it, end);
 | 
				
			||||||
            if (auto v = value->as<Value>())
 | 
					            if (auto v = val->as<Value>())
 | 
				
			||||||
                arr->get().push_back(value);
 | 
					                arr->get().push_back(val);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                throw_parse_exception("Arrays must be homogeneous");
 | 
					                throw_parse_exception("Arrays must be homogeneous");
 | 
				
			||||||
            skip_whitespace_and_comments(it, end);
 | 
					            skip_whitespace_and_comments(it, end);
 | 
				
			||||||
| 
						 | 
					@ -2871,7 +3069,7 @@ class parser
 | 
				
			||||||
                                               std::string::iterator& it,
 | 
					                                               std::string::iterator& it,
 | 
				
			||||||
                                               std::string::iterator& end)
 | 
					                                               std::string::iterator& end)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auto arr = make_element<Object>();
 | 
					        auto arr = detail::make_element<Object>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while (it != end && *it != ']')
 | 
					        while (it != end && *it != ']')
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					@ -2881,7 +3079,7 @@ class parser
 | 
				
			||||||
            arr->get().push_back(((*this).*fun)(it, end));
 | 
					            arr->get().push_back(((*this).*fun)(it, end));
 | 
				
			||||||
            skip_whitespace_and_comments(it, end);
 | 
					            skip_whitespace_and_comments(it, end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (*it != ',')
 | 
					            if (it == end || *it != ',')
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ++it;
 | 
					            ++it;
 | 
				
			||||||
| 
						 | 
					@ -2906,8 +3104,11 @@ class parser
 | 
				
			||||||
                throw_parse_exception("Unterminated inline table");
 | 
					                throw_parse_exception("Unterminated inline table");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            consume_whitespace(it, end);
 | 
					            consume_whitespace(it, end);
 | 
				
			||||||
 | 
					            if (it != end && *it != '}')
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                parse_key_value(it, end, tbl.get());
 | 
					                parse_key_value(it, end, tbl.get());
 | 
				
			||||||
                consume_whitespace(it, end);
 | 
					                consume_whitespace(it, end);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } while (*it == ',');
 | 
					        } while (*it == ',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (it == end || *it != '}')
 | 
					        if (it == end || *it != '}')
 | 
				
			||||||
| 
						 | 
					@ -2987,7 +3188,8 @@ class parser
 | 
				
			||||||
        if (it[4] != '-' || it[7] != '-')
 | 
					        if (it[4] != '-' || it[7] != '-')
 | 
				
			||||||
            return {};
 | 
					            return {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (len >= 19 && it[10] == 'T' && is_time(it + 11, date_end))
 | 
					        if (len >= 19 && (it[10] == 'T' || it[10] == ' ')
 | 
				
			||||||
 | 
					            && is_time(it + 11, date_end))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // datetime type
 | 
					            // datetime type
 | 
				
			||||||
            auto time_end = find_end_of_time(it + 11, date_end);
 | 
					            auto time_end = find_end_of_time(it + 11, date_end);
 | 
				
			||||||
| 
						 | 
					@ -3243,7 +3445,7 @@ class toml_writer
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                res += "\\\\";
 | 
					                res += "\\\\";
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if ((const uint32_t)*it <= 0x001f)
 | 
					            else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                res += "\\u";
 | 
					                res += "\\u";
 | 
				
			||||||
                std::stringstream ss;
 | 
					                std::stringstream ss;
 | 
				
			||||||
| 
						 | 
					@ -3274,12 +3476,21 @@ class toml_writer
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void write(const value<double>& v)
 | 
					    void write(const value<double>& v)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::ios::fmtflags flags{stream_.flags()};
 | 
					        std::stringstream ss;
 | 
				
			||||||
 | 
					        ss << std::showpoint
 | 
				
			||||||
 | 
					           << std::setprecision(std::numeric_limits<double>::max_digits10)
 | 
				
			||||||
 | 
					           << v.get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        stream_ << std::showpoint;
 | 
					        auto double_str = ss.str();
 | 
				
			||||||
        write(v.get());
 | 
					        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_.flags(flags);
 | 
					        stream_ << double_str;
 | 
				
			||||||
 | 
					        has_naked_endline_ = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -3287,8 +3498,8 @@ class toml_writer
 | 
				
			||||||
     * offset_datetime.
 | 
					     * offset_datetime.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    template <class T>
 | 
					    template <class T>
 | 
				
			||||||
    typename std::enable_if<is_one_of<T, int64_t, local_date, local_time,
 | 
					    typename std::enable_if<
 | 
				
			||||||
                                      local_datetime,
 | 
					        is_one_of<T, int64_t, local_date, local_time, local_datetime,
 | 
				
			||||||
                  offset_datetime>::value>::type
 | 
					                  offset_datetime>::value>::type
 | 
				
			||||||
    write(const value<T>& v)
 | 
					    write(const value<T>& v)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -3453,5 +3664,5 @@ inline std::ostream& operator<<(std::ostream& stream, const array& a)
 | 
				
			||||||
    a.accept(writer);
 | 
					    a.accept(writer);
 | 
				
			||||||
    return stream;
 | 
					    return stream;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					} // namespace cpptoml
 | 
				
			||||||
#endif
 | 
					#endif // CPPTOML_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue