2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-19 04:22:13 +00:00

Use std::from_chars, if available; otherwise, use boost::charconv::from_chars,

if available; otherwise, use the Spirit X3 number parsers.

Fixes #113.
This commit is contained in:
Zach Laine
2024-02-26 00:59:55 -06:00
parent 42f66d9079
commit e3c46c10c2
2 changed files with 126 additions and 14 deletions

View File

@@ -12,12 +12,29 @@
#ifndef BOOST_PARSER_DETAIL_NUMERIC_HPP
#define BOOST_PARSER_DETAIL_NUMERIC_HPP
#include <boost/parser/detail/text/unpack.hpp>
#include <version>
#if defined(__cpp_lib_to_chars)
#include <charconv>
#define BOOST_PARSER_HAVE_STD_CHARCONV
#elif __has_include(<boost/charconv.hpp>)
#include <boost/charconv.hpp>
#define BOOST_PARSER_HAVE_BOOST_CHARCONV
#endif
#include <type_traits>
#include <cmath>
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV) || \
defined(BOOST_PARSER_HAVE_BOOST_CHARCONV)
#define BOOST_PARSER_HAVE_CHARCONV
#endif
namespace boost { namespace parser { namespace detail_spirit_x3 {
struct unused_type{};
namespace boost::parser::detail_spirit_x3 {
struct unused_type
{};
// Copied from boost/spirit/home/support/char_class.hpp (Boost 1.71), and
// modified not to use Boost.TypeTraits.
@@ -963,7 +980,104 @@ namespace boost { namespace parser { namespace detail_spirit_x3 {
{
static bool const expect_dot = true;
};
}
}}}
namespace boost::parser::detail::numeric {
template<typename I, typename S>
constexpr bool common_range = std::is_same_v<I, S>;
template<typename I, typename S>
using unpacked_iter = decltype(text::unpack_iterator_and_sentinel(
std::declval<I>(), std::declval<S>())
.first);
template<typename I, typename S>
constexpr bool unpacks_to_chars =
std::is_pointer_v<unpacked_iter<I, S>> && std::is_same_v<
std::remove_cv_t<std::remove_reference_t<
std::remove_pointer_t<unpacked_iter<I, S>>>>,
char>;
template<int MinDigits, int MaxDigits, typename I, typename S>
#if defined(BOOST_PARSER_HAVE_CHARCONV)
constexpr bool use_charconv_int =
MinDigits == 1 && MaxDigits == -1 &&
common_range<I, S> && unpacks_to_chars<I, S>;
#else
constexpr bool use_charconv_int = false;
#endif
template<
bool Signed,
int Radix,
int MinDigits,
int MaxDigits,
typename I,
typename S,
typename T>
bool parse_int(I & first, S last, T & attr)
{
if constexpr (use_charconv_int<MinDigits, MaxDigits, I, S>) {
#if defined(BOOST_PARSER_HAVE_CHARCONV)
auto unpacked = text::unpack_iterator_and_sentinel(first, last);
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV)
std::from_chars_result const result = std::from_chars(
#else
charconv::from_chars_result const result = charconv::from_chars(
#endif
unpacked.first, unpacked.last, attr, Radix);
if (result.ec == std::errc()) {
first = unpacked.repack(result.ptr);
return true;
}
return false;
#endif
} else if constexpr (Signed) {
using extract =
detail_spirit_x3::extract_int<T, Radix, MinDigits, MaxDigits>;
return extract::call(first, last, attr);
} else {
using extract =
detail_spirit_x3::extract_uint<T, Radix, MinDigits, MaxDigits>;
return extract::call(first, last, attr);
}
}
template<typename I, typename S>
#if defined(BOOST_PARSER_HAVE_CHARCONV)
constexpr bool use_charconv_real =
common_range<I, S> && unpacks_to_chars<I, S>;
#else
constexpr bool use_charconv_real = false;
#endif
template<typename I, typename S, typename T>
bool parse_real(I & first, S last, T & attr)
{
if constexpr (use_charconv_real<I, S>) {
#if defined(BOOST_PARSER_HAVE_CHARCONV)
auto unpacked = text::unpack_iterator_and_sentinel(first, last);
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV)
std::from_chars_result const result = std::from_chars(
#else
charconv::from_chars_result const result = charconv::from_chars(
#endif
unpacked.first, unpacked.last, attr);
if (result.ec == std::errc()) {
first = unpacked.repack(result.ptr);
return true;
}
return false;
#endif
} else {
detail_spirit_x3::real_policies<T> policies;
using extract = detail_spirit_x3::
extract_real<T, detail_spirit_x3::real_policies<T>>;
return extract::parse(first, last, attr, policies);
}
}
}
#endif

View File

@@ -6882,11 +6882,12 @@ namespace boost { namespace parser {
{
auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
using extract =
detail_spirit_x3::extract_uint<T, Radix, MinDigits, MaxDigits>;
T attr = 0;
success = extract::call(first, last, attr);
if (attr != detail::resolve(context, expected_))
auto const initial = first;
success =
detail::numeric::parse_int<false, Radix, MinDigits, MaxDigits>(
first, last, attr);
if (first == initial || attr != detail::resolve(context, expected_))
success = false;
if (success)
detail::assign(retval, attr);
@@ -6993,11 +6994,11 @@ namespace boost { namespace parser {
{
auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
using extract =
detail_spirit_x3::extract_int<T, Radix, MinDigits, MaxDigits>;
T attr = 0;
auto const initial = first;
success = extract::call(first, last, attr);
success =
detail::numeric::parse_int<true, Radix, MinDigits, MaxDigits>(
first, last, attr);
if (first == initial || attr != detail::resolve(context, expected_))
success = false;
if (success)
@@ -7081,12 +7082,9 @@ namespace boost { namespace parser {
{
auto _ = detail::scoped_trace(
*this, first, last, context, flags, retval);
detail_spirit_x3::real_policies<T> policies;
using extract = detail_spirit_x3::
extract_real<T, detail_spirit_x3::real_policies<T>>;
T attr = 0;
auto const initial = first;
success = extract::parse(first, last, attr, policies);
success = detail::numeric::parse_real(first, last, attr);
if (first == initial)
success = false;
if (success)