/* * This source code is released into the public domain. */ #ifndef LIBLFJAIL_WORDS_HH #define LIBLFJAIL_WORDS_HH #include #include #include "ctype.hh" #include "generator.hh" namespace lfjail { /* * words: take a string-like object and split it into words using the given * predicate. Empty values are discarded (i.e., repeated separators are * ignored). * * words() returns std::strings, while wordsv() return std::string_views * which refer to the original string. */ namespace detail { template auto split(std::string_view text, Pred &&pred) -> std::generator { auto pos = std::ranges::begin(text); auto end = std::ranges::end(text); for (;;) { // Skip leading whitespace. pos = std::find_if_not(pos, end, pred); // If there's no string left, we're done. if (pos == end) break; // Find the end of the next word. auto split_pos = std::find_if(pos, end, pred); // Yield this word. co_yield R(pos, split_pos); pos = split_pos; } } template> Pred> requires (std::ranges::contiguous_range) struct words_view : std::ranges::view_interface> { words_view(auto &&base, auto &&pred) : _base(std::forward(base)) , _pred(std::forward(pred)) , _generator(detail::split( std::string_view(_base), _pred)) {} auto begin(this auto const &self) { return self._generator.begin(); } auto end(this auto const &self) { return self._generator.end(); } private: Range _base; Pred _pred; mutable std::generator _generator; }; template struct words_impl : std::ranges::range_adaptor_closure> { template constexpr auto operator()(Range &&range, Pred &&pred) const { return words_view( std::forward(range), std::forward(pred)); } }; } // namespace detail template> Pred> auto words(Range &&range, Pred &&pred) requires (std::ranges::borrowed_range) { return detail::words_impl{}( std::forward(range), std::forward(pred)); } template> Pred> auto words(Range &&range, Pred &&pred) requires (!std::ranges::borrowed_range) { return detail::words_impl{}( std::forward(range), std::forward(pred)); } template auto words(Range &&range, std::ranges::range_value_t ch) { return words(std::forward(range), [ch](auto c) { return c == ch; }); } template auto words(Range &&range) { return words(std::forward(range), is_c_space); } } // namespace lfjail #endif // LIBLFJAIL_WORDS_HH