sre_compile.py 15.9 KB
Newer Older
1 2 3 4 5
#
# Secret Labs' Regular Expression Engine
#
# convert template to internal format
#
Fredrik Lundh's avatar
Fredrik Lundh committed
6
# Copyright (c) 1997-2001 by Secret Labs AB.  All rights reserved.
7
#
8
# See the sre.py file for information on usage and redistribution.
9 10
#

11 12
"""Internal support module for sre"""

13
import _sre, sys
Christian Heimes's avatar
Christian Heimes committed
14
import sre_parse
15 16
from sre_constants import *

17 18
assert _sre.MAGIC == MAGIC, "SRE module mismatch"

19 20 21
if _sre.CODESIZE == 2:
    MAXCODE = 65535
else:
22
    MAXCODE = 0xFFFFFFFF
23

24 25 26
def _identityfunction(x):
    return x

27 28 29 30 31
_LITERAL_CODES = set([LITERAL, NOT_LITERAL])
_REPEATING_CODES = set([REPEAT, MIN_REPEAT, MAX_REPEAT])
_SUCCESS_CODES = set([SUCCESS, FAILURE])
_ASSERT_CODES = set([ASSERT, ASSERT_NOT])

Fredrik Lundh's avatar
Fredrik Lundh committed
32
def _compile(code, pattern, flags):
33
    # internal: compile a (sub)pattern
Fredrik Lundh's avatar
Fredrik Lundh committed
34
    emit = code.append
35
    _len = len
36 37 38 39
    LITERAL_CODES = _LITERAL_CODES
    REPEATING_CODES = _REPEATING_CODES
    SUCCESS_CODES = _SUCCESS_CODES
    ASSERT_CODES = _ASSERT_CODES
40
    for op, av in pattern:
41
        if op in LITERAL_CODES:
42 43
            if flags & SRE_FLAG_IGNORECASE:
                emit(OPCODES[OP_IGNORE[op]])
44
                emit(_sre.getlower(av, flags))
45 46
            else:
                emit(OPCODES[op])
47
                emit(av)
48 49 50 51
        elif op is IN:
            if flags & SRE_FLAG_IGNORECASE:
                emit(OPCODES[OP_IGNORE[op]])
                def fixup(literal, flags=flags):
52
                    return _sre.getlower(literal, flags)
53 54
            else:
                emit(OPCODES[op])
55 56
                fixup = _identityfunction
            skip = _len(code); emit(0)
57
            _compile_charset(av, flags, code, fixup)
58
            code[skip] = _len(code) - skip
59 60
        elif op is ANY:
            if flags & SRE_FLAG_DOTALL:
Fredrik Lundh's avatar
Fredrik Lundh committed
61
                emit(OPCODES[ANY_ALL])
62
            else:
Fredrik Lundh's avatar
Fredrik Lundh committed
63
                emit(OPCODES[ANY])
64
        elif op in REPEATING_CODES:
65
            if flags & SRE_FLAG_TEMPLATE:
66
                raise error("internal: unsupported template operator")
67
                emit(OPCODES[REPEAT])
68
                skip = _len(code); emit(0)
69 70 71 72
                emit(av[0])
                emit(av[1])
                _compile(code, av[2], flags)
                emit(OPCODES[SUCCESS])
73 74 75
                code[skip] = _len(code) - skip
            elif _simple(av) and op is not REPEAT:
                if op is MAX_REPEAT:
76 77 78
                    emit(OPCODES[REPEAT_ONE])
                else:
                    emit(OPCODES[MIN_REPEAT_ONE])
79
                skip = _len(code); emit(0)
Fredrik Lundh's avatar
Fredrik Lundh committed
80 81 82 83
                emit(av[0])
                emit(av[1])
                _compile(code, av[2], flags)
                emit(OPCODES[SUCCESS])
84
                code[skip] = _len(code) - skip
85
            else:
Fredrik Lundh's avatar
Fredrik Lundh committed
86
                emit(OPCODES[REPEAT])
87
                skip = _len(code); emit(0)
Fredrik Lundh's avatar
Fredrik Lundh committed
88 89 90
                emit(av[0])
                emit(av[1])
                _compile(code, av[2], flags)
91 92
                code[skip] = _len(code) - skip
                if op is MAX_REPEAT:
Fredrik Lundh's avatar
Fredrik Lundh committed
93
                    emit(OPCODES[MAX_UNTIL])
94
                else:
Fredrik Lundh's avatar
Fredrik Lundh committed
95
                    emit(OPCODES[MIN_UNTIL])
96
        elif op is SUBPATTERN:
97
            if av[0]:
98
                emit(OPCODES[MARK])
99
                emit((av[0]-1)*2)
100
            # _compile_info(code, av[1], flags)
101
            _compile(code, av[1], flags)
102
            if av[0]:
103
                emit(OPCODES[MARK])
104
                emit((av[0]-1)*2+1)
105
        elif op in SUCCESS_CODES:
106
            emit(OPCODES[op])
107
        elif op in ASSERT_CODES:
108
            emit(OPCODES[op])
109
            skip = _len(code); emit(0)
110 111 112 113 114
            if av[0] >= 0:
                emit(0) # look ahead
            else:
                lo, hi = av[1].getwidth()
                if lo != hi:
115
                    raise error("look-behind requires fixed-width pattern")
116 117 118
                emit(lo) # look behind
            _compile(code, av[1], flags)
            emit(OPCODES[SUCCESS])
119
            code[skip] = _len(code) - skip
120
        elif op is CALL:
121
            emit(OPCODES[op])
122
            skip = _len(code); emit(0)
123 124
            _compile(code, av, flags)
            emit(OPCODES[SUCCESS])
125
            code[skip] = _len(code) - skip
126 127 128
        elif op is AT:
            emit(OPCODES[op])
            if flags & SRE_FLAG_MULTILINE:
Fredrik Lundh's avatar
Fredrik Lundh committed
129 130 131 132 133 134
                av = AT_MULTILINE.get(av, av)
            if flags & SRE_FLAG_LOCALE:
                av = AT_LOCALE.get(av, av)
            elif flags & SRE_FLAG_UNICODE:
                av = AT_UNICODE.get(av, av)
            emit(ATCODES[av])
135
        elif op is BRANCH:
136
            emit(OPCODES[op])
137
            tail = []
138
            tailappend = tail.append
139
            for av in av[1]:
140
                skip = _len(code); emit(0)
141
                # _compile_info(code, av, flags)
142 143
                _compile(code, av, flags)
                emit(OPCODES[JUMP])
144 145
                tailappend(_len(code)); emit(0)
                code[skip] = _len(code) - skip
146 147
            emit(0) # end of branch
            for tail in tail:
148
                code[tail] = _len(code) - tail
149 150 151
        elif op is CATEGORY:
            emit(OPCODES[op])
            if flags & SRE_FLAG_LOCALE:
Fredrik Lundh's avatar
Fredrik Lundh committed
152
                av = CH_LOCALE[av]
153
            elif flags & SRE_FLAG_UNICODE:
Fredrik Lundh's avatar
Fredrik Lundh committed
154 155
                av = CH_UNICODE[av]
            emit(CHCODES[av])
156
        elif op is GROUPREF:
157 158 159 160 161
            if flags & SRE_FLAG_IGNORECASE:
                emit(OPCODES[OP_IGNORE[op]])
            else:
                emit(OPCODES[op])
            emit(av-1)
162 163
        elif op is GROUPREF_EXISTS:
            emit(OPCODES[op])
164
            emit(av[0]-1)
165
            skipyes = _len(code); emit(0)
166 167 168
            _compile(code, av[1], flags)
            if av[2]:
                emit(OPCODES[JUMP])
169 170
                skipno = _len(code); emit(0)
                code[skipyes] = _len(code) - skipyes + 1
171
                _compile(code, av[2], flags)
172
                code[skipno] = _len(code) - skipno
173
            else:
174
                code[skipyes] = _len(code) - skipyes + 1
175
        else:
176
            raise ValueError("unsupported operand type", op)
177

178 179 180
def _compile_charset(charset, flags, code, fixup=None):
    # compile charset subprogram
    emit = code.append
181
    if fixup is None:
182
        fixup = _identityfunction
183 184 185 186 187 188 189 190 191 192 193
    for op, av in _optimize_charset(charset, fixup):
        emit(OPCODES[op])
        if op is NEGATE:
            pass
        elif op is LITERAL:
            emit(fixup(av))
        elif op is RANGE:
            emit(fixup(av[0]))
            emit(fixup(av[1]))
        elif op is CHARSET:
            code.extend(av)
194 195
        elif op is BIGCHARSET:
            code.extend(av)
196 197 198 199 200 201 202 203
        elif op is CATEGORY:
            if flags & SRE_FLAG_LOCALE:
                emit(CHCODES[CH_LOCALE[av]])
            elif flags & SRE_FLAG_UNICODE:
                emit(CHCODES[CH_UNICODE[av]])
            else:
                emit(CHCODES[av])
        else:
204
            raise error("internal: unsupported set operator")
205 206 207 208 209
    emit(OPCODES[FAILURE])

def _optimize_charset(charset, fixup):
    # internal: optimize character set
    out = []
210
    outappend = out.append
211
    charmap = [0]*256
212 213 214
    try:
        for op, av in charset:
            if op is NEGATE:
215
                outappend((op, av))
216
            elif op is LITERAL:
217
                charmap[fixup(av)] = 1
218 219
            elif op is RANGE:
                for i in range(fixup(av[0]), fixup(av[1])+1):
220
                    charmap[i] = 1
221
            elif op is CATEGORY:
Fredrik Lundh's avatar
Fredrik Lundh committed
222
                # XXX: could append to charmap tail
223 224 225
                return charset # cannot compress
    except IndexError:
        # character set contains unicode characters
226
        return _optimize_unicode(charset, fixup)
227 228 229
    # compress character map
    i = p = n = 0
    runs = []
230
    runsappend = runs.append
231 232 233 234 235 236
    for c in charmap:
        if c:
            if n == 0:
                p = i
            n = n + 1
        elif n:
237
            runsappend((p, n))
238 239 240
            n = 0
        i = i + 1
    if n:
241
        runsappend((p, n))
242 243 244 245
    if len(runs) <= 2:
        # use literal/range
        for p, n in runs:
            if n == 1:
246
                outappend((LITERAL, p))
247
            else:
248
                outappend((RANGE, (p, p+n-1)))
249 250 251 252
        if len(out) < len(charset):
            return out
    else:
        # use bitmap
253
        data = _mk_bitmap(charmap)
254
        outappend((CHARSET, data))
255 256 257
        return out
    return charset

258 259
def _mk_bitmap(bits):
    data = []
260
    dataappend = data.append
261 262 263
    if _sre.CODESIZE == 2:
        start = (1, 0)
    else:
264
        start = (1, 0)
265
    m, v = start
266 267 268
    for c in bits:
        if c:
            v = v + m
269
        m = m + m
270
        if m > MAXCODE:
271
            dataappend(v)
272
            m, v = start
273 274 275 276
    return data

# To represent a big charset, first a bitmap of all characters in the
# set is constructed. Then, this bitmap is sliced into chunks of 256
277
# characters, duplicate chunks are eliminated, and each chunk is
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
# given a number. In the compiled expression, the charset is
# represented by a 16-bit word sequence, consisting of one word for
# the number of different chunks, a sequence of 256 bytes (128 words)
# of chunk numbers indexed by their original chunk position, and a
# sequence of chunks (16 words each).

