Skip to content

Commit

Permalink
karm-io: Simple glob pattern matcher.
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepy-monax committed Apr 5, 2024
1 parent da0b2cb commit 85feed2
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/libs/karm-base/rune.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ usize transcodeLen(Cursor<typename Source::Unit> input) {

while (input.rem()) {
Rune r;
if (Source::decode(r, input)) {
if (Source::decodeUnit(r, input)) {
result += 1;
}
}
Expand Down
100 changes: 100 additions & 0 deletions src/libs/karm-io/glob.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include "glob.h"

namespace Karm::Io {

bool _matchWildCard(SScan &glob, SScan &in);

bool _matchGroupe(SScan &glob, Rune curr) {
bool neg = glob.skip('^');

while (!glob.ended() &&
!glob.skip(']')) {
Rune rs = glob.next();
Rune re = rs;

if (glob.peek() == '-' && glob.peek(1) != ']') {
re = glob.peek(1);
glob.next(2);
}

if (re < rs)
std::swap(rs, re);

if (rs <= curr && curr <= re) {
while (!glob.ended() &&
!glob.skip(']')) {
glob.next();
}

return !neg;
}
}

return neg;
}

bool _matchWildCard(SScan &glob, SScan &in) {
for (usize n = in.rem(); n > 0; n--) {
SScan g = glob, s = in;
s.next(n);
if (matchGlob(g, s)) {
// We found a match, commit the changes
glob = g, in = s;
return true;
}
}

return matchGlob(glob, in);
}

bool matchGlob(SScan &glob, SScan &in) {
while (!glob.ended() && !in.ended()) {
auto op = glob.next();

switch (op) {
case '?':
if (in.ended())
return false;
in.next();
break;

case '[':
if (!_matchGroupe(glob, in.next()))
return false;
break;

case '*':
if (!_matchWildCard(glob, in))
return false;
break;

case '\\':
if (in.ended())
return false;

if (op != in.next())
return false;

break;

default:
if (in.ended())
return false;

if (op != in.next())
return false;

break;
}
}

return glob.ended() && in.ended();
}

bool matchGlob(Str glob, Str str) {
SScan globScan(glob);
SScan strScan(str);
return matchGlob(globScan, strScan);
}

} // namespace Karm::Io
21 changes: 21 additions & 0 deletions src/libs/karm-io/glob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <karm-io/sscan.h>

namespace Karm::Io {

// Match a glob pattern against a string.
// The glob pattern can contain the following special characters:
// - '?' matches any single character.
// - '*' matches any sequence of characters.
// - '[' matches any character in the group.
// - '[^' matches any character not in the group.
// - ']' closes the group.
// - '-' specifies a range in the group.
// - '^' negates the group.
bool matchGlob(SScan &glob, SScan &in);

// Match a string against a glob pattern.
bool matchGlob(Str glob, Str str);

} // namespace Karm::Io
51 changes: 51 additions & 0 deletions src/libs/karm-io/tests/test-glob.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <karm-io/glob.h>
#include <karm-test/macros.h>

namespace Karm::Io::Tests {

test$(globMatch) {
expect$(matchGlob("", ""));
expect$(matchGlob("a", "a"));
expect$(not matchGlob("a", ""));
expect$(not matchGlob("", "a"));
expect$(matchGlob("abcABC123", "abcABC123"));
expect$(not matchGlob("ABCabc123", "abcABC123"));
expect$(matchGlob("?", "a"));
expect$(matchGlob("?", "b"));
expect$(matchGlob("?", "1"));
expect$(matchGlob("?", "8"));
expect$(not matchGlob("?", ""));
expect$(matchGlob("abcABC*", "abcABC123"));
expect$(matchGlob("abc*123", "abcABC123"));
expect$(matchGlob("abc*123", "abc123"));
expect$(matchGlob("*ABC123", "abcABC123"));
expect$(not matchGlob("abcABC*", "ABCabc123"));
expect$(not matchGlob("abc*123", "ABCabc123"));
expect$(not matchGlob("abc*123", "abcABCXYZ"));
expect$(not matchGlob("*ABC123", "ABCabc123"));
expect$(matchGlob("[abc]", "a"));
expect$(matchGlob("[abc]", "b"));
expect$(matchGlob("[abc]", "c"));
expect$(not matchGlob("[abc]", "1"));
expect$(not matchGlob("[abc]", "2"));
expect$(not matchGlob("[abc]", "3"));
expect$(matchGlob("[a-z]", "a"));
expect$(matchGlob("[a-z]", "z"));
expect$(not matchGlob("[a-z]", "1"));
expect$(not matchGlob("[a-z]", "9"));
expect$(not matchGlob("[^a-z]", "a"));
expect$(not matchGlob("[^a-z]", "z"));
expect$(matchGlob("[^a-z]", "1"));
expect$(matchGlob("[^a-z]", "9"));
expect$(matchGlob("[a-z0-9]", "a"));
expect$(matchGlob("[a-z0-9]", "k"));
expect$(matchGlob("[a-z0-9]", "1"));
expect$(matchGlob("[a-z0-9]", "7"));
expect$(matchGlob("[a-z0-9]", "a"));
expect$(not matchGlob("[a-z0-9]", "A"));
expect$(not matchGlob("[a-z0-9]", "K"));

return Ok();
}

} // namespace Karm::Io::Tests

0 comments on commit 85feed2

Please sign in to comment.