/ externals / fmt / test / scan.h
scan.h
  1  // Formatting library for C++ - scanning API proof of concept
  2  //
  3  // Copyright (c) 2019 - present, Victor Zverovich
  4  // All rights reserved.
  5  //
  6  // For the license information refer to format.h.
  7  
  8  #include <array>
  9  #include <cassert>
 10  #include <climits>
 11  
 12  #include "fmt/format.h"
 13  
 14  FMT_BEGIN_NAMESPACE
 15  template <typename T, typename Char = char> struct scanner {
 16    // A deleted default constructor indicates a disabled scanner.
 17    scanner() = delete;
 18  };
 19  
 20  class scan_parse_context {
 21   private:
 22    string_view format_;
 23  
 24   public:
 25    using iterator = string_view::iterator;
 26  
 27    explicit FMT_CONSTEXPR scan_parse_context(string_view format)
 28        : format_(format) {}
 29  
 30    FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
 31    FMT_CONSTEXPR iterator end() const { return format_.end(); }
 32  
 33    void advance_to(iterator it) {
 34      format_.remove_prefix(detail::to_unsigned(it - begin()));
 35    }
 36  };
 37  
 38  struct scan_context {
 39   private:
 40    string_view input_;
 41  
 42   public:
 43    using iterator = const char*;
 44  
 45    explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {}
 46  
 47    iterator begin() const { return input_.data(); }
 48    iterator end() const { return begin() + input_.size(); }
 49  
 50    void advance_to(iterator it) {
 51      input_.remove_prefix(detail::to_unsigned(it - begin()));
 52    }
 53  };
 54  
 55  namespace detail {
 56  enum class scan_type {
 57    none_type,
 58    int_type,
 59    uint_type,
 60    long_long_type,
 61    ulong_long_type,
 62    string_type,
 63    string_view_type,
 64    custom_type
 65  };
 66  
 67  struct custom_scan_arg {
 68    void* value;
 69    void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
 70  };
 71  
 72  class scan_arg {
 73   public:
 74    scan_type type;
 75    union {
 76      int* int_value;
 77      unsigned* uint_value;
 78      long long* long_long_value;
 79      unsigned long long* ulong_long_value;
 80      std::string* string;
 81      fmt::string_view* string_view;
 82      custom_scan_arg custom;
 83      // TODO: more types
 84    };
 85  
 86    FMT_CONSTEXPR scan_arg() : type(scan_type::none_type), int_value(nullptr) {}
 87    FMT_CONSTEXPR scan_arg(int& value)
 88        : type(scan_type::int_type), int_value(&value) {}
 89    FMT_CONSTEXPR scan_arg(unsigned& value)
 90        : type(scan_type::uint_type), uint_value(&value) {}
 91    FMT_CONSTEXPR scan_arg(long long& value)
 92        : type(scan_type::long_long_type), long_long_value(&value) {}
 93    FMT_CONSTEXPR scan_arg(unsigned long long& value)
 94        : type(scan_type::ulong_long_type), ulong_long_value(&value) {}
 95    FMT_CONSTEXPR scan_arg(std::string& value)
 96        : type(scan_type::string_type), string(&value) {}
 97    FMT_CONSTEXPR scan_arg(fmt::string_view& value)
 98        : type(scan_type::string_view_type), string_view(&value) {}
 99    template <typename T>
100    FMT_CONSTEXPR scan_arg(T& value) : type(scan_type::custom_type) {
101      custom.value = &value;
102      custom.scan = scan_custom_arg<T>;
103    }
104  
105   private:
106    template <typename T>
107    static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
108                                scan_context& ctx) {
109      scanner<T> s;
110      parse_ctx.advance_to(s.parse(parse_ctx));
111      ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
112    }
113  };
114  }  // namespace detail
115  
116  struct scan_args {
117    int size;
118    const detail::scan_arg* data;
119  
120    template <size_t N>
121    FMT_CONSTEXPR scan_args(const std::array<detail::scan_arg, N>& store)
122        : size(N), data(store.data()) {
123      static_assert(N < INT_MAX, "too many arguments");
124    }
125  };
126  
127  namespace detail {
128  
129  struct scan_handler : error_handler {
130   private:
131    scan_parse_context parse_ctx_;
132    scan_context scan_ctx_;
133    scan_args args_;
134    int next_arg_id_;
135    scan_arg arg_;
136  
137    template <typename T = unsigned> T read_uint() {
138      T value = 0;
139      auto it = scan_ctx_.begin(), end = scan_ctx_.end();
140      while (it != end) {
141        char c = *it++;
142        if (c < '0' || c > '9') on_error("invalid input");
143        // TODO: check overflow
144        value = value * 10 + static_cast<unsigned>(c - '0');
145      }
146      scan_ctx_.advance_to(it);
147      return value;
148    }
149  
150    template <typename T = int> T read_int() {
151      auto it = scan_ctx_.begin(), end = scan_ctx_.end();
152      bool negative = it != end && *it == '-';
153      if (negative) ++it;
154      scan_ctx_.advance_to(it);
155      const auto value = read_uint<typename std::make_unsigned<T>::type>();
156      if (negative) return -static_cast<T>(value);
157      return static_cast<T>(value);
158    }
159  
160   public:
161    FMT_CONSTEXPR scan_handler(string_view format, string_view input,
162                               scan_args args)
163        : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
164  
165    const char* pos() const { return scan_ctx_.begin(); }
166  
167    void on_text(const char* begin, const char* end) {
168      auto size = to_unsigned(end - begin);
169      auto it = scan_ctx_.begin();
170      if (it + size > scan_ctx_.end() || !std::equal(begin, end, it))
171        on_error("invalid input");
172      scan_ctx_.advance_to(it + size);
173    }
174  
175    FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); }
176    FMT_CONSTEXPR int on_arg_id(int id) {
177      if (id >= args_.size) on_error("argument index out of range");
178      arg_ = args_.data[id];
179      return id;
180    }
181    FMT_CONSTEXPR int on_arg_id(string_view id) {
182      if (id.data()) on_error("invalid format");
183      return 0;
184    }
185  
186    void on_replacement_field(int, const char*) {
187      auto it = scan_ctx_.begin(), end = scan_ctx_.end();
188      switch (arg_.type) {
189      case scan_type::int_type:
190        *arg_.int_value = read_int();
191        break;
192      case scan_type::uint_type:
193        *arg_.uint_value = read_uint();
194        break;
195      case scan_type::long_long_type:
196        *arg_.long_long_value = read_int<long long>();
197        break;
198      case scan_type::ulong_long_type:
199        *arg_.ulong_long_value = read_uint<unsigned long long>();
200        break;
201      case scan_type::string_type:
202        while (it != end && *it != ' ') arg_.string->push_back(*it++);
203        scan_ctx_.advance_to(it);
204        break;
205      case scan_type::string_view_type: {
206        auto s = it;
207        while (it != end && *it != ' ') ++it;
208        *arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
209        scan_ctx_.advance_to(it);
210        break;
211      }
212      case scan_type::none_type:
213      case scan_type::custom_type:
214        assert(false);
215      }
216    }
217  
218    const char* on_format_specs(int, const char* begin, const char*) {
219      if (arg_.type != scan_type::custom_type) return begin;
220      parse_ctx_.advance_to(begin);
221      arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
222      return parse_ctx_.begin();
223    }
224  };
225  }  // namespace detail
226  
227  template <typename... Args>
228  std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
229    return {{args...}};
230  }
231  
232  string_view::iterator vscan(string_view input, string_view format_str,
233                              scan_args args) {
234    detail::scan_handler h(format_str, input, args);
235    detail::parse_format_string<false>(format_str, h);
236    return input.begin() + (h.pos() - &*input.begin());
237  }
238  
239  template <typename... Args>
240  string_view::iterator scan(string_view input, string_view format_str,
241                             Args&... args) {
242    return vscan(input, format_str, make_scan_args(args...));
243  }
244  FMT_END_NAMESPACE