summaryrefslogtreecommitdiffstats
path: root/src/third-party/scnlib/include/scn/scan/scan.h
blob: 20f4cd1a82a9ac7d479f6daa3faf454b6221e822 (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
// Copyright 2017 Elias Kosunen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file is a part of scnlib:
//     https://github.com/eliaskosunen/scnlib

#ifndef SCN_SCAN_SCAN_H
#define SCN_SCAN_SCAN_H

#include "../util/optional.h"
#include "common.h"
#include "vscan.h"

namespace scn {
    SCN_BEGIN_NAMESPACE

    namespace dummy {
    }

    /**
     * \tparam OriginalRange The type of the range passed to the scanning
     * function \param result Return value of `vscan` \return Result object
     *
     * \code{.cpp}
     * template <typename Range, typename... Args>
     * auto scan(Range&& r, string_view f, Args&... a) {
     *     auto range = scn::wrap(std::forward<Range>(r));
     *     auto args = scn::make_args_for(range, f, a...);
     *     auto ret = scn::vscan(std::move(range), f, {args});
     *     return scn::make_scan_result<Range>(std::move(ret));
     * }
     * \endcode
     */
    template <typename OriginalRange,
              typename Error = wrapped_error,
              typename WrappedRange>
    auto make_scan_result(vscan_result<WrappedRange> result)
        -> detail::scan_result_for_range<OriginalRange>
    {
        return detail::wrap_result(Error{result.err},
                                   detail::range_tag<OriginalRange>{},
                                   SCN_MOVE(result.range));
    }

    namespace detail {
        template <typename Range, typename Format, typename... Args>
        auto scan_boilerplate(Range&& r, const Format& f, Args&... a)
            -> detail::scan_result_for_range<Range>
        {
            static_assert(sizeof...(Args) > 0,
                          "Have to scan at least a single argument");
            static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
                          "Input needs to be a Range");

            auto range = wrap(SCN_FWD(r));
            auto format = detail::to_format(f);
            auto args = make_args_for(range, format, a...);
            auto ret = vscan(SCN_MOVE(range), format, {args});
            return make_scan_result<Range>(SCN_MOVE(ret));
        }

        template <typename Range, typename... Args>
        auto scan_boilerplate_default(Range&& r, Args&... a)
            -> detail::scan_result_for_range<Range>
        {
            static_assert(sizeof...(Args) > 0,
                          "Have to scan at least a single argument");
            static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
                          "Input needs to be a Range");

            auto range = wrap(SCN_FWD(r));
            auto format = static_cast<int>(sizeof...(Args));
            auto args = make_args_for(range, format, a...);
            auto ret = vscan_default(SCN_MOVE(range), format, {args});
            return make_scan_result<Range>(SCN_MOVE(ret));
        }

        template <typename Locale,
                  typename Range,
                  typename Format,
                  typename... Args>
        auto scan_boilerplate_localized(const Locale& loc,
                                        Range&& r,
                                        const Format& f,
                                        Args&... a)
            -> detail::scan_result_for_range<Range>
        {
            static_assert(sizeof...(Args) > 0,
                          "Have to scan at least a single argument");
            static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
                          "Input needs to be a Range");

            auto range = wrap(SCN_FWD(r));
            auto format = detail::to_format(f);
            SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
            auto locale =
                make_locale_ref<typename decltype(range)::char_type>(loc);
            SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE

            auto args = make_args_for(range, format, a...);
            auto ret = vscan_localized(SCN_MOVE(range), SCN_MOVE(locale),
                                       format, {args});
            return make_scan_result<Range>(SCN_MOVE(ret));
        }

    }  // namespace detail

    // scan

    // For some reason, Doxygen dislikes SCN_NODISCARD

    /**
     * The most fundamental part of the scanning API.
     * Reads from the range in \c r according to the format string \c f.
     *
     * \code{.cpp}
     * int i;
     * scn::scan("123", "{}", i);
     * // i == 123
     * \endcode
     */
#if SCN_DOXYGEN
    template <typename Range, typename Format, typename... Args>
    auto scan(Range&& r, const Format& f, Args&... a)
        -> detail::scan_result_for_range<Range>;
#else
    template <typename Range, typename Format, typename... Args>
    SCN_NODISCARD auto scan(Range&& r, const Format& f, Args&... a)
        -> detail::scan_result_for_range<Range>
    {
        return detail::scan_boilerplate(SCN_FWD(r), f, a...);
    }
#endif

    // default format

    /**
     * Equivalent to \ref scan, but with a
     * format string with the appropriate amount of space-separated `"{}"`s for
     * the number of arguments. Because this function doesn't have to parse the
     * format string, performance is improved.
     *
     * Adapted from the example for \ref scan
     * \code{.cpp}
     * int i;
     * scn::scan_default("123", i);
     * // i == 123
     * \endcode
     *
     * \see scan
     */
#if SCN_DOXYGEN
    template <typename Range, typename... Args>
    auto scan_default(Range&& r, Args&... a)
        -> detail::scan_result_for_range<Range>;
#else
    template <typename Range, typename... Args>
    SCN_NODISCARD auto scan_default(Range&& r, Args&... a)
        -> detail::scan_result_for_range<Range>
    {
        return detail::scan_boilerplate_default(std::forward<Range>(r), a...);
    }
#endif

    // scan localized

    /**
     * Read from the range in \c r using the locale in \c loc.
     * \c loc must be a \c std::locale. The parameter is a template to avoid
     * inclusion of `<locale>`.
     *
     * Use of this function is discouraged, due to the overhead involved
     * with locales. Note, that the other functions are completely
     * locale-agnostic, and aren't affected by changes to the global C
     * locale.
     *
     * \code{.cpp}
     * double d;
     * scn::scan_localized(std::locale{"fi_FI"}, "3,14", "{}", d);
     * // d == 3.14
     * \endcode
     *
     * \see scan
     */
#if SCN_DOXYGEN
    template <typename Locale,
              typename Range,
              typename Format,
              typename... Args>
    auto scan_localized(const Locale& loc,
                        Range&& r,
                        const Format& f,
                        Args&... a) -> detail::scan_result_for_range<Range>;
#else
    template <typename Locale,
              typename Range,
              typename Format,
              typename... Args>
    SCN_NODISCARD auto scan_localized(const Locale& loc,
                                      Range&& r,
                                      const Format& f,
                                      Args&... a)
        -> detail::scan_result_for_range<Range>
    {
        return detail::scan_boilerplate_localized(loc, std::forward<Range>(r),
                                                  f, a...);
    }
#endif

    // value

    /**
     * Scans a single value with the default options, returning it instead of
     * using an output parameter.
     *
     * The parsed value is in `ret.value()`, if `ret == true`.
     * The return type of this function is otherwise similar to other scanning
     * functions.
     *
     * \code{.cpp}
     * auto ret = scn::scan_value<int>("42");
     * if (ret) {
     *   // ret.value() == 42
     * }
     * \endcode
     */
#if SCN_DOXYGEN
    template <typename T, typename Range>
    auto scan_value(Range&& r)
        -> detail::generic_scan_result_for_range<expected<T>, Range>;
#else
    template <typename T, typename Range>
    SCN_NODISCARD auto scan_value(Range&& r)
        -> detail::generic_scan_result_for_range<expected<T>, Range>
    {
        T value;
        auto range = wrap(SCN_FWD(r));
        auto args = make_args_for(range, 1, value);
        auto ret = vscan_default(SCN_MOVE(range), 1, {args});
        if (ret.err) {
            return detail::wrap_result(expected<T>{value},
                                       detail::range_tag<Range>{},
                                       SCN_MOVE(ret.range));
        }
        return detail::wrap_result(expected<T>{ret.err},
                                   detail::range_tag<Range>{},
                                   SCN_MOVE(ret.range));
    }
#endif

    // input

    /**
     * Otherwise equivalent to \ref scan, expect reads from `stdin`.
     * Character type is determined by the format string.
     * Syncs with `<cstdio>`.
     */
    template <typename Format,
              typename... Args,
              typename CharT = ranges::range_value_t<Format>>
#if SCN_DOXYGEN
    auto input(const Format& f, Args&... a)
        -> detail::scan_result_for_range<basic_file<CharT>&>;
#else
    SCN_NODISCARD auto input(const Format& f, Args&... a)
        -> detail::scan_result_for_range<basic_file<CharT>&>
    {
        auto& range = stdin_range<CharT>();
        auto ret = detail::scan_boilerplate(range, f, a...);
        range.sync();
        ret.range().reset_begin_iterator();
        return ret;
    }
#endif

    // prompt

    namespace detail {
        inline void put_stdout(const char* str)
        {
            std::fputs(str, stdout);
        }
        inline void put_stdout(const wchar_t* str)
        {
            std::fputws(str, stdout);
        }
    }  // namespace detail

    /**
     * Equivalent to \ref input, except writes what's in `p` to `stdout`.
     *
     * \code{.cpp}
     * int i{};
     * scn::prompt("What's your favorite number? ", "{}", i);
     * // Equivalent to:
     * //   std::fputs("What's your favorite number? ", stdout);
     * //   scn::input("{}", i);
     * \endcode
     */
