add backtracking irc glob matching

This commit is contained in:
jesopo 2020-04-27 23:26:41 +01:00
parent 0921cb8086
commit ab66df4d43
2 changed files with 46 additions and 2 deletions

View file

@ -18,3 +18,37 @@ def _collapse(pattern: str) -> str:
i += 1 i += 1
return out return out
def _match(pattern: str, s: str):
i, j = 0, 0
i_backup = -1
j_backup = -1
while j < len(s):
p = (pattern[i:] or [None])[0]
if p == "*":
i += 1
i_backup = i
j_backup = j
elif p in ["?", s[j]]:
i += 1
j += 1
else:
if i_backup == -1:
return False
else:
j_backup += 1
j = j_backup
i = i_backup
return i == len(pattern)
class Glob(object):
def __init__(self, pattern: str):
self._pattern = pattern
def match(self, s: str) -> bool:
return _match(self._pattern, s)
def compile(pattern: str):
return Glob(_collapse(pattern))

View file

@ -1,7 +1,8 @@
from re import compile from re import compile as re_compile
from typing import Optional, Pattern from typing import Optional, Pattern
from irctokens import Hostmask from irctokens import Hostmask
from ..interface import IMatchResponseParam, IMatchResponseHostmask, IServer from ..interface import IMatchResponseParam, IMatchResponseHostmask, IServer
from ..glob import Glob, compile as glob_compile
from .. import formatting from .. import formatting
class Any(IMatchResponseParam): class Any(IMatchResponseParam):
@ -52,7 +53,7 @@ class Regex(IMatchResponseParam):
self._pattern: Optional[Pattern] = None self._pattern: Optional[Pattern] = None
def match(self, server: IServer, arg: str) -> bool: def match(self, server: IServer, arg: str) -> bool:
if self._pattern is None: if self._pattern is None:
self._pattern = compile(self._value) self._pattern = re_compile(self._value)
return bool(self._pattern.search(arg)) return bool(self._pattern.search(arg))
class Nickname(IMatchResponseHostmask): class Nickname(IMatchResponseHostmask):
@ -67,3 +68,12 @@ class Nickname(IMatchResponseHostmask):
if self._folded is None: if self._folded is None:
self._folded = server.casefold(self._nickname) self._folded = server.casefold(self._nickname)
return self._folded == server.casefold(hostmask.nickname) return self._folded == server.casefold(hostmask.nickname)
class Mask(IMatchResponseHostmask):
def __init__(self, mask: str):
self._mask = mask
self._compiled = Optional[Glob]
def match(self, server: IServer, hostmask: Hostmask):
if self._compiled is None:
self._compiled = glob_compile(self._mask)
return self._compiled.match(str(hostmask))