from __future__ import with_statement from operator import itemgetter class InvalidPattern(Exception): pass class NoMatch(Exception): pass class match(object): def __init__(self, pattern, value): self._pattern = pattern self._pattern_pieces = pattern.split() self._pattern_first = self._pattern_pieces[0] self._pattern_rest = None self._value = value def _parse_first(self): p = self._pattern_pieces[0] if '(' in p: type_name = p[0:p.index('(')] self._pattern_rest = self._pattern_pieces[1:] print "type_name = ", type_name return lambda x: x.__class__.__name__ == type_name elif p[0] == '[' and p[-1] == ']': if len(self._pattern_pieces) > 1: raise InvalidPattern("[] patterns can only be one piece") self._pattern_rest = self._pattern_pieces[:] return lambda x: hasattr(x, '__getslice__') else: self._pattern_rest = self._pattern_pieces[:] return lambda x: True def _parse_rest(self): def make_attr(p): def getter(value): return getattr(value, p) return getter def make_slicer(start, end): def getter(value): if start.isdigit() and end.isdigit(): _start, _end = int(start), int(end) return value[_start:_end] elif start.isdigit() and not end: _start = int(start) return (value[_start], value[_start:]) elif end.isdigit() and not start: _end = int(end) return (value[:_end], x[_end]) else: raise InvalidPattern("don't know how to do [%s:%s]" % \ (start, end)) return getter rest_getters = [] for i, p in enumerate(self._pattern_rest): if p.startswith('.'): # it's an attribute rest_getters.append(make_attr(p[1:])) elif p.startswith('[') and p.endswith(']') and ':' in p: start, end = p[1:-1].split(':', 1) rest_getters.append(make_slicer(start, end)) elif p == '_': continue else: rest_getters.append(itemgetter(i)) return rest_getters def _match(self): func = self._parse_first() if func and func(self._value): return self._match_rest() raise NoMatch() def _match_rest(self): try: funcs = self._parse_rest() return [f(self._value) for f in funcs] except: raise NoMatch() def __enter__(self): return self._match() def __exit__(self, exception, value, traceback): pass # tests with match('[1:3]', [1, 2, 3, 4]) as (a,): print a with match('[1:]', "hello world") as (a,): print a with match('str() x y', 'hello world') as (h, e): print 'h = ', h print 'e = ', e with match('x y z', [1, 2, 3]) as (x, y, z,): print z, y, x class obj(object): def __init__(self, x, y): self.x = x self.y = y with match('obj() .x .y', obj('x-ity', 'y-ity')) as (x, y): print 'x = ', x, ',', print 'y = ', y with match('x y _', [1, 2, 3]) as (x, y): print x, y # [2, 3] # ('e', 'ello world') # h = h # e = e # 3 2 1 # x = y-ity , y = y-ity # 1 2