#if SCN_DOXYGEN
    template <typename CharT, typename Format, typename... Args>
    auto prompt(const CharT* p, const Format& f, Args&... a)
        -> detail::scan_result_for_range<basic_file<CharT>&>;
#else
    template <typename CharT, typename Format, typename... Args>
    SCN_NODISCARD auto prompt(const CharT* p, const Format& f, Args&... a)
        -> detail::scan_result_for_range<basic_file<CharT>&>
    {
        SCN_EXPECT(p != nullptr);
        detail::put_stdout(p);

        return input(f, a...);
    }
#endif

    // parse_integer

    /**
     * Parses an integer into \c val in base \c base from \c str.
     * Returns a pointer past the last character read, or an error.
     *
     * @param str source, can't be empty, cannot have:
     *   - preceding whitespace
     *   - preceding \c "0x" or \c "0" (base is determined by the \c base
     * parameter)
     *   - \c '+' sign (\c '-' is fine)
     * @param val parsed integer, must be value-constructed
     * @param base between [2,36]
     */
#if SCN_DOXYGEN
    template <typename T, typename CharT>
    expected<const CharT*> parse_integer(basic_string_view<CharT> str,
                                         T& val,
                                         int base = 10);
#else
    template <typename T, typename CharT>
    SCN_NODISCARD expected<const CharT*>
    parse_integer(basic_string_view<CharT> str, T& val, int base = 10)
    {
        SCN_EXPECT(!str.empty());
        auto s = detail::simple_integer_scanner<T>{};
        SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
        auto ret =
            s.scan_lower(span<const CharT>(str.data(), str.size()), val, base);
        SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
        if (!ret) {
            return ret.error();
        }
        return {ret.value()};
    }
#endif

    /**
     * Parses float into \c val from \c str.
     * Returns a pointer past the last character read, or an error.
     *
     * @param str source, can't be empty
     * @param val parsed float, must be value-constructed
     */
#if SCN_DOXYGEN
    template <typename T, typename CharT>
    expected<const CharT*> parse_float(basic_string_view<CharT> str, T& val);
#else
    template <typename T, typename CharT>
    SCN_NODISCARD expected<const CharT*> parse_float(
        basic_string_view<CharT> str,
        T& val)
    {
        SCN_EXPECT(!str.empty());
        auto s = detail::float_scanner_access<T>{};
        auto ret = s._read_float(val, make_span(str.data(), str.size()),
                                 detail::ascii_widen<CharT>('.'));
        if (!ret) {
            return ret.error();
        }
        return {str.data() + ret.value()};
    }
#endif

    /**
     * A convenience function for creating scanners for user-provided types.
     *
     * Wraps \ref vscan_usertype
     *
     * Example use:
     *
     * \code{.cpp}
     * // Type has two integers, and its textual representation is
     * // "[val1, val2]"
     * struct user_type {
     *     int val1;
     *     int val2;
     * };
     *
     * template <>
     * struct scn::scanner<user_type> : public scn::empty_parser {
     *     template <typename Context>
     *     error scan(user_type& val, Context& ctx)
     *     {
     *         return scan_usertype(ctx, "[{}, {}]", val.val1, val.val2);
     *     }
     * };
     * \endcode
     *
     * \param ctx Context given to the scanning function
     * \param f Format string to parse
     * \param a Member types (etc) to parse
     */
#if SCN_DOXYGEN
    template <typename WrappedRange, typename Format, typename... Args>
    error scan_usertype(basic_context<WrappedRange>& ctx,
                        const Format& f,
                        Args&... a);
#else
    template <typename WrappedRange, typename Format, typename... Args>
    SCN_NODISCARD error scan_usertype(basic_context<WrappedRange>& ctx,
                                      const Format& f,
                                      Args&... a)
    {
        static_assert(sizeof...(Args) > 0,
                      "Have to scan at least a single argument");

        using char_type = typename WrappedRange::char_type;
        auto args = make_args<basic_context<WrappedRange>,
                              basic_parse_context<char_type>>(a...);
        return vscan_usertype(ctx, basic_string_view<char_type>(f), {args});
    }
#endif

    SCN_END_NAMESPACE
}  // namespace scn

#endif  // SCN_DETAIL_SCAN_H