blob: 4ce8ea277171229a19bb062288203f03c6db9936 [file] [log] [blame]
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2001-2011 Hartmut Kaiser
http://spirit.sourceforge.net/
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#if !defined(BOOST_SPIRIT_X3_EXTRACT_REAL_APRIL_18_2006_0901AM)
#define BOOST_SPIRIT_X3_EXTRACT_REAL_APRIL_18_2006_0901AM
#include <cmath>
#include <boost/limits.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/spirit/home/x3/support/numeric_utils/pow10.hpp>
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
#include <boost/assert.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(push)
# pragma warning(disable: 4100) // 'p': unreferenced formal parameter
# pragma warning(disable: 4127) // conditional expression is constant
#endif
namespace boost { namespace spirit { namespace x3 { namespace extension
{
using x3::traits::pow10;
template <typename T>
inline bool
scale(int exp, T& n)
{
constexpr auto max_exp = std::numeric_limits<T>::max_exponent10;
constexpr auto min_exp = std::numeric_limits<T>::min_exponent10;
if (exp >= 0)
{
// return false if exp exceeds the max_exp
// do this check only for primitive types!
if (is_floating_point<T>() && exp > max_exp)
return false;
n *= pow10<T>(exp);
}
else
{
if (exp < min_exp)
{
n /= pow10<T>(-min_exp);
// return false if exp still exceeds the min_exp
// do this check only for primitive types!
exp += -min_exp;
if (is_floating_point<T>() && exp < min_exp)
return false;
n /= pow10<T>(-exp);
}
else
{
n /= pow10<T>(-exp);
}
}
return true;
}
inline bool
scale(int /*exp*/, unused_type /*n*/)
{
// no-op for unused_type
return true;
}
template <typename T>
inline bool
scale(int exp, int frac, T& n)
{
return scale(exp - frac, n);
}
inline bool
scale(int /*exp*/, int /*frac*/, unused_type /*n*/)
{
// no-op for unused_type
return true;
}
inline float
negate(bool neg, float n)
{
return neg ? (std::copysignf)(n, -1.f) : n;
}
inline double
negate(bool neg, double n)
{
return neg ? (std::copysign)(n, -1.) : n;
}
inline long double
negate(bool neg, long double n)
{
return neg ? (std::copysignl)(n, -1.) : n;
}
template <typename T>
inline T
negate(bool neg, T const& n)
{
return neg ? -n : n;
}
inline unused_type
negate(bool /*neg*/, unused_type n)
{
// no-op for unused_type
return n;
}
}}}}
namespace boost { namespace spirit { namespace x3
{
template <typename T, typename RealPolicies>
struct extract_real
{
template <typename Iterator, typename Attribute>
static bool
parse(Iterator& first, Iterator const& last, Attribute& attr,
RealPolicies const& p)
{
if (first == last)
return false;
Iterator save = first;
// Start by parsing the sign. neg will be true if
// we got a "-" sign, false otherwise.
bool neg = p.parse_sign(first, last);
// Now attempt to parse an integer
T n = 0;
bool got_a_number = p.parse_n(first, last, n);
// If we did not get a number it might be a NaN, Inf or a leading
// dot.
if (!got_a_number)
{
// Check whether the number to parse is a NaN or Inf
if (p.parse_nan(first, last, n) ||
p.parse_inf(first, last, n))
{
// If we got a negative sign, negate the number
traits::move_to(extension::negate(neg, n), attr);
return true; // got a NaN or Inf, return early
}
// If we did not get a number and our policies do not
// allow a leading dot, fail and return early (no-match)
if (!p.allow_leading_dot)
{
first = save;
return false;
}
}
bool e_hit = false;
Iterator e_pos;
int frac_digits = 0;
// Try to parse the dot ('.' decimal point)
if (p.parse_dot(first, last))
{
// We got the decimal point. Now we will try to parse
// the fraction if it is there. If not, it defaults
// to zero (0) only if we already got a number.
Iterator savef = first;
if (p.parse_frac_n(first, last, n))
{
// Optimization note: don't compute frac_digits if T is
// an unused_type. This should be optimized away by the compiler.
if (!is_same<T, unused_type>::value)
frac_digits =
static_cast<int>(std::distance(savef, first));
BOOST_ASSERT(frac_digits >= 0);
}
else if (!got_a_number || !p.allow_trailing_dot)
{
// We did not get a fraction. If we still haven't got a
// number and our policies do not allow a trailing dot,
// return no-match.
first = save;
return false;
}
// Now, let's see if we can parse the exponent prefix
e_pos = first;
e_hit = p.parse_exp(first, last);
}
else
{
// No dot and no number! Return no-match.
if (!got_a_number)
{
first = save;
return false;
}
// If we must expect a dot and we didn't see an exponent
// prefix, return no-match.
e_pos = first;
e_hit = p.parse_exp(first, last);
if (p.expect_dot && !e_hit)
{
first = save;
return false;
}
}
if (e_hit)
{
// We got the exponent prefix. Now we will try to parse the
// actual exponent. It is an error if it is not there.
int exp = 0;
if (p.parse_exp_n(first, last, exp))
{
// Got the exponent value. Scale the number by
// exp-frac_digits.
if (!extension::scale(exp, frac_digits, n))
return false;
}
else
{
// If there is no number, disregard the exponent altogether.
// by resetting 'first' prior to the exponent prefix (e|E)
first = e_pos;
// Scale the number by -frac_digits.
if (!extension::scale(-frac_digits, n))
return false;
}
}
else if (frac_digits)
{
// No exponent found. Scale the number by -frac_digits.
if (!extension::scale(-frac_digits, n))
return false;
}
// If we got a negative sign, negate the number
traits::move_to(extension::negate(neg, n), attr);
// Success!!!
return true;
}
};
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(pop)
#endif
}}}
#endif