blob: 4ae7f9c1c0f8d69bb50bfc1d5ff77e269cb7e9c5 [file] [log] [blame]
#ifndef BOOST_SPIRIT_MATH_NONFINITE_NUM_FACETS_HPP
#define BOOST_SPIRIT_MATH_NONFINITE_NUM_FACETS_HPP
// Copyright (c) 2006 Johan Rade
// 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)
#include <cstring>
#include <ios>
#include <limits>
#include <locale>
#include <boost/spirit/home/support/detail/math/fpclassify.hpp>
#include <boost/spirit/home/support/detail/math/signbit.hpp>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4127 4511 4512 4706)
#endif
namespace boost {
namespace spirit {
namespace math {
// flags -----------------------------------------------------------------------
const int legacy = 0x1;
const int signed_zero = 0x2;
const int trap_infinity = 0x4;
const int trap_nan = 0x8;
// class nonfinite_num_put -----------------------------------------------------
template<
class CharType,
class OutputIterator = std::ostreambuf_iterator<CharType>
>
class nonfinite_num_put : public std::num_put<CharType, OutputIterator> {
public:
explicit nonfinite_num_put(int flags = 0) : flags_(flags) {}
protected:
virtual OutputIterator do_put(
OutputIterator it, std::ios_base& iosb,
CharType fill, double val) const
{
put_and_reset_width(it, iosb, fill, val);
return it;
}
virtual OutputIterator do_put(
OutputIterator it, std::ios_base& iosb,
CharType fill, long double val) const
{
put_and_reset_width(it, iosb, fill, val);
return it;
}
private:
template<class ValType> void put_and_reset_width(
OutputIterator& it, std::ios_base& iosb,
CharType fill, ValType val) const
{
put_impl(it, iosb, fill, val);
iosb.width(0);
}
template<class ValType> void put_impl(
OutputIterator& it, std::ios_base& iosb,
CharType fill, ValType val) const
{
switch((boost::math::fpclassify)(val)) {
case FP_INFINITE:
if(flags_ & trap_infinity)
throw std::ios_base::failure("Infinity");
else if((boost::math::signbit)(val))
put_num_and_fill(it, iosb, "-", "inf", fill);
else if(iosb.flags() & std::ios_base::showpos)
put_num_and_fill(it, iosb, "+", "inf", fill);
else
put_num_and_fill(it, iosb, "", "inf", fill);
break;
case FP_NAN:
if(flags_ & trap_nan)
throw std::ios_base::failure("NaN");
else if((boost::math::signbit)(val))
put_num_and_fill(it, iosb, "-", "nan", fill);
else if(iosb.flags() & std::ios_base::showpos)
put_num_and_fill(it, iosb, "+", "nan", fill);
else
put_num_and_fill(it, iosb, "", "nan", fill);
break;
case FP_ZERO:
if(flags_ & signed_zero) {
if((boost::math::signbit)(val))
put_num_and_fill(it, iosb, "-", "0", fill);
else if(iosb.flags() & std::ios_base::showpos)
put_num_and_fill(it, iosb, "+", "0", fill);
else
put_num_and_fill(it, iosb, "", "0", fill);
}
else
put_num_and_fill(it, iosb, "", "0", fill);
break;
default:
it = std::num_put<CharType, OutputIterator>::do_put(
it, iosb, fill, val);
break;
}
}
void put_num_and_fill(
OutputIterator& it, std::ios_base& iosb, const char* prefix,
const char* body, CharType fill) const
{
int width = (int)strlen(prefix) + (int)strlen(body);
std::ios_base::fmtflags adjust
= iosb.flags() & std::ios_base::adjustfield;
const std::ctype<CharType>& ct
= std::use_facet<std::ctype<CharType> >(iosb.getloc());
if(adjust != std::ios_base::internal && adjust != std::ios_base::left)
put_fill(it, iosb, fill, width);
while(*prefix)
*it = ct.widen(*(prefix++));
if(adjust == std::ios_base::internal)
put_fill(it, iosb, fill, width);
if(iosb.flags() & std::ios_base::uppercase) {
while(*body)
*it = ct.toupper(ct.widen(*(body++)));
}
else {
while(*body)
*it = ct.widen(*(body++));
}
if(adjust == std::ios_base::left)
put_fill(it, iosb, fill, width);
}
void put_fill(
OutputIterator& it, std::ios_base& iosb,
CharType fill, int width) const
{
for(int i = iosb.width() - width; i > 0; --i)
*it = fill;
}
private:
const int flags_;
};
// class nonfinite_num_get ------------------------------------------------------
template<
class CharType,
class InputIterator = std::istreambuf_iterator<CharType>
>
class nonfinite_num_get : public std::num_get<CharType, InputIterator> {
public:
explicit nonfinite_num_get(int flags = 0) : flags_(flags) {}
protected:
virtual InputIterator do_get(
InputIterator it, InputIterator end, std::ios_base& iosb,
std::ios_base::iostate& state, float& val) const
{
get_and_check_eof(it, end, iosb, state, val);
return it;
}
virtual InputIterator do_get(
InputIterator it, InputIterator end, std::ios_base& iosb,
std::ios_base::iostate& state, double& val) const
{
get_and_check_eof(it, end, iosb, state, val);
return it;
}
virtual InputIterator do_get(
InputIterator it, InputIterator end, std::ios_base& iosb,
std::ios_base::iostate& state, long double& val) const
{
get_and_check_eof(it, end, iosb, state, val);
return it;
}
//..............................................................................
private:
template<class ValType> static ValType positive_nan()
{
// on some platforms quiet_NaN() is negative
return (boost::math::copysign)(
std::numeric_limits<ValType>::quiet_NaN(), 1);
}
template<class ValType> void get_and_check_eof(
InputIterator& it, InputIterator end, std::ios_base& iosb,
std::ios_base::iostate& state, ValType& val) const
{
get_signed(it, end, iosb, state, val);
if(it == end)
state |= std::ios_base::eofbit;
}
template<class ValType> void get_signed(
InputIterator& it, InputIterator end, std::ios_base& iosb,
std::ios_base::iostate& state, ValType& val) const
{
const std::ctype<CharType>& ct
= std::use_facet<std::ctype<CharType> >(iosb.getloc());
char c = peek_char(it, end, ct);
bool negative = (c == '-');
if(negative || c == '+') {
++it;
c = peek_char(it, end, ct);
if(c == '-' || c == '+') {
// without this check, "++5" etc would be accepted
state |= std::ios_base::failbit;
return;
}
}
get_unsigned(it, end, iosb, ct, state, val);
if(negative)
val = (boost::math::changesign)(val);
}
template<class ValType> void get_unsigned(
InputIterator& it, InputIterator end, std::ios_base& iosb,
const std::ctype<CharType>& ct,
std::ios_base::iostate& state, ValType& val) const
{
switch(peek_char(it, end, ct)) {
case 'i':
get_i(it, end, ct, state, val);
break;
case 'n':
get_n(it, end, ct, state, val);
break;
case 'q':
case 's':
get_q(it, end, ct, state, val);
break;
default:
it = std::num_get<CharType, InputIterator>::do_get(
it, end, iosb, state, val);
if((flags_ & legacy) && val == static_cast<ValType>(1)
&& peek_char(it, end, ct) == '#')
get_one_hash(it, end, ct, state, val);
break;
}
}
//..........................................................................
template<class ValType> void get_i(
InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
std::ios_base::iostate& state, ValType& val) const
{
if(!std::numeric_limits<ValType>::has_infinity
|| (flags_ & trap_infinity)) {
state |= std::ios_base::failbit;
return;
}
++it;
if(!match_string(it, end, ct, "nf")) {
state |= std::ios_base::failbit;
return;
}
if(peek_char(it, end, ct) != 'i') {
val = std::numeric_limits<ValType>::infinity(); // "inf"
return;
}
++it;
if(!match_string(it, end, ct, "nity")) {
state |= std::ios_base::failbit;
return;
}
val = std::numeric_limits<ValType>::infinity(); // "infinity"
}
template<class ValType> void get_n(
InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
std::ios_base::iostate& state, ValType& val) const
{
if(!std::numeric_limits<ValType>::has_quiet_NaN
|| (flags_ & trap_nan)) {
state |= std::ios_base::failbit;
return;
}
++it;
if(!match_string(it, end, ct, "an")) {
state |= std::ios_base::failbit;
return;
}
switch(peek_char(it, end, ct)) {
case 'q':
case 's':
if(flags_ && legacy)
++it;
break; // "nanq", "nans"
case '(':
{
++it;
char c;
while((c = peek_char(it, end, ct))
&& c != ')' && c != ' ' && c != '\n' && c != '\t')
++it;
if(c != ')') {
state |= std::ios_base::failbit;
return;
}
++it;
break; // "nan(...)"
}
default:
break; // "nan"
}
val = positive_nan<ValType>();
}
template<class ValType> void get_q(
InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
std::ios_base::iostate& state, ValType& val) const
{
if(!std::numeric_limits<ValType>::has_quiet_NaN
|| (flags_ & trap_nan) || !(flags_ & legacy)) {
state |= std::ios_base::failbit;
return;
}
++it;
if(!match_string(it, end, ct, "nan")) {
state |= std::ios_base::failbit;
return;
}
val = positive_nan<ValType>(); // qnan, snan
}
template<class ValType> void get_one_hash(
InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
std::ios_base::iostate& state, ValType& val) const
{
++it;
switch(peek_char(it, end, ct)) {
case 'i':
get_one_hash_i(it, end, ct, state, val);
return;
case 'q':
case 's':
if(std::numeric_limits<ValType>::has_quiet_NaN
&& !(flags_ & trap_nan)) {
++it;
if(match_string(it, end, ct, "nan")) {
// "1.#QNAN", "1.#SNAN"
++it;
val = positive_nan<ValType>();
return;
}
}
break;
default:
break;
}
state |= std::ios_base::failbit;
}
template<class ValType> void get_one_hash_i(
InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
std::ios_base::iostate& state, ValType& val) const
{
++it;
if(peek_char(it, end, ct) == 'n') {
++it;
switch(peek_char(it, end, ct)) {
case 'f': // "1.#INF"
if(std::numeric_limits<ValType>::has_infinity
&& !(flags_ & trap_infinity)) {
++it;
val = std::numeric_limits<ValType>::infinity();
return;
}
break;
case 'd': // 1.#IND"
if(std::numeric_limits<ValType>::has_quiet_NaN
&& !(flags_ & trap_nan)) {
++it;
val = positive_nan<ValType>();
return;
}
break;
default:
break;
}
}
state |= std::ios_base::failbit;
}
//..........................................................................
char peek_char(
InputIterator& it, InputIterator end,
const std::ctype<CharType>& ct) const
{
if(it == end) return 0;
return ct.narrow(ct.tolower(*it), 0);
}
bool match_string(
InputIterator& it, InputIterator end,
const std::ctype<CharType>& ct, const char* s) const
{
while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0)) {
++s;
++it;
}
return !*s;
}
private:
const int flags_;
};
//------------------------------------------------------------------------------
} // namespace math
} // namespace spirit
} // namespace boost
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif