blob: 3594e36c941004b2f9f94e8e6689331e957d9218 [file] [log] [blame]
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001#!/usr/bin/env python
2# -*- mode: python -*-
3# $Id$
4
5import string
6import reop
7
8error = 're error'
9
10# compilation flags
11
12IGNORECASE = I = 0x01
13
14MULTILINE = M = 0x02
15DOTALL = S = 0x04
16VERBOSE = X = 0x08
17
Guido van Rossuma4f1a781997-07-17 22:38:10 +000018repetition_operators = ['*', '*?', '+', '+?', '?', '??', '{n}', '{n}?',
19 '{n,}', '{n,}?', '{n,m}', '{n,m}?']
Guido van Rossum5ca1b711997-07-10 21:00:31 +000020
21#
22#
23#
24
Guido van Rossum09bcfd61997-07-15 15:38:20 +000025def valid_identifier(id):
26 if len(id) == 0:
27 return 0
Guido van Rossuma4f1a781997-07-17 22:38:10 +000028 if (not reop.syntax_table[id[0]] & reop.word) or \
29 (reop.syntax_table[id[0]] & reop.digit):
Guido van Rossum09bcfd61997-07-15 15:38:20 +000030 return 0
31 for char in id[1:]:
Guido van Rossuma4f1a781997-07-17 22:38:10 +000032 if not reop.syntax_table[char] & reop.word:
Guido van Rossum09bcfd61997-07-15 15:38:20 +000033 return 0
34 return 1
35
36#
37#
38#
39
Guido van Rossum26d80e61997-07-15 18:59:04 +000040_cache = {}
41_MAXCACHE = 20
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +000042
Guido van Rossum9e18ec71997-07-17 22:39:13 +000043def _cachecompile(pattern, flags=0):
Guido van Rossum26d80e61997-07-15 18:59:04 +000044 key = (pattern, flags)
45 try:
46 return _cache[key]
47 except KeyError:
48 pass
49 value = compile(pattern, flags)
50 if len(_cache) >= _MAXCACHE:
51 _cache.clear()
52 _cache[key] = value
53 return value
54
Guido van Rossum5ca1b711997-07-10 21:00:31 +000055def match(pattern, string, flags=0):
Guido van Rossum26d80e61997-07-15 18:59:04 +000056 return _cachecompile(pattern, flags).match(string)
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +000057
Guido van Rossum5ca1b711997-07-10 21:00:31 +000058def search(pattern, string, flags=0):
Guido van Rossum26d80e61997-07-15 18:59:04 +000059 return _cachecompile(pattern, flags).search(string)
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +000060
Guido van Rossum5ca1b711997-07-10 21:00:31 +000061def sub(pattern, repl, string, count=0):
Guido van Rossum9e18ec71997-07-17 22:39:13 +000062 if type(pattern) == type(''):
63 pattern = _cachecompile(pattern)
64 return pattern.sub(repl, string, count)
Guido van Rossum5ca1b711997-07-10 21:00:31 +000065
66def subn(pattern, repl, string, count=0):
Guido van Rossum9e18ec71997-07-17 22:39:13 +000067 if type(pattern) == type(''):
68 pattern = _cachecompile(pattern)
69 return pattern.subn(repl, string, count)
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +000070
Guido van Rossum8a9a4a21997-07-11 20:48:25 +000071def split(pattern, string, maxsplit=0):
Guido van Rossum9e18ec71997-07-17 22:39:13 +000072 if type(pattern) == type(''):
73 pattern = _cachecompile(pattern)
74 return pattern.split(string, maxsplit)
Guido van Rossum5ca1b711997-07-10 21:00:31 +000075
76#
77#
78#
79
Guido van Rossum71fa97c1997-07-18 04:26:03 +000080def _expand(m, repl):
81 results = []
82 index = 0
83 size = len(repl)
84 while index < size:
85 found = string.find(repl, '\\', index)
86 if found < 0:
87 results.append(repl[index:])
88 break
89 if found > index:
90 results.append(repl[index:found])
91 escape_type, value, index = expand_escape(repl, found+1, REPLACEMENT)
92 if escape_type == CHAR:
93 results.append(value)
94 elif escape_type == MEMORY_REFERENCE:
95 r = m.group(value)
96 if r is None:
97 raise error, ('group "' + str(value) + '" did not contribute '
98 'to the match')
99 results.append(m.group(value))
100 else:
101 raise error, "bad escape in replacement"
102 return string.join(results, '')
103
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000104class RegexObject:
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000105 def __init__(self, pattern, flags, code, num_regs, groupindex):
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000106 self.code = code
107 self.num_regs = num_regs
108 self.flags = flags
109 self.pattern = pattern
110 self.groupindex = groupindex
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000111 self.fastmap = build_fastmap(code)
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000112
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000113 if code[0].name == 'bol':
114 self.anchor = 1
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000115
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000116 elif code[0].name == 'begbuf':
117 self.anchor = 2
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000118
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000119 else:
120 self.anchor = 0
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000121
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000122 self.buffer = assemble(code)
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000123 def search(self, string, pos=0):
124 regs = reop.search(self.buffer,
125 self.num_regs,
126 self.flags,
127 self.fastmap.can_be_null,
128 self.fastmap.fastmap(),
129 self.anchor,
130 string,
131 pos)
132 if regs is None:
133 return None
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000134
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000135 return MatchObject(self,
136 string,
137 pos,
138 regs)
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000139
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000140 def match(self, string, pos=0):
141 regs = reop.match(self.buffer,
142 self.num_regs,
143 self.flags,
144 self.fastmap.can_be_null,
145 self.fastmap.fastmap(),
146 self.anchor,
147 string,
148 pos)
149 if regs is None:
150 return None
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000151
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000152 return MatchObject(self,
153 string,
154 pos,
155 regs)
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000156
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000157 def sub(self, repl, string, count=0):
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000158 return self.subn(repl, string, count)[0]
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000159
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000160 def subn(self, repl, source, count=0):
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000161 if count < 0:
162 raise ValueError, "negative substibution count"
163 if count == 0:
164 import sys
165 count = sys.maxint
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000166 if type(repl) == type(''):
167 if '\\' in repl:
168 repl = lambda m, r=repl: _expand(m, r)
169 else:
170 repl = lambda m, r=repl: r
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000171 n = 0 # Number of matches
172 pos = 0 # Where to start searching
173 lastmatch = -1 # End of last match
174 results = [] # Substrings making up the result
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000175 end = len(source)
176 while n < count and pos <= end:
177 m = self.search(source, pos)
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000178 if not m:
179 break
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000180 i, j = m.span(0)
181 if i == j == lastmatch:
182 # Empty match adjacent to previous match
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000183 pos = pos + 1
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000184 results.append(source[lastmatch:pos])
185 continue
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000186 if pos < i:
187 results.append(source[pos:i])
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000188 results.append(repl(m))
189 pos = lastmatch = j
190 if i == j:
191 # Last match was empty; don't try here again
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000192 pos = pos + 1
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000193 results.append(source[lastmatch:pos])
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000194 n = n + 1
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000195 results.append(source[pos:])
196 return (string.join(results, ''), n)
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000197
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000198 def split(self, source, maxsplit=0):
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000199 if maxsplit < 0:
200 raise error, "negative split count"
201 if maxsplit == 0:
202 import sys
203 maxsplit = sys.maxint
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000204 n = 0
205 pos = 0
206 lastmatch = 0
207 results = []
208 end = len(source)
209 while n < maxsplit:
210 m = self.search(source, pos)
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000211 if not m:
212 break
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000213 i, j = m.span(0)
214 if i == j:
215 # Empty match
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000216 if pos >= end:
217 break
Guido van Rossum9e18ec71997-07-17 22:39:13 +0000218 pos = pos+1
219 continue
220 results.append(source[lastmatch:i])
221 g = m.group()
222 if g:
223 results[len(results):] = list(g)
224 pos = lastmatch = j
225 results.append(source[lastmatch:])
226 return results
227
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000228class MatchObject:
229 def __init__(self, re, string, pos, regs):
230 self.re = re
231 self.string = string
232 self.pos = pos
233 self.regs = regs
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000234
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000235 def start(self, g):
236 if type(g) == type(''):
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000237 try:
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000238 g = self.re.groupindex[g]
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000239 except (KeyError, TypeError):
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000240 raise IndexError, ('group "' + g + '" is undefined')
241 return self.regs[g][0]
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000242
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000243 def end(self, g):
244 if type(g) == type(''):
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000245 try:
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000246 g = self.re.groupindex[g]
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000247 except (KeyError, TypeError):
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000248 raise IndexError, ('group "' + g + '" is undefined')
249 return self.regs[g][1]
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000250
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000251 def span(self, g):
252 if type(g) == type(''):
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000253 try:
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000254 g = self.re.groupindex[g]
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000255 except (KeyError, TypeError):
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000256 raise IndexError, ('group "' + g + '" is undefined')
257 return self.regs[g]
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000258
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000259 def group(self, *groups):
260 if len(groups) == 0:
261 groups = range(1, self.re.num_regs)
Guido van Rossum53109751997-07-15 15:40:29 +0000262 use_all = 1
263 else:
264 use_all = 0
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000265 result = []
266 for g in groups:
267 if type(g) == type(''):
268 try:
269 g = self.re.groupindex[g]
270 except (KeyError, TypeError):
271 raise IndexError, ('group "' + g + '" is undefined')
Guido van Rossum04a1d741997-07-15 14:38:13 +0000272 if (self.regs[g][0] == -1) or (self.regs[g][1] == -1):
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000273 result.append(None)
274 else:
275 result.append(self.string[self.regs[g][0]:self.regs[g][1]])
Guido van Rossum53109751997-07-15 15:40:29 +0000276 if use_all or len(result) > 1:
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000277 return tuple(result)
278 elif len(result) == 1:
279 return result[0]
280 else:
281 return ()
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000282
283#
284# A set of classes to make assembly a bit easier, if a bit verbose.
285#
286
287class Instruction:
288 def __init__(self, opcode, size=1):
289 self.opcode = opcode
290 self.size = size
291 def assemble(self, position, labels):
292 return self.opcode
293 def __repr__(self):
294 return '%-15s' % (self.name)
295
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000296class End(Instruction):
297 name = 'end'
298 def __init__(self):
299 Instruction.__init__(self, chr(0))
300
301class Bol(Instruction):
302 name = 'bol'
303 def __init__(self):
304 self.name = 'bol'
305 Instruction.__init__(self, chr(1))
306
307class Eol(Instruction):
308 name = 'eol'
309 def __init__(self):
310 Instruction.__init__(self, chr(2))
311
312class Set(Instruction):
313 name = 'set'
314 def __init__(self, set):
315 self.set = set
316 Instruction.__init__(self, chr(3), 33)
Guido van Rossum63e18191997-07-11 11:08:38 +0000317 def assemble(self, position, labels):
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000318 result = self.opcode
319 temp = 0
320 for i, c in map(lambda x: (x, chr(x)), range(256)):
Guido van Rossum63e18191997-07-11 11:08:38 +0000321 if c in self.set:
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000322 temp = temp | (1 << (i & 7))
323 if (i % 8) == 7:
324 result = result + chr(temp)
325 temp = 0
326 return result
327 def __repr__(self):
328 result = '%-15s' % (self.name)
329 self.set.sort()
330 for char in self.set:
Guido van Rossum63e18191997-07-11 11:08:38 +0000331 result = result + char
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000332 return result
333
334class Exact(Instruction):
335 name = 'exact'
336 def __init__(self, char):
337 self.char = char
338 Instruction.__init__(self, chr(4), 2)
339 def assemble(self, position, labels):
340 return self.opcode + self.char
341 def __repr__(self):
342 return '%-15s %s' % (self.name, `self.char`)
343
344class AnyChar(Instruction):
345 name = 'anychar'
346 def __init__(self):
347 Instruction.__init__(self, chr(5))
348 def assemble(self, position, labels):
349 return self.opcode
350
351class MemoryInstruction(Instruction):
352 def __init__(self, opcode, register):
353 self.register = register
354 Instruction.__init__(self, opcode, 2)
355 def assemble(self, position, labels):
356 return self.opcode + chr(self.register)
357 def __repr__(self):
358 return '%-15s %i' % (self.name, self.register)
359
360class StartMemory(MemoryInstruction):
361 name = 'start_memory'
362 def __init__(self, register):
363 MemoryInstruction.__init__(self, chr(6), register)
364
365class EndMemory(MemoryInstruction):
366 name = 'end_memory'
367 def __init__(self, register):
368 MemoryInstruction.__init__(self, chr(7), register)
369
370class MatchMemory(MemoryInstruction):
371 name = 'match_memory'
372 def __init__(self, register):
373 MemoryInstruction.__init__(self, chr(8), register)
374
375class JumpInstruction(Instruction):
376 def __init__(self, opcode, label):
377 self.label = label
378 Instruction.__init__(self, opcode, 3)
379 def compute_offset(self, start, dest):
380 return dest - (start + 3)
381 def pack_offset(self, offset):
382 if offset > 32767:
383 raise error, 'offset out of range (pos)'
384 elif offset < -32768:
385 raise error, 'offset out of range (neg)'
386 elif offset < 0:
387 offset = offset + 65536
388 return chr(offset & 0xff) + chr((offset >> 8) & 0xff)
389 def assemble(self, position, labels):
390 return self.opcode + \
391 self.pack_offset(self.compute_offset(position,
392 labels[self.label]))
393 def __repr__(self):
394 return '%-15s %i' % (self.name, self.label)
395
396class Jump(JumpInstruction):
397 name = 'jump'
398 def __init__(self, label):
399 JumpInstruction.__init__(self, chr(9), label)
400
401class StarJump(JumpInstruction):
402 name = 'star_jump'
403 def __init__(self, label):
404 JumpInstruction.__init__(self, chr(10), label)
405
406class FailureJump(JumpInstruction):
407 name = 'failure_jump'
408 def __init__(self, label):
409 JumpInstruction.__init__(self, chr(11), label)
410
411class UpdateFailureJump(JumpInstruction):
412 name = 'update_failure_jump'
413 def __init__(self, label):
414 JumpInstruction.__init__(self, chr(12), label)
415
416class DummyFailureJump(JumpInstruction):
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000417 name = 'dummy_failure_jump'
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000418 def __init__(self, label):
419 JumpInstruction.__init__(self, chr(13), label)
420
421class BegBuf(Instruction):
422 name = 'begbuf'
423 def __init__(self):
424 Instruction.__init__(self, chr(14))
425
426class EndBuf(Instruction):
427 name = 'endbuf'
428 def __init__(self):
429 Instruction.__init__(self, chr(15))
430
431class WordBeg(Instruction):
432 name = 'wordbeg'
433 def __init__(self):
434 Instruction.__init__(self, chr(16))
435
436class WordEnd(Instruction):
437 name = 'wordend'
438 def __init__(self):
439 Instruction.__init__(self, chr(17))
440
441class WordBound(Instruction):
442 name = 'wordbound'
443 def __init__(self):
444 Instruction.__init__(self, chr(18))
445
446class NotWordBound(Instruction):
447 name = 'notwordbound'
448 def __init__(self):
449 Instruction.__init__(self, chr(18))
450
451class SyntaxSpec(Instruction):
452 name = 'syntaxspec'
453 def __init__(self, syntax):
454 self.syntax = syntax
455 Instruction.__init__(self, chr(20), 2)
456 def assemble(self, postition, labels):
457 return self.opcode + chr(self.syntax)
458
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000459class NotSyntaxSpec(Instruction):
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000460 name = 'notsyntaxspec'
461 def __init__(self, syntax):
462 self.syntax = syntax
463 Instruction.__init__(self, chr(21), 2)
464 def assemble(self, postition, labels):
465 return self.opcode + chr(self.syntax)
466
467class Label(Instruction):
468 name = 'label'
469 def __init__(self, label):
470 self.label = label
471 Instruction.__init__(self, '', 0)
472 def __repr__(self):
473 return '%-15s %i' % (self.name, self.label)
474
475class OpenParen(Instruction):
476 name = '('
477 def __init__(self, register):
478 self.register = register
479 Instruction.__init__(self, '', 0)
Guido van Rossum5d6de251997-07-11 21:10:17 +0000480 def assemble(self, position, labels):
481 raise error, 'unmatched open parenthesis'
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000482
483class Alternation(Instruction):
484 name = '|'
485 def __init__(self):
486 Instruction.__init__(self, '', 0)
Guido van Rossum5d6de251997-07-11 21:10:17 +0000487 def assemble(self, position, labels):
488 raise error, 'an alternation was not taken care of'
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000489
490#
491#
492#
493
494def assemble(instructions):
495 labels = {}
496 position = 0
497 pass1 = []
498 for instruction in instructions:
499 if instruction.name == 'label':
500 labels[instruction.label] = position
501 else:
502 pass1.append((position, instruction))
503 position = position + instruction.size
504 pass2 = ''
505 for position, instruction in pass1:
506 pass2 = pass2 + instruction.assemble(position, labels)
507 return pass2
508
509#
510#
511#
512
513def escape(pattern):
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000514 result = []
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000515 for char in pattern:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000516 if not reop.syntax_table[char] & reop.word:
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000517 result.append('\\')
518 result.append(char)
519 return string.join(result, '')
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000520
521#
522#
523#
524
525def registers_used(instructions):
526 result = []
527 for instruction in instructions:
528 if (instruction.name in ['set_memory', 'end_memory']) and \
529 (instruction.register not in result):
530 result.append(instruction.register)
531 return result
532
533#
534#
535#
536
537class Fastmap:
538 def __init__(self):
539 self.map = ['\000']*256
540 self.can_be_null = 0
541 def add(self, char):
542 self.map[ord(char)] = '\001'
543 def fastmap(self):
544 return string.join(self.map, '')
545 def __getitem__(self, char):
546 return ord(self.map[ord(char)])
547 def __repr__(self):
548 self.map.sort()
549 return 'Fastmap(' + `self.can_be_null` + ', ' + `self.map` + ')'
550
551#
552#
553#
554
555def find_label(code, label):
556 line = 0
557 for instruction in code:
558 if (instruction.name == 'label') and (instruction.label == label):
559 return line + 1
560 line = line + 1
561
562def build_fastmap_aux(code, pos, visited, fastmap):
563 if visited[pos]:
564 return
565 while 1:
566 instruction = code[pos]
567 visited[pos] = 1
568 pos = pos + 1
569 if instruction.name == 'end':
570 fastmap.can_be_null = 1
571 return
572 elif instruction.name == 'syntaxspec':
573 for char in map(chr, range(256)):
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000574 if reop.syntax_table[char] & instruction.syntax:
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000575 fastmap.add(char)
576 return
577 elif instruction.name == 'notsyntaxspec':
578 for char in map(chr, range(256)):
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000579 if not reop.syntax_table[char] & instruction.syntax:
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000580 fastmap.add(char)
581 return
582 elif instruction.name == 'eol':
583 fastmap.add('\n')
584 if fastmap.can_be_null == 0:
585 fastmap.can_be_null = 2
586 return
587 elif instruction.name == 'set':
588 for char in instruction.set:
589 fastmap.add(char)
590 return
591 elif instruction.name == 'exact':
592 fastmap.add(instruction.char)
593 elif instruction.name == 'anychar':
594 for char in map(chr, range(256)):
595 if char != '\n':
596 fastmap.add(char)
597 return
598 elif instruction.name == 'match_memory':
599 for char in map(chr, range(256)):
600 fastmap.add(char)
601 fastmap.can_be_null = 1
602 return
603 elif instruction.name in ['jump', 'dummy_failure_jump', \
604 'update_failure_jump', 'star_jump']:
605 pos = find_label(code, instruction.label)
606 if visited[pos]:
607 return
608 visited[pos] = 1
609 elif instruction.name == 'failure_jump':
610 build_fastmap_aux(code,
611 find_label(code, instruction.label),
612 visited,
613 fastmap)
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000614
615def build_fastmap(code, pos=0):
616 visited = [0] * len(code)
617 fastmap = Fastmap()
618 build_fastmap_aux(code, pos, visited, fastmap)
619 return fastmap
620
621#
622#
623#
624
Guido van Rossum04a1d741997-07-15 14:38:13 +0000625[NORMAL, CHARCLASS, REPLACEMENT] = range(3)
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000626[CHAR, MEMORY_REFERENCE, SYNTAX, NOT_SYNTAX, SET, WORD_BOUNDARY,
627 NOT_WORD_BOUNDARY, BEGINNING_OF_BUFFER, END_OF_BUFFER] = range(9)
Guido van Rossum04a1d741997-07-15 14:38:13 +0000628
629def expand_escape(pattern, index, context=NORMAL):
630 if index >= len(pattern):
631 raise error, 'escape ends too soon'
632
633 elif pattern[index] == 't':
634 return CHAR, chr(9), index + 1
635
636 elif pattern[index] == 'n':
637 return CHAR, chr(10), index + 1
638
639 elif pattern[index] == 'r':
640 return CHAR, chr(13), index + 1
641
642 elif pattern[index] == 'f':
643 return CHAR, chr(12), index + 1
644
645 elif pattern[index] == 'a':
646 return CHAR, chr(7), index + 1
647
648 elif pattern[index] == 'e':
649 return CHAR, chr(27), index + 1
650
651 elif pattern[index] == 'c':
652 if index + 1 >= len(pattern):
653 raise error, '\\c must be followed by another character'
654 elif pattern[index + 1] in 'abcdefghijklmnopqrstuvwxyz':
655 return CHAR, chr(ord(pattern[index + 1]) - ord('a') + 1), index + 2
656 else:
657 return CHAR, chr(ord(pattern[index + 1]) ^ 64), index + 2
658
659 elif pattern[index] == 'x':
660 # CAUTION: this is the Python rule, not the Perl rule!
661 end = index
662 while (end < len(pattern)) and (pattern[end] in string.hexdigits):
663 end = end + 1
664 if end == index:
665 raise error, "\\x must be followed by hex digit(s)"
666 # let Python evaluate it, so we don't incorrectly 2nd-guess
667 # what it's doing (and Python in turn passes it on to sscanf,
668 # so that *it* doesn't incorrectly 2nd-guess what C does!)
669 char = eval ('"' + pattern[index-2:end] + '"')
670 assert len(char) == 1
671 return CHAR, char, end
672
673 elif pattern[index] == 'b':
674 if context != NORMAL:
675 return CHAR, chr(8), index + 1
676 else:
677 return WORD_BOUNDARY, '', index + 1
678
679 elif pattern[index] == 'B':
680 if context != NORMAL:
681 return CHAR, 'B', index + 1
682 else:
683 return NOT_WORD_BOUNDARY, '', index + 1
684
685 elif pattern[index] == 'A':
686 if context != NORMAL:
687 return CHAR, 'A', index + 1
688 else:
689 return BEGINNING_OF_BUFFER, '', index + 1
690
691 elif pattern[index] == 'Z':
692 if context != NORMAL:
693 return 'Z', index + 1
694 else:
695 return END_OF_BUFFER, '', index + 1
696
697 elif pattern[index] in 'GluLUQE':
698 raise error, ('\\' + ch + ' is not allowed')
699
700 elif pattern[index] == 'w':
701 if context == NORMAL:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000702 return SYNTAX, reop.word, index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000703 elif context == CHARCLASS:
704 set = []
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000705 for char in reop.syntax_table.keys():
706 if reop.syntax_table[char] & reop.word:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000707 set.append(char)
708 return SET, set, index + 1
709 else:
710 return CHAR, 'w', index + 1
711
712 elif pattern[index] == 'W':
713 if context == NORMAL:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000714 return NOT_SYNTAX, reop.word, index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000715 elif context == CHARCLASS:
716 set = []
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000717 for char in reop.syntax_table.keys():
718 if not reop.syntax_table[char] & reop.word:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000719 set.append(char)
720 return SET, set, index + 1
721 else:
722 return CHAR, 'W', index + 1
723
724 elif pattern[index] == 's':
725 if context == NORMAL:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000726 return SYNTAX, reop.whitespace, index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000727 elif context == CHARCLASS:
728 set = []
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000729 for char in reop.syntax_table.keys():
730 if reop.syntax_table[char] & reop.whitespace:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000731 set.append(char)
732 return SET, set, index + 1
733 else:
734 return CHAR, 's', index + 1
735
736 elif pattern[index] == 'S':
737 if context == NORMAL:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000738 return NOT_SYNTAX, reop.whitespace, index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000739 elif context == CHARCLASS:
740 set = []
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000741 for char in reop.syntax_table.keys():
742 if not reop.syntax_table[char] & reop.word:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000743 set.append(char)
744 return SET, set, index + 1
745 else:
746 return CHAR, 'S', index + 1
747
748 elif pattern[index] == 'd':
749 if context == NORMAL:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000750 return SYNTAX, reop.digit, index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000751 elif context == CHARCLASS:
752 set = []
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000753 for char in reop.syntax_table.keys():
754 if reop.syntax_table[char] & reop.digit:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000755 set.append(char)
756 return SET, set, index + 1
757 else:
758 return CHAR, 'd', index + 1
759
760 elif pattern[index] == 'D':
761 if context == NORMAL:
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000762 return NOT_SYNTAX, reop.digit, index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000763 elif context == CHARCLASS:
764 set = []
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000765 for char in reop.syntax_table.keys():
766 if not reop.syntax_table[char] & reop.digit:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000767 set.append(char)
768 return SET, set, index + 1
769 else:
770 return CHAR, 'D', index + 1
771
772 elif pattern[index] in '0123456789':
Guido van Rossum04a1d741997-07-15 14:38:13 +0000773
Guido van Rossum9f845ec1997-07-15 18:11:42 +0000774 if pattern[index] == '0':
775 if (index + 1 < len(pattern)) and \
776 (pattern[index + 1] in string.octdigits):
777 if (index + 2 < len(pattern)) and \
778 (pattern[index + 2] in string.octdigits):
779 value = string.atoi(pattern[index:index + 3], 8)
780 index = index + 3
781
782 else:
783 value = string.atoi(pattern[index:index + 2], 8)
784 index = index + 2
785
786 else:
787 value = 0
788 index = index + 1
789
Guido van Rossum04a1d741997-07-15 14:38:13 +0000790 if value > 255:
Guido van Rossum9f845ec1997-07-15 18:11:42 +0000791 raise error, 'octal value out of range'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000792
Guido van Rossum9f845ec1997-07-15 18:11:42 +0000793 return CHAR, chr(value), index
794
Guido van Rossum04a1d741997-07-15 14:38:13 +0000795 else:
Guido van Rossum9f845ec1997-07-15 18:11:42 +0000796 if (index + 1 < len(pattern)) and \
797 (pattern[index + 1] in string.digits):
798 if (index + 2 < len(pattern)) and \
799 (pattern[index + 2] in string.octdigits) and \
800 (pattern[index + 1] in string.octdigits) and \
801 (pattern[index] in string.octdigits):
802 value = string.atoi(pattern[index:index + 3], 8)
803 if value > 255:
804 raise error, 'octal value out of range'
805
806 return CHAR, chr(value), index + 3
807
808 else:
809 value = string.atoi(pattern[index:index + 2])
810 if (value < 1) or (value > 99):
811 raise error, 'memory reference out of range'
812
813 if context == CHARCLASS:
814 raise error, ('cannot reference a register from '
815 'inside a character class')
816 return MEMORY_REFERENCE, value, index + 2
817
818 else:
819 if context == CHARCLASS:
820 raise error, ('cannot reference a register from '
821 'inside a character class')
822
823 value = string.atoi(pattern[index])
824 return MEMORY_REFERENCE, value, index + 1
825
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000826 elif pattern[index] == 'g':
827 if context != REPLACEMENT:
828 return CHAR, 'g', index + 1
Guido van Rossum04a1d741997-07-15 14:38:13 +0000829
Guido van Rossum71fa97c1997-07-18 04:26:03 +0000830 index = index + 1
831 if index >= len(pattern):
832 raise error, 'unfinished symbolic reference'
833 if pattern[index] != '<':
834 raise error, 'missing < in symbolic reference'
835
836 index = index + 1
837 end = string.find(pattern, '>', index)
838 if end == -1:
839 raise error, 'unfinished symbolic reference'
840 value = pattern[index:end]
841 if not valid_identifier(value):
842 raise error, 'illegal symbolic reference'
843 return MEMORY_REFERENCE, value, end + 1
844
Guido van Rossum04a1d741997-07-15 14:38:13 +0000845 else:
846 return CHAR, pattern[index], index + 1
847
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000848def compile(pattern, flags=0):
849 stack = []
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000850 label = 0
851 register = 1
852 groupindex = {}
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000853 lastop = ''
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000854
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000855 # look for embedded pattern modifiers at the beginning of the pattern
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000856
857 index = 0
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000858
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000859 if len(pattern) >= 3 and \
860 (pattern[:2] == '(?') and \
861 (pattern[2] in 'iImMsSxX'):
862 index = 2
863 while (index < len(pattern)) and (pattern[index] != ')'):
864 if pattern[index] in 'iI':
865 flags = flags | IGNORECASE
866 elif pattern[index] in 'mM':
867 flags = flags | MULTILINE
868 elif pattern[index] in 'sS':
869 flags = flags | DOTALL
870 elif pattern[index] in 'xX':
871 flags = flags | VERBOSE
872 else:
873 raise error, 'unknown modifier'
874 index = index + 1
875 index = index + 1
876
877 # compile the rest of the pattern
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +0000878
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000879 while (index < len(pattern)):
880 char = pattern[index]
881 index = index + 1
882 if char == '\\':
Guido van Rossum04a1d741997-07-15 14:38:13 +0000883 escape_type, value, index = expand_escape(pattern, index)
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000884
Guido van Rossum04a1d741997-07-15 14:38:13 +0000885 if escape_type == CHAR:
886 stack.append([Exact(value)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000887 lastop = '\\' + value
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000888
Guido van Rossum04a1d741997-07-15 14:38:13 +0000889 elif escape_type == MEMORY_REFERENCE:
890 if value >= register:
891 raise error, ('cannot reference a register '
892 'not yet used')
893 stack.append([MatchMemory(value)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000894 lastop = '\\1'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000895
896 elif escape_type == BEGINNING_OF_BUFFER:
897 stack.append([BegBuf()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000898 lastop = '\\A'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000899
900 elif escape_type == END_OF_BUFFER:
901 stack.append([EndBuf()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000902 lastop = '\\Z'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000903
904 elif escape_type == WORD_BOUNDARY:
905 stack.append([WordBound()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000906 lastop = '\\b'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000907
908 elif escape_type == NOT_WORD_BOUNDARY:
909 stack.append([NotWordBound()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000910 lastop = '\\B'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000911
912 elif escape_type == SYNTAX:
913 stack.append([SyntaxSpec(value)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000914 if value == reop.word:
915 lastop = '\\w'
916 elif value == reop.whitespace:
917 lastop = '\\s'
918 elif value == reop.digit:
919 lastop = '\\d'
920 else:
921 lastop = '\\?'
922
Guido van Rossum04a1d741997-07-15 14:38:13 +0000923 elif escape_type == NOT_SYNTAX:
924 stack.append([NotSyntaxSpec(value)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000925 if value == reop.word:
926 lastop = '\\W'
927 elif value == reop.whitespace:
928 lastop = '\\S'
929 elif value == reop.digit:
930 lastop = '\\D'
931 else:
932 lastop = '\\?'
Guido van Rossum04a1d741997-07-15 14:38:13 +0000933
934 elif escape_type == SET:
935 raise error, 'cannot use set escape type here'
936
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000937 else:
Guido van Rossum04a1d741997-07-15 14:38:13 +0000938 raise error, 'unknown escape type'
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000939
940 elif char == '|':
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000941 expr = []
Guido van Rossum04a1d741997-07-15 14:38:13 +0000942
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000943 while (len(stack) != 0) and \
944 (stack[-1][0].name != '(') and \
945 (stack[-1][0].name != '|'):
946 expr = stack[-1] + expr
947 del stack[-1]
948 stack.append([FailureJump(label)] + \
949 expr + \
950 [Jump(-1),
951 Label(label)])
Guido van Rossum8a9a4a21997-07-11 20:48:25 +0000952 stack.append([Alternation()])
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000953 label = label + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000954 lastop = '|'
955
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000956 elif char == '(':
957 if index >= len(pattern):
958 raise error, 'no matching close paren'
959
960 elif pattern[index] == '?':
961 # Perl style (?...) extensions
962 index = index + 1
963 if index >= len(pattern):
964 raise error, 'extension ends prematurely'
965
966 elif pattern[index] == 'P':
967 # Python extensions
968 index = index + 1
969 if index >= len(pattern):
970 raise error, 'extension ends prematurely'
971
972 elif pattern[index] == '<':
Guido van Rossum09bcfd61997-07-15 15:38:20 +0000973 # Handle Python symbolic group names (?P<...>...)
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000974 index = index + 1
975 end = string.find(pattern, '>', index)
976 if end == -1:
977 raise error, 'no end to symbolic group name'
978 name = pattern[index:end]
Guido van Rossum09bcfd61997-07-15 15:38:20 +0000979 if not valid_identifier(name):
980 raise error, ('symbolic group name must be a '
981 'valid identifier')
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000982 index = end + 1
983 groupindex[name] = register
984 stack.append([OpenParen(register)])
985 register = register + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +0000986 lastop = '('
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000987
988 elif pattern[index] == '=':
989 # backreference to symbolic group name
990 if index >= len(pattern):
991 raise error, '(?P= at the end of the pattern'
992 start = index + 1
993 end = string.find(pattern, ')', start)
994 if end == -1:
995 raise error, 'no ) to end symbolic group name'
996 name = pattern[start:end]
Guido van Rossum09bcfd61997-07-15 15:38:20 +0000997 if name not in groupindex.keys():
Guido van Rossum5ca1b711997-07-10 21:00:31 +0000998 raise error, ('symbolic group name ' + name + \
999 ' has not been used yet')
1000 stack.append([MatchMemory(groupindex[name])])
1001 index = end + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001002 lastop = '(?P=)'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001003
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001004 else:
Guido van Rossum8a9a4a21997-07-11 20:48:25 +00001005 raise error, ('unknown Python extension: ' + \
1006 pattern[index])
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001007
1008 elif pattern[index] == ':':
1009 # grouping, but no registers
1010 index = index + 1
Guido van Rossum8a9a4a21997-07-11 20:48:25 +00001011 stack.append([OpenParen(-1)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001012 lastop = '('
1013
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001014 elif pattern[index] == '#':
1015 # comment
1016 index = index + 1
1017 end = string.find(pattern, ')', index)
1018 if end == -1:
1019 raise error, 'no end to comment'
1020 index = end + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001021 # do not change lastop
1022
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001023 elif pattern[index] == '=':
1024 raise error, ('zero-width positive lookahead '
1025 'assertion is unsupported')
1026
1027 elif pattern[index] == '!':
1028 raise error, ('zero-width negative lookahead '
1029 'assertion is unsupported')
1030
1031 elif pattern[index] in 'iImMsSxX':
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001032 raise error, ('embedded pattern modifiers are only '
1033 'allowed at the beginning of the pattern')
1034
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001035 else:
1036 raise error, 'unknown extension'
1037
1038 else:
1039 stack.append([OpenParen(register)])
1040 register = register + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001041 lastop = '('
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001042
1043 elif char == ')':
1044 # make one expression out of everything on the stack up to
1045 # the marker left by the last parenthesis
1046 expr = []
1047 while (len(stack) > 0) and (stack[-1][0].name != '('):
1048 expr = stack[-1] + expr
1049 del stack[-1]
1050
1051 if len(stack) == 0:
1052 raise error, 'too many close parens'
Guido van Rossum04a1d741997-07-15 14:38:13 +00001053
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001054 # remove markers left by alternation
1055 expr = filter(lambda x: x.name != '|', expr)
1056
1057 # clean up jumps inserted by alternation
1058 need_label = 0
1059 for i in range(len(expr)):
1060 if (expr[i].name == 'jump') and (expr[i].label == -1):
Guido van Rossum04a1d741997-07-15 14:38:13 +00001061 expr[i] = Jump(label)
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001062 need_label = 1
1063 if need_label:
1064 expr.append(Label(label))
1065 label = label + 1
1066
Guido van Rossum63e18191997-07-11 11:08:38 +00001067 if stack[-1][0].register > 0:
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001068 expr = [StartMemory(stack[-1][0].register)] + \
1069 expr + \
1070 [EndMemory(stack[-1][0].register)]
1071 del stack[-1]
1072 stack.append(expr)
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001073 lastop = ')'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001074
1075 elif char == '{':
1076 if len(stack) == 0:
1077 raise error, 'no expression to repeat'
1078 end = string.find(pattern, '}', index)
1079 if end == -1:
1080 raise error, ('no close curly bracket to match'
1081 ' open curly bracket')
1082
1083 fields = map(string.strip,
1084 string.split(pattern[index:end], ','))
1085 index = end + 1
1086
1087 minimal = 0
1088 if (index < len(pattern)) and (pattern[index] == '?'):
1089 minimal = 1
1090 index = index + 1
1091
1092 if len(fields) == 1:
1093 # {n} or {n}? (there's really no difference)
1094 try:
1095 count = string.atoi(fields[0])
1096 except ValueError:
1097 raise error, ('count must be an integer '
1098 'inside curly braces')
1099 if count > 65535:
1100 raise error, 'repeat count out of range'
1101 expr = []
1102 while count > 0:
1103 expr = expr + stack[-1]
1104 count = count - 1
1105 del stack[-1]
1106 stack.append(expr)
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001107 if minimal:
1108 lastop = '{n}?'
1109 else:
1110 lastop = '{n}'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001111
1112 elif len(fields) == 2:
1113 # {n,} or {n,m}
1114 if fields[1] == '':
1115 # {n,}
1116 try:
1117 min = string.atoi(fields[0])
1118 except ValueError:
1119 raise error, ('minimum must be an integer '
1120 'inside curly braces')
1121 if min > 65535:
1122 raise error, 'minimum repeat count out of range'
1123
1124 expr = []
1125 while min > 0:
1126 expr = expr + stack[-1]
1127 min = min - 1
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001128 if minimal:
1129 expr = expr + \
1130 ([Jump(label + 1),
1131 Label(label)] + \
1132 stack[-1] + \
1133 [Label(label + 1),
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +00001134 FailureJump(label)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001135 lastop = '{n,}?'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001136 else:
1137 expr = expr + \
1138 ([Label(label),
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +00001139 FailureJump(label + 1)] +
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001140 stack[-1] +
1141 [StarJump(label),
1142 Label(label + 1)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001143 lastop = '{n,}'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001144
1145 del stack[-1]
1146 stack.append(expr)
1147 label = label + 2
1148
1149 else:
1150 # {n,m}
1151 try:
1152 min = string.atoi(fields[0])
1153 except ValueError:
1154 raise error, ('minimum must be an integer '
1155 'inside curly braces')
1156 try:
1157 max = string.atoi(fields[1])
1158 except ValueError:
1159 raise error, ('maximum must be an integer '
1160 'inside curly braces')
1161 if min > 65535:
1162 raise error, ('minumim repeat count out '
1163 'of range')
1164 if max > 65535:
1165 raise error, ('maximum repeat count out '
1166 'of range')
1167 if min > max:
1168 raise error, ('minimum repeat count must be '
1169 'less than the maximum '
1170 'repeat count')
1171 expr = []
1172 while min > 0:
1173 expr = expr + stack[-1]
1174 min = min - 1
1175 max = max - 1
1176 if minimal:
1177 while max > 0:
1178 expr = expr + \
1179 [FailureJump(label),
1180 Jump(label + 1),
1181 Label(label)] + \
1182 stack[-1] + \
1183 [Label(label + 1)]
Guido van Rossum26d80e61997-07-15 18:59:04 +00001184 max = max - 1
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001185 label = label + 2
1186 del stack[-1]
1187 stack.append(expr)
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001188 lastop = '{n,m}?'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001189 else:
1190 while max > 0:
1191 expr = expr + \
1192 [FailureJump(label)] + \
1193 stack[-1]
1194 max = max - 1
1195 del stack[-1]
1196 stack.append(expr + [Label(label)])
1197 label = label + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001198 lastop = '{n,m}'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001199
1200 else:
1201 raise error, ('there need to be one or two fields '
1202 'in a {} expression')
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001203
1204 elif char == '}':
1205 raise error, 'unbalanced close curly brace'
1206
1207 elif char == '*':
1208 # Kleene closure
1209 if len(stack) == 0:
Guido van Rossum5d6de251997-07-11 21:10:17 +00001210 raise error, '* needs something to repeat'
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001211
1212 if lastop in ['(', '|']:
Guido van Rossum5d6de251997-07-11 21:10:17 +00001213 raise error, '* needs something to repeat'
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001214
1215 if lastop in repetition_operators:
1216 raise error, 'nested repetition operators'
1217
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001218 if (index < len(pattern)) and (pattern[index] == '?'):
1219 # non-greedy matching
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +00001220 expr = [Jump(label + 1),
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001221 Label(label)] + \
1222 stack[-1] + \
1223 [Label(label + 1),
1224 FailureJump(label)]
1225 index = index + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001226 lastop = '*?'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001227 else:
1228 # greedy matching
1229 expr = [Label(label),
1230 FailureJump(label + 1)] + \
1231 stack[-1] + \
1232 [StarJump(label),
1233 Label(label + 1)]
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001234 lastop = '*'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001235 del stack[-1]
1236 stack.append(expr)
1237 label = label + 2
1238
1239 elif char == '+':
1240 # positive closure
1241 if len(stack) == 0:
Guido van Rossum5d6de251997-07-11 21:10:17 +00001242 raise error, '+ needs something to repeat'
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +00001243
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001244 if lastop in ['(', '|']:
Guido van Rossum5d6de251997-07-11 21:10:17 +00001245 raise error, '+ needs something to repeat'
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001246
1247 if lastop in repetition_operators:
1248 raise error, 'nested repetition operators'
1249
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001250 if (index < len(pattern)) and (pattern[index] == '?'):
1251 # non-greedy
1252 expr = [Label(label)] + \
1253 stack[-1] + \
1254 [FailureJump(label)]
1255 label = label + 1
1256 index = index + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001257 lastop = '+?'
1258
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001259 else:
1260 # greedy
1261 expr = [DummyFailureJump(label + 1),
1262 Label(label),
1263 FailureJump(label + 2),
1264 Label(label + 1)] + \
1265 stack[-1] + \
1266 [StarJump(label),
1267 Label(label + 2)]
1268 label = label + 3
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001269 lastop = '+'
1270
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001271 del stack[-1]
1272 stack.append(expr)
1273
1274 elif char == '?':
1275 if len(stack) == 0:
1276 raise error, 'need something to be optional'
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001277
1278 if len(stack) == 0:
1279 raise error, '? needs something to repeat'
1280
1281 if lastop in ['(', '|']:
1282 raise error, '? needs something to repeat'
1283
1284 if lastop in repetition_operators:
1285 raise error, 'nested repetition operators'
1286
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001287 if (index < len(pattern)) and (pattern[index] == '?'):
1288 # non-greedy matching
1289 expr = [FailureJump(label),
1290 Jump(label + 1),
1291 Label(label)] + \
1292 stack[-1] + \
1293 [Label(label + 1)]
1294 label = label + 2
1295 index = index + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001296 lastop = '??'
1297
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001298 else:
1299 # greedy matching
1300 expr = [FailureJump(label)] + \
1301 stack[-1] + \
1302 [Label(label)]
1303 label = label + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001304 lastop = '?'
1305
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001306 del stack[-1]
1307 stack.append(expr)
1308
1309 elif char == '.':
1310 if flags & DOTALL:
Guido van Rossuma0e4c1b1997-07-17 14:52:48 +00001311 stack.append([Set(map(chr, range(256)))])
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001312 else:
1313 stack.append([AnyChar()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001314 lastop = '.'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001315
1316 elif char == '^':
1317 if flags & MULTILINE:
1318 stack.append([Bol()])
1319 else:
1320 stack.append([BegBuf()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001321 lastop = '^'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001322
1323 elif char == '$':
1324 if flags & MULTILINE:
1325 stack.append([Eol()])
1326 else:
1327 stack.append([EndBuf()])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001328 lastop = '$'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001329
1330 elif char == '#':
1331 if flags & VERBOSE:
1332 # comment
1333 index = index + 1
1334 end = string.find(pattern, '\n', index)
1335 if end == -1:
1336 index = len(pattern)
1337 else:
1338 index = end + 1
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001339 # do not change lastop
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001340 else:
1341 stack.append([Exact(char)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001342 lastop = '#'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001343
1344 elif char in string.whitespace:
Guido van Rossum04a1d741997-07-15 14:38:13 +00001345 if not (flags & VERBOSE):
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001346 stack.append([Exact(char)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001347 lastop = char
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001348
1349 elif char == '[':
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001350 # compile character class
1351
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001352 if index >= len(pattern):
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001353 raise error, 'unclosed character class'
1354
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001355 negate = 0
1356 last = ''
1357 set = []
Guido van Rossum04a1d741997-07-15 14:38:13 +00001358
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001359 if pattern[index] == '^':
1360 negate = 1
1361 index = index + 1
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001362 if index >= len(pattern):
1363 raise error, 'unclosed character class'
Guido van Rossum04a1d741997-07-15 14:38:13 +00001364
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001365 if pattern[index] == ']':
1366 set.append(']')
1367 index = index + 1
1368 if index >= len(pattern):
1369 raise error, 'unclosed character class'
1370
1371 elif pattern[index] == '-':
1372 set.append('-')
1373 index = index + 1
1374 if index >= len(pattern):
1375 raise error, 'unclosed character class'
1376
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001377 while (index < len(pattern)) and (pattern[index] != ']'):
1378 next = pattern[index]
1379 index = index + 1
1380 if next == '-':
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001381 if index >= len(pattern):
1382 raise error, 'incomplete range in character class'
Guido van Rossum04a1d741997-07-15 14:38:13 +00001383
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001384 elif pattern[index] == ']':
1385 set.append('-')
Guido van Rossum04a1d741997-07-15 14:38:13 +00001386
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001387 else:
1388 if last == '':
1389 raise error, ('improper use of range in '
1390 'character class')
1391
1392 start = last
1393
1394 if pattern[index] == '\\':
1395 escape_type,
1396 value,
1397 index = expand_escape(pattern,
1398 index + 1,
1399 CHARCLASS)
1400
1401 if escape_type == CHAR:
1402 end = value
1403
1404 else:
1405 raise error, ('illegal escape in character '
1406 'class range')
1407 else:
1408 end = pattern[index]
1409 index = index + 1
1410
1411 if start > end:
1412 raise error, ('range arguments out of order '
1413 'in character class')
1414
1415 for char in map(chr, range(ord(start), ord(end) + 1)):
1416 if char not in set:
1417 set.append(char)
Guido van Rossum04a1d741997-07-15 14:38:13 +00001418
Guido van Rossum09bcfd61997-07-15 15:38:20 +00001419 last = ''
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001420
1421 elif next == '\\':
1422 # expand syntax meta-characters and add to set
1423 if index >= len(pattern):
1424 raise error, 'incomplete set'
Guido van Rossum04a1d741997-07-15 14:38:13 +00001425
1426 escape_type, value, index = expand_escape(pattern,
1427 index,
1428 CHARCLASS)
1429
1430 if escape_type == CHAR:
1431 set.append(value)
1432 last = value
1433
1434 elif escape_type == SET:
1435 for char in value:
1436 if char not in set:
1437 set.append(char)
1438 last = ''
1439
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001440 else:
Guido van Rossum04a1d741997-07-15 14:38:13 +00001441 raise error, 'illegal escape type in character class'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001442
1443 else:
1444 if next not in set:
1445 set.append(next)
1446 last = next
Guido van Rossum04a1d741997-07-15 14:38:13 +00001447
1448 if (index >= len(pattern)) or ( pattern[index] != ']'):
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001449 raise error, 'incomplete set'
1450
1451 index = index + 1
1452
1453 if negate:
1454 notset = []
1455 for char in map(chr, range(256)):
1456 if char not in set:
1457 notset.append(char)
Guido van Rossum04a1d741997-07-15 14:38:13 +00001458 if len(notset) == 0:
1459 raise error, 'empty negated set'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001460 stack.append([Set(notset)])
1461 else:
Guido van Rossum04a1d741997-07-15 14:38:13 +00001462 if len(set) == 0:
1463 raise error, 'empty set'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001464 stack.append([Set(set)])
1465
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001466 lastop = '[]'
1467
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001468 else:
1469 stack.append([Exact(char)])
Guido van Rossuma4f1a781997-07-17 22:38:10 +00001470 lastop = char
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001471
1472 code = []
1473 while len(stack) > 0:
1474 if stack[-1][0].name == '(':
1475 raise error, 'too many open parens'
1476 code = stack[-1] + code
1477 del stack[-1]
1478 if len(code) == 0:
1479 raise error, 'no code generated'
Guido van Rossum5ca1b711997-07-10 21:00:31 +00001480 code = filter(lambda x: x.name != '|', code)
1481 need_label = 0
1482 for i in range(len(code)):
1483 if (code[i].name == 'jump') and (code[i].label == -1):
1484 code[i] = Jump(label)
1485 need_label = 1
1486 if need_label:
1487 code.append(Label(label))
1488 label = label + 1
1489 code.append(End())
Guido van Rossum71fa97c1997-07-18 04:26:03 +00001490 return RegexObject(pattern, flags, code, register, groupindex)