summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/spirit/test/x3/extract_int.cpp
blob: f7b97cdefbfa0710ae1141e6f4d492150be2ac3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*=============================================================================
  Copyright (c) 2018 Nikita Kniazev

  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 <boost/detail/lightweight_test.hpp>
#include <boost/spirit/home/x3/support/numeric_utils/extract_int.hpp>
#include <cmath> // for std::pow
#include <cstdio>
#include <iosfwd>
#include <limits>

#ifdef _MSC_VER
# pragma warning(disable: 4127)   // conditional expression is constant
#endif

template <int Min, int Max>
struct custom_int
{
    custom_int() = default;
    constexpr custom_int(int value) : value_{value} {}

    custom_int operator+(custom_int x) const { return value_ + x.value_; }
    custom_int operator-(custom_int x) const { return value_ - x.value_; }
    custom_int operator*(custom_int x) const { return value_ * x.value_; }
    custom_int operator/(custom_int x) const { return value_ / x.value_; }

    custom_int& operator+=(custom_int x) { value_ += x.value_; return *this; }
    custom_int& operator-=(custom_int x) { value_ -= x.value_; return *this; }
    custom_int& operator*=(custom_int x) { value_ *= x.value_; return *this; }
    custom_int& operator/=(custom_int x) { value_ /= x.value_; return *this; }
    custom_int& operator++() { ++value_; return *this; }
    custom_int& operator--() { --value_; return *this; }
    custom_int operator++(int) { return value_++; }
    custom_int operator--(int) { return value_--; }

    custom_int operator+() { return +value_; }
    custom_int operator-() { return -value_; }

    bool operator< (custom_int x) const { return value_ <  x.value_; }
    bool operator> (custom_int x) const { return value_ >  x.value_; }
    bool operator<=(custom_int x) const { return value_ <= x.value_; }
    bool operator>=(custom_int x) const { return value_ >= x.value_; }
    bool operator==(custom_int x) const { return value_ == x.value_; }
    bool operator!=(custom_int x) const { return value_ != x.value_; }

    template <typename Char, typename Traits>
    friend std::basic_ostream<Char, Traits>&
    operator<<(std::basic_ostream<Char, Traits>& os, custom_int x) {
        return os << x.value_;
    }

    static constexpr int max = Max;
    static constexpr int min = Min;

private:
    int value_;
};

namespace utils {

template <int Min, int Max> struct digits;
template <> struct digits<-9,  9>  { static constexpr int r2 = 3, r10 = 1; };
template <> struct digits<-10, 10> { static constexpr int r2 = 3, r10 = 1; };
template <> struct digits<-15, 15> { static constexpr int r2 = 3, r10 = 1; };

}

namespace std {

template <int Min, int Max>
class numeric_limits<custom_int<Min, Max>> : public numeric_limits<int>
{
public:
    static constexpr custom_int<Min, Max> max() noexcept { return Max; }
    static constexpr custom_int<Min, Max> min() noexcept { return Min; }
    static constexpr custom_int<Min, Max> lowest() noexcept { return min(); }
    static_assert(numeric_limits<int>::radix == 2, "hardcoded for digits of radix 2");
    static constexpr int digits = utils::digits<Min, Max>::r2;
    static constexpr int digits10 = utils::digits<Min, Max>::r10;
};

}

namespace x3 = boost::spirit::x3;

template <typename T, int Base, int MaxDigits>
void test_overflow_handling(char const* begin, char const* end, int i)
{
    // Check that parser fails on overflow
    static_assert(std::numeric_limits<T>::is_bounded, "tests prerequest");
    BOOST_ASSERT_MSG(MaxDigits == -1 || static_cast<int>(std::pow(float(Base), MaxDigits)) > T::max,
                     "test prerequest");
    int initial = Base - i % Base; // just a 'random' non-equal to i number
    T x { initial };
    char const* it = begin;
    bool r = x3::extract_int<T, Base, 1, MaxDigits>::call(it, end, x);
    if (T::min <= i && i <= T::max) {
        BOOST_TEST(r);
        BOOST_TEST(it == end);
        BOOST_TEST_EQ(x, i);
    }
    else
        if (MaxDigits == -1) // TODO: Looks like a regression. See #430
    {
        BOOST_TEST(!r);
        BOOST_TEST(it == begin);
    }
}

template <typename T, int Base>
void test_unparsed_digits_are_not_consumed(char const* it, char const* end, int i)
{
    // Check that unparsed digits are not consumed
    static_assert(T::min <= -Base+1, "test prerequest");
    static_assert(T::max >=  Base-1, "test prerequest");
    bool has_sign = *it == '+' || *it == '-';
    auto len = end - it;
    int initial = Base - i % Base; // just a 'random' non-equal to i number
    T x { initial };
    bool r = x3::extract_int<T, Base, 1, 1>::call(it, end, x);
    BOOST_TEST(r);
    if (-Base < i && i < Base) {
        BOOST_TEST(it == end);
        BOOST_TEST_EQ(x, i);
    }
    else {
        BOOST_TEST_EQ(end - it, len - 1 - has_sign);
        BOOST_TEST_EQ(x, i / Base);
    }
}

template <typename T, int Base>
void run_tests(char const* begin, char const* end, int i)
{
    // Check that parser fails on overflow
    test_overflow_handling<T, Base, -1>(begin, end, i);
    // Check that MaxDigits > digits10 behave like MaxDigits=-1
    test_overflow_handling<T, Base, 2>(begin, end, i);
    // Check that unparsed digits are not consumed
    test_unparsed_digits_are_not_consumed<T, Base>(begin, end, i);
}

int main()
{
    for (int i = -30; i <= 30; ++i) {
        char s[4];
        std::snprintf(s, 4, "%d", i);
        auto begin = &s[0], end = begin + std::strlen(begin);

        // log(Base, abs(MinOrMax) + 1) == digits
        run_tests<custom_int<-9, 9>, 10>(begin, end, i);
        // (MinOrMax % Base) == 0
        run_tests<custom_int<-10, 10>, 10>(begin, end, i);
        // (MinOrMax % Base) != 0
        run_tests<custom_int<-15, 15>, 10>(begin, end, i);
    }

    return boost::report_errors();
}