aboutsummaryrefslogtreecommitdiff
path: root/src/cpptoml/cpptoml.h
diff options
context:
space:
mode:
authorDing Xiang Fei <dingxiangfei2009@gmail.com>2019-05-29 17:01:39 +0800
committerDing Xiang Fei <dingxiangfei2009@gmail.com>2019-05-29 17:01:39 +0800
commitabdedcdb387628aaa4d9dc362b7e588bbc62baae (patch)
tree9d9e647092d9d0bb38910720fd4bf32db444a082 /src/cpptoml/cpptoml.h
parent22f2744afdfa048de7bd078ba9d24dafbcc7d7c0 (diff)
bump cpptoml to v0.1.1
Diffstat (limited to 'src/cpptoml/cpptoml.h')
-rw-r--r--src/cpptoml/cpptoml.h569
1 files changed, 390 insertions, 179 deletions
diff --git a/src/cpptoml/cpptoml.h b/src/cpptoml/cpptoml.h
index 07de010b1..5a00da3b4 100644
--- a/src/cpptoml/cpptoml.h
+++ b/src/cpptoml/cpptoml.h
@@ -4,8 +4,8 @@
* @date May 2013
*/
-#ifndef _CPPTOML_H_
-#define _CPPTOML_H_
+#ifndef CPPTOML_H
+#define CPPTOML_H
#include <algorithm>
#include <cassert>
@@ -84,11 +84,12 @@ class option
return &value_;
}
- const T& value_or(const T& alternative) const
+ template <class U>
+ T value_or(U&& alternative) const
{
if (!empty_)
return value_;
- return alternative;
+ return static_cast<T>(std::forward<U>(alternative));
}
private:
@@ -295,13 +296,12 @@ struct valid_value_or_string_convertible
};
template <class T>
-struct value_traits<T, typename std::
- enable_if<valid_value_or_string_convertible<T>::
- value>::type>
+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 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>;
@@ -312,12 +312,11 @@ struct value_traits<T, typename std::
};
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>
+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;
@@ -330,11 +329,11 @@ struct value_traits<T,
};
template <class T>
-struct value_traits<T,
- typename std::
- enable_if<!valid_value_or_string_convertible<T>::value
- && std::is_signed<typename std::decay<T>::
- type>::value>::type>
+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;
@@ -356,11 +355,10 @@ struct value_traits<T,
};
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>
+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;
@@ -395,10 +393,15 @@ struct array_of_trait<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();
+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
@@ -576,7 +579,7 @@ class base : public std::enable_shared_from_this<base>
#if defined(CPPTOML_NO_RTTI)
base_type type() const
{
- return type_;
+ return type_;
}
protected:
@@ -698,7 +701,7 @@ inline std::shared_ptr<value<double>> base::as()
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()));;
+ return make_value<double>(static_cast<double>(v->get()));
}
#else
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 (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)
{
@@ -1027,11 +1031,14 @@ inline std::shared_ptr<array> make_array()
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
@@ -1060,7 +1067,7 @@ class table;
class table_array : public base
{
friend class table;
- friend std::shared_ptr<table_array> make_table_array();
+ friend std::shared_ptr<table_array> make_table_array(bool);
public:
std::shared_ptr<base> clone() const override;
@@ -1152,14 +1159,25 @@ class table_array : public base
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() : base(base_type::TABLE_ARRAY)
+ table_array(bool is_inline = false)
+ : base(base_type::TABLE_ARRAY), is_inline_(is_inline)
{
// nothing
}
#else
- table_array()
+ table_array(bool is_inline = false) : is_inline_(is_inline)
{
// nothing
}
@@ -1169,26 +1187,30 @@ class table_array : public base
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()
+inline std::shared_ptr<table_array> make_table_array(bool is_inline)
{
struct make_shared_enabler : public table_array
{
- make_shared_enabler()
+ make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline)
{
// nothing
}
};
- return std::make_shared<make_shared_enabler>();
+ 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();
+ 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
@@ -1679,11 +1701,14 @@ std::shared_ptr<table> make_table()
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
@@ -1702,7 +1727,7 @@ inline std::shared_ptr<base> 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());
for (const auto& ptr : array_)
result->array_.push_back(ptr->clone()->as_table());
@@ -1738,6 +1763,11 @@ 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.
*/
@@ -1766,6 +1796,13 @@ class consumer
[&](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;
@@ -1830,7 +1867,7 @@ inline std::istream& getline(std::istream& input, std::string& line)
line.push_back(static_cast<char>(c));
}
}
-}
+} // namespace detail
/**
* The parser class.
@@ -1914,21 +1951,25 @@ class parser
std::string full_table_name;
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())
throw_parse_exception("Empty component of table name");
if (!full_table_name.empty())
- full_table_name += ".";
+ 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())
@@ -1946,16 +1987,23 @@ class parser
curr_table->insert(part, make_table());
curr_table = static_cast<table*>(curr_table->get(part).get());
}
- consume_whitespace(it, end);
- if (it != end && *it == '.')
- ++it;
- consume_whitespace(it, end);
- }
+ };
+
+ 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)
{
@@ -1969,8 +2017,9 @@ class parser
// 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))
+ if (curr_table->empty()
+ || std::any_of(curr_table->begin(), curr_table->end(),
+ is_value))
{
throw_parse_exception("Redefinition of table "
+ full_table_name);
@@ -1989,36 +2038,45 @@ class parser
if (it == end || *it == ']')
throw_parse_exception("Table array name cannot be empty");
- std::string full_ta_name;
- while (it != end && *it != ']')
- {
- auto part = parse_key(it, end,
- [](char c) { return c == '.' || c == ']'; });
+ 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 += '.';
full_ta_name += part;
- consume_whitespace(it, end);
- if (it != end && *it == '.')
- ++it;
- consume_whitespace(it, end);
-
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
+ // 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();
}
@@ -2059,15 +2117,16 @@ class parser
= static_cast<table*>(curr_table->get(part).get());
}
}
- }
+ };
+
+ key_part_handler(parse_key(it, end, key_end, key_part_handler));
// consume the last "]]"
- if (it == end)
- throw_parse_exception("Unterminated table array name");
- ++it;
- if (it == end)
+ auto eat = make_consumer(it, end, [this]() {
throw_parse_exception("Unterminated table array name");
- ++it;
+ });
+ eat(']');
+ eat(']');
consume_whitespace(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,
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))
throw_parse_exception("Key " + key + " already present");
if (it == end || *it != '=')
@@ -2087,18 +2174,57 @@ class parser
consume_whitespace(it, end);
}
- template <class Function>
- std::string parse_key(std::string::iterator& it,
- const std::string::iterator& end, Function&& fun)
+ 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 == '"')
+
+ 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
{
- 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);
}
}
@@ -2142,12 +2268,6 @@ class parser
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
{
STRING = 1,
@@ -2193,7 +2313,7 @@ class parser
parse_type determine_value_type(const std::string::iterator& it,
const std::string::iterator& end)
{
- if(it == end)
+ if (it == end)
{
throw_parse_exception("Failed to parse value type");
}
@@ -2209,7 +2329,11 @@ class parser
{
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);
}
@@ -2235,6 +2359,13 @@ class parser
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 == '.')
@@ -2283,57 +2414,56 @@ class parser
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++;
- }
- };
+ 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);
@@ -2514,17 +2644,13 @@ class parser
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)
{
if (is_number(c))
return static_cast<uint32_t>(c - '0');
- return 10 + static_cast<uint32_t>(
- c - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
+ return 10
+ + static_cast<uint32_t>(c
+ - ((c >= 'a' && c <= 'f') ? 'a' : 'A'));
}
std::shared_ptr<base> parse_number(std::string::iterator& it,
@@ -2538,17 +2664,23 @@ class parser
++check_it;
};
- eat_sign();
+ 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_numbers = [&]() {
+ auto eat_digits = [&](bool (*check_char)(char)) {
auto beg = check_it;
- while (check_it != end && is_number(*check_it))
+ while (check_it != end && check_char(*check_it))
{
++check_it;
if (check_it != end && *check_it == '_')
{
++check_it;
- if (check_it == end || !is_number(*check_it))
+ if (check_it == end || !check_char(*check_it))
throw_parse_exception("Malformed number");
}
}
@@ -2557,15 +2689,63 @@ class parser
throw_parse_exception("Malformed number");
};
- auto check_no_leading_zero = [&]() {
- if (check_it != end && *check_it == '0' && check_it + 1 != check_end
- && check_it[1] != '.')
+ 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')
{
- throw_parse_exception("Numbers may not have leading zeros");
+ 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
@@ -2604,14 +2784,17 @@ class parser
}
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};
+ v = prefix + v;
v.erase(std::remove(v.begin(), v.end(), '_'), v.end());
it = end;
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)
{
@@ -2674,18 +2857,33 @@ class parser
std::string::iterator find_end_of_number(std::string::iterator it,
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'
- && 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 end)
{
- return std::find_if(it, end, [](char c) {
- return !is_number(c) && c != 'T' && c != 'Z' && c != ':' && c != '-'
- && c != '+' && c != '.';
+ 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 != '.';
});
}
@@ -2754,7 +2952,7 @@ class parser
if (it == date_end)
return make_value(ldate);
- eat('T');
+ eat.eat_or('T', ' ');
local_datetime ldt;
static_cast<local_date&>(ldt) = ldate;
@@ -2850,9 +3048,9 @@ class parser
auto arr = make_array();
while (it != end && *it != ']')
{
- auto value = parse_value(it, end);
- if (auto v = value->as<Value>())
- arr->get().push_back(value);
+ 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);
@@ -2871,7 +3069,7 @@ class parser
std::string::iterator& it,
std::string::iterator& end)
{
- auto arr = make_element<Object>();
+ auto arr = detail::make_element<Object>();
while (it != end && *it != ']')
{
@@ -2881,7 +3079,7 @@ class parser
arr->get().push_back(((*this).*fun)(it, end));
skip_whitespace_and_comments(it, end);
- if (*it != ',')
+ if (it == end || *it != ',')
break;
++it;
@@ -2906,8 +3104,11 @@ class parser
throw_parse_exception("Unterminated inline table");
consume_whitespace(it, end);
- parse_key_value(it, end, tbl.get());
- 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 != '}')
@@ -2987,7 +3188,8 @@ class parser
if (it[4] != '-' || it[7] != '-')
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
auto time_end = find_end_of_time(it + 11, date_end);
@@ -3243,7 +3445,7 @@ class toml_writer
{
res += "\\\\";
}
- else if ((const uint32_t)*it <= 0x001f)
+ else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f))
{
res += "\\u";
std::stringstream ss;
@@ -3274,12 +3476,21 @@ class toml_writer
*/
void write(const value<double>& v)
{
- std::ios::fmtflags flags{stream_.flags()};
-
- stream_ << std::showpoint;
- write(v.get());
-
- stream_.flags(flags);
+ 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;
}
/**
@@ -3287,9 +3498,9 @@ class toml_writer
* 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
+ 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());
@@ -3453,5 +3664,5 @@ inline std::ostream& operator<<(std::ostream& stream, const array& a)
a.accept(writer);
return stream;
}
-}
-#endif
+} // namespace cpptoml
+#endif // CPPTOML_H