6#include "TTauri/Foundation/required.hpp"
7#include "TTauri/Foundation/algorithm.hpp"
15enum class glob_token_type_t {
28 case glob_token_type_t::String: lhs <<
"String";
break;
29 case glob_token_type_t::StringList: lhs <<
"StringList";
break;
30 case glob_token_type_t::CharacterList: lhs <<
"CharacterList";
break;
31 case glob_token_type_t::InverseCharacterList: lhs <<
"InverseCharacterList";
break;
32 case glob_token_type_t::Separator: lhs <<
"Separator";
break;
33 case glob_token_type_t::AnyString: lhs <<
"AnyString";
break;
34 case glob_token_type_t::AnyCharacter: lhs <<
"AnyCharacter";
break;
35 case glob_token_type_t::AnyDirectory: lhs <<
"AnyDirectory";
break;
36 default: tt_no_default;
42 glob_token_type_t type;
52using glob_token_iterator = glob_token_list_t::iterator;
53using glob_token_const_iterator = glob_token_list_t::const_iterator;
57 return lhs.type == rhs.type && lhs.value == rhs.value && lhs.values == rhs.values;
62 if (rhs.value.size() > 0) {
63 lhs <<
":" << rhs.value;
64 }
else if (rhs.values.size() > 0) {
66 for (
size_t i = 0; i < rhs.values.size(); i++) {
96inline glob_token_list_t parseGlob(std::string_view glob)
104 FoundSlashDoubleStar,
108 state_t state = state_t::Idle;
113 bool isInverse =
false;
114 bool isFirstCharacter =
false;
115 bool isRange =
false;
117 auto i = glob.begin();
119 auto c = (i != glob.end()) ? *i :
'\0';
124 case '/': state = state_t::FoundSlash;
break;
125 case '?': r.emplace_back(glob_token_type_t::AnyCharacter);
break;
126 case '*': r.emplace_back(glob_token_type_t::AnyString);
break;
129 isFirstCharacter =
true;
131 state = state_t::FoundBracket;
133 case '{': state = state_t::FoundBrace;
break;
134 case '\\': state = state_t::FoundEscape;
break;
136 default: state = state_t::FoundText;
continue;
140 case state_t::FoundText:
141 if (c ==
'/' || c ==
'?' || c ==
'*' || c ==
'[' || c ==
'{' || c ==
'\0') {
142 r.emplace_back(glob_token_type_t::String, tmpString);
144 state = state_t::Idle;
146 }
else if (c ==
'\\') {
147 state = state_t::FoundEscape;
153 case state_t::FoundEscape:
155 r.emplace_back(glob_token_type_t::String, tmpString);
156 state = state_t::Idle;
160 state = state_t::FoundText;
164 case state_t::FoundSlash:
166 state = state_t::FoundSlashStar;
168 r.emplace_back(glob_token_type_t::Separator);
169 state = state_t::Idle;
174 case state_t::FoundSlashStar:
176 state = state_t::FoundSlashDoubleStar;
178 r.emplace_back(glob_token_type_t::Separator);
179 r.emplace_back(glob_token_type_t::AnyString);
180 state = state_t::Idle;
185 case state_t::FoundSlashDoubleStar:
187 r.emplace_back(glob_token_type_t::AnyDirectory);
188 r.emplace_back(glob_token_type_t::Separator);
189 state = state_t::Idle;
193 r.emplace_back(glob_token_type_t::Separator);
194 r.emplace_back(glob_token_type_t::AnyString);
195 state = state_t::Idle;
200 case state_t::FoundBracket:
203 if (isFirstCharacter) {
212 if (isFirstCharacter) {
220 r.emplace_back(glob_token_type_t::InverseCharacterList, tmpString);
222 r.emplace_back(glob_token_type_t::CharacterList, tmpString);
226 state = state_t::Idle;
228 isFirstCharacter =
false;
232 if (isFirstCharacter) {
237 isFirstCharacter =
false;
246 r.emplace_back(glob_token_type_t::InverseCharacterList, tmpString);
248 r.emplace_back(glob_token_type_t::CharacterList, tmpString);
250 state = state_t::Idle;
255 ttlet firstCharacter =
static_cast<uint8_t
>(tmpString.
back());
256 ttlet lastCharacter =
static_cast<uint8_t
>(c);
257 for (uint8_t character = firstCharacter + 1; character <= lastCharacter; character++) {
258 tmpString +=
static_cast<char>(character);
264 isFirstCharacter =
false;
269 case state_t::FoundBrace:
274 r.emplace_back(glob_token_type_t::StringList, tmpStringList);
275 tmpStringList.
clear();
276 state = state_t::Idle;
284 r.emplace_back(glob_token_type_t::StringList, tmpStringList);
285 state = state_t::Idle;
301enum class glob_match_result_t {
307inline glob_match_result_t matchGlob(glob_token_const_iterator index, glob_token_const_iterator end, std::string_view str)
310 return (str.size() == 0) ?
311 glob_match_result_t::Match :
312 glob_match_result_t::No;
314 }
else if (str.size() == 0) {
315 switch (index->type) {
316 case glob_token_type_t::Separator:
317 return glob_match_result_t::Partial;
318 case glob_token_type_t::AnyDirectory:
319 return glob_match_result_t::Partial;
320 case glob_token_type_t::AnyString:
321 return matchGlob(index + 1, end, str);
323 return glob_match_result_t::No;
327#define MATCH_GLOB_RECURSE(out, next, end, str)\
328 switch (ttlet tmp = matchGlob(next, end, str)) {\
329 case glob_match_result_t::No: break;\
330 case glob_match_result_t::Match: return tmp;\
331 case glob_match_result_t::Partial: out = tmp; break;\
332 default: tt_no_default;\
336 auto result = glob_match_result_t::No;
337 bool found_slash =
false;
338 ttlet next_index = index + 1;
340 switch (index->type) {
341 case glob_token_type_t::String:
342 if (starts_with(str, index->value)) {
343 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(index->value.size()));
347 case glob_token_type_t::StringList:
348 for (ttlet &value: index->
values) {
349 if (starts_with(str, value)) {
350 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(value.size()));
355 case glob_token_type_t::CharacterList:
356 if (index->value.find(str.front()) != std::string::npos) {
357 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(1));
361 case glob_token_type_t::InverseCharacterList:
362 if (index->value.find(str.front()) == std::string::npos) {
363 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(1));
367 case glob_token_type_t::Separator:
368 if (str.front() ==
'/') {
369 return matchGlob(next_index, end, str.substr(1));
371 return glob_match_result_t::No;
374 case glob_token_type_t::AnyCharacter:
375 if (str.front() !=
'/') {
376 return matchGlob(next_index, end, str.substr(1));
378 return glob_match_result_t::No;
381 case glob_token_type_t::AnyString:
383 for (
size_t i = 0; i <= str.size(); i++) {
384 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(i));
387 if (i < str.size() && str[i] ==
'/') {
393 case glob_token_type_t::AnyDirectory:
396 for (
size_t i = 0; i <= str.size(); i++) {
397 if (i == str.size() || str[i] ==
'/') {
398 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(i));
407#undef MATCH_GLOB_RECURSE
410inline glob_match_result_t matchGlob(glob_token_list_t
const &glob, std::string_view str)
412 return matchGlob(glob.begin(), glob.end(), str);
415inline glob_match_result_t matchGlob(std::string_view glob, std::string_view str)
417 return matchGlob(parseGlob(glob), str);
420inline std::string basePathOfGlob(glob_token_const_iterator first, glob_token_const_iterator last) {
427 return x.type == glob_token_type_t::String || x.type == glob_token_type_t::Separator;
430 if (endOfBase != last) {
434 endOfBase = rfind_if(first, endOfBase, [](ttlet &x) {
435 return x.type == glob_token_type_t::Separator;
440 if (endOfBase == first && first->type == glob_token_type_t::Separator) {
445 for (
auto index = first; index != endOfBase; index++) {
446 switch (index->type) {
447 case glob_token_type_t::String:
450 case glob_token_type_t::Separator:
460inline std::string basePathOfGlob(glob_token_list_t
const &glob)
462 return basePathOfGlob(glob.begin(), glob.end());
465inline std::string basePathOfGlob(std::string_view glob)
467 return basePathOfGlob(parseGlob(glob));