55 constexpr glob_pattern(std::u32string_view str) : _tokens(parse(str)) {}
101 for (hilet&
token : _tokens) {
102 r +=
token.u32string();
125 for (hilet&
token : _tokens) {
126 r +=
token.debug_u32string();
158 auto r = _tokens.
front().u32string();
159 if (_tokens.
size() >= 2
and _tokens[1].is_any_directory()) {
196 if (
auto i = text.rfind(
'/'); i == std::u32string::npos) {
203 auto path = std::filesystem::path{
std::move(text)};
204 path.make_preferred();
215 auto first = _tokens.
cbegin();
216 auto last = _tokens.
cend();
219 if (
not matches_strip(first, last, str)) {
223 }
else if (first == last) {
230 return matches(first, last, str);
240 return matches(std::u32string_view{str});
250 return matches(std::u32string_view{str});
270 return matches(std::string_view{str});
280 return matches(std::string_view{str});
290 return matches(path.generic_u32string());
294 enum class match_result_type { fail, success, unchecked };
305 constexpr token_type(token_type
const&)
noexcept =
default;
311 constexpr token_type(character_class_type&& rhs)
noexcept : _value(
std::move(rhs)) {}
312 constexpr token_type(alternation_type&& rhs)
noexcept : _value(
std::move(rhs)) {}
313 constexpr token_type(any_character_type&& rhs)
noexcept : _value(
std::move(rhs)) {}
315 constexpr token_type(any_directory_type&& rhs)
noexcept : _value(
std::move(rhs)) {}
319 return std::holds_alternative<text_type>(_value);
324 return std::holds_alternative<any_directory_type>(_value);
328 [[
nodiscard]]
constexpr match_result_type strip(std::u32string_view& str)
const noexcept
332 if (hilet
text_ptr = std::get_if<text_type>(&_value)) {
334 str.remove_prefix(
text_ptr->size());
335 return match_result_type::success;
338 str.remove_suffix(
text_ptr->size());
339 return match_result_type::success;
342 return match_result_type::fail;
347 return match_result_type::fail;
349 hilet c =
Left ? str.front() : str.back();
352 if constexpr (
Left) {
353 str.remove_prefix(1);
355 str.remove_suffix(1);
357 return match_result_type::success;
360 return match_result_type::fail;
362 }
else if (std::holds_alternative<any_character_type>(_value)) {
364 return match_result_type::fail;
366 if constexpr (
Left) {
367 str.remove_prefix(1);
369 str.remove_suffix(1);
371 return match_result_type::success;
374 return match_result_type::unchecked;
378 [[
nodiscard]]
constexpr match_result_type matches(std::u32string_view& str,
size_t iteration)
const noexcept
380 if (hilet
text_ptr = std::get_if<text_type>(&_value)) {
382 return match_result_type::fail;
384 }
else if (str.starts_with(*
text_ptr)) {
385 str.remove_prefix(
text_ptr->size());
386 return match_result_type::success;
389 return match_result_type::fail;
394 return match_result_type::fail;
396 hilet c = str.front();
399 str.remove_prefix(1);
400 return match_result_type::success;
403 return match_result_type::fail;
406 }
else if (hilet
alternation_ptr = std::get_if<alternation_type>(&_value)) {
408 return match_result_type::fail;
411 return match_result_type::success;
413 return match_result_type::unchecked;
416 }
else if (std::holds_alternative<any_character_type>(_value)) {
418 return match_result_type::fail;
420 str.remove_prefix(1);
421 return match_result_type::success;
424 }
else if (std::holds_alternative<any_text_type>(_value)) {
426 return match_result_type::fail;
429 return match_result_type::success;
432 }
else if (std::holds_alternative<any_directory_type>(_value)) {
433 if (str.empty()
or str.front() !=
'/') {
434 return match_result_type::fail;
436 for (
auto i = 0
_uz; i != std::u32string_view::npos; i = str.find(
'/', i + 1)) {
438 str.remove_prefix(i + 1);
439 return match_result_type::success;
442 return match_result_type::fail;
454 if (
auto text_ptr = std::get_if<text_type>(&_value)) {
470 }
else if (
auto alternation_ptr = std::get_if<alternation_type>(&_value)) {
480 }
else if (std::holds_alternative<any_character_type>(_value)) {
483 }
else if (std::holds_alternative<any_text_type>(_value)) {
486 }
else if (std::holds_alternative<any_directory_type>(_value)) {
500 if (
auto text_ptr = std::get_if<text_type>(&_value)) {
518 }
else if (
auto alternation_ptr = std::get_if<alternation_type>(&_value)) {
528 }
else if (std::holds_alternative<any_character_type>(_value)) {
531 }
else if (std::holds_alternative<any_text_type>(_value)) {
534 }
else if (std::holds_alternative<any_directory_type>(_value)) {
545 using variant_type = std::
546 variant<text_type, character_class_type, alternation_type, any_character_type, any_text_type, any_directory_type>;
552 using const_iterator = tokens_type::const_iterator;
556 [[
nodiscard]]
constexpr static token_type make_text(token_type::text_type&& rhs)
noexcept
561 [[
nodiscard]]
constexpr static token_type make_alternation(token_type::alternation_type&& rhs)
noexcept
566 [[
nodiscard]]
constexpr static token_type make_character_class(token_type::character_class_type&& rhs)
noexcept
573 return token_type{token_type::any_character_type{}};
578 return token_type{token_type::any_text_type{}};
583 return token_type{token_type::any_directory_type{}};
586 [[
nodiscard]]
constexpr static tokens_type parse(
auto first,
auto last)
588#define HI_GLOB_APPEND_TEXT() \
590 if (not text.empty()) { \
591 r.emplace_back(make_text(std::move(text))); \
597 using enum state_type;
599 static_assert(std::is_same_v<std::decay_t<
decltype(*first)>,
char32_t>);
601 auto r = tokens_type{};
604 auto text = token_type::text_type{};
618 HI_GLOB_APPEND_TEXT();
619 r.push_back(make_any_character());
625 HI_GLOB_APPEND_TEXT();
629 HI_GLOB_APPEND_TEXT();
639 throw parse_error(
"Double ** is only allowed between slashes, like /**/.");
641 HI_GLOB_APPEND_TEXT();
642 r.push_back(make_any_text());
663 HI_GLOB_APPEND_TEXT();
664 r.push_back(make_any_text());
672 HI_GLOB_APPEND_TEXT();
673 r.push_back(make_any_directory());
676 throw parse_error(
"Double ** is only allowed between slashes, like /**/.");
687 }
else if (c == U
']') {
698 throw parse_error(
"Double '--' is not allowed inside a character class, i.e. between '[' and ']'.");
699 }
else if (c == U
']') {
712 if (
not text.empty()) {
719 }
else if (c == U
',') {
736 HI_GLOB_APPEND_TEXT();
740 HI_GLOB_APPEND_TEXT();
741 r.push_back(make_any_text());
746 HI_GLOB_APPEND_TEXT();
751 HI_GLOB_APPEND_TEXT();
752 r.push_back(make_any_text());
756 HI_GLOB_APPEND_TEXT();
757 r.push_back(make_any_directory());
761 throw parse_error(
"Unclosed bracket '[' found in glob pattern.");
764 throw parse_error(
"Unclosed bracket '[' found in glob pattern.");
767 throw parse_error(
"Unclosed brace '{' found in glob pattern.");
772#undef HI_GLOB_APPEND_TEXT
777 return parse(std::ranges::begin(
range), std::ranges::end(
range));
782 matches_strip(const_iterator& first, const_iterator & last, std::u32string_view& str)
noexcept
784 while (first != last) {
785 hilet
it =
Left ? first : last - 1;
786 switch (
it->strip<
Left>(str)) {
787 case match_result_type::fail:
789 case match_result_type::unchecked:
791 case match_result_type::success:
792 if constexpr (
Left) {
806 matches_strip(const_iterator& first, const_iterator& last, std::u32string_view& str)
noexcept
811 [[
nodiscard]]
constexpr bool matches(const_iterator
it, const_iterator last, std::u32string_view original)
const noexcept
813 hi_assert(
it != last);
816 std::u32string_view str;
823 stack.emplace_back(original, 0);
828 case match_result_type::success:
829 if (
it + 1 == last) {
837 ++stack.back().iteration;
842 stack.emplace_back(str, 0);
847 case match_result_type::unchecked:
849 ++stack.back().iteration;
852 case match_result_type::fail:
854 if (stack.size() == 1) {
861 ++stack.back().iteration;