bump cpptoml to v0.1.1

This commit is contained in:
Ding Xiang Fei 2019-05-29 17:01:39 +08:00
parent 22f2744afd
commit abdedcdb38

View file

@ -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,
@ -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