c part ready to go
This commit is contained in:
137
CxxBuilder/src/FileMatcher.bf
Normal file
137
CxxBuilder/src/FileMatcher.bf
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CxxBuilder;
|
||||
|
||||
static class FileMatcher
|
||||
{
|
||||
public static void HandleMatches(Span<StringView> patterns, StringView directory, delegate void(StringView match) callback)
|
||||
{
|
||||
Runtime.Assert(Directory.Exists(directory), scope $"No such directory {directory}");
|
||||
void Dir(StringView dir)
|
||||
{
|
||||
for (let element in Directory.Enumerate(dir))
|
||||
{
|
||||
let path = element.GetFilePath(..scope .());
|
||||
path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
if (element.IsDirectory)
|
||||
{
|
||||
Dir(path);
|
||||
continue;
|
||||
}
|
||||
let relpath = Path.GetRelativePath(path, directory, ..scope .());
|
||||
for (let pattern in patterns)
|
||||
if (IsMatch(pattern, relpath))
|
||||
{
|
||||
callback(relpath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Dir(directory);
|
||||
}
|
||||
|
||||
public static bool IsMatch(StringView pattern, StringView path)
|
||||
{
|
||||
var pattern, path;
|
||||
|
||||
bool Matches(StringView pattern, char8 c)
|
||||
{
|
||||
if (pattern.IsEmpty) return false;
|
||||
|
||||
switch (pattern[0])
|
||||
{
|
||||
case '*':
|
||||
if (pattern.Length > 1 && pattern[1] == '*')
|
||||
return true;
|
||||
return !Path.IsDirectorySeparatorChar(c);
|
||||
case '?': return true;
|
||||
case '\\':
|
||||
Runtime.Assert(pattern.Length > 1, "Expected escape sequence");
|
||||
return c == pattern[1];
|
||||
case '/': return Path.IsDirectorySeparatorChar(c);
|
||||
case '[':
|
||||
int i = 0;
|
||||
char8 Next()
|
||||
{
|
||||
if (pattern.Length <= ++i)
|
||||
Runtime.FatalError("Character class not closed");
|
||||
return pattern[i];
|
||||
}
|
||||
bool negated = false;
|
||||
char8 current;
|
||||
switch (Next())
|
||||
{
|
||||
case '^', '!': negated = true; fallthrough;
|
||||
case '\\': current = Next();
|
||||
case ']': return false;
|
||||
default: current = _;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (c == current) return !negated;
|
||||
switch (Next())
|
||||
{
|
||||
case ']': return negated;
|
||||
case '-':
|
||||
let end = Next();
|
||||
if (c >= current && c <= end)
|
||||
return !negated;
|
||||
current = Next();
|
||||
case '\\': current = Next();
|
||||
default: current = _;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return c == _;
|
||||
}
|
||||
}
|
||||
|
||||
reduce: while (true)
|
||||
{
|
||||
if (path.IsEmpty) return true;
|
||||
if (pattern.IsEmpty) return false;
|
||||
if (!Matches(pattern, path[0])) return false;
|
||||
switch (pattern[0])
|
||||
{
|
||||
case '*':
|
||||
StringView lazy;
|
||||
if (pattern.Length > 1)
|
||||
{
|
||||
lazy = pattern;
|
||||
lazy.RemoveFromStart(pattern[1] == '*' ? 2 : 1);
|
||||
}
|
||||
else lazy = .();
|
||||
defer { pattern = lazy; }
|
||||
while (true)
|
||||
{
|
||||
if (IsMatch(lazy, path)) continue reduce;
|
||||
if (!Matches(pattern, path[0])) continue reduce;
|
||||
path.RemoveFromStart(1);
|
||||
if (path.IsEmpty) return lazy.IsEmpty;
|
||||
}
|
||||
case '[':
|
||||
bool escaped = false;
|
||||
while (true)
|
||||
{
|
||||
let c = pattern[0];
|
||||
pattern.RemoveFromStart(1);
|
||||
if (c == ']' && !escaped)
|
||||
{
|
||||
path.RemoveFromStart(1);
|
||||
continue reduce;
|
||||
}
|
||||
escaped = c == '\\';
|
||||
}
|
||||
case '\\':
|
||||
pattern.RemoveFromStart(2);
|
||||
path.RemoveFromStart(1);
|
||||
default:
|
||||
pattern.RemoveFromStart(1);
|
||||
path.RemoveFromStart(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user