mirror of https://github.com/trapexit/mergerfs.git
Antonio SJ Musumeci
10 months ago
25 changed files with 11350 additions and 0 deletions
-
1DEPENDENCIES
-
38src/toml.hpp
-
109src/toml/color.hpp
-
306src/toml/combinator.hpp
-
484src/toml/comments.hpp
-
631src/toml/datetime.hpp
-
83src/toml/exception.hpp
-
19src/toml/from.hpp
-
1154src/toml/get.hpp
-
19src/toml/into.hpp
-
294src/toml/lexer.hpp
-
113src/toml/literal.hpp
-
121src/toml/macros.hpp
-
2611src/toml/parser.hpp
-
416src/toml/region.hpp
-
717src/toml/result.hpp
-
994src/toml/serializer.hpp
-
239src/toml/source_location.hpp
-
43src/toml/storage.hpp
-
228src/toml/string.hpp
-
328src/toml/traits.hpp
-
173src/toml/types.hpp
-
150src/toml/utility.hpp
-
2037src/toml/value.hpp
-
42src/toml/version.hpp
@ -0,0 +1,38 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2017 Toru Niina |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef TOML_FOR_MODERN_CPP
|
|||
#define TOML_FOR_MODERN_CPP
|
|||
|
|||
#define TOML11_VERSION_MAJOR 3
|
|||
#define TOML11_VERSION_MINOR 8
|
|||
#define TOML11_VERSION_PATCH 1
|
|||
|
|||
#include "toml/parser.hpp"
|
|||
#include "toml/literal.hpp"
|
|||
#include "toml/serializer.hpp"
|
|||
#include "toml/get.hpp"
|
|||
#include "toml/macros.hpp"
|
|||
|
|||
#endif// TOML_FOR_MODERN_CPP
|
@ -0,0 +1,109 @@ |
|||
#ifndef TOML11_COLOR_HPP
|
|||
#define TOML11_COLOR_HPP
|
|||
#include <cstdint>
|
|||
#include <ostream>
|
|||
|
|||
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
|
|||
#define TOML11_ERROR_MESSAGE_COLORIZED true
|
|||
#else
|
|||
#define TOML11_ERROR_MESSAGE_COLORIZED false
|
|||
#endif
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
// put ANSI escape sequence to ostream
|
|||
namespace color_ansi |
|||
{ |
|||
namespace detail |
|||
{ |
|||
|
|||
inline int colorize_index() |
|||
{ |
|||
static const int index = std::ios_base::xalloc(); |
|||
return index; |
|||
} |
|||
|
|||
// Control color mode globally
|
|||
class color_mode |
|||
{ |
|||
public: |
|||
inline void enable() |
|||
{ |
|||
should_color_ = true; |
|||
} |
|||
inline void disable() |
|||
{ |
|||
should_color_ = false; |
|||
} |
|||
|
|||
inline bool should_color() const |
|||
{ |
|||
return should_color_; |
|||
} |
|||
|
|||
static color_mode& status() |
|||
{ |
|||
static color_mode status_; |
|||
return status_; |
|||
} |
|||
|
|||
private: |
|||
bool should_color_ = false; |
|||
}; |
|||
|
|||
} // detail
|
|||
|
|||
inline std::ostream& colorize(std::ostream& os) |
|||
{ |
|||
// by default, it is zero.
|
|||
os.iword(detail::colorize_index()) = 1; |
|||
return os; |
|||
} |
|||
inline std::ostream& nocolorize(std::ostream& os) |
|||
{ |
|||
os.iword(detail::colorize_index()) = 0; |
|||
return os; |
|||
} |
|||
inline std::ostream& reset (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} |
|||
inline std::ostream& bold (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} |
|||
inline std::ostream& grey (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} |
|||
inline std::ostream& red (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} |
|||
inline std::ostream& green (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} |
|||
inline std::ostream& yellow (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} |
|||
inline std::ostream& blue (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} |
|||
inline std::ostream& magenta(std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} |
|||
inline std::ostream& cyan (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} |
|||
inline std::ostream& white (std::ostream& os) |
|||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} |
|||
|
|||
inline void enable() |
|||
{ |
|||
return detail::color_mode::status().enable(); |
|||
} |
|||
inline void disable() |
|||
{ |
|||
return detail::color_mode::status().disable(); |
|||
} |
|||
|
|||
inline bool should_color() |
|||
{ |
|||
return detail::color_mode::status().should_color(); |
|||
} |
|||
|
|||
} // color_ansi
|
|||
|
|||
// ANSI escape sequence is the only and default colorization method currently
|
|||
namespace color = color_ansi; |
|||
|
|||
} // toml
|
|||
#endif// TOML11_COLOR_HPP
|
@ -0,0 +1,306 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_COMBINATOR_HPP
|
|||
#define TOML11_COMBINATOR_HPP
|
|||
#include <cassert>
|
|||
#include <cctype>
|
|||
#include <cstdio>
|
|||
|
|||
#include <array>
|
|||
#include <iomanip>
|
|||
#include <iterator>
|
|||
#include <limits>
|
|||
#include <type_traits>
|
|||
|
|||
#include "region.hpp"
|
|||
#include "result.hpp"
|
|||
#include "traits.hpp"
|
|||
#include "utility.hpp"
|
|||
|
|||
// they scans characters and returns region if it matches to the condition.
|
|||
// when they fail, it does not change the location.
|
|||
// in lexer.hpp, these are used.
|
|||
|
|||
namespace toml |
|||
{ |
|||
namespace detail |
|||
{ |
|||
|
|||
// to output character as an error message.
|
|||
inline std::string show_char(const char c) |
|||
{ |
|||
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
|
|||
// I'm not completely sure but they check the value of char to be in the
|
|||
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
|
|||
// has negative value (if char has sign). So here it re-interprets c as
|
|||
// unsigned char through pointer. In general, converting pointer to a
|
|||
// pointer that has different type cause UB, but `(signed|unsigned)?char`
|
|||
// are one of the exceptions. Converting pointer only to char and std::byte
|
|||
// (c++17) are valid.
|
|||
if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c)))) |
|||
{ |
|||
return std::string(1, c); |
|||
} |
|||
else |
|||
{ |
|||
std::array<char, 5> buf; |
|||
buf.fill('\0'); |
|||
const auto r = std::snprintf( |
|||
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF); |
|||
(void) r; // Unused variable warning
|
|||
assert(r == static_cast<int>(buf.size()) - 1); |
|||
return std::string(buf.data()); |
|||
} |
|||
} |
|||
|
|||
template<char C> |
|||
struct character |
|||
{ |
|||
static constexpr char target = C; |
|||
|
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
if(loc.iter() == loc.end()) {return none();} |
|||
const auto first = loc.iter(); |
|||
|
|||
const char c = *(loc.iter()); |
|||
if(c != target) |
|||
{ |
|||
return none(); |
|||
} |
|||
loc.advance(); // update location
|
|||
|
|||
return ok(region(loc, first, loc.iter())); |
|||
} |
|||
}; |
|||
template<char C> |
|||
constexpr char character<C>::target; |
|||
|
|||
// closed interval [Low, Up]. both Low and Up are included.
|
|||
template<char Low, char Up> |
|||
struct in_range |
|||
{ |
|||
// assuming ascii part of UTF-8...
|
|||
static_assert(Low <= Up, "lower bound should be less than upper bound."); |
|||
|
|||
static constexpr char upper = Up; |
|||
static constexpr char lower = Low; |
|||
|
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
if(loc.iter() == loc.end()) {return none();} |
|||
const auto first = loc.iter(); |
|||
|
|||
const char c = *(loc.iter()); |
|||
if(c < lower || upper < c) |
|||
{ |
|||
return none(); |
|||
} |
|||
|
|||
loc.advance(); |
|||
return ok(region(loc, first, loc.iter())); |
|||
} |
|||
}; |
|||
template<char L, char U> constexpr char in_range<L, U>::upper; |
|||
template<char L, char U> constexpr char in_range<L, U>::lower; |
|||
|
|||
// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
|
|||
// for detecting invalid characters, like control sequences in toml string.
|
|||
template<typename Combinator> |
|||
struct exclude |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
if(loc.iter() == loc.end()) {return none();} |
|||
auto first = loc.iter(); |
|||
|
|||
auto rslt = Combinator::invoke(loc); |
|||
if(rslt.is_ok()) |
|||
{ |
|||
loc.reset(first); |
|||
return none(); |
|||
} |
|||
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
|
|||
return ok(region(loc, first, loc.iter())); |
|||
} |
|||
}; |
|||
|
|||
// increment `iter`, if matches. otherwise, just return empty string.
|
|||
template<typename Combinator> |
|||
struct maybe |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
const auto rslt = Combinator::invoke(loc); |
|||
if(rslt.is_ok()) |
|||
{ |
|||
return rslt; |
|||
} |
|||
return ok(region(loc)); |
|||
} |
|||
}; |
|||
|
|||
template<typename ... Ts> |
|||
struct sequence; |
|||
|
|||
template<typename Head, typename ... Tail> |
|||
struct sequence<Head, Tail...> |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
const auto first = loc.iter(); |
|||
auto rslt = Head::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
loc.reset(first); |
|||
return none(); |
|||
} |
|||
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first); |
|||
} |
|||
|
|||
// called from the above function only, recursively.
|
|||
template<typename Iterator> |
|||
static result<region, none_t> |
|||
invoke(location& loc, region reg, Iterator first) |
|||
{ |
|||
const auto rslt = Head::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
loc.reset(first); |
|||
return none(); |
|||
} |
|||
reg += rslt.unwrap(); // concat regions
|
|||
return sequence<Tail...>::invoke(loc, std::move(reg), first); |
|||
} |
|||
}; |
|||
|
|||
template<typename Head> |
|||
struct sequence<Head> |
|||
{ |
|||
// would be called from sequence<T ...>::invoke only.
|
|||
template<typename Iterator> |
|||
static result<region, none_t> |
|||
invoke(location& loc, region reg, Iterator first) |
|||
{ |
|||
const auto rslt = Head::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
loc.reset(first); |
|||
return none(); |
|||
} |
|||
reg += rslt.unwrap(); // concat regions
|
|||
return ok(reg); |
|||
} |
|||
}; |
|||
|
|||
template<typename ... Ts> |
|||
struct either; |
|||
|
|||
template<typename Head, typename ... Tail> |
|||
struct either<Head, Tail...> |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
const auto rslt = Head::invoke(loc); |
|||
if(rslt.is_ok()) {return rslt;} |
|||
return either<Tail...>::invoke(loc); |
|||
} |
|||
}; |
|||
template<typename Head> |
|||
struct either<Head> |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
return Head::invoke(loc); |
|||
} |
|||
}; |
|||
|
|||
template<typename T, typename N> |
|||
struct repeat; |
|||
|
|||
template<std::size_t N> struct exactly{}; |
|||
template<std::size_t N> struct at_least{}; |
|||
struct unlimited{}; |
|||
|
|||
template<typename T, std::size_t N> |
|||
struct repeat<T, exactly<N>> |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
region retval(loc); |
|||
const auto first = loc.iter(); |
|||
for(std::size_t i=0; i<N; ++i) |
|||
{ |
|||
auto rslt = T::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
loc.reset(first); |
|||
return none(); |
|||
} |
|||
retval += rslt.unwrap(); |
|||
} |
|||
return ok(std::move(retval)); |
|||
} |
|||
}; |
|||
|
|||
template<typename T, std::size_t N> |
|||
struct repeat<T, at_least<N>> |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
region retval(loc); |
|||
|
|||
const auto first = loc.iter(); |
|||
for(std::size_t i=0; i<N; ++i) |
|||
{ |
|||
auto rslt = T::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
loc.reset(first); |
|||
return none(); |
|||
} |
|||
retval += rslt.unwrap(); |
|||
} |
|||
while(true) |
|||
{ |
|||
auto rslt = T::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
return ok(std::move(retval)); |
|||
} |
|||
retval += rslt.unwrap(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
template<typename T> |
|||
struct repeat<T, unlimited> |
|||
{ |
|||
static result<region, none_t> |
|||
invoke(location& loc) |
|||
{ |
|||
region retval(loc); |
|||
while(true) |
|||
{ |
|||
auto rslt = T::invoke(loc); |
|||
if(rslt.is_err()) |
|||
{ |
|||
return ok(std::move(retval)); |
|||
} |
|||
retval += rslt.unwrap(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
} // detail
|
|||
} // toml
|
|||
#endif// TOML11_COMBINATOR_HPP
|
@ -0,0 +1,484 @@ |
|||
// Copyright Toru Niina 2019.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_COMMENTS_HPP
|
|||
#define TOML11_COMMENTS_HPP
|
|||
#include <initializer_list>
|
|||
#include <iterator>
|
|||
#include <stdexcept>
|
|||
#include <string>
|
|||
#include <type_traits>
|
|||
#include <utility>
|
|||
#include <vector>
|
|||
|
|||
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
|
|||
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
|
|||
#else
|
|||
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
|
|||
#endif
|
|||
|
|||
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
|
|||
// Those two are a container that have the same interface as `std::vector<std::string>`
|
|||
// but bahaves in the opposite way. `preserve_comments` is just the same as
|
|||
// `std::vector<std::string>` and each `std::string` corresponds to a comment line.
|
|||
// Conversely, `discard_comments` discards all the strings and ignores everything
|
|||
// assigned in it. `discard_comments` is always empty and you will encounter an
|
|||
// error whenever you access to the element.
|
|||
namespace toml |
|||
{ |
|||
struct discard_comments; // forward decl
|
|||
|
|||
// use it in the following way
|
|||
//
|
|||
// const toml::basic_value<toml::preserve_comments> data =
|
|||
// toml::parse<toml::preserve_comments>("example.toml");
|
|||
//
|
|||
// the interface is almost the same as std::vector<std::string>.
|
|||
struct preserve_comments |
|||
{ |
|||
// `container_type` is not provided in discard_comments.
|
|||
// do not use this inner-type in a generic code.
|
|||
using container_type = std::vector<std::string>; |
|||
|
|||
using size_type = container_type::size_type; |
|||
using difference_type = container_type::difference_type; |
|||
using value_type = container_type::value_type; |
|||
using reference = container_type::reference; |
|||
using const_reference = container_type::const_reference; |
|||
using pointer = container_type::pointer; |
|||
using const_pointer = container_type::const_pointer; |
|||
using iterator = container_type::iterator; |
|||
using const_iterator = container_type::const_iterator; |
|||
using reverse_iterator = container_type::reverse_iterator; |
|||
using const_reverse_iterator = container_type::const_reverse_iterator; |
|||
|
|||
preserve_comments() = default; |
|||
~preserve_comments() = default; |
|||
preserve_comments(preserve_comments const&) = default; |
|||
preserve_comments(preserve_comments &&) = default; |
|||
preserve_comments& operator=(preserve_comments const&) = default; |
|||
preserve_comments& operator=(preserve_comments &&) = default; |
|||
|
|||
explicit preserve_comments(const std::vector<std::string>& c): comments(c){} |
|||
explicit preserve_comments(std::vector<std::string>&& c) |
|||
: comments(std::move(c)) |
|||
{} |
|||
preserve_comments& operator=(const std::vector<std::string>& c) |
|||
{ |
|||
comments = c; |
|||
return *this; |
|||
} |
|||
preserve_comments& operator=(std::vector<std::string>&& c) |
|||
{ |
|||
comments = std::move(c); |
|||
return *this; |
|||
} |
|||
|
|||
explicit preserve_comments(const discard_comments&) {} |
|||
|
|||
explicit preserve_comments(size_type n): comments(n) {} |
|||
preserve_comments(size_type n, const std::string& x): comments(n, x) {} |
|||
preserve_comments(std::initializer_list<std::string> x): comments(x) {} |
|||
template<typename InputIterator> |
|||
preserve_comments(InputIterator first, InputIterator last) |
|||
: comments(first, last) |
|||
{} |
|||
|
|||
template<typename InputIterator> |
|||
void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} |
|||
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);} |
|||
void assign(size_type n, const std::string& val) {comments.assign(n, val);} |
|||
|
|||
// Related to the issue #97.
|
|||
//
|
|||
// It is known that `std::vector::insert` and `std::vector::erase` in
|
|||
// the standard library implementation included in GCC 4.8.5 takes
|
|||
// `std::vector::iterator` instead of `std::vector::const_iterator`.
|
|||
// Because of the const-correctness, we cannot convert a `const_iterator` to
|
|||
// an `iterator`. It causes compilation error in GCC 4.8.5.
|
|||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
|
|||
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
|
|||
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
|
|||
# endif
|
|||
#endif
|
|||
|
|||
#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
|
|||
iterator insert(iterator p, const std::string& x) |
|||
{ |
|||
return comments.insert(p, x); |
|||
} |
|||
iterator insert(iterator p, std::string&& x) |
|||
{ |
|||
return comments.insert(p, std::move(x)); |
|||
} |
|||
void insert(iterator p, size_type n, const std::string& x) |
|||
{ |
|||
return comments.insert(p, n, x); |
|||
} |
|||
template<typename InputIterator> |
|||
void insert(iterator p, InputIterator first, InputIterator last) |
|||
{ |
|||
return comments.insert(p, first, last); |
|||
} |
|||
void insert(iterator p, std::initializer_list<std::string> ini) |
|||
{ |
|||
return comments.insert(p, ini); |
|||
} |
|||
|
|||
template<typename ... Ts> |
|||
iterator emplace(iterator p, Ts&& ... args) |
|||
{ |
|||
return comments.emplace(p, std::forward<Ts>(args)...); |
|||
} |
|||
|
|||
iterator erase(iterator pos) {return comments.erase(pos);} |
|||
iterator erase(iterator first, iterator last) |
|||
{ |
|||
return comments.erase(first, last); |
|||
} |
|||
#else
|
|||
iterator insert(const_iterator p, const std::string& x) |
|||
{ |
|||
return comments.insert(p, x); |
|||
} |
|||
iterator insert(const_iterator p, std::string&& x) |
|||
{ |
|||
return comments.insert(p, std::move(x)); |
|||
} |
|||
iterator insert(const_iterator p, size_type n, const std::string& x) |
|||
{ |
|||
return comments.insert(p, n, x); |
|||
} |
|||
template<typename InputIterator> |
|||
iterator insert(const_iterator p, InputIterator first, InputIterator last) |
|||
{ |
|||
return comments.insert(p, first, last); |
|||
} |
|||
iterator insert(const_iterator p, std::initializer_list<std::string> ini) |
|||
{ |
|||
return comments.insert(p, ini); |
|||
} |
|||
|
|||
template<typename ... Ts> |
|||
iterator emplace(const_iterator p, Ts&& ... args) |
|||
{ |
|||
return comments.emplace(p, std::forward<Ts>(args)...); |
|||
} |
|||
|
|||
iterator erase(const_iterator pos) {return comments.erase(pos);} |
|||
iterator erase(const_iterator first, const_iterator last) |
|||
{ |
|||
return comments.erase(first, last); |
|||
} |
|||
#endif
|
|||
|
|||
void swap(preserve_comments& other) {comments.swap(other.comments);} |
|||
|
|||
void push_back(const std::string& v) {comments.push_back(v);} |
|||
void push_back(std::string&& v) {comments.push_back(std::move(v));} |
|||
void pop_back() {comments.pop_back();} |
|||
|
|||
template<typename ... Ts> |
|||
void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);} |
|||
|
|||
void clear() {comments.clear();} |
|||
|
|||
size_type size() const noexcept {return comments.size();} |
|||
size_type max_size() const noexcept {return comments.max_size();} |
|||
size_type capacity() const noexcept {return comments.capacity();} |
|||
bool empty() const noexcept {return comments.empty();} |
|||
|
|||
void reserve(size_type n) {comments.reserve(n);} |
|||
void resize(size_type n) {comments.resize(n);} |
|||
void resize(size_type n, const std::string& c) {comments.resize(n, c);} |
|||
void shrink_to_fit() {comments.shrink_to_fit();} |
|||
|
|||
reference operator[](const size_type n) noexcept {return comments[n];} |
|||
const_reference operator[](const size_type n) const noexcept {return comments[n];} |
|||
reference at(const size_type n) {return comments.at(n);} |
|||
const_reference at(const size_type n) const {return comments.at(n);} |
|||
reference front() noexcept {return comments.front();} |
|||
const_reference front() const noexcept {return comments.front();} |
|||
reference back() noexcept {return comments.back();} |
|||
const_reference back() const noexcept {return comments.back();} |
|||
|
|||
pointer data() noexcept {return comments.data();} |
|||
const_pointer data() const noexcept {return comments.data();} |
|||
|
|||
iterator begin() noexcept {return comments.begin();} |
|||
iterator end() noexcept {return comments.end();} |
|||
const_iterator begin() const noexcept {return comments.begin();} |
|||
const_iterator end() const noexcept {return comments.end();} |
|||
const_iterator cbegin() const noexcept {return comments.cbegin();} |
|||
const_iterator cend() const noexcept {return comments.cend();} |
|||
|
|||
reverse_iterator rbegin() noexcept {return comments.rbegin();} |
|||
reverse_iterator rend() noexcept {return comments.rend();} |
|||
const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} |
|||
const_reverse_iterator rend() const noexcept {return comments.rend();} |
|||
const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} |
|||
const_reverse_iterator crend() const noexcept {return comments.crend();} |
|||
|
|||
friend bool operator==(const preserve_comments&, const preserve_comments&); |
|||
friend bool operator!=(const preserve_comments&, const preserve_comments&); |
|||
friend bool operator< (const preserve_comments&, const preserve_comments&); |
|||
friend bool operator<=(const preserve_comments&, const preserve_comments&); |
|||
friend bool operator> (const preserve_comments&, const preserve_comments&); |
|||
friend bool operator>=(const preserve_comments&, const preserve_comments&); |
|||
|
|||
friend void swap(preserve_comments&, std::vector<std::string>&); |
|||
friend void swap(std::vector<std::string>&, preserve_comments&); |
|||
|
|||
private: |
|||
|
|||
container_type comments; |
|||
}; |
|||
|
|||
inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} |
|||
inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} |
|||
inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} |
|||
inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} |
|||
inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} |
|||
inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} |
|||
|
|||
inline void swap(preserve_comments& lhs, preserve_comments& rhs) |
|||
{ |
|||
lhs.swap(rhs); |
|||
return; |
|||
} |
|||
inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs) |
|||
{ |
|||
lhs.comments.swap(rhs); |
|||
return; |
|||
} |
|||
inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs) |
|||
{ |
|||
lhs.swap(rhs.comments); |
|||
return; |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com) |
|||
{ |
|||
for(const auto& c : com) |
|||
{ |
|||
os << '#' << c << '\n'; |
|||
} |
|||
return os; |
|||
} |
|||
|
|||
namespace detail |
|||
{ |
|||
|
|||
// To provide the same interface with `preserve_comments`, `discard_comments`
|
|||
// should have an iterator. But it does not contain anything, so we need to
|
|||
// add an iterator that points nothing.
|
|||
//
|
|||
// It always points null, so DO NOT unwrap this iterator. It always crashes
|
|||
// your program.
|
|||
template<typename T, bool is_const> |
|||
struct empty_iterator |
|||
{ |
|||
using value_type = T; |
|||
using reference_type = typename std::conditional<is_const, T const&, T&>::type; |
|||
using pointer_type = typename std::conditional<is_const, T const*, T*>::type; |
|||
using difference_type = std::ptrdiff_t; |
|||
using iterator_category = std::random_access_iterator_tag; |
|||
|
|||
empty_iterator() = default; |
|||
~empty_iterator() = default; |
|||
empty_iterator(empty_iterator const&) = default; |
|||
empty_iterator(empty_iterator &&) = default; |
|||
empty_iterator& operator=(empty_iterator const&) = default; |
|||
empty_iterator& operator=(empty_iterator &&) = default; |
|||
|
|||
// DO NOT call these operators.
|
|||
reference_type operator*() const noexcept {std::terminate();} |
|||
pointer_type operator->() const noexcept {return nullptr;} |
|||
reference_type operator[](difference_type) const noexcept {return this->operator*();} |
|||
|
|||
// These operators do nothing.
|
|||
empty_iterator& operator++() noexcept {return *this;} |
|||
empty_iterator operator++(int) noexcept {return *this;} |
|||
empty_iterator& operator--() noexcept {return *this;} |
|||
empty_iterator operator--(int) noexcept {return *this;} |
|||
|
|||
empty_iterator& operator+=(difference_type) noexcept {return *this;} |
|||
empty_iterator& operator-=(difference_type) noexcept {return *this;} |
|||
|
|||
empty_iterator operator+(difference_type) const noexcept {return *this;} |
|||
empty_iterator operator-(difference_type) const noexcept {return *this;} |
|||
}; |
|||
|
|||
template<typename T, bool C> |
|||
bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} |
|||
template<typename T, bool C> |
|||
bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} |
|||
template<typename T, bool C> |
|||
bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} |
|||
template<typename T, bool C> |
|||
bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} |
|||
template<typename T, bool C> |
|||
bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;} |
|||
template<typename T, bool C> |
|||
bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;} |
|||
|
|||
template<typename T, bool C> |
|||
typename empty_iterator<T, C>::difference_type |
|||
operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;} |
|||
|
|||
template<typename T, bool C> |
|||
empty_iterator<T, C> |
|||
operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;} |
|||
template<typename T, bool C> |
|||
empty_iterator<T, C> |
|||
operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;} |
|||
|
|||
} // detail
|
|||
|
|||
// The default comment type. It discards all the comments. It requires only one
|
|||
// byte to contain, so the memory footprint is smaller than preserve_comments.
|
|||
//
|
|||
// It just ignores `push_back`, `insert`, `erase`, and any other modifications.
|
|||
// IT always returns size() == 0, the iterator taken by `begin()` is always the
|
|||
// same as that of `end()`, and accessing through `operator[]` or iterators
|
|||
// always causes a segmentation fault. DO NOT access to the element of this.
|
|||
//
|
|||
// Why this is chose as the default type is because the last version (2.x.y)
|
|||
// does not contain any comments in a value. To minimize the impact on the
|
|||
// efficiency, this is chosen as a default.
|
|||
//
|
|||
// To reduce the memory footprint, later we can try empty base optimization (EBO).
|
|||
struct discard_comments |
|||
{ |
|||
using size_type = std::size_t; |
|||
using difference_type = std::ptrdiff_t; |
|||
using value_type = std::string; |
|||
using reference = std::string&; |
|||
using const_reference = std::string const&; |
|||
using pointer = std::string*; |
|||
using const_pointer = std::string const*; |
|||
using iterator = detail::empty_iterator<std::string, false>; |
|||
using const_iterator = detail::empty_iterator<std::string, true>; |
|||
using reverse_iterator = detail::empty_iterator<std::string, false>; |
|||
using const_reverse_iterator = detail::empty_iterator<std::string, true>; |
|||
|
|||
discard_comments() = default; |
|||
~discard_comments() = default; |
|||
discard_comments(discard_comments const&) = default; |
|||
discard_comments(discard_comments &&) = default; |
|||
discard_comments& operator=(discard_comments const&) = default; |
|||
discard_comments& operator=(discard_comments &&) = default; |
|||
|
|||
explicit discard_comments(const std::vector<std::string>&) noexcept {} |
|||
explicit discard_comments(std::vector<std::string>&&) noexcept {} |
|||
discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;} |
|||
discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;} |
|||
|
|||
explicit discard_comments(const preserve_comments&) noexcept {} |
|||
|
|||
explicit discard_comments(size_type) noexcept {} |
|||
discard_comments(size_type, const std::string&) noexcept {} |
|||
discard_comments(std::initializer_list<std::string>) noexcept {} |
|||
template<typename InputIterator> |
|||
discard_comments(InputIterator, InputIterator) noexcept {} |
|||
|
|||
template<typename InputIterator> |
|||
void assign(InputIterator, InputIterator) noexcept {} |
|||
void assign(std::initializer_list<std::string>) noexcept {} |
|||
void assign(size_type, const std::string&) noexcept {} |
|||
|
|||
iterator insert(const_iterator, const std::string&) {return iterator{};} |
|||
iterator insert(const_iterator, std::string&&) {return iterator{};} |
|||
iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} |
|||
template<typename InputIterator> |
|||
iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} |
|||
iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};} |
|||
|
|||
template<typename ... Ts> |
|||
iterator emplace(const_iterator, Ts&& ...) {return iterator{};} |
|||
iterator erase(const_iterator) {return iterator{};} |
|||
iterator erase(const_iterator, const_iterator) {return iterator{};} |
|||
|
|||
void swap(discard_comments&) {return;} |
|||
|
|||
void push_back(const std::string&) {return;} |
|||
void push_back(std::string&& ) {return;} |
|||
void pop_back() {return;} |
|||
|
|||
template<typename ... Ts> |
|||
void emplace_back(Ts&& ...) {return;} |
|||
|
|||
void clear() {return;} |
|||
|
|||
size_type size() const noexcept {return 0;} |
|||
size_type max_size() const noexcept {return 0;} |
|||
size_type capacity() const noexcept {return 0;} |
|||
bool empty() const noexcept {return true;} |
|||
|
|||
void reserve(size_type) {return;} |
|||
void resize(size_type) {return;} |
|||
void resize(size_type, const std::string&) {return;} |
|||
void shrink_to_fit() {return;} |
|||
|
|||
// DO NOT access to the element of this container. This container is always
|
|||
// empty, so accessing through operator[], front/back, data causes address
|
|||
// error.
|
|||
|
|||
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");} |
|||
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");} |
|||
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} |
|||
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} |
|||
reference front() noexcept {never_call("toml::discard_comment::front");} |
|||
const_reference front() const noexcept {never_call("toml::discard_comment::front");} |
|||
reference back() noexcept {never_call("toml::discard_comment::back");} |
|||
const_reference back() const noexcept {never_call("toml::discard_comment::back");} |
|||
|
|||
pointer data() noexcept {return nullptr;} |
|||
const_pointer data() const noexcept {return nullptr;} |
|||
|
|||
iterator begin() noexcept {return iterator{};} |
|||
iterator end() noexcept {return iterator{};} |
|||
const_iterator begin() const noexcept {return const_iterator{};} |
|||
const_iterator end() const noexcept {return const_iterator{};} |
|||
const_iterator cbegin() const noexcept {return const_iterator{};} |
|||
const_iterator cend() const noexcept {return const_iterator{};} |
|||
|
|||
reverse_iterator rbegin() noexcept {return iterator{};} |
|||
reverse_iterator rend() noexcept {return iterator{};} |
|||
const_reverse_iterator rbegin() const noexcept {return const_iterator{};} |
|||
const_reverse_iterator rend() const noexcept {return const_iterator{};} |
|||
const_reverse_iterator crbegin() const noexcept {return const_iterator{};} |
|||
const_reverse_iterator crend() const noexcept {return const_iterator{};} |
|||
|
|||
private: |
|||
|
|||
[[noreturn]] static void never_call(const char *const this_function) |
|||
{ |
|||
#ifdef __has_builtin
|
|||
# if __has_builtin(__builtin_unreachable)
|
|||
__builtin_unreachable(); |
|||
# endif
|
|||
#endif
|
|||
throw std::logic_error{this_function}; |
|||
} |
|||
}; |
|||
|
|||
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} |
|||
inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} |
|||
inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} |
|||
inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} |
|||
inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} |
|||
inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} |
|||
|
|||
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&) |
|||
{ |
|||
return os; |
|||
} |
|||
|
|||
} // toml11
|
|||
#endif// TOML11_COMMENTS_HPP
|
@ -0,0 +1,631 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_DATETIME_HPP
|
|||
#define TOML11_DATETIME_HPP
|
|||
#include <cstdint>
|
|||
#include <cstdlib>
|
|||
#include <ctime>
|
|||
|
|||
#include <array>
|
|||
#include <chrono>
|
|||
#include <iomanip>
|
|||
#include <ostream>
|
|||
#include <tuple>
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
|
|||
// provided in the absolutely same purpose, but C++11 is actually not compatible
|
|||
// with C11. We need to dispatch the function depending on the OS.
|
|||
namespace detail |
|||
{ |
|||
// TODO: find more sophisticated way to handle this
|
|||
#if defined(_MSC_VER)
|
|||
inline std::tm localtime_s(const std::time_t* src) |
|||
{ |
|||
std::tm dst; |
|||
const auto result = ::localtime_s(&dst, src); |
|||
if (result) { throw std::runtime_error("localtime_s failed."); } |
|||
return dst; |
|||
} |
|||
inline std::tm gmtime_s(const std::time_t* src) |
|||
{ |
|||
std::tm dst; |
|||
const auto result = ::gmtime_s(&dst, src); |
|||
if (result) { throw std::runtime_error("gmtime_s failed."); } |
|||
return dst; |
|||
} |
|||
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
|
|||
inline std::tm localtime_s(const std::time_t* src) |
|||
{ |
|||
std::tm dst; |
|||
const auto result = ::localtime_r(src, &dst); |
|||
if (!result) { throw std::runtime_error("localtime_r failed."); } |
|||
return dst; |
|||
} |
|||
inline std::tm gmtime_s(const std::time_t* src) |
|||
{ |
|||
std::tm dst; |
|||
const auto result = ::gmtime_r(src, &dst); |
|||
if (!result) { throw std::runtime_error("gmtime_r failed."); } |
|||
return dst; |
|||
} |
|||
#else // fallback. not threadsafe
|
|||
inline std::tm localtime_s(const std::time_t* src) |
|||
{ |
|||
const auto result = std::localtime(src); |
|||
if (!result) { throw std::runtime_error("localtime failed."); } |
|||
return *result; |
|||
} |
|||
inline std::tm gmtime_s(const std::time_t* src) |
|||
{ |
|||
const auto result = std::gmtime(src); |
|||
if (!result) { throw std::runtime_error("gmtime failed."); } |
|||
return *result; |
|||
} |
|||
#endif
|
|||
} // detail
|
|||
|
|||
enum class month_t : std::uint8_t |
|||
{ |
|||
Jan = 0, |
|||
Feb = 1, |
|||
Mar = 2, |
|||
Apr = 3, |
|||
May = 4, |
|||
Jun = 5, |
|||
Jul = 6, |
|||
Aug = 7, |
|||
Sep = 8, |
|||
Oct = 9, |
|||
Nov = 10, |
|||
Dec = 11 |
|||
}; |
|||
|
|||
struct local_date |
|||
{ |
|||
std::int16_t year{}; // A.D. (like, 2018)
|
|||
std::uint8_t month{}; // [0, 11]
|
|||
std::uint8_t day{}; // [1, 31]
|
|||
|
|||
local_date(int y, month_t m, int d) |
|||
: year (static_cast<std::int16_t>(y)), |
|||
month(static_cast<std::uint8_t>(m)), |
|||
day (static_cast<std::uint8_t>(d)) |
|||
{} |
|||
|
|||
explicit local_date(const std::tm& t) |
|||
: year (static_cast<std::int16_t>(t.tm_year + 1900)), |
|||
month(static_cast<std::uint8_t>(t.tm_mon)), |
|||
day (static_cast<std::uint8_t>(t.tm_mday)) |
|||
{} |
|||
|
|||
explicit local_date(const std::chrono::system_clock::time_point& tp) |
|||
{ |
|||
const auto t = std::chrono::system_clock::to_time_t(tp); |
|||
const auto time = detail::localtime_s(&t); |
|||
*this = local_date(time); |
|||
} |
|||
|
|||
explicit local_date(const std::time_t t) |
|||
: local_date(std::chrono::system_clock::from_time_t(t)) |
|||
{} |
|||
|
|||
operator std::chrono::system_clock::time_point() const |
|||
{ |
|||
// std::mktime returns date as local time zone. no conversion needed
|
|||
std::tm t; |
|||
t.tm_sec = 0; |
|||
t.tm_min = 0; |
|||
t.tm_hour = 0; |
|||
t.tm_mday = static_cast<int>(this->day); |
|||
t.tm_mon = static_cast<int>(this->month); |
|||
t.tm_year = static_cast<int>(this->year) - 1900; |
|||
t.tm_wday = 0; // the value will be ignored
|
|||
t.tm_yday = 0; // the value will be ignored
|
|||
t.tm_isdst = -1; |
|||
return std::chrono::system_clock::from_time_t(std::mktime(&t)); |
|||
} |
|||
|
|||
operator std::time_t() const |
|||
{ |
|||
return std::chrono::system_clock::to_time_t( |
|||
std::chrono::system_clock::time_point(*this)); |
|||
} |
|||
|
|||
local_date() = default; |
|||
~local_date() = default; |
|||
local_date(local_date const&) = default; |
|||
local_date(local_date&&) = default; |
|||
local_date& operator=(local_date const&) = default; |
|||
local_date& operator=(local_date&&) = default; |
|||
}; |
|||
|
|||
inline bool operator==(const local_date& lhs, const local_date& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.year, lhs.month, lhs.day) == |
|||
std::make_tuple(rhs.year, rhs.month, rhs.day); |
|||
} |
|||
inline bool operator!=(const local_date& lhs, const local_date& rhs) |
|||
{ |
|||
return !(lhs == rhs); |
|||
} |
|||
inline bool operator< (const local_date& lhs, const local_date& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.year, lhs.month, lhs.day) < |
|||
std::make_tuple(rhs.year, rhs.month, rhs.day); |
|||
} |
|||
inline bool operator<=(const local_date& lhs, const local_date& rhs) |
|||
{ |
|||
return (lhs < rhs) || (lhs == rhs); |
|||
} |
|||
inline bool operator> (const local_date& lhs, const local_date& rhs) |
|||
{ |
|||
return !(lhs <= rhs); |
|||
} |
|||
inline bool operator>=(const local_date& lhs, const local_date& rhs) |
|||
{ |
|||
return !(lhs < rhs); |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date) |
|||
{ |
|||
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-'; |
|||
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-'; |
|||
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ; |
|||
return os; |
|||
} |
|||
|
|||
struct local_time |
|||
{ |
|||
std::uint8_t hour{}; // [0, 23]
|
|||
std::uint8_t minute{}; // [0, 59]
|
|||
std::uint8_t second{}; // [0, 60]
|
|||
std::uint16_t millisecond{}; // [0, 999]
|
|||
std::uint16_t microsecond{}; // [0, 999]
|
|||
std::uint16_t nanosecond{}; // [0, 999]
|
|||
|
|||
local_time(int h, int m, int s, |
|||
int ms = 0, int us = 0, int ns = 0) |
|||
: hour (static_cast<std::uint8_t>(h)), |
|||
minute(static_cast<std::uint8_t>(m)), |
|||
second(static_cast<std::uint8_t>(s)), |
|||
millisecond(static_cast<std::uint16_t>(ms)), |
|||
microsecond(static_cast<std::uint16_t>(us)), |
|||
nanosecond (static_cast<std::uint16_t>(ns)) |
|||
{} |
|||
|
|||
explicit local_time(const std::tm& t) |
|||
: hour (static_cast<std::uint8_t>(t.tm_hour)), |
|||
minute(static_cast<std::uint8_t>(t.tm_min)), |
|||
second(static_cast<std::uint8_t>(t.tm_sec)), |
|||
millisecond(0), microsecond(0), nanosecond(0) |
|||
{} |
|||
|
|||
template<typename Rep, typename Period> |
|||
explicit local_time(const std::chrono::duration<Rep, Period>& t) |
|||
{ |
|||
const auto h = std::chrono::duration_cast<std::chrono::hours>(t); |
|||
this->hour = static_cast<std::uint8_t>(h.count()); |
|||
const auto t2 = t - h; |
|||
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2); |
|||
this->minute = static_cast<std::uint8_t>(m.count()); |
|||
const auto t3 = t2 - m; |
|||
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3); |
|||
this->second = static_cast<std::uint8_t>(s.count()); |
|||
const auto t4 = t3 - s; |
|||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4); |
|||
this->millisecond = static_cast<std::uint16_t>(ms.count()); |
|||
const auto t5 = t4 - ms; |
|||
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5); |
|||
this->microsecond = static_cast<std::uint16_t>(us.count()); |
|||
const auto t6 = t5 - us; |
|||
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6); |
|||
this->nanosecond = static_cast<std::uint16_t>(ns.count()); |
|||
} |
|||
|
|||
operator std::chrono::nanoseconds() const |
|||
{ |
|||
return std::chrono::nanoseconds (this->nanosecond) + |
|||
std::chrono::microseconds(this->microsecond) + |
|||
std::chrono::milliseconds(this->millisecond) + |
|||
std::chrono::seconds(this->second) + |
|||
std::chrono::minutes(this->minute) + |
|||
std::chrono::hours(this->hour); |
|||
} |
|||
|
|||
local_time() = default; |
|||
~local_time() = default; |
|||
local_time(local_time const&) = default; |
|||
local_time(local_time&&) = default; |
|||
local_time& operator=(local_time const&) = default; |
|||
local_time& operator=(local_time&&) = default; |
|||
}; |
|||
|
|||
inline bool operator==(const local_time& lhs, const local_time& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == |
|||
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); |
|||
} |
|||
inline bool operator!=(const local_time& lhs, const local_time& rhs) |
|||
{ |
|||
return !(lhs == rhs); |
|||
} |
|||
inline bool operator< (const local_time& lhs, const local_time& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < |
|||
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); |
|||
} |
|||
inline bool operator<=(const local_time& lhs, const local_time& rhs) |
|||
{ |
|||
return (lhs < rhs) || (lhs == rhs); |
|||
} |
|||
inline bool operator> (const local_time& lhs, const local_time& rhs) |
|||
{ |
|||
return !(lhs <= rhs); |
|||
} |
|||
inline bool operator>=(const local_time& lhs, const local_time& rhs) |
|||
{ |
|||
return !(lhs < rhs); |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const local_time& time) |
|||
{ |
|||
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':'; |
|||
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':'; |
|||
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second); |
|||
if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) |
|||
{ |
|||
os << '.'; |
|||
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond); |
|||
if(time.microsecond != 0 || time.nanosecond != 0) |
|||
{ |
|||
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond); |
|||
if(time.nanosecond != 0) |
|||
{ |
|||
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond); |
|||
} |
|||
} |
|||
} |
|||
return os; |
|||
} |
|||
|
|||
struct time_offset |
|||
{ |
|||
std::int8_t hour{}; // [-12, 12]
|
|||
std::int8_t minute{}; // [-59, 59]
|
|||
|
|||
time_offset(int h, int m) |
|||
: hour (static_cast<std::int8_t>(h)), |
|||
minute(static_cast<std::int8_t>(m)) |
|||
{} |
|||
|
|||
operator std::chrono::minutes() const |
|||
{ |
|||
return std::chrono::minutes(this->minute) + |
|||
std::chrono::hours(this->hour); |
|||
} |
|||
|
|||
time_offset() = default; |
|||
~time_offset() = default; |
|||
time_offset(time_offset const&) = default; |
|||
time_offset(time_offset&&) = default; |
|||
time_offset& operator=(time_offset const&) = default; |
|||
time_offset& operator=(time_offset&&) = default; |
|||
}; |
|||
|
|||
inline bool operator==(const time_offset& lhs, const time_offset& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.hour, lhs.minute) == |
|||
std::make_tuple(rhs.hour, rhs.minute); |
|||
} |
|||
inline bool operator!=(const time_offset& lhs, const time_offset& rhs) |
|||
{ |
|||
return !(lhs == rhs); |
|||
} |
|||
inline bool operator< (const time_offset& lhs, const time_offset& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.hour, lhs.minute) < |
|||
std::make_tuple(rhs.hour, rhs.minute); |
|||
} |
|||
inline bool operator<=(const time_offset& lhs, const time_offset& rhs) |
|||
{ |
|||
return (lhs < rhs) || (lhs == rhs); |
|||
} |
|||
inline bool operator> (const time_offset& lhs, const time_offset& rhs) |
|||
{ |
|||
return !(lhs <= rhs); |
|||
} |
|||
inline bool operator>=(const time_offset& lhs, const time_offset& rhs) |
|||
{ |
|||
return !(lhs < rhs); |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset) |
|||
{ |
|||
if(offset.hour == 0 && offset.minute == 0) |
|||
{ |
|||
os << 'Z'; |
|||
return os; |
|||
} |
|||
int minute = static_cast<int>(offset.hour) * 60 + offset.minute; |
|||
if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} |
|||
os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; |
|||
os << std::setfill('0') << std::setw(2) << minute % 60; |
|||
return os; |
|||
} |
|||
|
|||
struct local_datetime |
|||
{ |
|||
local_date date{}; |
|||
local_time time{}; |
|||
|
|||
local_datetime(local_date d, local_time t): date(d), time(t) {} |
|||
|
|||
explicit local_datetime(const std::tm& t): date(t), time(t){} |
|||
|
|||
explicit local_datetime(const std::chrono::system_clock::time_point& tp) |
|||
{ |
|||
const auto t = std::chrono::system_clock::to_time_t(tp); |
|||
std::tm ltime = detail::localtime_s(&t); |
|||
|
|||
this->date = local_date(ltime); |
|||
this->time = local_time(ltime); |
|||
|
|||
// std::tm lacks subsecond information, so diff between tp and tm
|
|||
// can be used to get millisecond & microsecond information.
|
|||
const auto t_diff = tp - |
|||
std::chrono::system_clock::from_time_t(std::mktime(<ime)); |
|||
this->time.millisecond = static_cast<std::uint16_t>( |
|||
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count()); |
|||
this->time.microsecond = static_cast<std::uint16_t>( |
|||
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count()); |
|||
this->time.nanosecond = static_cast<std::uint16_t>( |
|||
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count()); |
|||
} |
|||
|
|||
explicit local_datetime(const std::time_t t) |
|||
: local_datetime(std::chrono::system_clock::from_time_t(t)) |
|||
{} |
|||
|
|||
operator std::chrono::system_clock::time_point() const |
|||
{ |
|||
using internal_duration = |
|||
typename std::chrono::system_clock::time_point::duration; |
|||
|
|||
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
|
|||
// of local_date and local_time independently, the conversion fails if
|
|||
// it is the day when DST begins or ends. Since local_date considers the
|
|||
// time is 00:00 A.M. and local_time does not consider DST because it
|
|||
// does not have any date information. We need to consider both date and
|
|||
// time information at the same time to convert it correctly.
|
|||
|
|||
std::tm t; |
|||
t.tm_sec = static_cast<int>(this->time.second); |
|||
t.tm_min = static_cast<int>(this->time.minute); |
|||
t.tm_hour = static_cast<int>(this->time.hour); |
|||
t.tm_mday = static_cast<int>(this->date.day); |
|||
t.tm_mon = static_cast<int>(this->date.month); |
|||
t.tm_year = static_cast<int>(this->date.year) - 1900; |
|||
t.tm_wday = 0; // the value will be ignored
|
|||
t.tm_yday = 0; // the value will be ignored
|
|||
t.tm_isdst = -1; |
|||
|
|||
// std::mktime returns date as local time zone. no conversion needed
|
|||
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); |
|||
dt += std::chrono::duration_cast<internal_duration>( |
|||
std::chrono::milliseconds(this->time.millisecond) + |
|||
std::chrono::microseconds(this->time.microsecond) + |
|||
std::chrono::nanoseconds (this->time.nanosecond)); |
|||
return dt; |
|||
} |
|||
|
|||
operator std::time_t() const |
|||
{ |
|||
return std::chrono::system_clock::to_time_t( |
|||
std::chrono::system_clock::time_point(*this)); |
|||
} |
|||
|
|||
local_datetime() = default; |
|||
~local_datetime() = default; |
|||
local_datetime(local_datetime const&) = default; |
|||
local_datetime(local_datetime&&) = default; |
|||
local_datetime& operator=(local_datetime const&) = default; |
|||
local_datetime& operator=(local_datetime&&) = default; |
|||
}; |
|||
|
|||
inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.date, lhs.time) == |
|||
std::make_tuple(rhs.date, rhs.time); |
|||
} |
|||
inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) |
|||
{ |
|||
return !(lhs == rhs); |
|||
} |
|||
inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.date, lhs.time) < |
|||
std::make_tuple(rhs.date, rhs.time); |
|||
} |
|||
inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) |
|||
{ |
|||
return (lhs < rhs) || (lhs == rhs); |
|||
} |
|||
inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) |
|||
{ |
|||
return !(lhs <= rhs); |
|||
} |
|||
inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) |
|||
{ |
|||
return !(lhs < rhs); |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt) |
|||
{ |
|||
os << dt.date << 'T' << dt.time; |
|||
return os; |
|||
} |
|||
|
|||
struct offset_datetime |
|||
{ |
|||
local_date date{}; |
|||
local_time time{}; |
|||
time_offset offset{}; |
|||
|
|||
offset_datetime(local_date d, local_time t, time_offset o) |
|||
: date(d), time(t), offset(o) |
|||
{} |
|||
offset_datetime(const local_datetime& dt, time_offset o) |
|||
: date(dt.date), time(dt.time), offset(o) |
|||
{} |
|||
explicit offset_datetime(const local_datetime& ld) |
|||
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) |
|||
// use the current local timezone offset
|
|||
{} |
|||
explicit offset_datetime(const std::chrono::system_clock::time_point& tp) |
|||
: offset(0, 0) // use gmtime
|
|||
{ |
|||
const auto timet = std::chrono::system_clock::to_time_t(tp); |
|||
const auto tm = detail::gmtime_s(&timet); |
|||
this->date = local_date(tm); |
|||
this->time = local_time(tm); |
|||
} |
|||
explicit offset_datetime(const std::time_t& t) |
|||
: offset(0, 0) // use gmtime
|
|||
{ |
|||
const auto tm = detail::gmtime_s(&t); |
|||
this->date = local_date(tm); |
|||
this->time = local_time(tm); |
|||
} |
|||
explicit offset_datetime(const std::tm& t) |
|||
: offset(0, 0) // assume gmtime
|
|||
{ |
|||
this->date = local_date(t); |
|||
this->time = local_time(t); |
|||
} |
|||
|
|||
operator std::chrono::system_clock::time_point() const |
|||
{ |
|||
// get date-time
|
|||
using internal_duration = |
|||
typename std::chrono::system_clock::time_point::duration; |
|||
|
|||
// first, convert it to local date-time information in the same way as
|
|||
// local_datetime does. later we will use time_t to adjust time offset.
|
|||
std::tm t; |
|||
t.tm_sec = static_cast<int>(this->time.second); |
|||
t.tm_min = static_cast<int>(this->time.minute); |
|||
t.tm_hour = static_cast<int>(this->time.hour); |
|||
t.tm_mday = static_cast<int>(this->date.day); |
|||
t.tm_mon = static_cast<int>(this->date.month); |
|||
t.tm_year = static_cast<int>(this->date.year) - 1900; |
|||
t.tm_wday = 0; // the value will be ignored
|
|||
t.tm_yday = 0; // the value will be ignored
|
|||
t.tm_isdst = -1; |
|||
const std::time_t tp_loc = std::mktime(std::addressof(t)); |
|||
|
|||
auto tp = std::chrono::system_clock::from_time_t(tp_loc); |
|||
tp += std::chrono::duration_cast<internal_duration>( |
|||
std::chrono::milliseconds(this->time.millisecond) + |
|||
std::chrono::microseconds(this->time.microsecond) + |
|||
std::chrono::nanoseconds (this->time.nanosecond)); |
|||
|
|||
// Since mktime uses local time zone, it should be corrected.
|
|||
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
|
|||
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
|
|||
// to add `+09:00` to `03:00:00Z`.
|
|||
// Here, it uses the time_t converted from date-time info to handle
|
|||
// daylight saving time.
|
|||
const auto ofs = get_local_offset(std::addressof(tp_loc)); |
|||
tp += std::chrono::hours (ofs.hour); |
|||
tp += std::chrono::minutes(ofs.minute); |
|||
|
|||
// We got `12:00:00Z` by correcting local timezone applied by mktime.
|
|||
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
|
|||
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
|
|||
// So we need to subtract the offset.
|
|||
tp -= std::chrono::minutes(this->offset); |
|||
return tp; |
|||
} |
|||
|
|||
operator std::time_t() const |
|||
{ |
|||
return std::chrono::system_clock::to_time_t( |
|||
std::chrono::system_clock::time_point(*this)); |
|||
} |
|||
|
|||
offset_datetime() = default; |
|||
~offset_datetime() = default; |
|||
offset_datetime(offset_datetime const&) = default; |
|||
offset_datetime(offset_datetime&&) = default; |
|||
offset_datetime& operator=(offset_datetime const&) = default; |
|||
offset_datetime& operator=(offset_datetime&&) = default; |
|||
|
|||
private: |
|||
|
|||
static time_offset get_local_offset(const std::time_t* tp) |
|||
{ |
|||
// get local timezone with the same date-time information as mktime
|
|||
const auto t = detail::localtime_s(tp); |
|||
|
|||
std::array<char, 6> buf; |
|||
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
|
|||
if(result != 5) |
|||
{ |
|||
throw std::runtime_error("toml::offset_datetime: cannot obtain " |
|||
"timezone information of current env"); |
|||
} |
|||
const int ofs = std::atoi(buf.data()); |
|||
const int ofs_h = ofs / 100; |
|||
const int ofs_m = ofs - (ofs_h * 100); |
|||
return time_offset(ofs_h, ofs_m); |
|||
} |
|||
}; |
|||
|
|||
inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.date, lhs.time, lhs.offset) == |
|||
std::make_tuple(rhs.date, rhs.time, rhs.offset); |
|||
} |
|||
inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) |
|||
{ |
|||
return !(lhs == rhs); |
|||
} |
|||
inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) |
|||
{ |
|||
return std::make_tuple(lhs.date, lhs.time, lhs.offset) < |
|||
std::make_tuple(rhs.date, rhs.time, rhs.offset); |
|||
} |
|||
inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) |
|||
{ |
|||
return (lhs < rhs) || (lhs == rhs); |
|||
} |
|||
inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) |
|||
{ |
|||
return !(lhs <= rhs); |
|||
} |
|||
inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) |
|||
{ |
|||
return !(lhs < rhs); |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const offset_datetime& dt) |
|||
{ |
|||
os << dt.date << 'T' << dt.time << dt.offset; |
|||
return os; |
|||
} |
|||
|
|||
}//toml
|
|||
#endif// TOML11_DATETIME
|
@ -0,0 +1,83 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_EXCEPTION_HPP
|
|||
#define TOML11_EXCEPTION_HPP
|
|||
|
|||
#include <array>
|
|||
#include <string>
|
|||
#include <stdexcept>
|
|||
|
|||
#include <cstring>
|
|||
|
|||
#include "source_location.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
struct file_io_error : public std::runtime_error |
|||
{ |
|||
public: |
|||
file_io_error(int errnum, const std::string& msg, const std::string& fname) |
|||
: std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)), |
|||
errno_(errnum) |
|||
{} |
|||
|
|||
int get_errno() const noexcept {return errno_;} |
|||
|
|||
private: |
|||
int errno_; |
|||
}; |
|||
|
|||
struct exception : public std::exception |
|||
{ |
|||
public: |
|||
explicit exception(const source_location& loc): loc_(loc) {} |
|||
virtual ~exception() noexcept override = default; |
|||
virtual const char* what() const noexcept override {return "";} |
|||
virtual source_location const& location() const noexcept {return loc_;} |
|||
|
|||
protected: |
|||
source_location loc_; |
|||
}; |
|||
|
|||
struct syntax_error : public toml::exception |
|||
{ |
|||
public: |
|||
explicit syntax_error(const std::string& what_arg, const source_location& loc) |
|||
: exception(loc), what_(what_arg) |
|||
{} |
|||
virtual ~syntax_error() noexcept override = default; |
|||
virtual const char* what() const noexcept override {return what_.c_str();} |
|||
|
|||
protected: |
|||
std::string what_; |
|||
}; |
|||
|
|||
struct type_error : public toml::exception |
|||
{ |
|||
public: |
|||
explicit type_error(const std::string& what_arg, const source_location& loc) |
|||
: exception(loc), what_(what_arg) |
|||
{} |
|||
virtual ~type_error() noexcept override = default; |
|||
virtual const char* what() const noexcept override {return what_.c_str();} |
|||
|
|||
protected: |
|||
std::string what_; |
|||
}; |
|||
|
|||
struct internal_error : public toml::exception |
|||
{ |
|||
public: |
|||
explicit internal_error(const std::string& what_arg, const source_location& loc) |
|||
: exception(loc), what_(what_arg) |
|||
{} |
|||
virtual ~internal_error() noexcept override = default; |
|||
virtual const char* what() const noexcept override {return what_.c_str();} |
|||
|
|||
protected: |
|||
std::string what_; |
|||
}; |
|||
|
|||
} // toml
|
|||
#endif // TOML_EXCEPTION
|
@ -0,0 +1,19 @@ |
|||
// Copyright Toru Niina 2019.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_FROM_HPP
|
|||
#define TOML11_FROM_HPP
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
template<typename T> |
|||
struct from; |
|||
// {
|
|||
// static T from_toml(const toml::value& v)
|
|||
// {
|
|||
// // User-defined conversions ...
|
|||
// }
|
|||
// };
|
|||
|
|||
} // toml
|
|||
#endif // TOML11_FROM_HPP
|
1154
src/toml/get.hpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,19 @@ |
|||
// Copyright Toru Niina 2019.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_INTO_HPP
|
|||
#define TOML11_INTO_HPP
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
template<typename T> |
|||
struct into; |
|||
// {
|
|||
// static toml::value into_toml(const T& user_defined_type)
|
|||
// {
|
|||
// // User-defined conversions ...
|
|||
// }
|
|||
// };
|
|||
|
|||
} // toml
|
|||
#endif // TOML11_INTO_HPP
|
@ -0,0 +1,294 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_LEXER_HPP
|
|||
#define TOML11_LEXER_HPP
|
|||
#include <istream>
|
|||
#include <sstream>
|
|||
#include <stdexcept>
|
|||
|
|||
#include "combinator.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
namespace detail |
|||
{ |
|||
|
|||
// these scans contents from current location in a container of char
|
|||
// and extract a region that matches their own pattern.
|
|||
// to see the implementation of each component, see combinator.hpp.
|
|||
|
|||
using lex_wschar = either<character<' '>, character<'\t'>>; |
|||
using lex_ws = repeat<lex_wschar, at_least<1>>; |
|||
using lex_newline = either<character<'\n'>, |
|||
sequence<character<'\r'>, character<'\n'>>>; |
|||
using lex_lower = in_range<'a', 'z'>; |
|||
using lex_upper = in_range<'A', 'Z'>; |
|||
using lex_alpha = either<lex_lower, lex_upper>; |
|||
using lex_digit = in_range<'0', '9'>; |
|||
using lex_nonzero = in_range<'1', '9'>; |
|||
using lex_oct_dig = in_range<'0', '7'>; |
|||
using lex_bin_dig = in_range<'0', '1'>; |
|||
using lex_hex_dig = either<lex_digit, in_range<'A', 'F'>, in_range<'a', 'f'>>; |
|||
|
|||
using lex_hex_prefix = sequence<character<'0'>, character<'x'>>; |
|||
using lex_oct_prefix = sequence<character<'0'>, character<'o'>>; |
|||
using lex_bin_prefix = sequence<character<'0'>, character<'b'>>; |
|||
using lex_underscore = character<'_'>; |
|||
using lex_plus = character<'+'>; |
|||
using lex_minus = character<'-'>; |
|||
using lex_sign = either<lex_plus, lex_minus>; |
|||
|
|||
// digit | nonzero 1*(digit | _ digit)
|
|||
using lex_unsigned_dec_int = either<sequence<lex_nonzero, repeat< |
|||
either<lex_digit, sequence<lex_underscore, lex_digit>>, at_least<1>>>, |
|||
lex_digit>; |
|||
// (+|-)? unsigned_dec_int
|
|||
using lex_dec_int = sequence<maybe<lex_sign>, lex_unsigned_dec_int>; |
|||
|
|||
// hex_prefix hex_dig *(hex_dig | _ hex_dig)
|
|||
using lex_hex_int = sequence<lex_hex_prefix, sequence<lex_hex_dig, repeat< |
|||
either<lex_hex_dig, sequence<lex_underscore, lex_hex_dig>>, unlimited>>>; |
|||
// oct_prefix oct_dig *(oct_dig | _ oct_dig)
|
|||
using lex_oct_int = sequence<lex_oct_prefix, sequence<lex_oct_dig, repeat< |
|||
either<lex_oct_dig, sequence<lex_underscore, lex_oct_dig>>, unlimited>>>; |
|||
// bin_prefix bin_dig *(bin_dig | _ bin_dig)
|
|||
using lex_bin_int = sequence<lex_bin_prefix, sequence<lex_bin_dig, repeat< |
|||
either<lex_bin_dig, sequence<lex_underscore, lex_bin_dig>>, unlimited>>>; |
|||
|
|||
// (dec_int | hex_int | oct_int | bin_int)
|
|||
using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>; |
|||
|
|||
// ===========================================================================
|
|||
|
|||
using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>; |
|||
using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>; |
|||
using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>; |
|||
|
|||
using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit, |
|||
sequence<lex_underscore, lex_digit>>, unlimited>>; |
|||
|
|||
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>; |
|||
|
|||
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>, |
|||
maybe<lex_sign>, lex_zero_prefixable_int>; |
|||
|
|||
using lex_float = either<lex_special_float, |
|||
sequence<lex_dec_int, either<lex_exponent_part, |
|||
sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>; |
|||
|
|||
// ===========================================================================
|
|||
|
|||
using lex_true = sequence<character<'t'>, character<'r'>, |
|||
character<'u'>, character<'e'>>; |
|||
using lex_false = sequence<character<'f'>, character<'a'>, character<'l'>, |
|||
character<'s'>, character<'e'>>; |
|||
using lex_boolean = either<lex_true, lex_false>; |
|||
|
|||
// ===========================================================================
|
|||
|
|||
using lex_date_fullyear = repeat<lex_digit, exactly<4>>; |
|||
using lex_date_month = repeat<lex_digit, exactly<2>>; |
|||
using lex_date_mday = repeat<lex_digit, exactly<2>>; |
|||
using lex_time_delim = either<character<'T'>, character<'t'>, character<' '>>; |
|||
using lex_time_hour = repeat<lex_digit, exactly<2>>; |
|||
using lex_time_minute = repeat<lex_digit, exactly<2>>; |
|||
using lex_time_second = repeat<lex_digit, exactly<2>>; |
|||
using lex_time_secfrac = sequence<character<'.'>, |
|||
repeat<lex_digit, at_least<1>>>; |
|||
|
|||
using lex_time_numoffset = sequence<either<character<'+'>, character<'-'>>, |
|||
sequence<lex_time_hour, character<':'>, |
|||
lex_time_minute>>; |
|||
using lex_time_offset = either<character<'Z'>, character<'z'>, |
|||
lex_time_numoffset>; |
|||
|
|||
using lex_partial_time = sequence<lex_time_hour, character<':'>, |
|||
lex_time_minute, character<':'>, |
|||
lex_time_second, maybe<lex_time_secfrac>>; |
|||
using lex_full_date = sequence<lex_date_fullyear, character<'-'>, |
|||
lex_date_month, character<'-'>, |
|||
lex_date_mday>; |
|||
using lex_full_time = sequence<lex_partial_time, lex_time_offset>; |
|||
|
|||
using lex_offset_date_time = sequence<lex_full_date, lex_time_delim, lex_full_time>; |
|||
using lex_local_date_time = sequence<lex_full_date, lex_time_delim, lex_partial_time>; |
|||
using lex_local_date = lex_full_date; |
|||
using lex_local_time = lex_partial_time; |
|||
|
|||
// ===========================================================================
|
|||
|
|||
using lex_quotation_mark = character<'"'>; |
|||
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
|
|||
in_range<0x0A, 0x1F>, |
|||
character<0x22>, character<0x5C>, |
|||
character<0x7F>>>; |
|||
|
|||
using lex_escape = character<'\\'>; |
|||
using lex_escape_unicode_short = sequence<character<'u'>, |
|||
repeat<lex_hex_dig, exactly<4>>>; |
|||
using lex_escape_unicode_long = sequence<character<'U'>, |
|||
repeat<lex_hex_dig, exactly<8>>>; |
|||
using lex_escape_seq_char = either<character<'"'>, character<'\\'>, |
|||
character<'b'>, character<'f'>, |
|||
character<'n'>, character<'r'>, |
|||
character<'t'>, |
|||
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
|
|||
character<'e'>, // ESC (0x1B)
|
|||
#endif
|
|||
lex_escape_unicode_short, |
|||
lex_escape_unicode_long |
|||
>; |
|||
using lex_escaped = sequence<lex_escape, lex_escape_seq_char>; |
|||
using lex_basic_char = either<lex_basic_unescaped, lex_escaped>; |
|||
using lex_basic_string = sequence<lex_quotation_mark, |
|||
repeat<lex_basic_char, unlimited>, |
|||
lex_quotation_mark>; |
|||
|
|||
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
|
|||
// are allowed to be used.
|
|||
// After this, the following strings are *explicitly* allowed.
|
|||
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
|
|||
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
|
|||
// - One or two `"`s can appear just before or after the delimiter.
|
|||
// ```toml
|
|||
// str4 = """Here are two quotation marks: "". Simple enough."""
|
|||
// str5 = """Here are three quotation marks: ""\"."""
|
|||
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
|
|||
// str7 = """"This," she said, "is just a pointless statement.""""
|
|||
// ```
|
|||
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
|
|||
// the above example. It is difficult to recognize `"` at the end of string body
|
|||
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
|
|||
// invalid `"`. Like this:
|
|||
// ```console
|
|||
// what(): [error] toml::parse_table: invalid line format
|
|||
// --> hoge.toml
|
|||
// |
|
|||
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
|
|||
// | ^- expected newline, but got '"'.
|
|||
// ```
|
|||
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
|
|||
// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
|
|||
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
|
|||
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
|
|||
// the string body.
|
|||
//
|
|||
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>; |
|||
using lex_ml_basic_string_open = lex_ml_basic_string_delim; |
|||
using lex_ml_basic_string_close = sequence< |
|||
repeat<lex_quotation_mark, exactly<3>>, |
|||
maybe<lex_quotation_mark>, maybe<lex_quotation_mark> |
|||
>; |
|||
|
|||
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
|
|||
in_range<0x0A, 0x1F>, |
|||
character<0x5C>, // backslash
|
|||
character<0x7F>, // DEL
|
|||
lex_ml_basic_string_delim>>; |
|||
|
|||
using lex_ml_basic_escaped_newline = sequence< |
|||
lex_escape, maybe<lex_ws>, lex_newline, |
|||
repeat<either<lex_ws, lex_newline>, unlimited>>; |
|||
|
|||
using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>; |
|||
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline, |
|||
lex_ml_basic_escaped_newline>, |
|||
unlimited>; |
|||
using lex_ml_basic_string = sequence<lex_ml_basic_string_open, |
|||
lex_ml_basic_body, |
|||
lex_ml_basic_string_close>; |
|||
|
|||
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>, |
|||
character<0x7F>, character<0x27>>>; |
|||
using lex_apostrophe = character<'\''>; |
|||
using lex_literal_string = sequence<lex_apostrophe, |
|||
repeat<lex_literal_char, unlimited>, |
|||
lex_apostrophe>; |
|||
|
|||
// the same reason as above.
|
|||
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>; |
|||
using lex_ml_literal_string_open = lex_ml_literal_string_delim; |
|||
using lex_ml_literal_string_close = sequence< |
|||
repeat<lex_apostrophe, exactly<3>>, |
|||
maybe<lex_apostrophe>, maybe<lex_apostrophe> |
|||
>; |
|||
|
|||
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>, |
|||
in_range<0x0A, 0x1F>, |
|||
character<0x7F>, |
|||
lex_ml_literal_string_delim>>; |
|||
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>, |
|||
unlimited>; |
|||
using lex_ml_literal_string = sequence<lex_ml_literal_string_open, |
|||
lex_ml_literal_body, |
|||
lex_ml_literal_string_close>; |
|||
|
|||
using lex_string = either<lex_ml_basic_string, lex_basic_string, |
|||
lex_ml_literal_string, lex_literal_string>; |
|||
|
|||
// ===========================================================================
|
|||
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>; |
|||
|
|||
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit, |
|||
character<'-'>, character<'_'>>, |
|||
at_least<1>>; |
|||
using lex_quoted_key = either<lex_basic_string, lex_literal_string>; |
|||
using lex_simple_key = either<lex_unquoted_key, lex_quoted_key>; |
|||
using lex_dotted_key = sequence<lex_simple_key, |
|||
repeat<sequence<lex_dot_sep, lex_simple_key>, |
|||
at_least<1> |
|||
> |
|||
>; |
|||
using lex_key = either<lex_dotted_key, lex_simple_key>; |
|||
|
|||
using lex_keyval_sep = sequence<maybe<lex_ws>, |
|||
character<'='>, |
|||
maybe<lex_ws>>; |
|||
|
|||
using lex_std_table_open = character<'['>; |
|||
using lex_std_table_close = character<']'>; |
|||
using lex_std_table = sequence<lex_std_table_open, |
|||
maybe<lex_ws>, |
|||
lex_key, |
|||
maybe<lex_ws>, |
|||
lex_std_table_close>; |
|||
|
|||
using lex_array_table_open = sequence<lex_std_table_open, lex_std_table_open>; |
|||
using lex_array_table_close = sequence<lex_std_table_close, lex_std_table_close>; |
|||
using lex_array_table = sequence<lex_array_table_open, |
|||
maybe<lex_ws>, |
|||
lex_key, |
|||
maybe<lex_ws>, |
|||
lex_array_table_close>; |
|||
|
|||
using lex_utf8_1byte = in_range<0x00, 0x7F>; |
|||
using lex_utf8_2byte = sequence< |
|||
in_range<'\xC2', '\xDF'>, |
|||
in_range<'\x80', '\xBF'> |
|||
>; |
|||
using lex_utf8_3byte = sequence<either< |
|||
sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>, |
|||
sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>, |
|||
sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>, |
|||
sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>> |
|||
>, in_range<'\x80', '\xBF'>>; |
|||
using lex_utf8_4byte = sequence<either< |
|||
sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>, |
|||
sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>, |
|||
sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>> |
|||
>, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>; |
|||
using lex_utf8_code = either< |
|||
lex_utf8_1byte, |
|||
lex_utf8_2byte, |
|||
lex_utf8_3byte, |
|||
lex_utf8_4byte |
|||
>; |
|||
|
|||
using lex_comment_start_symbol = character<'#'>; |
|||
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>; |
|||
using lex_comment = sequence<lex_comment_start_symbol, repeat<either< |
|||
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>; |
|||
|
|||
} // detail
|
|||
} // toml
|
|||
#endif // TOML_LEXER_HPP
|
@ -0,0 +1,113 @@ |
|||
// Copyright Toru Niina 2019.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_LITERAL_HPP
|
|||
#define TOML11_LITERAL_HPP
|
|||
#include "parser.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
inline namespace literals |
|||
{ |
|||
inline namespace toml_literals |
|||
{ |
|||
|
|||
// implementation
|
|||
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector> |
|||
literal_internal_impl(::toml::detail::location loc) |
|||
{ |
|||
using value_type = ::toml::basic_value< |
|||
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; |
|||
// if there are some comments or empty lines, skip them.
|
|||
using skip_line = ::toml::detail::repeat<toml::detail::sequence< |
|||
::toml::detail::maybe<::toml::detail::lex_ws>, |
|||
::toml::detail::maybe<::toml::detail::lex_comment>, |
|||
::toml::detail::lex_newline |
|||
>, ::toml::detail::at_least<1>>; |
|||
skip_line::invoke(loc); |
|||
|
|||
// if there are some whitespaces before a value, skip them.
|
|||
using skip_ws = ::toml::detail::repeat< |
|||
::toml::detail::lex_ws, ::toml::detail::at_least<1>>; |
|||
skip_ws::invoke(loc); |
|||
|
|||
// to distinguish arrays and tables, first check it is a table or not.
|
|||
//
|
|||
// "[1,2,3]"_toml; // this is an array
|
|||
// "[table]"_toml; // a table that has an empty table named "table" inside.
|
|||
// "[[1,2,3]]"_toml; // this is an array of arrays
|
|||
// "[[table]]"_toml; // this is a table that has an array of tables inside.
|
|||
//
|
|||
// "[[1]]"_toml; // this can be both... (currently it becomes a table)
|
|||
// "1 = [{}]"_toml; // this is a table that has an array of table named 1.
|
|||
// "[[1,]]"_toml; // this is an array of arrays.
|
|||
// "[[1],]"_toml; // this also.
|
|||
|
|||
const auto the_front = loc.iter(); |
|||
|
|||
const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); |
|||
loc.reset(the_front); |
|||
|
|||
const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); |
|||
loc.reset(the_front); |
|||
|
|||
// If it is neither a table-key or a array-of-table-key, it may be a value.
|
|||
if(!is_table_key && !is_aots_key) |
|||
{ |
|||
if(auto data = ::toml::detail::parse_value<value_type>(loc, 0)) |
|||
{ |
|||
return data.unwrap(); |
|||
} |
|||
} |
|||
|
|||
// Note that still it can be a table, because the literal might be something
|
|||
// like the following.
|
|||
// ```cpp
|
|||
// R"( // c++11 raw string literals
|
|||
// key = "value"
|
|||
// int = 42
|
|||
// )"_toml;
|
|||
// ```
|
|||
// It is a valid toml file.
|
|||
// It should be parsed as if we parse a file with this content.
|
|||
|
|||
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc)) |
|||
{ |
|||
return data.unwrap(); |
|||
} |
|||
else // none of them.
|
|||
{ |
|||
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); |
|||
} |
|||
|
|||
} |
|||
|
|||
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector> |
|||
operator"" _toml(const char* str, std::size_t len) |
|||
{ |
|||
::toml::detail::location loc( |
|||
std::string("TOML literal encoded in a C++ code"), |
|||
std::vector<char>(str, str + len)); |
|||
// literal length does not include the null character at the end.
|
|||
return literal_internal_impl(std::move(loc)); |
|||
} |
|||
|
|||
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
|
|||
// So here we use the feature test macro for `char8_t` itself.
|
|||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
|||
// value of u8"" literal has been changed from char to char8_t and char8_t is
|
|||
// NOT compatible to char
|
|||
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector> |
|||
operator"" _toml(const char8_t* str, std::size_t len) |
|||
{ |
|||
::toml::detail::location loc( |
|||
std::string("TOML literal encoded in a C++ code"), |
|||
std::vector<char>(reinterpret_cast<const char*>(str), |
|||
reinterpret_cast<const char*>(str) + len)); |
|||
return literal_internal_impl(std::move(loc)); |
|||
} |
|||
#endif
|
|||
|
|||
} // toml_literals
|
|||
} // literals
|
|||
} // toml
|
|||
#endif//TOML11_LITERAL_HPP
|
@ -0,0 +1,121 @@ |
|||
#ifndef TOML11_MACROS_HPP
|
|||
#define TOML11_MACROS_HPP
|
|||
|
|||
#define TOML11_STRINGIZE_AUX(x) #x
|
|||
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
|
|||
|
|||
#define TOML11_CONCATENATE_AUX(x, y) x##y
|
|||
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
|
|||
|
|||
// ============================================================================
|
|||
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
|
|||
|
|||
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
|
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
// TOML11_ARGS_SIZE
|
|||
|
|||
#define TOML11_INDEX_RSEQ() \
|
|||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ |
|||
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 |
|||
#define TOML11_ARGS_SIZE_IMPL(\
|
|||
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ |
|||
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ |
|||
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ |
|||
ARG31, ARG32, N, ...) N |
|||
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
|
|||
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
|
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
// TOML11_FOR_EACH_VA_ARGS
|
|||
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
|
|||
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
|
|||
|
|||
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
|
|||
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) |
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
|
|||
|
|||
// use it in the following way.
|
|||
// ```cpp
|
|||
// namespace foo
|
|||
// {
|
|||
// struct Foo
|
|||
// {
|
|||
// std::string s;
|
|||
// double d;
|
|||
// int i;
|
|||
// };
|
|||
// } // foo
|
|||
//
|
|||
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
|
|||
// ```
|
|||
// And then you can use `toml::find<foo::Foo>(file, "foo");`
|
|||
//
|
|||
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
|
|||
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME)); |
|||
|
|||
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
|
|||
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; |
|||
|
|||
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
|
|||
namespace toml { \ |
|||
template<> \ |
|||
struct from<NAME> \ |
|||
{ \ |
|||
template<typename C, template<typename ...> class T, \ |
|||
template<typename ...> class A> \ |
|||
static NAME from_toml(const basic_value<C, T, A>& v) \ |
|||
{ \ |
|||
NAME obj; \ |
|||
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ |
|||
return obj; \ |
|||
} \ |
|||
}; \ |
|||
template<> \ |
|||
struct into<NAME> \ |
|||
{ \ |
|||
static value into_toml(const NAME& obj) \ |
|||
{ \ |
|||
::toml::value v = ::toml::table{}; \ |
|||
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ |
|||
return v; \ |
|||
} \ |
|||
}; \ |
|||
} /* toml */ |
|||
|
|||
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
|
|||
|
|||
#endif// TOML11_MACROS_HPP
|
2611
src/toml/parser.hpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,416 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_REGION_HPP
|
|||
#define TOML11_REGION_HPP
|
|||
#include <memory>
|
|||
#include <vector>
|
|||
#include <algorithm>
|
|||
#include <initializer_list>
|
|||
#include <iterator>
|
|||
#include <iomanip>
|
|||
#include <cassert>
|
|||
#include "color.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
namespace detail |
|||
{ |
|||
|
|||
// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
|
|||
template<typename Iterator> |
|||
std::string make_string(Iterator first, Iterator last) |
|||
{ |
|||
if(first == last) {return "";} |
|||
return std::string(first, last); |
|||
} |
|||
inline std::string make_string(std::size_t len, char c) |
|||
{ |
|||
if(len == 0) {return "";} |
|||
return std::string(len, c); |
|||
} |
|||
|
|||
// region_base is a base class of location and region that are defined below.
|
|||
// it will be used to generate better error messages.
|
|||
struct region_base |
|||
{ |
|||
region_base() = default; |
|||
virtual ~region_base() = default; |
|||
region_base(const region_base&) = default; |
|||
region_base(region_base&& ) = default; |
|||
region_base& operator=(const region_base&) = default; |
|||
region_base& operator=(region_base&& ) = default; |
|||
|
|||
virtual bool is_ok() const noexcept {return false;} |
|||
virtual char front() const noexcept {return '\0';} |
|||
|
|||
virtual std::string str() const {return std::string("unknown region");} |
|||
virtual std::string name() const {return std::string("unknown file");} |
|||
virtual std::string line() const {return std::string("unknown line");} |
|||
virtual std::string line_num() const {return std::string("?");} |
|||
|
|||
// length of the region
|
|||
virtual std::size_t size() const noexcept {return 0;} |
|||
// number of characters in the line before the region
|
|||
virtual std::size_t before() const noexcept {return 0;} |
|||
// number of characters in the line after the region
|
|||
virtual std::size_t after() const noexcept {return 0;} |
|||
|
|||
virtual std::vector<std::string> comments() const {return {};} |
|||
// ```toml
|
|||
// # comment_before
|
|||
// key = "value" # comment_inline
|
|||
// ```
|
|||
}; |
|||
|
|||
// location represents a position in a container, which contains a file content.
|
|||
// it can be considered as a region that contains only one character.
|
|||
//
|
|||
// it contains pointer to the file content and iterator that points the current
|
|||
// location.
|
|||
struct location final : public region_base |
|||
{ |
|||
using const_iterator = typename std::vector<char>::const_iterator; |
|||
using difference_type = typename std::iterator_traits<const_iterator>::difference_type; |
|||
using source_ptr = std::shared_ptr<const std::vector<char>>; |
|||
|
|||
location(std::string source_name, std::vector<char> cont) |
|||
: source_(std::make_shared<std::vector<char>>(std::move(cont))), |
|||
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) |
|||
{} |
|||
location(std::string source_name, const std::string& cont) |
|||
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())), |
|||
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) |
|||
{} |
|||
|
|||
location(const location&) = default; |
|||
location(location&&) = default; |
|||
location& operator=(const location&) = default; |
|||
location& operator=(location&&) = default; |
|||
~location() = default; |
|||
|
|||
bool is_ok() const noexcept override {return static_cast<bool>(source_);} |
|||
char front() const noexcept override {return *iter_;} |
|||
|
|||
// this const prohibits codes like `++(loc.iter())`.
|
|||
std::add_const<const_iterator>::type iter() const noexcept {return iter_;} |
|||
|
|||
const_iterator begin() const noexcept {return source_->cbegin();} |
|||
const_iterator end() const noexcept {return source_->cend();} |
|||
|
|||
// XXX `location::line_num()` used to be implemented using `std::count` to
|
|||
// count a number of '\n'. But with a long toml file (typically, 10k lines),
|
|||
// it becomes intolerably slow because each time it generates error messages,
|
|||
// it counts '\n' from thousands of characters. To workaround it, I decided
|
|||
// to introduce `location::line_number_` member variable and synchronize it
|
|||
// to the location changes the point to look. So an overload of `iter()`
|
|||
// which returns mutable reference is removed and `advance()`, `retrace()`
|
|||
// and `reset()` is added.
|
|||
void advance(difference_type n = 1) noexcept |
|||
{ |
|||
this->line_number_ += static_cast<std::size_t>( |
|||
std::count(this->iter_, std::next(this->iter_, n), '\n')); |
|||
this->iter_ += n; |
|||
return; |
|||
} |
|||
void retrace(difference_type n = 1) noexcept |
|||
{ |
|||
this->line_number_ -= static_cast<std::size_t>( |
|||
std::count(std::prev(this->iter_, n), this->iter_, '\n')); |
|||
this->iter_ -= n; |
|||
return; |
|||
} |
|||
void reset(const_iterator rollback) noexcept |
|||
{ |
|||
// since c++11, std::distance works in both ways for random-access
|
|||
// iterators and returns a negative value if `first > last`.
|
|||
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
|
|||
{ |
|||
this->line_number_ -= static_cast<std::size_t>( |
|||
std::count(rollback, this->iter_, '\n')); |
|||
} |
|||
else // iter < rollback [[unlikely]]
|
|||
{ |
|||
this->line_number_ += static_cast<std::size_t>( |
|||
std::count(this->iter_, rollback, '\n')); |
|||
} |
|||
this->iter_ = rollback; |
|||
return; |
|||
} |
|||
|
|||
std::string str() const override {return make_string(1, *this->iter());} |
|||
std::string name() const override {return source_name_;} |
|||
|
|||
std::string line_num() const override |
|||
{ |
|||
return std::to_string(this->line_number_); |
|||
} |
|||
|
|||
std::string line() const override |
|||
{ |
|||
return make_string(this->line_begin(), this->line_end()); |
|||
} |
|||
|
|||
const_iterator line_begin() const noexcept |
|||
{ |
|||
using reverse_iterator = std::reverse_iterator<const_iterator>; |
|||
return std::find(reverse_iterator(this->iter()), |
|||
reverse_iterator(this->begin()), '\n').base(); |
|||
} |
|||
const_iterator line_end() const noexcept |
|||
{ |
|||
return std::find(this->iter(), this->end(), '\n'); |
|||
} |
|||
|
|||
// location is always points a character. so the size is 1.
|
|||
std::size_t size() const noexcept override |
|||
{ |
|||
return 1u; |
|||
} |
|||
std::size_t before() const noexcept override |
|||
{ |
|||
const auto sz = std::distance(this->line_begin(), this->iter()); |
|||
assert(sz >= 0); |
|||
return static_cast<std::size_t>(sz); |
|||
} |
|||
std::size_t after() const noexcept override |
|||
{ |
|||
const auto sz = std::distance(this->iter(), this->line_end()); |
|||
assert(sz >= 0); |
|||
return static_cast<std::size_t>(sz); |
|||
} |
|||
|
|||
source_ptr const& source() const& noexcept {return source_;} |
|||
source_ptr&& source() && noexcept {return std::move(source_);} |
|||
|
|||
private: |
|||
|
|||
source_ptr source_; |
|||
std::size_t line_number_; |
|||
std::string source_name_; |
|||
const_iterator iter_; |
|||
}; |
|||
|
|||
// region represents a range in a container, which contains a file content.
|
|||
//
|
|||
// it contains pointer to the file content and iterator that points the first
|
|||
// and last location.
|
|||
struct region final : public region_base |
|||
{ |
|||
using const_iterator = typename std::vector<char>::const_iterator; |
|||
using source_ptr = std::shared_ptr<const std::vector<char>>; |
|||
|
|||
// delete default constructor. source_ never be null.
|
|||
region() = delete; |
|||
|
|||
explicit region(const location& loc) |
|||
: source_(loc.source()), source_name_(loc.name()), |
|||
first_(loc.iter()), last_(loc.iter()) |
|||
{} |
|||
explicit region(location&& loc) |
|||
: source_(loc.source()), source_name_(loc.name()), |
|||
first_(loc.iter()), last_(loc.iter()) |
|||
{} |
|||
|
|||
region(const location& loc, const_iterator f, const_iterator l) |
|||
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) |
|||
{} |
|||
region(location&& loc, const_iterator f, const_iterator l) |
|||
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) |
|||
{} |
|||
|
|||
region(const region&) = default; |
|||
region(region&&) = default; |
|||
region& operator=(const region&) = default; |
|||
region& operator=(region&&) = default; |
|||
~region() = default; |
|||
|
|||
region& operator+=(const region& other) |
|||
{ |
|||
// different regions cannot be concatenated
|
|||
assert(this->source_ == other.source_ && this->last_ == other.first_); |
|||
|
|||
this->last_ = other.last_; |
|||
return *this; |
|||
} |
|||
|
|||
bool is_ok() const noexcept override {return static_cast<bool>(source_);} |
|||
char front() const noexcept override {return *first_;} |
|||
|
|||
std::string str() const override {return make_string(first_, last_);} |
|||
std::string line() const override |
|||
{ |
|||
if(this->contain_newline()) |
|||
{ |
|||
return make_string(this->line_begin(), |
|||
std::find(this->line_begin(), this->last(), '\n')); |
|||
} |
|||
return make_string(this->line_begin(), this->line_end()); |
|||
} |
|||
std::string line_num() const override |
|||
{ |
|||
return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); |
|||
} |
|||
|
|||
std::size_t size() const noexcept override |
|||
{ |
|||
const auto sz = std::distance(first_, last_); |
|||
assert(sz >= 0); |
|||
return static_cast<std::size_t>(sz); |
|||
} |
|||
std::size_t before() const noexcept override |
|||
{ |
|||
const auto sz = std::distance(this->line_begin(), this->first()); |
|||
assert(sz >= 0); |
|||
return static_cast<std::size_t>(sz); |
|||
} |
|||
std::size_t after() const noexcept override |
|||
{ |
|||
const auto sz = std::distance(this->last(), this->line_end()); |
|||
assert(sz >= 0); |
|||
return static_cast<std::size_t>(sz); |
|||
} |
|||
|
|||
bool contain_newline() const noexcept |
|||
{ |
|||
return std::find(this->first(), this->last(), '\n') != this->last(); |
|||
} |
|||
|
|||
const_iterator line_begin() const noexcept |
|||
{ |
|||
using reverse_iterator = std::reverse_iterator<const_iterator>; |
|||
return std::find(reverse_iterator(this->first()), |
|||
reverse_iterator(this->begin()), '\n').base(); |
|||
} |
|||
const_iterator line_end() const noexcept |
|||
{ |
|||
return std::find(this->last(), this->end(), '\n'); |
|||
} |
|||
|
|||
const_iterator begin() const noexcept {return source_->cbegin();} |
|||
const_iterator end() const noexcept {return source_->cend();} |
|||
const_iterator first() const noexcept {return first_;} |
|||
const_iterator last() const noexcept {return last_;} |
|||
|
|||
source_ptr const& source() const& noexcept {return source_;} |
|||
source_ptr&& source() && noexcept {return std::move(source_);} |
|||
|
|||
std::string name() const override {return source_name_;} |
|||
|
|||
std::vector<std::string> comments() const override |
|||
{ |
|||
// assuming the current region (`*this`) points a value.
|
|||
// ```toml
|
|||
// a = "value"
|
|||
// ^^^^^^^- this region
|
|||
// ```
|
|||
using rev_iter = std::reverse_iterator<const_iterator>; |
|||
|
|||
std::vector<std::string> com{}; |
|||
{ |
|||
// find comments just before the current region.
|
|||
// ```toml
|
|||
// # this should be collected.
|
|||
// # this also.
|
|||
// a = value # not this.
|
|||
// ```
|
|||
|
|||
// # this is a comment for `a`, not array elements.
|
|||
// a = [1, 2, 3, 4, 5]
|
|||
if(this->first() == std::find_if(this->line_begin(), this->first(), |
|||
[](const char c) noexcept -> bool {return c == '[' || c == '{';})) |
|||
{ |
|||
auto iter = this->line_begin(); // points the first character
|
|||
while(iter != this->begin()) |
|||
{ |
|||
iter = std::prev(iter); |
|||
|
|||
// range [line_start, iter) represents the previous line
|
|||
const auto line_start = std::find( |
|||
rev_iter(iter), rev_iter(this->begin()), '\n').base(); |
|||
const auto comment_found = std::find(line_start, iter, '#'); |
|||
if(comment_found == iter) |
|||
{ |
|||
break; // comment not found.
|
|||
} |
|||
|
|||
// exclude the following case.
|
|||
// > a = "foo" # comment // <-- this is not a comment for b but a.
|
|||
// > b = "current value"
|
|||
if(std::all_of(line_start, comment_found, |
|||
[](const char c) noexcept -> bool { |
|||
return c == ' ' || c == '\t'; |
|||
})) |
|||
{ |
|||
// unwrap the first '#' by std::next.
|
|||
auto s = make_string(std::next(comment_found), iter); |
|||
if(!s.empty() && s.back() == '\r') {s.pop_back();} |
|||
com.push_back(std::move(s)); |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
iter = line_start; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(com.size() > 1) |
|||
{ |
|||
std::reverse(com.begin(), com.end()); |
|||
} |
|||
|
|||
{ |
|||
// find comments just after the current region.
|
|||
// ```toml
|
|||
// # not this.
|
|||
// a = value # this one.
|
|||
// a = [ # not this (technically difficult)
|
|||
//
|
|||
// ] # and this.
|
|||
// ```
|
|||
// The reason why it's difficult is that it requires parsing in the
|
|||
// following case.
|
|||
// ```toml
|
|||
// a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
|
|||
// # ...
|
|||
// ] # this is apparently a comment for a.
|
|||
//
|
|||
// b = [
|
|||
// 3.14 ] # there is no way to add a comment to `3.14` currently.
|
|||
//
|
|||
// c = [
|
|||
// 3.14 # do this if you need a comment here.
|
|||
// ]
|
|||
// ```
|
|||
const auto comment_found = |
|||
std::find(this->last(), this->line_end(), '#'); |
|||
if(comment_found != this->line_end()) // '#' found
|
|||
{ |
|||
// table = {key = "value"} # what is this for?
|
|||
// the above comment is not for "value", but {key="value"}.
|
|||
if(comment_found == std::find_if(this->last(), comment_found, |
|||
[](const char c) noexcept -> bool { |
|||
return !(c == ' ' || c == '\t' || c == ','); |
|||
})) |
|||
{ |
|||
// unwrap the first '#' by std::next.
|
|||
auto s = make_string(std::next(comment_found), this->line_end()); |
|||
if(!s.empty() && s.back() == '\r') {s.pop_back();} |
|||
com.push_back(std::move(s)); |
|||
} |
|||
} |
|||
} |
|||
return com; |
|||
} |
|||
|
|||
private: |
|||
|
|||
source_ptr source_; |
|||
std::string source_name_; |
|||
const_iterator first_, last_; |
|||
}; |
|||
|
|||
} // detail
|
|||
} // toml
|
|||
#endif// TOML11_REGION_H
|
@ -0,0 +1,717 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_RESULT_HPP
|
|||
#define TOML11_RESULT_HPP
|
|||
#include "traits.hpp"
|
|||
#include <type_traits>
|
|||
#include <stdexcept>
|
|||
#include <utility>
|
|||
#include <new>
|
|||
#include <string>
|
|||
#include <sstream>
|
|||
#include <cassert>
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
template<typename T> |
|||
struct success |
|||
{ |
|||
using value_type = T; |
|||
value_type value; |
|||
|
|||
explicit success(const value_type& v) |
|||
noexcept(std::is_nothrow_copy_constructible<value_type>::value) |
|||
: value(v) |
|||
{} |
|||
explicit success(value_type&& v) |
|||
noexcept(std::is_nothrow_move_constructible<value_type>::value) |
|||
: value(std::move(v)) |
|||
{} |
|||
|
|||
template<typename U> |
|||
explicit success(U&& v): value(std::forward<U>(v)) {} |
|||
|
|||
template<typename U> |
|||
explicit success(const success<U>& v): value(v.value) {} |
|||
template<typename U> |
|||
explicit success(success<U>&& v): value(std::move(v.value)) {} |
|||
|
|||
~success() = default; |
|||
success(const success&) = default; |
|||
success(success&&) = default; |
|||
success& operator=(const success&) = default; |
|||
success& operator=(success&&) = default; |
|||
}; |
|||
|
|||
template<typename T> |
|||
struct failure |
|||
{ |
|||
using value_type = T; |
|||
value_type value; |
|||
|
|||
explicit failure(const value_type& v) |
|||
noexcept(std::is_nothrow_copy_constructible<value_type>::value) |
|||
: value(v) |
|||
{} |
|||
explicit failure(value_type&& v) |
|||
noexcept(std::is_nothrow_move_constructible<value_type>::value) |
|||
: value(std::move(v)) |
|||
{} |
|||
|
|||
template<typename U> |
|||
explicit failure(U&& v): value(std::forward<U>(v)) {} |
|||
|
|||
template<typename U> |
|||
explicit failure(const failure<U>& v): value(v.value) {} |
|||
template<typename U> |
|||
explicit failure(failure<U>&& v): value(std::move(v.value)) {} |
|||
|
|||
~failure() = default; |
|||
failure(const failure&) = default; |
|||
failure(failure&&) = default; |
|||
failure& operator=(const failure&) = default; |
|||
failure& operator=(failure&&) = default; |
|||
}; |
|||
|
|||
template<typename T> |
|||
success<typename std::remove_cv<typename std::remove_reference<T>::type>::type> |
|||
ok(T&& v) |
|||
{ |
|||
return success< |
|||
typename std::remove_cv<typename std::remove_reference<T>::type>::type |
|||
>(std::forward<T>(v)); |
|||
} |
|||
template<typename T> |
|||
failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type> |
|||
err(T&& v) |
|||
{ |
|||
return failure< |
|||
typename std::remove_cv<typename std::remove_reference<T>::type>::type |
|||
>(std::forward<T>(v)); |
|||
} |
|||
|
|||
inline success<std::string> ok(const char* literal) |
|||
{ |
|||
return success<std::string>(std::string(literal)); |
|||
} |
|||
inline failure<std::string> err(const char* literal) |
|||
{ |
|||
return failure<std::string>(std::string(literal)); |
|||
} |
|||
|
|||
|
|||
template<typename T, typename E> |
|||
struct result |
|||
{ |
|||
using value_type = T; |
|||
using error_type = E; |
|||
using success_type = success<value_type>; |
|||
using failure_type = failure<error_type>; |
|||
|
|||
result(const success_type& s): is_ok_(true) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(s); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
result(const failure_type& f): is_ok_(false) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
result(success_type&& s): is_ok_(true) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
result(failure_type&& f): is_ok_(false) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
|
|||
template<typename U> |
|||
result(const success<U>& s): is_ok_(true) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
template<typename U> |
|||
result(const failure<U>& f): is_ok_(false) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
template<typename U> |
|||
result(success<U>&& s): is_ok_(true) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
template<typename U> |
|||
result(failure<U>&& f): is_ok_(false) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
|
|||
result& operator=(const success_type& s) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = true; |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(s); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
result& operator=(const failure_type& f) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = false; |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
result& operator=(success_type&& s) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = true; |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
result& operator=(failure_type&& f) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = false; |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
|
|||
template<typename U> |
|||
result& operator=(const success<U>& s) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = true; |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
template<typename U> |
|||
result& operator=(const failure<U>& f) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = false; |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
template<typename U> |
|||
result& operator=(success<U>&& s) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = true; |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
template<typename U> |
|||
result& operator=(failure<U>&& f) |
|||
{ |
|||
this->cleanup(); |
|||
this->is_ok_ = false; |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
return *this; |
|||
} |
|||
|
|||
~result() noexcept {this->cleanup();} |
|||
|
|||
result(const result& other): is_ok_(other.is_ok()) |
|||
{ |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
} |
|||
result(result&& other): is_ok_(other.is_ok()) |
|||
{ |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
} |
|||
|
|||
template<typename U, typename F> |
|||
result(const result<U, F>& other): is_ok_(other.is_ok()) |
|||
{ |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
} |
|||
template<typename U, typename F> |
|||
result(result<U, F>&& other): is_ok_(other.is_ok()) |
|||
{ |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
} |
|||
|
|||
result& operator=(const result& other) |
|||
{ |
|||
this->cleanup(); |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
is_ok_ = other.is_ok(); |
|||
return *this; |
|||
} |
|||
result& operator=(result&& other) |
|||
{ |
|||
this->cleanup(); |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
is_ok_ = other.is_ok(); |
|||
return *this; |
|||
} |
|||
|
|||
template<typename U, typename F> |
|||
result& operator=(const result<U, F>& other) |
|||
{ |
|||
this->cleanup(); |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
is_ok_ = other.is_ok(); |
|||
return *this; |
|||
} |
|||
template<typename U, typename F> |
|||
result& operator=(result<U, F>&& other) |
|||
{ |
|||
this->cleanup(); |
|||
if(other.is_ok()) |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); |
|||
assert(tmp == std::addressof(this->succ)); |
|||
(void)tmp; |
|||
} |
|||
else |
|||
{ |
|||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); |
|||
assert(tmp == std::addressof(this->fail)); |
|||
(void)tmp; |
|||
} |
|||
is_ok_ = other.is_ok(); |
|||
return *this; |
|||
} |
|||
|
|||
bool is_ok() const noexcept {return is_ok_;} |
|||
bool is_err() const noexcept {return !is_ok_;} |
|||
|
|||
operator bool() const noexcept {return is_ok_;} |
|||
|
|||
value_type& unwrap() & |
|||
{ |
|||
if(is_err()) |
|||
{ |
|||
throw std::runtime_error("toml::result: bad unwrap: " + |
|||
format_error(this->as_err())); |
|||
} |
|||
return this->succ.value; |
|||
} |
|||
value_type const& unwrap() const& |
|||
{ |
|||
if(is_err()) |
|||
{ |
|||
throw std::runtime_error("toml::result: bad unwrap: " + |
|||
format_error(this->as_err())); |
|||
} |
|||
return this->succ.value; |
|||
} |
|||
value_type&& unwrap() && |
|||
{ |
|||
if(is_err()) |
|||
{ |
|||
throw std::runtime_error("toml::result: bad unwrap: " + |
|||
format_error(this->as_err())); |
|||
} |
|||
return std::move(this->succ.value); |
|||
} |
|||
|
|||
value_type& unwrap_or(value_type& opt) & |
|||
{ |
|||
if(is_err()) {return opt;} |
|||
return this->succ.value; |
|||
} |
|||
value_type const& unwrap_or(value_type const& opt) const& |
|||
{ |
|||
if(is_err()) {return opt;} |
|||
return this->succ.value; |
|||
} |
|||
value_type unwrap_or(value_type opt) && |
|||
{ |
|||
if(is_err()) {return opt;} |
|||
return this->succ.value; |
|||
} |
|||
|
|||
error_type& unwrap_err() & |
|||
{ |
|||
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} |
|||
return this->fail.value; |
|||
} |
|||
error_type const& unwrap_err() const& |
|||
{ |
|||
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} |
|||
return this->fail.value; |
|||
} |
|||
error_type&& unwrap_err() && |
|||
{ |
|||
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} |
|||
return std::move(this->fail.value); |
|||
} |
|||
|
|||
value_type& as_ok() & noexcept {return this->succ.value;} |
|||
value_type const& as_ok() const& noexcept {return this->succ.value;} |
|||
value_type&& as_ok() && noexcept {return std::move(this->succ.value);} |
|||
|
|||
error_type& as_err() & noexcept {return this->fail.value;} |
|||
error_type const& as_err() const& noexcept {return this->fail.value;} |
|||
error_type&& as_err() && noexcept {return std::move(this->fail.value);} |
|||
|
|||
|
|||
// prerequisities
|
|||
// F: T -> U
|
|||
// retval: result<U, E>
|
|||
template<typename F> |
|||
result<detail::return_type_of_t<F, value_type&>, error_type> |
|||
map(F&& f) & |
|||
{ |
|||
if(this->is_ok()){return ok(f(this->as_ok()));} |
|||
return err(this->as_err()); |
|||
} |
|||
template<typename F> |
|||
result<detail::return_type_of_t<F, value_type const&>, error_type> |
|||
map(F&& f) const& |
|||
{ |
|||
if(this->is_ok()){return ok(f(this->as_ok()));} |
|||
return err(this->as_err()); |
|||
} |
|||
template<typename F> |
|||
result<detail::return_type_of_t<F, value_type &&>, error_type> |
|||
map(F&& f) && |
|||
{ |
|||
if(this->is_ok()){return ok(f(std::move(this->as_ok())));} |
|||
return err(std::move(this->as_err())); |
|||
} |
|||
|
|||
// prerequisities
|
|||
// F: E -> F
|
|||
// retval: result<T, F>
|
|||
template<typename F> |
|||
result<value_type, detail::return_type_of_t<F, error_type&>> |
|||
map_err(F&& f) & |
|||
{ |
|||
if(this->is_err()){return err(f(this->as_err()));} |
|||
return ok(this->as_ok()); |
|||
} |
|||
template<typename F> |
|||
result<value_type, detail::return_type_of_t<F, error_type const&>> |
|||
map_err(F&& f) const& |
|||
{ |
|||
if(this->is_err()){return err(f(this->as_err()));} |
|||
return ok(this->as_ok()); |
|||
} |
|||
template<typename F> |
|||
result<value_type, detail::return_type_of_t<F, error_type&&>> |
|||
map_err(F&& f) && |
|||
{ |
|||
if(this->is_err()){return err(f(std::move(this->as_err())));} |
|||
return ok(std::move(this->as_ok())); |
|||
} |
|||
|
|||
// prerequisities
|
|||
// F: T -> U
|
|||
// retval: U
|
|||
template<typename F, typename U> |
|||
detail::return_type_of_t<F, value_type&> |
|||
map_or_else(F&& f, U&& opt) & |
|||
{ |
|||
if(this->is_err()){return std::forward<U>(opt);} |
|||
return f(this->as_ok()); |
|||
} |
|||
template<typename F, typename U> |
|||
detail::return_type_of_t<F, value_type const&> |
|||
map_or_else(F&& f, U&& opt) const& |
|||
{ |
|||
if(this->is_err()){return std::forward<U>(opt);} |
|||
return f(this->as_ok()); |
|||
} |
|||
template<typename F, typename U> |
|||
detail::return_type_of_t<F, value_type&&> |
|||
map_or_else(F&& f, U&& opt) && |
|||
{ |
|||
if(this->is_err()){return std::forward<U>(opt);} |
|||
return f(std::move(this->as_ok())); |
|||
} |
|||
|
|||
// prerequisities
|
|||
// F: E -> U
|
|||
// retval: U
|
|||
template<typename F, typename U> |
|||
detail::return_type_of_t<F, error_type&> |
|||
map_err_or_else(F&& f, U&& opt) & |
|||
{ |
|||
if(this->is_ok()){return std::forward<U>(opt);} |
|||
return f(this->as_err()); |
|||
} |
|||
template<typename F, typename U> |
|||
detail::return_type_of_t<F, error_type const&> |
|||
map_err_or_else(F&& f, U&& opt) const& |
|||
{ |
|||
if(this->is_ok()){return std::forward<U>(opt);} |
|||
return f(this->as_err()); |
|||
} |
|||
template<typename F, typename U> |
|||
detail::return_type_of_t<F, error_type&&> |
|||
map_err_or_else(F&& f, U&& opt) && |
|||
{ |
|||
if(this->is_ok()){return std::forward<U>(opt);} |
|||
return f(std::move(this->as_err())); |
|||
} |
|||
|
|||
// prerequisities:
|
|||
// F: func T -> U
|
|||
// toml::err(error_type) should be convertible to U.
|
|||
// normally, type U is another result<S, F> and E is convertible to F
|
|||
template<typename F> |
|||
detail::return_type_of_t<F, value_type&> |
|||
and_then(F&& f) & |
|||
{ |
|||
if(this->is_ok()){return f(this->as_ok());} |
|||
return err(this->as_err()); |
|||
} |
|||
template<typename F> |
|||
detail::return_type_of_t<F, value_type const&> |
|||
and_then(F&& f) const& |
|||
{ |
|||
if(this->is_ok()){return f(this->as_ok());} |
|||
return err(this->as_err()); |
|||
} |
|||
template<typename F> |
|||
detail::return_type_of_t<F, value_type&&> |
|||
and_then(F&& f) && |
|||
{ |
|||
if(this->is_ok()){return f(std::move(this->as_ok()));} |
|||
return err(std::move(this->as_err())); |
|||
} |
|||
|
|||
// prerequisities:
|
|||
// F: func E -> U
|
|||
// toml::ok(value_type) should be convertible to U.
|
|||
// normally, type U is another result<S, F> and T is convertible to S
|
|||
template<typename F> |
|||
detail::return_type_of_t<F, error_type&> |
|||
or_else(F&& f) & |
|||
{ |
|||
if(this->is_err()){return f(this->as_err());} |
|||
return ok(this->as_ok()); |
|||
} |
|||
template<typename F> |
|||
detail::return_type_of_t<F, error_type const&> |
|||
or_else(F&& f) const& |
|||
{ |
|||
if(this->is_err()){return f(this->as_err());} |
|||
return ok(this->as_ok()); |
|||
} |
|||
template<typename F> |
|||
detail::return_type_of_t<F, error_type&&> |
|||
or_else(F&& f) && |
|||
{ |
|||
if(this->is_err()){return f(std::move(this->as_err()));} |
|||
return ok(std::move(this->as_ok())); |
|||
} |
|||
|
|||
// if *this is error, returns *this. otherwise, returns other.
|
|||
result and_other(const result& other) const& |
|||
{ |
|||
return this->is_err() ? *this : other; |
|||
} |
|||
result and_other(result&& other) && |
|||
{ |
|||
return this->is_err() ? std::move(*this) : std::move(other); |
|||
} |
|||
|
|||
// if *this is okay, returns *this. otherwise, returns other.
|
|||
result or_other(const result& other) const& |
|||
{ |
|||
return this->is_ok() ? *this : other; |
|||
} |
|||
result or_other(result&& other) && |
|||
{ |
|||
return this->is_ok() ? std::move(*this) : std::move(other); |
|||
} |
|||
|
|||
void swap(result<T, E>& other) |
|||
{ |
|||
result<T, E> tmp(std::move(*this)); |
|||
*this = std::move(other); |
|||
other = std::move(tmp); |
|||
return ; |
|||
} |
|||
|
|||
private: |
|||
|
|||
static std::string format_error(std::exception const& excpt) |
|||
{ |
|||
return std::string(excpt.what()); |
|||
} |
|||
template<typename U, typename std::enable_if<!std::is_base_of< |
|||
std::exception, U>::value, std::nullptr_t>::type = nullptr> |
|||
static std::string format_error(U const& others) |
|||
{ |
|||
std::ostringstream oss; oss << others; |
|||
return oss.str(); |
|||
} |
|||
|
|||
void cleanup() noexcept |
|||
{ |
|||
if(this->is_ok_) {this->succ.~success_type();} |
|||
else {this->fail.~failure_type();} |
|||
return; |
|||
} |
|||
|
|||
private: |
|||
|
|||
bool is_ok_; |
|||
union |
|||
{ |
|||
success_type succ; |
|||
failure_type fail; |
|||
}; |
|||
}; |
|||
|
|||
template<typename T, typename E> |
|||
void swap(result<T, E>& lhs, result<T, E>& rhs) |
|||
{ |
|||
lhs.swap(rhs); |
|||
return; |
|||
} |
|||
|
|||
// this might be confusing because it eagerly evaluated, while in the other
|
|||
// cases operator && and || are short-circuited.
|
|||
//
|
|||
// template<typename T, typename E>
|
|||
// inline result<T, E>
|
|||
// operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
|
|||
// {
|
|||
// return lhs.is_ok() ? rhs : lhs;
|
|||
// }
|
|||
//
|
|||
// template<typename T, typename E>
|
|||
// inline result<T, E>
|
|||
// operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
|
|||
// {
|
|||
// return lhs.is_ok() ? lhs : rhs;
|
|||
// }
|
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
// re-use result<T, E> as a optional<T> with none_t
|
|||
|
|||
namespace detail |
|||
{ |
|||
struct none_t {}; |
|||
inline bool operator==(const none_t&, const none_t&) noexcept {return true;} |
|||
inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} |
|||
inline bool operator< (const none_t&, const none_t&) noexcept {return false;} |
|||
inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} |
|||
inline bool operator> (const none_t&, const none_t&) noexcept {return false;} |
|||
inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} |
|||
template<typename charT, typename traitsT> |
|||
std::basic_ostream<charT, traitsT>& |
|||
operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&) |
|||
{ |
|||
os << "none"; |
|||
return os; |
|||
} |
|||
inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};} |
|||
} // detail
|
|||
} // toml11
|
|||
#endif// TOML11_RESULT_H
|
@ -0,0 +1,994 @@ |
|||
// Copyright Toru Niina 2019.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_SERIALIZER_HPP
|
|||
#define TOML11_SERIALIZER_HPP
|
|||
#include <cmath>
|
|||
#include <cstdio>
|
|||
|
|||
#include <limits>
|
|||
|
|||
#if defined(_WIN32)
|
|||
#include <locale.h>
|
|||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
|||
#include <xlocale.h>
|
|||
#elif defined(__linux__)
|
|||
#include <locale.h>
|
|||
#endif
|
|||
|
|||
#include "lexer.hpp"
|
|||
#include "value.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
// This function serialize a key. It checks a string is a bare key and
|
|||
// escapes special characters if the string is not compatible to a bare key.
|
|||
// ```cpp
|
|||
// std::string k("non.bare.key"); // the key itself includes `.`s.
|
|||
// std::string formatted = toml::format_key(k);
|
|||
// assert(formatted == "\"non.bare.key\"");
|
|||
// ```
|
|||
//
|
|||
// This function is exposed to make it easy to write a user-defined serializer.
|
|||
// Since toml restricts characters available in a bare key, generally a string
|
|||
// should be escaped. But checking whether a string needs to be surrounded by
|
|||
// a `"` and escaping some special character is boring.
|
|||
template<typename charT, typename traits, typename Alloc> |
|||
std::basic_string<charT, traits, Alloc> |
|||
format_key(const std::basic_string<charT, traits, Alloc>& k) |
|||
{ |
|||
if(k.empty()) |
|||
{ |
|||
return std::string("\"\""); |
|||
} |
|||
|
|||
// check the key can be a bare (unquoted) key
|
|||
detail::location loc(k, std::vector<char>(k.begin(), k.end())); |
|||
detail::lex_unquoted_key::invoke(loc); |
|||
if(loc.iter() == loc.end()) |
|||
{ |
|||
return k; // all the tokens are consumed. the key is unquoted-key.
|
|||
} |
|||
|
|||
//if it includes special characters, then format it in a "quoted" key.
|
|||
std::basic_string<charT, traits, Alloc> serialized("\""); |
|||
for(const char c : k) |
|||
{ |
|||
switch(c) |
|||
{ |
|||
case '\\': {serialized += "\\\\"; break;} |
|||
case '\"': {serialized += "\\\""; break;} |
|||
case '\b': {serialized += "\\b"; break;} |
|||
case '\t': {serialized += "\\t"; break;} |
|||
case '\f': {serialized += "\\f"; break;} |
|||
case '\n': {serialized += "\\n"; break;} |
|||
case '\r': {serialized += "\\r"; break;} |
|||
default: { |
|||
if (c >= 0x00 && c < 0x20) |
|||
{ |
|||
std::array<char, 7> buf; |
|||
std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast<int>(c)); |
|||
serialized += buf.data(); |
|||
} |
|||
else |
|||
{ |
|||
serialized += c; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
serialized += "\""; |
|||
return serialized; |
|||
} |
|||
|
|||
template<typename charT, typename traits, typename Alloc> |
|||
std::basic_string<charT, traits, Alloc> |
|||
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys) |
|||
{ |
|||
if(keys.empty()) |
|||
{ |
|||
return std::string("\"\""); |
|||
} |
|||
|
|||
std::basic_string<charT, traits, Alloc> serialized; |
|||
for(const auto& ky : keys) |
|||
{ |
|||
serialized += format_key(ky); |
|||
serialized += charT('.'); |
|||
} |
|||
serialized.pop_back(); // remove the last dot '.'
|
|||
return serialized; |
|||
} |
|||
|
|||
template<typename Value> |
|||
struct serializer |
|||
{ |
|||
static_assert(detail::is_basic_value<Value>::value, |
|||
"toml::serializer is for toml::value and its variants, " |
|||
"toml::basic_value<...>."); |
|||
|
|||
using value_type = Value; |
|||
using key_type = typename value_type::key_type ; |
|||
using comment_type = typename value_type::comment_type ; |
|||
using boolean_type = typename value_type::boolean_type ; |
|||
using integer_type = typename value_type::integer_type ; |
|||
using floating_type = typename value_type::floating_type ; |
|||
using string_type = typename value_type::string_type ; |
|||
using local_time_type = typename value_type::local_time_type ; |
|||
using local_date_type = typename value_type::local_date_type ; |
|||
using local_datetime_type = typename value_type::local_datetime_type ; |
|||
using offset_datetime_type = typename value_type::offset_datetime_type; |
|||
using array_type = typename value_type::array_type ; |
|||
using table_type = typename value_type::table_type ; |
|||
|
|||
serializer(const std::size_t w = 80u, |
|||
const int float_prec = std::numeric_limits<toml::floating>::max_digits10, |
|||
const bool can_be_inlined = false, |
|||
const bool no_comment = false, |
|||
std::vector<toml::key> ks = {}, |
|||
const bool value_has_comment = false) |
|||
: can_be_inlined_(can_be_inlined), no_comment_(no_comment), |
|||
value_has_comment_(value_has_comment && !no_comment), |
|||
float_prec_(float_prec), width_(w), keys_(std::move(ks)) |
|||
{} |
|||
~serializer() = default; |
|||
|
|||
std::string operator()(const boolean_type& b) const |
|||
{ |
|||
return b ? "true" : "false"; |
|||
} |
|||
std::string operator()(const integer_type i) const |
|||
{ |
|||
#if defined(_WIN32)
|
|||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE); |
|||
const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); |
|||
setlocale(LC_NUMERIC, "C"); |
|||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
|||
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); |
|||
locale_t original_locale(0); |
|||
if(c_locale != locale_t(0)) |
|||
{ |
|||
original_locale = uselocale(c_locale); |
|||
} |
|||
#endif
|
|||
|
|||
const auto str = std::to_string(i); |
|||
|
|||
#if defined(_WIN32)
|
|||
setlocale(LC_NUMERIC, original_locale.c_str()); |
|||
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE); |
|||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
|||
if(original_locale != locale_t(0)) |
|||
{ |
|||
uselocale(original_locale); |
|||
} |
|||
#endif
|
|||
return str; |
|||
} |
|||
std::string operator()(const floating_type f) const |
|||
{ |
|||
if(std::isnan(f)) |
|||
{ |
|||
if(std::signbit(f)) |
|||
{ |
|||
return std::string("-nan"); |
|||
} |
|||
else |
|||
{ |
|||
return std::string("nan"); |
|||
} |
|||
} |
|||
else if(!std::isfinite(f)) |
|||
{ |
|||
if(std::signbit(f)) |
|||
{ |
|||
return std::string("-inf"); |
|||
} |
|||
else |
|||
{ |
|||
return std::string("inf"); |
|||
} |
|||
} |
|||
|
|||
// set locale to "C".
|
|||
// To make it thread-local, we use OS-specific features.
|
|||
// If we set process-global locale, it can break other thread that also
|
|||
// outputs something simultaneously.
|
|||
#if defined(_WIN32)
|
|||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE); |
|||
const std::string original_locale(setlocale(LC_NUMERIC, nullptr)); |
|||
setlocale(LC_NUMERIC, "C"); |
|||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
|||
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0)); |
|||
locale_t original_locale(0); |
|||
if(c_locale != locale_t(0)) |
|||
{ |
|||
original_locale = uselocale(c_locale); |
|||
} |
|||
#endif
|
|||
|
|||
const auto fmt = "%.*g"; |
|||
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); |
|||
// +1 for null character(\0)
|
|||
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0'); |
|||
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); |
|||
|
|||
// restore the original locale
|
|||
#if defined(_WIN32)
|
|||
setlocale(LC_NUMERIC, original_locale.c_str()); |
|||
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE); |
|||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
|||
if(original_locale != locale_t(0)) |
|||
{ |
|||
uselocale(original_locale); |
|||
} |
|||
#endif
|
|||
|
|||
std::string token(buf.begin(), std::prev(buf.end())); |
|||
if(!token.empty() && token.back() == '.') // 1. => 1.0
|
|||
{ |
|||
token += '0'; |
|||
} |
|||
|
|||
const auto e = std::find_if( |
|||
token.cbegin(), token.cend(), [](const char c) noexcept -> bool { |
|||
return c == 'e' || c == 'E'; |
|||
}); |
|||
const auto has_exponent = (token.cend() != e); |
|||
const auto has_fraction = (token.cend() != std::find( |
|||
token.cbegin(), token.cend(), '.')); |
|||
|
|||
if(!has_exponent && !has_fraction) |
|||
{ |
|||
// the resulting value does not have any float specific part!
|
|||
token += ".0"; |
|||
} |
|||
return token; |
|||
} |
|||
std::string operator()(const string_type& s) const |
|||
{ |
|||
if(s.kind == string_t::basic) |
|||
{ |
|||
if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || |
|||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && |
|||
this->width_ != (std::numeric_limits<std::size_t>::max)()) |
|||
{ |
|||
// if linefeed or double-quote is contained,
|
|||
// make it multiline basic string.
|
|||
const auto escaped = this->escape_ml_basic_string(s.str); |
|||
std::string open("\"\"\""); |
|||
std::string close("\"\"\""); |
|||
if(escaped.find('\n') != std::string::npos || |
|||
this->width_ < escaped.size() + 6) |
|||
{ |
|||
// if the string body contains newline or is enough long,
|
|||
// add newlines after and before delimiters.
|
|||
open += "\n"; |
|||
close = std::string("\\\n") + close; |
|||
} |
|||
return open + escaped + close; |
|||
} |
|||
|
|||
// no linefeed. try to make it oneline-string.
|
|||
std::string oneline = this->escape_basic_string(s.str); |
|||
if(oneline.size() + 2 < width_ || width_ < 2) |
|||
{ |
|||
const std::string quote("\""); |
|||
return quote + oneline + quote; |
|||
} |
|||
|
|||
// the line is too long compared to the specified width.
|
|||
// split it into multiple lines.
|
|||
std::string token("\"\"\"\n"); |
|||
while(!oneline.empty()) |
|||
{ |
|||
if(oneline.size() < width_) |
|||
{ |
|||
token += oneline; |
|||
oneline.clear(); |
|||
} |
|||
else if(oneline.at(width_-2) == '\\') |
|||
{ |
|||
token += oneline.substr(0, width_-2); |
|||
token += "\\\n"; |
|||
oneline.erase(0, width_-2); |
|||
} |
|||
else |
|||
{ |
|||
token += oneline.substr(0, width_-1); |
|||
token += "\\\n"; |
|||
oneline.erase(0, width_-1); |
|||
} |
|||
} |
|||
return token + std::string("\\\n\"\"\""); |
|||
} |
|||
else // the string `s` is literal-string.
|
|||
{ |
|||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || |
|||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) |
|||
{ |
|||
std::string open("'''"); |
|||
if(this->width_ + 6 < s.str.size()) |
|||
{ |
|||
open += '\n'; // the first newline is ignored by TOML spec
|
|||
} |
|||
const std::string close("'''"); |
|||
return open + s.str + close; |
|||
} |
|||
else |
|||
{ |
|||
const std::string quote("'"); |
|||
return quote + s.str + quote; |
|||
} |
|||
} |
|||
} |
|||
|
|||
std::string operator()(const local_date_type& d) const |
|||
{ |
|||
std::ostringstream oss; |
|||
oss << d; |
|||
return oss.str(); |
|||
} |
|||
std::string operator()(const local_time_type& t) const |
|||
{ |
|||
std::ostringstream oss; |
|||
oss << t; |
|||
return oss.str(); |
|||
} |
|||
std::string operator()(const local_datetime_type& dt) const |
|||
{ |
|||
std::ostringstream oss; |
|||
oss << dt; |
|||
return oss.str(); |
|||
} |
|||
std::string operator()(const offset_datetime_type& odt) const |
|||
{ |
|||
std::ostringstream oss; |
|||
oss << odt; |
|||
return oss.str(); |
|||
} |
|||
|
|||
std::string operator()(const array_type& v) const |
|||
{ |
|||
if(v.empty()) |
|||
{ |
|||
return std::string("[]"); |
|||
} |
|||
if(this->is_array_of_tables(v)) |
|||
{ |
|||
return make_array_of_tables(v); |
|||
} |
|||
|
|||
// not an array of tables. normal array.
|
|||
// first, try to make it inline if none of the elements have a comment.
|
|||
if( ! this->has_comment_inside(v)) |
|||
{ |
|||
const auto inl = this->make_inline_array(v); |
|||
if(inl.size() < this->width_ && |
|||
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) |
|||
{ |
|||
return inl; |
|||
} |
|||
} |
|||
|
|||
// if the length exceeds this->width_, print multiline array.
|
|||
// key = [
|
|||
// # ...
|
|||
// 42,
|
|||
// ...
|
|||
// ]
|
|||
std::string token; |
|||
std::string current_line; |
|||
token += "[\n"; |
|||
for(const auto& item : v) |
|||
{ |
|||
if( ! item.comments().empty() && !no_comment_) |
|||
{ |
|||
// if comment exists, the element must be the only element in the line.
|
|||
// e.g. the following is not allowed.
|
|||
// ```toml
|
|||
// array = [
|
|||
// # comment for what?
|
|||
// 1, 2, 3, 4, 5
|
|||
// ]
|
|||
// ```
|
|||
if(!current_line.empty()) |
|||
{ |
|||
if(current_line.back() != '\n') |
|||
{ |
|||
current_line += '\n'; |
|||
} |
|||
token += current_line; |
|||
current_line.clear(); |
|||
} |
|||
for(const auto& c : item.comments()) |
|||
{ |
|||
token += '#'; |
|||
token += c; |
|||
token += '\n'; |
|||
} |
|||
token += toml::visit(*this, item); |
|||
if(!token.empty() && token.back() == '\n') {token.pop_back();} |
|||
token += ",\n"; |
|||
continue; |
|||
} |
|||
std::string next_elem; |
|||
if(item.is_table()) |
|||
{ |
|||
serializer ser(*this); |
|||
ser.can_be_inlined_ = true; |
|||
ser.width_ = (std::numeric_limits<std::size_t>::max)(); |
|||
next_elem += toml::visit(ser, item); |
|||
} |
|||
else |
|||
{ |
|||
next_elem += toml::visit(*this, item); |
|||
} |
|||
|
|||
// comma before newline.
|
|||
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} |
|||
|
|||
// if current line does not exceeds the width limit, continue.
|
|||
if(current_line.size() + next_elem.size() + 1 < this->width_) |
|||
{ |
|||
current_line += next_elem; |
|||
current_line += ','; |
|||
} |
|||
else if(current_line.empty()) |
|||
{ |
|||
// if current line was empty, force put the next_elem because
|
|||
// next_elem is not splittable
|
|||
token += next_elem; |
|||
token += ",\n"; |
|||
// current_line is kept empty
|
|||
} |
|||
else // reset current_line
|
|||
{ |
|||
assert(current_line.back() == ','); |
|||
token += current_line; |
|||
token += '\n'; |
|||
current_line = next_elem; |
|||
current_line += ','; |
|||
} |
|||
} |
|||
if(!current_line.empty()) |
|||
{ |
|||
if(!current_line.empty() && current_line.back() != '\n') |
|||
{ |
|||
current_line += '\n'; |
|||
} |
|||
token += current_line; |
|||
} |
|||
token += "]\n"; |
|||
return token; |
|||
} |
|||
|
|||
// templatize for any table-like container
|
|||
std::string operator()(const table_type& v) const |
|||
{ |
|||
// if an element has a comment, then it can't be inlined.
|
|||
// table = {# how can we write a comment for this? key = "value"}
|
|||
if(this->can_be_inlined_ && !(this->has_comment_inside(v))) |
|||
{ |
|||
std::string token; |
|||
if(!this->keys_.empty()) |
|||
{ |
|||
token += format_key(this->keys_.back()); |
|||
token += " = "; |
|||
} |
|||
token += this->make_inline_table(v); |
|||
if(token.size() < this->width_ && |
|||
token.end() == std::find(token.begin(), token.end(), '\n')) |
|||
{ |
|||
return token; |
|||
} |
|||
} |
|||
|
|||
std::string token; |
|||
if(!keys_.empty()) |
|||
{ |
|||
token += '['; |
|||
token += format_keys(keys_); |
|||
token += "]\n"; |
|||
} |
|||
token += this->make_multiline_table(v); |
|||
return token; |
|||
} |
|||
|
|||
private: |
|||
|
|||
std::string escape_basic_string(const std::string& s) const |
|||
{ |
|||
//XXX assuming `s` is a valid utf-8 sequence.
|
|||
std::string retval; |
|||
for(const char c : s) |
|||
{ |
|||
switch(c) |
|||
{ |
|||
case '\\': {retval += "\\\\"; break;} |
|||
case '\"': {retval += "\\\""; break;} |
|||
case '\b': {retval += "\\b"; break;} |
|||
case '\t': {retval += "\\t"; break;} |
|||
case '\f': {retval += "\\f"; break;} |
|||
case '\n': {retval += "\\n"; break;} |
|||
case '\r': {retval += "\\r"; break;} |
|||
default : |
|||
{ |
|||
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) |
|||
{ |
|||
retval += "\\u00"; |
|||
retval += char(48 + (c / 16)); |
|||
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); |
|||
} |
|||
else |
|||
{ |
|||
retval += c; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return retval; |
|||
} |
|||
|
|||
std::string escape_ml_basic_string(const std::string& s) const |
|||
{ |
|||
std::string retval; |
|||
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) |
|||
{ |
|||
switch(*i) |
|||
{ |
|||
case '\\': {retval += "\\\\"; break;} |
|||
// One or two consecutive "s are allowed.
|
|||
// Later we will check there are no three consecutive "s.
|
|||
// case '\"': {retval += "\\\""; break;}
|
|||
case '\b': {retval += "\\b"; break;} |
|||
case '\t': {retval += "\\t"; break;} |
|||
case '\f': {retval += "\\f"; break;} |
|||
case '\n': {retval += "\n"; break;} |
|||
case '\r': |
|||
{ |
|||
if(std::next(i) != e && *std::next(i) == '\n') |
|||
{ |
|||
retval += "\r\n"; |
|||
++i; |
|||
} |
|||
else |
|||
{ |
|||
retval += "\\r"; |
|||
} |
|||
break; |
|||
} |
|||
default : |
|||
{ |
|||
const auto c = *i; |
|||
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) |
|||
{ |
|||
retval += "\\u00"; |
|||
retval += char(48 + (c / 16)); |
|||
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); |
|||
} |
|||
else |
|||
{ |
|||
retval += c; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
|
|||
// 3 consecutive `"`s are considered as a closing delimiter.
|
|||
// We need to check if there are 3 or more consecutive `"`s and insert
|
|||
// backslash to break them down into several short `"`s like the `str6`
|
|||
// in the following example.
|
|||
// ```toml
|
|||
// str4 = """Here are two quotation marks: "". Simple enough."""
|
|||
// # str5 = """Here are three quotation marks: """.""" # INVALID
|
|||
// str5 = """Here are three quotation marks: ""\"."""
|
|||
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
|
|||
// ```
|
|||
auto found_3_quotes = retval.find("\"\"\""); |
|||
while(found_3_quotes != std::string::npos) |
|||
{ |
|||
retval.replace(found_3_quotes, 3, "\"\"\\\""); |
|||
found_3_quotes = retval.find("\"\"\""); |
|||
} |
|||
return retval; |
|||
} |
|||
|
|||
// if an element of a table or an array has a comment, it cannot be inlined.
|
|||
bool has_comment_inside(const array_type& a) const noexcept |
|||
{ |
|||
// if no_comment is set, comments would not be written.
|
|||
if(this->no_comment_) {return false;} |
|||
|
|||
for(const auto& v : a) |
|||
{ |
|||
if(!v.comments().empty()) {return true;} |
|||
} |
|||
return false; |
|||
} |
|||
bool has_comment_inside(const table_type& t) const noexcept |
|||
{ |
|||
// if no_comment is set, comments would not be written.
|
|||
if(this->no_comment_) {return false;} |
|||
|
|||
for(const auto& kv : t) |
|||
{ |
|||
if(!kv.second.comments().empty()) {return true;} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
std::string make_inline_array(const array_type& v) const |
|||
{ |
|||
assert(!has_comment_inside(v)); |
|||
std::string token; |
|||
token += '['; |
|||
bool is_first = true; |
|||
for(const auto& item : v) |
|||
{ |
|||
if(is_first) {is_first = false;} else {token += ',';} |
|||
token += visit(serializer( |
|||
(std::numeric_limits<std::size_t>::max)(), this->float_prec_, |
|||
/* inlined */ true, /*no comment*/ false, /*keys*/ {}, |
|||
/*has_comment*/ !item.comments().empty()), item); |
|||
} |
|||
token += ']'; |
|||
return token; |
|||
} |
|||
|
|||
std::string make_inline_table(const table_type& v) const |
|||
{ |
|||
assert(!has_comment_inside(v)); |
|||
assert(this->can_be_inlined_); |
|||
std::string token; |
|||
token += '{'; |
|||
bool is_first = true; |
|||
for(const auto& kv : v) |
|||
{ |
|||
// in inline tables, trailing comma is not allowed (toml-lang #569).
|
|||
if(is_first) {is_first = false;} else {token += ',';} |
|||
token += format_key(kv.first); |
|||
token += '='; |
|||
token += visit(serializer( |
|||
(std::numeric_limits<std::size_t>::max)(), this->float_prec_, |
|||
/* inlined */ true, /*no comment*/ false, /*keys*/ {}, |
|||
/*has_comment*/ !kv.second.comments().empty()), kv.second); |
|||
} |
|||
token += '}'; |
|||
return token; |
|||
} |
|||
|
|||
std::string make_multiline_table(const table_type& v) const |
|||
{ |
|||
std::string token; |
|||
|
|||
// print non-table elements first.
|
|||
// ```toml
|
|||
// [foo] # a table we're writing now here
|
|||
// key = "value" # <- non-table element, "key"
|
|||
// # ...
|
|||
// [foo.bar] # <- table element, "bar"
|
|||
// ```
|
|||
// because after printing [foo.bar], the remaining non-table values will
|
|||
// be assigned into [foo.bar], not [foo]. Those values should be printed
|
|||
// earlier.
|
|||
for(const auto& kv : v) |
|||
{ |
|||
if(kv.second.is_table() || is_array_of_tables(kv.second)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
token += write_comments(kv.second); |
|||
|
|||
const auto key_and_sep = format_key(kv.first) + " = "; |
|||
const auto residual_width = (this->width_ > key_and_sep.size()) ? |
|||
this->width_ - key_and_sep.size() : 0; |
|||
token += key_and_sep; |
|||
token += visit(serializer(residual_width, this->float_prec_, |
|||
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, |
|||
/*has_comment*/ !kv.second.comments().empty()), kv.second); |
|||
|
|||
if(token.back() != '\n') |
|||
{ |
|||
token += '\n'; |
|||
} |
|||
} |
|||
|
|||
// normal tables / array of tables
|
|||
|
|||
// after multiline table appeared, the other tables cannot be inline
|
|||
// because the table would be assigned into the table.
|
|||
// [foo]
|
|||
// ...
|
|||
// bar = {...} # <- bar will be a member of [foo].
|
|||
bool multiline_table_printed = false; |
|||
for(const auto& kv : v) |
|||
{ |
|||
if(!kv.second.is_table() && !is_array_of_tables(kv.second)) |
|||
{ |
|||
continue; // other stuff are already serialized. skip them.
|
|||
} |
|||
|
|||
std::vector<toml::key> ks(this->keys_); |
|||
ks.push_back(kv.first); |
|||
|
|||
auto tmp = visit(serializer(this->width_, this->float_prec_, |
|||
!multiline_table_printed, this->no_comment_, ks, |
|||
/*has_comment*/ !kv.second.comments().empty()), kv.second); |
|||
|
|||
// If it is the first time to print a multi-line table, it would be
|
|||
// helpful to separate normal key-value pair and subtables by a
|
|||
// newline.
|
|||
// (this checks if the current key-value pair contains newlines.
|
|||
// but it is not perfect because multi-line string can also contain
|
|||
// a newline. in such a case, an empty line will be written) TODO
|
|||
if((!multiline_table_printed) && |
|||
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) |
|||
{ |
|||
multiline_table_printed = true; |
|||
token += '\n'; // separate key-value pairs and subtables
|
|||
|
|||
token += write_comments(kv.second); |
|||
token += tmp; |
|||
|
|||
// care about recursive tables (all tables in each level prints
|
|||
// newline and there will be a full of newlines)
|
|||
if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && |
|||
tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) |
|||
{ |
|||
token += '\n'; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
token += write_comments(kv.second); |
|||
token += tmp; |
|||
token += '\n'; |
|||
} |
|||
} |
|||
return token; |
|||
} |
|||
|
|||
std::string make_array_of_tables(const array_type& v) const |
|||
{ |
|||
// if it's not inlined, we need to add `[[table.key]]`.
|
|||
// but if it can be inlined, we can format it as the following.
|
|||
// ```
|
|||
// table.key = [
|
|||
// {...},
|
|||
// # comment
|
|||
// {...},
|
|||
// ]
|
|||
// ```
|
|||
// This function checks if inlinization is possible or not, and then
|
|||
// format the array-of-tables in a proper way.
|
|||
//
|
|||
// Note about comments:
|
|||
//
|
|||
// If the array itself has a comment (value_has_comment_ == true), we
|
|||
// should try to make it inline.
|
|||
// ```toml
|
|||
// # comment about array
|
|||
// array = [
|
|||
// # comment about table element
|
|||
// {of = "table"}
|
|||
// ]
|
|||
// ```
|
|||
// If it is formatted as a multiline table, the two comments becomes
|
|||
// indistinguishable.
|
|||
// ```toml
|
|||
// # comment about array
|
|||
// # comment about table element
|
|||
// [[array]]
|
|||
// of = "table"
|
|||
// ```
|
|||
// So we need to try to make it inline, and it force-inlines regardless
|
|||
// of the line width limit.
|
|||
// It may fail if the element of a table has comment. In that case,
|
|||
// the array-of-tables will be formatted as a multiline table.
|
|||
if(this->can_be_inlined_ || this->value_has_comment_) |
|||
{ |
|||
std::string token; |
|||
if(!keys_.empty()) |
|||
{ |
|||
token += format_key(keys_.back()); |
|||
token += " = "; |
|||
} |
|||
|
|||
bool failed = false; |
|||
token += "[\n"; |
|||
for(const auto& item : v) |
|||
{ |
|||
// if an element of the table has a comment, the table
|
|||
// cannot be inlined.
|
|||
if(this->has_comment_inside(item.as_table())) |
|||
{ |
|||
failed = true; |
|||
break; |
|||
} |
|||
// write comments for the table itself
|
|||
token += write_comments(item); |
|||
|
|||
const auto t = this->make_inline_table(item.as_table()); |
|||
|
|||
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
|||
std::find(t.cbegin(), t.cend(), '\n') != t.cend()) |
|||
{ |
|||
// if the value itself has a comment, ignore the line width limit
|
|||
if( ! this->value_has_comment_) |
|||
{ |
|||
failed = true; |
|||
break; |
|||
} |
|||
} |
|||
token += t; |
|||
token += ",\n"; |
|||
} |
|||
|
|||
if( ! failed) |
|||
{ |
|||
token += "]\n"; |
|||
return token; |
|||
} |
|||
// if failed, serialize them as [[array.of.tables]].
|
|||
} |
|||
|
|||
std::string token; |
|||
for(const auto& item : v) |
|||
{ |
|||
token += write_comments(item); |
|||
token += "[["; |
|||
token += format_keys(keys_); |
|||
token += "]]\n"; |
|||
token += this->make_multiline_table(item.as_table()); |
|||
} |
|||
return token; |
|||
} |
|||
|
|||
std::string write_comments(const value_type& v) const |
|||
{ |
|||
std::string retval; |
|||
if(this->no_comment_) {return retval;} |
|||
|
|||
for(const auto& c : v.comments()) |
|||
{ |
|||
retval += '#'; |
|||
retval += c; |
|||
retval += '\n'; |
|||
} |
|||
return retval; |
|||
} |
|||
|
|||
bool is_array_of_tables(const value_type& v) const |
|||
{ |
|||
if(!v.is_array() || v.as_array().empty()) {return false;} |
|||
return is_array_of_tables(v.as_array()); |
|||
} |
|||
bool is_array_of_tables(const array_type& v) const |
|||
{ |
|||
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
|
|||
// check all the element in an array to check if the array is an array
|
|||
// of tables.
|
|||
return std::all_of(v.begin(), v.end(), [](const value_type& elem) { |
|||
return elem.is_table(); |
|||
}); |
|||
} |
|||
|
|||
private: |
|||
|
|||
bool can_be_inlined_; |
|||
bool no_comment_; |
|||
bool value_has_comment_; |
|||
int float_prec_; |
|||
std::size_t width_; |
|||
std::vector<toml::key> keys_; |
|||
}; |
|||
|
|||
template<typename C, |
|||
template<typename ...> class M, template<typename ...> class V> |
|||
std::string |
|||
format(const basic_value<C, M, V>& v, std::size_t w = 80u, |
|||
int fprec = std::numeric_limits<toml::floating>::max_digits10, |
|||
bool no_comment = false, bool force_inline = false) |
|||
{ |
|||
using value_type = basic_value<C, M, V>; |
|||
// if value is a table, it is considered to be a root object.
|
|||
// the root object can't be an inline table.
|
|||
if(v.is_table()) |
|||
{ |
|||
std::ostringstream oss; |
|||
if(!v.comments().empty()) |
|||
{ |
|||
oss << v.comments(); |
|||
oss << '\n'; // to split the file comment from the first element
|
|||
} |
|||
const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v); |
|||
oss << serialized; |
|||
return oss.str(); |
|||
} |
|||
return visit(serializer<value_type>(w, fprec, force_inline), v); |
|||
} |
|||
|
|||
namespace detail |
|||
{ |
|||
template<typename charT, typename traits> |
|||
int comment_index(std::basic_ostream<charT, traits>&) |
|||
{ |
|||
static const int index = std::ios_base::xalloc(); |
|||
return index; |
|||
} |
|||
} // detail
|
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
nocomment(std::basic_ostream<charT, traits>& os) |
|||
{ |
|||
// by default, it is zero. and by default, it shows comments.
|
|||
os.iword(detail::comment_index(os)) = 1; |
|||
return os; |
|||
} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
showcomment(std::basic_ostream<charT, traits>& os) |
|||
{ |
|||
// by default, it is zero. and by default, it shows comments.
|
|||
os.iword(detail::comment_index(os)) = 0; |
|||
return os; |
|||
} |
|||
|
|||
template<typename charT, typename traits, typename C, |
|||
template<typename ...> class M, template<typename ...> class V> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v) |
|||
{ |
|||
using value_type = basic_value<C, M, V>; |
|||
|
|||
// get status of std::setw().
|
|||
const auto w = static_cast<std::size_t>(os.width()); |
|||
const int fprec = static_cast<int>(os.precision()); |
|||
os.width(0); |
|||
|
|||
// by default, iword is initialized by 0. And by default, toml11 outputs
|
|||
// comments. So `0` means showcomment. 1 means nocommnet.
|
|||
const bool no_comment = (1 == os.iword(detail::comment_index(os))); |
|||
|
|||
if(!no_comment && v.is_table() && !v.comments().empty()) |
|||
{ |
|||
os << v.comments(); |
|||
os << '\n'; // to split the file comment from the first element
|
|||
} |
|||
// the root object can't be an inline table. so pass `false`.
|
|||
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v); |
|||
os << serialized; |
|||
|
|||
// if v is a non-table value, and has only one comment, then
|
|||
// put a comment just after a value. in the following way.
|
|||
//
|
|||
// ```toml
|
|||
// key = "value" # comment.
|
|||
// ```
|
|||
//
|
|||
// Since the top-level toml object is a table, one who want to put a
|
|||
// non-table toml value must use this in a following way.
|
|||
//
|
|||
// ```cpp
|
|||
// toml::value v;
|
|||
// std::cout << "user-defined-key = " << v << std::endl;
|
|||
// ```
|
|||
//
|
|||
// In this case, it is impossible to put comments before key-value pair.
|
|||
// The only way to preserve comments is to put all of them after a value.
|
|||
if(!no_comment && !v.is_table() && !v.comments().empty()) |
|||
{ |
|||
os << " #"; |
|||
for(const auto& c : v.comments()) {os << c;} |
|||
} |
|||
return os; |
|||
} |
|||
|
|||
} // toml
|
|||
#endif// TOML11_SERIALIZER_HPP
|
@ -0,0 +1,239 @@ |
|||
// Copyright Toru Niina 2019.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_SOURCE_LOCATION_HPP
|
|||
#define TOML11_SOURCE_LOCATION_HPP
|
|||
#include <cstdint>
|
|||
#include <sstream>
|
|||
|
|||
#include "region.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
// A struct to contain location in a toml file.
|
|||
// The interface imitates std::experimental::source_location,
|
|||
// but not completely the same.
|
|||
//
|
|||
// It would be constructed by toml::value. It can be used to generate
|
|||
// user-defined error messages.
|
|||
//
|
|||
// - std::uint_least32_t line() const noexcept
|
|||
// - returns the line number where the region is on.
|
|||
// - std::uint_least32_t column() const noexcept
|
|||
// - returns the column number where the region starts.
|
|||
// - std::uint_least32_t region() const noexcept
|
|||
// - returns the size of the region.
|
|||
//
|
|||
// +-- line() +-- region of interest (region() == 9)
|
|||
// v .---+---.
|
|||
// 12 | value = "foo bar"
|
|||
// ^
|
|||
// +-- column()
|
|||
//
|
|||
// - std::string const& file_name() const noexcept;
|
|||
// - name of the file.
|
|||
// - std::string const& line_str() const noexcept;
|
|||
// - the whole line that contains the region of interest.
|
|||
//
|
|||
struct source_location |
|||
{ |
|||
public: |
|||
|
|||
source_location() |
|||
: line_num_(1), column_num_(1), region_size_(1), |
|||
file_name_("unknown file"), line_str_("") |
|||
{} |
|||
|
|||
explicit source_location(const detail::region_base* reg) |
|||
: line_num_(1), column_num_(1), region_size_(1), |
|||
file_name_("unknown file"), line_str_("") |
|||
{ |
|||
if(reg) |
|||
{ |
|||
if(reg->line_num() != detail::region_base().line_num()) |
|||
{ |
|||
line_num_ = static_cast<std::uint_least32_t>( |
|||
std::stoul(reg->line_num())); |
|||
} |
|||
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1); |
|||
region_size_ = static_cast<std::uint_least32_t>(reg->size()); |
|||
file_name_ = reg->name(); |
|||
line_str_ = reg->line(); |
|||
} |
|||
} |
|||
|
|||
explicit source_location(const detail::region& reg) |
|||
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))), |
|||
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)), |
|||
region_size_(static_cast<std::uint_least32_t>(reg.size())), |
|||
file_name_(reg.name()), |
|||
line_str_ (reg.line()) |
|||
{} |
|||
explicit source_location(const detail::location& loc) |
|||
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))), |
|||
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)), |
|||
region_size_(static_cast<std::uint_least32_t>(loc.size())), |
|||
file_name_(loc.name()), |
|||
line_str_ (loc.line()) |
|||
{} |
|||
|
|||
~source_location() = default; |
|||
source_location(source_location const&) = default; |
|||
source_location(source_location &&) = default; |
|||
source_location& operator=(source_location const&) = default; |
|||
source_location& operator=(source_location &&) = default; |
|||
|
|||
std::uint_least32_t line() const noexcept {return line_num_;} |
|||
std::uint_least32_t column() const noexcept {return column_num_;} |
|||
std::uint_least32_t region() const noexcept {return region_size_;} |
|||
|
|||
std::string const& file_name() const noexcept {return file_name_;} |
|||
std::string const& line_str() const noexcept {return line_str_;} |
|||
|
|||
private: |
|||
|
|||
std::uint_least32_t line_num_; |
|||
std::uint_least32_t column_num_; |
|||
std::uint_least32_t region_size_; |
|||
std::string file_name_; |
|||
std::string line_str_; |
|||
}; |
|||
|
|||
namespace detail |
|||
{ |
|||
|
|||
// internal error message generation.
|
|||
inline std::string format_underline(const std::string& message, |
|||
const std::vector<std::pair<source_location, std::string>>& loc_com, |
|||
const std::vector<std::string>& helps = {}, |
|||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) |
|||
{ |
|||
std::size_t line_num_width = 0; |
|||
for(const auto& lc : loc_com) |
|||
{ |
|||
std::uint_least32_t line = lc.first.line(); |
|||
std::size_t digit = 0; |
|||
while(line != 0) |
|||
{ |
|||
line /= 10; |
|||
digit += 1; |
|||
} |
|||
line_num_width = (std::max)(line_num_width, digit); |
|||
} |
|||
// 1 is the minimum width
|
|||
line_num_width = std::max<std::size_t>(line_num_width, 1); |
|||
|
|||
std::ostringstream retval; |
|||
|
|||
if(color::should_color() || colorize) |
|||
{ |
|||
retval << color::colorize; // turn on ANSI color
|
|||
} |
|||
|
|||
// XXX
|
|||
// Here, before `colorize` support, it does not output `[error]` prefix
|
|||
// automatically. So some user may output it manually and this change may
|
|||
// duplicate the prefix. To avoid it, check the first 7 characters and
|
|||
// if it is "[error]", it removes that part from the message shown.
|
|||
if(message.size() > 7 && message.substr(0, 7) == "[error]") |
|||
{ |
|||
retval |
|||
#ifndef TOML11_NO_ERROR_PREFIX
|
|||
<< color::bold << color::red << "[error]" << color::reset |
|||
#endif
|
|||
<< color::bold << message.substr(7) << color::reset << '\n'; |
|||
} |
|||
else |
|||
{ |
|||
retval |
|||
#ifndef TOML11_NO_ERROR_PREFIX
|
|||
<< color::bold << color::red << "[error] " << color::reset |
|||
#endif
|
|||
<< color::bold << message << color::reset << '\n'; |
|||
} |
|||
|
|||
const auto format_one_location = [line_num_width] |
|||
(std::ostringstream& oss, |
|||
const source_location& loc, const std::string& comment) -> void |
|||
{ |
|||
oss << ' ' << color::bold << color::blue |
|||
<< std::setw(static_cast<int>(line_num_width)) |
|||
<< std::right << loc.line() << " | " << color::reset |
|||
<< loc.line_str() << '\n'; |
|||
|
|||
oss << make_string(line_num_width + 1, ' ') |
|||
<< color::bold << color::blue << " | " << color::reset |
|||
<< make_string(loc.column()-1 /*1-origin*/, ' '); |
|||
|
|||
if(loc.region() == 1) |
|||
{ |
|||
// invalid
|
|||
// ^------
|
|||
oss << color::bold << color::red << "^---" << color::reset; |
|||
} |
|||
else |
|||
{ |
|||
// invalid
|
|||
// ~~~~~~~
|
|||
const auto underline_len = (std::min)( |
|||
static_cast<std::size_t>(loc.region()), loc.line_str().size()); |
|||
oss << color::bold << color::red |
|||
<< make_string(underline_len, '~') << color::reset; |
|||
} |
|||
oss << ' '; |
|||
oss << comment; |
|||
return; |
|||
}; |
|||
|
|||
assert(!loc_com.empty()); |
|||
|
|||
// --> example.toml
|
|||
// |
|
|||
retval << color::bold << color::blue << " --> " << color::reset |
|||
<< loc_com.front().first.file_name() << '\n'; |
|||
retval << make_string(line_num_width + 1, ' ') |
|||
<< color::bold << color::blue << " |\n" << color::reset; |
|||
// 1 | key value
|
|||
// | ^--- missing =
|
|||
format_one_location(retval, loc_com.front().first, loc_com.front().second); |
|||
|
|||
// process the rest of the locations
|
|||
for(std::size_t i=1; i<loc_com.size(); ++i) |
|||
{ |
|||
const auto& prev = loc_com.at(i-1); |
|||
const auto& curr = loc_com.at(i); |
|||
|
|||
retval << '\n'; |
|||
// if the filenames are the same, print "..."
|
|||
if(prev.first.file_name() == curr.first.file_name()) |
|||
{ |
|||
retval << color::bold << color::blue << " ...\n" << color::reset; |
|||
} |
|||
else // if filename differs, print " --> filename.toml" again
|
|||
{ |
|||
retval << color::bold << color::blue << " --> " << color::reset |
|||
<< curr.first.file_name() << '\n'; |
|||
retval << make_string(line_num_width + 1, ' ') |
|||
<< color::bold << color::blue << " |\n" << color::reset; |
|||
} |
|||
|
|||
format_one_location(retval, curr.first, curr.second); |
|||
} |
|||
|
|||
if(!helps.empty()) |
|||
{ |
|||
retval << '\n'; |
|||
retval << make_string(line_num_width + 1, ' '); |
|||
retval << color::bold << color::blue << " |" << color::reset; |
|||
for(const auto& help : helps) |
|||
{ |
|||
retval << color::bold << "\nHint: " << color::reset; |
|||
retval << help; |
|||
} |
|||
} |
|||
return retval.str(); |
|||
} |
|||
|
|||
} // detail
|
|||
} // toml
|
|||
#endif// TOML11_SOURCE_LOCATION_HPP
|
@ -0,0 +1,43 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_STORAGE_HPP
|
|||
#define TOML11_STORAGE_HPP
|
|||
#include "utility.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
namespace detail |
|||
{ |
|||
|
|||
// this contains pointer and deep-copy the content if copied.
|
|||
// to avoid recursive pointer.
|
|||
template<typename T> |
|||
struct storage |
|||
{ |
|||
using value_type = T; |
|||
|
|||
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {} |
|||
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {} |
|||
~storage() = default; |
|||
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {} |
|||
storage& operator=(const storage& rhs) |
|||
{ |
|||
this->ptr = toml::make_unique<T>(*rhs.ptr); |
|||
return *this; |
|||
} |
|||
storage(storage&&) = default; |
|||
storage& operator=(storage&&) = default; |
|||
|
|||
bool is_ok() const noexcept {return static_cast<bool>(ptr);} |
|||
|
|||
value_type& value() & noexcept {return *ptr;} |
|||
value_type const& value() const& noexcept {return *ptr;} |
|||
value_type&& value() && noexcept {return std::move(*ptr);} |
|||
|
|||
private: |
|||
std::unique_ptr<value_type> ptr; |
|||
}; |
|||
|
|||
} // detail
|
|||
} // toml
|
|||
#endif// TOML11_STORAGE_HPP
|
@ -0,0 +1,228 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_STRING_HPP
|
|||
#define TOML11_STRING_HPP
|
|||
|
|||
#include "version.hpp"
|
|||
|
|||
#include <cstdint>
|
|||
|
|||
#include <algorithm>
|
|||
#include <string>
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
|
|||
#if __has_include(<string_view>)
|
|||
#define TOML11_USING_STRING_VIEW 1
|
|||
#include <string_view>
|
|||
#endif
|
|||
#endif
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
enum class string_t : std::uint8_t |
|||
{ |
|||
basic = 0, |
|||
literal = 1, |
|||
}; |
|||
|
|||
struct string |
|||
{ |
|||
string() = default; |
|||
~string() = default; |
|||
string(const string& s) = default; |
|||
string(string&& s) = default; |
|||
string& operator=(const string& s) = default; |
|||
string& operator=(string&& s) = default; |
|||
|
|||
string(const std::string& s): kind(string_t::basic), str(s){} |
|||
string(const std::string& s, string_t k): kind(k), str(s){} |
|||
string(const char* s): kind(string_t::basic), str(s){} |
|||
string(const char* s, string_t k): kind(k), str(s){} |
|||
|
|||
string(std::string&& s): kind(string_t::basic), str(std::move(s)){} |
|||
string(std::string&& s, string_t k): kind(k), str(std::move(s)){} |
|||
|
|||
string& operator=(const std::string& s) |
|||
{kind = string_t::basic; str = s; return *this;} |
|||
string& operator=(std::string&& s) |
|||
{kind = string_t::basic; str = std::move(s); return *this;} |
|||
|
|||
operator std::string& () & noexcept {return str;} |
|||
operator std::string const& () const& noexcept {return str;} |
|||
operator std::string&& () && noexcept {return std::move(str);} |
|||
|
|||
string& operator+=(const char* rhs) {str += rhs; return *this;} |
|||
string& operator+=(const char rhs) {str += rhs; return *this;} |
|||
string& operator+=(const std::string& rhs) {str += rhs; return *this;} |
|||
string& operator+=(const string& rhs) {str += rhs.str; return *this;} |
|||
|
|||
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
|
|||
explicit string(std::string_view s): kind(string_t::basic), str(s){} |
|||
string(std::string_view s, string_t k): kind(k), str(s){} |
|||
|
|||
string& operator=(std::string_view s) |
|||
{kind = string_t::basic; str = s; return *this;} |
|||
|
|||
explicit operator std::string_view() const noexcept |
|||
{return std::string_view(str);} |
|||
|
|||
string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} |
|||
#endif
|
|||
|
|||
string_t kind; |
|||
std::string str; |
|||
}; |
|||
|
|||
inline bool operator==(const string& lhs, const string& rhs) |
|||
{ |
|||
return lhs.kind == rhs.kind && lhs.str == rhs.str; |
|||
} |
|||
inline bool operator!=(const string& lhs, const string& rhs) |
|||
{ |
|||
return !(lhs == rhs); |
|||
} |
|||
inline bool operator<(const string& lhs, const string& rhs) |
|||
{ |
|||
return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); |
|||
} |
|||
inline bool operator>(const string& lhs, const string& rhs) |
|||
{ |
|||
return rhs < lhs; |
|||
} |
|||
inline bool operator<=(const string& lhs, const string& rhs) |
|||
{ |
|||
return !(rhs < lhs); |
|||
} |
|||
inline bool operator>=(const string& lhs, const string& rhs) |
|||
{ |
|||
return !(lhs < rhs); |
|||
} |
|||
|
|||
inline bool |
|||
operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} |
|||
inline bool |
|||
operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} |
|||
inline bool |
|||
operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} |
|||
inline bool |
|||
operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} |
|||
inline bool |
|||
operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} |
|||
inline bool |
|||
operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} |
|||
|
|||
inline bool |
|||
operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} |
|||
inline bool |
|||
operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} |
|||
inline bool |
|||
operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} |
|||
inline bool |
|||
operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} |
|||
inline bool |
|||
operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} |
|||
inline bool |
|||
operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} |
|||
|
|||
inline bool |
|||
operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} |
|||
inline bool |
|||
operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} |
|||
inline bool |
|||
operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} |
|||
inline bool |
|||
operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} |
|||
inline bool |
|||
operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} |
|||
inline bool |
|||
operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} |
|||
|
|||
inline bool |
|||
operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} |
|||
inline bool |
|||
operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} |
|||
inline bool |
|||
operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} |
|||
inline bool |
|||
operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} |
|||
inline bool |
|||
operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} |
|||
inline bool |
|||
operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} |
|||
|
|||
template<typename charT, typename traits> |
|||
std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, const string& s) |
|||
{ |
|||
if(s.kind == string_t::basic) |
|||
{ |
|||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) |
|||
{ |
|||
// it contains newline. make it multiline string.
|
|||
os << "\"\"\"\n"; |
|||
for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) |
|||
{ |
|||
switch(*i) |
|||
{ |
|||
case '\\': {os << "\\\\"; break;} |
|||
case '\"': {os << "\\\""; break;} |
|||
case '\b': {os << "\\b"; break;} |
|||
case '\t': {os << "\\t"; break;} |
|||
case '\f': {os << "\\f"; break;} |
|||
case '\n': {os << '\n'; break;} |
|||
case '\r': |
|||
{ |
|||
// since it is a multiline string,
|
|||
// CRLF is not needed to be escaped.
|
|||
if(std::next(i) != e && *std::next(i) == '\n') |
|||
{ |
|||
os << "\r\n"; |
|||
++i; |
|||
} |
|||
else |
|||
{ |
|||
os << "\\r"; |
|||
} |
|||
break; |
|||
} |
|||
default: {os << *i; break;} |
|||
} |
|||
} |
|||
os << "\\\n\"\"\""; |
|||
return os; |
|||
} |
|||
// no newline. make it inline.
|
|||
os << "\""; |
|||
for(const auto c : s.str) |
|||
{ |
|||
switch(c) |
|||
{ |
|||
case '\\': {os << "\\\\"; break;} |
|||
case '\"': {os << "\\\""; break;} |
|||
case '\b': {os << "\\b"; break;} |
|||
case '\t': {os << "\\t"; break;} |
|||
case '\f': {os << "\\f"; break;} |
|||
case '\n': {os << "\\n"; break;} |
|||
case '\r': {os << "\\r"; break;} |
|||
default : {os << c; break;} |
|||
} |
|||
} |
|||
os << "\""; |
|||
return os; |
|||
} |
|||
// the string `s` is literal-string.
|
|||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || |
|||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) |
|||
{ |
|||
// contains newline or single quote. make it multiline.
|
|||
os << "'''\n" << s.str << "'''"; |
|||
return os; |
|||
} |
|||
// normal literal string
|
|||
os << '\'' << s.str << '\''; |
|||
return os; |
|||
} |
|||
|
|||
} // toml
|
|||
#endif// TOML11_STRING_H
|
@ -0,0 +1,328 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_TRAITS_HPP
|
|||
#define TOML11_TRAITS_HPP
|
|||
|
|||
#include "from.hpp"
|
|||
#include "into.hpp"
|
|||
#include "version.hpp"
|
|||
|
|||
#include <chrono>
|
|||
#include <forward_list>
|
|||
#include <string>
|
|||
#include <tuple>
|
|||
#include <type_traits>
|
|||
#include <utility>
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
|
|||
#if __has_include(<string_view>)
|
|||
#include <string_view>
|
|||
#endif // has_include(<string_view>)
|
|||
#endif // cplusplus >= C++17
|
|||
|
|||
namespace toml |
|||
{ |
|||
template<typename C, template<typename ...> class T, template<typename ...> class A> |
|||
class basic_value; |
|||
|
|||
namespace detail |
|||
{ |
|||
// ---------------------------------------------------------------------------
|
|||
// check whether type T is a kind of container/map class
|
|||
|
|||
struct has_iterator_impl |
|||
{ |
|||
template<typename T> static std::true_type check(typename T::iterator*); |
|||
template<typename T> static std::false_type check(...); |
|||
}; |
|||
struct has_value_type_impl |
|||
{ |
|||
template<typename T> static std::true_type check(typename T::value_type*); |
|||
template<typename T> static std::false_type check(...); |
|||
}; |
|||
struct has_key_type_impl |
|||
{ |
|||
template<typename T> static std::true_type check(typename T::key_type*); |
|||
template<typename T> static std::false_type check(...); |
|||
}; |
|||
struct has_mapped_type_impl |
|||
{ |
|||
template<typename T> static std::true_type check(typename T::mapped_type*); |
|||
template<typename T> static std::false_type check(...); |
|||
}; |
|||
struct has_reserve_method_impl |
|||
{ |
|||
template<typename T> static std::false_type check(...); |
|||
template<typename T> static std::true_type check( |
|||
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*); |
|||
}; |
|||
struct has_push_back_method_impl |
|||
{ |
|||
template<typename T> static std::false_type check(...); |
|||
template<typename T> static std::true_type check( |
|||
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*); |
|||
}; |
|||
struct is_comparable_impl |
|||
{ |
|||
template<typename T> static std::false_type check(...); |
|||
template<typename T> static std::true_type check( |
|||
decltype(std::declval<T>() < std::declval<T>())*); |
|||
}; |
|||
|
|||
struct has_from_toml_method_impl |
|||
{ |
|||
template<typename T, typename C, |
|||
template<typename ...> class Tb, template<typename ...> class A> |
|||
static std::true_type check( |
|||
decltype(std::declval<T>().from_toml( |
|||
std::declval<::toml::basic_value<C, Tb, A>>()))*); |
|||
|
|||
template<typename T, typename C, |
|||
template<typename ...> class Tb, template<typename ...> class A> |
|||
static std::false_type check(...); |
|||
}; |
|||
struct has_into_toml_method_impl |
|||
{ |
|||
template<typename T> |
|||
static std::true_type check(decltype(std::declval<T>().into_toml())*); |
|||
template<typename T> |
|||
static std::false_type check(...); |
|||
}; |
|||
|
|||
struct has_specialized_from_impl |
|||
{ |
|||
template<typename T> |
|||
static std::false_type check(...); |
|||
template<typename T, std::size_t S = sizeof(::toml::from<T>)> |
|||
static std::true_type check(::toml::from<T>*); |
|||
}; |
|||
struct has_specialized_into_impl |
|||
{ |
|||
template<typename T> |
|||
static std::false_type check(...); |
|||
template<typename T, std::size_t S = sizeof(::toml::into<T>)> |
|||
static std::true_type check(::toml::from<T>*); |
|||
}; |
|||
|
|||
|
|||
/// Intel C++ compiler can not use decltype in parent class declaration, here
|
|||
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
|
|||
#ifdef __INTEL_COMPILER
|
|||
#define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
|
|||
#endif
|
|||
|
|||
template<typename T> |
|||
struct has_iterator : decltype(has_iterator_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct has_value_type : decltype(has_value_type_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){}; |
|||
|
|||
template<typename T, typename C, |
|||
template<typename ...> class Tb, template<typename ...> class A> |
|||
struct has_from_toml_method |
|||
: decltype(has_from_toml_method_impl::check<T, C, Tb, A>(nullptr)){}; |
|||
|
|||
template<typename T> |
|||
struct has_into_toml_method |
|||
: decltype(has_into_toml_method_impl::check<T>(nullptr)){}; |
|||
|
|||
template<typename T> |
|||
struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){}; |
|||
template<typename T> |
|||
struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){}; |
|||
|
|||
#ifdef __INTEL_COMPILER
|
|||
#undef decltype
|
|||
#endif
|
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// C++17 and/or/not
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
|
|||
|
|||
using std::conjunction; |
|||
using std::disjunction; |
|||
using std::negation; |
|||
|
|||
#else
|
|||
|
|||
template<typename ...> struct conjunction : std::true_type{}; |
|||
template<typename T> struct conjunction<T> : T{}; |
|||
template<typename T, typename ... Ts> |
|||
struct conjunction<T, Ts...> : |
|||
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type |
|||
{}; |
|||
|
|||
template<typename ...> struct disjunction : std::false_type{}; |
|||
template<typename T> struct disjunction<T> : T {}; |
|||
template<typename T, typename ... Ts> |
|||
struct disjunction<T, Ts...> : |
|||
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type |
|||
{}; |
|||
|
|||
template<typename T> |
|||
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{}; |
|||
|
|||
#endif
|
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// type checkers
|
|||
|
|||
template<typename T> struct is_std_pair : std::false_type{}; |
|||
template<typename T1, typename T2> |
|||
struct is_std_pair<std::pair<T1, T2>> : std::true_type{}; |
|||
|
|||
template<typename T> struct is_std_tuple : std::false_type{}; |
|||
template<typename ... Ts> |
|||
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{}; |
|||
|
|||
template<typename T> struct is_std_forward_list : std::false_type{}; |
|||
template<typename T> |
|||
struct is_std_forward_list<std::forward_list<T>> : std::true_type{}; |
|||
|
|||
template<typename T> struct is_chrono_duration: std::false_type{}; |
|||
template<typename Rep, typename Period> |
|||
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{}; |
|||
|
|||
template<typename T> |
|||
struct is_map : conjunction< // map satisfies all the following conditions
|
|||
has_iterator<T>, // has T::iterator
|
|||
has_value_type<T>, // has T::value_type
|
|||
has_key_type<T>, // has T::key_type
|
|||
has_mapped_type<T> // has T::mapped_type
|
|||
>{}; |
|||
template<typename T> struct is_map<T&> : is_map<T>{}; |
|||
template<typename T> struct is_map<T const&> : is_map<T>{}; |
|||
template<typename T> struct is_map<T volatile&> : is_map<T>{}; |
|||
template<typename T> struct is_map<T const volatile&> : is_map<T>{}; |
|||
|
|||
template<typename T> |
|||
struct is_container : conjunction< |
|||
negation<is_map<T>>, // not a map
|
|||
negation<std::is_same<T, std::string>>, // not a std::string
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
|
|||
#if __has_include(<string_view>)
|
|||
negation<std::is_same<T, std::string_view>>, // not a std::string_view
|
|||
#endif // has_include(<string_view>)
|
|||
#endif
|
|||
has_iterator<T>, // has T::iterator
|
|||
has_value_type<T> // has T::value_type
|
|||
>{}; |
|||
template<typename T> struct is_container<T&> : is_container<T>{}; |
|||
template<typename T> struct is_container<T const&> : is_container<T>{}; |
|||
template<typename T> struct is_container<T volatile&> : is_container<T>{}; |
|||
template<typename T> struct is_container<T const volatile&> : is_container<T>{}; |
|||
|
|||
template<typename T> |
|||
struct is_basic_value: std::false_type{}; |
|||
template<typename T> struct is_basic_value<T&> : is_basic_value<T>{}; |
|||
template<typename T> struct is_basic_value<T const&> : is_basic_value<T>{}; |
|||
template<typename T> struct is_basic_value<T volatile&> : is_basic_value<T>{}; |
|||
template<typename T> struct is_basic_value<T const volatile&> : is_basic_value<T>{}; |
|||
template<typename C, template<typename ...> class M, template<typename ...> class V> |
|||
struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{}; |
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// C++14 index_sequence
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
|
|||
|
|||
using std::index_sequence; |
|||
using std::make_index_sequence; |
|||
|
|||
#else
|
|||
|
|||
template<std::size_t ... Ns> struct index_sequence{}; |
|||
|
|||
template<typename IS, std::size_t N> struct push_back_index_sequence{}; |
|||
template<std::size_t N, std::size_t ... Ns> |
|||
struct push_back_index_sequence<index_sequence<Ns...>, N> |
|||
{ |
|||
typedef index_sequence<Ns..., N> type; |
|||
}; |
|||
|
|||
template<std::size_t N> |
|||
struct index_sequence_maker |
|||
{ |
|||
typedef typename push_back_index_sequence< |
|||
typename index_sequence_maker<N-1>::type, N>::type type; |
|||
}; |
|||
template<> |
|||
struct index_sequence_maker<0> |
|||
{ |
|||
typedef index_sequence<0> type; |
|||
}; |
|||
template<std::size_t N> |
|||
using make_index_sequence = typename index_sequence_maker<N-1>::type; |
|||
|
|||
#endif // cplusplus >= 2014
|
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// C++14 enable_if_t
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
|
|||
|
|||
using std::enable_if_t; |
|||
|
|||
#else
|
|||
|
|||
template<bool B, typename T> |
|||
using enable_if_t = typename std::enable_if<B, T>::type; |
|||
|
|||
#endif // cplusplus >= 2014
|
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// return_type_of_t
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
|
|||
|
|||
template<typename F, typename ... Args> |
|||
using return_type_of_t = std::invoke_result_t<F, Args...>; |
|||
|
|||
#else
|
|||
// result_of is deprecated after C++17
|
|||
template<typename F, typename ... Args> |
|||
using return_type_of_t = typename std::result_of<F(Args...)>::type; |
|||
|
|||
#endif
|
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// is_string_literal
|
|||
//
|
|||
// to use this, pass `typename remove_reference<T>::type` to T.
|
|||
|
|||
template<typename T> |
|||
struct is_string_literal: |
|||
disjunction< |
|||
std::is_same<const char*, T>, |
|||
conjunction< |
|||
std::is_array<T>, |
|||
std::is_same<const char, typename std::remove_extent<T>::type> |
|||
> |
|||
>{}; |
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// C++20 remove_cvref_t
|
|||
|
|||
template<typename T> |
|||
struct remove_cvref |
|||
{ |
|||
using type = typename std::remove_cv< |
|||
typename std::remove_reference<T>::type>::type; |
|||
}; |
|||
|
|||
template<typename T> |
|||
using remove_cvref_t = typename remove_cvref<T>::type; |
|||
|
|||
}// detail
|
|||
}//toml
|
|||
#endif // TOML_TRAITS
|
@ -0,0 +1,173 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_TYPES_HPP
|
|||
#define TOML11_TYPES_HPP
|
|||
#include <unordered_map>
|
|||
#include <vector>
|
|||
|
|||
#include "comments.hpp"
|
|||
#include "datetime.hpp"
|
|||
#include "string.hpp"
|
|||
#include "traits.hpp"
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
template<typename Comment, // discard/preserve_comment
|
|||
template<typename ...> class Table, // map-like class
|
|||
template<typename ...> class Array> // vector-like class
|
|||
class basic_value; |
|||
|
|||
using character = char; |
|||
using key = std::string; |
|||
|
|||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
|
|||
# pragma GCC diagnostic push
|
|||
# pragma GCC diagnostic ignored "-Wshadow"
|
|||
#endif
|
|||
|
|||
using boolean = bool; |
|||
using integer = std::int64_t; |
|||
using floating = double; // "float" is a keyword, cannot use it here.
|
|||
// the following stuffs are structs defined here, so aliases are not needed.
|
|||
// - string
|
|||
// - offset_datetime
|
|||
// - offset_datetime
|
|||
// - local_datetime
|
|||
// - local_date
|
|||
// - local_time
|
|||
|
|||
#if defined(__GNUC__) && !defined(__clang__)
|
|||
# pragma GCC diagnostic pop
|
|||
#endif
|
|||
|
|||
// default toml::value and default array/table. these are defined after defining
|
|||
// basic_value itself.
|
|||
// using value = basic_value<discard_comments, std::unordered_map, std::vector>;
|
|||
// using array = typename value::array_type;
|
|||
// using table = typename value::table_type;
|
|||
|
|||
// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
|
|||
// GCC -Wshadow=global.
|
|||
#if defined(__GNUC__) && !defined(__clang__)
|
|||
# pragma GCC diagnostic push
|
|||
# if 7 <= __GNUC__
|
|||
# pragma GCC diagnostic ignored "-Wshadow=global"
|
|||
# else // gcc-6 or older
|
|||
# pragma GCC diagnostic ignored "-Wshadow"
|
|||
# endif
|
|||
#endif
|
|||
enum class value_t : std::uint8_t |
|||
{ |
|||
empty = 0, |
|||
boolean = 1, |
|||
integer = 2, |
|||
floating = 3, |
|||
string = 4, |
|||
offset_datetime = 5, |
|||
local_datetime = 6, |
|||
local_date = 7, |
|||
local_time = 8, |
|||
array = 9, |
|||
table = 10, |
|||
}; |
|||
#if defined(__GNUC__) && !defined(__clang__)
|
|||
# pragma GCC diagnostic pop
|
|||
#endif
|
|||
|
|||
template<typename charT, typename traits> |
|||
inline std::basic_ostream<charT, traits>& |
|||
operator<<(std::basic_ostream<charT, traits>& os, value_t t) |
|||
{ |
|||
switch(t) |
|||
{ |
|||
case value_t::boolean : os << "boolean"; return os; |
|||
case value_t::integer : os << "integer"; return os; |
|||
case value_t::floating : os << "floating"; return os; |
|||
case value_t::string : os << "string"; return os; |
|||
case value_t::offset_datetime : os << "offset_datetime"; return os; |
|||
case value_t::local_datetime : os << "local_datetime"; return os; |
|||
case value_t::local_date : os << "local_date"; return os; |
|||
case value_t::local_time : os << "local_time"; return os; |
|||
case value_t::array : os << "array"; return os; |
|||
case value_t::table : os << "table"; return os; |
|||
case value_t::empty : os << "empty"; return os; |
|||
default : os << "unknown"; return os; |
|||
} |
|||
} |
|||
|
|||
template<typename charT = char, |
|||
typename traits = std::char_traits<charT>, |
|||
typename alloc = std::allocator<charT>> |
|||
inline std::basic_string<charT, traits, alloc> stringize(value_t t) |
|||
{ |
|||
std::basic_ostringstream<charT, traits, alloc> oss; |
|||
oss << t; |
|||
return oss.str(); |
|||
} |
|||
|
|||
namespace detail |
|||
{ |
|||
|
|||
// helper to define a type that represents a value_t value.
|
|||
template<value_t V> |
|||
using value_t_constant = std::integral_constant<value_t, V>; |
|||
|
|||
// meta-function that convertes from value_t to the exact toml type that corresponds to.
|
|||
// It takes toml::basic_value type because array and table types depend on it.
|
|||
template<value_t t, typename Value> struct enum_to_type {using type = void ;}; |
|||
template<typename Value> struct enum_to_type<value_t::empty , Value>{using type = void ;}; |
|||
template<typename Value> struct enum_to_type<value_t::boolean , Value>{using type = boolean ;}; |
|||
template<typename Value> struct enum_to_type<value_t::integer , Value>{using type = integer ;}; |
|||
template<typename Value> struct enum_to_type<value_t::floating , Value>{using type = floating ;}; |
|||
template<typename Value> struct enum_to_type<value_t::string , Value>{using type = string ;}; |
|||
template<typename Value> struct enum_to_type<value_t::offset_datetime, Value>{using type = offset_datetime ;}; |
|||
template<typename Value> struct enum_to_type<value_t::local_datetime , Value>{using type = local_datetime ;}; |
|||
template<typename Value> struct enum_to_type<value_t::local_date , Value>{using type = local_date ;}; |
|||
template<typename Value> struct enum_to_type<value_t::local_time , Value>{using type = local_time ;}; |
|||
template<typename Value> struct enum_to_type<value_t::array , Value>{using type = typename Value::array_type;}; |
|||
template<typename Value> struct enum_to_type<value_t::table , Value>{using type = typename Value::table_type;}; |
|||
|
|||
// meta-function that converts from an exact toml type to the enum that corresponds to.
|
|||
template<typename T, typename Value> |
|||
struct type_to_enum : std::conditional< |
|||
std::is_same<T, typename Value::array_type>::value, // if T == array_type,
|
|||
value_t_constant<value_t::array>, // then value_t::array
|
|||
typename std::conditional< // else...
|
|||
std::is_same<T, typename Value::table_type>::value, // if T == table_type
|
|||
value_t_constant<value_t::table>, // then value_t::table
|
|||
value_t_constant<value_t::empty> // else value_t::empty
|
|||
>::type |
|||
>::type {}; |
|||
template<typename Value> struct type_to_enum<boolean , Value>: value_t_constant<value_t::boolean > {}; |
|||
template<typename Value> struct type_to_enum<integer , Value>: value_t_constant<value_t::integer > {}; |
|||
template<typename Value> struct type_to_enum<floating , Value>: value_t_constant<value_t::floating > {}; |
|||
template<typename Value> struct type_to_enum<string , Value>: value_t_constant<value_t::string > {}; |
|||
template<typename Value> struct type_to_enum<offset_datetime, Value>: value_t_constant<value_t::offset_datetime> {}; |
|||
template<typename Value> struct type_to_enum<local_datetime , Value>: value_t_constant<value_t::local_datetime > {}; |
|||
template<typename Value> struct type_to_enum<local_date , Value>: value_t_constant<value_t::local_date > {}; |
|||
template<typename Value> struct type_to_enum<local_time , Value>: value_t_constant<value_t::local_time > {}; |
|||
|
|||
// meta-function that checks the type T is the same as one of the toml::* types.
|
|||
template<typename T, typename Value> |
|||
struct is_exact_toml_type : disjunction< |
|||
std::is_same<T, boolean >, |
|||
std::is_same<T, integer >, |
|||
std::is_same<T, floating >, |
|||
std::is_same<T, string >, |
|||
std::is_same<T, offset_datetime>, |
|||
std::is_same<T, local_datetime >, |
|||
std::is_same<T, local_date >, |
|||
std::is_same<T, local_time >, |
|||
std::is_same<T, typename Value::array_type>, |
|||
std::is_same<T, typename Value::table_type> |
|||
>{}; |
|||
template<typename T, typename V> struct is_exact_toml_type<T&, V> : is_exact_toml_type<T, V>{}; |
|||
template<typename T, typename V> struct is_exact_toml_type<T const&, V> : is_exact_toml_type<T, V>{}; |
|||
template<typename T, typename V> struct is_exact_toml_type<T volatile&, V> : is_exact_toml_type<T, V>{}; |
|||
template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>: is_exact_toml_type<T, V>{}; |
|||
|
|||
} // detail
|
|||
} // toml
|
|||
|
|||
#endif// TOML11_TYPES_H
|
@ -0,0 +1,150 @@ |
|||
// Copyright Toru Niina 2017.
|
|||
// Distributed under the MIT License.
|
|||
#ifndef TOML11_UTILITY_HPP
|
|||
#define TOML11_UTILITY_HPP
|
|||
#include <memory>
|
|||
#include <sstream>
|
|||
#include <utility>
|
|||
|
|||
#include "traits.hpp"
|
|||
#include "version.hpp"
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
|
|||
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
|
|||
#elif defined(__GNUC__)
|
|||
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
|||
#elif defined(_MSC_VER)
|
|||
# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
|
|||
#else
|
|||
# define TOML11_MARK_AS_DEPRECATED
|
|||
#endif
|
|||
|
|||
namespace toml |
|||
{ |
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
|
|||
|
|||
using std::make_unique; |
|||
|
|||
#else
|
|||
|
|||
template<typename T, typename ... Ts> |
|||
inline std::unique_ptr<T> make_unique(Ts&& ... args) |
|||
{ |
|||
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...)); |
|||
} |
|||
|
|||
#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
|
|||
|
|||
namespace detail |
|||
{ |
|||
template<typename Container> |
|||
void try_reserve_impl(Container& container, std::size_t N, std::true_type) |
|||
{ |
|||
container.reserve(N); |
|||
return; |
|||
} |
|||
template<typename Container> |
|||
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept |
|||
{ |
|||
return; |
|||
} |
|||
} // detail
|
|||
|
|||
template<typename Container> |
|||
void try_reserve(Container& container, std::size_t N) |
|||
{ |
|||
if(N <= container.size()) {return;} |
|||
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{}); |
|||
return; |
|||
} |
|||
|
|||
namespace detail |
|||
{ |
|||
inline std::string concat_to_string_impl(std::ostringstream& oss) |
|||
{ |
|||
return oss.str(); |
|||
} |
|||
template<typename T, typename ... Ts> |
|||
std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) |
|||
{ |
|||
oss << std::forward<T>(head); |
|||
return concat_to_string_impl(oss, std::forward<Ts>(tail) ... ); |
|||
} |
|||
} // detail
|
|||
|
|||
template<typename ... Ts> |
|||
std::string concat_to_string(Ts&& ... args) |
|||
{ |
|||
std::ostringstream oss; |
|||
oss << std::boolalpha << std::fixed; |
|||
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...); |
|||
} |
|||
|
|||
template<typename T> |
|||
T from_string(const std::string& str, T opt) |
|||
{ |
|||
T v(opt); |
|||
std::istringstream iss(str); |
|||
iss >> v; |
|||
return v; |
|||
} |
|||
|
|||
namespace detail |
|||
{ |
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
|
|||
template<typename T> |
|||
decltype(auto) last_one(T&& tail) noexcept |
|||
{ |
|||
return std::forward<T>(tail); |
|||
} |
|||
|
|||
template<typename T, typename ... Ts> |
|||
decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept |
|||
{ |
|||
return last_one(std::forward<Ts>(tail)...); |
|||
} |
|||
#else // C++11
|
|||
// The following code
|
|||
// ```cpp
|
|||
// 1 | template<typename T, typename ... Ts>
|
|||
// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
|
|||
// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
|
|||
// 4 | {
|
|||
// 5 | return last_one(std::forward<Ts>(tail)...);
|
|||
// 6 | }
|
|||
// ```
|
|||
// does not work because the function `last_one(...)` is not yet defined at
|
|||
// line #3, so `decltype()` cannot deduce the type returned from `last_one`.
|
|||
// So we need to determine return type in a different way, like a meta func.
|
|||
|
|||
template<typename T, typename ... Ts> |
|||
struct last_one_in_pack |
|||
{ |
|||
using type = typename last_one_in_pack<Ts...>::type; |
|||
}; |
|||
template<typename T> |
|||
struct last_one_in_pack<T> |
|||
{ |
|||
using type = T; |
|||
}; |
|||
template<typename ... Ts> |
|||
using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type; |
|||
|
|||
template<typename T> |
|||
T&& last_one(T&& tail) noexcept |
|||
{ |
|||
return std::forward<T>(tail); |
|||
} |
|||
template<typename T, typename ... Ts> |
|||
enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>> |
|||
last_one(T&& /*head*/, Ts&& ... tail) |
|||
{ |
|||
return last_one(std::forward<Ts>(tail)...); |
|||
} |
|||
|
|||
#endif
|
|||
} // detail
|
|||
|
|||
}// toml
|
|||
#endif // TOML11_UTILITY
|
2037
src/toml/value.hpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,42 @@ |
|||
#ifndef TOML11_VERSION_HPP
|
|||
#define TOML11_VERSION_HPP
|
|||
|
|||
// This file checks C++ version.
|
|||
|
|||
#ifndef __cplusplus
|
|||
# error "__cplusplus is not defined"
|
|||
#endif
|
|||
|
|||
// Since MSVC does not define `__cplusplus` correctly unless you pass
|
|||
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
|
|||
// Those enables you to define version manually or to use MSVC specific
|
|||
// version macro automatically.
|
|||
//
|
|||
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
|
|||
// MSVC ignores the value, maybe because of backward compatibility. Instead,
|
|||
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
|
|||
// the C++ standard. First we check the manual version definition, and then
|
|||
// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
|
|||
//
|
|||
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
|
|||
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
|
|||
//
|
|||
#if defined(TOML11_ENFORCE_CXX11)
|
|||
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
|
|||
#elif defined(TOML11_ENFORCE_CXX14)
|
|||
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
|
|||
#elif defined(TOML11_ENFORCE_CXX17)
|
|||
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
|
|||
#elif defined(TOML11_ENFORCE_CXX20)
|
|||
# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
|
|||
#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
|
|||
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
|
|||
#else
|
|||
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
|
|||
#endif
|
|||
|
|||
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
|
|||
# error "toml11 requires C++11 or later."
|
|||
#endif
|
|||
|
|||
#endif// TOML11_VERSION_HPP
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue