from __future__ import with_statement from operator import itemgetter class NoMatch(Exception): pass class FinishedBlock(Exception): pass class structural_matching(object): def __init__(self, value): self._value = value def __enter__(self): return lambda pattern: _Matcher(pattern, self._value) def __exit__(self, exception, value, traceback): if exception == FinishedBlock: return True class _Matcher(object): def __init__(self, pattern, value): self._pattern = pattern self._pattern_pieces = pattern.split() self._value = value def _parse_first(self): p = self._pattern_pieces[0] if '(' in p: type_name = p[0:p.index('(')] return lambda x: x.__class__.__name__ == type_name elif p[0] == '"' and p[-1] == '"': return lambda x: x == p[1:-1] return None def _parse_rest(self): rest_getters = [] for i, p in enumerate(self._pattern_pieces[1:]): if p.startswith('.'): rest_getters.append(itemgetter(p[1:])) 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): if exception == NoMatch: return True raise FinishedBlock() # testing this with structural_matching((1, 2, 3)) as match: # won't work.. PEP-0377 which was rejected would have saved me. # still useful as with match('tuple() x _ z') as (x, z), but # simplification would be better. with match('list() x y z') as (x, y, z): print x, y, z with match('tuple() x _ z') as (x, z): print "tuple case" print x, z