# Compression is normally good: in a typical charset, large ranges of
# Unicode will be either completely excluded (e.g. if only cyrillic
# letters are to be matched), or completely included (e.g. if large
# subranges of Kanji match). These ranges will be represented by
# chunks of all one-bits or all zero-bits.

# Matching can be also done efficiently: the more significant byte of
# the Unicode character is an index into the chunk number, and the
# less significant byte is a bit index in the chunk (just like the
# CHARSET matching).

295 296 297 298 299 300
# In UCS-4 mode, the BIGCHARSET opcode still supports only subsets
# of the basic multilingual plane; an efficient representation
# for all of UTF-16 has not yet been developed. This means,
# in particular, that negated charsets cannot be represented as
# bigcharsets.

301
def _optimize_unicode(charset, fixup):
302 303 304 305
    try:
        import array
    except ImportError:
        return charset
306
    charmap = [0]*65536
307
    negate = 0
308 309 310 311 312
    try:
        for op, av in charset:
            if op is NEGATE:
                negate = 1
            elif op is LITERAL:
313
                charmap[fixup(av)] = 1
314
            elif op is RANGE:
315
                for i in range(fixup(av[0]), fixup(av[1])+1):
316
                    charmap[i] = 1
317 318 319 320 321 322
            elif op is CATEGORY:
                # XXX: could expand category
                return charset # cannot compress
    except IndexError:
        # non-BMP characters
        return charset
323
    if negate:
324 325 326
        if sys.maxunicode != 65535:
            # XXX: negation does not work with big charsets
            return charset
327
        for i in range(65536):
328 329 330 331 332
            charmap[i] = not charmap[i]
    comps = {}
    mapping = [0]*256
    block = 0
    data = []
333
    for i in range(256):
334 335 336 337
        chunk = tuple(charmap[i*256:(i+1)*256])
        new = comps.setdefault(chunk, block)
        mapping[i] = new
        if new == block:
338 339
            block = block + 1
            data = data + _mk_bitmap(chunk)
340
    header = [block]
341
    if _sre.CODESIZE == 2:
342 343
        code = 'H'
    else:
344
        code = 'I'
345
    # Convert block indices to byte array of 256 bytes
346
    mapping = array.array('b', mapping).tobytes()
347
    # Convert byte array to word array
348 349
    mapping = array.array(code, mapping)
    assert mapping.itemsize == _sre.CODESIZE
350
    assert len(mapping) * mapping.itemsize == 256
351
    header = header + mapping.tolist()
352
    data[0:0] = header
353
    return [(BIGCHARSET, data)]
354

355 356 357
def _simple(av):
    # check if av is a "simple" operator
    lo, hi = av[2].getwidth()
358
    if lo == 0 and hi == MAXREPEAT:
359
        raise error("nothing to repeat")
360 361
    return lo == hi == 1 and av[2][0][0] != SUBPATTERN

362 363
def _compile_info(code, pattern, flags):
    # internal: compile an info block.  in the current version,
364 365
    # this contains min/max pattern width, and an optional literal
    # prefix or a character map
366 367
    lo, hi = pattern.getwidth()
    if lo == 0:
368
        return # not worth it
369 370
    # look for a literal prefix
    prefix = []
371
    prefixappend = prefix.append
372
    prefix_skip = 0
373
    charset = [] # not used
374
    charsetappend = charset.append
375
    if not (flags & SRE_FLAG_IGNORECASE):
376
        # look for literal prefix
377 378
        for op, av in pattern.data:
            if op is LITERAL:
379 380
                if len(prefix) == prefix_skip:
                    prefix_skip = prefix_skip + 1
381
                prefixappend(av)
382 383 384
            elif op is SUBPATTERN and len(av[1]) == 1:
                op, av = av[1][0]
                if op is LITERAL:
385
                    prefixappend(av)
386 387
                else:
                    break
388 389
            else:
                break
