chore: update auto-generated files and header libraries from build [skip ci]

This commit is contained in:
github-actions[bot]
2026-05-19 15:41:00 +00:00
parent 1557bddc57
commit 3693efb89e
77 changed files with 43306 additions and 0 deletions

34
include/date/chrono_io.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef CHRONO_IO_H
#define CHRONO_IO_H
// The MIT License (MIT)
//
// Copyright (c) 2016, 2017 Howard Hinnant
//
// 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.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that would involve another several millennia of evolution).
// We did not mean to shout.
// This functionality has moved to "date.h"
#include "date.h"
#endif // CHRONO_IO_H

8356
include/date/date.h Normal file

File diff suppressed because it is too large Load Diff

50
include/date/ios.h Normal file
View File

@@ -0,0 +1,50 @@
//
// ios.h
// DateTimeLib
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Alexander Kormanovsky
//
// 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 ios_hpp
#define ios_hpp
#if __APPLE__
# include <TargetConditionals.h>
# if TARGET_OS_IPHONE
# include <string>
namespace date
{
namespace iOSUtils
{
std::string get_tzdata_path();
std::string get_current_timezone();
} // namespace iOSUtils
} // namespace date
# endif // TARGET_OS_IPHONE
#else // !__APPLE__
# define TARGET_OS_IPHONE 0
#endif // !__APPLE__
#endif // ios_hpp

3031
include/date/islamic.h Normal file

File diff suppressed because it is too large Load Diff

1761
include/date/iso_week.h Normal file

File diff suppressed because it is too large Load Diff

3052
include/date/julian.h Normal file

File diff suppressed because it is too large Load Diff

952
include/date/ptz.h Normal file
View File

@@ -0,0 +1,952 @@
#ifndef PTZ_H
#define PTZ_H
// The MIT License (MIT)
//
// Copyright (c) 2017 Howard Hinnant
//
// 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.
// This header allows Posix-style time zones as specified for TZ here:
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
//
// Posix::time_zone can be constructed with a posix-style string and then used in
// a zoned_time like so:
//
// zoned_time<system_clock::duration, Posix::time_zone> zt{"EST5EDT,M3.2.0,M11.1.0",
// system_clock::now()};
// or:
//
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
//
// In C++17 CTAD simplifies this to:
//
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time zt{tz, system_clock::now()};
//
// Extension to the Posix rules to allow a constant daylight saving offset:
//
// If the rule set is missing (everything starting with ','), then
// there must be exactly one abbreviation (std or daylight) with
// length 3 or greater, and that will be used as the constant offset. If
// there are two, the std abbreviation is silently set to "", and the
// result is constant daylight saving. If there are zero abbreviations
// with no rule set, an exception is thrown.
//
// Example:
// "EST5" yields a constant offset of -5h with 0h save and "EST abbreviation.
// "5EDT" yields a constant offset of -4h with 1h save and "EDT" abbreviation.
// "EST5EDT" and "5EDT4" are both equal to "5EDT".
//
// Note, Posix-style time zones are not recommended for all of the reasons described here:
// https://stackoverflow.com/tags/timezone/info
//
// They are provided here as a non-trivial custom time zone example, and if you really
// have to have Posix time zones, you're welcome to use this one.
#include "date/tz.h"
#include <algorithm>
#include <cctype>
#include <ostream>
#include <string>
namespace Posix
{
namespace detail
{
#if HAS_STRING_VIEW
using string_t = std::string_view;
#else // !HAS_STRING_VIEW
using string_t = std::string;
#endif // !HAS_STRING_VIEW
class rule;
void throw_invalid(const string_t& s, unsigned i, const string_t& message);
unsigned read_date(const string_t& s, unsigned i, rule& r);
unsigned read_name(const string_t& s, unsigned i, std::string& name);
unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t);
unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t);
unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
const string_t& message = string_t{});
class rule
{
enum {off, J, M, N};
date::month m_;
date::weekday wd_;
unsigned short n_ : 14;
unsigned short mode_ : 2;
std::chrono::duration<std::int32_t> time_ = std::chrono::hours{2};
public:
rule() : mode_(off) {}
bool ok() const {return mode_ != off;}
date::local_seconds operator()(date::year y) const;
std::string to_string() const;
friend std::ostream& operator<<(std::ostream& os, const rule& r);
friend unsigned read_date(const string_t& s, unsigned i, rule& r);
friend bool operator==(const rule& x, const rule& y);
};
inline
bool
operator==(const rule& x, const rule& y)
{
if (x.mode_ != y.mode_)
return false;
switch (x.mode_)
{
case rule::J:
case rule::N:
return x.n_ == y.n_;
case rule::M:
return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_;
default:
return true;
}
}
inline
bool
operator!=(const rule& x, const rule& y)
{
return !(x == y);
}
inline
date::local_seconds
rule::operator()(date::year y) const
{
using date::local_days;
using date::January;
using date::days;
using date::last;
using sec = std::chrono::seconds;
date::local_seconds t;
switch (mode_)
{
case J:
t = local_days{y/January/0} + days{n_ + (y.is_leap() && n_ > 59)} + sec{time_};
break;
case M:
t = (n_ == 5 ? local_days{y/m_/wd_[last]} : local_days{y/m_/wd_[n_]}) + sec{time_};
break;
case N:
t = local_days{y/January/1} + days{n_} + sec{time_};
break;
default:
assert(!"rule called with bad mode");
}
return t;
}
inline
std::string
rule::to_string() const
{
using namespace std::chrono;
auto print_offset = [](seconds off)
{
std::string nm;
if (off != hours{2})
{
date::hh_mm_ss<seconds> offset{off};
nm = '/';
nm += std::to_string(offset.hours().count());
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
{
nm += ':';
if (offset.minutes() < minutes{10})
nm += '0';
nm += std::to_string(offset.minutes().count());
if (offset.seconds() != seconds{0})
{
nm += ':';
if (offset.seconds() < seconds{10})
nm += '0';
nm += std::to_string(offset.seconds().count());
}
}
}
return nm;
};
std::string nm;
switch (mode_)
{
case rule::J:
nm = 'J';
nm += std::to_string(n_);
break;
case rule::M:
nm = 'M';
nm += std::to_string(static_cast<unsigned>(m_));
nm += '.';
nm += std::to_string(n_);
nm += '.';
nm += std::to_string(wd_.c_encoding());
break;
case rule::N:
nm = std::to_string(n_);
break;
default:
break;
}
nm += print_offset(time_);
return nm;
}
inline
std::ostream&
operator<<(std::ostream& os, const rule& r)
{
switch (r.mode_)
{
case rule::J:
os << 'J' << r.n_ << date::format(" %T", r.time_);
break;
case rule::M:
if (r.n_ == 5)
os << r.m_/r.wd_[date::last];
else
os << r.m_/r.wd_[r.n_];
os << date::format(" %T", r.time_);
break;
case rule::N:
os << r.n_ << date::format(" %T", r.time_);
break;
default:
break;
}
return os;
}
} // namespace detail
class time_zone
{
std::string std_abbrev_;
std::string dst_abbrev_ = {};
std::chrono::seconds offset_;
std::chrono::seconds save_ = std::chrono::hours{1};
detail::rule start_rule_;
detail::rule end_rule_;
public:
explicit time_zone(const detail::string_t& name);
template <class Duration>
date::sys_info get_info(date::sys_time<Duration> st) const;
template <class Duration>
date::local_info get_info(date::local_time<Duration> tp) const;
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(date::local_time<Duration> tp) const;
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(date::local_time<Duration> tp, date::choose z) const;
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_local(date::sys_time<Duration> tp) const;
friend std::ostream& operator<<(std::ostream& os, const time_zone& z);
const time_zone* operator->() const {return this;}
std::string name() const;
friend bool operator==(const time_zone& x, const time_zone& y);
private:
date::sys_seconds get_start(date::year y) const;
date::sys_seconds get_prev_start(date::year y) const;
date::sys_seconds get_next_start(date::year y) const;
date::sys_seconds get_end(date::year y) const;
date::sys_seconds get_prev_end(date::year y) const;
date::sys_seconds get_next_end(date::year y) const;
date::sys_info contant_offset() const;
};
inline
date::sys_seconds
time_zone::get_start(date::year y) const
{
return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_prev_start(date::year y) const
{
return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_next_start(date::year y) const
{
return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_end(date::year y) const
{
return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_prev_end(date::year y) const
{
return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_next_end(date::year y) const
{
return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_info
time_zone::contant_offset() const
{
using date::year;
using date::sys_info;
using date::sys_days;
using date::January;
using date::December;
using date::last;
using date::days;
using std::chrono::minutes;
sys_info r;
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last} + days{1} - std::chrono::seconds{1};
if (std_abbrev_.size() > 0)
{
r.abbrev = std_abbrev_;
r.offset = offset_;
r.save = {};
}
else
{
r.abbrev = dst_abbrev_;
r.offset = offset_ + save_;
r.save = date::ceil<minutes>(save_);
}
return r;
}
inline
time_zone::time_zone(const detail::string_t& s)
{
using detail::read_name;
using detail::read_signed_time;
using detail::throw_invalid;
auto i = read_name(s, 0, std_abbrev_);
auto std_name_i = i;
auto abbrev_name_i = i;
i = read_signed_time(s, i, offset_);
offset_ = -offset_;
if (i != s.size())
{
i = read_name(s, i, dst_abbrev_);
abbrev_name_i = i;
if (i != s.size())
{
if (s[i] != ',')
{
i = read_signed_time(s, i, save_);
save_ = -save_ - offset_;
}
if (i != s.size())
{
if (s[i] != ',')
throw_invalid(s, i, "Expecting end of string or ',' to start rule");
++i;
i = read_date(s, i, start_rule_);
if (i == s.size() || s[i] != ',')
throw_invalid(s, i, "Expecting ',' and then the ending rule");
++i;
i = read_date(s, i, end_rule_);
if (i != s.size())
throw_invalid(s, i, "Found unexpected trailing characters");
}
}
}
if (start_rule_.ok())
{
if (std_abbrev_.size() < 3)
throw_invalid(s, std_name_i, "Zone with rules must have a std"
" abbreviation of length 3 or greater");
if (dst_abbrev_.size() < 3)
throw_invalid(s, abbrev_name_i, "Zone with rules must have a daylight"
" abbreviation of length 3 or greater");
}
else
{
if (dst_abbrev_.size() >= 3)
{
std_abbrev_.clear();
}
else if (std_abbrev_.size() < 3)
{
throw_invalid(s, std_name_i, "Zone must have at least one abbreviation"
" of length 3 or greater");
}
else
{
dst_abbrev_.clear();
save_ = {};
}
}
}
template <class Duration>
date::sys_info
time_zone::get_info(date::sys_time<Duration> st) const
{
using date::sys_info;
using date::year_month_day;
using date::sys_days;
using date::floor;
using date::ceil;
using date::days;
using date::year;
using date::January;
using date::December;
using date::last;
using std::chrono::minutes;
sys_info r{};
r.offset = offset_;
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(st)}.year();
if (st >= get_next_start(y))
++y;
else if (st < get_prev_end(y))
--y;
auto start = get_start(y);
auto end = get_end(y);
if (start <= end) // (northern hemisphere)
{
if (start <= st && st < end)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else if (st < start)
{
r.begin = get_prev_end(y);
r.end = start;
r.abbrev = std_abbrev_;
}
else // st >= end
{
r.begin = end;
r.end = get_next_start(y);
r.abbrev = std_abbrev_;
}
}
else // end < start (southern hemisphere)
{
if (end <= st && st < start)
{
r.begin = end;
r.end = start;
r.abbrev = std_abbrev_;
}
else if (st < end)
{
r.begin = get_prev_start(y);
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else // st >= start
{
r.begin = start;
r.end = get_next_end(y);
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
}
}
else
r = contant_offset();
using seconds = std::chrono::seconds;
assert(r.begin <= floor<seconds>(st) && floor<seconds>(st) <= r.end);
return r;
}
template <class Duration>
date::local_info
time_zone::get_info(date::local_time<Duration> tp) const
{
using date::local_info;
using date::year_month_day;
using date::days;
using date::sys_days;
using date::sys_seconds;
using date::year;
using date::ceil;
using date::January;
using date::December;
using date::last;
using std::chrono::seconds;
using std::chrono::minutes;
local_info r{};
using date::floor;
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(tp)}.year();
auto start = get_start(y);
auto end = get_end(y);
auto utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()};
auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()};
auto northern = start <= end;
if ((utcs < start) != (utcd < start))
{
if (northern)
r.first.begin = get_prev_end(y);
else
r.first.begin = end;
r.first.end = start;
r.first.offset = offset_;
r.first.abbrev = std_abbrev_;
r.second.begin = start;
if (northern)
r.second.end = end;
else
r.second.end = get_next_end(y);
r.second.abbrev = dst_abbrev_;
r.second.offset = offset_ + save_;
r.second.save = ceil<minutes>(save_);
r.result = save_ > seconds{0} ? local_info::nonexistent
: local_info::ambiguous;
}
else if ((utcs < end) != (utcd < end))
{
if (northern)
r.first.begin = start;
else
r.first.begin = get_prev_start(y);
r.first.end = end;
r.first.offset = offset_ + save_;
r.first.save = ceil<minutes>(save_);
r.first.abbrev = dst_abbrev_;
r.second.begin = end;
if (northern)
r.second.end = get_next_start(y);
else
r.second.end = start;
r.second.abbrev = std_abbrev_;
r.second.offset = offset_;
r.result = save_ > seconds{0} ? local_info::ambiguous
: local_info::nonexistent;
}
else
r.first = get_info(utcs);
}
else
r.first = contant_offset();
return r;
}
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(date::local_time<Duration> tp) const
{
using date::local_info;
using date::sys_time;
using date::ambiguous_local_time;
using date::nonexistent_local_time;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
throw nonexistent_local_time(tp, i);
else if (i.result == local_info::ambiguous)
throw ambiguous_local_time(tp, i);
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(date::local_time<Duration> tp, date::choose z) const
{
using date::local_info;
using date::sys_time;
using date::choose;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
{
return i.first.end;
}
else if (i.result == local_info::ambiguous)
{
if (z == choose::latest)
return sys_time<Duration>{tp.time_since_epoch()} - i.second.offset;
}
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_local(date::sys_time<Duration> tp) const
{
using date::local_time;
using std::chrono::seconds;
using LT = local_time<typename std::common_type<Duration, seconds>::type>;
auto i = get_info(tp);
return LT{(tp + i.offset).time_since_epoch()};
}
inline
std::ostream&
operator<<(std::ostream& os, const time_zone& z)
{
using date::operator<<;
os << '{';
os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_)
<< date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}";
return os;
}
inline
std::string
time_zone::name() const
{
using namespace date;
using namespace std::chrono;
auto print_abbrev = [](std::string const& nm)
{
if (std::any_of(nm.begin(), nm.end(),
[](char c)
{
return !std::isalpha(c);
}))
{
return '<' + nm + '>';
}
return nm;
};
auto print_offset = [](seconds off)
{
std::string nm;
date::hh_mm_ss<seconds> offset{-off};
if (offset.is_negative())
nm += '-';
nm += std::to_string(offset.hours().count());
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
{
nm += ':';
if (offset.minutes() < minutes{10})
nm += '0';
nm += std::to_string(offset.minutes().count());
if (offset.seconds() != seconds{0})
{
nm += ':';
if (offset.seconds() < seconds{10})
nm += '0';
nm += std::to_string(offset.seconds().count());
}
}
return nm;
};
auto nm = print_abbrev(std_abbrev_);
nm += print_offset(offset_);
if (!dst_abbrev_.empty())
{
nm += print_abbrev(dst_abbrev_);
if (save_ != hours{1})
nm += print_offset(offset_+save_);
if (start_rule_.ok())
{
nm += ',';
nm += start_rule_.to_string();
nm += ',';
nm += end_rule_.to_string();
}
}
return nm;
}
inline
bool
operator==(const time_zone& x, const time_zone& y)
{
return x.std_abbrev_ == y.std_abbrev_ &&
x.dst_abbrev_ == y. dst_abbrev_ &&
x.offset_ == y.offset_ &&
x.save_ == y.save_ &&
x.start_rule_ == y.start_rule_ &&
x.end_rule_ == y.end_rule_;
}
inline
bool
operator!=(const time_zone& x, const time_zone& y)
{
return !(x == y);
}
namespace detail
{
inline
void
throw_invalid(const string_t& s, unsigned i, const string_t& message)
{
throw std::runtime_error(std::string("Invalid time_zone initializer.\n") +
std::string(message) + ":\n" +
std::string(s) + '\n' +
"\x1b[1;32m" +
std::string(i, '~') + '^' +
std::string(i < s.size() ? s.size()-i-1 : 0, '~') +
"\x1b[0m");
}
inline
unsigned
read_date(const string_t& s, unsigned i, rule& r)
{
using date::month;
using date::weekday;
if (i == s.size())
throw_invalid(s, i, "Expected rule but found end of string");
if (s[i] == 'J')
{
++i;
unsigned n;
i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
if (!(1 <= n && n <= 365))
throw_invalid(s, i-1, "Expected Julian day to be in the range [1, 365]");
r.mode_ = rule::J;
r.n_ = n;
}
else if (s[i] == 'M')
{
++i;
unsigned m;
i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
if (!(1 <= m && m <= 12))
throw_invalid(s, i-1, "Expected month to be in the range [1, 12]");
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after month");
++i;
unsigned n;
i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]");
if (!(1 <= n && n <= 5))
throw_invalid(s, i-1, "Expected week number to be in the range [1, 5]");
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after weekday index");
++i;
unsigned wd;
i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]");
if (wd > 6)
throw_invalid(s, i-1, "Expected day of week to be in the range [0, 6]");
r.mode_ = rule::M;
r.m_ = month{m};
r.wd_ = weekday{wd};
r.n_ = n;
}
else if (std::isdigit(s[i]))
{
unsigned n;
i = read_unsigned(s, i, 3, n);
if (n > 365)
throw_invalid(s, i-1, "Expected Julian day to be in the range [0, 365]");
r.mode_ = rule::N;
r.n_ = n;
}
else
throw_invalid(s, i, "Expected 'J', 'M', or a digit to start rule");
if (i != s.size() && s[i] == '/')
{
++i;
std::chrono::seconds t;
i = read_unsigned_time(s, i, t);
r.time_ = t;
}
return i;
}
inline
unsigned
read_name(const string_t& s, unsigned i, std::string& name)
{
if (i == s.size())
throw_invalid(s, i, "Expected a name but found end of string");
if (s[i] == '<')
{
++i;
while (true)
{
if (i == s.size())
throw_invalid(s, i,
"Expected to find closing '>', but found end of string");
if (s[i] == '>')
break;
name.push_back(s[i]);
++i;
}
++i;
}
else
{
while (i != s.size() && std::isalpha(s[i]))
{
name.push_back(s[i]);
++i;
}
}
return i;
}
inline
unsigned
read_signed_time(const string_t& s, unsigned i,
std::chrono::seconds& t)
{
if (i == s.size())
throw_invalid(s, i, "Expected to read signed time, but found end of string");
bool negative = false;
if (s[i] == '-')
{
negative = true;
++i;
}
else if (s[i] == '+')
++i;
i = read_unsigned_time(s, i, t);
if (negative)
t = -t;
return i;
}
inline
unsigned
read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
{
using std::chrono::seconds;
using std::chrono::minutes;
using std::chrono::hours;
if (i == s.size())
throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
unsigned x;
i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
if (x > 24)
throw_invalid(s, i-1, "Expected hours to be in the range [0, 24]");
t = hours{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]");
if (x > 59)
throw_invalid(s, i-1, "Expected minutes to be in the range [0, 59]");
t += minutes{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]");
if (x > 59)
throw_invalid(s, i-1, "Expected seconds to be in the range [0, 59]");
t += seconds{x};
}
}
return i;
}
inline
unsigned
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
const string_t& message)
{
if (i == s.size() || !std::isdigit(s[i]))
throw_invalid(s, i, message);
u = static_cast<unsigned>(s[i] - '0');
unsigned count = 1;
for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count)
u = u * 10 + static_cast<unsigned>(s[i] - '0');
return i;
}
} // namespace detail
} // namespace Posix
namespace date
{
template <>
struct zoned_traits<Posix::time_zone>
{
#if HAS_STRING_VIEW
static
Posix::time_zone
locate_zone(std::string_view name)
{
return Posix::time_zone{name};
}
#else // !HAS_STRING_VIEW
static
Posix::time_zone
locate_zone(const std::string& name)
{
return Posix::time_zone{name};
}
static
Posix::time_zone
locate_zone(const char* name)
{
return Posix::time_zone{name};
}
#endif // !HAS_STRING_VIEW
};
} // namespace date
#endif // PTZ_H

3151
include/date/solar_hijri.h Normal file

File diff suppressed because it is too large Load Diff

2808
include/date/tz.h Normal file

File diff suppressed because it is too large Load Diff

315
include/date/tz_private.h Normal file
View File

@@ -0,0 +1,315 @@
#ifndef TZ_PRIVATE_H
#define TZ_PRIVATE_H
// The MIT License (MIT)
//
// Copyright (c) 2015, 2016 Howard Hinnant
//
// 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.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that would involve another several millennia of evolution).
// We did not mean to shout.
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
#include "tz.h"
#else
#include "date.h"
#include <vector>
#endif
namespace date
{
namespace detail
{
#if !USE_OS_TZDB
enum class tz {utc, local, standard};
//forward declare to avoid warnings in gcc 6.2
class MonthDayTime;
std::istream& operator>>(std::istream& is, MonthDayTime& x);
std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
class MonthDayTime
{
private:
struct pair
{
#if defined(_MSC_VER) && (_MSC_VER < 1900)
pair() : month_day_(date::jan / 1), weekday_(0U) {}
pair(const date::month_day& month_day, const date::weekday& weekday)
: month_day_(month_day), weekday_(weekday) {}
#endif
date::month_day month_day_;
date::weekday weekday_;
};
enum Type {month_day, month_last_dow, lteq, gteq};
Type type_{month_day};
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
union U
#else
struct U
#endif
{
date::month_day month_day_;
date::month_weekday_last month_weekday_last_;
pair month_day_weekday_;
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
U() : month_day_{date::jan/1} {}
#else
U() :
month_day_(date::jan/1),
month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U)))
{}
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
U& operator=(const date::month_day& x);
U& operator=(const date::month_weekday_last& x);
U& operator=(const pair& x);
} u;
std::chrono::hours h_{0};
std::chrono::minutes m_{0};
std::chrono::seconds s_{0};
tz zone_{tz::local};
public:
MonthDayTime() = default;
MonthDayTime(local_seconds tp, tz timezone);
MonthDayTime(const date::month_day& md, tz timezone);
date::day day() const;
date::month month() const;
tz zone() const {return zone_;}
void canonicalize(date::year y);
sys_seconds
to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const;
sys_days to_sys_days(date::year y) const;
sys_seconds to_time_point(date::year y) const;
int compare(date::year y, const MonthDayTime& x, date::year yx,
std::chrono::seconds offset, std::chrono::minutes prev_save) const;
friend std::istream& operator>>(std::istream& is, MonthDayTime& x);
friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
};
// A Rule specifies one or more set of datetimes without using an offset.
// Multiple dates are specified with multiple years. The years in effect
// go from starting_year_ to ending_year_, inclusive. starting_year_ <=
// ending_year_. save_ is in effect for times from the specified time
// onward, including the specified time. When the specified time is
// local, it uses the save_ from the chronologically previous Rule, or if
// there is none, 0.
//forward declare to avoid warnings in gcc 6.2
class Rule;
bool operator==(const Rule& x, const Rule& y);
bool operator<(const Rule& x, const Rule& y);
bool operator==(const Rule& x, const date::year& y);
bool operator<(const Rule& x, const date::year& y);
bool operator==(const date::year& x, const Rule& y);
bool operator<(const date::year& x, const Rule& y);
bool operator==(const Rule& x, const std::string& y);
bool operator<(const Rule& x, const std::string& y);
bool operator==(const std::string& x, const Rule& y);
bool operator<(const std::string& x, const Rule& y);
std::ostream& operator<<(std::ostream& os, const Rule& r);
class Rule
{
private:
std::string name_;
date::year starting_year_{0};
date::year ending_year_{0};
MonthDayTime starting_at_;
std::chrono::minutes save_{0};
std::string abbrev_;
public:
Rule() = default;
explicit Rule(const std::string& s);
Rule(const Rule& r, date::year starting_year, date::year ending_year);
const std::string& name() const {return name_;}
const std::string& abbrev() const {return abbrev_;}
const MonthDayTime& mdt() const {return starting_at_;}
const date::year& starting_year() const {return starting_year_;}
const date::year& ending_year() const {return ending_year_;}
const std::chrono::minutes& save() const {return save_;}
static void split_overlaps(std::vector<Rule>& rules);
friend bool operator==(const Rule& x, const Rule& y);
friend bool operator<(const Rule& x, const Rule& y);
friend bool operator==(const Rule& x, const date::year& y);
friend bool operator<(const Rule& x, const date::year& y);
friend bool operator==(const date::year& x, const Rule& y);
friend bool operator<(const date::year& x, const Rule& y);
friend bool operator==(const Rule& x, const std::string& y);
friend bool operator<(const Rule& x, const std::string& y);
friend bool operator==(const std::string& x, const Rule& y);
friend bool operator<(const std::string& x, const Rule& y);
friend std::ostream& operator<<(std::ostream& os, const Rule& r);
private:
date::day day() const;
date::month month() const;
static void split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e);
static bool overlaps(const Rule& x, const Rule& y);
static void split(std::vector<Rule>& rules, std::size_t i, std::size_t k,
std::size_t& e);
};
inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);}
inline bool operator> (const Rule& x, const Rule& y) {return y < x;}
inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);}
inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);}
inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);}
inline bool operator> (const Rule& x, const date::year& y) {return y < x;}
inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);}
inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);}
inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);}
inline bool operator> (const date::year& x, const Rule& y) {return y < x;}
inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);}
inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);}
inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);}
inline bool operator> (const Rule& x, const std::string& y) {return y < x;}
inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);}
inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);}
inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);}
inline bool operator> (const std::string& x, const Rule& y) {return y < x;}
inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);}
inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);}
struct zonelet
{
enum tag {has_rule, has_save, is_empty};
std::chrono::seconds gmtoff_;
tag tag_ = has_rule;
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
union U
#else
struct U
#endif
{
std::string rule_;
std::chrono::minutes save_;
~U() {}
U() {}
U(const U&) {}
U& operator=(const U&) = delete;
} u;
std::string format_;
date::year until_year_{0};
MonthDayTime until_date_;
sys_seconds until_utc_;
local_seconds until_std_;
local_seconds until_loc_;
std::chrono::minutes initial_save_{0};
std::string initial_abbrev_;
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};
~zonelet();
zonelet();
zonelet(const zonelet& i);
zonelet& operator=(const zonelet&) = delete;
};
#else // USE_OS_TZDB
struct ttinfo
{
std::int32_t tt_gmtoff;
unsigned char tt_isdst;
unsigned char tt_abbrind;
unsigned char pad[2];
};
static_assert(sizeof(ttinfo) == 8, "");
struct expanded_ttinfo
{
std::chrono::seconds offset;
std::string abbrev;
bool is_dst;
};
struct transition
{
sys_seconds timepoint;
const expanded_ttinfo* info;
transition(sys_seconds tp, const expanded_ttinfo* i = nullptr)
: timepoint(tp)
, info(i)
{}
friend
std::ostream&
operator<<(std::ostream& os, const transition& t)
{
date::operator<<(os, t.timepoint) << "Z ";
if (t.info->offset >= std::chrono::seconds{0})
os << '+';
os << make_time(t.info->offset);
if (t.info->is_dst > 0)
os << " daylight ";
else
os << " standard ";
os << t.info->abbrev;
return os;
}
};
#endif // USE_OS_TZDB
} // namespace detail
} // namespace date
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#include "tz.h"
#endif
#endif // TZ_PRIVATE_H

300
include/libcron/Cron.h Normal file
View File

@@ -0,0 +1,300 @@
#pragma once
#include <string>
#include <chrono>
#include <memory>
#include <mutex>
#include <map>
#include <unordered_map>
#include <vector>
#include "Task.h"
#include "CronClock.h"
#include "TaskQueue.h"
namespace libcron
{
class NullLock
{
public:
void lock() {}
void unlock() {}
};
class Locker
{
public:
void lock() { m.lock(); }
void unlock() { m.unlock(); }
private:
std::recursive_mutex m{};
};
template<typename ClockType, typename LockType>
class Cron;
template<typename ClockType, typename LockType>
std::ostream& operator<<(std::ostream& stream, const Cron<ClockType, LockType>& c);
template<typename ClockType = libcron::LocalClock,
typename LockType = libcron::NullLock>
class Cron
{
public:
bool add_schedule(std::string name, const std::string& schedule, Task::TaskFunction work);
template<typename Schedules = std::map<std::string, std::string>>
std::tuple<bool, std::string, std::string>
add_schedule(const Schedules& name_schedule_map, Task::TaskFunction work);
void clear_schedules();
void remove_schedule(const std::string& name);
size_t count() const
{
return tasks.size();
}
// Tick is expected to be called at least once a second to prevent missing schedules.
size_t
tick()
{
return tick(clock.now());
}
size_t
tick(std::chrono::system_clock::time_point now);
std::chrono::system_clock::duration
time_until_next() const;
ClockType& get_clock()
{
return clock;
}
void recalculate_schedule()
{
for (auto& t : tasks.get_tasks())
{
using namespace std::chrono_literals;
// Ensure that next schedule is in the future
t.calculate_next(clock.now() + 1s);
}
}
void get_time_until_expiry_for_tasks(
std::vector<std::tuple<std::string, std::chrono::system_clock::duration>>& status) const;
friend std::ostream& operator<<<>(std::ostream& stream, const Cron<ClockType, LockType>& c);
private:
TaskQueue<LockType> tasks{};
ClockType clock{};
bool first_tick = true;
std::chrono::system_clock::time_point last_tick{};
};
template<typename ClockType, typename LockType>
bool Cron<ClockType, LockType>::add_schedule(std::string name, const std::string& schedule, Task::TaskFunction work)
{
auto cron = CronData::create(schedule);
bool res = cron.is_valid();
if (res)
{
tasks.lock_queue();
Task t{std::move(name), CronSchedule{cron}, work };
if (t.calculate_next(clock.now()))
{
tasks.push(t);
tasks.sort();
}
tasks.release_queue();
}
return res;
}
template<typename ClockType, typename LockType>
template<typename Schedules>
std::tuple<bool, std::string, std::string>
Cron<ClockType, LockType>::add_schedule(const Schedules& name_schedule_map, Task::TaskFunction work)
{
bool is_valid = true;
std::tuple<bool, std::string, std::string> res{false, "", ""};
std::vector<Task> tasks_to_add;
tasks_to_add.reserve(name_schedule_map.size());
for (auto it = name_schedule_map.begin(); is_valid && it != name_schedule_map.end(); ++it)
{
const auto& [name, schedule] = *it;
auto cron = CronData::create(schedule);
is_valid = cron.is_valid();
if (is_valid)
{
Task t{std::move(name), CronSchedule{cron}, work };
if (t.calculate_next(clock.now()))
{
tasks_to_add.push_back(std::move(t));
}
}
else
{
std::get<1>(res) = name;
std::get<2>(res) = schedule;
}
}
// Only add tasks and sort once if all elements in the map where valid
if (is_valid && tasks_to_add.size() > 0)
{
tasks.lock_queue();
tasks.push(tasks_to_add);
tasks.sort();
tasks.release_queue();
}
std::get<0>(res) = is_valid;
return res;
}
template<typename ClockType, typename LockType>
void Cron<ClockType, LockType>::clear_schedules()
{
tasks.clear();
}
template<typename ClockType, typename LockType>
void Cron<ClockType, LockType>::remove_schedule(const std::string& name)
{
tasks.remove(name);
}
template<typename ClockType, typename LockType>
std::chrono::system_clock::duration Cron<ClockType, LockType>::time_until_next() const
{
std::chrono::system_clock::duration d{};
if (tasks.empty())
{
d = std::numeric_limits<std::chrono::minutes>::max();
}
else
{
d = tasks.top().time_until_expiry(clock.now());
}
return d;
}
template<typename ClockType, typename LockType>
size_t Cron<ClockType, LockType>::tick(std::chrono::system_clock::time_point now)
{
tasks.lock_queue();
size_t res = 0;
if(!first_tick)
{
// Only allow time to flow if at least one second has passed since the last tick,
// either forward or backward.
auto diff = now - last_tick;
constexpr auto one_second = std::chrono::seconds{1};
if(diff < one_second && diff > -one_second)
{
now = last_tick;
}
}
if (first_tick)
{
first_tick = false;
}
else
{
// https://linux.die.net/man/8/cron
constexpr auto three_hours = std::chrono::hours{3};
auto diff = now - last_tick;
auto absolute_diff = diff > diff.zero() ? diff : -diff;
if(absolute_diff >= three_hours)
{
// Time changes of more than 3 hours are considered to be corrections to the
// clock or timezone, and the new time is used immediately.
for (auto& t : tasks.get_tasks())
{
t.calculate_next(now);
}
}
else
{
// Change of less than three hours
// If time has moved backwards: Since tasks are not rescheduled, they won't run before
// we're back at least the original point in time which prevents running tasks twice.
// If time has moved forward, tasks that would have run since last tick will be run.
}
}
last_tick = now;
if (!tasks.empty())
{
for (size_t i = 0; i < tasks.size(); i++)
{
if (tasks.at(i).is_expired(now))
{
auto& t = tasks.at(i);
t.execute(now);
using namespace std::chrono_literals;
if (!t.calculate_next(now + 1s))
{
tasks.remove(t);
}
res++;
}
}
// Only sort if at least one task was executed
if (res > 0)
{
tasks.sort();
}
}
tasks.release_queue();
return res;
}
template<typename ClockType, typename LockType>
void Cron<ClockType, LockType>::get_time_until_expiry_for_tasks(std::vector<std::tuple<std::string,
std::chrono::system_clock::duration>>& status) const
{
auto now = clock.now();
status.clear();
std::for_each(tasks.get_tasks().cbegin(), tasks.get_tasks().cend(),
[&status, &now](const Task& t)
{
status.emplace_back(t.get_name(), t.time_until_expiry(now));
});
}
template<typename ClockType, typename LockType>
std::ostream& operator<<(std::ostream& stream, const Cron<ClockType, LockType>& c)
{
std::for_each(c.tasks.get_tasks().cbegin(), c.tasks.get_tasks().cend(),
[&stream, &c](const Task& t)
{
stream << t.get_status(c.clock.now()) << '\n';
});
return stream;
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <chrono>
namespace libcron
{
class ICronClock
{
public:
virtual std::chrono::system_clock::time_point now() const = 0;
virtual std::chrono::seconds utc_offset(std::chrono::system_clock::time_point now) const = 0;
};
class UTCClock
: public ICronClock
{
public:
std::chrono::system_clock::time_point now() const override
{
return std::chrono::system_clock::now();
}
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point) const override
{
using namespace std::chrono;
return 0s;
}
};
class LocalClock
: public ICronClock
{
public:
std::chrono::system_clock::time_point now() const override
{
auto now = std::chrono::system_clock::now();
return now + utc_offset(now);
}
std::chrono::seconds utc_offset(std::chrono::system_clock::time_point now) const override;
};
}

389
include/libcron/CronData.h Normal file
View File

@@ -0,0 +1,389 @@
#pragma once
#include <set>
#include <regex>
#include <string>
#include <vector>
#include <unordered_map>
#include <libcron/TimeTypes.h>
namespace libcron
{
class CronData
{
public:
static const int NUMBER_OF_LONG_MONTHS = 7;
static const libcron::Months months_with_31[NUMBER_OF_LONG_MONTHS];
static CronData create(const std::string& cron_expression);
CronData() = default;
CronData(const CronData&) = default;
CronData& operator=(const CronData&) = default;
bool is_valid() const
{
return valid;
}
const std::set<Seconds>& get_seconds() const
{
return seconds;
}
const std::set<Minutes>& get_minutes() const
{
return minutes;
}
const std::set<Hours>& get_hours() const
{
return hours;
}
const std::set<DayOfMonth>& get_day_of_month() const
{
return day_of_month;
}
const std::set<Months>& get_months() const
{
return months;
}
const std::set<DayOfWeek>& get_day_of_week() const
{
return day_of_week;
}
template<typename T>
static uint8_t value_of(T t)
{
return static_cast<uint8_t>(t);
}
template<typename T>
static bool has_any_in_range(const std::set<T>& set, uint8_t low, uint8_t high)
{
bool found = false;
for (auto i = low; !found && i <= high; ++i)
{
found |= set.find(static_cast<T>(i)) != set.end();
}
return found;
}
template<typename T>
bool convert_from_string_range_to_number_range(const std::string& range, std::set<T>& numbers);
template<typename T>
static std::string& replace_string_name_with_numeric(std::string& s);
private:
void parse(const std::string& cron_expression);
template<typename T>
bool validate_numeric(const std::string& s, std::set<T>& numbers);
template<typename T>
bool validate_literal(const std::string& s,
std::set<T>& numbers,
const std::vector<std::string>& names);
template<typename T>
bool process_parts(const std::vector<std::string>& parts, std::set<T>& numbers);
template<typename T>
bool add_number(std::set<T>& set, int32_t number);
template<typename T>
bool is_within_limits(int32_t low, int32_t high);
template<typename T>
bool get_range(const std::string& s, T& low, T& high);
template<typename T>
bool get_step(const std::string& s, uint8_t& start, uint8_t& step);
std::vector<std::string> split(const std::string& s, char token);
bool is_number(const std::string& s);
bool is_between(int32_t value, int32_t low_limit, int32_t high_limit);
bool validate_date_vs_months() const;
bool check_dom_vs_dow(const std::string& dom, const std::string& dow) const;
std::set<Seconds> seconds{};
std::set<Minutes> minutes{};
std::set<Hours> hours{};
std::set<DayOfMonth> day_of_month{};
std::set<Months> months{};
std::set<DayOfWeek> day_of_week{};
bool valid = false;
static const std::vector<std::string> month_names;
static const std::vector<std::string> day_names;
static std::unordered_map<std::string, CronData> cache;
template<typename T>
void add_full_range(std::set<T>& set);
};
template<typename T>
bool CronData::validate_numeric(const std::string& s, std::set<T>& numbers)
{
std::vector<std::string> parts = split(s, ',');
return process_parts(parts, numbers);
}
template<typename T>
bool CronData::validate_literal(const std::string& s,
std::set<T>& numbers,
const std::vector<std::string>& names)
{
std::vector<std::string> parts = split(s, ',');
auto value_of_first_name = value_of(T::First);
// Replace each found name with the corresponding value.
for (const auto& name : names)
{
std::regex m(name, std::regex_constants::ECMAScript | std::regex_constants::icase);
for (auto& part : parts)
{
std::string replaced;
std::regex_replace(std::back_inserter(replaced), part.begin(), part.end(), m,
std::to_string(value_of_first_name));
part = replaced;
}
value_of_first_name++;
}
return process_parts(parts, numbers);
}
template<typename T>
bool CronData::process_parts(const std::vector<std::string>& parts, std::set<T>& numbers)
{
bool res = true;
for (const auto& p : parts)
{
res &= convert_from_string_range_to_number_range(p, numbers);
}
return res;
}
template<typename T>
bool CronData::get_range(const std::string& s, T& low, T& high)
{
bool res = false;
auto value_range = R"#((\d+)-(\d+))#";
std::regex range(value_range, std::regex_constants::ECMAScript);
std::smatch match;
if (std::regex_match(s.begin(), s.end(), match, range))
{
auto left = std::stoi(match[1].str());
auto right = std::stoi(match[2].str());
if (is_within_limits<T>(left, right))
{
low = static_cast<T>(left);
high = static_cast<T>(right);
res = true;
}
}
return res;
}
template<typename T>
bool CronData::get_step(const std::string& s, uint8_t& start, uint8_t& step)
{
bool res = false;
auto value_range = R"#((\d+|\*)/(\d+))#";
std::regex range(value_range, std::regex_constants::ECMAScript);
std::smatch match;
if (std::regex_match(s.begin(), s.end(), match, range))
{
int raw_start;
if (match[1].str() == "*")
{
raw_start = value_of(T::First);
}
else
{
raw_start = std::stoi(match[1].str());
}
auto raw_step = std::stoi(match[2].str());
if (is_within_limits<T>(raw_start, raw_start) && raw_step > 0)
{
start = static_cast<uint8_t>(raw_start);
step = static_cast<uint8_t>(raw_step);
res = true;
}
}
return res;
}
template<typename T>
void CronData::add_full_range(std::set<T>& set)
{
for (auto v = value_of(T::First); v <= value_of(T::Last); ++v)
{
if (set.find(static_cast<T>(v)) == set.end())
{
set.emplace(static_cast<T>(v));
}
}
}
template<typename T>
bool CronData::add_number(std::set<T>& set, int32_t number)
{
bool res = true;
// Don't add if already there
if (set.find(static_cast<T>(number)) == set.end())
{
// Check range
if (is_within_limits<T>(number, number))
{
set.emplace(static_cast<T>(number));
}
else
{
res = false;
}
}
return res;
}
template<typename T>
bool CronData::is_within_limits(int32_t low, int32_t high)
{
return is_between(low, value_of(T::First), value_of(T::Last))
&& is_between(high, value_of(T::First), value_of(T::Last));
}
template<typename T>
bool CronData::convert_from_string_range_to_number_range(const std::string& range, std::set<T>& numbers)
{
T left;
T right;
uint8_t step_start;
uint8_t step;
bool res = true;
if (range == "*" || range == "?")
{
// We treat the ignore-character '?' the same as the full range being allowed.
add_full_range<T>(numbers);
}
else if (is_number(range))
{
res = add_number<T>(numbers, std::stoi(range));
}
else if (get_range<T>(range, left, right))
{
// A range can be written as both 1-22 or 22-1, meaning totally different ranges.
// First case is 1...22 while 22-1 is only four hours: 22, 23, 0, 1.
if (left <= right)
{
for (auto v = value_of(left); v <= value_of(right); ++v)
{
res &= add_number(numbers, v);
}
}
else
{
// 'left' and 'right' are not in value order. First, get values between 'left' and T::Last, inclusive
for (auto v = value_of(left); v <= value_of(T::Last); ++v)
{
res = add_number(numbers, v);
}
// Next, get values between T::First and 'right', inclusive.
for (auto v = value_of(T::First); v <= value_of(right); ++v)
{
res = add_number(numbers, v);
}
}
}
else if (get_step<T>(range, step_start, step))
{
// Add from step_start to T::Last with a step of 'step'
for (auto v = step_start; v <= value_of(T::Last); v += step)
{
res = add_number(numbers, v);
}
}
else
{
res = false;
}
return res;
}
template<typename T>
std::string & CronData::replace_string_name_with_numeric(std::string& s)
{
auto value = static_cast<int>(T::First);
const std::vector<std::string>* name_source{};
static_assert(std::is_same<T, libcron::Months>()
|| std::is_same<T, libcron::DayOfWeek>(),
"T must be either Months or DayOfWeek");
if constexpr (std::is_same<T, libcron::Months>())
{
name_source = &month_names;
}
else
{
name_source = &day_names;
}
for (const auto& name : *name_source)
{
std::regex m(name, std::regex_constants::ECMAScript | std::regex_constants::icase);
std::string replaced;
std::regex_replace(std::back_inserter(replaced), s.begin(), s.end(), m, std::to_string(value));
s = replaced;
++value;
}
return s;
}
}

View File

@@ -0,0 +1,100 @@
#pragma once
#include <tuple>
#include <random>
#include <regex>
#include <functional>
#include "CronData.h"
namespace libcron
{
class CronRandomization
{
public:
std::tuple<bool, std::string> parse(const std::string& cron_schedule);
CronRandomization();
CronRandomization(const CronRandomization&) = delete;
CronRandomization & operator=(const CronRandomization &) = delete;
private:
template<typename T>
std::pair<bool, std::string> get_random_in_range(const std::string& section,
int& selected_value,
std::pair<int, int> limit = std::make_pair(-1, -1));
std::pair<int, int> day_limiter(const std::set<Months>& month);
int cap(int value, int lower, int upper);
std::regex const rand_expression{ R"#([rR]\((\d+)\-(\d+)\))#", std::regex_constants::ECMAScript };
std::random_device rd{};
std::mt19937 twister;
};
template<typename T>
std::pair<bool, std::string> CronRandomization::get_random_in_range(const std::string& section,
int& selected_value,
std::pair<int, int> limit)
{
auto res = std::make_pair(true, std::string{});
selected_value = -1;
std::smatch random_match;
if (std::regex_match(section.cbegin(), section.cend(), random_match, rand_expression))
{
// Random range, get left and right numbers.
auto left = std::stoi(random_match[1].str());
auto right = std::stoi(random_match[2].str());
if (limit.first != -1 && limit.second != -1)
{
left = cap(left, limit.first, limit.second);
right = cap(right, limit.first, limit.second);
}
libcron::CronData cd;
std::set<T> numbers;
res.first = cd.convert_from_string_range_to_number_range<T>(
std::to_string(left) + "-" + std::to_string(right), numbers);
// Remove items outside limits.
if (limit.first != -1 && limit.second != -1)
{
for (auto it = numbers.begin(); it != numbers.end(); )
{
if (CronData::value_of(*it) < limit.first || CronData::value_of(*it) > limit.second)
{
it = numbers.erase(it);
}
else
{
++it;
}
}
}
if (res.first)
{
// Generate random indexes to select one of the numbers in the range.
std::uniform_int_distribution<> dis(0, static_cast<int>(numbers.size() - 1));
// Select the random number to use as the schedule
auto it = numbers.begin();
std::advance(it, dis(twister));
selected_value = CronData::value_of(*it);
res.second = std::to_string(selected_value);
}
}
else
{
// Not random, just append input to output.
res.second = section;
}
return res;
}
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include "libcron/CronData.h"
#include <chrono>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4244)
#endif
#include <date/date.h>
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#include "libcron/DateTime.h"
namespace libcron
{
class CronSchedule
{
public:
explicit CronSchedule(CronData& data)
: data(data)
{
}
CronSchedule(const CronSchedule&) = default;
CronSchedule& operator=(const CronSchedule&) = default;
std::tuple<bool, std::chrono::system_clock::time_point>
calculate_from(const std::chrono::system_clock::time_point& from) const;
// https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#obtaining-ymd-hms-components-from-a-time_point
static DateTime to_calendar_time(std::chrono::system_clock::time_point time)
{
auto daypoint = date::floor<date::days>(time);
auto ymd = date::year_month_day(daypoint); // calendar date
auto time_of_day = date::make_time(time - daypoint); // Yields time_of_day type
// Obtain individual components as integers
DateTime dt{
int(ymd.year()),
unsigned(ymd.month()),
unsigned(ymd.day()),
static_cast<uint8_t>(time_of_day.hours().count()),
static_cast<uint8_t>(time_of_day.minutes().count()),
static_cast<uint8_t>(time_of_day.seconds().count())};
return dt;
}
private:
CronData data;
};
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
namespace libcron
{
struct DateTime
{
int year = 0;
unsigned month = 0;
unsigned day = 0;
uint8_t hour = 0;
uint8_t min = 0;
uint8_t sec = 0;
};
}

100
include/libcron/Task.h Normal file
View File

@@ -0,0 +1,100 @@
#pragma once
#include <functional>
#include <chrono>
#include <utility>
#include "CronData.h"
#include "CronSchedule.h"
namespace libcron
{
class TaskInformation
{
public:
virtual ~TaskInformation() = default;
virtual std::chrono::system_clock::duration get_delay() const = 0;
virtual std::string get_name() const = 0;
};
class Task : public TaskInformation
{
public:
using TaskFunction = std::function<void(const TaskInformation&)>;
Task(std::string name, const CronSchedule schedule, TaskFunction task)
: name(std::move(name)), schedule(std::move(schedule)), task(std::move(task))
{
}
void execute(std::chrono::system_clock::time_point now)
{
// Next Schedule is still the current schedule, calculate delay (actual execution - planned execution)
delay = now - next_schedule;
last_run = now;
task(*this);
}
std::chrono::system_clock::duration get_delay() const override
{
return delay;
}
Task(const Task& other) = default;
Task& operator=(const Task&) = default;
bool calculate_next(std::chrono::system_clock::time_point from);
bool operator>(const Task& other) const
{
return next_schedule > other.next_schedule;
}
bool operator<(const Task& other) const
{
return next_schedule < other.next_schedule;
}
bool is_expired(std::chrono::system_clock::time_point now) const;
std::chrono::system_clock::duration
time_until_expiry(std::chrono::system_clock::time_point now) const;
std::string get_name() const override
{
return name;
}
std::string get_status(std::chrono::system_clock::time_point now) const;
private:
std::string name;
CronSchedule schedule;
std::chrono::system_clock::time_point next_schedule;
std::chrono::system_clock::duration delay = std::chrono::seconds(-1);
TaskFunction task;
bool valid = false;
std::chrono::system_clock::time_point last_run = std::numeric_limits<std::chrono::system_clock::time_point>::min();
};
}
inline bool operator==(const std::string &lhs, const libcron::Task &rhs)
{
return lhs == rhs.get_name();
}
inline bool operator==(const libcron::Task &lhs, const std::string &rhs)
{
return lhs.get_name() == rhs;
}
inline bool operator!=(const std::string &lhs, const libcron::Task &rhs)
{
return !(lhs == rhs);
}
inline bool operator!=(const libcron::Task &lhs, const std::string &rhs)
{
return !(lhs == rhs);
}

115
include/libcron/TaskQueue.h Normal file
View File

@@ -0,0 +1,115 @@
#pragma once
#include <vector>
#include <map>
#include <unordered_map>
#include <vector>
#include "Task.h"
namespace libcron
{
template<typename LockType>
class TaskQueue
{
public:
const std::vector<Task>& get_tasks() const
{
return c;
}
std::vector<Task>& get_tasks()
{
return c;
}
size_t size() const noexcept
{
return c.size();
}
bool empty() const noexcept
{
return c.empty();
}
void push(Task& t)
{
c.push_back(std::move(t));
}
void push(Task&& t)
{
c.push_back(std::move(t));
}
void push(std::vector<Task>& tasks_to_insert)
{
c.reserve(c.size() + tasks_to_insert.size());
c.insert(c.end(), std::make_move_iterator(tasks_to_insert.begin()), std::make_move_iterator(tasks_to_insert.end()));
}
const Task& top() const
{
return c[0];
}
Task& at(const size_t i)
{
return c[i];
}
void sort()
{
std::sort(c.begin(), c.end(), std::less<>());
}
void clear()
{
lock.lock();
c.clear();
lock.unlock();
}
void remove(Task& to_remove)
{
auto it = std::find_if(c.begin(), c.end(), [&to_remove] (const Task& to_compare) {
return to_remove.get_name() == to_compare;
});
if (it != c.end())
{
c.erase(it);
}
}
void remove(std::string to_remove)
{
lock.lock();
auto it = std::find_if(c.begin(), c.end(), [&to_remove] (const Task& to_compare) {
return to_remove == to_compare;
});
if (it != c.end())
{
c.erase(it);
}
lock.unlock();
}
void lock_queue()
{
/* Do not allow to manipulate the Queue */
lock.lock();
}
void release_queue()
{
/* Allow Access to the Queue Manipulating-Functions */
lock.unlock();
}
private:
LockType lock;
std::vector<Task> c;
};
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <cstdint>
namespace libcron
{
enum class Seconds : int8_t
{
First = 0,
Last = 59
};
enum class Minutes : int8_t
{
First = 0,
Last = 59
};
enum class Hours : int8_t
{
First = 0,
Last = 23
};
enum class DayOfMonth : uint8_t
{
First = 1,
Last = 31
};
enum class Months : uint8_t
{
First = 1,
January = First,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December = 12,
Last = December
};
enum class DayOfWeek : uint8_t
{
// Sunday = 0 ... Saturday = 6
First = 0,
Last = 6,
};
}

62
include/toml.hpp Normal file
View File

@@ -0,0 +1,62 @@
#ifndef TOML11_TOML_HPP
#define TOML11_TOML_HPP
// The MIT License (MIT)
//
// Copyright (c) 2017-now 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.
// IWYU pragma: begin_exports
#include "toml11/color.hpp"
#include "toml11/comments.hpp"
#include "toml11/compat.hpp"
#include "toml11/context.hpp"
#include "toml11/conversion.hpp"
#include "toml11/datetime.hpp"
#include "toml11/error_info.hpp"
#include "toml11/exception.hpp"
#include "toml11/find.hpp"
#include "toml11/format.hpp"
#include "toml11/from.hpp"
#include "toml11/get.hpp"
#include "toml11/into.hpp"
#include "toml11/literal.hpp"
#include "toml11/location.hpp"
#include "toml11/ordered_map.hpp"
#include "toml11/parser.hpp"
#include "toml11/region.hpp"
#include "toml11/result.hpp"
#include "toml11/scanner.hpp"
#include "toml11/serializer.hpp"
#include "toml11/skip.hpp"
#include "toml11/source_location.hpp"
#include "toml11/spec.hpp"
#include "toml11/storage.hpp"
#include "toml11/syntax.hpp"
#include "toml11/traits.hpp"
#include "toml11/types.hpp"
#include "toml11/utility.hpp"
#include "toml11/value.hpp"
#include "toml11/value_t.hpp"
#include "toml11/version.hpp"
#include "toml11/visit.hpp"
// IWYU pragma: end_exports
#endif// TOML11_TOML_HPP

10
include/toml11/color.hpp Normal file
View File

@@ -0,0 +1,10 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include "fwd/color_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/color_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_COLOR_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_COMMENTS_HPP
#define TOML11_COMMENTS_HPP
#include "fwd/comments_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/comments_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_COMMENTS_HPP

858
include/toml11/compat.hpp Normal file
View File

@@ -0,0 +1,858 @@
#ifndef TOML11_COMPAT_HPP
#define TOML11_COMPAT_HPP
#include "version.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <cassert>
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if __has_include(<bit>)
# include <bit>
# endif
#endif
#include <cstring>
// ----------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if __has_cpp_attribute(deprecated)
# define TOML11_HAS_ATTR_DEPRECATED 1
# endif
#endif
#if defined(TOML11_HAS_ATTR_DEPRECATED)
# define TOML11_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_DEPRECATED(msg) __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
# define TOML11_DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define TOML11_DEPRECATED(msg)
#endif
// ----------------------------------------------------------------------------
#if defined(__cpp_if_constexpr)
# if __cpp_if_constexpr >= 201606L
# define TOML11_HAS_CONSTEXPR_IF 1
# endif
#endif
#if defined(TOML11_HAS_CONSTEXPR_IF)
# define TOML11_CONSTEXPR_IF if constexpr
#else
# define TOML11_CONSTEXPR_IF if
#endif
// ----------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_make_unique)
# if __cpp_lib_make_unique >= 201304L
# define TOML11_HAS_STD_MAKE_UNIQUE 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_MAKE_UNIQUE)
using std::make_unique;
#else
template<typename T, typename ... Ts>
std::unique_ptr<T> make_unique(Ts&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // TOML11_HAS_STD_MAKE_UNIQUE
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_make_reverse_iterator)
# if __cpp_lib_make_reverse_iterator >= 201402L
# define TOML11_HAS_STD_MAKE_REVERSE_ITERATOR 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
# if defined(TOML11_HAS_STD_MAKE_REVERSE_ITERATOR)
using std::make_reverse_iterator;
#else
template<typename Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator iter)
{
return std::reverse_iterator<Iterator>(iter);
}
#endif // TOML11_HAS_STD_MAKE_REVERSE_ITERATOR
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if defined(__cpp_lib_clamp)
# if __cpp_lib_clamp >= 201603L
# define TOML11_HAS_STD_CLAMP 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_CLAMP)
using std::clamp;
#else
template<typename T>
T clamp(const T& x, const T& low, const T& high) noexcept
{
assert(low <= high);
return (std::min)((std::max)(x, low), high);
}
#endif // TOML11_HAS_STD_CLAMP
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if defined(__cpp_lib_bit_cast)
# if __cpp_lib_bit_cast >= 201806L
# define TOML11_HAS_STD_BIT_CAST 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_BIT_CAST)
using std::bit_cast;
#else
template<typename U, typename T>
U bit_cast(const T& x) noexcept
{
static_assert(sizeof(T) == sizeof(U), "");
static_assert(std::is_default_constructible<T>::value, "");
U z;
std::memcpy(reinterpret_cast<char*>(std::addressof(z)),
reinterpret_cast<const char*>(std::addressof(x)),
sizeof(T));
return z;
}
#endif // TOML11_HAS_STD_BIT_CAST
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
// C++20 remove_cvref_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX20_VALUE
# if defined(__cpp_lib_remove_cvref)
# if __cpp_lib_remove_cvref >= 201711L
# define TOML11_HAS_STD_REMOVE_CVREF 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_REMOVE_CVREF)
using std::remove_cvref;
using std::remove_cvref_t;
#else
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;
#endif // TOML11_HAS_STD_REMOVE_CVREF
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_logical_traits)
# if __cpp_lib_logical_traits >= 201510L
# define TOML11_HAS_STD_CONJUNCTION 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_CONJUNCTION)
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 // TOML11_HAS_STD_CONJUNCTION
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_integer_sequence)
# if __cpp_lib_integer_sequence >= 201304L
# define TOML11_HAS_STD_INTEGER_SEQUENCE 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_INTEGER_SEQUENCE)
using std::index_sequence;
using std::make_index_sequence;
#else
template<std::size_t ... Ns> struct index_sequence{};
template<bool B, std::size_t N, typename T>
struct double_index_sequence;
template<std::size_t N, std::size_t ... Is>
struct double_index_sequence<true, N, index_sequence<Is...>>
{
using type = index_sequence<Is..., (Is+N)..., N*2>;
};
template<std::size_t N, std::size_t ... Is>
struct double_index_sequence<false, N, index_sequence<Is...>>
{
using type = index_sequence<Is..., (Is+N)...>;
};
template<std::size_t N>
struct index_sequence_maker
{
using type = typename double_index_sequence<
N % 2 == 1, N/2, typename index_sequence_maker<N/2>::type
>::type;
};
template<>
struct index_sequence_maker<0>
{
using type = index_sequence<>;
};
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N>::type;
#endif // TOML11_HAS_STD_INTEGER_SEQUENCE
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if defined(__cpp_lib_transformation_trait_aliases)
# if __cpp_lib_transformation_trait_aliases >= 201304L
# define TOML11_HAS_STD_ENABLE_IF_T 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_ENABLE_IF_T)
using std::enable_if_t;
#else
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // TOML11_HAS_STD_ENABLE_IF_T
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
// return_type_of_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_is_invocable)
# if __cpp_lib_is_invocable >= 201703
# define TOML11_HAS_STD_INVOKE_RESULT 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_INVOKE_RESULT)
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 // TOML11_HAS_STD_INVOKE_RESULT
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ---------------------------------------------------------------------------
// C++17 void_t
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_void_t)
# if __cpp_lib_void_t >= 201411L
# define TOML11_HAS_STD_VOID_T 1
# endif
# endif
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
#if defined(TOML11_HAS_STD_VOID_T)
using std::void_t;
#else
template<typename ...>
using void_t = void;
#endif // TOML11_HAS_STD_VOID_T
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
// ----------------------------------------------------------------------------
// (subset of) source_location
#if ! defined(TOML11_DISABLE_SOURCE_LOCATION) && TOML11_CPLUSPLUS_STANDARD_VERSION >= 202002L
# if __has_include(<source_location>)
# define TOML11_HAS_STD_SOURCE_LOCATION
# endif // has_include
#endif // c++20
#if ! defined(TOML11_DISABLE_SOURCE_LOCATION) && ! defined(TOML11_HAS_STD_SOURCE_LOCATION)
# if defined(__GNUC__) && ! defined(__clang__)
# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX14_VALUE
# if __has_include(<experimental/source_location>)
# define TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION
# endif
# endif
# endif // GNU g++
#endif // not TOML11_HAS_STD_SOURCE_LOCATION
#if ! defined(TOML11_DISABLE_SOURCE_LOCATION) && ! defined(TOML11_HAS_STD_SOURCE_LOCATION) && ! defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION)
# if defined(__GNUC__) && ! defined(__clang__)
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
# define TOML11_HAS_BUILTIN_FILE_LINE 1
# define TOML11_BUILTIN_LINE_TYPE int
# endif
# elif defined(__clang__) // clang 9.0.0 implements builtin_FILE/LINE
# if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE)
# define TOML11_HAS_BUILTIN_FILE_LINE 1
# define TOML11_BUILTIN_LINE_TYPE unsigned int
# endif
# elif defined(_MSVC_LANG) && defined(_MSC_VER)
# if _MSC_VER > 1926
# define TOML11_HAS_BUILTIN_FILE_LINE 1
# define TOML11_BUILTIN_LINE_TYPE int
# endif
# endif
#endif
#if defined(TOML11_HAS_STD_SOURCE_LOCATION)
#include <source_location>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
using source_location = std::source_location;
inline std::string to_string(const source_location& loc)
{
const char* fname = loc.file_name();
if(fname)
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in file ") + std::string(fname);
}
else
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in unknown file");
}
}
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#elif defined(TOML11_HAS_EXPERIMENTAL_SOURCE_LOCATION)
#include <experimental/source_location>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
using source_location = std::experimental::source_location;
inline std::string to_string(const source_location& loc)
{
const char* fname = loc.file_name();
if(fname)
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in file ") + std::string(fname);
}
else
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in unknown file");
}
}
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#elif defined(TOML11_HAS_BUILTIN_FILE_LINE)
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
struct source_location
{
using line_type = TOML11_BUILTIN_LINE_TYPE;
static source_location current(const line_type line = __builtin_LINE(),
const char* file = __builtin_FILE())
{
return source_location(line, file);
}
source_location(const line_type line, const char* file)
: line_(line), file_name_(file)
{}
line_type line() const noexcept {return line_;}
const char* file_name() const noexcept {return file_name_;}
private:
line_type line_;
const char* file_name_;
};
inline std::string to_string(const source_location& loc)
{
const char* fname = loc.file_name();
if(fname)
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in file ") + std::string(fname);
}
else
{
return std::string(" at line ") + std::to_string(loc.line()) +
std::string(" in unknown file");
}
}
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#else // no builtin
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
struct source_location
{
static source_location current() { return source_location{}; }
};
inline std::string to_string(const source_location&)
{
return std::string("");
}
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_HAS_STD_SOURCE_LOCATION
// ----------------------------------------------------------------------------
// (subset of) optional
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if __has_include(<optional>)
# include <optional>
# endif // has_include(optional)
#endif // C++17
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if defined(__cpp_lib_optional)
# if __cpp_lib_optional >= 201606L
# define TOML11_HAS_STD_OPTIONAL 1
# endif
# endif
#endif
#if defined(TOML11_HAS_STD_OPTIONAL)
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
using std::optional;
inline std::nullopt_t make_nullopt() {return std::nullopt;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const std::nullopt_t&)
{
os << "nullopt";
return os;
}
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#else // TOML11_HAS_STD_OPTIONAL
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace cxx
{
struct nullopt_t{};
inline nullopt_t make_nullopt() {return nullopt_t{};}
inline bool operator==(const nullopt_t&, const nullopt_t&) noexcept {return true;}
inline bool operator!=(const nullopt_t&, const nullopt_t&) noexcept {return false;}
inline bool operator< (const nullopt_t&, const nullopt_t&) noexcept {return false;}
inline bool operator<=(const nullopt_t&, const nullopt_t&) noexcept {return true;}
inline bool operator> (const nullopt_t&, const nullopt_t&) noexcept {return false;}
inline bool operator>=(const nullopt_t&, const nullopt_t&) noexcept {return true;}
template<typename charT, typename traitsT>
std::basic_ostream<charT, traitsT>&
operator<<(std::basic_ostream<charT, traitsT>& os, const nullopt_t&)
{
os << "nullopt";
return os;
}
template<typename T>
class optional
{
public:
using value_type = T;
public:
optional() noexcept : has_value_(false), null_('\0') {}
optional(nullopt_t) noexcept : has_value_(false), null_('\0') {}
optional(const T& x): has_value_(true), value_(x) {}
optional(T&& x): has_value_(true), value_(std::move(x)) {}
template<typename U, enable_if_t<std::is_constructible<T, U>::value, std::nullptr_t> = nullptr>
explicit optional(U&& x): has_value_(true), value_(std::forward<U>(x)) {}
optional(const optional& rhs): has_value_(rhs.has_value_)
{
if(rhs.has_value_)
{
this->assigner(rhs.value_);
}
}
optional(optional&& rhs): has_value_(rhs.has_value_)
{
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
}
optional& operator=(const optional& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(rhs.value_);
}
return *this;
}
optional& operator=(optional&& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
return *this;
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
explicit optional(const optional<U>& rhs): has_value_(rhs.has_value_), null_('\0')
{
if(rhs.has_value_)
{
this->assigner(rhs.value_);
}
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
explicit optional(optional<U>&& rhs): has_value_(rhs.has_value_), null_('\0')
{
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
optional& operator=(const optional<U>& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(rhs.value_);
}
return *this;
}
template<typename U, enable_if_t<conjunction<
negation<std::is_same<T, U>>, std::is_constructible<T, U>
>::value, std::nullptr_t> = nullptr>
optional& operator=(optional<U>&& rhs)
{
if(this == std::addressof(rhs)) {return *this;}
this->cleanup();
this->has_value_ = rhs.has_value_;
if(this->has_value_)
{
this->assigner(std::move(rhs.value_));
}
return *this;
}
~optional() noexcept
{
this->cleanup();
}
explicit operator bool() const noexcept
{
return has_value_;
}
bool has_value() const noexcept {return has_value_;}
value_type const& value(source_location loc = source_location::current()) const
{
if( ! this->has_value_)
{
throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc));
}
return this->value_;
}
value_type& value(source_location loc = source_location::current())
{
if( ! this->has_value_)
{
throw std::runtime_error("optional::value(): bad_unwrap" + to_string(loc));
}
return this->value_;
}
value_type const& value_or(const value_type& opt) const
{
if(this->has_value_) {return this->value_;} else {return opt;}
}
value_type& value_or(value_type& opt)
{
if(this->has_value_) {return this->value_;} else {return opt;}
}
private:
void cleanup() noexcept
{
if(this->has_value_)
{
value_.~T();
}
}
template<typename U>
void assigner(U&& x)
{
const auto tmp = ::new(std::addressof(this->value_)) value_type(std::forward<U>(x));
assert(tmp == std::addressof(this->value_));
(void)tmp;
}
private:
bool has_value_;
union
{
char null_;
T value_;
};
};
} // cxx
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_HAS_STD_OPTIONAL
#endif // TOML11_COMPAT_HPP

View File

@@ -0,0 +1,74 @@
#ifndef TOML11_CONTEXT_HPP
#define TOML11_CONTEXT_HPP
#include "error_info.hpp"
#include "spec.hpp"
#include <vector>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
template<typename TypeConfig>
class context
{
public:
explicit context(const spec& toml_spec)
: toml_spec_(toml_spec), errors_{}
{}
bool has_error() const noexcept {return !errors_.empty();}
std::vector<error_info> const& errors() const noexcept {return errors_;}
semantic_version& toml_version() noexcept {return toml_spec_.version;}
semantic_version const& toml_version() const noexcept {return toml_spec_.version;}
spec& toml_spec() noexcept {return toml_spec_;}
spec const& toml_spec() const noexcept {return toml_spec_;}
void report_error(error_info err)
{
this->errors_.push_back(std::move(err));
}
error_info pop_last_error()
{
assert( ! errors_.empty());
auto e = std::move(errors_.back());
errors_.pop_back();
return e;
}
private:
spec toml_spec_;
std::vector<error_info> errors_;
};
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#if defined(TOML11_COMPILE_SOURCES)
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
struct type_config;
struct ordered_type_config;
namespace detail
{
extern template class context<::toml::type_config>;
extern template class context<::toml::ordered_type_config>;
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_COMPILE_SOURCES
#endif // TOML11_CONTEXT_HPP

View File

@@ -0,0 +1,212 @@
#ifndef TOML11_CONVERSION_HPP
#define TOML11_CONVERSION_HPP
#include "find.hpp"
#include "from.hpp" // IWYU pragma: keep
#include "into.hpp" // IWYU pragma: keep
#include "version.hpp"
#if defined(TOML11_HAS_OPTIONAL)
#include <optional>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
template<typename T>
inline constexpr bool is_optional_v = false;
template<typename T>
inline constexpr bool is_optional_v<std::optional<T>> = true;
template<typename T, typename TC>
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
{
if constexpr(is_optional_v<T>)
{
if(v.contains(var_name))
{
obj = toml::find<typename T::value_type>(v, var_name);
}
else
{
obj = std::nullopt;
}
}
else
{
obj = toml::find<T>(v, var_name);
}
}
template<typename T, typename TC>
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
{
if constexpr(is_optional_v<T>)
{
if(obj.has_value())
{
v[var_name] = obj.value();
}
}
else
{
v[var_name] = obj;
}
}
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#else
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
template<typename T, typename TC>
void find_member_variable_from_value(T& obj, const basic_value<TC>& v, const char* var_name)
{
obj = toml::find<T>(v, var_name);
}
template<typename T, typename TC>
void assign_member_variable_to_value(const T& obj, basic_value<TC>& v, const char* var_name)
{
v[var_name] = obj;
}
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // optional
// 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::get<foo::Foo>(v)` and `toml::find<foo::Foo>(file, "foo");`
//
#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__)
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
toml::detail::find_member_variable_from_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
toml::detail::assign_member_variable_to_value(obj.VAR_NAME, v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
namespace toml { \
inline namespace TOML11_INLINE_VERSION_NAMESPACE { \
template<> \
struct from<NAME> \
{ \
template<typename TC> \
static NAME from_toml(const basic_value<TC>& v) \
{ \
NAME obj; \
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
return obj; \
} \
}; \
template<> \
struct into<NAME> \
{ \
template<typename TC> \
static basic_value<TC> into_toml(const NAME& obj) \
{ \
::toml::basic_value<TC> v = typename ::toml::basic_value<TC>::table_type{}; \
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
return v; \
} \
}; \
} /* TOML11_INLINE_VERSION_NAMESPACE */ \
} /* toml */
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
#endif // TOML11_CONVERSION_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_DATETIME_HPP
#define TOML11_DATETIME_HPP
#include "fwd/datetime_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/datetime_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_DATETIME_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_ERROR_INFO_HPP
#define TOML11_ERROR_INFO_HPP
#include "fwd/error_info_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/error_info_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_ERROR_INFO_HPP

View File

@@ -0,0 +1,22 @@
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include <exception>
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
struct exception : public std::exception
{
public:
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override {return "";}
};
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOMl11_EXCEPTION_HPP

592
include/toml11/find.hpp Normal file
View File

@@ -0,0 +1,592 @@
#ifndef TOML11_FIND_HPP
#define TOML11_FIND_HPP
#include <algorithm>
#include "get.hpp"
#include "value.hpp"
#include "version.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// ----------------------------------------------------------------------------
// find<T>(value, key);
template<typename T, typename TC>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))>
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
return ::toml::get<T>(v.at(ky));
}
template<typename T, typename TC>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))>
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
return ::toml::get<T>(v.at(ky));
}
template<typename T, typename TC>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))>
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
{
return ::toml::get<T>(std::move(v.at(ky)));
}
// ----------------------------------------------------------------------------
// find<T>(value, idx)
template<typename T, typename TC>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC> const&>()))>
find(const basic_value<TC>& v, const std::size_t idx)
{
return ::toml::get<T>(v.at(idx));
}
template<typename T, typename TC>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))>
find(basic_value<TC>& v, const std::size_t idx)
{
return ::toml::get<T>(v.at(idx));
}
template<typename T, typename TC>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))>
find(basic_value<TC>&& v, const std::size_t idx)
{
return ::toml::get<T>(std::move(v.at(idx)));
}
// ----------------------------------------------------------------------------
// find(value, key/idx), w/o conversion
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
return v.at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find(basic_value<TC> const& v, const typename basic_value<TC>::key_type& ky)
{
return v.at(ky);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
{
return basic_value<TC>(std::move(v.at(ky)));
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find(basic_value<TC>& v, const std::size_t idx)
{
return v.at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find(basic_value<TC> const& v, const std::size_t idx)
{
return v.at(idx);
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find(basic_value<TC>&& v, const std::size_t idx)
{
return basic_value<TC>(std::move(v.at(idx)));
}
// --------------------------------------------------------------------------
// find<optional<T>>
#if defined(TOML11_HAS_OPTIONAL)
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
if(v.contains(ky))
{
return ::toml::get<typename T::value_type>(v.at(ky));
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& ky)
{
if(v.contains(ky))
{
return ::toml::get<typename T::value_type>(v.at(ky));
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& ky)
{
if(v.contains(ky))
{
return ::toml::get<typename T::value_type>(std::move(v.at(ky)));
}
else
{
return std::nullopt;
}
}
template<typename T, typename K, typename TC>
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K>::value, T>
find(const basic_value<TC>& v, const K& k)
{
if(static_cast<std::size_t>(k) < v.size())
{
return ::toml::get<typename T::value_type>(v.at(static_cast<std::size_t>(k)));
}
else
{
return std::nullopt;
}
}
template<typename T, typename K, typename TC>
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K>::value, T>
find(basic_value<TC>& v, const K& k)
{
if(static_cast<std::size_t>(k) < v.size())
{
return ::toml::get<typename T::value_type>(v.at(static_cast<std::size_t>(k)));
}
else
{
return std::nullopt;
}
}
template<typename T, typename K, typename TC>
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K>::value, T>
find(basic_value<TC>&& v, const K& k)
{
if(static_cast<std::size_t>(k) < v.size())
{
return ::toml::get<typename T::value_type>(std::move(v.at(static_cast<std::size_t>(k))));
}
else
{
return std::nullopt;
}
}
#endif // optional
// --------------------------------------------------------------------------
// toml::find(toml::value, toml::key, Ts&& ... keys)
namespace detail
{
// It suppresses warnings by -Wsign-conversion when we pass integer literal
// to toml::find. integer literal `0` is deduced as an int, and will be
// converted to std::size_t. This causes sign-conversion.
template<typename TC>
std::size_t key_cast(const std::size_t& v) noexcept
{
return v;
}
template<typename TC, typename T>
cxx::enable_if_t<std::is_integral<cxx::remove_cvref_t<T>>::value, std::size_t>
key_cast(const T& v) noexcept
{
return static_cast<std::size_t>(v);
}
// for string-like (string, string literal, string_view)
template<typename TC>
typename basic_value<TC>::key_type const&
key_cast(const typename basic_value<TC>::key_type& v) noexcept
{
return v;
}
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const typename basic_value<TC>::key_type::value_type* v)
{
return typename basic_value<TC>::key_type(v);
}
#if defined(TOML11_HAS_STRING_VIEW)
template<typename TC>
typename basic_value<TC>::key_type
key_cast(const std::string_view v)
{
return typename basic_value<TC>::key_type(v);
}
#endif // string_view
} // detail
// ----------------------------------------------------------------------------
// find(v, keys...)
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
}
// ----------------------------------------------------------------------------
// find<T>(v, keys...)
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<const basic_value<TC>&>()))>
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC>&>()))>
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find<T>(v.at(detail::key_cast<TC>(k1)), detail::key_cast<TC>(k2), ks...);
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<cxx::negation<detail::is_std_optional<T>>::value,
decltype(::toml::get<T>(std::declval<basic_value<TC>&&>()))>
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
return find<T>(std::move(v.at(detail::key_cast<TC>(k1))), detail::key_cast<TC>(k2), ks...);
}
#if defined(TOML11_HAS_OPTIONAL)
template<typename T, typename TC, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
find(const basic_value<TC>& v, const typename basic_value<TC>::key_type& k1, const K2& k2, const Ks& ... ks)
{
if(v.contains(k1))
{
return find<T>(v.at(k1), detail::key_cast<TC>(k2), ks...);
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
find(basic_value<TC>& v, const typename basic_value<TC>::key_type& k1, const K2& k2, const Ks& ... ks)
{
if(v.contains(k1))
{
return find<T>(v.at(k1), detail::key_cast<TC>(k2), ks...);
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_std_optional<T>::value, T>
find(basic_value<TC>&& v, const typename basic_value<TC>::key_type& k1, const K2& k2, const Ks& ... ks)
{
if(v.contains(k1))
{
return find<T>(v.at(k1), detail::key_cast<TC>(k2), ks...);
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K1>::value, T>
find(const basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
if(static_cast<std::size_t>(k1) < v.size())
{
return find<T>(v.at(static_cast<std::size_t>(k1)), detail::key_cast<TC>(k2), ks...);
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K1>::value, T>
find(basic_value<TC>& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
if(static_cast<std::size_t>(k1) < v.size())
{
return find<T>(v.at(static_cast<std::size_t>(k1)), detail::key_cast<TC>(k2), ks...);
}
else
{
return std::nullopt;
}
}
template<typename T, typename TC, typename K1, typename K2, typename ... Ks>
cxx::enable_if_t<detail::is_std_optional<T>::value && std::is_integral<K1>::value, T>
find(basic_value<TC>&& v, const K1& k1, const K2& k2, const Ks& ... ks)
{
if(static_cast<std::size_t>(k1) < v.size())
{
return find<T>(v.at(static_cast<std::size_t>(k1)), detail::key_cast<TC>(k2), ks...);
}
else
{
return std::nullopt;
}
}
#endif // optional
// ===========================================================================
// find_or<T>(value, key, fallback)
// ---------------------------------------------------------------------------
// find_or(v, key, other_v)
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
find_or(basic_value<TC>& v, const K& k, basic_value<TC>& opt) noexcept
{
try
{
return ::toml::find(v, detail::key_cast<TC>(k));
}
catch(...)
{
return opt;
}
}
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
find_or(const basic_value<TC>& v, const K& k, const basic_value<TC>& opt) noexcept
{
try
{
return ::toml::find(v, detail::key_cast<TC>(k));
}
catch(...)
{
return opt;
}
}
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
find_or(basic_value<TC>&& v, const K& k, basic_value<TC>&& opt) noexcept
{
try
{
return ::toml::find(v, detail::key_cast<TC>(k));
}
catch(...)
{
return opt;
}
}
// ---------------------------------------------------------------------------
// toml types (return type can be a reference)
template<typename T, typename TC, typename K>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
cxx::remove_cvref_t<T> const&>
find_or(const basic_value<TC>& v, const K& k, const T& opt)
{
try
{
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
}
catch(...)
{
return opt;
}
}
template<typename T, typename TC, typename K>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_const<T>>,
detail::is_exact_toml_type<T, basic_value<TC>>
>::value, cxx::remove_cvref_t<T>&>
find_or(basic_value<TC>& v, const K& k, T& opt)
{
try
{
return ::toml::get<T>(v.at(detail::key_cast<TC>(k)));
}
catch(...)
{
return opt;
}
}
template<typename T, typename TC, typename K>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value,
cxx::remove_cvref_t<T>>
find_or(basic_value<TC>&& v, const K& k, T opt)
{
try
{
return ::toml::get<T>(std::move(v.at(detail::key_cast<TC>(k))));
}
catch(...)
{
return T(std::move(opt));
}
}
// ---------------------------------------------------------------------------
// string literal (deduced as std::string)
// XXX to avoid confusion when T is explicitly specified in find_or<T>(),
// we restrict the string type as std::string.
template<typename TC, typename K>
cxx::enable_if_t<detail::is_type_config<TC>::value, std::string>
find_or(const basic_value<TC>& v, const K& k, const char* opt)
{
try
{
return ::toml::get<std::string>(v.at(detail::key_cast<TC>(k)));
}
catch(...)
{
return std::string(opt);
}
}
// ---------------------------------------------------------------------------
// other types (requires type conversion and return type cannot be a reference)
template<typename T, typename TC, typename K>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<detail::is_basic_value<cxx::remove_cvref_t<T>>>,
detail::is_not_toml_type<cxx::remove_cvref_t<T>, basic_value<TC>>,
cxx::negation<std::is_same<cxx::remove_cvref_t<T>,
const typename basic_value<TC>::string_type::value_type*>>
>::value, cxx::remove_cvref_t<T>>
find_or(const basic_value<TC>& v, const K& ky, T opt)
{
try
{
return ::toml::get<cxx::remove_cvref_t<T>>(v.at(detail::key_cast<TC>(ky)));
}
catch(...)
{
return cxx::remove_cvref_t<T>(std::move(opt));
}
}
// ----------------------------------------------------------------------------
// recursive
namespace detail
{
template<typename ...Ts>
auto last_one(Ts&&... args)
-> decltype(std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...)))
{
return std::get<sizeof...(Ts)-1>(std::forward_as_tuple(std::forward<Ts>(args)...));
}
} // detail
template<typename Value, typename K1, typename K2, typename K3, typename ... Ks>
auto find_or(Value&& v, const K1& k1, const K2& k2, K3&& k3, Ks&& ... keys) noexcept
-> cxx::enable_if_t<
detail::is_basic_value<cxx::remove_cvref_t<Value>>::value,
decltype(find_or(v, k2, std::forward<K3>(k3), std::forward<Ks>(keys)...))
>
{
try
{
return find_or(v.at(k1), k2, std::forward<K3>(k3), std::forward<Ks>(keys)...);
}
catch(...)
{
return detail::last_one(k3, keys...);
}
}
template<typename T, typename TC, typename K1, typename K2, typename K3, typename ... Ks>
T find_or(const basic_value<TC>& v, const K1& k1, const K2& k2, const K3& k3, const Ks& ... keys) noexcept
{
try
{
return find_or<T>(v.at(k1), k2, k3, keys...);
}
catch(...)
{
return static_cast<T>(detail::last_one(k3, keys...));
}
}
// ===========================================================================
// find_or_default<T>(value, key)
template<typename T, typename TC, typename K>
cxx::enable_if_t<std::is_default_constructible<T>::value, T>
find_or_default(const basic_value<TC>& v, K&& k) noexcept(std::is_nothrow_default_constructible<T>::value)
{
try
{
return ::toml::get<T>(v.at(detail::key_cast<TC>(std::forward<K>(k))));
}
catch(...)
{
return T();
}
}
template<typename T, typename TC, typename K1, typename ... Ks>
cxx::enable_if_t<std::is_default_constructible<T>::value, T>
find_or_default(const basic_value<TC>& v, K1&& k1, Ks&& ... keys) noexcept(std::is_nothrow_default_constructible<T>::value)
{
try
{
return find_or_default<T>(v.at(std::forward<K1>(k1)), std::forward<Ks>(keys)...);
}
catch(...)
{
return T();
}
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_FIND_HPP

10
include/toml11/format.hpp Normal file
View File

@@ -0,0 +1,10 @@
#ifndef TOML11_FORMAT_HPP
#define TOML11_FORMAT_HPP
#include "fwd/format_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/format_impl.hpp" // IWYU pragma: export
#endif
#endif// TOML11_FORMAT_HPP

21
include/toml11/from.hpp Normal file
View File

@@ -0,0 +1,21 @@
#ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
template<typename T>
struct from;
// {
// static T from_toml(const toml::value& v)
// {
// // User-defined conversions ...
// }
// };
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_FROM_HPP

View File

@@ -0,0 +1,93 @@
#ifndef TOML11_COLOR_FWD_HPP
#define TOML11_COLOR_FWD_HPP
#include <iosfwd>
#include "../version.hpp"
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
#else
#define TOML11_ERROR_MESSAGE_COLORIZED false
#endif
#ifdef TOML11_USE_THREAD_LOCAL_COLORIZATION
#define TOML11_THREAD_LOCAL_COLORIZATION thread_local
#else
#define TOML11_THREAD_LOCAL_COLORIZATION
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace color
{
// put ANSI escape sequence to ostream
inline namespace ansi
{
namespace detail
{
// Control color mode globally
class color_mode
{
public:
void enable() noexcept
{
should_color_ = true;
}
void disable() noexcept
{
should_color_ = false;
}
bool should_color() const noexcept
{
return should_color_;
}
private:
bool should_color_ = TOML11_ERROR_MESSAGE_COLORIZED;
};
inline color_mode& color_status() noexcept
{
static TOML11_THREAD_LOCAL_COLORIZATION color_mode status;
return status;
}
} // detail
std::ostream& reset (std::ostream& os);
std::ostream& bold (std::ostream& os);
std::ostream& grey (std::ostream& os);
std::ostream& gray (std::ostream& os);
std::ostream& red (std::ostream& os);
std::ostream& green (std::ostream& os);
std::ostream& yellow (std::ostream& os);
std::ostream& blue (std::ostream& os);
std::ostream& magenta(std::ostream& os);
std::ostream& cyan (std::ostream& os);
std::ostream& white (std::ostream& os);
} // ansi
inline void enable()
{
return detail::color_status().enable();
}
inline void disable()
{
return detail::color_status().disable();
}
inline bool should_color()
{
return detail::color_status().should_color();
}
} // color
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_COLOR_FWD_HPP

View File

@@ -0,0 +1,454 @@
#ifndef TOML11_COMMENTS_FWD_HPP
#define TOML11_COMMENTS_FWD_HPP
// to use __has_builtin
#include "../version.hpp" // IWYU pragma: keep
#include <exception>
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <ostream>
// 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
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
class discard_comments; // forward decl
class preserve_comments
{
public:
// `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;
public:
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.
//
// `std::vector::insert` and `std::vector::erase` in the STL implementation
// included in GCC 4.8.5 takes `std::vector::iterator` instead of
// `std::vector::const_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;
};
bool operator==(const preserve_comments& lhs, const preserve_comments& rhs);
bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs);
bool operator< (const preserve_comments& lhs, const preserve_comments& rhs);
bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs);
bool operator> (const preserve_comments& lhs, const preserve_comments& rhs);
bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs);
void swap(preserve_comments& lhs, preserve_comments& rhs);
void swap(preserve_comments& lhs, std::vector<std::string>& rhs);
void swap(std::vector<std::string>& lhs, preserve_comments& rhs);
std::ostream& operator<<(std::ostream& os, const preserve_comments& com);
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).
class discard_comments
{
public:
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>;
public:
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)
{
#if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
#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;}
inline std::ostream& operator<<(std::ostream& os, const discard_comments&) {return os;}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml11
#endif // TOML11_COMMENTS_FWD_HPP

View File

@@ -0,0 +1,266 @@
#ifndef TOML11_DATETIME_FWD_HPP
#define TOML11_DATETIME_FWD_HPP
#include <chrono>
#include <iosfwd>
#include <string>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
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{0}; // A.D. (like, 2018)
std::uint8_t month{0}; // [0, 11]
std::uint8_t day{0}; // [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);
explicit local_date(const std::time_t t);
operator std::chrono::system_clock::time_point() const;
operator std::time_t() const;
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;
};
bool operator==(const local_date& lhs, const local_date& rhs);
bool operator!=(const local_date& lhs, const local_date& rhs);
bool operator< (const local_date& lhs, const local_date& rhs);
bool operator<=(const local_date& lhs, const local_date& rhs);
bool operator> (const local_date& lhs, const local_date& rhs);
bool operator>=(const local_date& lhs, const local_date& rhs);
std::ostream& operator<<(std::ostream& os, const local_date& date);
std::string to_string(const local_date& date);
// -----------------------------------------------------------------------------
struct local_time
{
std::uint8_t hour{0}; // [0, 23]
std::uint8_t minute{0}; // [0, 59]
std::uint8_t second{0}; // [0, 60]
std::uint16_t millisecond{0}; // [0, 999]
std::uint16_t microsecond{0}; // [0, 999]
std::uint16_t nanosecond{0}; // [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;
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;
};
bool operator==(const local_time& lhs, const local_time& rhs);
bool operator!=(const local_time& lhs, const local_time& rhs);
bool operator< (const local_time& lhs, const local_time& rhs);
bool operator<=(const local_time& lhs, const local_time& rhs);
bool operator> (const local_time& lhs, const local_time& rhs);
bool operator>=(const local_time& lhs, const local_time& rhs);
std::ostream& operator<<(std::ostream& os, const local_time& time);
std::string to_string(const local_time& time);
// ----------------------------------------------------------------------------
struct time_offset
{
std::int8_t hour{0}; // [-12, 12]
std::int8_t minute{0}; // [-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;
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;
};
bool operator==(const time_offset& lhs, const time_offset& rhs);
bool operator!=(const time_offset& lhs, const time_offset& rhs);
bool operator< (const time_offset& lhs, const time_offset& rhs);
bool operator<=(const time_offset& lhs, const time_offset& rhs);
bool operator> (const time_offset& lhs, const time_offset& rhs);
bool operator>=(const time_offset& lhs, const time_offset& rhs);
std::ostream& operator<<(std::ostream& os, const time_offset& offset);
std::string to_string(const time_offset& offset);
// -----------------------------------------------------------------------------
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);
explicit local_datetime(const std::time_t t);
operator std::chrono::system_clock::time_point() const;
operator std::time_t() const;
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;
};
bool operator==(const local_datetime& lhs, const local_datetime& rhs);
bool operator!=(const local_datetime& lhs, const local_datetime& rhs);
bool operator< (const local_datetime& lhs, const local_datetime& rhs);
bool operator<=(const local_datetime& lhs, const local_datetime& rhs);
bool operator> (const local_datetime& lhs, const local_datetime& rhs);
bool operator>=(const local_datetime& lhs, const local_datetime& rhs);
std::ostream& operator<<(std::ostream& os, const local_datetime& dt);
std::string to_string(const local_datetime& dt);
// -----------------------------------------------------------------------------
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}
{}
// use the current local timezone offset
explicit offset_datetime(const local_datetime& ld);
explicit offset_datetime(const std::chrono::system_clock::time_point& tp);
explicit offset_datetime(const std::time_t& t);
explicit offset_datetime(const std::tm& t);
operator std::chrono::system_clock::time_point() const;
operator std::time_t() const;
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);
};
bool operator==(const offset_datetime& lhs, const offset_datetime& rhs);
bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs);
bool operator< (const offset_datetime& lhs, const offset_datetime& rhs);
bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs);
bool operator> (const offset_datetime& lhs, const offset_datetime& rhs);
bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs);
std::ostream& operator<<(std::ostream& os, const offset_datetime& dt);
std::string to_string(const offset_datetime& dt);
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_DATETIME_FWD_HPP

View File

@@ -0,0 +1,101 @@
#ifndef TOML11_ERROR_INFO_FWD_HPP
#define TOML11_ERROR_INFO_FWD_HPP
#include "../source_location.hpp"
#include "../utility.hpp"
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// error info returned from parser.
struct error_info
{
error_info(std::string t, source_location l, std::string m, std::string s = "")
: title_(std::move(t)), locations_{std::make_pair(std::move(l), std::move(m))},
suffix_(std::move(s))
{}
error_info(std::string t, std::vector<std::pair<source_location, std::string>> l,
std::string s = "")
: title_(std::move(t)), locations_(std::move(l)), suffix_(std::move(s))
{}
std::string const& title() const noexcept {return title_;}
std::string & title() noexcept {return title_;}
std::vector<std::pair<source_location, std::string>> const&
locations() const noexcept {return locations_;}
void add_locations(source_location loc, std::string msg) noexcept
{
locations_.emplace_back(std::move(loc), std::move(msg));
}
std::string const& suffix() const noexcept {return suffix_;}
std::string & suffix() noexcept {return suffix_;}
private:
std::string title_;
std::vector<std::pair<source_location, std::string>> locations_;
std::string suffix_; // hint or something like that
};
// forward decl
template<typename TypeConfig>
class basic_value;
namespace detail
{
inline error_info make_error_info_rec(error_info e)
{
return e;
}
inline error_info make_error_info_rec(error_info e, std::string s)
{
e.suffix() = s;
return e;
}
template<typename TC, typename ... Ts>
error_info make_error_info_rec(error_info e,
const basic_value<TC>& v, std::string msg, Ts&& ... tail);
template<typename ... Ts>
error_info make_error_info_rec(error_info e,
source_location loc, std::string msg, Ts&& ... tail)
{
e.add_locations(std::move(loc), std::move(msg));
return make_error_info_rec(std::move(e), std::forward<Ts>(tail)...);
}
} // detail
template<typename ... Ts>
error_info make_error_info(
std::string title, source_location loc, std::string msg, Ts&& ... tail)
{
error_info ei(std::move(title), std::move(loc), std::move(msg));
return detail::make_error_info_rec(ei, std::forward<Ts>(tail) ... );
}
std::string format_error(const std::string& errkind, const error_info& err);
std::string format_error(const error_info& err);
// for custom error message
template<typename ... Ts>
std::string format_error(std::string title,
source_location loc, std::string msg, Ts&& ... tail)
{
return format_error("", make_error_info(std::move(title),
std::move(loc), std::move(msg), std::forward<Ts>(tail)...));
}
std::ostream& operator<<(std::ostream& os, const error_info& e);
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_ERROR_INFO_FWD_HPP

View File

@@ -0,0 +1,255 @@
#ifndef TOML11_FORMAT_FWD_HPP
#define TOML11_FORMAT_FWD_HPP
#include <iosfwd>
#include <string>
#include <utility>
#include <cstddef>
#include <cstdint>
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// toml types with serialization info
enum class indent_char : std::uint8_t
{
space, // use space
tab, // use tab
none // no indent
};
std::ostream& operator<<(std::ostream& os, const indent_char& c);
std::string to_string(const indent_char c);
// ----------------------------------------------------------------------------
// boolean
struct boolean_format_info
{
// nothing, for now
};
inline bool operator==(const boolean_format_info&, const boolean_format_info&) noexcept
{
return true;
}
inline bool operator!=(const boolean_format_info&, const boolean_format_info&) noexcept
{
return false;
}
// ----------------------------------------------------------------------------
// integer
enum class integer_format : std::uint8_t
{
dec = 0,
bin = 1,
oct = 2,
hex = 3,
};
std::ostream& operator<<(std::ostream& os, const integer_format f);
std::string to_string(const integer_format);
struct integer_format_info
{
integer_format fmt = integer_format::dec;
bool uppercase = true; // hex with uppercase
std::size_t width = 0; // minimal width (may exceed)
std::size_t spacer = 0; // position of `_` (if 0, no spacer)
std::string suffix = ""; // _suffix (library extension)
};
bool operator==(const integer_format_info&, const integer_format_info&) noexcept;
bool operator!=(const integer_format_info&, const integer_format_info&) noexcept;
// ----------------------------------------------------------------------------
// floating
enum class floating_format : std::uint8_t
{
defaultfloat = 0,
fixed = 1, // does not include exponential part
scientific = 2, // always include exponential part
hex = 3 // hexfloat extension
};
std::ostream& operator<<(std::ostream& os, const floating_format f);
std::string to_string(const floating_format);
struct floating_format_info
{
floating_format fmt = floating_format::defaultfloat;
std::size_t prec = 0; // precision (if 0, use the default)
std::string suffix = ""; // 1.0e+2_suffix (library extension)
};
bool operator==(const floating_format_info&, const floating_format_info&) noexcept;
bool operator!=(const floating_format_info&, const floating_format_info&) noexcept;
// ----------------------------------------------------------------------------
// string
enum class string_format : std::uint8_t
{
basic = 0,
literal = 1,
multiline_basic = 2,
multiline_literal = 3
};
std::ostream& operator<<(std::ostream& os, const string_format f);
std::string to_string(const string_format);
struct string_format_info
{
string_format fmt = string_format::basic;
bool start_with_newline = false;
};
bool operator==(const string_format_info&, const string_format_info&) noexcept;
bool operator!=(const string_format_info&, const string_format_info&) noexcept;
// ----------------------------------------------------------------------------
// datetime
enum class datetime_delimiter_kind : std::uint8_t
{
upper_T = 0,
lower_t = 1,
space = 2,
};
std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d);
std::string to_string(const datetime_delimiter_kind);
struct offset_datetime_format_info
{
datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T;
bool has_seconds = true;
std::size_t subsecond_precision = 6; // [us]
};
bool operator==(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept;
bool operator!=(const offset_datetime_format_info&, const offset_datetime_format_info&) noexcept;
struct local_datetime_format_info
{
datetime_delimiter_kind delimiter = datetime_delimiter_kind::upper_T;
bool has_seconds = true;
std::size_t subsecond_precision = 6; // [us]
};
bool operator==(const local_datetime_format_info&, const local_datetime_format_info&) noexcept;
bool operator!=(const local_datetime_format_info&, const local_datetime_format_info&) noexcept;
struct local_date_format_info
{
// nothing, for now
};
bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept;
bool operator!=(const local_date_format_info&, const local_date_format_info&) noexcept;
struct local_time_format_info
{
bool has_seconds = true;
std::size_t subsecond_precision = 6; // [us]
};
bool operator==(const local_time_format_info&, const local_time_format_info&) noexcept;
bool operator!=(const local_time_format_info&, const local_time_format_info&) noexcept;
// ----------------------------------------------------------------------------
// array
enum class array_format : std::uint8_t
{
default_format = 0,
oneline = 1,
multiline = 2,
array_of_tables = 3 // [[format.in.this.way]]
};
std::ostream& operator<<(std::ostream& os, const array_format f);
std::string to_string(const array_format);
struct array_format_info
{
array_format fmt = array_format::default_format;
indent_char indent_type = indent_char::space;
std::int32_t body_indent = 4; // indent in case of multiline
std::int32_t closing_indent = 0; // indent of `]`
};
bool operator==(const array_format_info&, const array_format_info&) noexcept;
bool operator!=(const array_format_info&, const array_format_info&) noexcept;
// ----------------------------------------------------------------------------
// table
enum class table_format : std::uint8_t
{
multiline = 0, // [foo] \n bar = "baz"
oneline = 1, // foo = {bar = "baz"}
dotted = 2, // foo.bar = "baz"
multiline_oneline = 3, // foo = { \n bar = "baz" \n }
implicit = 4 // [x] defined by [x.y.z]. skip in serializer.
};
std::ostream& operator<<(std::ostream& os, const table_format f);
std::string to_string(const table_format);
struct table_format_info
{
table_format fmt = table_format::multiline;
indent_char indent_type = indent_char::space;
std::int32_t body_indent = 0; // indent of values
std::int32_t name_indent = 0; // indent of [table]
std::int32_t closing_indent = 0; // in case of {inline-table}
};
bool operator==(const table_format_info&, const table_format_info&) noexcept;
bool operator!=(const table_format_info&, const table_format_info&) noexcept;
// ----------------------------------------------------------------------------
// wrapper
namespace detail
{
template<typename T, typename F>
struct value_with_format
{
using value_type = T;
using format_type = F;
value_with_format() = default;
~value_with_format() = default;
value_with_format(const value_with_format&) = default;
value_with_format(value_with_format&&) = default;
value_with_format& operator=(const value_with_format&) = default;
value_with_format& operator=(value_with_format&&) = default;
value_with_format(value_type v, format_type f)
: value{std::move(v)}, format{std::move(f)}
{}
template<typename U>
value_with_format(value_with_format<U, format_type> other)
: value{std::move(other.value)}, format{std::move(other.format)}
{}
value_type value;
format_type format;
};
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_FORMAT_FWD_HPP

View File

@@ -0,0 +1,36 @@
#ifndef TOML11_LITERAL_FWD_HPP
#define TOML11_LITERAL_FWD_HPP
#include "../location.hpp"
#include "../types.hpp"
#include "../version.hpp" // IWYU pragma: keep for TOML11_HAS_CHAR8_T
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
// implementation
::toml::value literal_internal_impl(location loc);
} // detail
inline namespace literals
{
inline namespace toml_literals
{
::toml::value operator""_toml(const char* str, std::size_t len);
#if defined(TOML11_HAS_CHAR8_T)
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
::toml::value operator"" _toml(const char8_t* str, std::size_t len);
#endif
} // toml_literals
} // literals
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_LITERAL_FWD_HPP

View File

@@ -0,0 +1,153 @@
#ifndef TOML11_LOCATION_FWD_HPP
#define TOML11_LOCATION_FWD_HPP
#include "../result.hpp"
#include "../version.hpp"
#include <memory>
#include <string>
#include <vector>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
class region; // fwd decl
//
// To represent where we are reading in the parse functions.
// Since it "points" somewhere in the input stream, the length is always 1.
//
class location
{
public:
using char_type = unsigned char; // must be unsigned
using container_type = std::vector<char_type>;
using difference_type = typename container_type::difference_type; // to suppress sign-conversion warning
using source_ptr = std::shared_ptr<const container_type>;
public:
location(source_ptr src, std::string src_name)
: source_(std::move(src)), source_name_(std::move(src_name)),
location_(0), line_number_(1), column_number_(1)
{}
location(const location&) = default;
location(location&&) = default;
location& operator=(const location&) = default;
location& operator=(location&&) = default;
~location() = default;
void advance(std::size_t n = 1) noexcept;
void retrace() noexcept;
bool is_ok() const noexcept { return static_cast<bool>(this->source_); }
bool eof() const noexcept;
char_type current() const;
char_type peek();
std::size_t get_location() const noexcept
{
return this->location_;
}
std::size_t line_number() const noexcept
{
return this->line_number_;
}
std::size_t column_number() const noexcept
{
return this->column_number_;
}
std::string get_line() const;
source_ptr const& source() const noexcept {return this->source_;}
std::string const& source_name() const noexcept {return this->source_name_;}
private:
void advance_impl(const std::size_t n);
void retrace_impl();
std::size_t calc_column_number() const noexcept;
private:
friend region;
private:
source_ptr source_;
std::string source_name_;
std::size_t location_; // std::vector<>::difference_type is signed
std::size_t line_number_;
std::size_t column_number_;
};
bool operator==(const location& lhs, const location& rhs) noexcept;
bool operator!=(const location& lhs, const location& rhs);
location prev(const location& loc);
location next(const location& loc);
location make_temporary_location(const std::string& str) noexcept;
template<typename F>
result<location, none_t>
find_if(const location& first, const location& last, const F& func) noexcept
{
if(first.source() != last.source()) { return err(); }
if(first.get_location() >= last.get_location()) { return err(); }
auto loc = first;
while(loc.get_location() != last.get_location())
{
if(func(loc.current()))
{
return ok(loc);
}
loc.advance();
}
return err();
}
template<typename F>
result<location, none_t>
rfind_if(location first, const location& last, const F& func)
{
if(first.source() != last.source()) { return err(); }
if(first.get_location() >= last.get_location()) { return err(); }
auto loc = last;
while(loc.get_location() != first.get_location())
{
if(func(loc.current()))
{
return ok(loc);
}
loc.retrace();
}
if(func(first.current()))
{
return ok(first);
}
return err();
}
result<location, none_t> find(const location& first, const location& last,
const location::char_type val);
result<location, none_t> rfind(const location& first, const location& last,
const location::char_type val);
std::size_t count(const location& first, const location& last,
const location::char_type& c);
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_LOCATION_FWD_HPP

View File

@@ -0,0 +1,114 @@
#ifndef TOML11_REGION_FWD_HPP
#define TOML11_REGION_FWD_HPP
#include "../location.hpp"
#include "../version.hpp"
#include <string>
#include <vector>
#include <cassert>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
//
// To represent where is a toml::value defined, or where does an error occur.
// Stored in toml::value. source_location will be constructed based on this.
//
class region
{
public:
using char_type = location::char_type;
using container_type = location::container_type;
using difference_type = location::difference_type;
using source_ptr = location::source_ptr;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
public:
// a value that is constructed manually does not have input stream info
region()
: source_(nullptr), source_name_(""), length_(0),
first_(0), first_line_(0), first_column_(0), last_(0), last_line_(0),
last_column_(0)
{}
// a value defined in [first, last).
// Those source must be the same. Instread, `region` does not make sense.
region(const location& first, const location& last);
// shorthand of [loc, loc+1)
explicit region(const location& loc);
~region() = default;
region(const region&) = default;
region(region&&) = default;
region& operator=(const region&) = default;
region& operator=(region&&) = default;
bool is_ok() const noexcept { return static_cast<bool>(this->source_); }
operator bool() const noexcept { return this->is_ok(); }
std::size_t length() const noexcept {return this->length_;}
std::size_t first_line_number() const noexcept
{
return this->first_line_;
}
std::size_t first_column_number() const noexcept
{
return this->first_column_;
}
std::size_t last_line_number() const noexcept
{
return this->last_line_;
}
std::size_t last_column_number() const noexcept
{
return this->last_column_;
}
char_type at(std::size_t i) const;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
std::string as_string() const;
std::vector<std::pair<std::string, std::size_t>> as_lines() const;
source_ptr const& source() const noexcept {return this->source_;}
std::string const& source_name() const noexcept {return this->source_name_;}
private:
std::pair<std::string, std::size_t>
take_line(const_iterator begin, const_iterator end) const;
private:
source_ptr source_;
std::string source_name_;
std::size_t length_;
std::size_t first_;
std::size_t first_line_;
std::size_t first_column_;
std::size_t last_;
std::size_t last_line_;
std::size_t last_column_;
};
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_REGION_FWD_HPP

View File

@@ -0,0 +1,370 @@
#ifndef TOML11_SCANNER_FWD_HPP
#define TOML11_SCANNER_FWD_HPP
#include "../region.hpp"
#include "../version.hpp"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <cassert>
#include <cstdio>
#include <cctype>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
class scanner_base
{
public:
virtual ~scanner_base() = default;
virtual region scan(location& loc) const = 0;
virtual scanner_base* clone() const = 0;
// returns expected character or set of characters or literal.
// to show the error location, it changes loc (in `sequence`, especially).
virtual std::string expected_chars(location& loc) const = 0;
virtual std::string name() const = 0;
};
// make `scanner*` copyable
struct scanner_storage
{
template<typename Scanner, cxx::enable_if_t<
std::is_base_of<scanner_base, cxx::remove_cvref_t<Scanner>>::value,
std::nullptr_t> = nullptr>
explicit scanner_storage(Scanner&& s)
: scanner_(cxx::make_unique<cxx::remove_cvref_t<Scanner>>(std::forward<Scanner>(s)))
{}
~scanner_storage() = default;
scanner_storage(const scanner_storage& other);
scanner_storage& operator=(const scanner_storage& other);
scanner_storage(scanner_storage&&) = default;
scanner_storage& operator=(scanner_storage&&) = default;
bool is_ok() const noexcept {return static_cast<bool>(scanner_);}
region scan(location& loc) const;
std::string expected_chars(location& loc) const;
scanner_base& get() const noexcept;
std::string name() const;
private:
std::unique_ptr<scanner_base> scanner_;
};
// ----------------------------------------------------------------------------
class character final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit character(const char_type c) noexcept
: value_(c)
{}
~character() override = default;
region scan(location& loc) const override;
std::string expected_chars(location&) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
char_type value_;
};
// ----------------------------------------------------------------------------
class character_either final : public scanner_base
{
public:
using char_type = location::char_type;
public:
template<std::size_t N>
explicit character_either(const char (&cs)[N]) noexcept
: value_(cs), size_(N-1) // remove null character at the end
{}
~character_either() override = default;
region scan(location& loc) const override;
std::string expected_chars(location&) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
const char* value_;
std::size_t size_;
};
// ----------------------------------------------------------------------------
class character_in_range final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit character_in_range(const char_type from, const char_type to) noexcept
: from_(from), to_(to)
{}
~character_in_range() override = default;
region scan(location& loc) const override;
std::string expected_chars(location&) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
char_type from_;
char_type to_;
};
// ----------------------------------------------------------------------------
class literal final : public scanner_base
{
public:
using char_type = location::char_type;
public:
template<std::size_t N>
explicit literal(const char (&cs)[N]) noexcept
: value_(cs), size_(N-1) // remove null character at the end
{}
~literal() override = default;
region scan(location& loc) const override;
std::string expected_chars(location&) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
const char* value_;
std::size_t size_;
};
// ----------------------------------------------------------------------------
class sequence final: public scanner_base
{
public:
using char_type = location::char_type;
public:
template<typename ... Ts>
explicit sequence(Ts&& ... args)
{
push_back_all(std::forward<Ts>(args)...);
}
sequence(const sequence&) = default;
sequence(sequence&&) = default;
sequence& operator=(const sequence&) = default;
sequence& operator=(sequence&&) = default;
~sequence() override = default;
region scan(location& loc) const override;
std::string expected_chars(location& loc) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
void push_back_all()
{
return;
}
template<typename T, typename ... Ts>
void push_back_all(T&& head, Ts&& ... args)
{
others_.emplace_back(std::forward<T>(head));
push_back_all(std::forward<Ts>(args)...);
return;
}
private:
std::vector<scanner_storage> others_;
};
// ----------------------------------------------------------------------------
class either final: public scanner_base
{
public:
using char_type = location::char_type;
public:
template<typename ... Ts>
explicit either(Ts&& ... args)
{
push_back_all(std::forward<Ts>(args)...);
}
either(const either&) = default;
either(either&&) = default;
either& operator=(const either&) = default;
either& operator=(either&&) = default;
~either() override = default;
region scan(location& loc) const override;
std::string expected_chars(location& loc) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
void push_back_all()
{
return;
}
template<typename T, typename ... Ts>
void push_back_all(T&& head, Ts&& ... args)
{
others_.emplace_back(std::forward<T>(head));
push_back_all(std::forward<Ts>(args)...);
return;
}
private:
std::vector<scanner_storage> others_;
};
// ----------------------------------------------------------------------------
class repeat_exact final: public scanner_base
{
public:
using char_type = location::char_type;
public:
template<typename Scanner>
repeat_exact(const std::size_t length, Scanner&& other)
: length_(length), other_(std::forward<Scanner>(other))
{}
repeat_exact(const repeat_exact&) = default;
repeat_exact(repeat_exact&&) = default;
repeat_exact& operator=(const repeat_exact&) = default;
repeat_exact& operator=(repeat_exact&&) = default;
~repeat_exact() override = default;
region scan(location& loc) const override;
std::string expected_chars(location& loc) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
std::size_t length_;
scanner_storage other_;
};
// ----------------------------------------------------------------------------
class repeat_at_least final: public scanner_base
{
public:
using char_type = location::char_type;
public:
template<typename Scanner>
repeat_at_least(const std::size_t length, Scanner&& s)
: length_(length), other_(std::forward<Scanner>(s))
{}
repeat_at_least(const repeat_at_least&) = default;
repeat_at_least(repeat_at_least&&) = default;
repeat_at_least& operator=(const repeat_at_least&) = default;
repeat_at_least& operator=(repeat_at_least&&) = default;
~repeat_at_least() override = default;
region scan(location& loc) const override;
std::string expected_chars(location& loc) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
std::size_t length_;
scanner_storage other_;
};
// ----------------------------------------------------------------------------
class maybe final: public scanner_base
{
public:
using char_type = location::char_type;
public:
template<typename Scanner>
explicit maybe(Scanner&& s)
: other_(std::forward<Scanner>(s))
{}
maybe(const maybe&) = default;
maybe(maybe&&) = default;
maybe& operator=(const maybe&) = default;
maybe& operator=(maybe&&) = default;
~maybe() override = default;
region scan(location& loc) const override;
std::string expected_chars(location&) const override;
scanner_base* clone() const override;
std::string name() const override;
private:
scanner_storage other_;
};
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_SCANNER_FWD_HPP

View File

@@ -0,0 +1,152 @@
#ifndef TOML11_SOURCE_LOCATION_FWD_HPP
#define TOML11_SOURCE_LOCATION_FWD_HPP
#include "../region.hpp"
#include "../version.hpp"
#include <sstream>
#include <string>
#include <vector>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
//
// A struct to contain location in a toml file.
//
// To reduce memory consumption, it omits unrelated parts of long lines. like:
//
// 1. one long line, short region
// ```
// |
// 1 | ... "foo", "bar", baz, "qux", "foobar", ...
// | ^-- unknown value
// ```
// 2. long region
// ```
// |
// 1 | array = [ "foo", ... "bar" ]
// | ^^^^^^^^^^^^^^^^^^^^- in this array
// ```
// 3. many lines
// |
// 1 | array = [ "foo",
// | ^^^^^^^^
// | ...
// | ^^^
// |
// 10 | , "bar"]
// | ^^^^^^^^- in this array
// ```
//
struct source_location
{
public:
explicit source_location(const detail::region& r);
~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;
bool is_ok() const noexcept {return this->is_ok_;}
std::size_t length() const noexcept {return this->length_;}
std::size_t first_line_number() const noexcept {return this->first_line_;}
std::size_t first_column_number() const noexcept {return this->first_column_;}
std::size_t last_line_number() const noexcept {return this->last_line_;}
std::size_t last_column_number() const noexcept {return this->last_column_;}
std::string const& file_name() const noexcept {return this->file_name_;}
std::size_t num_lines() const noexcept {return this->line_str_.size();}
std::string const& first_line() const;
std::string const& last_line() const;
std::vector<std::string> const& lines() const noexcept {return line_str_;}
// for internal use
std::size_t first_column_offset() const noexcept {return this->first_offset_;}
std::size_t last_column_offset() const noexcept {return this->last_offset_;}
private:
bool is_ok_;
std::size_t first_line_;
std::size_t first_column_; // column num in the actual file
std::size_t first_offset_; // column num in the shown line
std::size_t last_line_;
std::size_t last_column_; // column num in the actual file
std::size_t last_offset_; // column num in the shown line
std::size_t length_;
std::string file_name_;
std::vector<std::string> line_str_;
};
namespace detail
{
std::size_t integer_width_base10(std::size_t i) noexcept;
inline std::size_t line_width() noexcept {return 0;}
template<typename ... Ts>
std::size_t line_width(const source_location& loc, const std::string& /*msg*/,
const Ts& ... tail) noexcept
{
return (std::max)(
integer_width_base10(loc.last_line_number()), line_width(tail...));
}
std::ostringstream&
format_filename(std::ostringstream& oss, const source_location& loc);
std::ostringstream&
format_empty_line(std::ostringstream& oss, const std::size_t lnw);
std::ostringstream& format_line(std::ostringstream& oss,
const std::size_t lnw, const std::size_t linenum, const std::string& line);
std::ostringstream& format_underline(std::ostringstream& oss,
const std::size_t lnw, const std::size_t col, const std::size_t len,
const std::string& msg);
std::string format_location_impl(const std::size_t lnw,
const std::string& prev_fname,
const source_location& loc, const std::string& msg);
inline std::string format_location_rec(const std::size_t, const std::string&)
{
return "";
}
template<typename ... Ts>
std::string format_location_rec(const std::size_t lnw,
const std::string& prev_fname,
const source_location& loc, const std::string& msg,
const Ts& ... tail)
{
return format_location_impl(lnw, prev_fname, loc, msg) +
format_location_rec(lnw, loc.file_name(), tail...);
}
} // namespace detail
// format a location info without title
template<typename ... Ts>
std::string format_location(
const source_location& loc, const std::string& msg, const Ts& ... tail)
{
const auto lnw = detail::line_width(loc, msg, tail...);
const std::string f(""); // at the 1st iteration, no prev_filename is given
return detail::format_location_rec(lnw, f, loc, msg, tail...);
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_SOURCE_LOCATION_FWD_HPP

View File

@@ -0,0 +1,419 @@
#ifndef TOML11_SYNTAX_FWD_HPP
#define TOML11_SYNTAX_FWD_HPP
#include "../scanner.hpp"
#include "../spec.hpp"
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
namespace syntax
{
using char_type = location::char_type;
// ===========================================================================
// UTF-8
// avoid redundant representation and out-of-unicode sequence
character_in_range const& utf8_1byte (const spec&);
sequence const& utf8_2bytes(const spec&);
sequence const& utf8_3bytes(const spec&);
sequence const& utf8_4bytes(const spec&);
class non_ascii final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit non_ascii(const spec& s) noexcept
: utf8_2B_(utf8_2bytes(s)),
utf8_3B_(utf8_3bytes(s)),
utf8_4B_(utf8_4bytes(s))
{}
~non_ascii() override = default;
region scan(location& loc) const override
{
{
const auto reg = utf8_2B_.scan(loc);
if(reg.is_ok()) {return reg;}
}
{
const auto reg = utf8_3B_.scan(loc);
if(reg.is_ok()) {return reg;}
}
{
const auto reg = utf8_4B_.scan(loc);
if(reg.is_ok()) {return reg;}
}
return region{};
}
std::string expected_chars(location&) const override
{
return "non-ascii utf-8 bytes";
}
scanner_base* clone() const override
{
return new non_ascii(*this);
}
std::string name() const override
{
return "non_ascii";
}
private:
sequence utf8_2B_;
sequence utf8_3B_;
sequence utf8_4B_;
};
// ===========================================================================
// Whitespace
character_either const& wschar(const spec&);
repeat_at_least const& ws(const spec& s);
// ===========================================================================
// Newline
either const& newline(const spec&);
// ===========================================================================
// Comments
either const& allowed_comment_char(const spec& s);
// XXX Note that it does not take newline
sequence const& comment(const spec& s);
// ===========================================================================
// Boolean
either const& boolean(const spec&);
// ===========================================================================
// Integer
class digit final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit digit(const spec&) noexcept
: scanner_(char_type('0'), char_type('9'))
{}
~digit() override = default;
region scan(location& loc) const override
{
return scanner_.scan(loc);
}
std::string expected_chars(location&) const override
{
return "digit [0-9]";
}
scanner_base* clone() const override
{
return new digit(*this);
}
std::string name() const override
{
return "digit";
}
private:
character_in_range scanner_;
};
class alpha final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit alpha(const spec&) noexcept
: lowercase_(char_type('a'), char_type('z')),
uppercase_(char_type('A'), char_type('Z'))
{}
~alpha() override = default;
region scan(location& loc) const override
{
{
const auto reg = lowercase_.scan(loc);
if(reg.is_ok()) {return reg;}
}
{
const auto reg = uppercase_.scan(loc);
if(reg.is_ok()) {return reg;}
}
return region{};
}
std::string expected_chars(location&) const override
{
return "alpha [a-zA-Z]";
}
scanner_base* clone() const override
{
return new alpha(*this);
}
std::string name() const override
{
return "alpha";
}
private:
character_in_range lowercase_;
character_in_range uppercase_;
};
class hexdig final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit hexdig(const spec& s) noexcept
: digit_(s),
lowercase_(char_type('a'), char_type('f')),
uppercase_(char_type('A'), char_type('F'))
{}
~hexdig() override = default;
region scan(location& loc) const override
{
{
const auto reg = digit_.scan(loc);
if(reg.is_ok()) {return reg;}
}
{
const auto reg = lowercase_.scan(loc);
if(reg.is_ok()) {return reg;}
}
{
const auto reg = uppercase_.scan(loc);
if(reg.is_ok()) {return reg;}
}
return region{};
}
std::string expected_chars(location&) const override
{
return "hex [0-9a-fA-F]";
}
scanner_base* clone() const override
{
return new hexdig(*this);
}
std::string name() const override
{
return "hexdig";
}
private:
digit digit_;
character_in_range lowercase_;
character_in_range uppercase_;
};
sequence const& num_suffix(const spec& s);
sequence const& dec_int(const spec& s);
sequence const& hex_int(const spec& s);
sequence const& oct_int(const spec&);
sequence const& bin_int(const spec&);
either const& integer(const spec& s);
// ===========================================================================
// Floating
sequence const& zero_prefixable_int(const spec& s);
sequence const& fractional_part(const spec& s);
sequence const& exponent_part(const spec& s);
sequence const& hex_floating(const spec& s);
either const& floating(const spec& s);
// ===========================================================================
// Datetime
sequence const& local_date(const spec& s);
sequence const& local_time(const spec& s);
either const& time_offset(const spec& s);
sequence const& full_time(const spec& s);
character_either const& time_delim(const spec&);
sequence const& local_datetime(const spec& s);
sequence const& offset_datetime(const spec& s);
// ===========================================================================
// String
sequence const& escaped_x2(const spec& s);
sequence const& escaped_u4(const spec& s);
sequence const& escaped_U8(const spec& s);
sequence const& escaped (const spec& s);
either const& basic_char (const spec& s);
sequence const& basic_string(const spec& s);
// ---------------------------------------------------------------------------
// multiline string
sequence const& escaped_newline(const spec& s);
sequence const& ml_basic_string(const spec& s);
// ---------------------------------------------------------------------------
// literal string
either const& literal_char(const spec& s);
sequence const& literal_string(const spec& s);
sequence const& ml_literal_string(const spec& s);
either const& string(const spec& s);
// ===========================================================================
// Keys
// to keep `expected_chars` simple
class non_ascii_key_char final : public scanner_base
{
public:
using char_type = location::char_type;
private:
using in_range = character_in_range; // make definition short
public:
explicit non_ascii_key_char(const spec& s) noexcept;
~non_ascii_key_char() override = default;
region scan(location& loc) const override;
std::string expected_chars(location&) const override
{
return "bare key non-ASCII script";
}
scanner_base* clone() const override
{
return new non_ascii_key_char(*this);
}
std::string name() const override
{
return "non-ASCII bare key";
}
private:
std::uint32_t read_utf8(location& loc) const;
};
repeat_at_least const& unquoted_key(const spec& s);
either const& quoted_key(const spec& s);
either const& simple_key(const spec& s);
sequence const& dot_sep(const spec& s);
sequence const& dotted_key(const spec& s);
class key final : public scanner_base
{
public:
using char_type = location::char_type;
public:
explicit key(const spec& s) noexcept
: dotted_(dotted_key(s)),
simple_(simple_key(s))
{}
~key() override = default;
region scan(location& loc) const override
{
{
const auto reg = dotted_.scan(loc);
if(reg.is_ok()) {return reg;}
}
{
const auto reg = simple_.scan(loc);
if(reg.is_ok()) {return reg;}
}
return region{};
}
std::string expected_chars(location&) const override
{
return "basic key([a-zA-Z0-9_-]) or quoted key(\" or ')";
}
scanner_base* clone() const override
{
return new key(*this);
}
std::string name() const override
{
return "key";
}
private:
sequence dotted_;
either simple_;
};
sequence const& keyval_sep(const spec& s);
// ===========================================================================
// Table key
sequence const& std_table(const spec& s);
sequence const& array_table(const spec& s);
// ===========================================================================
// extension: null
literal const& null_value(const spec&);
} // namespace syntax
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_SYNTAX_FWD_HPP

View File

@@ -0,0 +1,121 @@
#ifndef TOML11_VALUE_T_FWD_HPP
#define TOML11_VALUE_T_FWD_HPP
#include "../compat.hpp"
#include "../format.hpp"
#include "../version.hpp"
#include <iosfwd>
#include <string>
#include <type_traits>
#include <cstdint>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// forward decl
template<typename TypeConfig>
class basic_value;
// ----------------------------------------------------------------------------
// enum representing toml types
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
};
std::ostream& operator<<(std::ostream& os, value_t t);
std::string to_string(value_t t);
// ----------------------------------------------------------------------------
// meta functions for internal use
namespace detail
{
template<value_t V>
using value_t_constant = std::integral_constant<value_t, V>;
template<typename T, typename Value>
struct type_to_enum : value_t_constant<value_t::empty> {};
template<typename V> struct type_to_enum<typename V::boolean_type , V> : value_t_constant<value_t::boolean > {};
template<typename V> struct type_to_enum<typename V::integer_type , V> : value_t_constant<value_t::integer > {};
template<typename V> struct type_to_enum<typename V::floating_type , V> : value_t_constant<value_t::floating > {};
template<typename V> struct type_to_enum<typename V::string_type , V> : value_t_constant<value_t::string > {};
template<typename V> struct type_to_enum<typename V::offset_datetime_type, V> : value_t_constant<value_t::offset_datetime> {};
template<typename V> struct type_to_enum<typename V::local_datetime_type , V> : value_t_constant<value_t::local_datetime > {};
template<typename V> struct type_to_enum<typename V::local_date_type , V> : value_t_constant<value_t::local_date > {};
template<typename V> struct type_to_enum<typename V::local_time_type , V> : value_t_constant<value_t::local_time > {};
template<typename V> struct type_to_enum<typename V::array_type , V> : value_t_constant<value_t::array > {};
template<typename V> struct type_to_enum<typename V::table_type , V> : value_t_constant<value_t::table > {};
template<value_t V, typename Value>
struct enum_to_type { using type = void; };
template<typename V> struct enum_to_type<value_t::boolean , V> { using type = typename V::boolean_type ; };
template<typename V> struct enum_to_type<value_t::integer , V> { using type = typename V::integer_type ; };
template<typename V> struct enum_to_type<value_t::floating , V> { using type = typename V::floating_type ; };
template<typename V> struct enum_to_type<value_t::string , V> { using type = typename V::string_type ; };
template<typename V> struct enum_to_type<value_t::offset_datetime, V> { using type = typename V::offset_datetime_type; };
template<typename V> struct enum_to_type<value_t::local_datetime , V> { using type = typename V::local_datetime_type ; };
template<typename V> struct enum_to_type<value_t::local_date , V> { using type = typename V::local_date_type ; };
template<typename V> struct enum_to_type<value_t::local_time , V> { using type = typename V::local_time_type ; };
template<typename V> struct enum_to_type<value_t::array , V> { using type = typename V::array_type ; };
template<typename V> struct enum_to_type<value_t::table , V> { using type = typename V::table_type ; };
template<value_t V, typename Value>
using enum_to_type_t = typename enum_to_type<V, Value>::type;
template<value_t V>
struct enum_to_fmt_type { using type = void; };
template<> struct enum_to_fmt_type<value_t::boolean > { using type = boolean_format_info ; };
template<> struct enum_to_fmt_type<value_t::integer > { using type = integer_format_info ; };
template<> struct enum_to_fmt_type<value_t::floating > { using type = floating_format_info ; };
template<> struct enum_to_fmt_type<value_t::string > { using type = string_format_info ; };
template<> struct enum_to_fmt_type<value_t::offset_datetime> { using type = offset_datetime_format_info; };
template<> struct enum_to_fmt_type<value_t::local_datetime > { using type = local_datetime_format_info ; };
template<> struct enum_to_fmt_type<value_t::local_date > { using type = local_date_format_info ; };
template<> struct enum_to_fmt_type<value_t::local_time > { using type = local_time_format_info ; };
template<> struct enum_to_fmt_type<value_t::array > { using type = array_format_info ; };
template<> struct enum_to_fmt_type<value_t::table > { using type = table_format_info ; };
template<value_t V>
using enum_to_fmt_type_t = typename enum_to_fmt_type<V>::type;
template<typename T, typename Value>
struct is_exact_toml_type0 : cxx::disjunction<
std::is_same<T, typename Value::boolean_type >,
std::is_same<T, typename Value::integer_type >,
std::is_same<T, typename Value::floating_type >,
std::is_same<T, typename Value::string_type >,
std::is_same<T, typename Value::offset_datetime_type>,
std::is_same<T, typename Value::local_datetime_type >,
std::is_same<T, typename Value::local_date_type >,
std::is_same<T, typename Value::local_time_type >,
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: is_exact_toml_type0<cxx::remove_cvref_t<T>, V> {};
template<typename T, typename V> struct is_not_toml_type : cxx::negation<is_exact_toml_type<T, V>> {};
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_VALUE_T_FWD_HPP

655
include/toml11/get.hpp Normal file
View File

@@ -0,0 +1,655 @@
#ifndef TOML11_GET_HPP
#define TOML11_GET_HPP
#include <algorithm>
#include "from.hpp"
#include "types.hpp"
#include "value.hpp"
#include "version.hpp"
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif // string_view
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// ============================================================================
// T is toml::value; identity transformation.
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>&
get(basic_value<TC>& v)
{
return v;
}
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T> const&
get(const basic_value<TC>& v)
{
return v;
}
template<typename T, typename TC>
cxx::enable_if_t<std::is_same<T, basic_value<TC>>::value, T>
get(basic_value<TC>&& v)
{
return basic_value<TC>(std::move(v));
}
// ============================================================================
// exact toml::* type
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> &
get(basic_value<TC>& v)
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
return detail::getter<TC, ty>::get(v);
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const&
get(const basic_value<TC>& v)
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
return detail::getter<TC, ty>::get(v);
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<T, basic_value<TC>>::value, T>
get(basic_value<TC>&& v)
{
constexpr auto ty = detail::type_to_enum<T, basic_value<TC>>::value;
return detail::getter<TC, ty>::get(std::move(v));
}
// ============================================================================
// T is toml::basic_value<U>
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_basic_value<T>,
cxx::negation<std::is_same<T, basic_value<TC>>>
>::value, T>
get(basic_value<TC> v)
{
return T(std::move(v));
}
// ============================================================================
// integer convertible from toml::value::integer_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_integral<T>,
cxx::negation<std::is_same<T, bool>>,
detail::is_not_toml_type<T, basic_value<TC>>,
cxx::negation<detail::has_from_toml_method<T, TC>>,
cxx::negation<detail::has_specialized_from<T>>
>::value, T>
get(const basic_value<TC>& v)
{
return static_cast<T>(v.as_integer());
}
// ============================================================================
// floating point convertible from toml::value::floating_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_floating_point<T>,
detail::is_not_toml_type<T, basic_value<TC>>,
cxx::negation<detail::has_from_toml_method<T, TC>>,
cxx::negation<detail::has_specialized_from<T>>
>::value, T>
get(const basic_value<TC>& v)
{
return static_cast<T>(v.as_floating());
}
// ============================================================================
// std::string with different char/trait/allocator
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_not_toml_type<T, basic_value<TC>>,
detail::is_1byte_std_basic_string<T>
>::value, T>
get(const basic_value<TC>& v)
{
return detail::string_conv<cxx::remove_cvref_t<T>>(v.as_string());
}
// ============================================================================
// std::string_view
#if defined(TOML11_HAS_STRING_VIEW)
template<typename T, typename TC>
cxx::enable_if_t<detail::is_string_view_of<T, typename basic_value<TC>::string_type>::value, T>
get(const basic_value<TC>& v)
{
return T(v.as_string());
}
#endif // string_view
// ============================================================================
// std::chrono::duration from toml::local_time
template<typename T, typename TC>
cxx::enable_if_t<detail::is_chrono_duration<T>::value, T>
get(const basic_value<TC>& v)
{
return std::chrono::duration_cast<T>(
std::chrono::nanoseconds(v.as_local_time()));
}
// ============================================================================
// std::chrono::system_clock::time_point from toml::datetime variants
template<typename T, typename TC>
cxx::enable_if_t<
std::is_same<std::chrono::system_clock::time_point, T>::value, T>
get(const basic_value<TC>& v)
{
switch(v.type())
{
case value_t::local_date:
{
return std::chrono::system_clock::time_point(v.as_local_date());
}
case value_t::local_datetime:
{
return std::chrono::system_clock::time_point(v.as_local_datetime());
}
case value_t::offset_datetime:
{
return std::chrono::system_clock::time_point(v.as_offset_datetime());
}
default:
{
const auto loc = v.location();
throw type_error(format_error("toml::get: "
"bad_cast to std::chrono::system_clock::time_point", loc,
"the actual type is " + to_string(v.type())), loc);
}
}
}
// ============================================================================
// forward declaration to use this recursively. ignore this and go ahead.
// array-like (w/ push_back)
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_container<T>, // T is a container
detail::has_push_back_method<T>, // .push_back() works
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT>
#if defined(TOML11_HAS_STRING_VIEW)
cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT>
#endif
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, T>
get(const basic_value<TC>&);
// std::array
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_array<T>::value, T>
get(const basic_value<TC>&);
// std::forward_list
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_forward_list<T>::value, T>
get(const basic_value<TC>&);
// std::pair<T1, T2>
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_pair<T>::value, T>
get(const basic_value<TC>&);
// std::tuple<T1, T2, ...>
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_tuple<T>::value, T>
get(const basic_value<TC>&);
// std::map<key, value> (key is convertible from toml::value::key_type)
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_map<T>, // T is map
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
std::is_convertible<typename basic_value<TC>::key_type,
typename T::key_type>, // keys are convertible
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, T>
get(const basic_value<TC>& v);
// std::map<key, value> (key is not convertible from toml::value::key_type, but
// is a std::basic_string)
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_map<T>, // T is map
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
cxx::negation<std::is_convertible<typename basic_value<TC>::key_type,
typename T::key_type>>, // keys are NOT convertible
detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, T>
get(const basic_value<TC>& v);
// toml::from<T>::from_toml(v)
template<typename T, typename TC>
cxx::enable_if_t<detail::has_specialized_from<T>::value, T>
get(const basic_value<TC>&);
// has T.from_toml(v) but no from<T>
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::has_from_toml_method<T, TC>, // has T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
std::is_default_constructible<T> // T{} works
>::value, T>
get(const basic_value<TC>&);
// T(const toml::value&) and T is not toml::basic_value,
// and it does not have `from<T>` nor `from_toml`.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&)
cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself
cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml()
cxx::negation<detail::has_specialized_from<T>> // no toml::from<T>
>::value, T>
get(const basic_value<TC>&);
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_container<T>, // T is a container
detail::has_push_back_method<T>, // .push_back() works
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::array
cxx::negation<detail::is_std_basic_string<T>>, // but not std::basic_string<CharT>
#if defined(TOML11_HAS_STRING_VIEW)
cxx::negation<detail::is_std_basic_string_view<T>>, // but not std::basic_string_view<CharT>
#endif
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, T>
get(const basic_value<TC>& v)
{
using value_type = typename T::value_type;
const auto& a = v.as_array();
T container;
detail::try_reserve(container, a.size()); // if T has .reserve(), call it
for(const auto& elem : a)
{
container.push_back(get<value_type>(elem));
}
return container;
}
// ============================================================================
// std::array
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_array<T>::value, T>
get(const basic_value<TC>& v)
{
using value_type = typename T::value_type;
const auto& a = v.as_array();
T container;
if(a.size() != container.size())
{
const auto loc = v.location();
throw std::out_of_range(format_error("toml::get: while converting to an array: "
" array size is " + std::to_string(container.size()) +
" but there are " + std::to_string(a.size()) + " elements in toml array.",
loc, "here"));
}
for(std::size_t i=0; i<a.size(); ++i)
{
container.at(i) = ::toml::get<value_type>(a.at(i));
}
return container;
}
// ============================================================================
// std::forward_list
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_forward_list<T>::value, T>
get(const basic_value<TC>& v)
{
using value_type = typename T::value_type;
T container;
for(const auto& elem : v.as_array())
{
container.push_front(get<value_type>(elem));
}
container.reverse();
return container;
}
// ============================================================================
// std::pair
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_pair<T>::value, T>
get(const basic_value<TC>& v)
{
using first_type = typename T::first_type;
using second_type = typename T::second_type;
const auto& ar = v.as_array();
if(ar.size() != 2)
{
const auto loc = v.location();
throw std::out_of_range(format_error("toml::get: while converting std::pair: "
" but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.",
loc, "here"));
}
return std::make_pair(::toml::get<first_type >(ar.at(0)),
::toml::get<second_type>(ar.at(1)));
}
// ============================================================================
// std::tuple.
namespace detail
{
template<typename T, typename Array, std::size_t ... I>
T get_tuple_impl(const Array& a, cxx::index_sequence<I...>)
{
return std::make_tuple(
::toml::get<typename std::tuple_element<I, T>::type>(a.at(I))...);
}
} // detail
template<typename T, typename TC>
cxx::enable_if_t<detail::is_std_tuple<T>::value, T>
get(const basic_value<TC>& v)
{
const auto& ar = v.as_array();
if(ar.size() != std::tuple_size<T>::value)
{
const auto loc = v.location();
throw std::out_of_range(format_error("toml::get: while converting std::tuple: "
" there are " + std::to_string(ar.size()) + " > " +
std::to_string(std::tuple_size<T>::value) + " elements in toml array.",
loc, "here"));
}
return detail::get_tuple_impl<T>(ar,
cxx::make_index_sequence<std::tuple_size<T>::value>{});
}
// ============================================================================
// std::unordered_set
template<typename T, typename TC>
cxx::enable_if_t<toml::detail::is_unordered_set<T>::value, T>
get(const basic_value<TC>& v)
{
using value_type = typename T::value_type;
const auto& a = v.as_array();
T container;
for (const auto& elem : a)
{
container.insert(get<value_type>(elem));
}
return container;
}
// ============================================================================
// map-like types; most likely STL map, like std::map or std::unordered_map.
// key is convertible from toml::value::key_type
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_map<T>, // T is map
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
std::is_convertible<typename basic_value<TC>::key_type,
typename T::key_type>, // keys are convertible
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, T>
get(const basic_value<TC>& v)
{
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
static_assert(
std::is_convertible<typename basic_value<TC>::key_type, key_type>::value,
"toml::get only supports map type of which key_type is "
"convertible from toml::basic_value::key_type.");
T m;
for(const auto& kv : v.as_table())
{
m.emplace(key_type(kv.first), get<mapped_type>(kv.second));
}
return m;
}
// key is NOT convertible from toml::value::key_type but std::basic_string
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::is_map<T>, // T is map
detail::is_not_toml_type<T, basic_value<TC>>, // but not toml::table
cxx::negation<std::is_convertible<typename basic_value<TC>::key_type,
typename T::key_type>>, // keys are NOT convertible
detail::is_1byte_std_basic_string<typename T::key_type>, // is std::basic_string
cxx::negation<detail::has_from_toml_method<T, TC>>, // no T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
cxx::negation<std::is_constructible<T, const basic_value<TC>&>>
>::value, T>
get(const basic_value<TC>& v)
{
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
T m;
for(const auto& kv : v.as_table())
{
m.emplace(detail::string_conv<key_type>(kv.first), get<mapped_type>(kv.second));
}
return m;
}
// ============================================================================
// user-defined, but convertible types.
// toml::from<T>
template<typename T, typename TC>
cxx::enable_if_t<detail::has_specialized_from<T>::value, T>
get(const basic_value<TC>& v)
{
return ::toml::from<T>::from_toml(v);
}
// has T.from_toml(v) but no from<T>
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
detail::has_from_toml_method<T, TC>, // has T.from_toml()
cxx::negation<detail::has_specialized_from<T>>, // no toml::from<T>
std::is_default_constructible<T> // T{} works
>::value, T>
get(const basic_value<TC>& v)
{
T ud;
ud.from_toml(v);
return ud;
}
// T(const toml::value&) and T is not toml::basic_value,
// and it does not have `from<T>` nor `from_toml`.
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
std::is_constructible<T, const basic_value<TC>&>, // has T(const basic_value&)
cxx::negation<detail::is_basic_value<T>>, // but not basic_value itself
cxx::negation<detail::has_from_toml_method<T, TC>>, // no .from_toml()
cxx::negation<detail::has_specialized_from<T>> // no toml::from<T>
>::value, T>
get(const basic_value<TC>& v)
{
return T(v);
}
// ============================================================================
// get_or(value, fallback)
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>> const&
get_or(const basic_value<TC>& v, const basic_value<TC>&)
{
return v;
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>&
get_or(basic_value<TC>& v, basic_value<TC>&)
{
return v;
}
template<typename TC>
cxx::enable_if_t<detail::is_type_config<TC>::value, basic_value<TC>>
get_or(basic_value<TC>&& v, basic_value<TC>&&)
{
return v;
}
// ----------------------------------------------------------------------------
// specialization for the exact toml types (return type becomes lvalue ref)
template<typename T, typename TC>
cxx::enable_if_t<
detail::is_exact_toml_type<T, basic_value<TC>>::value, T> const&
get_or(const basic_value<TC>& v, const T& opt) noexcept
{
try
{
return get<cxx::remove_cvref_t<T>>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_const<T>>,
detail::is_exact_toml_type<T, basic_value<TC>>
>::value, T>&
get_or(basic_value<TC>& v, T& opt) noexcept
{
try
{
return get<cxx::remove_cvref_t<T>>(v);
}
catch(...)
{
return opt;
}
}
template<typename T, typename TC>
cxx::enable_if_t<detail::is_exact_toml_type<cxx::remove_cvref_t<T>,
basic_value<TC>>::value, cxx::remove_cvref_t<T>>
get_or(basic_value<TC>&& v, T&& opt) noexcept
{
try
{
return get<cxx::remove_cvref_t<T>>(std::move(v));
}
catch(...)
{
return cxx::remove_cvref_t<T>(std::forward<T>(opt));
}
}
// ----------------------------------------------------------------------------
// specialization for string literal
// template<std::size_t N, typename TC>
// typename basic_value<TC>::string_type
// get_or(const basic_value<TC>& v,
// const typename basic_value<TC>::string_type::value_type (&opt)[N])
// {
// try
// {
// return v.as_string();
// }
// catch(...)
// {
// return typename basic_value<TC>::string_type(opt);
// }
// }
//
// The above only matches to the literal, like `get_or(v, "foo");` but not
// ```cpp
// const auto opt = "foo";
// const auto str = get_or(v, opt);
// ```
// . And the latter causes an error.
// To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to
// a character here.
template<typename TC>
typename basic_value<TC>::string_type
get_or(const basic_value<TC>& v,
const typename basic_value<TC>::string_type::value_type* opt)
{
try
{
return v.as_string();
}
catch(...)
{
return typename basic_value<TC>::string_type(opt);
}
}
// ----------------------------------------------------------------------------
// others (require type conversion and return type cannot be lvalue reference)
template<typename T, typename TC>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<detail::is_basic_value<T>>,
cxx::negation<detail::is_exact_toml_type<T, basic_value<TC>>>,
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, typename basic_value<TC>::string_type::value_type const*>>
>::value, cxx::remove_cvref_t<T>>
get_or(const basic_value<TC>& v, T&& opt)
{
try
{
return get<cxx::remove_cvref_t<T>>(v);
}
catch(...)
{
return cxx::remove_cvref_t<T>(std::forward<T>(opt));
}
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_GET_HPP

View File

@@ -0,0 +1,79 @@
#ifndef TOML11_COLOR_IMPL_HPP
#define TOML11_COLOR_IMPL_HPP
#include "../fwd/color_fwd.hpp"
#include "../version.hpp"
#include <ostream>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace color
{
// put ANSI escape sequence to ostream
inline namespace ansi
{
TOML11_INLINE std::ostream& reset(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[00m";}
return os;
}
TOML11_INLINE std::ostream& bold(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[01m";}
return os;
}
TOML11_INLINE std::ostream& grey(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[30m";}
return os;
}
TOML11_INLINE std::ostream& gray(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[30m";}
return os;
}
TOML11_INLINE std::ostream& red(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[31m";}
return os;
}
TOML11_INLINE std::ostream& green(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[32m";}
return os;
}
TOML11_INLINE std::ostream& yellow(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[33m";}
return os;
}
TOML11_INLINE std::ostream& blue(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[34m";}
return os;
}
TOML11_INLINE std::ostream& magenta(std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[35m";}
return os;
}
TOML11_INLINE std::ostream& cyan (std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[36m";}
return os;
}
TOML11_INLINE std::ostream& white (std::ostream& os)
{
if(detail::color_status().should_color()) {os << "\033[37m";}
return os;
}
} // ansi
} // color
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_COLOR_IMPL_HPP

View File

@@ -0,0 +1,50 @@
#ifndef TOML11_COMMENTS_IMPL_HPP
#define TOML11_COMMENTS_IMPL_HPP
#include "../fwd/comments_fwd.hpp" // IWYU pragma: keep
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
TOML11_INLINE bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;}
TOML11_INLINE bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;}
TOML11_INLINE bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;}
TOML11_INLINE bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;}
TOML11_INLINE bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;}
TOML11_INLINE bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;}
TOML11_INLINE void swap(preserve_comments& lhs, preserve_comments& rhs)
{
lhs.swap(rhs);
return;
}
TOML11_INLINE void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
{
lhs.comments.swap(rhs);
return;
}
TOML11_INLINE void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
{
lhs.swap(rhs.comments);
return;
}
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const preserve_comments& com)
{
for(const auto& c : com)
{
if(c.front() != '#')
{
os << '#';
}
os << c << '\n';
}
return os;
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml11
#endif // TOML11_COMMENTS_IMPL_HPP

View File

@@ -0,0 +1,521 @@
#ifndef TOML11_DATETIME_IMPL_HPP
#define TOML11_DATETIME_IMPL_HPP
#include "../fwd/datetime_fwd.hpp"
#include "../version.hpp"
#include <array>
#include <iomanip>
#include <ostream>
#include <sstream>
#include <tuple>
#include <cstdlib>
#include <ctime>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// 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)
TOML11_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;
}
TOML11_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)
TOML11_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;
}
TOML11_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
TOML11_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;
}
TOML11_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
// ----------------------------------------------------------------------------
TOML11_INLINE local_date::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);
}
TOML11_INLINE local_date::local_date(const std::time_t t)
: local_date{std::chrono::system_clock::from_time_t(t)}
{}
TOML11_INLINE local_date::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));
}
TOML11_INLINE local_date::operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
TOML11_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);
}
TOML11_INLINE bool operator!=(const local_date& lhs, const local_date& rhs)
{
return !(lhs == rhs);
}
TOML11_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);
}
TOML11_INLINE bool operator<=(const local_date& lhs, const local_date& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
TOML11_INLINE bool operator> (const local_date& lhs, const local_date& rhs)
{
return !(lhs <= rhs);
}
TOML11_INLINE bool operator>=(const local_date& lhs, const local_date& rhs)
{
return !(lhs < rhs);
}
TOML11_INLINE std::ostream& operator<<(std::ostream& 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;
}
TOML11_INLINE std::string to_string(const local_date& date)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << date;
return oss.str();
}
// -----------------------------------------------------------------------------
TOML11_INLINE local_time::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);
}
TOML11_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);
}
TOML11_INLINE bool operator!=(const local_time& lhs, const local_time& rhs)
{
return !(lhs == rhs);
}
TOML11_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);
}
TOML11_INLINE bool operator<=(const local_time& lhs, const local_time& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
TOML11_INLINE bool operator> (const local_time& lhs, const local_time& rhs)
{
return !(lhs <= rhs);
}
TOML11_INLINE bool operator>=(const local_time& lhs, const local_time& rhs)
{
return !(lhs < rhs);
}
TOML11_INLINE std::ostream& operator<<(std::ostream& 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;
}
TOML11_INLINE std::string to_string(const local_time& time)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << time;
return oss.str();
}
// ----------------------------------------------------------------------------
TOML11_INLINE time_offset::operator std::chrono::minutes() const
{
return std::chrono::minutes(this->minute) +
std::chrono::hours(this->hour);
}
TOML11_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);
}
TOML11_INLINE bool operator!=(const time_offset& lhs, const time_offset& rhs)
{
return !(lhs == rhs);
}
TOML11_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);
}
TOML11_INLINE bool operator<=(const time_offset& lhs, const time_offset& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
TOML11_INLINE bool operator> (const time_offset& lhs, const time_offset& rhs)
{
return !(lhs <= rhs);
}
TOML11_INLINE bool operator>=(const time_offset& lhs, const time_offset& rhs)
{
return !(lhs < rhs);
}
TOML11_INLINE std::ostream& operator<<(std::ostream& 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;
}
TOML11_INLINE std::string to_string(const time_offset& offset)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << offset;
return oss.str();
}
// -----------------------------------------------------------------------------
TOML11_INLINE local_datetime::local_datetime(const std::chrono::system_clock::time_point& tp)
{
const auto t = std::chrono::system_clock::to_time_t(tp);
std::tm ltime = detail::localtime_s(&t);
this->date = local_date(ltime);
this->time = local_time(ltime);
// std::tm lacks subsecond information, so diff between tp and tm
// can be used to get millisecond & microsecond information.
const auto t_diff = tp -
std::chrono::system_clock::from_time_t(std::mktime(&ltime));
this->time.millisecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
this->time.microsecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
this->time.nanosecond = static_cast<std::uint16_t>(
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
}
TOML11_INLINE local_datetime::local_datetime(const std::time_t t)
: local_datetime{std::chrono::system_clock::from_time_t(t)}
{}
TOML11_INLINE local_datetime::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;
}
TOML11_INLINE local_datetime::operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
TOML11_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);
}
TOML11_INLINE bool operator!=(const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs == rhs);
}
TOML11_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);
}
TOML11_INLINE bool operator<=(const local_datetime& lhs, const local_datetime& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
TOML11_INLINE bool operator> (const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs <= rhs);
}
TOML11_INLINE bool operator>=(const local_datetime& lhs, const local_datetime& rhs)
{
return !(lhs < rhs);
}
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
{
os << dt.date << 'T' << dt.time;
return os;
}
TOML11_INLINE std::string to_string(const local_datetime& dt)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << dt;
return oss.str();
}
// -----------------------------------------------------------------------------
TOML11_INLINE offset_datetime::offset_datetime(const local_datetime& ld)
: date{ld.date}, time{ld.time}, offset{get_local_offset(nullptr)}
// use the current local timezone offset
{}
TOML11_INLINE offset_datetime::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);
}
TOML11_INLINE offset_datetime::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);
}
TOML11_INLINE offset_datetime::offset_datetime(const std::tm& t)
: offset{0, 0} // assume gmtime
{
this->date = local_date(t);
this->time = local_time(t);
}
TOML11_INLINE offset_datetime::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;
}
TOML11_INLINE offset_datetime::operator std::time_t() const
{
return std::chrono::system_clock::to_time_t(
std::chrono::system_clock::time_point(*this));
}
TOML11_INLINE time_offset offset_datetime::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);
}
TOML11_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);
}
TOML11_INLINE bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs == rhs);
}
TOML11_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);
}
TOML11_INLINE bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return (lhs < rhs) || (lhs == rhs);
}
TOML11_INLINE bool operator> (const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs <= rhs);
}
TOML11_INLINE bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs)
{
return !(lhs < rhs);
}
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
{
os << dt.date << 'T' << dt.time << dt.offset;
return os;
}
TOML11_INLINE std::string to_string(const offset_datetime& dt)
{
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss << dt;
return oss.str();
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_DATETIME_IMPL_HPP

View File

@@ -0,0 +1,79 @@
#ifndef TOML11_ERROR_INFO_IMPL_HPP
#define TOML11_ERROR_INFO_IMPL_HPP
#include "../fwd/error_info_fwd.hpp"
#include "../fwd/color_fwd.hpp"
#include "../version.hpp"
#include <sstream>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
TOML11_INLINE std::string format_error(const std::string& errkind, const error_info& err)
{
std::string errmsg;
if( ! errkind.empty())
{
errmsg = errkind;
errmsg += ' ';
}
errmsg += err.title();
errmsg += '\n';
const auto lnw = [&err]() {
std::size_t width = 0;
for(const auto& l : err.locations())
{
width = (std::max)(detail::integer_width_base10(l.first.last_line_number()), width);
}
return width;
}();
bool first = true;
std::string prev_fname;
for(const auto& lm : err.locations())
{
if( ! first)
{
std::ostringstream oss;
oss << detail::make_string(lnw + 1, ' ')
<< color::bold << color::blue << " |" << color::reset
<< color::bold << " ...\n" << color::reset;
oss << detail::make_string(lnw + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
errmsg += oss.str();
}
const auto& l = lm.first;
const auto& m = lm.second;
errmsg += detail::format_location_impl(lnw, prev_fname, l, m);
prev_fname = l.file_name();
first = false;
}
errmsg += err.suffix();
return errmsg;
}
TOML11_INLINE std::string format_error(const error_info& err)
{
std::ostringstream oss;
oss << color::red << color::bold << "[error]" << color::reset;
return format_error(oss.str(), err);
}
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const error_info& e)
{
os << format_error(e);
return os;
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_ERROR_INFO_IMPL_HPP

View File

@@ -0,0 +1,300 @@
#ifndef TOML11_FORMAT_IMPL_HPP
#define TOML11_FORMAT_IMPL_HPP
#include "../fwd/format_fwd.hpp"
#include "../version.hpp"
#include <ostream>
#include <sstream>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// toml types with serialization info
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const indent_char& c)
{
switch(c)
{
case indent_char::space: {os << "space" ; break;}
case indent_char::tab: {os << "tab" ; break;}
case indent_char::none: {os << "none" ; break;}
default:
{
os << "unknown indent char: " << static_cast<std::uint8_t>(c);
}
}
return os;
}
TOML11_INLINE std::string to_string(const indent_char c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
// ----------------------------------------------------------------------------
// boolean
// ----------------------------------------------------------------------------
// integer
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const integer_format f)
{
switch(f)
{
case integer_format::dec: {os << "dec"; break;}
case integer_format::bin: {os << "bin"; break;}
case integer_format::oct: {os << "oct"; break;}
case integer_format::hex: {os << "hex"; break;}
default:
{
os << "unknown integer_format: " << static_cast<std::uint8_t>(f);
break;
}
}
return os;
}
TOML11_INLINE std::string to_string(const integer_format c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
TOML11_INLINE bool operator==(const integer_format_info& lhs, const integer_format_info& rhs) noexcept
{
return lhs.fmt == rhs.fmt &&
lhs.uppercase == rhs.uppercase &&
lhs.width == rhs.width &&
lhs.spacer == rhs.spacer &&
lhs.suffix == rhs.suffix ;
}
TOML11_INLINE bool operator!=(const integer_format_info& lhs, const integer_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// floating
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const floating_format f)
{
switch(f)
{
case floating_format::defaultfloat: {os << "defaultfloat"; break;}
case floating_format::fixed : {os << "fixed" ; break;}
case floating_format::scientific : {os << "scientific" ; break;}
case floating_format::hex : {os << "hex" ; break;}
default:
{
os << "unknown floating_format: " << static_cast<std::uint8_t>(f);
break;
}
}
return os;
}
TOML11_INLINE std::string to_string(const floating_format c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
TOML11_INLINE bool operator==(const floating_format_info& lhs, const floating_format_info& rhs) noexcept
{
return lhs.fmt == rhs.fmt &&
lhs.prec == rhs.prec &&
lhs.suffix == rhs.suffix ;
}
TOML11_INLINE bool operator!=(const floating_format_info& lhs, const floating_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// string
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const string_format f)
{
switch(f)
{
case string_format::basic : {os << "basic" ; break;}
case string_format::literal : {os << "literal" ; break;}
case string_format::multiline_basic : {os << "multiline_basic" ; break;}
case string_format::multiline_literal: {os << "multiline_literal"; break;}
default:
{
os << "unknown string_format: " << static_cast<std::uint8_t>(f);
break;
}
}
return os;
}
TOML11_INLINE std::string to_string(const string_format c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
TOML11_INLINE bool operator==(const string_format_info& lhs, const string_format_info& rhs) noexcept
{
return lhs.fmt == rhs.fmt &&
lhs.start_with_newline == rhs.start_with_newline ;
}
TOML11_INLINE bool operator!=(const string_format_info& lhs, const string_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// datetime
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const datetime_delimiter_kind d)
{
switch(d)
{
case datetime_delimiter_kind::upper_T: { os << "upper_T, "; break; }
case datetime_delimiter_kind::lower_t: { os << "lower_t, "; break; }
case datetime_delimiter_kind::space: { os << "space, "; break; }
default:
{
os << "unknown datetime delimiter: " << static_cast<std::uint8_t>(d);
break;
}
}
return os;
}
TOML11_INLINE std::string to_string(const datetime_delimiter_kind c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
TOML11_INLINE bool operator==(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept
{
return lhs.delimiter == rhs.delimiter &&
lhs.has_seconds == rhs.has_seconds &&
lhs.subsecond_precision == rhs.subsecond_precision ;
}
TOML11_INLINE bool operator!=(const offset_datetime_format_info& lhs, const offset_datetime_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
TOML11_INLINE bool operator==(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept
{
return lhs.delimiter == rhs.delimiter &&
lhs.has_seconds == rhs.has_seconds &&
lhs.subsecond_precision == rhs.subsecond_precision ;
}
TOML11_INLINE bool operator!=(const local_datetime_format_info& lhs, const local_datetime_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
TOML11_INLINE bool operator==(const local_date_format_info&, const local_date_format_info&) noexcept
{
return true;
}
TOML11_INLINE bool operator!=(const local_date_format_info& lhs, const local_date_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
TOML11_INLINE bool operator==(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept
{
return lhs.has_seconds == rhs.has_seconds &&
lhs.subsecond_precision == rhs.subsecond_precision ;
}
TOML11_INLINE bool operator!=(const local_time_format_info& lhs, const local_time_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// array
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const array_format f)
{
switch(f)
{
case array_format::default_format : {os << "default_format" ; break;}
case array_format::oneline : {os << "oneline" ; break;}
case array_format::multiline : {os << "multiline" ; break;}
case array_format::array_of_tables: {os << "array_of_tables"; break;}
default:
{
os << "unknown array_format: " << static_cast<std::uint8_t>(f);
break;
}
}
return os;
}
TOML11_INLINE std::string to_string(const array_format c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
TOML11_INLINE bool operator==(const array_format_info& lhs, const array_format_info& rhs) noexcept
{
return lhs.fmt == rhs.fmt &&
lhs.indent_type == rhs.indent_type &&
lhs.body_indent == rhs.body_indent &&
lhs.closing_indent == rhs.closing_indent ;
}
TOML11_INLINE bool operator!=(const array_format_info& lhs, const array_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
// ----------------------------------------------------------------------------
// table
TOML11_INLINE std::ostream& operator<<(std::ostream& os, const table_format f)
{
switch(f)
{
case table_format::multiline : {os << "multiline" ; break;}
case table_format::oneline : {os << "oneline" ; break;}
case table_format::dotted : {os << "dotted" ; break;}
case table_format::multiline_oneline: {os << "multiline_oneline"; break;}
case table_format::implicit : {os << "implicit" ; break;}
default:
{
os << "unknown table_format: " << static_cast<std::uint8_t>(f);
break;
}
}
return os;
}
TOML11_INLINE std::string to_string(const table_format c)
{
std::ostringstream oss;
oss << c;
return oss.str();
}
TOML11_INLINE bool operator==(const table_format_info& lhs, const table_format_info& rhs) noexcept
{
return lhs.fmt == rhs.fmt &&
lhs.indent_type == rhs.indent_type &&
lhs.body_indent == rhs.body_indent &&
lhs.name_indent == rhs.name_indent &&
lhs.closing_indent == rhs.closing_indent ;
}
TOML11_INLINE bool operator!=(const table_format_info& lhs, const table_format_info& rhs) noexcept
{
return !(lhs == rhs);
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_FORMAT_IMPL_HPP

View File

@@ -0,0 +1,178 @@
#ifndef TOML11_LITERAL_IMPL_HPP
#define TOML11_LITERAL_IMPL_HPP
#include "../fwd/literal_fwd.hpp"
#include "../parser.hpp"
#include "../syntax.hpp"
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
// implementation
TOML11_INLINE ::toml::value literal_internal_impl(location loc)
{
const auto s = ::toml::spec::default_version();
context<type_config> ctx(s);
const auto front = loc;
// ------------------------------------------------------------------------
// check if it is a raw value.
// skip empty lines and comment lines
auto sp = skip_multiline_spacer(loc, ctx);
if(loc.eof())
{
::toml::value val;
if(sp.has_value())
{
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
{
val.comments().push_back(std::move(sp.value().comments.at(i)));
}
}
return val;
}
// to distinguish arrays and tables, first check it is a table or not.
//
// "[1,2,3]"_toml; // json: [1, 2, 3]
// "[table]"_toml; // json: {"table": {}}
// "[[1,2,3]]"_toml; // json: [[1, 2, 3]]
// "[[table]]"_toml; // json: {"table": [{}]}
//
// "[[1]]"_toml; // json: {"1": [{}]}
// "1 = [{}]"_toml; // json: {"1": [{}]}
// "[[1,]]"_toml; // json: [[1]]
// "[[1],]"_toml; // json: [[1]]
const auto val_start = loc;
const bool is_table_key = syntax::std_table(s).scan(loc).is_ok();
loc = val_start;
const bool is_aots_key = syntax::array_table(s).scan(loc).is_ok();
loc = val_start;
// 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)
{
auto data = parse_value(loc, ctx);
if(data.is_ok())
{
auto val = std::move(data.unwrap());
if(sp.has_value())
{
for(std::size_t i=0; i<sp.value().comments.size(); ++i)
{
val.comments().push_back(std::move(sp.value().comments.at(i)));
}
}
auto com_res = parse_comment_line(loc, ctx);
if(com_res.is_ok() && com_res.unwrap().has_value())
{
val.comments().push_back(com_res.unwrap().value());
}
return val;
}
}
// -------------------------------------------------------------------------
// Note that still it can be a table, because the literal might be something
// like the following.
// ```cpp
// // c++11 raw-string literal
// const auto val = R"(
// key = "value"
// int = 42
// )"_toml;
// ```
// It is a valid toml file.
// It should be parsed as if we parse a file with this content.
loc = front;
auto data = parse_file(loc, ctx);
if(data.is_ok())
{
return data.unwrap();
}
else // not a value && not a file. error.
{
std::string msg;
for(const auto& err : data.unwrap_err())
{
msg += format_error(err);
}
throw ::toml::syntax_error(std::move(msg), std::move(data.unwrap_err()));
}
}
} // detail
inline namespace literals
{
inline namespace toml_literals
{
TOML11_INLINE ::toml::value
operator""_toml(const char* str, std::size_t len)
{
if(len == 0)
{
return ::toml::value{};
}
::toml::detail::location::container_type c(len);
std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str),
reinterpret_cast<const ::toml::detail::location::char_type*>(str + len),
c.begin());
if( ! c.empty() && c.back())
{
c.push_back('\n'); // to make it easy to parse comment, we add newline
}
return literal_internal_impl(::toml::detail::location(
std::make_shared<const toml::detail::location::container_type>(std::move(c)),
"TOML literal encoded in a C++ code"));
}
#if defined(__cpp_char8_t)
# if __cpp_char8_t >= 201811L
# define TOML11_HAS_CHAR8_T 1
# endif
#endif
#if defined(TOML11_HAS_CHAR8_T)
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
TOML11_INLINE ::toml::value
operator"" _toml(const char8_t* str, std::size_t len)
{
if(len == 0)
{
return ::toml::value{};
}
::toml::detail::location::container_type c(len);
std::copy(reinterpret_cast<const ::toml::detail::location::char_type*>(str),
reinterpret_cast<const ::toml::detail::location::char_type*>(str + len),
c.begin());
if( ! c.empty() && c.back())
{
c.push_back('\n'); // to make it easy to parse comment, we add newline
}
return literal_internal_impl(::toml::detail::location(
std::make_shared<const toml::detail::location::container_type>(std::move(c)),
"TOML literal encoded in a C++ code"));
}
#endif
} // toml_literals
} // literals
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_LITERAL_IMPL_HPP

View File

@@ -0,0 +1,212 @@
#ifndef TOML11_LOCATION_IMPL_HPP
#define TOML11_LOCATION_IMPL_HPP
#include "../fwd/location_fwd.hpp"
#include "../utility.hpp"
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
TOML11_INLINE void location::advance(std::size_t n) noexcept
{
assert(this->is_ok());
if(this->location_ + n < this->source_->size())
{
this->advance_impl(n);
}
else
{
this->advance_impl(this->source_->size() - this->location_);
assert(this->location_ == this->source_->size());
}
}
TOML11_INLINE void location::retrace(/*restricted to n=1*/) noexcept
{
assert(this->is_ok());
if(this->location_ == 0)
{
this->location_ = 0;
this->line_number_ = 1;
this->column_number_ = 1;
}
else
{
this->retrace_impl();
}
}
TOML11_INLINE bool location::eof() const noexcept
{
assert(this->is_ok());
return this->location_ >= this->source_->size();
}
TOML11_INLINE location::char_type location::current() const
{
assert(this->is_ok());
if(this->eof()) {return '\0';}
assert(this->location_ < this->source_->size());
return this->source_->at(this->location_);
}
TOML11_INLINE location::char_type location::peek()
{
assert(this->is_ok());
if(this->location_ >= this->source_->size())
{
return '\0';
}
else
{
return this->source_->at(this->location_ + 1);
}
}
TOML11_INLINE std::string location::get_line() const
{
assert(this->is_ok());
const auto iter = std::next(this->source_->cbegin(), static_cast<difference_type>(this->location_));
const auto riter = cxx::make_reverse_iterator(iter);
const auto prev = std::find(riter, this->source_->crend(), char_type('\n'));
const auto next = std::find(iter, this->source_->cend(), char_type('\n'));
return make_string(std::next(prev.base()), next);
}
TOML11_INLINE std::size_t location::calc_column_number() const noexcept
{
assert(this->is_ok());
const auto iter = std::next(this->source_->cbegin(), static_cast<difference_type>(this->location_));
const auto riter = cxx::make_reverse_iterator(iter);
const auto prev = std::find(riter, this->source_->crend(), char_type('\n'));
assert(prev.base() <= iter);
return static_cast<std::size_t>(std::distance(prev.base(), iter) + 1); // 1-origin
}
TOML11_INLINE void location::advance_impl(const std::size_t n)
{
assert(this->is_ok());
assert(this->location_ + n <= this->source_->size());
auto iter = this->source_->cbegin();
std::advance(iter, static_cast<difference_type>(this->location_));
for(std::size_t i=0; i<n; ++i)
{
const auto c = *iter;
if(c == char_type('\n'))
{
this->line_number_ += 1;
this->column_number_ = 1;
}
else
{
this->column_number_ += 1;
}
iter++;
}
this->location_ += n;
return;
}
TOML11_INLINE void location::retrace_impl(/*n == 1*/)
{
assert(this->is_ok());
assert(this->location_ != 0);
this->location_ -= 1;
auto iter = this->source_->cbegin();
std::advance(iter, static_cast<difference_type>(this->location_));
if(*iter == '\n')
{
this->line_number_ -= 1;
this->column_number_ = this->calc_column_number();
}
return;
}
TOML11_INLINE bool operator==(const location& lhs, const location& rhs) noexcept
{
if( ! lhs.is_ok() || ! rhs.is_ok())
{
return (!lhs.is_ok()) && (!rhs.is_ok());
}
return lhs.source() == rhs.source() &&
lhs.source_name() == rhs.source_name() &&
lhs.get_location() == rhs.get_location();
}
TOML11_INLINE bool operator!=(const location& lhs, const location& rhs)
{
return !(lhs == rhs);
}
TOML11_INLINE location prev(const location& loc)
{
location p(loc);
p.retrace();
return p;
}
TOML11_INLINE location next(const location& loc)
{
location p(loc);
p.advance(1);
return p;
}
TOML11_INLINE location make_temporary_location(const std::string& str) noexcept
{
location::container_type cont(str.size());
std::transform(str.begin(), str.end(), cont.begin(),
[](const std::string::value_type& c) {
return cxx::bit_cast<location::char_type>(c);
});
return location(std::make_shared<const location::container_type>(
std::move(cont)), "internal temporary");
}
TOML11_INLINE result<location, none_t>
find(const location& first, const location& last, const location::char_type val)
{
return find_if(first, last, [val](const location::char_type c) {
return c == val;
});
}
TOML11_INLINE result<location, none_t>
rfind(const location& first, const location& last, const location::char_type val)
{
return rfind_if(first, last, [val](const location::char_type c) {
return c == val;
});
}
TOML11_INLINE std::size_t
count(const location& first, const location& last, const location::char_type& c)
{
if(first.source() != last.source()) { return 0; }
if(first.get_location() >= last.get_location()) { return 0; }
auto loc = first;
std::size_t num = 0;
while(loc.get_location() != last.get_location())
{
if(loc.current() == c)
{
num += 1;
}
loc.advance();
}
return num;
}
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_LOCATION_HPP

View File

@@ -0,0 +1,250 @@
#ifndef TOML11_REGION_IMPL_HPP
#define TOML11_REGION_IMPL_HPP
#include "../fwd/region_fwd.hpp"
#include "../utility.hpp"
#include "../version.hpp"
#include <algorithm>
#include <iterator>
#include <string>
#include <sstream>
#include <vector>
#include <cassert>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
// a value defined in [first, last).
// Those source must be the same. Instread, `region` does not make sense.
TOML11_INLINE region::region(const location& first, const location& last)
: source_(first.source()), source_name_(first.source_name()),
length_(last.get_location() - first.get_location()),
first_(first.get_location()),
first_line_(first.line_number()),
first_column_(first.column_number()),
last_(last.get_location()),
last_line_(last.line_number()),
last_column_(last.column_number())
{
assert(first.source() == last.source());
assert(first.source_name() == last.source_name());
}
// shorthand of [loc, loc+1)
TOML11_INLINE region::region(const location& loc)
: source_(loc.source()), source_name_(loc.source_name()), length_(0),
first_line_(0), first_column_(0), last_line_(0), last_column_(0)
{
// if the file ends with LF, the resulting region points no char.
if(loc.eof())
{
if(loc.get_location() == 0)
{
this->length_ = 0;
this->first_ = 0;
this->first_line_ = 0;
this->first_column_ = 0;
this->last_ = 0;
this->last_line_ = 0;
this->last_column_ = 0;
}
else
{
const auto first = prev(loc);
this->first_ = first.get_location();
this->first_line_ = first.line_number();
this->first_column_ = first.column_number();
this->last_ = loc.get_location();
this->last_line_ = loc.line_number();
this->last_column_ = loc.column_number();
this->length_ = 1;
}
}
else
{
this->first_ = loc.get_location();
this->first_line_ = loc.line_number();
this->first_column_ = loc.column_number();
this->last_ = loc.get_location() + 1;
this->last_line_ = loc.line_number();
this->last_column_ = loc.column_number() + 1;
this->length_ = 1;
}
}
TOML11_INLINE region::char_type region::at(std::size_t i) const
{
if(this->last_ <= this->first_ + i)
{
throw std::out_of_range("range::at: index " + std::to_string(i) +
" exceeds length " + std::to_string(this->length_));
}
const auto iter = std::next(this->source_->cbegin(),
static_cast<difference_type>(this->first_ + i));
return *iter;
}
TOML11_INLINE region::const_iterator region::begin() const noexcept
{
return std::next(this->source_->cbegin(),
static_cast<difference_type>(this->first_));
}
TOML11_INLINE region::const_iterator region::end() const noexcept
{
return std::next(this->source_->cbegin(),
static_cast<difference_type>(this->last_));
}
TOML11_INLINE region::const_iterator region::cbegin() const noexcept
{
return std::next(this->source_->cbegin(),
static_cast<difference_type>(this->first_));
}
TOML11_INLINE region::const_iterator region::cend() const noexcept
{
return std::next(this->source_->cbegin(),
static_cast<difference_type>(this->last_));
}
TOML11_INLINE std::string region::as_string() const
{
if(this->is_ok())
{
const auto begin = std::next(this->source_->cbegin(), static_cast<difference_type>(this->first_));
const auto end = std::next(this->source_->cbegin(), static_cast<difference_type>(this->last_ ));
return ::toml::detail::make_string(begin, end);
}
else
{
return std::string("");
}
}
TOML11_INLINE std::pair<std::string, std::size_t>
region::take_line(const_iterator begin, const_iterator end) const
{
// To omit long line, we cap region by before/after 30 chars
const auto dist_before = std::distance(source_->cbegin(), begin);
const auto dist_after = std::distance(end, source_->cend());
const const_iterator capped_begin = (dist_before <= 30) ? source_->cbegin() : std::prev(begin, 30);
const const_iterator capped_end = (dist_after <= 30) ? source_->cend() : std::next(end, 30);
const auto lf = char_type('\n');
const auto lf_before = std::find(cxx::make_reverse_iterator(begin),
cxx::make_reverse_iterator(capped_begin), lf);
const auto lf_after = std::find(end, capped_end, lf);
auto offset = static_cast<std::size_t>(std::distance(lf_before.base(), begin));
std::string retval = make_string(lf_before.base(), lf_after);
if(lf_before.base() != source_->cbegin() && *lf_before != lf)
{
retval = "... " + retval;
offset += 4;
}
if(lf_after != source_->cend() && *lf_after != lf)
{
retval = retval + " ...";
}
return std::make_pair(retval, offset);
}
TOML11_INLINE std::vector<std::pair<std::string, std::size_t>> region::as_lines() const
{
assert(this->is_ok());
if(this->length_ == 0)
{
return std::vector<std::pair<std::string, std::size_t>>{
std::make_pair("", std::size_t(0))
};
}
// Consider the following toml file
// ```
// array = [
// 1, 2, 3,
// ] # comment
// ```
// and the region represnets
// ```
// [
// 1, 2, 3,
// ]
// ```
// but we want to show the following.
// ```
// array = [
// 1, 2, 3,
// ] # comment
// ```
// So we need to find LFs before `begin` and after `end`.
//
// But, if region ends with LF, it should not include the next line.
// ```
// a = 42
// ^^^- with the last LF
// ```
// So we start from `end-1` when looking for LF.
const auto begin_idx = static_cast<difference_type>(this->first_);
const auto end_idx = static_cast<difference_type>(this->last_) - 1;
// length_ != 0, so begin < end. then begin <= end-1
assert(begin_idx <= end_idx);
const auto begin = std::next(this->source_->cbegin(), begin_idx);
const auto end = std::next(this->source_->cbegin(), end_idx);
assert(this->first_line_number() <= this->last_line_number());
if(this->first_line_number() == this->last_line_number())
{
return std::vector<std::pair<std::string, std::size_t>>{
this->take_line(begin, end)
};
}
// we have multiple lines. `begin` and `end` points different lines.
// that means that there is at least one `LF` between `begin` and `end`.
const auto after_begin = std::distance(begin, this->source_->cend());
const auto before_end = std::distance(this->source_->cbegin(), end);
const_iterator capped_file_end = this->source_->cend();
const_iterator capped_file_begin = this->source_->cbegin();
if(60 < after_begin) {capped_file_end = std::next(begin, 50);}
if(60 < before_end) {capped_file_begin = std::prev(end, 50);}
const auto lf = char_type('\n');
const auto first_line_end = std::find(begin, capped_file_end, lf);
const auto last_line_begin = std::find(capped_file_begin, end, lf);
const auto first_line = this->take_line(begin, first_line_end);
const auto last_line = this->take_line(last_line_begin, end);
if(this->first_line_number() + 1 == this->last_line_number())
{
return std::vector<std::pair<std::string, std::size_t>>{
first_line, last_line
};
}
else
{
return std::vector<std::pair<std::string, std::size_t>>{
first_line, std::make_pair("...", 0), last_line
};
}
}
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_REGION_IMPL_HPP

View File

@@ -0,0 +1,476 @@
#ifndef TOML11_SCANNER_IMPL_HPP
#define TOML11_SCANNER_IMPL_HPP
#include "../fwd/scanner_fwd.hpp"
#include "../utility.hpp"
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
TOML11_INLINE scanner_storage::scanner_storage(const scanner_storage& other)
: scanner_(nullptr)
{
if(other.is_ok())
{
scanner_.reset(other.get().clone());
}
}
TOML11_INLINE scanner_storage& scanner_storage::operator=(const scanner_storage& other)
{
if(this == std::addressof(other)) {return *this;}
if(other.is_ok())
{
scanner_.reset(other.get().clone());
}
return *this;
}
TOML11_INLINE region scanner_storage::scan(location& loc) const
{
assert(this->is_ok());
return this->scanner_->scan(loc);
}
TOML11_INLINE std::string scanner_storage::expected_chars(location& loc) const
{
assert(this->is_ok());
return this->scanner_->expected_chars(loc);
}
TOML11_INLINE scanner_base& scanner_storage::get() const noexcept
{
assert(this->is_ok());
return *scanner_;
}
TOML11_INLINE std::string scanner_storage::name() const
{
assert(this->is_ok());
return this->scanner_->name();
}
// ----------------------------------------------------------------------------
TOML11_INLINE region character::scan(location& loc) const
{
if(loc.eof()) {return region{};}
if(loc.current() == this->value_)
{
const auto first = loc;
loc.advance(1);
return region(first, loc);
}
return region{};
}
TOML11_INLINE std::string character::expected_chars(location&) const
{
return show_char(value_);
}
TOML11_INLINE scanner_base* character::clone() const
{
return new character(*this);
}
TOML11_INLINE std::string character::name() const
{
return "character{" + show_char(value_) + "}";
}
// ----------------------------------------------------------------------------
TOML11_INLINE region character_either::scan(location& loc) const
{
if(loc.eof()) {return region{};}
for(std::size_t i=0; i<this->size_; ++i)
{
const auto c = char_type(this->value_[i]);
if(loc.current() == c)
{
const auto first = loc;
loc.advance(1);
return region(first, loc);
}
}
return region{};
}
TOML11_INLINE std::string character_either::expected_chars(location&) const
{
assert( this->value_ );
assert( this->size_ != 0 );
std::string expected;
if(this->size_ == 1)
{
expected += show_char(char_type(value_[0]));
}
else if(this->size_ == 2)
{
expected += show_char(char_type(value_[0])) + " or " +
show_char(char_type(value_[1]));
}
else
{
for(std::size_t i=0; i<this->size_; ++i)
{
if(i != 0)
{
expected += ", ";
}
if(i + 1 == this->size_)
{
expected += "or ";
}
expected += show_char(char_type(value_[i]));
}
}
return expected;
}
TOML11_INLINE scanner_base* character_either::clone() const
{
return new character_either(*this);
}
TOML11_INLINE std::string character_either::name() const
{
std::string n("character_either{");
for(std::size_t i=0; i<this->size_; ++i)
{
const auto c = char_type(this->value_[i]);
n += show_char(c);
n += ", ";
}
if(this->size_ != 0)
{
n.pop_back();
n.pop_back();
}
n += "}";
return n;
}
// ----------------------------------------------------------------------------
// character_in_range
TOML11_INLINE region character_in_range::scan(location& loc) const
{
if(loc.eof()) {return region{};}
const auto curr = loc.current();
if(this->from_ <= curr && curr <= this->to_)
{
const auto first = loc;
loc.advance(1);
return region(first, loc);
}
return region{};
}
TOML11_INLINE std::string character_in_range::expected_chars(location&) const
{
std::string expected("from `");
expected += show_char(from_);
expected += "` to `";
expected += show_char(to_);
expected += "`";
return expected;
}
TOML11_INLINE scanner_base* character_in_range::clone() const
{
return new character_in_range(*this);
}
TOML11_INLINE std::string character_in_range::name() const
{
return "character_in_range{" + show_char(from_) + "," + show_char(to_) + "}";
}
// ----------------------------------------------------------------------------
// literal
TOML11_INLINE region literal::scan(location& loc) const
{
const auto first = loc;
for(std::size_t i=0; i<size_; ++i)
{
if(loc.eof() || char_type(value_[i]) != loc.current())
{
loc = first;
return region{};
}
loc.advance(1);
}
return region(first, loc);
}
TOML11_INLINE std::string literal::expected_chars(location&) const
{
return std::string(value_);
}
TOML11_INLINE scanner_base* literal::clone() const
{
return new literal(*this);
}
TOML11_INLINE std::string literal::name() const
{
return std::string("literal{") + std::string(value_, size_) + "}";
}
// ----------------------------------------------------------------------------
// sequence
TOML11_INLINE region sequence::scan(location& loc) const
{
const auto first = loc;
for(const auto& other : others_)
{
const auto reg = other.scan(loc);
if( ! reg.is_ok())
{
loc = first;
return region{};
}
}
return region(first, loc);
}
TOML11_INLINE std::string sequence::expected_chars(location& loc) const
{
const auto first = loc;
for(const auto& other : others_)
{
const auto reg = other.scan(loc);
if( ! reg.is_ok())
{
return other.expected_chars(loc);
}
}
assert(false);
return ""; // XXX
}
TOML11_INLINE scanner_base* sequence::clone() const
{
return new sequence(*this);
}
TOML11_INLINE std::string sequence::name() const
{
std::string n("sequence{");
for(const auto& other : others_)
{
n += other.name();
n += ", ";
}
if( ! this->others_.empty())
{
n.pop_back();
n.pop_back();
}
n += "}";
return n;
}
// ----------------------------------------------------------------------------
// either
TOML11_INLINE region either::scan(location& loc) const
{
for(const auto& other : others_)
{
const auto reg = other.scan(loc);
if(reg.is_ok())
{
return reg;
}
}
return region{};
}
TOML11_INLINE std::string either::expected_chars(location& loc) const
{
assert( ! others_.empty());
std::string expected = others_.at(0).expected_chars(loc);
if(others_.size() == 2)
{
expected += " or ";
expected += others_.at(1).expected_chars(loc);
}
else
{
for(std::size_t i=1; i<others_.size(); ++i)
{
expected += ", ";
if(i + 1 == others_.size())
{
expected += "or ";
}
expected += others_.at(i).expected_chars(loc);
}
}
return expected;
}
TOML11_INLINE scanner_base* either::clone() const
{
return new either(*this);
}
TOML11_INLINE std::string either::name() const
{
std::string n("either{");
for(const auto& other : others_)
{
n += other.name();
n += ", ";
}
if( ! this->others_.empty())
{
n.pop_back();
n.pop_back();
}
n += "}";
return n;
}
// ----------------------------------------------------------------------------
// repeat_exact
TOML11_INLINE region repeat_exact::scan(location& loc) const
{
const auto first = loc;
for(std::size_t i=0; i<length_; ++i)
{
const auto reg = other_.scan(loc);
if( ! reg.is_ok())
{
loc = first;
return region{};
}
}
return region(first, loc);
}
TOML11_INLINE std::string repeat_exact::expected_chars(location& loc) const
{
for(std::size_t i=0; i<length_; ++i)
{
const auto reg = other_.scan(loc);
if( ! reg.is_ok())
{
return other_.expected_chars(loc);
}
}
assert(false);
return "";
}
TOML11_INLINE scanner_base* repeat_exact::clone() const
{
return new repeat_exact(*this);
}
TOML11_INLINE std::string repeat_exact::name() const
{
return "repeat_exact{" + std::to_string(length_) + ", " + other_.name() + "}";
}
// ----------------------------------------------------------------------------
// repeat_at_least
TOML11_INLINE region repeat_at_least::scan(location& loc) const
{
const auto first = loc;
for(std::size_t i=0; i<length_; ++i)
{
const auto reg = other_.scan(loc);
if( ! reg.is_ok())
{
loc = first;
return region{};
}
}
while( ! loc.eof())
{
const auto checkpoint = loc;
const auto reg = other_.scan(loc);
if( ! reg.is_ok())
{
loc = checkpoint;
return region(first, loc);
}
}
return region(first, loc);
}
TOML11_INLINE std::string repeat_at_least::expected_chars(location& loc) const
{
for(std::size_t i=0; i<length_; ++i)
{
const auto reg = other_.scan(loc);
if( ! reg.is_ok())
{
return other_.expected_chars(loc);
}
}
assert(false);
return "";
}
TOML11_INLINE scanner_base* repeat_at_least::clone() const
{
return new repeat_at_least(*this);
}
TOML11_INLINE std::string repeat_at_least::name() const
{
return "repeat_at_least{" + std::to_string(length_) + ", " + other_.name() + "}";
}
// ----------------------------------------------------------------------------
// maybe
TOML11_INLINE region maybe::scan(location& loc) const
{
const auto first = loc;
const auto reg = other_.scan(loc);
if( ! reg.is_ok())
{
loc = first;
}
return region(first, loc);
}
TOML11_INLINE std::string maybe::expected_chars(location&) const
{
return "";
}
TOML11_INLINE scanner_base* maybe::clone() const
{
return new maybe(*this);
}
TOML11_INLINE std::string maybe::name() const
{
return "maybe{" + other_.name() + "}";
}
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_SCANNER_IMPL_HPP

View File

@@ -0,0 +1,215 @@
#ifndef TOML11_SOURCE_LOCATION_IMPL_HPP
#define TOML11_SOURCE_LOCATION_IMPL_HPP
#include "../fwd/source_location_fwd.hpp"
#include "../color.hpp"
#include "../utility.hpp"
#include "../version.hpp"
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <cctype>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
TOML11_INLINE source_location::source_location(const detail::region& r)
: is_ok_(false),
first_line_(1),
first_column_(1),
first_offset_(1),
last_line_(1),
last_column_(1),
last_offset_(1),
length_(0),
file_name_("unknown file")
{
if(r.is_ok())
{
this->is_ok_ = true;
this->file_name_ = r.source_name();
this->first_line_ = r.first_line_number();
this->first_column_ = r.first_column_number();
this->last_line_ = r.last_line_number();
this->last_column_ = r.last_column_number();
this->length_ = r.length();
const auto lines = r.as_lines();
assert( ! lines.empty());
for(const auto& l : lines)
{
this->line_str_.push_back(l.first);
}
this->first_offset_ = lines.at( 0).second + 1; // to 1-origin
this->last_offset_ = lines.at(lines.size()-1).second + 1;
}
}
TOML11_INLINE std::string const& source_location::first_line() const
{
if(this->line_str_.size() == 0)
{
throw std::out_of_range("toml::source_location::first_line: `lines` is empty");
}
return this->line_str_.front();
}
TOML11_INLINE std::string const& source_location::last_line() const
{
if(this->line_str_.size() == 0)
{
throw std::out_of_range("toml::source_location::first_line: `lines` is empty");
}
return this->line_str_.back();
}
namespace detail
{
TOML11_INLINE std::size_t integer_width_base10(std::size_t i) noexcept
{
std::size_t width = 0;
while(i != 0)
{
i /= 10;
width += 1;
}
return width;
}
TOML11_INLINE std::ostringstream&
format_filename(std::ostringstream& oss, const source_location& loc)
{
// --> example.toml
oss << color::bold << color::blue << " --> " << color::reset
<< color::bold << loc.file_name() << '\n' << color::reset;
return oss;
}
TOML11_INLINE std::ostringstream& format_empty_line(std::ostringstream& oss,
const std::size_t lnw)
{
// |
oss << detail::make_string(lnw + 1, ' ')
<< color::bold << color::blue << " |\n" << color::reset;
return oss;
}
TOML11_INLINE std::ostringstream& format_line(std::ostringstream& oss,
const std::size_t lnw, const std::size_t linenum, const std::string& line)
{
// 10 | key = "value"
oss << ' ' << color::bold << color::blue
<< std::setw(static_cast<int>(lnw))
<< std::right << linenum << " | " << color::reset;
for(const char c : line)
{
if(std::isgraph(c) || c == ' ')
{
oss << c;
}
else
{
oss << show_char(c);
}
}
oss << '\n';
return oss;
}
TOML11_INLINE std::ostringstream& format_underline(std::ostringstream& oss,
const std::size_t lnw, const std::size_t col, const std::size_t len,
const std::string& msg)
{
// | ^^^^^^^-- this part
oss << make_string(lnw + 1, ' ')
<< color::bold << color::blue << " | " << color::reset;
// in case col is 0, so we don't create a string with size_t max length
const std::size_t sanitized_col = col == 0 ? 0 : col - 1 /*1-origin*/;
oss << make_string(sanitized_col, ' ')
<< color::bold << color::red
<< make_string(len, '^') << "-- "
<< color::reset << msg << '\n';
return oss;
}
TOML11_INLINE std::string format_location_impl(const std::size_t lnw,
const std::string& prev_fname,
const source_location& loc, const std::string& msg)
{
std::ostringstream oss;
if(loc.file_name() != prev_fname)
{
format_filename(oss, loc);
if( ! loc.lines().empty())
{
format_empty_line(oss, lnw);
}
}
if(loc.lines().size() == 1)
{
// when column points LF, it exceeds the size of the first line.
std::size_t underline_limit = 1;
if(loc.first_line().size() < loc.first_column_offset())
{
underline_limit = 1;
}
else
{
underline_limit = loc.first_line().size() - loc.first_column_offset() + 1;
}
const auto underline_len = (std::min)(underline_limit, loc.length());
format_line(oss, lnw, loc.first_line_number(), loc.first_line());
format_underline(oss, lnw, loc.first_column_offset(), underline_len, msg);
}
else if(loc.lines().size() == 2)
{
const auto first_underline_len =
loc.first_line().size() - loc.first_column_offset() + 1;
format_line(oss, lnw, loc.first_line_number(), loc.first_line());
format_underline(oss, lnw, loc.first_column_offset(),
first_underline_len, "");
format_line(oss, lnw, loc.last_line_number(), loc.last_line());
format_underline(oss, lnw, 1, loc.last_column_offset(), msg);
}
else if(loc.lines().size() > 2)
{
const auto first_underline_len =
loc.first_line().size() - loc.first_column_offset() + 1;
format_line(oss, lnw, loc.first_line_number(), loc.first_line());
format_underline(oss, lnw, loc.first_column_offset(),
first_underline_len, "and");
if(loc.lines().size() == 3)
{
format_line(oss, lnw, loc.first_line_number()+1, loc.lines().at(1));
format_underline(oss, lnw, 1, loc.lines().at(1).size(), "and");
}
else
{
format_line(oss, lnw, loc.first_line_number()+1, " ...");
format_empty_line(oss, lnw);
}
format_line(oss, lnw, loc.last_line_number(), loc.last_line());
format_underline(oss, lnw, 1, loc.last_column_offset(), msg);
}
// if loc is empty, do nothing.
return oss.str();
}
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_SOURCE_LOCATION_IMPL_HPP

View File

@@ -0,0 +1,893 @@
#ifndef TOML11_SYNTAX_IMPL_HPP
#define TOML11_SYNTAX_IMPL_HPP
#include "../fwd/syntax_fwd.hpp"
#include "../scanner.hpp"
#include "../spec.hpp"
#include "../version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
namespace syntax
{
using char_type = location::char_type;
template<typename F>
struct syntax_cache
{
using value_type = cxx::return_type_of_t<F, const spec&>;
static_assert(std::is_base_of<scanner_base, value_type>::value, "");
explicit syntax_cache(F f)
: func_(std::move(f)), cache_(cxx::make_nullopt())
{}
value_type const& at(const spec& s)
{
if( ! this->cache_.has_value() || this->cache_.value().first != s)
{
this->cache_ = std::make_pair(s, func_(s));
}
return this->cache_.value().second;
}
private:
F func_;
cxx::optional<std::pair<spec, value_type>> cache_;
};
template<typename F>
syntax_cache<cxx::remove_cvref_t<F>> make_cache(F&& f)
{
return syntax_cache<cxx::remove_cvref_t<F>>(std::forward<F>(f));
}
// ===========================================================================
// UTF-8
// avoid redundant representation and out-of-unicode sequence
TOML11_INLINE character_in_range const& utf8_1byte(const spec&)
{
static thread_local character_in_range cache(0x00, 0x7F);
return cache;
}
TOML11_INLINE sequence const& utf8_2bytes(const spec&)
{
static thread_local sequence cache(
character_in_range(0xC2, 0xDF),
character_in_range(0x80, 0xBF));
return cache;
}
TOML11_INLINE sequence const& utf8_3bytes(const spec&)
{
static thread_local sequence cache(/*1~2 bytes = */either(
sequence(character (0xE0), character_in_range(0xA0, 0xBF)),
sequence(character_in_range(0xE1, 0xEC), character_in_range(0x80, 0xBF)),
sequence(character (0xED), character_in_range(0x80, 0x9F)),
sequence(character_in_range(0xEE, 0xEF), character_in_range(0x80, 0xBF))
), /*3rd byte = */ character_in_range(0x80, 0xBF));
return cache;
}
TOML11_INLINE sequence const& utf8_4bytes(const spec&)
{
static thread_local sequence cache(/*1~2 bytes = */either(
sequence(character (0xF0), character_in_range(0x90, 0xBF)),
sequence(character_in_range(0xF1, 0xF3), character_in_range(0x80, 0xBF)),
sequence(character (0xF4), character_in_range(0x80, 0x8F))
), character_in_range(0x80, 0xBF), character_in_range(0x80, 0xBF));
return cache;
}
// ===========================================================================
// Whitespace
TOML11_INLINE character_either const& wschar(const spec&)
{
static thread_local character_either cache(" \t");
return cache;
}
TOML11_INLINE repeat_at_least const& ws(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s){
return repeat_at_least(0, wschar(s));
});
return cache.at(sp);
}
// ===========================================================================
// Newline
TOML11_INLINE either const& newline(const spec&)
{
static thread_local either cache(character(char_type('\n')), literal("\r\n"));
return cache;
}
// ===========================================================================
// Comments
TOML11_INLINE either const& allowed_comment_char(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s){
if(s.ext_allow_control_characters_in_comments)
{
return either(
character_in_range(0x01, 0x09),
character_in_range(0x0E, 0x7F),
non_ascii(s)
);
}
else
{
return either(
character(0x09),
character_in_range(0x20, 0x7E),
non_ascii(s)
);
}
});
return cache.at(sp);
}
// XXX Note that it does not take newline
TOML11_INLINE sequence const& comment(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s){
return sequence(character(char_type('#')),
repeat_at_least(0, allowed_comment_char(s)));
});
return cache.at(sp);
}
// ===========================================================================
// Boolean
TOML11_INLINE either const& boolean(const spec&)
{
static thread_local either cache(literal("true"), literal("false"));
return cache;
}
// ===========================================================================
// Integer
// non-digit-graph = ([a-zA-Z]|unicode mb char)
// graph = ([a-zA-Z0-9]|unicode mb char)
// suffix = _ non-digit-graph (graph | _graph)
TOML11_INLINE sequence const& num_suffix(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto non_digit_graph = [&s]() {
return either(
alpha(s),
non_ascii(s)
);
};
const auto graph = [&s]() {
return either(
alpha(s),
digit(s),
non_ascii(s)
);
};
return sequence(
character(char_type('_')),
non_digit_graph(),
repeat_at_least(0,
either(
sequence(character(char_type('_')), graph()),
graph()
)
)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& dec_int(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto digit19 = []() {
return character_in_range(char_type('1'), char_type('9'));
};
return sequence(
maybe(character_either("+-")),
either(
sequence(
digit19(),
repeat_at_least(1,
either(
digit(s),
sequence(character(char_type('_')), digit(s))
)
)
),
digit(s)
)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& hex_int(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
literal("0x"),
hexdig(s),
repeat_at_least(0,
either(
hexdig(s),
sequence(character(char_type('_')), hexdig(s))
)
)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& oct_int(const spec& s)
{
static thread_local auto cache = make_cache([](const spec&) {
const auto digit07 = []() {
return character_in_range(char_type('0'), char_type('7'));
};
return sequence(
literal("0o"),
digit07(),
repeat_at_least(0,
either(
digit07(),
sequence(character(char_type('_')), digit07())
)
)
);
});
return cache.at(s);
}
TOML11_INLINE sequence const& bin_int(const spec& s)
{
static thread_local auto cache = make_cache([](const spec&) {
const auto digit01 = []() {
return character_either("01");
};
return sequence(
literal("0b"),
digit01(),
repeat_at_least(0,
either(
digit01(),
sequence(character(char_type('_')), digit01())
)
)
);
});
return cache.at(s);
}
TOML11_INLINE either const& integer(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(
hex_int(s),
oct_int(s),
bin_int(s),
dec_int(s)
);
});
return cache.at(sp);
}
// ===========================================================================
// Floating
TOML11_INLINE sequence const& zero_prefixable_int(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
digit(s),
repeat_at_least(0,
either(
digit(s),
sequence(character('_'), digit(s))
)
)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& fractional_part(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
character('.'),
zero_prefixable_int(s)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& exponent_part(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
character_either("eE"),
maybe(character_either("+-")),
zero_prefixable_int(s)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& hex_floating(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
// C99 hexfloat (%a)
// [+-]? 0x ( [0-9a-fA-F]*\.[0-9a-fA-F]+ | [0-9a-fA-F]+\.? ) [pP] [+-]? [0-9]+
// - 0x(int).(frac)p[+-](int)
// - 0x(int).p[+-](int)
// - 0x.(frac)p[+-](int)
// - 0x(int)p[+-](int)
return sequence(
maybe(character_either("+-")),
character('0'),
character_either("xX"),
either(
sequence(
repeat_at_least(0, hexdig(s)),
character('.'),
repeat_at_least(1, hexdig(s))
),
sequence(
repeat_at_least(1, hexdig(s)),
maybe(character('.'))
)
),
character_either("pP"),
maybe(character_either("+-")),
repeat_at_least(1, character_in_range('0', '9'))
);
});
return cache.at(sp);
}
TOML11_INLINE either const& floating(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(
sequence(
dec_int(s),
either(
exponent_part(s),
sequence(fractional_part(s), maybe(exponent_part(s)))
)
),
sequence(
maybe(character_either("+-")),
either(literal("inf"), literal("nan"))
)
);
});
return cache.at(sp);
}
// ===========================================================================
// Datetime
TOML11_INLINE sequence const& local_date(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
repeat_exact(4, digit(s)),
character('-'),
repeat_exact(2, digit(s)),
character('-'),
repeat_exact(2, digit(s))
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& local_time(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
if(s.v1_1_0_make_seconds_optional)
{
return sequence(
repeat_exact(2, digit(s)),
character(':'),
repeat_exact(2, digit(s)),
maybe(sequence(
character(':'),
repeat_exact(2, digit(s)),
maybe(sequence(character('.'), repeat_at_least(1, digit(s))))
)));
}
else
{
return sequence(
repeat_exact(2, digit(s)),
character(':'),
repeat_exact(2, digit(s)),
character(':'),
repeat_exact(2, digit(s)),
maybe(sequence(character('.'), repeat_at_least(1, digit(s))))
);
}
});
return cache.at(sp);
}
TOML11_INLINE either const& time_offset(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(
character_either("zZ"),
sequence(character_either("+-"),
repeat_exact(2, digit(s)),
character(':'),
repeat_exact(2, digit(s))
)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& full_time(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(local_time(s), time_offset(s));
});
return cache.at(sp);
}
TOML11_INLINE character_either const& time_delim(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec&) {
return character_either("Tt ");
});
return cache.at(sp);
}
TOML11_INLINE sequence const& local_datetime(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(local_date(s), time_delim(s), local_time(s));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& offset_datetime(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(local_date(s), time_delim(s), full_time(s));
});
return cache.at(sp);
}
// ===========================================================================
// String
TOML11_INLINE sequence const& escaped_x2(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(character('x'), repeat_exact(2, hexdig(s)));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& escaped_u4(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(character('u'), repeat_exact(4, hexdig(s)));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& escaped_U8(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(character('U'), repeat_exact(8, hexdig(s)));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& escaped(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto escape_char = [&s] {
if(s.v1_1_0_add_escape_sequence_e)
{
return character_either("\"\\bfnrte");
}
else
{
return character_either("\"\\bfnrt");
}
};
const auto escape_seq = [&s, &escape_char] {
if(s.v1_1_0_add_escape_sequence_x)
{
return either(
escape_char(),
escaped_u4(s),
escaped_U8(s),
escaped_x2(s)
);
}
else
{
return either(
escape_char(),
escaped_u4(s),
escaped_U8(s)
);
}
};
return sequence(character('\\'), escape_seq());
});
return cache.at(sp);
}
TOML11_INLINE either const& basic_char(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto basic_unescaped = [&s]() {
return either(
wschar(s),
character(0x21), // 22 is "
character_in_range(0x23, 0x5B), // 5C is backslash
character_in_range(0x5D, 0x7E), // 7F is DEL
non_ascii(s)
);
};
return either(basic_unescaped(), escaped(s));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& basic_string(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
character('"'),
repeat_at_least(0, basic_char(s)),
character('"')
);
});
return cache.at(sp);
}
// ---------------------------------------------------------------------------
// multiline string
TOML11_INLINE sequence const& escaped_newline(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
character('\\'), ws(s), newline(s),
repeat_at_least(0, either(wschar(s), newline(s)))
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& ml_basic_string(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto mlb_content = [&s]() {
return either(basic_char(s), newline(s), escaped_newline(s));
};
const auto mlb_quotes = []() {
return either(literal("\"\""), character('\"'));
};
return sequence(
literal("\"\"\""),
maybe(newline(s)),
repeat_at_least(0, mlb_content()),
repeat_at_least(0,
sequence(
mlb_quotes(),
repeat_at_least(1, mlb_content())
)
),
// XXX """ and mlb_quotes are intentionally reordered to avoid
// unexpected match of mlb_quotes
literal("\"\"\""),
maybe(mlb_quotes())
);
});
return cache.at(sp);
}
// ---------------------------------------------------------------------------
// literal string
TOML11_INLINE either const& literal_char(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(
character (0x09),
character_in_range(0x20, 0x26),
character_in_range(0x28, 0x7E),
non_ascii(s)
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& literal_string(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
character('\''),
repeat_at_least(0, literal_char(s)),
character('\'')
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& ml_literal_string(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto mll_quotes = []() {
return either(literal("''"), character('\''));
};
const auto mll_content = [&s]() {
return either(literal_char(s), newline(s));
};
return sequence(
literal("'''"),
maybe(newline(s)),
repeat_at_least(0, mll_content()),
repeat_at_least(0, sequence(
mll_quotes(),
repeat_at_least(1, mll_content())
)
),
literal("'''"),
maybe(mll_quotes())
// XXX ''' and mll_quotes are intentionally reordered to avoid
// unexpected match of mll_quotes
);
});
return cache.at(sp);
}
TOML11_INLINE either const& string(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(
ml_basic_string(s),
ml_literal_string(s),
basic_string(s),
literal_string(s)
);
});
return cache.at(sp);
}
// ===========================================================================
// Keys
// to keep `expected_chars` simple
TOML11_INLINE non_ascii_key_char::non_ascii_key_char(const spec& s) noexcept
{
assert(s.ext_allow_non_english_in_bare_keys);
(void)s; // for NDEBUG
}
TOML11_INLINE std::uint32_t non_ascii_key_char::read_utf8(location& loc) const
{
// U+0000 ... U+0079 ; 0xxx_xxxx
// U+0080 ... U+07FF ; 110y_yyyx 10xx_xxxx;
// U+0800 ... U+FFFF ; 1110_yyyy 10yx_xxxx 10xx_xxxx
// U+010000 ... U+10FFFF; 1111_0yyy 10yy_xxxx 10xx_xxxx 10xx_xxxx
const unsigned char b1 = loc.current(); loc.advance(1);
if(b1 < 0x80)
{
return static_cast<std::uint32_t>(b1);
}
else if((b1 >> 5) == 6) // 0b110 == 6
{
const auto b2 = loc.current(); loc.advance(1);
const std::uint32_t c1 = b1 & ((1 << 5) - 1);
const std::uint32_t c2 = b2 & ((1 << 6) - 1);
const std::uint32_t codep = (c1 << 6) + c2;
if(codep < 0x80)
{
return 0xFFFFFFFF;
}
return codep;
}
else if((b1 >> 4) == 14) // 0b1110 == 14
{
const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;}
const auto b3 = loc.current(); loc.advance(1);
const std::uint32_t c1 = b1 & ((1 << 4) - 1);
const std::uint32_t c2 = b2 & ((1 << 6) - 1);
const std::uint32_t c3 = b3 & ((1 << 6) - 1);
const std::uint32_t codep = (c1 << 12) + (c2 << 6) + c3;
if(codep < 0x800)
{
return 0xFFFFFFFF;
}
return codep;
}
else if((b1 >> 3) == 30) // 0b11110 == 30
{
const auto b2 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;}
const auto b3 = loc.current(); loc.advance(1); if(loc.eof()) {return 0xFFFFFFFF;}
const auto b4 = loc.current(); loc.advance(1);
const std::uint32_t c1 = b1 & ((1 << 3) - 1);
const std::uint32_t c2 = b2 & ((1 << 6) - 1);
const std::uint32_t c3 = b3 & ((1 << 6) - 1);
const std::uint32_t c4 = b4 & ((1 << 6) - 1);
const std::uint32_t codep = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;
if(codep < 0x10000)
{
return 0xFFFFFFFF;
}
return codep;
}
else // not a Unicode codepoint in UTF-8
{
return 0xFFFFFFFF;
}
}
TOML11_INLINE region non_ascii_key_char::scan(location& loc) const
{
if(loc.eof()) {return region{};}
const auto first = loc;
const auto cp = read_utf8(loc);
if(cp == 0xFFFFFFFF)
{
return region{};
}
// ALPHA / DIGIT / %x2D / %x5F ; a-z A-Z 0-9 - _
// / %xB2 / %xB3 / %xB9 / %xBC-BE ; superscript digits, fractions
// / %xC0-D6 / %xD8-F6 / %xF8-37D ; non-symbol chars in Latin block
// / %x37F-1FFF ; exclude GREEK QUESTION MARK, which is basically a semi-colon
// / %x200C-200D / %x203F-2040 ; from General Punctuation Block, include the two tie symbols and ZWNJ, ZWJ
// / %x2070-218F / %x2460-24FF ; include super-/subscripts, letterlike/numberlike forms, enclosed alphanumerics
// / %x2C00-2FEF / %x3001-D7FF ; skip arrows, math, box drawing etc, skip 2FF0-3000 ideographic up/down markers and spaces
// / %xF900-FDCF / %xFDF0-FFFD ; skip D800-DFFF surrogate block, E000-F8FF Private Use area, FDD0-FDEF intended for process-internal use (unicode)
// / %x10000-EFFFF ; all chars outside BMP range, excluding Private Use planes (F0000-10FFFF)
if(cp == 0xB2 || cp == 0xB3 || cp == 0xB9 || (0xBC <= cp && cp <= 0xBE) ||
(0xC0 <= cp && cp <= 0xD6 ) || (0xD8 <= cp && cp <= 0xF6) || (0xF8 <= cp && cp <= 0x37D) ||
(0x37F <= cp && cp <= 0x1FFF) ||
(0x200C <= cp && cp <= 0x200D) || (0x203F <= cp && cp <= 0x2040) ||
(0x2070 <= cp && cp <= 0x218F) || (0x2460 <= cp && cp <= 0x24FF) ||
(0x2C00 <= cp && cp <= 0x2FEF) || (0x3001 <= cp && cp <= 0xD7FF) ||
(0xF900 <= cp && cp <= 0xFDCF) || (0xFDF0 <= cp && cp <= 0xFFFD) ||
(0x10000 <= cp && cp <= 0xEFFFF) )
{
return region(first, loc);
}
loc = first;
return region{};
}
TOML11_INLINE repeat_at_least const& unquoted_key(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
const auto keychar = [&s] {
if(s.ext_allow_non_english_in_bare_keys)
{
return either(alpha(s), digit(s), character{0x2D}, character{0x5F},
non_ascii_key_char(s));
}
else
{
return either(alpha(s), digit(s), character{0x2D}, character{0x5F});
}
};
return repeat_at_least(1, keychar());
});
return cache.at(sp);
}
TOML11_INLINE either const& quoted_key(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(basic_string(s), literal_string(s));
});
return cache.at(sp);
}
TOML11_INLINE either const& simple_key(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return either(unquoted_key(s), quoted_key(s));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& dot_sep(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(ws(s), character('.'), ws(s));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& dotted_key(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(
simple_key(s),
repeat_at_least(1, sequence(dot_sep(s), simple_key(s)))
);
});
return cache.at(sp);
}
TOML11_INLINE sequence const& keyval_sep(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(ws(s), character('='), ws(s));
});
return cache.at(sp);
}
// ===========================================================================
// Table key
TOML11_INLINE sequence const& std_table(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(character('['), ws(s), key(s), ws(s), character(']'));
});
return cache.at(sp);
}
TOML11_INLINE sequence const& array_table(const spec& sp)
{
static thread_local auto cache = make_cache([](const spec& s) {
return sequence(literal("[["), ws(s), key(s), ws(s), literal("]]"));
});
return cache.at(sp);
}
// ===========================================================================
// extension: null
TOML11_INLINE literal const& null_value(const spec&)
{
static thread_local literal cache("null");
return cache;
}
} // namespace syntax
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_SYNTAX_IMPL_HPP

View File

@@ -0,0 +1,44 @@
#ifndef TOML11_VALUE_T_IMPL_HPP
#define TOML11_VALUE_T_IMPL_HPP
#include "../fwd/value_t_fwd.hpp"
#include "../version.hpp"
#include <ostream>
#include <sstream>
#include <string>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
TOML11_INLINE std::ostream& operator<<(std::ostream& 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;
}
}
TOML11_INLINE std::string to_string(value_t t)
{
std::ostringstream oss;
oss << t;
return oss.str();
}
} //TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_VALUE_T_IMPL_HPP

21
include/toml11/into.hpp Normal file
View File

@@ -0,0 +1,21 @@
#ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
template<typename T>
struct into;
// {
// static toml::value into_toml(const T& user_defined_type)
// {
// // User-defined conversions ...
// }
// };
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_INTO_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_LITERAL_HPP
#define TOML11_LITERAL_HPP
#include "fwd/literal_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/literal_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_LITERAL_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_LOCATION_HPP
#define TOML11_LOCATION_HPP
#include "fwd/location_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/location_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_LOCATION_HPP

View File

@@ -0,0 +1,296 @@
#ifndef TOML11_ORDERED_MAP_HPP
#define TOML11_ORDERED_MAP_HPP
#include <algorithm>
#include <stdexcept>
#include <utility>
#include <vector>
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
template<typename Cmp>
struct ordered_map_ebo_container
{
Cmp cmp_; // empty base optimization for empty Cmp type
};
} // detail
template<typename Key, typename Val, typename Cmp = std::equal_to<Key>,
typename Allocator = std::allocator<std::pair<Key, Val>>>
class ordered_map : detail::ordered_map_ebo_container<Cmp>
{
public:
using key_type = Key;
using mapped_type = Val;
using value_type = std::pair<Key, Val>;
using key_compare = Cmp;
using allocator_type = Allocator;
using container_type = std::vector<value_type, Allocator>;
using reference = typename container_type::reference;
using pointer = typename container_type::pointer;
using const_reference = typename container_type::const_reference;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
private:
using ebo_base = detail::ordered_map_ebo_container<Cmp>;
public:
ordered_map() = default;
~ordered_map() = default;
ordered_map(const ordered_map&) = default;
ordered_map(ordered_map&&) = default;
ordered_map& operator=(const ordered_map&) = default;
ordered_map& operator=(ordered_map&&) = default;
ordered_map(const ordered_map& other, const Allocator& alloc)
: container_(other.container_, alloc)
{}
ordered_map(ordered_map&& other, const Allocator& alloc)
: container_(std::move(other.container_), alloc)
{}
explicit ordered_map(const Cmp& cmp, const Allocator& alloc = Allocator())
: ebo_base{cmp}, container_(alloc)
{}
explicit ordered_map(const Allocator& alloc)
: container_(alloc)
{}
template<typename InputIterator>
ordered_map(InputIterator first, InputIterator last, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator())
: ebo_base{cmp}, container_(first, last, alloc)
{}
template<typename InputIterator>
ordered_map(InputIterator first, InputIterator last, const Allocator& alloc)
: container_(first, last, alloc)
{}
ordered_map(std::initializer_list<value_type> v, const Cmp& cmp = Cmp(), const Allocator& alloc = Allocator())
: ebo_base{cmp}, container_(std::move(v), alloc)
{}
ordered_map(std::initializer_list<value_type> v, const Allocator& alloc)
: container_(std::move(v), alloc)
{}
ordered_map& operator=(std::initializer_list<value_type> v)
{
this->container_ = std::move(v);
return *this;
}
iterator begin() noexcept {return container_.begin();}
iterator end() noexcept {return container_.end();}
const_iterator begin() const noexcept {return container_.begin();}
const_iterator end() const noexcept {return container_.end();}
const_iterator cbegin() const noexcept {return container_.cbegin();}
const_iterator cend() const noexcept {return container_.cend();}
bool empty() const noexcept {return container_.empty();}
std::size_t size() const noexcept {return container_.size();}
std::size_t max_size() const noexcept {return container_.max_size();}
void clear() {container_.clear();}
void push_back(const value_type& v)
{
if(this->contains(v.first))
{
throw std::out_of_range("ordered_map: value already exists");
}
container_.push_back(v);
}
void push_back(value_type&& v)
{
if(this->contains(v.first))
{
throw std::out_of_range("ordered_map: value already exists");
}
container_.push_back(std::move(v));
}
void emplace_back(key_type k, mapped_type v)
{
if(this->contains(k))
{
throw std::out_of_range("ordered_map: value already exists");
}
container_.emplace_back(std::move(k), std::move(v));
}
void pop_back() {container_.pop_back();}
void insert(value_type kv)
{
if(this->contains(kv.first))
{
throw std::out_of_range("ordered_map: value already exists");
}
container_.push_back(std::move(kv));
}
void emplace(key_type k, mapped_type v)
{
if(this->contains(k))
{
throw std::out_of_range("ordered_map: value already exists");
}
container_.emplace_back(std::move(k), std::move(v));
}
std::size_t count(const key_type& key) const
{
if(this->find(key) != this->end())
{
return 1;
}
else
{
return 0;
}
}
bool contains(const key_type& key) const
{
return this->find(key) != this->end();
}
iterator find(const key_type& key) noexcept
{
return std::find_if(this->begin(), this->end(),
[&key, this](const value_type& v) {return this->cmp_(v.first, key);});
}
const_iterator find(const key_type& key) const noexcept
{
return std::find_if(this->begin(), this->end(),
[&key, this](const value_type& v) {return this->cmp_(v.first, key);});
}
mapped_type& at(const key_type& k)
{
const auto iter = this->find(k);
if(iter == this->end())
{
throw std::out_of_range("ordered_map: no such element");
}
return iter->second;
}
mapped_type const& at(const key_type& k) const
{
const auto iter = this->find(k);
if(iter == this->end())
{
throw std::out_of_range("ordered_map: no such element");
}
return iter->second;
}
iterator erase(iterator pos)
{
return container_.erase(pos);
}
iterator erase(const_iterator pos)
{
return container_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
return container_.erase(first, last);
}
size_type erase(const key_type& key)
{
auto it = this->find(key);
if (it != this->end())
{
container_.erase(it);
return 1;
}
return 0;
}
mapped_type& operator[](const key_type& k)
{
const auto iter = this->find(k);
if(iter == this->end())
{
this->container_.emplace_back(k, mapped_type{});
return this->container_.back().second;
}
return iter->second;
}
mapped_type const& operator[](const key_type& k) const
{
const auto iter = this->find(k);
if(iter == this->end())
{
throw std::out_of_range("ordered_map: no such element");
}
return iter->second;
}
key_compare key_comp() const {return this->cmp_;}
void swap(ordered_map& other)
{
container_.swap(other.container_);
}
private:
container_type container_;
};
template<typename K, typename V, typename C, typename A>
bool operator==(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
{
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
}
template<typename K, typename V, typename C, typename A>
bool operator!=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
{
return !(lhs == rhs);
}
template<typename K, typename V, typename C, typename A>
bool operator<(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
{
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
template<typename K, typename V, typename C, typename A>
bool operator>(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
{
return rhs < lhs;
}
template<typename K, typename V, typename C, typename A>
bool operator<=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
{
return !(lhs > rhs);
}
template<typename K, typename V, typename C, typename A>
bool operator>=(const ordered_map<K,V,C,A>& lhs, const ordered_map<K,V,C,A>& rhs)
{
return !(lhs < rhs);
}
template<typename K, typename V, typename C, typename A>
void swap(ordered_map<K,V,C,A>& lhs, ordered_map<K,V,C,A>& rhs)
{
lhs.swap(rhs);
return;
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_ORDERED_MAP_HPP

3838
include/toml11/parser.hpp Normal file

File diff suppressed because it is too large Load Diff

10
include/toml11/region.hpp Normal file
View File

@@ -0,0 +1,10 @@
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include "fwd/region_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/region_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_REGION_HPP

490
include/toml11/result.hpp Normal file
View File

@@ -0,0 +1,490 @@
#ifndef TOML11_RESULT_HPP
#define TOML11_RESULT_HPP
#include "compat.hpp"
#include "exception.hpp"
#include "version.hpp"
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
#include <cassert>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
struct bad_result_access final : public ::toml::exception
{
public:
explicit bad_result_access(std::string what_arg)
: what_(std::move(what_arg))
{}
~bad_result_access() noexcept override = default;
const char* what() const noexcept override {return what_.c_str();}
private:
std::string what_;
};
// -----------------------------------------------------------------------------
template<typename T>
struct success
{
static_assert( ! std::is_void<T>::value, "");
using value_type = T;
explicit success(value_type v)
noexcept(std::is_nothrow_move_constructible<value_type>::value)
: value(std::move(v))
{}
template<typename U, cxx::enable_if_t<
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
std::nullptr_t> = nullptr>
explicit success(U&& v): value(std::forward<U>(v)) {}
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;
value_type& get() noexcept {return value;}
value_type const& get() const noexcept {return value;}
private:
value_type value;
};
template<typename T>
struct success<std::reference_wrapper<T>>
{
static_assert( ! std::is_void<T>::value, "");
using value_type = T;
explicit success(std::reference_wrapper<value_type> v) noexcept
: value(std::move(v))
{}
~success() = default;
success(const success&) = default;
success(success&&) = default;
success& operator=(const success&) = default;
success& operator=(success&&) = default;
value_type& get() noexcept {return value.get();}
value_type const& get() const noexcept {return value.get();}
private:
std::reference_wrapper<value_type> value;
};
template<typename T>
success<typename std::decay<T>::type> ok(T&& v)
{
return success<typename std::decay<T>::type>(std::forward<T>(v));
}
template<std::size_t N>
success<std::string> ok(const char (&literal)[N])
{
return success<std::string>(std::string(literal));
}
// -----------------------------------------------------------------------------
template<typename T>
struct failure
{
using value_type = T;
explicit failure(value_type v)
noexcept(std::is_nothrow_move_constructible<value_type>::value)
: value(std::move(v))
{}
template<typename U, cxx::enable_if_t<
std::is_convertible<cxx::remove_cvref_t<U>, T>::value,
std::nullptr_t> = nullptr>
explicit failure(U&& v): value(std::forward<U>(v)) {}
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;
value_type& get() noexcept {return value;}
value_type const& get() const noexcept {return value;}
private:
value_type value;
};
template<typename T>
struct failure<std::reference_wrapper<T>>
{
using value_type = T;
explicit failure(std::reference_wrapper<value_type> v) noexcept
: value(std::move(v))
{}
~failure() = default;
failure(const failure&) = default;
failure(failure&&) = default;
failure& operator=(const failure&) = default;
failure& operator=(failure&&) = default;
value_type& get() noexcept {return value.get();}
value_type const& get() const noexcept {return value.get();}
private:
std::reference_wrapper<value_type> value;
};
template<typename T>
failure<typename std::decay<T>::type> err(T&& v)
{
return failure<typename std::decay<T>::type>(std::forward<T>(v));
}
template<std::size_t N>
failure<std::string> err(const char (&literal)[N])
{
return failure<std::string>(std::string(literal));
}
/* ============================================================================
* _ _
* _ _ ___ ____ _| | |_
* | '_/ -_|_-< || | | _|
* |_| \___/__/\_,_|_|\__|
*/
template<typename T, typename E>
struct result
{
using success_type = success<T>;
using failure_type = failure<E>;
using value_type = typename success_type::value_type;
using error_type = typename failure_type::value_type;
result(success_type s): is_ok_(true), succ_(std::move(s)) {}
result(failure_type f): is_ok_(false), fail_(std::move(f)) {}
template<typename U, cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
std::is_convertible<cxx::remove_cvref_t<U>, value_type>
>::value, std::nullptr_t> = nullptr>
result(success<U> s): is_ok_(true), succ_(std::move(s.value)) {}
template<typename U, cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, error_type>>,
std::is_convertible<cxx::remove_cvref_t<U>, error_type>
>::value, std::nullptr_t> = nullptr>
result(failure<U> f): is_ok_(false), fail_(std::move(f.value)) {}
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=(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.succ_);
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
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.succ_));
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
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.succ_);
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(other.fail_);
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.succ_));
assert(tmp == std::addressof(this->succ_));
(void)tmp;
}
else
{
auto tmp = ::new(std::addressof(this->fail_)) failure_type(std::move(other.fail_));
assert(tmp == std::addressof(this->fail_));
(void)tmp;
}
is_ok_ = other.is_ok();
return *this;
}
template<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
>::value, std::nullptr_t> = nullptr>
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;
}
}
template<typename U, typename F, cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<U>, value_type>>,
cxx::negation<std::is_same<cxx::remove_cvref_t<F>, error_type>>,
std::is_convertible<cxx::remove_cvref_t<U>, value_type>,
std::is_convertible<cxx::remove_cvref_t<F>, error_type>
>::value, std::nullptr_t> = nullptr>
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_;}
explicit operator bool() const noexcept {return is_ok_;}
value_type& unwrap(cxx::source_location loc = cxx::source_location::current())
{
if(this->is_err())
{
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
}
return this->succ_.get();
}
value_type const& unwrap(cxx::source_location loc = cxx::source_location::current()) const
{
if(this->is_err())
{
throw bad_result_access("toml::result: bad unwrap" + cxx::to_string(loc));
}
return this->succ_.get();
}
value_type& unwrap_or(value_type& opt) noexcept
{
if(this->is_err()) {return opt;}
return this->succ_.get();
}
value_type const& unwrap_or(value_type const& opt) const noexcept
{
if(this->is_err()) {return opt;}
return this->succ_.get();
}
error_type& unwrap_err(cxx::source_location loc = cxx::source_location::current())
{
if(this->is_ok())
{
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
}
return this->fail_.get();
}
error_type const& unwrap_err(cxx::source_location loc = cxx::source_location::current()) const
{
if(this->is_ok())
{
throw bad_result_access("toml::result: bad unwrap_err" + cxx::to_string(loc));
}
return this->fail_.get();
}
value_type& as_ok() noexcept
{
assert(this->is_ok());
return this->succ_.get();
}
value_type const& as_ok() const noexcept
{
assert(this->is_ok());
return this->succ_.get();
}
error_type& as_err() noexcept
{
assert(this->is_err());
return this->fail_.get();
}
error_type const& as_err() const noexcept
{
assert(this->is_err());
return this->fail_.get();
}
private:
void cleanup() noexcept
{
#if defined(__GNUC__) && ! defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wduplicated-branches"
#endif
if(this->is_ok_) {this->succ_.~success_type();}
else {this->fail_.~failure_type();}
#if defined(__GNUC__) && ! defined(__clang__)
#pragma GCC diagnostic pop
#endif
return;
}
private:
bool is_ok_;
union
{
success_type succ_;
failure_type fail_;
};
};
// ----------------------------------------------------------------------------
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;}
inline std::ostream& operator<<(std::ostream& os, const none_t&)
{
os << "none";
return os;
}
} // detail
inline success<detail::none_t> ok() noexcept
{
return success<detail::none_t>(detail::none_t{});
}
inline failure<detail::none_t> err() noexcept
{
return failure<detail::none_t>(detail::none_t{});
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_RESULT_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_SCANNER_HPP
#define TOML11_SCANNER_HPP
#include "fwd/scanner_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/scanner_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_SCANNER_HPP

File diff suppressed because it is too large Load Diff

399
include/toml11/skip.hpp Normal file
View File

@@ -0,0 +1,399 @@
#ifndef TOML11_SKIP_HPP
#define TOML11_SKIP_HPP
#include "context.hpp"
#include "region.hpp"
#include "scanner.hpp"
#include "syntax.hpp"
#include "types.hpp"
#include "version.hpp"
#include <cassert>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
template<typename TC>
bool skip_whitespace(location& loc, const context<TC>& ctx)
{
return syntax::ws(ctx.toml_spec()).scan(loc).is_ok();
}
template<typename TC>
bool skip_empty_lines(location& loc, const context<TC>& ctx)
{
return repeat_at_least(1, sequence(
syntax::ws(ctx.toml_spec()),
syntax::newline(ctx.toml_spec())
)).scan(loc).is_ok();
}
// For error recovery.
//
// In case if a comment line contains an invalid character, we need to skip it
// to advance parsing.
template<typename TC>
void skip_comment_block(location& loc, const context<TC>& ctx)
{
while( ! loc.eof())
{
skip_whitespace(loc, ctx);
if(loc.current() == '#')
{
while( ! loc.eof())
{
// both CRLF and LF ends with LF.
if(loc.current() == '\n')
{
loc.advance();
break;
}
}
}
else if(syntax::newline(ctx.toml_spec()).scan(loc).is_ok())
{
; // an empty line. skip this also
}
else
{
// the next token is neither a comment nor empty line.
return ;
}
}
return ;
}
template<typename TC>
void skip_empty_or_comment_lines(location& loc, const context<TC>& ctx)
{
const auto& spec = ctx.toml_spec();
repeat_at_least(0, sequence(
syntax::ws(spec),
maybe(syntax::comment(spec)),
syntax::newline(spec))
).scan(loc);
return ;
}
// For error recovery.
//
// Sometimes we need to skip a value and find a delimiter, like `,`, `]`, or `}`.
// To find delimiter, we need to skip delimiters in a string.
// Since we are skipping invalid value while error recovery, we don't need
// to check the syntax. Here we just skip string-like region until closing quote
// is found.
template<typename TC>
void skip_string_like(location& loc, const context<TC>&)
{
// if """ is found, skip until the closing """ is found.
if(literal("\"\"\"").scan(loc).is_ok())
{
while( ! loc.eof())
{
if(literal("\"\"\"").scan(loc).is_ok())
{
return;
}
loc.advance();
}
}
else if(literal("'''").scan(loc).is_ok())
{
while( ! loc.eof())
{
if(literal("'''").scan(loc).is_ok())
{
return;
}
loc.advance();
}
}
// if " is found, skip until the closing " or newline is found.
else if(loc.current() == '"')
{
while( ! loc.eof())
{
loc.advance();
if(loc.current() == '"' || loc.current() == '\n')
{
loc.advance();
return;
}
}
}
else if(loc.current() == '\'')
{
while( ! loc.eof())
{
loc.advance();
if(loc.current() == '\'' || loc.current() == '\n')
{
loc.advance();
return ;
}
}
}
return;
}
template<typename TC>
void skip_value(location& loc, const context<TC>& ctx);
template<typename TC>
void skip_array_like(location& loc, const context<TC>& ctx);
template<typename TC>
void skip_inline_table_like(location& loc, const context<TC>& ctx);
template<typename TC>
void skip_key_value_pair(location& loc, const context<TC>& ctx);
template<typename TC>
result<value_t, error_info>
guess_value_type(const location& loc, const context<TC>& ctx);
template<typename TC>
void skip_array_like(location& loc, const context<TC>& ctx)
{
const auto& spec = ctx.toml_spec();
assert(loc.current() == '[');
loc.advance();
while( ! loc.eof())
{
if(loc.current() == '\"' || loc.current() == '\'')
{
skip_string_like(loc, ctx);
}
else if(loc.current() == '#')
{
skip_comment_block(loc, ctx);
}
else if(loc.current() == '{')
{
skip_inline_table_like(loc, ctx);
}
else if(loc.current() == '[')
{
const auto checkpoint = loc;
if(syntax::std_table(spec).scan(loc).is_ok() ||
syntax::array_table(spec).scan(loc).is_ok())
{
loc = checkpoint;
break;
}
// if it is not a table-definition, then it is an array.
skip_array_like(loc, ctx);
}
else if(loc.current() == '=')
{
// key-value pair cannot be inside the array.
// guessing the error is "missing closing bracket `]`".
// find the previous key just before `=`.
while(loc.get_location() != 0)
{
loc.retrace();
if(loc.current() == '\n')
{
loc.advance();
break;
}
}
break;
}
else if(loc.current() == ']')
{
break; // found closing bracket
}
else
{
loc.advance();
}
}
return ;
}
template<typename TC>
void skip_inline_table_like(location& loc, const context<TC>& ctx)
{
assert(loc.current() == '{');
loc.advance();
const auto& spec = ctx.toml_spec();
while( ! loc.eof())
{
if(loc.current() == '\n' && ! spec.v1_1_0_allow_newlines_in_inline_tables)
{
break; // missing closing `}`.
}
else if(loc.current() == '\"' || loc.current() == '\'')
{
skip_string_like(loc, ctx);
}
else if(loc.current() == '#')
{
skip_comment_block(loc, ctx);
if( ! spec.v1_1_0_allow_newlines_in_inline_tables)
{
// comment must end with newline.
break; // missing closing `}`.
}
}
else if(loc.current() == '[')
{
const auto checkpoint = loc;
if(syntax::std_table(spec).scan(loc).is_ok() ||
syntax::array_table(spec).scan(loc).is_ok())
{
loc = checkpoint;
break; // missing closing `}`.
}
// if it is not a table-definition, then it is an array.
skip_array_like(loc, ctx);
}
else if(loc.current() == '{')
{
skip_inline_table_like(loc, ctx);
}
else if(loc.current() == '}')
{
// closing brace found. guessing the error is inside the table.
break;
}
else
{
// skip otherwise.
loc.advance();
}
}
return ;
}
template<typename TC>
void skip_value(location& loc, const context<TC>& ctx)
{
value_t ty = guess_value_type(loc, ctx).unwrap_or(value_t::empty);
if(ty == value_t::string)
{
skip_string_like(loc, ctx);
}
else if(ty == value_t::array)
{
skip_array_like(loc, ctx);
}
else if(ty == value_t::table)
{
// In case of multiline tables, it may skip key-value pair but not the
// whole table.
skip_inline_table_like(loc, ctx);
}
else // others are an "in-line" values. skip until the next line
{
while( ! loc.eof())
{
if(loc.current() == '\n')
{
break;
}
else if(loc.current() == ',' || loc.current() == ']' || loc.current() == '}')
{
break;
}
loc.advance();
}
}
return;
}
template<typename TC>
void skip_key_value_pair(location& loc, const context<TC>& ctx)
{
while( ! loc.eof())
{
if(loc.current() == '=')
{
skip_whitespace(loc, ctx);
skip_value(loc, ctx);
return;
}
else if(loc.current() == '\n')
{
// newline is found before finding `=`. assuming "missing `=`".
return;
}
loc.advance();
}
return ;
}
template<typename TC>
void skip_until_next_table(location& loc, const context<TC>& ctx)
{
const auto& spec = ctx.toml_spec();
while( ! loc.eof())
{
if(loc.current() == '\n')
{
loc.advance();
const auto line_begin = loc;
skip_whitespace(loc, ctx);
if(syntax::std_table(spec).scan(loc).is_ok())
{
loc = line_begin;
return ;
}
if(syntax::array_table(spec).scan(loc).is_ok())
{
loc = line_begin;
return ;
}
}
loc.advance();
}
}
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#if defined(TOML11_COMPILE_SOURCES)
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
struct type_config;
struct ordered_type_config;
namespace detail
{
extern template bool skip_whitespace <type_config>(location& loc, const context<type_config>&);
extern template bool skip_empty_lines <type_config>(location& loc, const context<type_config>&);
extern template void skip_comment_block <type_config>(location& loc, const context<type_config>&);
extern template void skip_empty_or_comment_lines<type_config>(location& loc, const context<type_config>&);
extern template void skip_string_like <type_config>(location& loc, const context<type_config>&);
extern template void skip_array_like <type_config>(location& loc, const context<type_config>&);
extern template void skip_inline_table_like <type_config>(location& loc, const context<type_config>&);
extern template void skip_value <type_config>(location& loc, const context<type_config>&);
extern template void skip_key_value_pair <type_config>(location& loc, const context<type_config>&);
extern template void skip_until_next_table <type_config>(location& loc, const context<type_config>&);
extern template bool skip_whitespace <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template bool skip_empty_lines <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_comment_block <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_empty_or_comment_lines<ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_string_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_array_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_inline_table_like <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_value <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_key_value_pair <ordered_type_config>(location& loc, const context<ordered_type_config>&);
extern template void skip_until_next_table <ordered_type_config>(location& loc, const context<ordered_type_config>&);
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_COMPILE_SOURCES
#endif // TOML11_SKIP_HPP

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP
#include "fwd/source_location_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/source_location_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_SOURCE_LOCATION_HPP

176
include/toml11/spec.hpp Normal file
View File

@@ -0,0 +1,176 @@
#ifndef TOML11_SPEC_HPP
#define TOML11_SPEC_HPP
#include <array>
#include <functional>
#include <ostream>
#include <sstream>
#include <utility>
#include <cstdint>
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
struct semantic_version
{
constexpr semantic_version(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
: major{mjr}, minor{mnr}, patch{p}
{}
std::uint32_t major;
std::uint32_t minor;
std::uint32_t patch;
};
constexpr inline semantic_version
make_semver(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
{
return semantic_version(mjr, mnr, p);
}
constexpr inline bool
operator==(const semantic_version& lhs, const semantic_version& rhs) noexcept
{
return lhs.major == rhs.major &&
lhs.minor == rhs.minor &&
lhs.patch == rhs.patch;
}
constexpr inline bool
operator!=(const semantic_version& lhs, const semantic_version& rhs) noexcept
{
return !(lhs == rhs);
}
constexpr inline bool
operator<(const semantic_version& lhs, const semantic_version& rhs) noexcept
{
return lhs.major < rhs.major ||
(lhs.major == rhs.major && lhs.minor < rhs.minor) ||
(lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch < rhs.patch);
}
constexpr inline bool
operator>(const semantic_version& lhs, const semantic_version& rhs) noexcept
{
return rhs < lhs;
}
constexpr inline bool
operator<=(const semantic_version& lhs, const semantic_version& rhs) noexcept
{
return !(lhs > rhs);
}
constexpr inline bool
operator>=(const semantic_version& lhs, const semantic_version& rhs) noexcept
{
return !(lhs < rhs);
}
inline std::ostream& operator<<(std::ostream& os, const semantic_version& v)
{
os << v.major << '.' << v.minor << '.' << v.patch;
return os;
}
inline std::string to_string(const semantic_version& v)
{
std::ostringstream oss;
oss << v;
return oss.str();
}
struct spec
{
constexpr static spec default_version() noexcept
{
return spec::v(1, 0, 0);
}
constexpr static spec v(std::uint32_t mjr, std::uint32_t mnr, std::uint32_t p) noexcept
{
return spec(make_semver(mjr, mnr, p));
}
constexpr explicit spec(const semantic_version& semver) noexcept
: version{semver},
v1_1_0_allow_newlines_in_inline_tables {semantic_version{1, 1, 0} <= semver},
v1_1_0_allow_trailing_comma_in_inline_tables{semantic_version{1, 1, 0} <= semver},
v1_1_0_add_escape_sequence_e {semantic_version{1, 1, 0} <= semver},
v1_1_0_add_escape_sequence_x {semantic_version{1, 1, 0} <= semver},
v1_1_0_make_seconds_optional {semantic_version{1, 1, 0} <= semver},
ext_allow_control_characters_in_comments{false},
ext_allow_non_english_in_bare_keys{false},
ext_hex_float {false},
ext_num_suffix{false},
ext_null_value{false}
{}
semantic_version version; // toml version
// diff from v1.0.0 -> v1.1.0
bool v1_1_0_allow_newlines_in_inline_tables;
bool v1_1_0_allow_trailing_comma_in_inline_tables;
bool v1_1_0_add_escape_sequence_e;
bool v1_1_0_add_escape_sequence_x;
bool v1_1_0_make_seconds_optional;
// discussed in toml-lang, but currently not in it
bool ext_allow_control_characters_in_comments;
bool ext_allow_non_english_in_bare_keys;
// library extensions
bool ext_hex_float; // allow hex float (in C++ style)
bool ext_num_suffix; // allow number suffix (in C++ style)
bool ext_null_value; // allow `null` as a value
};
namespace detail
{
inline std::pair<const semantic_version&, std::array<bool, 10>>
to_tuple(const spec& s) noexcept
{
return std::make_pair(std::cref(s.version), std::array<bool, 10>{{
s.v1_1_0_allow_newlines_in_inline_tables,
s.v1_1_0_allow_trailing_comma_in_inline_tables,
s.v1_1_0_add_escape_sequence_e,
s.v1_1_0_add_escape_sequence_x,
s.v1_1_0_make_seconds_optional,
s.ext_allow_control_characters_in_comments,
s.ext_allow_non_english_in_bare_keys,
s.ext_hex_float,
s.ext_num_suffix,
s.ext_null_value
}});
}
} // detail
inline bool operator==(const spec& lhs, const spec& rhs) noexcept
{
return detail::to_tuple(lhs) == detail::to_tuple(rhs);
}
inline bool operator!=(const spec& lhs, const spec& rhs) noexcept
{
return detail::to_tuple(lhs) != detail::to_tuple(rhs);
}
inline bool operator< (const spec& lhs, const spec& rhs) noexcept
{
return detail::to_tuple(lhs) < detail::to_tuple(rhs);
}
inline bool operator<=(const spec& lhs, const spec& rhs) noexcept
{
return detail::to_tuple(lhs) <= detail::to_tuple(rhs);
}
inline bool operator> (const spec& lhs, const spec& rhs) noexcept
{
return detail::to_tuple(lhs) > detail::to_tuple(rhs);
}
inline bool operator>=(const spec& lhs, const spec& rhs) noexcept
{
return detail::to_tuple(lhs) >= detail::to_tuple(rhs);
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_SPEC_HPP

View File

@@ -0,0 +1,53 @@
#ifndef TOML11_STORAGE_HPP
#define TOML11_STORAGE_HPP
#include "compat.hpp"
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
// It owns a pointer to T. It does deep-copy when copied.
// This struct is introduced to implement a recursive type.
//
// `toml::value` contains `std::vector<toml::value>` to represent a toml array.
// But, in the definition of `toml::value`, `toml::value` is still incomplete.
// `std::vector` of an incomplete type is not allowed in C++11 (it is allowed
// after C++17). To avoid this, we need to use a pointer to `toml::value`, like
// `std::vector<std::unique_ptr<toml::value>>`. Although `std::unique_ptr` is
// noncopyable, we want to make `toml::value` copyable. `storage` is introduced
// to resolve those problems.
template<typename T>
struct storage
{
using value_type = T;
explicit storage(value_type v): ptr_(cxx::make_unique<T>(std::move(v))) {}
~storage() = default;
storage(const storage& rhs): ptr_(cxx::make_unique<T>(*rhs.ptr_)) {}
storage& operator=(const storage& rhs)
{
this->ptr_ = cxx::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& get() const noexcept {return *ptr_;}
private:
std::unique_ptr<value_type> ptr_;
};
} // detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_STORAGE_HPP

10
include/toml11/syntax.hpp Normal file
View File

@@ -0,0 +1,10 @@
#ifndef TOML11_SYNTAX_HPP
#define TOML11_SYNTAX_HPP
#include "fwd/syntax_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/syntax_impl.hpp" // IWYU pragma: export
#endif
#endif// TOML11_SYNTAX_HPP

270
include/toml11/traits.hpp Normal file
View File

@@ -0,0 +1,270 @@
#ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP
#include "from.hpp"
#include "into.hpp"
#include "compat.hpp"
#include "version.hpp"
#include <array>
#include <chrono>
#include <forward_list>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_set>
#include <utility>
#if defined(TOML11_HAS_STRING_VIEW)
#include <string_view>
#endif
#if defined(TOML11_HAS_OPTIONAL)
#include <optional>
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
template<typename TypeConcig>
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 TC>
static std::true_type check(
decltype(std::declval<T>().from_toml(std::declval<::toml::basic_value<TC>>()))*);
template<typename T, typename TC>
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_template_into_toml_method_impl
{
template<typename T, typename TypeConfig>
static std::true_type check(decltype(std::declval<T>().template into_toml<TypeConfig>())*);
template<typename T, typename TypeConfig>
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::into<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 TC>
struct has_from_toml_method: decltype(has_from_toml_method_impl::check<T, TC>(nullptr)){};
template<typename T>
struct has_into_toml_method: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
template<typename T, typename TypeConfig>
struct has_template_into_toml_method: decltype(has_template_into_toml_method_impl::check<T, TypeConfig>(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
// ---------------------------------------------------------------------------
// type checkers
template<typename T> struct is_std_pair_impl : std::false_type{};
template<typename T1, typename T2>
struct is_std_pair_impl<std::pair<T1, T2>> : std::true_type{};
template<typename T>
using is_std_pair = is_std_pair_impl<cxx::remove_cvref_t<T>>;
template<typename T> struct is_std_tuple_impl : std::false_type{};
template<typename ... Ts>
struct is_std_tuple_impl<std::tuple<Ts...>> : std::true_type{};
template<typename T>
using is_std_tuple = is_std_tuple_impl<cxx::remove_cvref_t<T>>;
template<typename T> struct is_unordered_set_impl : std::false_type {};
template<typename T>
struct is_unordered_set_impl<std::unordered_set<T>> : std::true_type {};
template<typename T>
using is_unordered_set = is_unordered_set_impl<cxx::remove_cvref_t<T>>;
#if defined(TOML11_HAS_OPTIONAL)
template<typename T> struct is_std_optional_impl : std::false_type{};
template<typename T>
struct is_std_optional_impl<std::optional<T>> : std::true_type{};
template<typename T>
using is_std_optional = is_std_optional_impl<cxx::remove_cvref_t<T>>;
#else
template<typename T> struct is_std_optional : std::false_type{};
#endif // > C++17
template<typename T> struct is_std_array_impl : std::false_type{};
template<typename T, std::size_t N>
struct is_std_array_impl<std::array<T, N>> : std::true_type{};
template<typename T>
using is_std_array = is_std_array_impl<cxx::remove_cvref_t<T>>;
template<typename T> struct is_std_forward_list_impl : std::false_type{};
template<typename T>
struct is_std_forward_list_impl<std::forward_list<T>> : std::true_type{};
template<typename T>
using is_std_forward_list = is_std_forward_list_impl<cxx::remove_cvref_t<T>>;
template<typename T> struct is_std_basic_string_impl : std::false_type{};
template<typename C, typename T, typename A>
struct is_std_basic_string_impl<std::basic_string<C, T, A>> : std::true_type{};
template<typename T>
using is_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>;
template<typename T> struct is_1byte_std_basic_string_impl : std::false_type{};
template<typename C, typename T, typename A>
struct is_1byte_std_basic_string_impl<std::basic_string<C, T, A>>
: std::integral_constant<bool, sizeof(C) == sizeof(char)> {};
template<typename T>
using is_1byte_std_basic_string = is_std_basic_string_impl<cxx::remove_cvref_t<T>>;
#if defined(TOML11_HAS_STRING_VIEW)
template<typename T> struct is_std_basic_string_view_impl : std::false_type{};
template<typename C, typename T>
struct is_std_basic_string_view_impl<std::basic_string_view<C, T>> : std::true_type{};
template<typename T>
using is_std_basic_string_view = is_std_basic_string_view_impl<cxx::remove_cvref_t<T>>;
template<typename V, typename S>
struct is_string_view_of : std::false_type {};
template<typename C, typename T>
struct is_string_view_of<std::basic_string_view<C, T>, std::basic_string<C, T>> : std::true_type {};
#else
template<typename T>
struct is_std_basic_string_view : std::false_type {};
template<typename V, typename S>
struct is_string_view_of : std::false_type {};
#endif
template<typename T> struct is_chrono_duration_impl: std::false_type{};
template<typename Rep, typename Period>
struct is_chrono_duration_impl<std::chrono::duration<Rep, Period>>: std::true_type{};
template<typename T>
using is_chrono_duration = is_chrono_duration_impl<cxx::remove_cvref_t<T>>;
template<typename T>
struct is_map_impl : cxx::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>
using is_map = is_map_impl<cxx::remove_cvref_t<T>>;
template<typename T>
struct is_container_impl : cxx::conjunction<
cxx::negation<is_map<T>>, // not a map
cxx::negation<std::is_same<T, std::string>>, // not a std::string
#ifdef TOML11_HAS_STRING_VIEW
cxx::negation<std::is_same<T, std::string_view>>, // not a std::string_view
#endif
has_iterator<T>, // has T::iterator
has_value_type<T> // has T::value_type
>{};
template<typename T>
using is_container = is_container_impl<cxx::remove_cvref_t<T>>;
template<typename T>
struct is_basic_value_impl: std::false_type{};
template<typename TC>
struct is_basic_value_impl<::toml::basic_value<TC>>: std::true_type{};
template<typename T>
using is_basic_value = is_basic_value_impl<cxx::remove_cvref_t<T>>;
}// detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_TRAITS_HPP

381
include/toml11/types.hpp Normal file
View File

@@ -0,0 +1,381 @@
#ifndef TOML11_TYPES_HPP
#define TOML11_TYPES_HPP
#include "comments.hpp"
#include "compat.hpp"
#include "error_info.hpp"
#include "format.hpp"
#include "ordered_map.hpp"
#include "value.hpp"
#include "version.hpp"
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <cstdint>
#include <cstdio>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
// forward decl
template<typename TypeConfig>
class basic_value;
// when you use a special integer type as toml::value::integer_type, parse must
// be able to read it. So, type_config has static member functions that read the
// integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<<
// is enough. To make config easy, we provide the default read functions.
//
// Before this functions is called, syntax is checked and prefix(`0x` etc) and
// spacer(`_`) are removed.
template<typename T>
result<T, error_info>
read_dec_int(const std::string& str, const source_location src)
{
constexpr auto max_digits = std::numeric_limits<T>::digits;
assert( ! str.empty());
T val{0};
std::istringstream iss(str);
iss >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_dec_integer: "
"too large integer: current max digits = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
return ok(val);
}
template<typename T>
result<T, error_info>
read_hex_int(const std::string& str, const source_location src)
{
constexpr auto max_digits = std::numeric_limits<T>::digits;
assert( ! str.empty());
T val{0};
std::istringstream iss(str);
iss >> std::hex >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_hex_integer: "
"too large integer: current max value = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
return ok(val);
}
template<typename T>
result<T, error_info>
read_oct_int(const std::string& str, const source_location src)
{
constexpr auto max_digits = std::numeric_limits<T>::digits;
assert( ! str.empty());
T val{0};
std::istringstream iss(str);
iss >> std::oct >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_oct_integer: "
"too large integer: current max value = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
return ok(val);
}
template<typename T>
result<T, error_info>
read_bin_int(const std::string& str, const source_location src)
{
constexpr auto is_bounded = std::numeric_limits<T>::is_bounded;
constexpr auto max_digits = std::numeric_limits<T>::digits;
const auto max_value = (std::numeric_limits<T>::max)();
T val{0};
T base{1};
for(auto i = str.rbegin(); i != str.rend(); ++i)
{
const auto c = *i;
if(c == '1')
{
val += base;
// prevent `base` from overflow
if(is_bounded && max_value / 2 < base && std::next(i) != str.rend())
{
base = 0;
}
else
{
base *= 2;
}
}
else
{
assert(c == '0');
if(is_bounded && max_value / 2 < base && std::next(i) != str.rend())
{
base = 0;
}
else
{
base *= 2;
}
}
}
if(base == 0)
{
return err(make_error_info("toml::parse_bin_integer: "
"too large integer: current max value = 2^" + std::to_string(max_digits),
std::move(src), "must be < 2^" + std::to_string(max_digits)));
}
return ok(val);
}
template<typename T>
result<T, error_info>
read_int(const std::string& str, const source_location src, const std::uint8_t base)
{
assert(base == 10 || base == 16 || base == 8 || base == 2);
switch(base)
{
case 2: { return read_bin_int<T>(str, src); }
case 8: { return read_oct_int<T>(str, src); }
case 16: { return read_hex_int<T>(str, src); }
default:
{
assert(base == 10);
return read_dec_int<T>(str, src);
}
}
}
inline result<float, error_info>
read_hex_float(const std::string& str, const source_location src, float val)
{
#if defined(_MSC_VER) && ! defined(__clang__)
const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val));
#else
const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val));
#endif
if(res != 1)
{
return err(make_error_info("toml::parse_floating: "
"failed to read hexadecimal floating point value ",
std::move(src), "here"));
}
return ok(val);
}
inline result<double, error_info>
read_hex_float(const std::string& str, const source_location src, double val)
{
#if defined(_MSC_VER) && ! defined(__clang__)
const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val));
#else
const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val));
#endif
if(res != 1)
{
return err(make_error_info("toml::parse_floating: "
"failed to read hexadecimal floating point value ",
std::move(src), "here"));
}
return ok(val);
}
template<typename T>
cxx::enable_if_t<cxx::conjunction<
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, double>>,
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, float>>
>::value, result<T, error_info>>
read_hex_float(const std::string&, const source_location src, T)
{
return err(make_error_info("toml::parse_floating: failed to read "
"floating point value because of unknown type in type_config",
std::move(src), "here"));
}
template<typename T>
result<T, error_info>
read_dec_float(const std::string& str, const source_location src)
{
T val;
std::istringstream iss(str);
iss >> val;
if(iss.fail())
{
return err(make_error_info("toml::parse_floating: "
"failed to read floating point value from stream",
std::move(src), "here"));
}
return ok(val);
}
template<typename T>
result<T, error_info>
read_float(const std::string& str, const source_location src, const bool is_hex)
{
if(is_hex)
{
return read_hex_float(str, src, T{});
}
else
{
return read_dec_float<T>(str, src);
}
}
struct type_config
{
using comment_type = preserve_comments;
using boolean_type = bool;
using integer_type = std::int64_t;
using floating_type = double;
using string_type = std::string;
template<typename T>
using array_type = std::vector<T>;
template<typename K, typename T>
using table_type = std::unordered_map<K, T>;
static result<integer_type, error_info>
parse_int(const std::string& str, const source_location src, const std::uint8_t base)
{
return read_int<integer_type>(str, src, base);
}
static result<floating_type, error_info>
parse_float(const std::string& str, const source_location src, const bool is_hex)
{
return read_float<floating_type>(str, src, is_hex);
}
};
using value = basic_value<type_config>;
using table = typename value::table_type;
using array = typename value::array_type;
struct ordered_type_config
{
using comment_type = preserve_comments;
using boolean_type = bool;
using integer_type = std::int64_t;
using floating_type = double;
using string_type = std::string;
template<typename T>
using array_type = std::vector<T>;
template<typename K, typename T>
using table_type = ordered_map<K, T>;
static result<integer_type, error_info>
parse_int(const std::string& str, const source_location src, const std::uint8_t base)
{
return read_int<integer_type>(str, src, base);
}
static result<floating_type, error_info>
parse_float(const std::string& str, const source_location src, const bool is_hex)
{
return read_float<floating_type>(str, src, is_hex);
}
};
using ordered_value = basic_value<ordered_type_config>;
using ordered_table = typename ordered_value::table_type;
using ordered_array = typename ordered_value::array_type;
// ----------------------------------------------------------------------------
// meta functions for internal use
namespace detail
{
// ----------------------------------------------------------------------------
// check if type T has all the needed member types
template<typename T, typename U = void>
struct has_comment_type: std::false_type{};
template<typename T>
struct has_comment_type<T, cxx::void_t<typename T::comment_type>>: std::true_type{};
template<typename T, typename U = void>
struct has_integer_type: std::false_type{};
template<typename T>
struct has_integer_type<T, cxx::void_t<typename T::integer_type>>: std::true_type{};
template<typename T, typename U = void>
struct has_floating_type: std::false_type{};
template<typename T>
struct has_floating_type<T, cxx::void_t<typename T::floating_type>>: std::true_type{};
template<typename T, typename U = void>
struct has_string_type: std::false_type{};
template<typename T>
struct has_string_type<T, cxx::void_t<typename T::string_type>>: std::true_type{};
template<typename T, typename U = void>
struct has_array_type: std::false_type{};
template<typename T>
struct has_array_type<T, cxx::void_t<typename T::template array_type<int>>>: std::true_type{};
template<typename T, typename U = void>
struct has_table_type: std::false_type{};
template<typename T>
struct has_table_type<T, cxx::void_t<typename T::template table_type<int, int>>>: std::true_type{};
template<typename T, typename U = void>
struct has_parse_int: std::false_type{};
template<typename T>
struct has_parse_int<T, cxx::void_t<decltype(std::declval<T>().parse_int(
std::declval<std::string const&>(),
std::declval<::toml::source_location const&>(),
std::declval<std::uint8_t>()
))>>: std::true_type{};
template<typename T, typename U = void>
struct has_parse_float: std::false_type{};
template<typename T>
struct has_parse_float<T, cxx::void_t<decltype(std::declval<T>().parse_float(
std::declval<std::string const&>(),
std::declval<::toml::source_location const&>(),
std::declval<bool>()
))>>: std::true_type{};
template<typename T>
using is_type_config = cxx::conjunction<
has_comment_type<T>,
has_integer_type<T>,
has_floating_type<T>,
has_string_type<T>,
has_array_type<T>,
has_table_type<T>,
has_parse_int<T>,
has_parse_float<T>
>;
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#if defined(TOML11_COMPILE_SOURCES)
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
extern template class basic_value<type_config>;
extern template class basic_value<ordered_type_config>;
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_COMPILE_SOURCES
#endif // TOML11_TYPES_HPP

174
include/toml11/utility.hpp Normal file
View File

@@ -0,0 +1,174 @@
#ifndef TOML11_UTILITY_HPP
#define TOML11_UTILITY_HPP
#include "result.hpp"
#include "traits.hpp"
#include "version.hpp"
#include <array>
#include <sstream>
#include <cassert>
#include <cctype>
#include <cstring>
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
// to output character in an error message.
inline std::string show_char(const int c)
{
using char_type = unsigned char;
if(std::isgraph(c))
{
return std::string(1, static_cast<char>(c));
}
else
{
std::array<char, 5> buf;
buf.fill('\0');
const auto r = std::snprintf(buf.data(), buf.size(), "0x%02x", c & 0xFF);
assert(r == static_cast<int>(buf.size()) - 1);
(void) r; // Unused variable warning
auto in_hex = std::string(buf.data());
switch(c)
{
case char_type('\0'): {in_hex += "(NUL)"; break;}
case char_type(' ') : {in_hex += "(SPACE)"; break;}
case char_type('\n'): {in_hex += "(LINE FEED)"; break;}
case char_type('\r'): {in_hex += "(CARRIAGE RETURN)"; break;}
case char_type('\t'): {in_hex += "(TAB)"; break;}
case char_type('\v'): {in_hex += "(VERTICAL TAB)"; break;}
case char_type('\f'): {in_hex += "(FORM FEED)"; break;}
case char_type('\x1B'): {in_hex += "(ESCAPE)"; break;}
default: break;
}
return in_hex;
}
}
// ---------------------------------------------------------------------------
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;
}
template<typename Container>
void try_reserve(Container& container, std::size_t N)
{
try_reserve_impl(container, N, has_reserve_method<Container>{});
return;
}
// ---------------------------------------------------------------------------
template<typename T>
result<T, none_t> from_string(const std::string& str)
{
T v;
std::istringstream iss(str);
iss >> v;
if(iss.fail())
{
return err();
}
return ok(v);
}
// ---------------------------------------------------------------------------
// 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);
}
// ---------------------------------------------------------------------------
template<typename Char, typename Traits, typename Alloc,
typename Char2, typename Traits2, typename Alloc2>
struct string_conv_impl
{
static_assert(sizeof(Char) == sizeof(char), "");
static_assert(sizeof(Char2) == sizeof(char), "");
static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char2, Traits2, Alloc2> s)
{
std::basic_string<Char, Traits, Alloc> retval;
std::transform(s.begin(), s.end(), std::back_inserter(retval),
[](const Char2 c) {return static_cast<Char>(c);});
return retval;
}
template<std::size_t N>
static std::basic_string<Char, Traits, Alloc> invoke(const Char2 (&s)[N])
{
std::basic_string<Char, Traits, Alloc> retval;
// "string literal" has null-char at the end. to skip it, we use prev.
std::transform(std::begin(s), std::prev(std::end(s)), std::back_inserter(retval),
[](const Char2 c) {return static_cast<Char>(c);});
return retval;
}
};
template<typename Char, typename Traits, typename Alloc>
struct string_conv_impl<Char, Traits, Alloc, Char, Traits, Alloc>
{
static_assert(sizeof(Char) == sizeof(char), "");
static std::basic_string<Char, Traits, Alloc> invoke(std::basic_string<Char, Traits, Alloc> s)
{
return s;
}
template<std::size_t N>
static std::basic_string<Char, Traits, Alloc> invoke(const Char (&s)[N])
{
return std::basic_string<Char, Traits, Alloc>(s);
}
};
template<typename S, typename Char2, typename Traits2, typename Alloc2>
cxx::enable_if_t<is_std_basic_string<S>::value, S>
string_conv(std::basic_string<Char2, Traits2, Alloc2> s)
{
using C = typename S::value_type;
using T = typename S::traits_type;
using A = typename S::allocator_type;
return string_conv_impl<C, T, A, Char2, Traits2, Alloc2>::invoke(std::move(s));
}
template<typename S, std::size_t N>
cxx::enable_if_t<is_std_basic_string<S>::value, S>
string_conv(const char (&s)[N])
{
using C = typename S::value_type;
using T = typename S::traits_type;
using A = typename S::allocator_type;
using C2 = char;
using T2 = std::char_traits<C2>;
using A2 = std::allocator<C2>;
return string_conv_impl<C, T, A, C2, T2, A2>::template invoke<N>(s);
}
} // namespace detail
} // TOML11_INLINE_VERSION_NAMESPACE
} // namespace toml
#endif // TOML11_UTILITY_HPP

