/* * 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). */ namespace detail { template> Pred> auto split(Range &&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 std::string(pos, split_pos); pos = split_pos; } } template struct words_fn : std::ranges::range_adaptor_closure> { words_fn(Pred p) : _pred(std::move(p)) {} template constexpr auto operator()(Range &&range) const { return detail::split(std::forward(range), std::move(_pred)); } private: Pred _pred; }; } // namespace detail template> Pred> auto words(Range &&range, Pred pred) { return detail::split(std::forward(range), std::move(pred)); } template auto words(Range &&range, Char 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); } template requires(!std::integral && !std::ranges::range) auto words(Pred pred) { return detail::words_fn(std::move(pred)); } auto words() { return words(is_c_space); } template auto words(Char ch) { return words([ch](auto c) { return c == ch; }); } //constexpr words_impl words{}; } // namespace lfjail #endif // LIBLFJAIL_WORDS_HH