390 391 392 393 394 395
        # if no prefix, look for charset prefix
        if not prefix and pattern.data:
            op, av = pattern.data[0]
            if op is SUBPATTERN and av[1]:
                op, av = av[1][0]
                if op is LITERAL:
396
                    charsetappend((op, av))
397 398
                elif op is BRANCH:
                    c = []
399
                    cappend = c.append
400 401 402 403 404
                    for p in av[1]:
                        if not p:
                            break
                        op, av = p[0]
                        if op is LITERAL:
405
                            cappend((op, av))
406 407 408 409 410 411
                        else:
                            break
                    else:
                        charset = c
            elif op is BRANCH:
                c = []
412
                cappend = c.append
413 414 415 416 417
                for p in av[1]:
                    if not p:
                        break
                    op, av = p[0]
                    if op is LITERAL:
418
                        cappend((op, av))
419 420 421 422 423 424 425 426 427 428
                    else:
                        break
                else:
                    charset = c
            elif op is IN:
                charset = av
##     if prefix:
##         print "*** PREFIX", prefix, prefix_skip
##     if charset:
##         print "*** CHARSET", charset
429 430 431 432 433 434
    # add an info block
    emit = code.append
    emit(OPCODES[INFO])
    skip = len(code); emit(0)
    # literal flag
    mask = 0
435 436
    if prefix:
        mask = SRE_INFO_PREFIX
437
        if len(prefix) == prefix_skip == len(pattern.data):
438 439 440
            mask = mask + SRE_INFO_LITERAL
    elif charset:
        mask = mask + SRE_INFO_CHARSET
441 442
    emit(mask)
    # pattern length
443 444 445 446 447 448
    if lo < MAXCODE:
        emit(lo)
    else:
        emit(MAXCODE)
        prefix = prefix[:MAXCODE]
    if hi < MAXCODE:
449
        emit(hi)
450
    else:
451
        emit(0)
452 453
    # add literal prefix
    if prefix:
454 455 456 457 458
        emit(len(prefix)) # length
        emit(prefix_skip) # skip
        code.extend(prefix)
        # generate overlap table
        table = [-1] + ([0]*len(prefix))
459
        for i in range(len(prefix)):
460 461 462 463
            table[i+1] = table[i]+1
            while table[i+1] > 0 and prefix[i] != prefix[table[i+1]-1]:
                table[i+1] = table[table[i+1]-1]+1
        code.extend(table[1:]) # don't store first entry
464
    elif charset:
465
        _compile_charset(charset, flags, code)
466 467
    code[skip] = len(code) - skip

468
def isstring(obj):
469
    return isinstance(obj, (str, bytes))
470

471
def _code(p, flags):
472

473
    flags = p.pattern.flags | flags
Fredrik Lundh's avatar
Fredrik Lundh committed
474
    code = []
475 476 477 478 479

    # compile info block
    _compile_info(code, p, flags)

    # compile the pattern
480
    _compile(code, p.data, flags)
481

482
    code.append(OPCODES[SUCCESS])
483

484 485 486 487 488
    return code

def compile(p, flags=0):
    # internal: convert pattern list to internal format

489
    if isstring(p):
490 491 492 493 494
        pattern = p
        p = sre_parse.parse(p, flags)
    else:
        pattern = None

495
    code = _code(p, flags)
496

497 498
    # print code

Fredrik Lundh's avatar
Fredrik Lundh committed
499
    # XXX: <fl> get rid of this limitation!
500 501 502 503
    if p.pattern.groups > 100:
        raise AssertionError(
            "sorry, but this version only supports 100 named groups"
            )
504

505 506 507 508 509 510
    # map in either direction
    groupindex = p.pattern.groupdict
    indexgroup = [None] * p.pattern.groups
    for k, i in groupindex.items():
        indexgroup[i] = k

511
    return _sre.compile(
512
        pattern, flags | p.pattern.flags, code,
513 514
        p.pattern.groups-1,
        groupindex, indexgroup
515
        )