2513
include/toml11/value.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
#ifndef TOML11_VALUE_T_HPP
#define TOML11_VALUE_T_HPP
#include "fwd/value_t_fwd.hpp" // IWYU pragma: export
#if ! defined(TOML11_COMPILE_SOURCES)
#include "impl/value_t_impl.hpp" // IWYU pragma: export
#endif
#endif // TOML11_VALUE_T_HPP

143
include/toml11/version.hpp Normal file
View File

@@ -0,0 +1,143 @@
#ifndef TOML11_VERSION_HPP
#define TOML11_VERSION_HPP
#define TOML11_VERSION_MAJOR 4
#define TOML11_VERSION_MINOR 4
#define TOML11_VERSION_PATCH 0
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
/*
* Defines a name for an inline namespace that includes the current version
* number. This becomes necessary if multiple software packages use toml11 as an
* internal build-time dependency, since multiple packages will in general not
* use the same version of toml11. An inline namespace with a version number
* ensures that the symbols emitted by compiling toml11 into downstream
* applications will be distinguished by the specific used version of toml11,
* making it possible to link multiple packages that internally use toml11 in
* different versions.
*/
#define TOML11_CONCAT(a, b, c, d, e, f) a##b##c##d##e##f
#define TOML11_GENERATE_INLINE_VERSION_NAMESPACE(major, minor, patch) \
TOML11_CONCAT(toml11_, major, _, minor, _, patch)
#define TOML11_INLINE_VERSION_NAMESPACE \
TOML11_GENERATE_INLINE_VERSION_NAMESPACE( \
TOML11_VERSION_MAJOR, TOML11_VERSION_MINOR, TOML11_VERSION_PATCH)
// Since MSVC does not define `__cplusplus` correctly unless you pass
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
//
// 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. So we check if _MSVC_LANG is defined before using `__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(_MSVC_LANG) && defined(_MSC_VER) && 190024210 <= _MSC_FULL_VER
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
#else
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L
# error "toml11 requires C++11 or later."
#endif
#if ! defined(__has_include)
# define __has_include(x) 0
#endif
#if ! defined(__has_cpp_attribute)
# define __has_cpp_attribute(x) 0
#endif
#if ! defined(__has_builtin)
# define __has_builtin(x) 0
#endif
// hard to remember
#ifndef TOML11_CXX14_VALUE
#define TOML11_CXX14_VALUE 201402L
#endif//TOML11_CXX14_VALUE
#ifndef TOML11_CXX17_VALUE
#define TOML11_CXX17_VALUE 201703L
#endif//TOML11_CXX17_VALUE
#ifndef TOML11_CXX20_VALUE
#define TOML11_CXX20_VALUE 202002L
#endif//TOML11_CXX20_VALUE
#if defined(__cpp_char8_t)
# if __cpp_char8_t >= 201811L
# define TOML11_HAS_CHAR8_T 1
# endif
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if __has_include(<string_view>)
# define TOML11_HAS_STRING_VIEW 1
# endif
#endif
#ifndef TOML11_DISABLE_STD_FILESYSTEM
# if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if __has_include(<filesystem>)
# define TOML11_HAS_FILESYSTEM 1
# endif
# endif
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= TOML11_CXX17_VALUE
# if __has_include(<optional>)
# define TOML11_HAS_OPTIONAL 1
# endif
#endif
#if defined(TOML11_COMPILE_SOURCES)
# define TOML11_INLINE
#else
# define TOML11_INLINE inline
#endif
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
inline const char* license_notice() noexcept
{
return R"(The MIT License (MIT)
Copyright (c) 2017-now 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.)";
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_VERSION_HPP

