summaryrefslogtreecommitdiffstats
path: root/src/third-party/scnlib/include/scn/scan
diff options
context:
space:
mode:
Diffstat (limited to 'src/third-party/scnlib/include/scn/scan')
-rw-r--r--src/third-party/scnlib/include/scn/scan/common.h131
-rw-r--r--src/third-party/scnlib/include/scn/scan/getline.h186
-rw-r--r--src/third-party/scnlib/include/scn/scan/ignore.h189
-rw-r--r--src/third-party/scnlib/include/scn/scan/istream.h147
-rw-r--r--src/third-party/scnlib/include/scn/scan/list.h450
-rw-r--r--src/third-party/scnlib/include/scn/scan/scan.h444
-rw-r--r--src/third-party/scnlib/include/scn/scan/vscan.h208
7 files changed, 1755 insertions, 0 deletions
diff --git a/src/third-party/scnlib/include/scn/scan/common.h b/src/third-party/scnlib/include/scn/scan/common.h
new file mode 100644
index 0000000..ccdd825
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/common.h
@@ -0,0 +1,131 @@
+// 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_COMMON_H
+#define SCN_SCAN_COMMON_H
+
+#include "../detail/locale.h"
+#include "../detail/result.h"
+#include "../unicode/common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename CharT>
+ constexpr int to_format(int i)
+ {
+ return i;
+ }
+ template <typename T>
+ constexpr auto to_format(T&& f) -> decltype(string_view{SCN_FWD(f)})
+ {
+ return {SCN_FWD(f)};
+ }
+ template <typename T>
+ constexpr auto to_format(T&& f) -> decltype(wstring_view{SCN_FWD(f)})
+ {
+ return {SCN_FWD(f)};
+ }
+ template <typename CharT>
+ basic_string_view<CharT> to_format(const std::basic_string<CharT>& str)
+ {
+ return {str.data(), str.size()};
+ }
+
+ template <typename CharT>
+ struct until_pred {
+ array<CharT, 4> until;
+ size_t size;
+
+ constexpr until_pred(CharT ch) : until({{ch}}), size(1) {}
+ until_pred(code_point cp)
+ {
+ auto ret = encode_code_point(until.begin(), until.end(), cp);
+ SCN_ENSURE(ret);
+ size = ret.value() - until.begin();
+ }
+
+ SCN_CONSTEXPR14 bool operator()(span<const CharT> ch) const
+ {
+ if (ch.size() != size) {
+ return false;
+ }
+ for (size_t i = 0; i < ch.size(); ++i) {
+ if (ch[i] != until[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ static constexpr bool is_localized()
+ {
+ return false;
+ }
+ constexpr bool is_multibyte() const
+ {
+ return size != 1;
+ }
+ };
+
+ template <typename Error, typename Range>
+ using generic_scan_result_for_range = decltype(detail::wrap_result(
+ SCN_DECLVAL(Error),
+ SCN_DECLVAL(detail::range_tag<Range>),
+ SCN_DECLVAL(range_wrapper_for_t<Range>)));
+ template <typename Range>
+ using scan_result_for_range =
+ generic_scan_result_for_range<wrapped_error, Range>;
+ } // namespace detail
+
+ template <typename T>
+ struct discard_type {
+ discard_type() = default;
+ };
+
+ /**
+ * Scans an instance of `T`, but doesn't store it anywhere.
+ * Uses `scn::temp` internally, so the user doesn't have to bother.
+ *
+ * \code{.cpp}
+ * int i{};
+ * // 123 is discarded, 456 is read into `i`
+ * auto result = scn::scan("123 456", "{} {}", scn::discard<T>(), i);
+ * // result == true
+ * // i == 456
+ * \endcode
+ */
+ template <typename T>
+ discard_type<T>& discard()
+ {
+ return temp(discard_type<T>{})();
+ }
+
+ template <typename T>
+ struct scanner<discard_type<T>> : public scanner<T> {
+ template <typename Context>
+ error scan(discard_type<T>&, Context& ctx)
+ {
+ T tmp;
+ return scanner<T>::scan(tmp, ctx);
+ }
+ };
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/getline.h b/src/third-party/scnlib/include/scn/scan/getline.h
new file mode 100644
index 0000000..c330969
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/getline.h
@@ -0,0 +1,186 @@
+// 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_GETLINE_H
+#define SCN_SCAN_GETLINE_H
+
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename WrappedRange,
+ typename String,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error getline_impl(WrappedRange& r, String& str, Until until)
+ {
+ auto pred = until_pred<CharT>{until};
+ auto s = read_until_space_zero_copy(r, pred, true);
+ if (!s) {
+ return s.error();
+ }
+ if (s.value().size() != 0) {
+ auto size = s.value().size();
+ if (pred(s.value().last(1))) {
+ --size;
+ }
+ str.clear();
+ str.resize(size);
+ std::copy(s.value().begin(), s.value().begin() + size,
+ str.begin());
+ return {};
+ }
+
+ String tmp;
+ auto out = std::back_inserter(tmp);
+ auto e = read_until_space(r, out, pred, true);
+ if (!e) {
+ return e;
+ }
+ if (pred(span<const CharT>(&*(tmp.end() - 1), 1))) {
+ tmp.pop_back();
+ }
+ str = SCN_MOVE(tmp);
+ return {};
+ }
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error getline_impl(WrappedRange& r,
+ basic_string_view<CharT>& str,
+ Until until)
+ {
+ static_assert(
+ WrappedRange::is_contiguous,
+ "Cannot getline a string_view from a non-contiguous range");
+ auto pred = until_pred<CharT>{until};
+ auto s = read_until_space_zero_copy(r, pred, true);
+ if (!s) {
+ return s.error();
+ }
+ SCN_ASSERT(s.value().size(), "");
+ auto size = s.value().size();
+ if (pred(s.value().last(1))) {
+ --size;
+ }
+ str = basic_string_view<CharT>{s.value().data(), size};
+ return {};
+ }
+#if SCN_HAS_STRING_VIEW
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ auto getline_impl(WrappedRange& r,
+ std::basic_string_view<CharT>& str,
+ Until until) -> error
+ {
+ auto sv = ::scn::basic_string_view<CharT>{};
+ auto ret = getline_impl(r, sv, until);
+ str = ::std::basic_string_view<CharT>{sv.data(), sv.size()};
+ return ret;
+ }
+#endif
+ } // namespace detail
+
+ /**
+ * Read the range in \c r into \c str until \c until is found.
+ * \c until will be skipped in parsing: it will not be pushed into \c
+ * str, and the returned range will go past it.
+ *
+ * If `str` is convertible to a `basic_string_view`:
+ * - And if `r` is a `contiguous_range`:
+ * - `str` is set to point inside `r` with the appropriate length
+ * - if not, returns an error
+ *
+ * Otherwise, clears `str` by calling `str.clear()`, and then reads the
+ * range into `str` as if by repeatedly calling \c str.push_back.
+ * `str.reserve()` is also required to be present.
+ *
+ * `Until` can either be the same as `r` character type (`char` or
+ * `wchar_t`), or `code_point`.
+ *
+ * \code{.cpp}
+ * auto source = "hello\nworld"
+ * std::string line;
+ * auto result = scn::getline(source, line, '\n');
+ * // line == "hello"
+ * // result.range() == "world"
+ *
+ * // Using the other overload
+ * result = scn::getline(result.range(), line);
+ * // line == "world"
+ * // result.empty() == true
+ * \endcode
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename String, typename Until>
+ auto getline(Range&& r, String& str, Until until)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename String, typename Until>
+ SCN_NODISCARD auto getline(Range&& r, String& str, Until until)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto wrapped = wrap(SCN_FWD(r));
+ auto err = getline_impl(wrapped, str, until);
+ if (!err) {
+ auto e = wrapped.reset_to_rollback_point();
+ if (!e) {
+ err = e;
+ }
+ }
+ else {
+ wrapped.set_rollback_point();
+ }
+ return detail::wrap_result(
+ wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped));
+ }
+#endif
+
+ /**
+ * Equivalent to \ref getline with the last parameter set to
+ * <tt>'\\n'</tt> with the appropriate character type.
+ *
+ * In other words, reads `r` into `str` until <tt>'\\n'</tt> is found.
+ *
+ * The character type is determined by `r`.
+ */
+#if SCN_DOXYGEN
+ template <typename Range,
+ typename String,
+ typename CharT = typename detail::extract_char_type<
+ ranges::iterator_t<range_wrapper_for_t<Range>>>::type>
+ auto getline(Range&& r, String& str)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range,
+ typename String,
+ typename CharT = typename detail::extract_char_type<
+ ranges::iterator_t<range_wrapper_for_t<Range>>>::type>
+ SCN_NODISCARD auto getline(Range&& r, String& str)
+ -> detail::scan_result_for_range<Range>
+ {
+ return getline(SCN_FWD(r), str, detail::ascii_widen<CharT>('\n'));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/ignore.h b/src/third-party/scnlib/include/scn/scan/ignore.h
new file mode 100644
index 0000000..415a877
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/ignore.h
@@ -0,0 +1,189 @@
+// 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_IGNORE_H
+#define SCN_SCAN_IGNORE_H
+
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename CharT>
+ struct ignore_iterator {
+ using value_type = CharT;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::output_iterator_tag;
+
+ constexpr ignore_iterator() = default;
+
+ SCN_CONSTEXPR14 ignore_iterator& operator=(CharT) noexcept
+ {
+ return *this;
+ }
+ constexpr const ignore_iterator& operator=(CharT) const noexcept
+ {
+ return *this;
+ }
+
+ SCN_CONSTEXPR14 ignore_iterator& operator*() noexcept
+ {
+ return *this;
+ }
+ constexpr const ignore_iterator& operator*() const noexcept
+ {
+ return *this;
+ }
+
+ SCN_CONSTEXPR14 ignore_iterator& operator++() noexcept
+ {
+ return *this;
+ }
+ constexpr const ignore_iterator& operator++() const noexcept
+ {
+ return *this;
+ }
+ };
+
+ template <typename CharT>
+ struct ignore_iterator_n {
+ using value_type = CharT;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::output_iterator_tag;
+
+ ignore_iterator_n() = default;
+ ignore_iterator_n(difference_type n) : i(n) {}
+
+ constexpr const ignore_iterator_n& operator=(CharT) const noexcept
+ {
+ return *this;
+ }
+
+ constexpr const ignore_iterator_n& operator*() const noexcept
+ {
+ return *this;
+ }
+
+ SCN_CONSTEXPR14 ignore_iterator_n& operator++() noexcept
+ {
+ ++i;
+ return *this;
+ }
+
+ constexpr bool operator==(const ignore_iterator_n& o) const noexcept
+ {
+ return i == o.i;
+ }
+ constexpr bool operator!=(const ignore_iterator_n& o) const noexcept
+ {
+ return !(*this == o);
+ }
+
+ difference_type i{0};
+ };
+
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error ignore_until_impl(WrappedRange& r, Until until)
+ {
+ ignore_iterator<CharT> it{};
+ return read_until_space(r, it, until_pred<CharT>{until}, false);
+ }
+
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error ignore_until_n_impl(WrappedRange& r,
+ ranges::range_difference_t<WrappedRange> n,
+ Until until)
+ {
+ ignore_iterator_n<CharT> begin{}, end{n};
+ return read_until_space_ranged(r, begin, end,
+ until_pred<CharT>{until}, false);
+ }
+ } // namespace detail
+
+ /**
+ * Advances the beginning of \c r until \c until is found.
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Until>
+ auto ignore_until(Range&& r, Until until)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Until>
+ SCN_NODISCARD auto ignore_until(Range&& r, Until until)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto wrapped = wrap(SCN_FWD(r));
+ auto err = detail::ignore_until_impl(wrapped, until);
+ if (!err) {
+ auto e = wrapped.reset_to_rollback_point();
+ if (!e) {
+ err = e;
+ }
+ }
+ else {
+ wrapped.set_rollback_point();
+ }
+ return detail::wrap_result(
+ wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped));
+ }
+#endif
+
+ /**
+ * Advances the beginning of \c r until \c until is found, or the
+ * beginning has been advanced \c n times.
+ *
+ * `Until` can be the `r` character type (`char` or `wchar_t`), or
+ * `code_point`.
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Until>
+ auto ignore_until_n(Range&& r,
+ ranges::range_difference_t<Range> n,
+ Until until) -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Until>
+ SCN_NODISCARD auto ignore_until_n(Range&& r,
+ ranges::range_difference_t<Range> n,
+ Until until)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto wrapped = wrap(SCN_FWD(r));
+ auto err = detail::ignore_until_n_impl(wrapped, n, until);
+ if (!err) {
+ auto e = wrapped.reset_to_rollback_point();
+ if (!e) {
+ err = e;
+ }
+ }
+ return detail::wrap_result(
+ wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/istream.h b/src/third-party/scnlib/include/scn/scan/istream.h
new file mode 100644
index 0000000..a9aef86
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/istream.h
@@ -0,0 +1,147 @@
+// 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_ISTREAM_H
+#define SCN_SCAN_ISTREAM_H
+
+#include "../reader/common.h"
+#include "../detail/result.h"
+
+#include <istream>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename WrappedRange>
+ class range_streambuf
+ : public std::basic_streambuf<typename WrappedRange::char_type> {
+ using base = std::basic_streambuf<typename WrappedRange::char_type>;
+
+ public:
+ using range_type = WrappedRange;
+ using char_type = typename WrappedRange::char_type;
+ using traits_type = typename base::traits_type;
+ using int_type = typename base::int_type;
+
+ explicit range_streambuf(range_type& r) : m_range(std::addressof(r))
+ {
+ }
+
+ private:
+ int_type underflow() override
+ {
+ // already read
+ if (!traits_type::eq_int_type(m_ch, traits_type::eof())) {
+ return m_ch;
+ }
+
+ auto ret = read_code_unit(*m_range);
+ if (!ret) {
+ // error
+ // m_ch is already eof
+ return traits_type::eof();
+ }
+ m_ch = traits_type::to_int_type(ret.value());
+ return m_ch;
+ }
+ int_type uflow() override
+ {
+ auto ret = underflow();
+ if (ret != traits_type::eof()) {
+ m_ch = traits_type::eof();
+ }
+ return ret;
+ }
+ std::streamsize showmanyc() override
+ {
+ return traits_type::eq_int_type(m_ch, traits_type::eof()) ? 0
+ : 1;
+ }
+ int_type pbackfail(int_type) override
+ {
+ auto e = putback_n(*m_range, 1);
+ if (!e) {
+ return traits_type::eof();
+ }
+ return traits_type::to_int_type(0);
+ }
+
+ range_type* m_range;
+ int_type m_ch{traits_type::eof()};
+ };
+
+ // Trick stolen from {fmt}
+ template <typename CharT>
+ struct test_std_stream : std::basic_istream<CharT> {
+ private:
+ struct null;
+ // Hide all operator>> from std::basic_istream<CharT>
+ void operator>>(null);
+ };
+
+ // Check for user-defined operator>>
+ template <typename CharT, typename T, typename = void>
+ struct is_std_streamable : std::false_type {
+ };
+
+ template <typename CharT, typename T>
+ struct is_std_streamable<
+ CharT,
+ T,
+ void_t<decltype(SCN_DECLVAL(test_std_stream<CharT>&) >>
+ SCN_DECLVAL(T&))>> : std::true_type {
+ };
+ } // namespace detail
+
+ template <typename T>
+ struct scanner<T,
+ typename std::enable_if<
+ detail::is_std_streamable<char, T>::value ||
+ detail::is_std_streamable<wchar_t, T>::value>::type>
+ : public empty_parser {
+ template <typename Context>
+ error scan(T& val, Context& ctx)
+ {
+ static_assert(detail::is_std_streamable<typename Context::char_type,
+ T>::value,
+ "Type can not be read from a basic_istream of this "
+ "character type");
+ detail::range_streambuf<typename Context::range_type> streambuf(
+ ctx.range());
+ std::basic_istream<typename Context::char_type> stream(
+ std::addressof(streambuf));
+
+ if (!(stream >> val)) {
+ if (stream.eof()) {
+ return {error::end_of_range, "EOF"};
+ }
+ if (stream.bad()) {
+ return {error::unrecoverable_source_error,
+ "Bad std::istream after reading"};
+ }
+ return {error::invalid_scanned_value,
+ "Failed to read with std::istream"};
+ }
+ return {};
+ }
+ };
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_ISTREAM_H
diff --git a/src/third-party/scnlib/include/scn/scan/list.h b/src/third-party/scnlib/include/scn/scan/list.h
new file mode 100644
index 0000000..a5eeaf5
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/list.h
@@ -0,0 +1,450 @@
+// 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_LIST_H
+#define SCN_SCAN_LIST_H
+
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * Adapts a `span` into a type that can be read into using \ref
+ * scan_list. This way, potentially unnecessary dynamic memory
+ * allocations can be avoided. To use as a parameter to \ref scan_list,
+ * use \ref make_span_list_wrapper.
+ *
+ * \code{.cpp}
+ * std::vector<int> buffer(8, 0);
+ * scn::span<int> s = scn::make_span(buffer);
+ *
+ * auto wrapper = scn::span_list_wrapper<int>(s);
+ * scn::scan_list("123 456", wrapper);
+ * // s[0] == buffer[0] == 123
+ * // s[1] == buffer[1] == 456
+ * \endcode
+ *
+ * \see scan_list
+ * \see make_span_list_wrapper
+ */
+ template <typename T>
+ struct span_list_wrapper {
+ using value_type = T;
+
+ span_list_wrapper(span<T> s) : m_span(s) {}
+
+ void push_back(T val)
+ {
+ SCN_EXPECT(n < max_size());
+ m_span[n] = SCN_MOVE(val);
+ ++n;
+ }
+
+ SCN_NODISCARD constexpr std::size_t size() const noexcept
+ {
+ return n;
+ }
+ SCN_NODISCARD constexpr std::size_t max_size() const noexcept
+ {
+ return m_span.size();
+ }
+
+ span<T> m_span;
+ std::size_t n{0};
+ };
+
+ namespace detail {
+ template <typename T>
+ using span_list_wrapper_for =
+ span_list_wrapper<typename decltype(make_span(
+ SCN_DECLVAL(T&)))::value_type>;
+ }
+
+ /**
+ * Adapts a contiguous buffer into a type containing a `span` that can
+ * be read into using \ref scn::scan_list.
+ *
+ * Example adapted from \ref span_list_wrapper:
+ * \code{.cpp}
+ * std::vector<int> buffer(8, 0);
+ * scn::scan_list("123 456", scn::make_span_list_wrapper(buffer));
+ * // s[0] == buffer[0] == 123
+ * // s[1] == buffer[1] == 456
+ * \endcode
+ *
+ * \see scan_list
+ * \see span_list_wrapper
+ */
+ template <typename T>
+ auto make_span_list_wrapper(T& s)
+ -> temporary<detail::span_list_wrapper_for<T>>
+ {
+ auto _s = make_span(s);
+ return temp(span_list_wrapper<typename decltype(_s)::value_type>(_s));
+ }
+
+ /**
+ * Used to customize `scan_list_ex()`.
+ *
+ * \tparam CharT Can be a code unit type (`char` or `wchar_t`, depending on
+ * the source range), or `code_point`.
+ *
+ * `list_separator`, `list_until` and `list_separator_and_until` can be used
+ * to create a value of this type, taking advantage of template argument
+ * deduction (no need to hand-specify `CharT`).
+ */
+ template <typename CharT>
+ struct scan_list_options {
+ /**
+ * If set, up to one separator character can be accepted between values,
+ * which may be surrounded by whitespace.
+ */
+ optional<CharT> separator{};
+ /**
+ * If set, reading the list is stopped if this character is found
+ * between values.
+ *
+ * In that case, it is advanced over, and no error is returned.
+ */
+ optional<CharT> until{};
+
+ scan_list_options() = default;
+ scan_list_options(optional<CharT> s, optional<CharT> u)
+ : separator(SCN_MOVE(s)), until(SCN_MOVE(u))
+ {
+ }
+ };
+
+ /**
+ * Create a `scan_list_options` for `scan_list_ex`, by using `ch` as the
+ * separator character.
+ */
+ template <typename CharT>
+ scan_list_options<CharT> list_separator(CharT ch)
+ {
+ return {optional<CharT>{ch}, nullopt};
+ }
+ /**
+ * Create a `scan_list_options` for `scan_list_ex`, by using `ch` as the
+ * until-character.
+ */
+ template <typename CharT>
+ scan_list_options<CharT> list_until(CharT ch)
+ {
+ return {nullopt, optional<CharT>{ch}};
+ }
+ /**
+ * Create a `scan_list_options` for `scan_list_ex`, by using `sep` as the
+ * separator, and `until` as the until-character.
+ */
+ template <typename CharT>
+ scan_list_options<CharT> list_separator_and_until(CharT sep, CharT until)
+ {
+ return {optional<CharT>{sep}, optional<CharT>{until}};
+ }
+
+ namespace detail {
+ template <typename WrappedRange, typename CharT>
+ expected<CharT> check_separator(WrappedRange& r, size_t& n, CharT)
+ {
+ auto ret = read_code_unit(r);
+ if (!ret) {
+ return ret.error();
+ }
+ n = 1;
+ return ret.value();
+ }
+ template <typename WrappedRange>
+ expected<code_point> check_separator(WrappedRange& r,
+ size_t& n,
+ code_point)
+ {
+ unsigned char buf[4] = {0};
+ auto ret = read_code_point(r, make_span(buf, 4));
+ if (!ret) {
+ return ret.error();
+ }
+ n = ret.value().chars.size();
+ return ret.value().cp;
+ }
+
+ template <typename Context, typename Container, typename Separator>
+ auto scan_list_impl(Context& ctx,
+ bool localized,
+ Container& c,
+ scan_list_options<Separator> options) -> error
+ {
+ using char_type = typename Context::char_type;
+ using value_type = typename Container::value_type;
+ value_type value;
+
+ auto args = make_args_for(ctx.range(), 1, value);
+
+ bool scanning = true;
+ while (scanning) {
+ if (c.size() == c.max_size()) {
+ break;
+ }
+
+ // read value
+ auto pctx = make_parse_context(1, ctx.locale(), localized);
+ auto err = visit(ctx, pctx, basic_args<char_type>{args});
+ if (!err) {
+ if (err == error::end_of_range) {
+ break;
+ }
+ return err;
+ }
+ c.push_back(SCN_MOVE(value));
+
+ auto next = static_cast<Separator>(0);
+ size_t n{0};
+
+ auto read_next = [&]() -> error {
+ auto ret = check_separator(ctx.range(), n,
+ static_cast<Separator>(0));
+ if (!ret) {
+ if (ret.error() == error::end_of_range) {
+ scanning = false;
+ return {};
+ }
+ return ret.error();
+ }
+ next = ret.value();
+
+ err =
+ putback_n(ctx.range(), static_cast<std::ptrdiff_t>(n));
+ if (!err) {
+ return err;
+ }
+
+ return {};
+ };
+
+ bool sep_found = false;
+ while (true) {
+ // read until
+ if (options.until) {
+ err = read_next();
+ if (!err) {
+ return err;
+ }
+ if (!scanning) {
+ break;
+ }
+
+ if (next == options.until.get()) {
+ scanning = false;
+ break;
+ }
+ }
+
+ // read sep
+ if (options.separator && !sep_found) {
+ err = read_next();
+ if (!err) {
+ return err;
+ }
+ if (!scanning) {
+ break;
+ }
+
+ if (next == options.separator.get()) {
+ // skip to next char
+ ctx.range().advance(static_cast<std::ptrdiff_t>(n));
+ continue;
+ }
+ }
+
+ err = read_next();
+ if (!err) {
+ return err;
+ }
+ if (!scanning) {
+ break;
+ }
+
+ if (ctx.locale().get_static().is_space(next)) {
+ // skip ws
+ ctx.range().advance(static_cast<std::ptrdiff_t>(n));
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return {};
+ }
+ } // namespace detail
+
+ /**
+ * Reads values repeatedly from `r` and writes them into `c`.
+ *
+ * The values read are of type `Container::value_type`, and they are
+ * written into `c` using `c.push_back`.
+ * The values are separated by whitespace.
+ *
+ * The range is read, until:
+ * - `c.max_size()` is reached, or
+ * - range `EOF` is reached
+ *
+ * In these cases, an error will not be returned, and the beginning
+ * of the returned range will point to the first character after the
+ * scanned list.
+ *
+ * If an invalid value is scanned, `error::invalid_scanned_value` is
+ * returned, but the values already in `vec` will remain there. The range is
+ * put back to the state it was before reading the invalid value.
+ *
+ * To scan into `span`, use \ref span_list_wrapper.
+ * \ref make_span_list_wrapper
+ *
+ * \code{.cpp}
+ * std::vector<int> vec{};
+ * auto result = scn::scan_list("123 456", vec);
+ * // vec == [123, 456]
+ * // result.empty() == true
+ *
+ * vec.clear();
+ * result = scn::scan_list("123 456 abc", vec);
+ * // vec == [123, 456]
+ * // result.error() == invalid_scanned_value
+ * // result.range() == " abc"
+ * \endcode
+ *
+ * \param r Range to read from
+ * \param c Container to write values to, using `c.push_back()`.
+ * `Container::value_type` will be used to determine the type of the values
+ * to read.
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Container>
+ auto scan_list(Range&& r, Container& c)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Container>
+ SCN_NODISCARD auto scan_list(Range&& r, Container& c)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto range = wrap(SCN_FWD(r));
+ auto ctx = make_context(SCN_MOVE(range));
+ using char_type = typename decltype(ctx)::char_type;
+
+ auto err = detail::scan_list_impl(ctx, false, c,
+ scan_list_options<char_type>{});
+
+ return detail::wrap_result(wrapped_error{err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ctx.range()));
+ }
+#endif
+
+ /**
+ * Otherwise equivalent to `scan_list()`, except can react to additional
+ * characters, based on `options`.
+ *
+ * See `scan_list_options` for more information.
+ *
+ * \param r Range to scan from
+ * \param c Container to write read values into
+ * \param options Options to use
+ *
+ * \code{.cpp}
+ * std::vector<int> vec{};
+ * auto result = scn::scan_list_ex("123, 456", vec,
+ * scn::list_separator(','));
+ * // vec == [123, 456]
+ * // result.empty() == true
+ * \endcode
+ *
+ * \see scan_list
+ * \see scan_list_options
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Container, typename CharT>
+ auto scan_list_ex(Range&& r, Container& c, scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Container, typename CharT>
+ SCN_NODISCARD auto scan_list_ex(Range&& r,
+ Container& c,
+ scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto range = wrap(SCN_FWD(r));
+ auto ctx = make_context(SCN_MOVE(range));
+
+ auto err = detail::scan_list_impl(ctx, false, c, options);
+
+ return detail::wrap_result(wrapped_error{err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ctx.range()));
+ }
+#endif
+
+ /**
+ * Otherwise equivalent to `scan_list_ex()`, except uses `loc` to scan the
+ * values.
+ *
+ * \param loc Locale to use for scanning. Must be a `std::locale`.
+ * \param r Range to scan from
+ * \param c Container to write read values into
+ * \param options Options to use
+ *
+ * \see scan_list_ex()
+ * \see scan_localized()
+ */
+#if SCN_DOXYGEN
+ template <typename Locale,
+ typename Range,
+ typename Container,
+ typename CharT>
+ auto scan_list_localized(const Locale& loc,
+ Range&& r,
+ Container& c,
+ scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Locale,
+ typename Range,
+ typename Container,
+ typename CharT>
+ SCN_NODISCARD auto scan_list_localized(const Locale& loc,
+ Range&& r,
+ Container& c,
+ scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto range = wrap(SCN_FWD(r));
+ using char_type = typename decltype(range)::char_type;
+ auto locale = make_locale_ref<char_type>(loc);
+ auto ctx = make_context(SCN_MOVE(range), SCN_MOVE(locale));
+
+ auto err = detail::scan_list_impl(ctx, true, c, options);
+
+ return detail::wrap_result(wrapped_error{err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ctx.range()));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/scan.h b/src/third-party/scnlib/include/scn/scan/scan.h
new file mode 100644
index 0000000..20f4cd1
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/scan.h
@@ -0,0 +1,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
diff --git a/src/third-party/scnlib/include/scn/scan/vscan.h b/src/third-party/scnlib/include/scn/scan/vscan.h
new file mode 100644
index 0000000..86bf23e
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/vscan.h
@@ -0,0 +1,208 @@
+// 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_VSCAN_H
+#define SCN_SCAN_VSCAN_H
+
+#include "../detail/context.h"
+#include "../detail/file.h"
+#include "../detail/parse_context.h"
+#include "../detail/visitor.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ // Avoid documentation issues: without this, Doxygen will think
+ // SCN_BEGIN_NAMESPACE is a part of the vscan declaration
+ namespace dummy {
+ }
+
+ /**
+ * Type returned by `vscan` and others
+ */
+ template <typename WrappedRange>
+ struct vscan_result {
+ error err;
+ WrappedRange range;
+ };
+
+ namespace detail {
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_boilerplate(
+ WrappedRange&& r,
+ basic_string_view<CharT> fmt,
+ basic_args<CharT> args)
+ {
+ auto ctx = make_context(SCN_MOVE(r));
+ auto pctx = make_parse_context(fmt, ctx.locale());
+ auto err = visit(ctx, pctx, SCN_MOVE(args));
+ return {err, SCN_MOVE(ctx.range())};
+ }
+
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_boilerplate_default(
+ WrappedRange&& r,
+ int n_args,
+ basic_args<CharT> args)
+ {
+ auto ctx = make_context(SCN_MOVE(r));
+ auto pctx = make_parse_context(n_args, ctx.locale());
+ auto err = visit(ctx, pctx, SCN_MOVE(args));
+ return {err, SCN_MOVE(ctx.range())};
+ }
+
+ template <typename WrappedRange,
+ typename Format,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_boilerplate_localized(
+ WrappedRange&& r,
+ basic_locale_ref<CharT>&& loc,
+ const Format& fmt,
+ basic_args<CharT> args)
+ {
+ auto ctx = make_context(SCN_MOVE(r), SCN_MOVE(loc));
+ auto pctx = make_parse_context(fmt, ctx.locale());
+ auto err = visit(ctx, pctx, SCN_MOVE(args));
+ return {err, SCN_MOVE(ctx.range())};
+ }
+ } // namespace detail
+
+ /**
+ * In the spirit of {fmt}/`std::format` and `vformat`, `vscan` behaves
+ * similarly to \ref scan, except instead of taking a variadic argument
+ * pack, it takes an object of type `basic_args`, which type-erases the
+ * arguments to scan. This, in effect, will decrease generated code size and
+ * compile times dramatically.
+ *
+ * \param range Source range that has been wrapped with `detail::wrap`, and
+ * passed in as an rvalue.
+ * \param fmt Format string to use
+ * \param args Type-erased values to read
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan(WrappedRange range,
+ basic_string_view<CharT> fmt,
+ basic_args<CharT>&& args)
+ {
+ return detail::vscan_boilerplate(SCN_MOVE(range), fmt, SCN_MOVE(args));
+ }
+
+ /**
+ * To be used with `scan_default`
+ *
+ * \param range Source range that has been wrapped with `detail::wrap`, and
+ * passed in as an rvalue.
+ * \param n_args Number of arguments in args
+ * \param args Type-erased values to read
+ *
+ * \see vscan
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_default(WrappedRange range,
+ int n_args,
+ basic_args<CharT>&& args)
+ {
+ return detail::vscan_boilerplate_default(SCN_MOVE(range), n_args,
+ SCN_MOVE(args));
+ }
+
+ /**
+ * To be used with `scan_localized`
+ *
+ * \param loc Locale to use
+ * \param range Source range that has been wrapped with `detail::wrap`, and
+ * passed in as an rvalue.
+ * \param fmt Format string to use
+ * \param args Type-erased values to read
+ *
+ * \see vscan
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_localized(WrappedRange range,
+ basic_locale_ref<CharT>&& loc,
+ basic_string_view<CharT> fmt,
+ basic_args<CharT>&& args)
+ {
+ return detail::vscan_boilerplate_localized(
+ SCN_MOVE(range), SCN_MOVE(loc), fmt, SCN_MOVE(args));
+ }
+
+ /**
+ * \see scan_usertype
+ * \see vscan
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ error vscan_usertype(basic_context<WrappedRange>& ctx,
+ basic_string_view<CharT> f,
+ basic_args<CharT>&& args)
+ {
+ auto pctx = make_parse_context(f, ctx.locale());
+ return visit(ctx, pctx, SCN_MOVE(args));
+ }
+
+#if !defined(SCN_HEADER_ONLY) || !SCN_HEADER_ONLY
+
+#define SCN_VSCAN_DECLARE(Range, WrappedAlias, CharAlias) \
+ namespace detail { \
+ namespace vscan_macro { \
+ using WrappedAlias = range_wrapper_for_t<Range>; \
+ using CharAlias = typename WrappedAlias::char_type; \
+ } \
+ } \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan( \
+ detail::vscan_macro::WrappedAlias&&, \
+ basic_string_view<detail::vscan_macro::CharAlias>, \
+ basic_args<detail::vscan_macro::CharAlias>&&); \
+ \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan_default( \
+ detail::vscan_macro::WrappedAlias&&, int, \
+ basic_args<detail::vscan_macro::CharAlias>&&); \
+ \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan_localized( \
+ detail::vscan_macro::WrappedAlias&&, \
+ basic_locale_ref<detail::vscan_macro::CharAlias>&&, \
+ basic_string_view<detail::vscan_macro::CharAlias>, \
+ basic_args<detail::vscan_macro::CharAlias>&&); \
+ \
+ error vscan_usertype(basic_context<detail::vscan_macro::WrappedAlias>&, \
+ basic_string_view<detail::vscan_macro::CharAlias>, \
+ basic_args<detail::vscan_macro::CharAlias>&&)
+
+ SCN_VSCAN_DECLARE(string_view, string_view_wrapped, string_view_char);
+ SCN_VSCAN_DECLARE(wstring_view, wstring_view_wrapped, wstring_view_char);
+ SCN_VSCAN_DECLARE(std::string, string_wrapped, string_char);
+ SCN_VSCAN_DECLARE(std::wstring, wstring_wrapped, wstring_char);
+ SCN_VSCAN_DECLARE(file&, file_ref_wrapped, file_ref_char);
+ SCN_VSCAN_DECLARE(wfile&, wfile_ref_wrapped, wfile_ref_char);
+
+#endif // !SCN_HEADER_ONLY
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_VSCAN_CPP)
+#include "vscan.cpp"
+#endif
+
+#endif // SCN_SCAN_VSCAN_H