Glob pattern matcher (#122)
This commit is contained in:
119
include/Support/GlobPattern.h
Normal file
119
include/Support/GlobPattern.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
using GlobCharSet = std::bitset<256>;
|
||||
|
||||
/// This class implements a glob pattern matcher to parse patterns to
|
||||
/// watch relative to the base path.
|
||||
///
|
||||
/// Glob patterns can have the following syntax:
|
||||
/// - `*` to match one or more characters in a path segment
|
||||
/// - `?` to match on one character in a path segment
|
||||
/// - `**` to match any number of path segments, including none
|
||||
/// - `{}` to group conditions (e.g. `***.{ts,js}` matches all TypeScript
|
||||
/// and JavaScript files)
|
||||
/// - `[]` to declare a range of characters to match in a path segment
|
||||
/// (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
|
||||
/// - `[!...]` to negate a range of characters to match in a path segment
|
||||
/// (e.g., `example.[!0-9]` to match on `example.a`, `example.b`,
|
||||
/// but not `example.0`)
|
||||
///
|
||||
/// Note: Use only `/` for path segment seperator
|
||||
class GlobPattern {
|
||||
public:
|
||||
/// \param Pat the pattern to match against
|
||||
/// \param MaxSubPatterns if provided limit the number of allowed subpatterns
|
||||
/// created from expanding braces otherwise disable
|
||||
/// brace expansion
|
||||
static std::expected<GlobPattern, std::string>
|
||||
create(llvm::StringRef s, std::optional<size_t> max_subpattern_num = {});
|
||||
|
||||
// Returns true for glob pattern "*" or "**". Can be used to avoid expensive
|
||||
// preparation/acquisition of the input for match().
|
||||
bool isTrivialMatchAll() const {
|
||||
if(!prefix.empty()) {
|
||||
// have no prefix
|
||||
return false;
|
||||
}
|
||||
if((sub_globs.size() == 1 && sub_globs[0].getPat() == "*") ||
|
||||
(sub_globs.size() == 2 && sub_globs[0].getPat() == "**")) {
|
||||
// not "*" or "**"
|
||||
return true;
|
||||
}
|
||||
// default return false
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \returns \p true if \p str matches this glob pattern
|
||||
bool match(llvm::StringRef s);
|
||||
|
||||
private:
|
||||
/// GlobPattern is seperated into `Prefix + SubGlobPattern`
|
||||
std::string prefix;
|
||||
|
||||
/// if prefix contains full path:
|
||||
///
|
||||
/// xxx/yyy/*.c
|
||||
/// ~~~~~~^
|
||||
///
|
||||
/// prefix contains incomplete path:
|
||||
/// xxx/yyy*.c
|
||||
/// ~~~~~~^
|
||||
///
|
||||
bool prefix_at_seg_end = false;
|
||||
|
||||
/// SubGlobPattern:
|
||||
/// Pattern `foo.{c,cpp,cppm}`
|
||||
/// -> extend to 3 SubGlobPatterns: `foo.c`, `foo.cpp`, `foo.cppm`
|
||||
struct SubGlobPattern {
|
||||
/// \param Pat the pattern to match against
|
||||
static std::expected<SubGlobPattern, std::string> create(llvm::StringRef s);
|
||||
/// \returns \p true if \p S matches this glob pattern
|
||||
bool match(llvm::StringRef str) const;
|
||||
|
||||
llvm::StringRef getPat() const {
|
||||
return llvm::StringRef{pat.data(), pat.size()};
|
||||
}
|
||||
|
||||
struct Bracket {
|
||||
size_t next_offset;
|
||||
GlobCharSet bytes;
|
||||
};
|
||||
|
||||
llvm::SmallVector<Bracket, 0> brackets;
|
||||
|
||||
// GlobSegment devide patterm into segments by '/'
|
||||
// SubPattern:
|
||||
// **include/aaa/bbb/test[0-9].cc
|
||||
// ^~~~~~~~~^~~~^~~~^~~~~~~~~~~~~^
|
||||
// 1 2 3 4
|
||||
// Devided into 4 segments
|
||||
//
|
||||
// SubPattern:
|
||||
// **/include/aaa/bbb/test[0-9].h
|
||||
// ^~^~~~~~~~^~~~^~~~^~~~~~~~~~~~^
|
||||
// 1 2 3 4 5
|
||||
// Devided into 5 segments
|
||||
struct GlobSegment {
|
||||
size_t start;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
llvm::SmallVector<GlobSegment, 6> glob_segments;
|
||||
|
||||
llvm::SmallVector<char, 0> pat;
|
||||
};
|
||||
|
||||
llvm::SmallVector<SubGlobPattern, 1> sub_globs;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
562
src/Support/GlobPattern.cpp
Normal file
562
src/Support/GlobPattern.cpp
Normal file
@@ -0,0 +1,562 @@
|
||||
#include <format>
|
||||
|
||||
#include "Support/GlobPattern.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
// Expands character ranges and returns a bitmap.
|
||||
// For example, "a-cf-hz" is expanded to "abcfghz".
|
||||
static std::expected<GlobCharSet, std::string> expand(llvm::StringRef s, llvm::StringRef original) {
|
||||
GlobCharSet bv{false};
|
||||
|
||||
for(size_t i = 0, e = s.size(); i < e; ++i) {
|
||||
switch(s[i]) {
|
||||
case '\\': {
|
||||
++i;
|
||||
if(i == e) {
|
||||
return std::unexpected{"Invalid expansions: stary `\\`"};
|
||||
}
|
||||
if(s[i] != '/') {
|
||||
bv.set(s[i], true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '-': {
|
||||
if(i == 0 || i + 1 == e) {
|
||||
bv.set('-', true);
|
||||
break;
|
||||
}
|
||||
char c_begin = s[i - 1];
|
||||
char c_end = s[i + 1];
|
||||
++i;
|
||||
if(c_end == '\\') {
|
||||
++i;
|
||||
if(s[i] == e) {
|
||||
return std::unexpected{"Invalid expansions: stary `\\`"};
|
||||
}
|
||||
c_end = s[i];
|
||||
}
|
||||
if(c_begin > c_end) {
|
||||
return std::unexpected{
|
||||
std::format("Invalid expansion: `{}` is larger than `{}`", c_begin, c_end)};
|
||||
}
|
||||
for(char c = c_begin; c <= c_end; ++c) {
|
||||
if(c != '/') {
|
||||
bv.set(c, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if(s[i] != '/') {
|
||||
bv.set(s[i], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bv;
|
||||
}
|
||||
|
||||
// Identify brace expansions in S and return the list of patterns they expand
|
||||
// into.
|
||||
static std::expected<llvm::SmallVector<std::string, 1>, std::string>
|
||||
parseBraceExpansions(llvm::StringRef s, std::optional<size_t> max_subpattern_num) {
|
||||
llvm::SmallVector<std::string> subpatterns = {s.str()};
|
||||
if(!max_subpattern_num || !s.contains('{')) {
|
||||
return subpatterns;
|
||||
}
|
||||
|
||||
struct BraceExpansion {
|
||||
size_t start;
|
||||
size_t length;
|
||||
llvm::SmallVector<llvm::StringRef, 2> terms;
|
||||
};
|
||||
|
||||
llvm::SmallVector<BraceExpansion, 0> brace_expansions;
|
||||
|
||||
BraceExpansion* current_be = nullptr;
|
||||
size_t term_begin = 0;
|
||||
for(size_t i = 0, e = s.size(); i != e; ++i) {
|
||||
if(s[i] == '[') {
|
||||
++i;
|
||||
if(s[i] == ']') {
|
||||
++i;
|
||||
}
|
||||
while(i != e && s[i] != ']') {
|
||||
if(s[i] == '\\') {
|
||||
if(i == e) {
|
||||
return std::unexpected{
|
||||
"Invalid glob pattern, unmatched '[', with stray " "'\\' inside"};
|
||||
}
|
||||
++i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if(i == e) {
|
||||
return std::unexpected{"Invalid glob pattern, unmatched '['"};
|
||||
}
|
||||
} else if(s[i] == '{') {
|
||||
if(current_be) {
|
||||
return std::unexpected{"Nested brace expansions are not supported"};
|
||||
}
|
||||
current_be = &brace_expansions.emplace_back();
|
||||
current_be->start = i;
|
||||
term_begin = i + 1;
|
||||
} else if(s[i] == ',') {
|
||||
if(!current_be) {
|
||||
continue;
|
||||
}
|
||||
current_be->terms.push_back(s.substr(term_begin, i - term_begin));
|
||||
term_begin = i + 1;
|
||||
} else if(s[i] == '}') {
|
||||
if(!current_be) {
|
||||
continue;
|
||||
}
|
||||
if(current_be->terms.empty() && i - term_begin == 0) {
|
||||
return std::unexpected{"Empty brace expression is not supported"};
|
||||
}
|
||||
current_be->terms.push_back(s.substr(term_begin, i - term_begin));
|
||||
current_be->length = i - current_be->start + 1;
|
||||
current_be = nullptr;
|
||||
} else if(s[i] == '\\') {
|
||||
++i;
|
||||
if(i == e) {
|
||||
return std::unexpected{"Invalid glob pattern, stray '\\'"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(current_be) {
|
||||
return std::unexpected{"Incomplete brace expansion"};
|
||||
}
|
||||
|
||||
size_t subpattern_num = 1;
|
||||
for(auto& be: brace_expansions) {
|
||||
if(subpattern_num > std::numeric_limits<size_t>::max() / be.terms.size()) {
|
||||
subpattern_num = std::numeric_limits<size_t>::max();
|
||||
break;
|
||||
}
|
||||
subpattern_num *= be.terms.size();
|
||||
}
|
||||
|
||||
if(subpattern_num > *max_subpattern_num) {
|
||||
return std::unexpected{"Too many brace expansions"};
|
||||
}
|
||||
|
||||
for(auto& be: reverse(brace_expansions)) {
|
||||
llvm::SmallVector<std::string> orig_sub_patterns;
|
||||
std::swap(subpatterns, orig_sub_patterns);
|
||||
for(llvm::StringRef term: be.terms) {
|
||||
for(llvm::StringRef orig: orig_sub_patterns) {
|
||||
subpatterns.emplace_back(orig).replace(be.start, be.length, term);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subpatterns;
|
||||
}
|
||||
|
||||
std::expected<GlobPattern, std::string>
|
||||
GlobPattern::create(llvm::StringRef s, std::optional<size_t> max_subpattern_num) {
|
||||
// Store the prefix that does not contain any metacharacter.
|
||||
GlobPattern pat;
|
||||
size_t prefix_size = s.find_first_of("?*[{\\");
|
||||
if(prefix_size == std::string::npos) {
|
||||
pat.prefix = s.substr(0, prefix_size).str();
|
||||
// check if there is multiple `/` in prefix
|
||||
size_t last_slash = 0;
|
||||
size_t size = pat.prefix.size();
|
||||
for(size_t i = 0; i < size; ++i) {
|
||||
if(s[i] == '/' && i - last_slash == 1) {
|
||||
return std::unexpected{"Multiple `/` is not allowed"};
|
||||
}
|
||||
last_slash = i;
|
||||
}
|
||||
return pat;
|
||||
}
|
||||
if(prefix_size != 0 && s[prefix_size - 1] == '/') {
|
||||
pat.prefix_at_seg_end = true;
|
||||
--prefix_size;
|
||||
}
|
||||
pat.prefix = s.substr(0, prefix_size).str();
|
||||
// check if there is multiple `/` in prefix
|
||||
size_t last_slash = 0;
|
||||
size_t size = pat.prefix.size();
|
||||
for(size_t i = 0; i < size; ++i) {
|
||||
if(s[i] == '/' && i - last_slash == 1) {
|
||||
return std::unexpected{"Multiple `/` is not allowed"};
|
||||
}
|
||||
last_slash = i;
|
||||
}
|
||||
s = s.substr(pat.prefix_at_seg_end ? prefix_size + 1 : prefix_size);
|
||||
|
||||
llvm::SmallVector<std::string, 1> sub_pats;
|
||||
if(auto res = parseBraceExpansions(s, max_subpattern_num); res.has_value()) {
|
||||
sub_pats = res.value();
|
||||
} else {
|
||||
return std::unexpected{res.error()};
|
||||
}
|
||||
|
||||
for(auto sub_pat: sub_pats) {
|
||||
if(auto res = SubGlobPattern::create(sub_pat); res.has_value()) {
|
||||
pat.sub_globs.push_back(res.value());
|
||||
} else {
|
||||
return std::unexpected{res.error()};
|
||||
}
|
||||
}
|
||||
|
||||
return pat;
|
||||
}
|
||||
|
||||
std::expected<GlobPattern::SubGlobPattern, std::string>
|
||||
GlobPattern::SubGlobPattern::create(llvm::StringRef s) {
|
||||
SubGlobPattern pat;
|
||||
llvm::SmallVector<GlobSegment, 0> glob_segments;
|
||||
GlobSegment* current_gs = &glob_segments.emplace_back();
|
||||
current_gs->start = 0;
|
||||
pat.pat.assign(s.begin(), s.end());
|
||||
// Parse brackets.
|
||||
size_t e = s.size();
|
||||
for(size_t i = 0; i < e; ++i) {
|
||||
if(!current_gs) {
|
||||
current_gs = &glob_segments.emplace_back();
|
||||
current_gs->start = i;
|
||||
}
|
||||
if(s[i] == '[') {
|
||||
++i;
|
||||
size_t j = i;
|
||||
if(s[j] == ']') {
|
||||
// ']' is allowed as the first character of a character class. '[]'
|
||||
// is invalid. So, just skip the first character.
|
||||
++j;
|
||||
}
|
||||
while(j != e && s[j] != ']') {
|
||||
// Check if there is invalid escape char
|
||||
++j;
|
||||
if(s[j - 1] == '\\') {
|
||||
if(j == e) {
|
||||
// Reach here in such case:
|
||||
// [a-zA-Z\]
|
||||
// ~~~~~~~~^
|
||||
return std::unexpected{
|
||||
"Invalid glob pattern, unmatched '[' with stray '\\' inside"};
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
if(j == e) {
|
||||
return std::unexpected{"Invalid glob pattern, unmatched '['"};
|
||||
}
|
||||
llvm::StringRef chars = s.substr(i, j - i);
|
||||
bool invert = s[i] == '^' || s[i] == '!';
|
||||
auto bv = invert ? expand(chars.substr(1), s) : expand(chars, s);
|
||||
if(!bv.has_value()) {
|
||||
return std::unexpected{bv.error()};
|
||||
}
|
||||
if(invert) {
|
||||
bv->flip();
|
||||
}
|
||||
pat.brackets.push_back(Bracket{j + 1, bv.value()});
|
||||
i = j;
|
||||
} else if(s[i] == '\\') {
|
||||
if(++i == e) {
|
||||
return std::unexpected{"Invalid glob pattern, stray '\\'"};
|
||||
}
|
||||
} else if(s[i] == '/') {
|
||||
if(i - current_gs->start == 1) {
|
||||
return std::unexpected{"Multiple `/` is not allowed"};
|
||||
}
|
||||
current_gs->end = i;
|
||||
current_gs = nullptr;
|
||||
} else if(s[i] == '*') {
|
||||
// check if there is multiple `*`
|
||||
if(i + 1 < e - 1 && s[i + 1] == '*' && s[i + 2] == '*') {
|
||||
return std::unexpected{"Multiple `*` is not allowed"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(current_gs) {
|
||||
current_gs->end = e;
|
||||
current_gs = nullptr;
|
||||
}
|
||||
|
||||
std::swap(pat.glob_segments, glob_segments);
|
||||
return pat;
|
||||
}
|
||||
|
||||
bool GlobPattern::match(llvm::StringRef str) {
|
||||
if(!str.consume_front(prefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(str.empty() && sub_globs.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!str.empty() && prefix_at_seg_end) {
|
||||
if(str[0] != '/') {
|
||||
return false;
|
||||
}
|
||||
str = str.substr(1);
|
||||
}
|
||||
|
||||
for(auto& Glob: sub_globs) {
|
||||
if(Glob.match(str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GlobPattern::SubGlobPattern::match(llvm::StringRef str) const {
|
||||
// p: Current position in the pattern
|
||||
// s: Current position in the String
|
||||
// b: Current processed bracket num
|
||||
// s_start: String start
|
||||
// s_end: String end
|
||||
// p_start: Pattern start
|
||||
// p_end: Pattern end
|
||||
// seg_start: Position of the start of current segment
|
||||
// s_end: Position of the end of current segment
|
||||
// b: Current matched bracket
|
||||
// current_glob_seg: Index of glob patterns
|
||||
// wild_mode: Whether wildcards are allowed to cross segments
|
||||
const char* s = str.data();
|
||||
const char* const s_start = s;
|
||||
const char* const s_end = s + str.size();
|
||||
const char* p = pat.data();
|
||||
const char* seg_start = p;
|
||||
const char* const p_start = p;
|
||||
const char* const p_end = p + pat.size();
|
||||
const char* seg_end = p + glob_segments[0].end;
|
||||
size_t b = 0;
|
||||
size_t current_glob_seg = 0;
|
||||
bool wild_mode = false;
|
||||
|
||||
// Status for backtracing
|
||||
struct BacktraceStat {
|
||||
size_t b;
|
||||
size_t glob_seg;
|
||||
bool wild_mode;
|
||||
const char* p;
|
||||
const char* s;
|
||||
const char* seg_end;
|
||||
const char* seg_start;
|
||||
};
|
||||
|
||||
llvm::SmallVector<BacktraceStat, 6> backtrace_stack;
|
||||
const size_t seg_num = glob_segments.size();
|
||||
|
||||
auto save_stat =
|
||||
[&backtrace_stack, &b, ¤t_glob_seg, &wild_mode, &p, &s, &seg_end, &seg_start]() {
|
||||
backtrace_stack.push_back({.b = b,
|
||||
.glob_seg = current_glob_seg,
|
||||
.wild_mode = wild_mode,
|
||||
.p = p,
|
||||
.s = s,
|
||||
.seg_end = seg_end,
|
||||
.seg_start = seg_start});
|
||||
};
|
||||
|
||||
while(current_glob_seg < seg_num) {
|
||||
|
||||
if(s == s_end) {
|
||||
// Return true if all pattern characters are processed or only
|
||||
// '*' or '/' characters remain
|
||||
return getPat().find_first_not_of("*/", p - pat.data()) == std::string::npos;
|
||||
}
|
||||
|
||||
if(p != seg_end) {
|
||||
switch(*p) {
|
||||
case '*': {
|
||||
if(p + 1 != p_end && *(p + 1) == '*') {
|
||||
// Met '**'
|
||||
p += 2;
|
||||
wild_mode = true;
|
||||
// Consume extra '*'
|
||||
while(p != p_end && (*p == '*' || *p == '/')) {
|
||||
if(*p == '/') {
|
||||
if(current_glob_seg + 1 == seg_num) {
|
||||
return true;
|
||||
}
|
||||
++current_glob_seg;
|
||||
seg_start = p_start + glob_segments[current_glob_seg].start;
|
||||
seg_end = p_start + glob_segments[current_glob_seg].end;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
if(p == seg_end) {
|
||||
// '**' at segment end
|
||||
if(current_glob_seg + 1 == seg_num) {
|
||||
// '**' at pattern end
|
||||
return true;
|
||||
}
|
||||
|
||||
// Goto next segment
|
||||
++current_glob_seg;
|
||||
while(s != s_end && *s == '/') {
|
||||
++s;
|
||||
}
|
||||
p = p_start + glob_segments[current_glob_seg].start;
|
||||
seg_start = p;
|
||||
seg_end = p_start + glob_segments[current_glob_seg].end;
|
||||
}
|
||||
save_stat();
|
||||
} else {
|
||||
// Met '*'
|
||||
++p;
|
||||
wild_mode = false;
|
||||
if(p == seg_end) {
|
||||
while(s != s_end && *s != '/') {
|
||||
++s;
|
||||
}
|
||||
if(s == s_end) {
|
||||
continue;
|
||||
}
|
||||
if(s + 1 != s_end) {
|
||||
++s;
|
||||
}
|
||||
if(current_glob_seg + 1 == seg_num) {
|
||||
return true;
|
||||
}
|
||||
++current_glob_seg;
|
||||
p = p_start + glob_segments[current_glob_seg].start;
|
||||
seg_start = p;
|
||||
seg_end = p_start + glob_segments[current_glob_seg].end;
|
||||
}
|
||||
save_stat();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case '?': {
|
||||
if(p + 1 != seg_end && *(p + 1) == '*') {
|
||||
// Handle '?*'
|
||||
unsigned offset = *s == '\\' ? 2 : 1;
|
||||
s += offset;
|
||||
save_stat();
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
if(s != s_end && *s != '/') {
|
||||
++p;
|
||||
s = *s == '\\' ? s + 2 : s + 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '[': {
|
||||
if(b < brackets.size() && brackets[b].bytes[uint8_t(*s)]) {
|
||||
if(p == seg_start && !(s == s_start || *(s - 1) == '/')) {
|
||||
// Segment start dismatch
|
||||
break;
|
||||
}
|
||||
p = pat.data() + brackets[b].next_offset;
|
||||
++b;
|
||||
++s;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '\\': {
|
||||
if(p + 1 != seg_end && *(p + 1) == *s) {
|
||||
if(p == seg_start && !(s == s_start || *(s - 1) == '/')) {
|
||||
// Segment start dismatch
|
||||
break;
|
||||
}
|
||||
p += 2;
|
||||
++s;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if(*p == *s) {
|
||||
if(p == seg_start && !(s == s_start || *(s - 1) == '/')) {
|
||||
// Segment start dismatch
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
++s;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// P comes to a segment end
|
||||
if(seg_end != p_end) {
|
||||
if(wild_mode) {
|
||||
// Step to next segment
|
||||
++current_glob_seg;
|
||||
while(s != s_end && *s != '/') {
|
||||
++s;
|
||||
}
|
||||
if(s != s_end && *s == '/') {
|
||||
++s;
|
||||
}
|
||||
if(current_glob_seg >= seg_num) {
|
||||
return s == s_end;
|
||||
}
|
||||
p = p_start + glob_segments[current_glob_seg].start;
|
||||
seg_start = p;
|
||||
seg_end = p_start + glob_segments[current_glob_seg].end;
|
||||
continue;
|
||||
} else {
|
||||
if(*seg_end != *s) {
|
||||
return false;
|
||||
}
|
||||
// *S and *SegEnd should be '/'
|
||||
// Escape repeat '/'
|
||||
while(s != s_end && *s == '/') {
|
||||
++s;
|
||||
}
|
||||
// Step to next segment
|
||||
++current_glob_seg;
|
||||
p = p_start + glob_segments[current_glob_seg].start;
|
||||
seg_start = p;
|
||||
seg_end = p_start + glob_segments[current_glob_seg].end;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(backtrace_stack.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Backstrace
|
||||
auto& state = backtrace_stack.back();
|
||||
|
||||
p = state.p;
|
||||
s = ++state.s;
|
||||
b = state.b;
|
||||
current_glob_seg = state.glob_seg;
|
||||
wild_mode = state.wild_mode;
|
||||
seg_start = state.seg_start;
|
||||
seg_end = state.seg_end;
|
||||
|
||||
// In non-WildMode, pop back invalid stat in the stack
|
||||
if(!wild_mode && (s == s_end || *s == '/')) {
|
||||
backtrace_stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// In non-WildMode, '/' is a barrier
|
||||
if(!wild_mode && *s == '/') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return s == s_end;
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
@@ -148,7 +148,6 @@ int main() {
|
||||
params.command = "clang++ -std=c++20 main.cpp";
|
||||
params.content = content;
|
||||
params.outPath = path::join(".", "header2.pch");
|
||||
params.bound.reset();
|
||||
|
||||
{
|
||||
auto AST = compile(params);
|
||||
|
||||
484
unittests/Support/GlobPattern.cpp
Normal file
484
unittests/Support/GlobPattern.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "Test/Test.h"
|
||||
#include "Support/GlobPattern.h"
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
#define PATDEF(NAME, PAT) \
|
||||
const char* PatString_##NAME = PAT; \
|
||||
auto Res##NAME = clice::GlobPattern::create(PatString_##NAME, 100); \
|
||||
if(!Res##NAME.has_value()) { \
|
||||
std::cout << Res##NAME.error() << '\n'; \
|
||||
} \
|
||||
assert(Res##NAME.has_value()); \
|
||||
auto NAME = Res##NAME.value();
|
||||
|
||||
|
||||
TEST(GlobPattern, PattermSema) {
|
||||
auto Pat1 = clice::GlobPattern::create("**/****.{c,cc}", 100);
|
||||
EXPECT_EQ(Pat1.has_value(), false);
|
||||
|
||||
auto Pat2 = clice::GlobPattern::create("/foo/bar/baz////aaa.{c,cc}", 100);
|
||||
EXPECT_EQ(Pat2.has_value(), false);
|
||||
|
||||
auto Pat3 = clice::GlobPattern::create("/foo/bar/baz/**////*.{c,cc}", 100);
|
||||
EXPECT_EQ(Pat3.has_value(), false);
|
||||
}
|
||||
|
||||
TEST(GlobPattern, Simple) {
|
||||
PATDEF(Pat1, "node_modules")
|
||||
EXPECT_EQ(Pat1.match("node_modules"), true);
|
||||
EXPECT_EQ(Pat1.match("node_module"), false);
|
||||
EXPECT_EQ(Pat1.match("/node_modules"), false);
|
||||
EXPECT_EQ(Pat1.match("test/node_modules"), false);
|
||||
|
||||
PATDEF(Pat2, "test.txt")
|
||||
EXPECT_EQ(Pat2.match("test.txt"), true);
|
||||
EXPECT_EQ(Pat2.match("test?txt"), false);
|
||||
EXPECT_EQ(Pat2.match("/text.txt"), false);
|
||||
EXPECT_EQ(Pat2.match("test/test.txt"), false);
|
||||
|
||||
PATDEF(Pat3, "test(.txt")
|
||||
EXPECT_EQ(Pat3.match("test(.txt"), true);
|
||||
EXPECT_EQ(Pat3.match("test?txt"), false);
|
||||
|
||||
PATDEF(Pat4, "qunit")
|
||||
EXPECT_EQ(Pat4.match("qunit"), true);
|
||||
EXPECT_EQ(Pat4.match("qunit.css"), false);
|
||||
EXPECT_EQ(Pat4.match("test/qunit"), false);
|
||||
|
||||
PATDEF(Pat5, "/DNXConsoleApp/**/*.cs")
|
||||
EXPECT_EQ(Pat5.match("/DNXConsoleApp/Program.cs"), true);
|
||||
EXPECT_EQ(Pat5.match("/DNXConsoleApp/foo/Program.cs"), true);
|
||||
}
|
||||
|
||||
TEST(GlobPattern, DotHidden) {
|
||||
PATDEF(Pat1, ".*");
|
||||
EXPECT_EQ(Pat1.match(".git"), true);
|
||||
EXPECT_EQ(Pat1.match(".hidden.txt"), true);
|
||||
EXPECT_EQ(Pat1.match("git"), false);
|
||||
EXPECT_EQ(Pat1.match("hidden.txt"), false);
|
||||
EXPECT_EQ(Pat1.match("path/.git"), false);
|
||||
EXPECT_EQ(Pat1.match("path/.hidden.txt"), false);
|
||||
|
||||
PATDEF(Pat2, "**/.*");
|
||||
EXPECT_EQ(Pat2.match(".git"), true);
|
||||
EXPECT_EQ(Pat2.match("/.git"), true);
|
||||
EXPECT_EQ(Pat2.match(".hidden.txt"), true);
|
||||
EXPECT_EQ(Pat2.match("git"), false);
|
||||
EXPECT_EQ(Pat2.match("hidden.txt"), false);
|
||||
EXPECT_EQ(Pat2.match("path/.git"), true);
|
||||
EXPECT_EQ(Pat2.match("path/.hidden.txt"), true);
|
||||
EXPECT_EQ(Pat2.match("/path/.git"), true);
|
||||
EXPECT_EQ(Pat2.match("/path/.hidden.txt"), true);
|
||||
EXPECT_EQ(Pat2.match("path/git"), false);
|
||||
EXPECT_EQ(Pat2.match("pat.h/hidden.txt"), false);
|
||||
|
||||
PATDEF(Pat3, "._*");
|
||||
EXPECT_EQ(Pat3.match("._git"), true);
|
||||
EXPECT_EQ(Pat3.match("._hidden.txt"), true);
|
||||
EXPECT_EQ(Pat3.match("git"), false);
|
||||
EXPECT_EQ(Pat3.match("hidden.txt"), false);
|
||||
EXPECT_EQ(Pat3.match("path/._git"), false);
|
||||
EXPECT_EQ(Pat3.match("path/._hidden.txt"), false);
|
||||
|
||||
PATDEF(Pat4, "**/._*");
|
||||
EXPECT_EQ(Pat4.match("._git"), true);
|
||||
EXPECT_EQ(Pat4.match("._hidden.txt"), true);
|
||||
EXPECT_EQ(Pat4.match("git"), false);
|
||||
EXPECT_EQ(Pat4.match("hidden._txt"), false);
|
||||
EXPECT_EQ(Pat4.match("path/._git"), true);
|
||||
EXPECT_EQ(Pat4.match("path/._hidden.txt"), true);
|
||||
EXPECT_EQ(Pat4.match("/path/._git"), true);
|
||||
EXPECT_EQ(Pat4.match("/path/._hidden.txt"), true);
|
||||
EXPECT_EQ(Pat4.match("path/git"), false);
|
||||
EXPECT_EQ(Pat4.match("pat.h/hidden._txt"), false);
|
||||
}
|
||||
|
||||
TEST(GlobPattern, EscapeCharacter) {
|
||||
PATDEF(Pat1, R"(\*star)")
|
||||
EXPECT_EQ(Pat1.match("*star"), true);
|
||||
|
||||
PATDEF(Pat2, R"(\{\*\})")
|
||||
EXPECT_EQ(Pat2.match("{*}"), true);
|
||||
}
|
||||
|
||||
TEST(GlobPattern, BracketExpr) {
|
||||
PATDEF(Pat1, R"([a-zA-Z\]])")
|
||||
EXPECT_EQ(Pat1.match(R"(])"), true);
|
||||
EXPECT_EQ(Pat1.match(R"([)"), false);
|
||||
EXPECT_EQ(Pat1.match(R"(s)"), true);
|
||||
EXPECT_EQ(Pat1.match(R"(S)"), true);
|
||||
EXPECT_EQ(Pat1.match(R"(0)"), false);
|
||||
|
||||
PATDEF(Pat2, R"([\^a-zA-Z""\\])")
|
||||
EXPECT_EQ(Pat2.match(R"(")"), true);
|
||||
EXPECT_EQ(Pat2.match(R"(^)"), true);
|
||||
EXPECT_EQ(Pat2.match(R"(\)"), true);
|
||||
EXPECT_EQ(Pat2.match(R"(")"), true);
|
||||
EXPECT_EQ(Pat2.match(R"(x)"), true);
|
||||
EXPECT_EQ(Pat2.match(R"(X)"), true);
|
||||
EXPECT_EQ(Pat2.match(R"(0)"), false);
|
||||
|
||||
PATDEF(Pat3, R"([!0-9a-fA-F\-+\*])")
|
||||
EXPECT_EQ(Pat3.match("1"), false);
|
||||
EXPECT_EQ(Pat3.match("*"), false);
|
||||
EXPECT_EQ(Pat3.match("s"), true);
|
||||
EXPECT_EQ(Pat3.match("S"), true);
|
||||
EXPECT_EQ(Pat3.match("H"), true);
|
||||
EXPECT_EQ(Pat3.match("]"), true);
|
||||
|
||||
PATDEF(Pat4, R"([^\^0-9a-fA-F\-+\*])")
|
||||
EXPECT_EQ(Pat4.match("1"), false);
|
||||
EXPECT_EQ(Pat4.match("*"), false);
|
||||
EXPECT_EQ(Pat4.match("^"), false);
|
||||
EXPECT_EQ(Pat4.match("s"), true);
|
||||
EXPECT_EQ(Pat4.match("S"), true);
|
||||
EXPECT_EQ(Pat4.match("H"), true);
|
||||
EXPECT_EQ(Pat4.match("]"), true);
|
||||
|
||||
PATDEF(Pat5, R"([\*-\^])")
|
||||
EXPECT_EQ(Pat5.match("*"), true);
|
||||
EXPECT_EQ(Pat5.match("a"), false);
|
||||
EXPECT_EQ(Pat5.match("z"), false);
|
||||
EXPECT_EQ(Pat5.match("A"), true);
|
||||
EXPECT_EQ(Pat5.match("Z"), true);
|
||||
EXPECT_EQ(Pat5.match("\\"), true);
|
||||
EXPECT_EQ(Pat5.match("^"), true);
|
||||
EXPECT_EQ(Pat5.match("-"), true);
|
||||
|
||||
PATDEF(Pat6, "foo.[^0-9]")
|
||||
EXPECT_EQ(Pat6.match("foo.5"), false);
|
||||
EXPECT_EQ(Pat6.match("foo.8"), false);
|
||||
EXPECT_EQ(Pat6.match("bar.5"), false);
|
||||
EXPECT_EQ(Pat6.match("foo.f"), true);
|
||||
|
||||
PATDEF(Pat7, "foo.[!0-9]")
|
||||
EXPECT_EQ(Pat7.match("foo.5"), false);
|
||||
EXPECT_EQ(Pat7.match("foo.8"), false);
|
||||
EXPECT_EQ(Pat7.match("bar.5"), false);
|
||||
EXPECT_EQ(Pat7.match("foo.f"), true);
|
||||
|
||||
PATDEF(Pat8, "foo.[0!^*?]")
|
||||
EXPECT_EQ(Pat8.match("foo.5"), false);
|
||||
EXPECT_EQ(Pat8.match("foo.8"), false);
|
||||
EXPECT_EQ(Pat8.match("foo.0"), true);
|
||||
EXPECT_EQ(Pat8.match("foo.!"), true);
|
||||
EXPECT_EQ(Pat8.match("foo.^"), true);
|
||||
EXPECT_EQ(Pat8.match("foo.*"), true);
|
||||
EXPECT_EQ(Pat8.match("foo.?"), true);
|
||||
|
||||
PATDEF(Pat9, "foo[/]bar")
|
||||
EXPECT_EQ(Pat9.match("foo/bar"), false);
|
||||
|
||||
PATDEF(Pat10, "foo.[[]")
|
||||
EXPECT_EQ(Pat10.match("foo.["), true);
|
||||
|
||||
PATDEF(Pat11, "foo.[]]")
|
||||
EXPECT_EQ(Pat11.match("foo.]"), true);
|
||||
|
||||
PATDEF(Pat12, "foo.[][!]")
|
||||
EXPECT_EQ(Pat12.match("foo.]"), true);
|
||||
EXPECT_EQ(Pat12.match("foo.["), true);
|
||||
EXPECT_EQ(Pat12.match("foo.!"), true);
|
||||
|
||||
PATDEF(Pat13, "foo.[]-]")
|
||||
EXPECT_EQ(Pat13.match("foo.]"), true);
|
||||
EXPECT_EQ(Pat13.match("foo.-"), true);
|
||||
|
||||
PATDEF(Pat14, "foo.[0-9]")
|
||||
EXPECT_EQ(Pat14.match("foo.5"), true);
|
||||
EXPECT_EQ(Pat14.match("foo.8"), true);
|
||||
EXPECT_EQ(Pat14.match("bar.5"), false);
|
||||
EXPECT_EQ(Pat14.match("foo.f"), false);
|
||||
|
||||
}
|
||||
|
||||
TEST(GlobPattern, BraceExpr) {
|
||||
PATDEF(Pat1, "*foo[0-9a-z].{c,cpp,cppm,?pp}")
|
||||
EXPECT_EQ(Pat1.match("foo1.cc"), false);
|
||||
EXPECT_EQ(Pat1.match("foo2.cpp"), true);
|
||||
EXPECT_EQ(Pat1.match("foo3.cppm"), true);
|
||||
EXPECT_EQ(Pat1.match("foot.cppm"), true);
|
||||
EXPECT_EQ(Pat1.match("foot.hpp"), true);
|
||||
EXPECT_EQ(Pat1.match("foot.app"), true);
|
||||
EXPECT_EQ(Pat1.match("fooD.cppm"), false);
|
||||
EXPECT_EQ(Pat1.match("BarfooD.cppm"), false);
|
||||
EXPECT_EQ(Pat1.match("foofooD.cppm"), false);
|
||||
|
||||
PATDEF(Pat2, "proj/{build*,include,src}/*.{cc,cpp,h,hpp}")
|
||||
EXPECT_EQ(Pat2.match("proj/include/foo.cc"), true);
|
||||
EXPECT_EQ(Pat2.match("proj/include/bar.cpp"), true);
|
||||
EXPECT_EQ(Pat2.match("proj/include/xxx/yyy/zzz/foo.cc"), false);
|
||||
EXPECT_EQ(Pat2.match("proj/build-yyy/foo.h"), true);
|
||||
EXPECT_EQ(Pat2.match("proj/build-xxx/foo.cpp"), true);
|
||||
EXPECT_EQ(Pat2.match("proj/build/foo.cpp"), true);
|
||||
EXPECT_EQ(Pat2.match("proj/build-xxx/xxx/yyy/zzz/foo.cpp"), false);
|
||||
|
||||
PATDEF(Pat3, "*.{html,js}")
|
||||
EXPECT_EQ(Pat3.match("foo.js"), true);
|
||||
EXPECT_EQ(Pat3.match("foo.html"), true);
|
||||
EXPECT_EQ(Pat3.match("folder/foo.js"), false);
|
||||
EXPECT_EQ(Pat3.match("/node_modules/foo.js"), false);
|
||||
EXPECT_EQ(Pat3.match("foo.jss"), false);
|
||||
EXPECT_EQ(Pat3.match("some.js/test"), false);
|
||||
|
||||
PATDEF(Pat4, "*.{html}")
|
||||
EXPECT_EQ(Pat4.match("foo.html"), true);
|
||||
EXPECT_EQ(Pat4.match("foo.js"), false);
|
||||
EXPECT_EQ(Pat4.match("folder/foo.js"), false);
|
||||
EXPECT_EQ(Pat4.match("/node_modules/foo.js"), false);
|
||||
EXPECT_EQ(Pat4.match("foo.jss"), false);
|
||||
EXPECT_EQ(Pat4.match("some.js/test"), false);
|
||||
|
||||
PATDEF(Pat5, "{node_modules,testing}")
|
||||
EXPECT_EQ(Pat5.match("node_modules"), true);
|
||||
EXPECT_EQ(Pat5.match("testing"), true);
|
||||
EXPECT_EQ(Pat5.match("node_module"), false);
|
||||
EXPECT_EQ(Pat5.match("dtesting"), false);
|
||||
|
||||
PATDEF(Pat6, "**/{foo,bar}")
|
||||
EXPECT_EQ(Pat6.match("foo"), true);
|
||||
EXPECT_EQ(Pat6.match("bar"), true);
|
||||
EXPECT_EQ(Pat6.match("test/foo"), true);
|
||||
EXPECT_EQ(Pat6.match("test/bar"), true);
|
||||
EXPECT_EQ(Pat6.match("other/more/foo"), true);
|
||||
EXPECT_EQ(Pat6.match("other/more/bar"), true);
|
||||
EXPECT_EQ(Pat6.match("/foo"), true);
|
||||
EXPECT_EQ(Pat6.match("/bar"), true);
|
||||
EXPECT_EQ(Pat6.match("/test/foo"), true);
|
||||
EXPECT_EQ(Pat6.match("/test/bar"), true);
|
||||
EXPECT_EQ(Pat6.match("/other/more/foo"), true);
|
||||
EXPECT_EQ(Pat6.match("/other/more/bar"), true);
|
||||
|
||||
PATDEF(Pat7, "{foo,bar}/**")
|
||||
EXPECT_EQ(Pat7.match("foo"), true);
|
||||
EXPECT_EQ(Pat7.match("bar"), true);
|
||||
EXPECT_EQ(Pat7.match("bar/"), true);
|
||||
EXPECT_EQ(Pat7.match("foo/test"), true);
|
||||
EXPECT_EQ(Pat7.match("bar/test"), true);
|
||||
EXPECT_EQ(Pat7.match("bar/test/"), true);
|
||||
EXPECT_EQ(Pat7.match("foo/other/more"), true);
|
||||
EXPECT_EQ(Pat7.match("bar/other/more"), true);
|
||||
EXPECT_EQ(Pat7.match("bar/other/more/"), true);
|
||||
|
||||
PATDEF(Pat8, "{**/*.d.ts,**/*.js}")
|
||||
EXPECT_EQ(Pat8.match("foo.js"), true);
|
||||
EXPECT_EQ(Pat8.match("testing/foo.js"), true);
|
||||
EXPECT_EQ(Pat8.match("/testing/foo.js"), true);
|
||||
EXPECT_EQ(Pat8.match("foo.d.ts"), true);
|
||||
EXPECT_EQ(Pat8.match("testing/foo.d.ts"), true);
|
||||
EXPECT_EQ(Pat8.match("/testing/foo.d.ts"), true);
|
||||
EXPECT_EQ(Pat8.match("foo.d"), false);
|
||||
EXPECT_EQ(Pat8.match("testing/foo.d"), false);
|
||||
EXPECT_EQ(Pat8.match("/testing/foo.d"), false);
|
||||
|
||||
PATDEF(Pat9, "{**/*.d.ts,**/*.js,path/simple.jgs}")
|
||||
EXPECT_EQ(Pat9.match("foo.js"), true);
|
||||
EXPECT_EQ(Pat9.match("testing/foo.js"), true);
|
||||
EXPECT_EQ(Pat9.match("/testing/foo.js"), true);
|
||||
EXPECT_EQ(Pat9.match("path/simple.jgs"), true);
|
||||
EXPECT_EQ(Pat9.match("/path/simple.jgs"), false);
|
||||
|
||||
PATDEF(Pat10, "{**/*.d.ts,**/*.js,foo.[0-9]}")
|
||||
EXPECT_EQ(Pat10.match("foo.5"), true);
|
||||
EXPECT_EQ(Pat10.match("foo.8"), true);
|
||||
EXPECT_EQ(Pat10.match("bar.5"), false);
|
||||
EXPECT_EQ(Pat10.match("foo.f"), false);
|
||||
EXPECT_EQ(Pat10.match("foo.js"), true);
|
||||
|
||||
PATDEF(Pat11, "prefix/{**/*.d.ts,**/*.js,foo.[0-9]}")
|
||||
EXPECT_EQ(Pat11.match("prefix/foo.5"), true);
|
||||
EXPECT_EQ(Pat11.match("prefix/foo.8"), true);
|
||||
EXPECT_EQ(Pat11.match("prefix/bar.5"), false);
|
||||
EXPECT_EQ(Pat11.match("prefix/foo.f"), false);
|
||||
EXPECT_EQ(Pat11.match("prefix/foo.js"), true);
|
||||
}
|
||||
|
||||
TEST(GlobPattern, WildGlob) {
|
||||
PATDEF(Pat1, "**/*")
|
||||
EXPECT_EQ(Pat1.match("foo"), true);
|
||||
EXPECT_EQ(Pat1.match("foo/bar/baz"), true);
|
||||
|
||||
PATDEF(Pat2, "**/[0-9]*")
|
||||
EXPECT_EQ(Pat2.match("114514foo"), true);
|
||||
EXPECT_EQ(Pat2.match("foo/bar/baz/xxx/yyy/zzz"), false);
|
||||
EXPECT_EQ(Pat2.match("foo/bar/baz/xxx/yyy/zzz114514"), false);
|
||||
EXPECT_EQ(Pat2.match("foo/bar/baz/xxx/yyy/114514"), true);
|
||||
EXPECT_EQ(Pat2.match("foo/bar/baz/xxx/yyy/114514zzz"), true);
|
||||
|
||||
PATDEF(Pat3, "**/*[0-9]")
|
||||
EXPECT_EQ(Pat3.match("foo5"), true);
|
||||
EXPECT_EQ(Pat3.match("foo/bar/baz/xxx/yyy/zzz"), false);
|
||||
EXPECT_EQ(Pat3.match("foo/bar/baz/xxx/yyy/zzz114514"), true);
|
||||
|
||||
PATDEF(Pat4, "**/include/test/*.{cc,hh,c,h,cpp,hpp}")
|
||||
EXPECT_EQ(Pat4.match("include/test/aaa.cc"), true);
|
||||
EXPECT_EQ(Pat4.match("/include/test/aaa.cc"), true);
|
||||
EXPECT_EQ(Pat4.match("xxx/yyy/include/test/aaa.cc"), true);
|
||||
EXPECT_EQ(Pat4.match("include/foo/bar/baz/include/test/bbb.hh"), true);
|
||||
EXPECT_EQ(Pat4.match("include/include/include/include/include/test/bbb.hpp"), true);
|
||||
|
||||
PATDEF(Pat5, "**include/test/*.{cc,hh,c,h,cpp,hpp}")
|
||||
EXPECT_EQ(Pat5.match("include/test/fff.hpp"), true);
|
||||
EXPECT_EQ(Pat5.match("xxx-yyy-include/test/fff.hpp"), true);
|
||||
EXPECT_EQ(Pat5.match("xxx-yyy-include/test/.hpp"), true);
|
||||
EXPECT_EQ(Pat5.match("/include/test/aaa.cc"), true);
|
||||
EXPECT_EQ(Pat5.match("include/foo/bar/baz/include/test/bbb.hh"), true);
|
||||
|
||||
PATDEF(Pat6, "**/*foo.{c,cpp}")
|
||||
EXPECT_EQ(Pat6.match("bar/foo.cpp"), true);
|
||||
EXPECT_EQ(Pat6.match("bar/barfoo.cpp"), true);
|
||||
EXPECT_EQ(Pat6.match("/foofoo.cpp"), true);
|
||||
EXPECT_EQ(Pat6.match("foo/foo/foo/foo/foofoo.cpp"), true);
|
||||
EXPECT_EQ(Pat6.match("foofoo.cpp"), true);
|
||||
EXPECT_EQ(Pat6.match("barfoo.cpp"), true);
|
||||
EXPECT_EQ(Pat6.match("foo.cpp"), true);
|
||||
|
||||
// Boundary test of `**`
|
||||
PATDEF(Pat7, "**")
|
||||
EXPECT_EQ(Pat7.match("foo"), true);
|
||||
EXPECT_EQ(Pat7.match("foo/bar/baz"), true);
|
||||
|
||||
PATDEF(Pat8, "x/**")
|
||||
EXPECT_EQ(Pat8.match("x/"), true);
|
||||
EXPECT_EQ(Pat8.match("x/foo/bar/baz"), true);
|
||||
EXPECT_EQ(Pat8.match("x"), true);
|
||||
|
||||
PATDEF(Pat9, "**/x")
|
||||
EXPECT_EQ(Pat9.match("x"), true);
|
||||
EXPECT_EQ(Pat9.match("/x"), true);
|
||||
EXPECT_EQ(Pat9.match("/x/x/x/x/x"), true);
|
||||
|
||||
PATDEF(Pat10, "**/*")
|
||||
EXPECT_EQ(Pat10.match("foo"), true);
|
||||
EXPECT_EQ(Pat10.match("foo/bar"), true);
|
||||
EXPECT_EQ(Pat10.match("foo/bar/baz"), true);
|
||||
|
||||
PATDEF(Pat11, "**/*.{cc,cpp}")
|
||||
EXPECT_EQ(Pat11.match("foo/bar/baz.cc"), true);
|
||||
EXPECT_EQ(Pat11.match("foo/foo/foo.cpp"), true);
|
||||
EXPECT_EQ(Pat11.match("foo/bar/.cc"), true);
|
||||
|
||||
PATDEF(Pat12, "**/*?.{cc,cpp}")
|
||||
EXPECT_EQ(Pat12.match("foo/bar/baz/xxx/yyy/zzz/aaa.cc"), true);
|
||||
EXPECT_EQ(Pat12.match("foo/bar/baz/xxx/yyy/zzz/a.cc"), true);
|
||||
EXPECT_EQ(Pat12.match("foo/bar/baz/xxx/yyy/zzz/.cc"), false);
|
||||
|
||||
PATDEF(Pat13, "**/?*.{cc,cpp}")
|
||||
EXPECT_EQ(Pat13.match("foo/bar/baz/xxx/yyy/zzz/aaa.cc"), true);
|
||||
EXPECT_EQ(Pat13.match("foo/bar/baz/xxx/yyy/zzz/a.cc"), true);
|
||||
EXPECT_EQ(Pat13.match("foo/bar/baz/xxx/yyy/zzz/.cc"), false);
|
||||
|
||||
PATDEF(Pat14, "**/*[0-9]")
|
||||
EXPECT_EQ(Pat14.match("foo5"), true);
|
||||
EXPECT_EQ(Pat14.match("foo/bar/baz/xxx/yyy/zzz"), false);
|
||||
EXPECT_EQ(Pat14.match("foo/bar/baz/xxx/yyy/zzz114514"), true);
|
||||
|
||||
PATDEF(Pat15, "**/*")
|
||||
EXPECT_EQ(Pat15.match("foo"), true);
|
||||
EXPECT_EQ(Pat15.match("foo/bar/baz"), true);
|
||||
|
||||
PATDEF(Pat16, "**/*.js")
|
||||
EXPECT_EQ(Pat16.match("foo.js"), true);
|
||||
EXPECT_EQ(Pat16.match("/foo.js"), true);
|
||||
EXPECT_EQ(Pat16.match("folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat16.match("/node_modules/foo.js"), true);
|
||||
EXPECT_EQ(Pat16.match("foo.jss"), false);
|
||||
EXPECT_EQ(Pat16.match("some.js/test"), false);
|
||||
EXPECT_EQ(Pat16.match("/some.js/test"), false);
|
||||
|
||||
PATDEF(Pat17, "**/project.json")
|
||||
EXPECT_EQ(Pat17.match("project.json"), true);
|
||||
EXPECT_EQ(Pat17.match("/project.json"), true);
|
||||
EXPECT_EQ(Pat17.match("some/folder/project.json"), true);
|
||||
EXPECT_EQ(Pat17.match("/some/folder/project.json"), true);
|
||||
EXPECT_EQ(Pat17.match("some/folder/file_project.json"), false);
|
||||
EXPECT_EQ(Pat17.match("some/folder/fileproject.json"), false);
|
||||
EXPECT_EQ(Pat17.match("some/rrproject.json"), false);
|
||||
|
||||
PATDEF(Pat18, "test/**")
|
||||
EXPECT_EQ(Pat18.match("test"), true);
|
||||
EXPECT_EQ(Pat18.match("test/foo"), true);
|
||||
EXPECT_EQ(Pat18.match("test/foo/"), true);
|
||||
EXPECT_EQ(Pat18.match("test/foo.js"), true);
|
||||
EXPECT_EQ(Pat18.match("test/other/foo.js"), true);
|
||||
EXPECT_EQ(Pat18.match("est/other/foo.js"), false);
|
||||
|
||||
PATDEF(Pat19, "**")
|
||||
EXPECT_EQ(Pat19.match("/"), true);
|
||||
EXPECT_EQ(Pat19.match("foo.js"), true);
|
||||
EXPECT_EQ(Pat19.match("folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat19.match("folder/foo/"), true);
|
||||
EXPECT_EQ(Pat19.match("/node_modules/foo.js"), true);
|
||||
EXPECT_EQ(Pat19.match("foo.jss"), true);
|
||||
EXPECT_EQ(Pat19.match("some.js/test"), true);
|
||||
|
||||
PATDEF(Pat20, "test/**/*.js")
|
||||
EXPECT_EQ(Pat20.match("test/foo.js"), true);
|
||||
EXPECT_EQ(Pat20.match("test/other/foo.js"), true);
|
||||
EXPECT_EQ(Pat20.match("test/other/more/foo.js"), true);
|
||||
EXPECT_EQ(Pat20.match("test/foo.ts"), false);
|
||||
EXPECT_EQ(Pat20.match("test/other/foo.ts"), false);
|
||||
EXPECT_EQ(Pat20.match("test/other/more/foo.ts"), false);
|
||||
|
||||
PATDEF(Pat21, "**/**/*.js")
|
||||
EXPECT_EQ(Pat21.match("foo.js"), true);
|
||||
EXPECT_EQ(Pat21.match("/foo.js"), true);
|
||||
EXPECT_EQ(Pat21.match("folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat21.match("/node_modules/foo.js"), true);
|
||||
EXPECT_EQ(Pat21.match("foo.jss"), false);
|
||||
EXPECT_EQ(Pat21.match("some.js/test"), false);
|
||||
|
||||
PATDEF(Pat22, "**/node_modules/**/*.js")
|
||||
EXPECT_EQ(Pat22.match("foo.js"), false);
|
||||
EXPECT_EQ(Pat22.match("folder/foo.js"), false);
|
||||
EXPECT_EQ(Pat22.match("node_modules/foo.js"), true);
|
||||
EXPECT_EQ(Pat22.match("/node_modules/foo.js"), true);
|
||||
EXPECT_EQ(Pat22.match("node_modules/some/folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat22.match("/node_modules/some/folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat22.match("node_modules/some/folder/foo.ts"), false);
|
||||
EXPECT_EQ(Pat22.match("foo.jss"), false);
|
||||
EXPECT_EQ(Pat22.match("some.js/test"), false);
|
||||
|
||||
PATDEF(Pat23, "{**/node_modules/**,**/.git/**,**/bower_components/**}")
|
||||
EXPECT_EQ(Pat23.match("node_modules"), true);
|
||||
EXPECT_EQ(Pat23.match("/node_modules"), true);
|
||||
EXPECT_EQ(Pat23.match("/node_modules/more"), true);
|
||||
EXPECT_EQ(Pat23.match("some/test/node_modules"), true);
|
||||
EXPECT_EQ(Pat23.match("/some/test/node_modules"), true);
|
||||
EXPECT_EQ(Pat23.match("bower_components"), true);
|
||||
EXPECT_EQ(Pat23.match("bower_components/more"), true);
|
||||
EXPECT_EQ(Pat23.match("/bower_components"), true);
|
||||
EXPECT_EQ(Pat23.match("some/test/bower_components"), true);
|
||||
EXPECT_EQ(Pat23.match("/some/test/bower_components"), true);
|
||||
EXPECT_EQ(Pat23.match(".git"), true);
|
||||
EXPECT_EQ(Pat23.match("/.git"), true);
|
||||
EXPECT_EQ(Pat23.match("some/test/.git"), true);
|
||||
EXPECT_EQ(Pat23.match("/some/test/.git"), true);
|
||||
EXPECT_EQ(Pat23.match("tempting"), false);
|
||||
EXPECT_EQ(Pat23.match("/tempting"), false);
|
||||
EXPECT_EQ(Pat23.match("some/test/tempting"), false);
|
||||
EXPECT_EQ(Pat23.match("/some/test/tempting"), false);
|
||||
|
||||
PATDEF(Pat24, "{**/package.json,**/project.json}")
|
||||
EXPECT_EQ(Pat24.match("package.json"), true);
|
||||
EXPECT_EQ(Pat24.match("/package.json"), true);
|
||||
EXPECT_EQ(Pat24.match("xpackage.json"), false);
|
||||
EXPECT_EQ(Pat24.match("/xpackage.json"), false);
|
||||
|
||||
PATDEF(Pat25, "some/**/*.js")
|
||||
EXPECT_EQ(Pat25.match("some/foo.js"), true);
|
||||
EXPECT_EQ(Pat25.match("some/folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat25.match("something/foo.js"), false);
|
||||
EXPECT_EQ(Pat25.match("something/folder/foo.js"), false);
|
||||
|
||||
PATDEF(Pat26, "some/**/*")
|
||||
EXPECT_EQ(Pat26.match("some/foo.js"), true);
|
||||
EXPECT_EQ(Pat26.match("some/folder/foo.js"), true);
|
||||
EXPECT_EQ(Pat26.match("something/foo.js"), false);
|
||||
EXPECT_EQ(Pat26.match("something/folder/foo.js"), false);
|
||||
}
|
||||
|
||||
} // namespace clice::testing
|
||||
Reference in New Issue
Block a user