140
include/toml11/visit.hpp Normal file
View File

@@ -0,0 +1,140 @@
#ifndef TOML11_VISIT_HPP
#define TOML11_VISIT_HPP
#include "exception.hpp"
#include "traits.hpp"
#include "value.hpp"
#include "version.hpp"
namespace toml
{
inline namespace TOML11_INLINE_VERSION_NAMESPACE
{
namespace detail
{
template<typename F, typename ... Ts>
using visit_result_t = decltype(std::declval<F>()(std::declval<Ts>().as_boolean() ...));
template<typename F, typename T>
struct front_binder
{
template<typename ... Args>
auto operator()(Args&& ... args) -> decltype(std::declval<F>()(std::declval<T>(), std::forward<Args>(args)...))
{
return func(std::move(front), std::forward<Args>(args)...);
}
F func;
T front;
};
template<typename F, typename T>
front_binder<cxx::remove_cvref_t<F>, cxx::remove_cvref_t<T>>
bind_front(F&& f, T&& t)
{
return front_binder<cxx::remove_cvref_t<F>, cxx::remove_cvref_t<T>>{
std::forward<F>(f), std::forward<T>(t)
};
}
template<typename Visitor, typename TC, typename ... Args>
visit_result_t<Visitor, const basic_value<TC>&, Args...>
visit_impl(Visitor&& visitor, const basic_value<TC>& v, Args&& ... args);
template<typename Visitor, typename TC, typename ... Args>
visit_result_t<Visitor, basic_value<TC>&, Args...>
visit_impl(Visitor&& visitor, basic_value<TC>& v, Args&& ... args);
template<typename Visitor, typename TC, typename ... Args>
visit_result_t<Visitor, basic_value<TC>, Args...>
visit_impl(Visitor&& visitor, basic_value<TC>&& v, Args&& ... args);
template<typename Visitor>
visit_result_t<Visitor> visit_impl(Visitor&& visitor)
{
return visitor();
}
template<typename Visitor, typename TC, typename ... Args>
visit_result_t<Visitor, basic_value<TC>&, Args...>
visit_impl(Visitor&& visitor, basic_value<TC>& v, Args&& ... args)
{
switch(v.type())
{
case value_t::boolean : {return visit_impl(bind_front(visitor, std::ref(v.as_boolean ())), std::forward<Args>(args)...);}
case value_t::integer : {return visit_impl(bind_front(visitor, std::ref(v.as_integer ())), std::forward<Args>(args)...);}
case value_t::floating : {return visit_impl(bind_front(visitor, std::ref(v.as_floating ())), std::forward<Args>(args)...);}
case value_t::string : {return visit_impl(bind_front(visitor, std::ref(v.as_string ())), std::forward<Args>(args)...);}
case value_t::offset_datetime: {return visit_impl(bind_front(visitor, std::ref(v.as_offset_datetime())), std::forward<Args>(args)...);}
case value_t::local_datetime : {return visit_impl(bind_front(visitor, std::ref(v.as_local_datetime ())), std::forward<Args>(args)...);}
case value_t::local_date : {return visit_impl(bind_front(visitor, std::ref(v.as_local_date ())), std::forward<Args>(args)...);}
case value_t::local_time : {return visit_impl(bind_front(visitor, std::ref(v.as_local_time ())), std::forward<Args>(args)...);}
case value_t::array : {return visit_impl(bind_front(visitor, std::ref(v.as_array ())), std::forward<Args>(args)...);}
case value_t::table : {return visit_impl(bind_front(visitor, std::ref(v.as_table ())), std::forward<Args>(args)...);}
case value_t::empty : break;
default: break;
}
throw type_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid type.", v.location(), "here"), v.location());
}
template<typename Visitor, typename TC, typename ... Args>
visit_result_t<Visitor, const basic_value<TC>&, Args...>
visit_impl(Visitor&& visitor, const basic_value<TC>& v, Args&& ... args)
{
switch(v.type())
{
case value_t::boolean : {return visit_impl(bind_front(visitor, std::cref(v.as_boolean ())), std::forward<Args>(args)...);}
case value_t::integer : {return visit_impl(bind_front(visitor, std::cref(v.as_integer ())), std::forward<Args>(args)...);}
case value_t::floating : {return visit_impl(bind_front(visitor, std::cref(v.as_floating ())), std::forward<Args>(args)...);}
case value_t::string : {return visit_impl(bind_front(visitor, std::cref(v.as_string ())), std::forward<Args>(args)...);}
case value_t::offset_datetime: {return visit_impl(bind_front(visitor, std::cref(v.as_offset_datetime())), std::forward<Args>(args)...);}
case value_t::local_datetime : {return visit_impl(bind_front(visitor, std::cref(v.as_local_datetime ())), std::forward<Args>(args)...);}
case value_t::local_date : {return visit_impl(bind_front(visitor, std::cref(v.as_local_date ())), std::forward<Args>(args)...);}
case value_t::local_time : {return visit_impl(bind_front(visitor, std::cref(v.as_local_time ())), std::forward<Args>(args)...);}
case value_t::array : {return visit_impl(bind_front(visitor, std::cref(v.as_array ())), std::forward<Args>(args)...);}
case value_t::table : {return visit_impl(bind_front(visitor, std::cref(v.as_table ())), std::forward<Args>(args)...);}
case value_t::empty : break;
default: break;
}
throw type_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid type.", v.location(), "here"), v.location());
}
template<typename Visitor, typename TC, typename ... Args>
visit_result_t<Visitor, basic_value<TC>, Args...>
visit_impl(Visitor&& visitor, basic_value<TC>&& v, Args&& ... args)
{
switch(v.type())
{
case value_t::boolean : {return visit_impl(bind_front(visitor, std::move(v.as_boolean ())), std::forward<Args>(args)...);}
case value_t::integer : {return visit_impl(bind_front(visitor, std::move(v.as_integer ())), std::forward<Args>(args)...);}
case value_t::floating : {return visit_impl(bind_front(visitor, std::move(v.as_floating ())), std::forward<Args>(args)...);}
case value_t::string : {return visit_impl(bind_front(visitor, std::move(v.as_string ())), std::forward<Args>(args)...);}
case value_t::offset_datetime: {return visit_impl(bind_front(visitor, std::move(v.as_offset_datetime())), std::forward<Args>(args)...);}
case value_t::local_datetime : {return visit_impl(bind_front(visitor, std::move(v.as_local_datetime ())), std::forward<Args>(args)...);}
case value_t::local_date : {return visit_impl(bind_front(visitor, std::move(v.as_local_date ())), std::forward<Args>(args)...);}
case value_t::local_time : {return visit_impl(bind_front(visitor, std::move(v.as_local_time ())), std::forward<Args>(args)...);}
case value_t::array : {return visit_impl(bind_front(visitor, std::move(v.as_array ())), std::forward<Args>(args)...);}
case value_t::table : {return visit_impl(bind_front(visitor, std::move(v.as_table ())), std::forward<Args>(args)...);}
case value_t::empty : break;
default: break;
}
throw type_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid type.", v.location(), "here"), v.location());
}
} // detail
template<typename Visitor, typename ... Args>
detail::visit_result_t<Visitor, Args...>
visit(Visitor&& visitor, Args&& ... args)
{
return detail::visit_impl(std::forward<Visitor>(visitor), std::forward<Args>(args)...);
}
} // TOML11_INLINE_VERSION_NAMESPACE
} // toml
#endif // TOML11_VISIT_HPP