import collections import itertools import string import sys from voussoirkit import basenumber class Landmark: def __init__(self, opener, closer, parser): self.opener = opener self.closer = closer self.parser = parser def barsplit(chars): wordlist = [] wordbuff = [] def flush(): if not wordbuff: return word = fusk_join(wordbuff) wordlist.append(word) wordbuff.clear() for item in chars: if item == '|': flush() else: wordbuff.append(item) flush() return wordlist def fusk_join(items): form = '' fusks = [] result = [] for item in items: if isinstance(item, str): form += item else: form += '{}' fusks.append(item) product = itertools.product(*fusks) for group in product: f = form.format(*group) result.append(f) return result def fusk_spinner(items): for item in items: if isinstance(item, str): yield item else: yield from item def parse_spinner(characters): words = barsplit(characters) spinner = fusk_spinner(words) return spinner def fusk_range(lo, hi, padto=0, base=10, lower=False): for x in range(lo, hi+1): x = basenumber.to_base(x, base) x = x.rjust(padto, '0') if lower: x = x.lower() yield x def parse_range(characters): r = ''.join(characters) (lo, hi) = r.split('-') lo = lo.strip() hi = hi.strip() lowers = string.digits + string.ascii_lowercase uppers = string.digits + string.ascii_uppercase lohi = lo + hi lower = False if all(c in string.digits for c in lohi): base = 10 elif all(c in lowers for c in lohi): lower = True base = 36 elif all(c in uppers for c in lohi): base = 36 else: base = 62 if (not lo) or (not hi): raise ValueError('Invalid range', r) if len(lo) > 1 and lo.startswith('0'): padto = len(lo) if len(hi) != padto: raise ValueError('Inconsistent padding', lo, hi) else: padto = 0 lo = basenumber.from_base(lo, base) hi = basenumber.from_base(hi, base) frange = fusk_range(lo, hi, padto=padto, base=base, lower=lower) return frange landmarks = { '{': Landmark('{', '}', parse_spinner), '[': Landmark('[', ']', parse_range), } def fusker(fstring, landmark=None, depth=0): escaped = False result = [] buff = [] if isinstance(fstring, str): fstring = collections.deque(fstring) while fstring: character = fstring.popleft() if escaped: buff.append('\\' + character) escaped = False elif character == '\\': escaped = True elif landmark and character == landmark.closer: buff = [landmark.parser(buff)] break elif character in landmarks: subtotal = fusker(fstring, landmark=landmarks[character]) buff.extend(subtotal) else: buff.append(character) if not landmark: buff = parse_spinner(buff) return buff return result if __name__ == '__main__': pattern = sys.argv[1] fusk = fusker(pattern) for result in fusk: print(result)