0001"""
0002Iterator based sre token scanner
0003"""
0004import sre_parse, sre_compile, sre_constants
0005from sre_constants import BRANCH, SUBPATTERN
0006from re import VERBOSE, MULTILINE, DOTALL
0007import re
0008
0009__all__ = ['Scanner', 'pattern']
0010
0011FLAGS = (VERBOSE | MULTILINE | DOTALL)
0012class Scanner(object):
0013    def __init__(self, lexicon, flags=FLAGS):
0014        self.actions = [None]
0015        # combine phrases into a compound pattern
0016        s = sre_parse.Pattern()
0017        s.flags = flags
0018        p = []
0019        for idx, token in enumerate(lexicon):
0020            phrase = token.pattern
0021            try:
0022                subpattern = sre_parse.SubPattern(s,
0023                    [(SUBPATTERN, (idx + 1, sre_parse.parse(phrase, flags)))])
0024            except sre_constants.error:
0025                raise
0026            p.append(subpattern)
0027            self.actions.append(token)
0028
0029        s.groups = len(p)+1  # NOTE(guido): Added to make SRE validation work
0030        p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
0031        self.scanner = sre_compile.compile(p)
0032
0033
0034    def iterscan(self, string, idx=0, context=None):
0035        """
0036        Yield match, end_idx for each match
0037        """
0038        match = self.scanner.scanner(string, idx).match
0039        actions = self.actions
0040        lastend = idx
0041        end = len(string)
0042        while True:
0043            m = match()
0044            if m is None:
0045                break
0046            matchbegin, matchend = m.span()
0047            if lastend == matchend:
0048                break
0049            action = actions[m.lastindex]
0050            if action is not None:
0051                rval, next_pos = action(m, context)
0052                if next_pos is not None and next_pos != matchend:
0053                    # "fast forward" the scanner
0054                    matchend = next_pos
0055                    match = self.scanner.scanner(string, matchend).match
0056                yield rval, matchend
0057            lastend = matchend
0058
0059def pattern(pattern, flags=FLAGS):
0060    def decorator(fn):
0061        fn.pattern = pattern
0062        fn.regex = re.compile(pattern, flags)
0063        return fn
0064    return decorator