• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Secret Labs' Regular Expression Engine
3#
4# convert re-style regular expression to sre pattern
5#
6# Copyright (c) 1998-2001 by Secret Labs AB.  All rights reserved.
7#
8# See the sre.py file for information on usage and redistribution.
9#
10
11"""Internal support module for sre"""
12
13# XXX: show string offset and offending character for all errors
14
15import sys
16
17from sre_constants import *
18
19SPECIAL_CHARS = ".\\[{()*+?^$|"
20REPEAT_CHARS = "*+?{"
21
22DIGITS = set("0123456789")
23
24OCTDIGITS = set("01234567")
25HEXDIGITS = set("0123456789abcdefABCDEF")
26ASCIILETTERS = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
27
28WHITESPACE = set(" \t\n\r\v\f")
29
30ESCAPES = {
31    r"\a": (LITERAL, ord("\a")),
32    r"\b": (LITERAL, ord("\b")),
33    r"\f": (LITERAL, ord("\f")),
34    r"\n": (LITERAL, ord("\n")),
35    r"\r": (LITERAL, ord("\r")),
36    r"\t": (LITERAL, ord("\t")),
37    r"\v": (LITERAL, ord("\v")),
38    r"\\": (LITERAL, ord("\\"))
39}
40
41CATEGORIES = {
42    r"\A": (AT, AT_BEGINNING_STRING), # start of string
43    r"\b": (AT, AT_BOUNDARY),
44    r"\B": (AT, AT_NON_BOUNDARY),
45    r"\d": (IN, [(CATEGORY, CATEGORY_DIGIT)]),
46    r"\D": (IN, [(CATEGORY, CATEGORY_NOT_DIGIT)]),
47    r"\s": (IN, [(CATEGORY, CATEGORY_SPACE)]),
48    r"\S": (IN, [(CATEGORY, CATEGORY_NOT_SPACE)]),
49    r"\w": (IN, [(CATEGORY, CATEGORY_WORD)]),
50    r"\W": (IN, [(CATEGORY, CATEGORY_NOT_WORD)]),
51    r"\Z": (AT, AT_END_STRING), # end of string
52}
53
54FLAGS = {
55    # standard flags
56    "i": SRE_FLAG_IGNORECASE,
57    "L": SRE_FLAG_LOCALE,
58    "m": SRE_FLAG_MULTILINE,
59    "s": SRE_FLAG_DOTALL,
60    "x": SRE_FLAG_VERBOSE,
61    # extensions
62    "t": SRE_FLAG_TEMPLATE,
63    "u": SRE_FLAG_UNICODE,
64}
65
66class Pattern:
67    # master pattern object.  keeps track of global attributes
68    def __init__(self):
69        self.flags = 0
70        self.open = []
71        self.groups = 1
72        self.groupdict = {}
73        self.lookbehind = 0
74
75    def opengroup(self, name=None):
76        gid = self.groups
77        self.groups = gid + 1
78        if name is not None:
79            ogid = self.groupdict.get(name, None)
80            if ogid is not None:
81                raise error, ("redefinition of group name %s as group %d; "
82                              "was group %d" % (repr(name), gid,  ogid))
83            self.groupdict[name] = gid
84        self.open.append(gid)
85        return gid
86    def closegroup(self, gid):
87        self.open.remove(gid)
88    def checkgroup(self, gid):
89        return gid < self.groups and gid not in self.open
90
91class SubPattern:
92    # a subpattern, in intermediate form
93    def __init__(self, pattern, data=None):
94        self.pattern = pattern
95        if data is None:
96            data = []
97        self.data = data
98        self.width = None
99    def dump(self, level=0):
100        seqtypes = (tuple, list)
101        for op, av in self.data:
102            print level*"  " + op,
103            if op == IN:
104                # member sublanguage
105                print
106                for op, a in av:
107                    print (level+1)*"  " + op, a
108            elif op == BRANCH:
109                print
110                for i, a in enumerate(av[1]):
111                    if i:
112                        print level*"  " + "or"
113                    a.dump(level+1)
114            elif op == GROUPREF_EXISTS:
115                condgroup, item_yes, item_no = av
116                print condgroup
117                item_yes.dump(level+1)
118                if item_no:
119                    print level*"  " + "else"
120                    item_no.dump(level+1)
121            elif isinstance(av, seqtypes):
122                nl = 0
123                for a in av:
124                    if isinstance(a, SubPattern):
125                        if not nl:
126                            print
127                        a.dump(level+1)
128                        nl = 1
129                    else:
130                        print a,
131                        nl = 0
132                if not nl:
133                    print
134            else:
135                print av
136    def __repr__(self):
137        return repr(self.data)
138    def __len__(self):
139        return len(self.data)
140    def __delitem__(self, index):
141        del self.data[index]
142    def __getitem__(self, index):
143        if isinstance(index, slice):
144            return SubPattern(self.pattern, self.data[index])
145        return self.data[index]
146    def __setitem__(self, index, code):
147        self.data[index] = code
148    def insert(self, index, code):
149        self.data.insert(index, code)
150    def append(self, code):
151        self.data.append(code)
152    def getwidth(self):
153        # determine the width (min, max) for this subpattern
154        if self.width:
155            return self.width
156        lo = hi = 0
157        UNITCODES = (ANY, RANGE, IN, LITERAL, NOT_LITERAL, CATEGORY)
158        REPEATCODES = (MIN_REPEAT, MAX_REPEAT)
159        for op, av in self.data:
160            if op is BRANCH:
161                i = MAXREPEAT - 1
162                j = 0
163                for av in av[1]:
164                    l, h = av.getwidth()
165                    i = min(i, l)
166                    j = max(j, h)
167                lo = lo + i
168                hi = hi + j
169            elif op is CALL:
170                i, j = av.getwidth()
171                lo = lo + i
172                hi = hi + j
173            elif op is SUBPATTERN:
174                i, j = av[1].getwidth()
175                lo = lo + i
176                hi = hi + j
177            elif op in REPEATCODES:
178                i, j = av[2].getwidth()
179                lo = lo + i * av[0]
180                hi = hi + j * av[1]
181            elif op in UNITCODES:
182                lo = lo + 1
183                hi = hi + 1
184            elif op == SUCCESS:
185                break
186        self.width = min(lo, MAXREPEAT - 1), min(hi, MAXREPEAT)
187        return self.width
188
189class Tokenizer:
190    def __init__(self, string):
191        self.string = string
192        self.index = 0
193        self.__next()
194    def __next(self):
195        if self.index >= len(self.string):
196            self.next = None
197            return
198        char = self.string[self.index]
199        if char[0] == "\\":
200            try:
201                c = self.string[self.index + 1]
202            except IndexError:
203                raise error, "bogus escape (end of line)"
204            char = char + c
205        self.index = self.index + len(char)
206        self.next = char
207    def match(self, char, skip=1):
208        if char == self.next:
209            if skip:
210                self.__next()
211            return 1
212        return 0
213    def get(self):
214        this = self.next
215        self.__next()
216        return this
217    def tell(self):
218        return self.index, self.next
219    def seek(self, index):
220        self.index, self.next = index
221
222def isident(char):
223    return "a" <= char <= "z" or "A" <= char <= "Z" or char == "_"
224
225def isdigit(char):
226    return "0" <= char <= "9"
227
228def isname(name):
229    # check that group name is a valid string
230    if not isident(name[0]):
231        return False
232    for char in name[1:]:
233        if not isident(char) and not isdigit(char):
234            return False
235    return True
236
237def _class_escape(source, escape, nested):
238    # handle escape code inside character class
239    code = ESCAPES.get(escape)
240    if code:
241        return code
242    code = CATEGORIES.get(escape)
243    if code and code[0] == IN:
244        return code
245    try:
246        c = escape[1:2]
247        if c == "x":
248            # hexadecimal escape (exactly two digits)
249            while source.next in HEXDIGITS and len(escape) < 4:
250                escape = escape + source.get()
251            escape = escape[2:]
252            if len(escape) != 2:
253                raise error, "bogus escape: %s" % repr("\\" + escape)
254            return LITERAL, int(escape, 16) & 0xff
255        elif c in OCTDIGITS:
256            # octal escape (up to three digits)
257            while source.next in OCTDIGITS and len(escape) < 4:
258                escape = escape + source.get()
259            escape = escape[1:]
260            return LITERAL, int(escape, 8) & 0xff
261        elif c in DIGITS:
262            raise error, "bogus escape: %s" % repr(escape)
263        if len(escape) == 2:
264            if sys.py3kwarning and c in ASCIILETTERS:
265                import warnings
266                if c in 'Uu':
267                    warnings.warn('bad escape %s; Unicode escapes are '
268                                  'supported only since Python 3.3' % escape,
269                                  FutureWarning, stacklevel=nested + 6)
270                else:
271                    warnings.warnpy3k('bad escape %s' % escape,
272                                      DeprecationWarning, stacklevel=nested + 6)
273            return LITERAL, ord(escape[1])
274    except ValueError:
275        pass
276    raise error, "bogus escape: %s" % repr(escape)
277
278def _escape(source, escape, state, nested):
279    # handle escape code in expression
280    code = CATEGORIES.get(escape)
281    if code:
282        return code
283    code = ESCAPES.get(escape)
284    if code:
285        return code
286    try:
287        c = escape[1:2]
288        if c == "x":
289            # hexadecimal escape
290            while source.next in HEXDIGITS and len(escape) < 4:
291                escape = escape + source.get()
292            if len(escape) != 4:
293                raise ValueError
294            return LITERAL, int(escape[2:], 16) & 0xff
295        elif c == "0":
296            # octal escape
297            while source.next in OCTDIGITS and len(escape) < 4:
298                escape = escape + source.get()
299            return LITERAL, int(escape[1:], 8) & 0xff
300        elif c in DIGITS:
301            # octal escape *or* decimal group reference (sigh)
302            if source.next in DIGITS:
303                escape = escape + source.get()
304                if (escape[1] in OCTDIGITS and escape[2] in OCTDIGITS and
305                    source.next in OCTDIGITS):
306                    # got three octal digits; this is an octal escape
307                    escape = escape + source.get()
308                    return LITERAL, int(escape[1:], 8) & 0xff
309            # not an octal escape, so this is a group reference
310            group = int(escape[1:])
311            if group < state.groups:
312                if not state.checkgroup(group):
313                    raise error, "cannot refer to open group"
314                if state.lookbehind:
315                    import warnings
316                    warnings.warn('group references in lookbehind '
317                                  'assertions are not supported',
318                                  RuntimeWarning, stacklevel=nested + 6)
319                return GROUPREF, group
320            raise ValueError
321        if len(escape) == 2:
322            if sys.py3kwarning and c in ASCIILETTERS:
323                import warnings
324                if c in 'Uu':
325                    warnings.warn('bad escape %s; Unicode escapes are '
326                                  'supported only since Python 3.3' % escape,
327                                  FutureWarning, stacklevel=nested + 6)
328                else:
329                    warnings.warnpy3k('bad escape %s' % escape,
330                                      DeprecationWarning, stacklevel=nested + 6)
331            return LITERAL, ord(escape[1])
332    except ValueError:
333        pass
334    raise error, "bogus escape: %s" % repr(escape)
335
336def _parse_sub(source, state, nested):
337    # parse an alternation: a|b|c
338
339    items = []
340    itemsappend = items.append
341    sourcematch = source.match
342    while 1:
343        itemsappend(_parse(source, state, nested + 1))
344        if sourcematch("|"):
345            continue
346        if not nested:
347            break
348        if not source.next or sourcematch(")", 0):
349            break
350        else:
351            raise error, "pattern not properly closed"
352
353    if len(items) == 1:
354        return items[0]
355
356    subpattern = SubPattern(state)
357    subpatternappend = subpattern.append
358
359    # check if all items share a common prefix
360    while 1:
361        prefix = None
362        for item in items:
363            if not item:
364                break
365            if prefix is None:
366                prefix = item[0]
367            elif item[0] != prefix:
368                break
369        else:
370            # all subitems start with a common "prefix".
371            # move it out of the branch
372            for item in items:
373                del item[0]
374            subpatternappend(prefix)
375            continue # check next one
376        break
377
378    # check if the branch can be replaced by a character set
379    for item in items:
380        if len(item) != 1 or item[0][0] != LITERAL:
381            break
382    else:
383        # we can store this as a character set instead of a
384        # branch (the compiler may optimize this even more)
385        set = []
386        setappend = set.append
387        for item in items:
388            setappend(item[0])
389        subpatternappend((IN, set))
390        return subpattern
391
392    subpattern.append((BRANCH, (None, items)))
393    return subpattern
394
395def _parse_sub_cond(source, state, condgroup, nested):
396    item_yes = _parse(source, state, nested + 1)
397    if source.match("|"):
398        item_no = _parse(source, state, nested + 1)
399        if source.match("|"):
400            raise error, "conditional backref with more than two branches"
401    else:
402        item_no = None
403    if source.next and not source.match(")", 0):
404        raise error, "pattern not properly closed"
405    subpattern = SubPattern(state)
406    subpattern.append((GROUPREF_EXISTS, (condgroup, item_yes, item_no)))
407    return subpattern
408
409_PATTERNENDERS = set("|)")
410_ASSERTCHARS = set("=!<")
411_LOOKBEHINDASSERTCHARS = set("=!")
412_REPEATCODES = set([MIN_REPEAT, MAX_REPEAT])
413
414def _parse(source, state, nested):
415    # parse a simple pattern
416    subpattern = SubPattern(state)
417
418    # precompute constants into local variables
419    subpatternappend = subpattern.append
420    sourceget = source.get
421    sourcematch = source.match
422    _len = len
423    PATTERNENDERS = _PATTERNENDERS
424    ASSERTCHARS = _ASSERTCHARS
425    LOOKBEHINDASSERTCHARS = _LOOKBEHINDASSERTCHARS
426    REPEATCODES = _REPEATCODES
427
428    while 1:
429
430        if source.next in PATTERNENDERS:
431            break # end of subpattern
432        this = sourceget()
433        if this is None:
434            break # end of pattern
435
436        if state.flags & SRE_FLAG_VERBOSE:
437            # skip whitespace and comments
438            if this in WHITESPACE:
439                continue
440            if this == "#":
441                while 1:
442                    this = sourceget()
443                    if this in (None, "\n"):
444                        break
445                continue
446
447        if this and this[0] not in SPECIAL_CHARS:
448            subpatternappend((LITERAL, ord(this)))
449
450        elif this == "[":
451            # character set
452            set = []
453            setappend = set.append
454##          if sourcematch(":"):
455##              pass # handle character classes
456            if sourcematch("^"):
457                setappend((NEGATE, None))
458            # check remaining characters
459            start = set[:]
460            while 1:
461                this = sourceget()
462                if this == "]" and set != start:
463                    break
464                elif this and this[0] == "\\":
465                    code1 = _class_escape(source, this, nested + 1)
466                elif this:
467                    code1 = LITERAL, ord(this)
468                else:
469                    raise error, "unexpected end of regular expression"
470                if sourcematch("-"):
471                    # potential range
472                    this = sourceget()
473                    if this == "]":
474                        if code1[0] is IN:
475                            code1 = code1[1][0]
476                        setappend(code1)
477                        setappend((LITERAL, ord("-")))
478                        break
479                    elif this:
480                        if this[0] == "\\":
481                            code2 = _class_escape(source, this, nested + 1)
482                        else:
483                            code2 = LITERAL, ord(this)
484                        if code1[0] != LITERAL or code2[0] != LITERAL:
485                            raise error, "bad character range"
486                        lo = code1[1]
487                        hi = code2[1]
488                        if hi < lo:
489                            raise error, "bad character range"
490                        setappend((RANGE, (lo, hi)))
491                    else:
492                        raise error, "unexpected end of regular expression"
493                else:
494                    if code1[0] is IN:
495                        code1 = code1[1][0]
496                    setappend(code1)
497
498            # XXX: <fl> should move set optimization to compiler!
499            if _len(set)==1 and set[0][0] is LITERAL:
500                subpatternappend(set[0]) # optimization
501            elif _len(set)==2 and set[0][0] is NEGATE and set[1][0] is LITERAL:
502                subpatternappend((NOT_LITERAL, set[1][1])) # optimization
503            else:
504                # XXX: <fl> should add charmap optimization here
505                subpatternappend((IN, set))
506
507        elif this and this[0] in REPEAT_CHARS:
508            # repeat previous item
509            if this == "?":
510                min, max = 0, 1
511            elif this == "*":
512                min, max = 0, MAXREPEAT
513
514            elif this == "+":
515                min, max = 1, MAXREPEAT
516            elif this == "{":
517                if source.next == "}":
518                    subpatternappend((LITERAL, ord(this)))
519                    continue
520                here = source.tell()
521                min, max = 0, MAXREPEAT
522                lo = hi = ""
523                while source.next in DIGITS:
524                    lo = lo + source.get()
525                if sourcematch(","):
526                    while source.next in DIGITS:
527                        hi = hi + sourceget()
528                else:
529                    hi = lo
530                if not sourcematch("}"):
531                    subpatternappend((LITERAL, ord(this)))
532                    source.seek(here)
533                    continue
534                if lo:
535                    min = int(lo)
536                    if min >= MAXREPEAT:
537                        raise OverflowError("the repetition number is too large")
538                if hi:
539                    max = int(hi)
540                    if max >= MAXREPEAT:
541                        raise OverflowError("the repetition number is too large")
542                    if max < min:
543                        raise error("bad repeat interval")
544            else:
545                raise error, "not supported"
546            # figure out which item to repeat
547            if subpattern:
548                item = subpattern[-1:]
549            else:
550                item = None
551            if not item or (_len(item) == 1 and item[0][0] == AT):
552                raise error, "nothing to repeat"
553            if item[0][0] in REPEATCODES:
554                raise error, "multiple repeat"
555            if sourcematch("?"):
556                subpattern[-1] = (MIN_REPEAT, (min, max, item))
557            else:
558                subpattern[-1] = (MAX_REPEAT, (min, max, item))
559
560        elif this == ".":
561            subpatternappend((ANY, None))
562
563        elif this == "(":
564            group = 1
565            name = None
566            condgroup = None
567            if sourcematch("?"):
568                group = 0
569                # options
570                if sourcematch("P"):
571                    # python extensions
572                    if sourcematch("<"):
573                        # named group: skip forward to end of name
574                        name = ""
575                        while 1:
576                            char = sourceget()
577                            if char is None:
578                                raise error, "unterminated name"
579                            if char == ">":
580                                break
581                            name = name + char
582                        group = 1
583                        if not name:
584                            raise error("missing group name")
585                        if not isname(name):
586                            raise error("bad character in group name %r" %
587                                        name)
588                    elif sourcematch("="):
589                        # named backreference
590                        name = ""
591                        while 1:
592                            char = sourceget()
593                            if char is None:
594                                raise error, "unterminated name"
595                            if char == ")":
596                                break
597                            name = name + char
598                        if not name:
599                            raise error("missing group name")
600                        if not isname(name):
601                            raise error("bad character in backref group name "
602                                        "%r" % name)
603                        gid = state.groupdict.get(name)
604                        if gid is None:
605                            msg = "unknown group name: {0!r}".format(name)
606                            raise error(msg)
607                        if state.lookbehind:
608                            import warnings
609                            warnings.warn('group references in lookbehind '
610                                          'assertions are not supported',
611                                          RuntimeWarning, stacklevel=nested + 6)
612                        subpatternappend((GROUPREF, gid))
613                        continue
614                    else:
615                        char = sourceget()
616                        if char is None:
617                            raise error, "unexpected end of pattern"
618                        raise error, "unknown specifier: ?P%s" % char
619                elif sourcematch(":"):
620                    # non-capturing group
621                    group = 2
622                elif sourcematch("#"):
623                    # comment
624                    while 1:
625                        if source.next is None or source.next == ")":
626                            break
627                        sourceget()
628                    if not sourcematch(")"):
629                        raise error, "unbalanced parenthesis"
630                    continue
631                elif source.next in ASSERTCHARS:
632                    # lookahead assertions
633                    char = sourceget()
634                    dir = 1
635                    if char == "<":
636                        if source.next not in LOOKBEHINDASSERTCHARS:
637                            raise error, "syntax error"
638                        dir = -1 # lookbehind
639                        char = sourceget()
640                        state.lookbehind += 1
641                    p = _parse_sub(source, state, nested + 1)
642                    if dir < 0:
643                        state.lookbehind -= 1
644                    if not sourcematch(")"):
645                        raise error, "unbalanced parenthesis"
646                    if char == "=":
647                        subpatternappend((ASSERT, (dir, p)))
648                    else:
649                        subpatternappend((ASSERT_NOT, (dir, p)))
650                    continue
651                elif sourcematch("("):
652                    # conditional backreference group
653                    condname = ""
654                    while 1:
655                        char = sourceget()
656                        if char is None:
657                            raise error, "unterminated name"
658                        if char == ")":
659                            break
660                        condname = condname + char
661                    group = 2
662                    if not condname:
663                        raise error("missing group name")
664                    if isname(condname):
665                        condgroup = state.groupdict.get(condname)
666                        if condgroup is None:
667                            msg = "unknown group name: {0!r}".format(condname)
668                            raise error(msg)
669                    else:
670                        try:
671                            condgroup = int(condname)
672                        except ValueError:
673                            raise error, "bad character in group name"
674                    if state.lookbehind:
675                        import warnings
676                        warnings.warn('group references in lookbehind '
677                                      'assertions are not supported',
678                                      RuntimeWarning, stacklevel=nested + 6)
679                else:
680                    # flags
681                    if not source.next in FLAGS:
682                        raise error, "unexpected end of pattern"
683                    while source.next in FLAGS:
684                        state.flags = state.flags | FLAGS[sourceget()]
685            if group:
686                # parse group contents
687                if group == 2:
688                    # anonymous group
689                    group = None
690                else:
691                    group = state.opengroup(name)
692                if condgroup:
693                    p = _parse_sub_cond(source, state, condgroup, nested + 1)
694                else:
695                    p = _parse_sub(source, state, nested + 1)
696                if not sourcematch(")"):
697                    raise error, "unbalanced parenthesis"
698                if group is not None:
699                    state.closegroup(group)
700                subpatternappend((SUBPATTERN, (group, p)))
701            else:
702                while 1:
703                    char = sourceget()
704                    if char is None:
705                        raise error, "unexpected end of pattern"
706                    if char == ")":
707                        break
708                    raise error, "unknown extension"
709
710        elif this == "^":
711            subpatternappend((AT, AT_BEGINNING))
712
713        elif this == "$":
714            subpattern.append((AT, AT_END))
715
716        elif this and this[0] == "\\":
717            code = _escape(source, this, state, nested + 1)
718            subpatternappend(code)
719
720        else:
721            raise error, "parser error"
722
723    return subpattern
724
725def parse(str, flags=0, pattern=None):
726    # parse 're' pattern into list of (opcode, argument) tuples
727
728    source = Tokenizer(str)
729
730    if pattern is None:
731        pattern = Pattern()
732    pattern.flags = flags
733    pattern.str = str
734
735    p = _parse_sub(source, pattern, 0)
736    if (sys.py3kwarning and
737        (p.pattern.flags & SRE_FLAG_LOCALE) and
738        (p.pattern.flags & SRE_FLAG_UNICODE)):
739        import warnings
740        warnings.warnpy3k("LOCALE and UNICODE flags are incompatible",
741                          DeprecationWarning, stacklevel=5)
742
743    tail = source.get()
744    if tail == ")":
745        raise error, "unbalanced parenthesis"
746    elif tail:
747        raise error, "bogus characters at end of regular expression"
748
749    if not (flags & SRE_FLAG_VERBOSE) and p.pattern.flags & SRE_FLAG_VERBOSE:
750        # the VERBOSE flag was switched on inside the pattern.  to be
751        # on the safe side, we'll parse the whole thing again...
752        return parse(str, p.pattern.flags)
753
754    if flags & SRE_FLAG_DEBUG:
755        p.dump()
756
757    return p
758
759def parse_template(source, pattern):
760    # parse 're' replacement string into list of literals and
761    # group references
762    s = Tokenizer(source)
763    sget = s.get
764    p = []
765    a = p.append
766    def literal(literal, p=p, pappend=a):
767        if p and p[-1][0] is LITERAL:
768            p[-1] = LITERAL, p[-1][1] + literal
769        else:
770            pappend((LITERAL, literal))
771    sep = source[:0]
772    if type(sep) is type(""):
773        makechar = chr
774    else:
775        makechar = unichr
776    while 1:
777        this = sget()
778        if this is None:
779            break # end of replacement string
780        if this and this[0] == "\\":
781            # group
782            c = this[1:2]
783            if c == "g":
784                name = ""
785                if s.match("<"):
786                    while 1:
787                        char = sget()
788                        if char is None:
789                            raise error, "unterminated group name"
790                        if char == ">":
791                            break
792                        name = name + char
793                if not name:
794                    raise error, "missing group name"
795                try:
796                    index = int(name)
797                    if index < 0:
798                        raise error, "negative group number"
799                except ValueError:
800                    if not isname(name):
801                        raise error, "bad character in group name"
802                    try:
803                        index = pattern.groupindex[name]
804                    except KeyError:
805                        msg = "unknown group name: {0!r}".format(name)
806                        raise IndexError(msg)
807                a((MARK, index))
808            elif c == "0":
809                if s.next in OCTDIGITS:
810                    this = this + sget()
811                    if s.next in OCTDIGITS:
812                        this = this + sget()
813                literal(makechar(int(this[1:], 8) & 0xff))
814            elif c in DIGITS:
815                isoctal = False
816                if s.next in DIGITS:
817                    this = this + sget()
818                    if (c in OCTDIGITS and this[2] in OCTDIGITS and
819                        s.next in OCTDIGITS):
820                        this = this + sget()
821                        isoctal = True
822                        literal(makechar(int(this[1:], 8) & 0xff))
823                if not isoctal:
824                    a((MARK, int(this[1:])))
825            else:
826                try:
827                    this = makechar(ESCAPES[this][1])
828                except KeyError:
829                    if sys.py3kwarning and c in ASCIILETTERS:
830                        import warnings
831                        warnings.warnpy3k('bad escape %s' % this,
832                                          DeprecationWarning, stacklevel=4)
833                literal(this)
834        else:
835            literal(this)
836    # convert template to groups and literals lists
837    i = 0
838    groups = []
839    groupsappend = groups.append
840    literals = [None] * len(p)
841    for c, s in p:
842        if c is MARK:
843            groupsappend((i, s))
844            # literal[i] is already None
845        else:
846            literals[i] = s
847        i = i + 1
848    return groups, literals
849
850def expand_template(template, match):
851    g = match.group
852    sep = match.string[:0]
853    groups, literals = template
854    literals = literals[:]
855    try:
856        for index, group in groups:
857            literals[index] = s = g(group)
858            if s is None:
859                raise error, "unmatched group"
860    except IndexError:
861        raise error, "invalid group reference"
862    return sep.join(literals)
863