(source, escape, state)
| 374 | raise source.error("bad escape %s" % escape, len(escape)) |
| 375 | |
| 376 | def _escape(source, escape, state): |
| 377 | # handle escape code in expression |
| 378 | code = CATEGORIES.get(escape) |
| 379 | if code: |
| 380 | return code |
| 381 | code = ESCAPES.get(escape) |
| 382 | if code: |
| 383 | return code |
| 384 | try: |
| 385 | c = escape[1:2] |
| 386 | if c == "x": |
| 387 | # hexadecimal escape |
| 388 | escape += source.getwhile(2, HEXDIGITS) |
| 389 | if len(escape) != 4: |
| 390 | raise source.error("incomplete escape %s" % escape, len(escape)) |
| 391 | return LITERAL, int(escape[2:], 16) |
| 392 | elif c == "u" and source.istext: |
| 393 | # unicode escape (exactly four digits) |
| 394 | escape += source.getwhile(4, HEXDIGITS) |
| 395 | if len(escape) != 6: |
| 396 | raise source.error("incomplete escape %s" % escape, len(escape)) |
| 397 | return LITERAL, int(escape[2:], 16) |
| 398 | elif c == "U" and source.istext: |
| 399 | # unicode escape (exactly eight digits) |
| 400 | escape += source.getwhile(8, HEXDIGITS) |
| 401 | if len(escape) != 10: |
| 402 | raise source.error("incomplete escape %s" % escape, len(escape)) |
| 403 | c = int(escape[2:], 16) |
| 404 | chr(c) # raise ValueError for invalid code |
| 405 | return LITERAL, c |
| 406 | elif c == "N" and source.istext: |
| 407 | import unicodedata |
| 408 | # named unicode escape e.g. \N{EM DASH} |
| 409 | if not source.match('{'): |
| 410 | raise source.error("missing {") |
| 411 | charname = source.getuntil('}', 'character name') |
| 412 | try: |
| 413 | c = ord(unicodedata.lookup(charname)) |
| 414 | except (KeyError, TypeError): |
| 415 | raise source.error("undefined character name %r" % charname, |
| 416 | len(charname) + len(r'\N{}')) from None |
| 417 | return LITERAL, c |
| 418 | elif c == "0": |
| 419 | # octal escape |
| 420 | escape += source.getwhile(2, OCTDIGITS) |
| 421 | return LITERAL, int(escape[1:], 8) |
| 422 | elif c in DIGITS: |
| 423 | # octal escape *or* decimal group reference (sigh) |
| 424 | if source.next in DIGITS: |
| 425 | escape += source.get() |
| 426 | if (escape[1] in OCTDIGITS and escape[2] in OCTDIGITS and |
| 427 | source.next in OCTDIGITS): |
| 428 | # got three octal digits; this is an octal escape |
| 429 | escape += source.get() |
| 430 | c = int(escape[1:], 8) |
| 431 | if c > 0o377: |
| 432 | raise source.error('octal escape value %s outside of ' |
| 433 | 'range 0-0o377' % escape, |
no test coverage detected