|
6 | 6 |
|
7 | 7 | from types import MappingProxyType |
8 | 8 |
|
9 | | -from ._re import ( |
10 | | - RE_DATETIME, |
11 | | - RE_LOCALTIME, |
12 | | - RE_NUMBER, |
13 | | - match_to_datetime, |
14 | | - match_to_localtime, |
15 | | - match_to_number, |
16 | | -) |
17 | | - |
18 | 9 | TYPE_CHECKING = False |
19 | 10 | if TYPE_CHECKING: |
20 | 11 | from collections.abc import Iterable |
21 | 12 | from typing import IO, Any, Final |
22 | 13 |
|
23 | 14 | from ._types import Key, ParseFloat, Pos |
24 | 15 |
|
| 16 | + _REGEX_IMPORTED = True |
| 17 | + from ._re import ( |
| 18 | + RE_DATETIME, |
| 19 | + RE_LOCALTIME, |
| 20 | + RE_NUMBER, |
| 21 | + match_to_datetime, |
| 22 | + match_to_localtime, |
| 23 | + match_to_number, |
| 24 | + ) |
| 25 | +else: |
| 26 | + # Regular expressions are lazy imported to speed up startup time |
| 27 | + _REGEX_IMPORTED = False |
| 28 | + |
25 | 29 | ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) |
26 | 30 |
|
27 | 31 | # Neither of these sets include quotation mark or backslash. They are |
|
41 | 45 | ) |
42 | 46 | KEY_INITIAL_CHARS: Final = BARE_KEY_CHARS | frozenset("\"'") |
43 | 47 | HEXDIGIT_CHARS: Final = frozenset("abcdef" "ABCDEF" "0123456789") |
| 48 | +_DECDIGIT_CHARS: Final = frozenset("0123456789") |
44 | 49 |
|
45 | 50 | BASIC_STR_ESCAPE_REPLACEMENTS: Final = MappingProxyType( |
46 | 51 | { |
@@ -665,6 +670,25 @@ def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: |
665 | 670 | pos += 1 |
666 | 671 |
|
667 | 672 |
|
| 673 | +def _parse_simple_number( |
| 674 | + src: str, pos: Pos |
| 675 | +) -> None | tuple[Pos, int]: |
| 676 | + start = pos |
| 677 | + src = src.rstrip() |
| 678 | + end = len(src) |
| 679 | + while src[pos] in _DECDIGIT_CHARS: |
| 680 | + pos += 1 |
| 681 | + if pos >= end: |
| 682 | + break |
| 683 | + else: |
| 684 | + if src[pos] != "\n": |
| 685 | + return None |
| 686 | + digits = src[start:pos] |
| 687 | + if digits.startswith("0") and len(digits) > 1: |
| 688 | + return None |
| 689 | + return pos, int(digits) |
| 690 | + |
| 691 | + |
668 | 692 | def parse_value( |
669 | 693 | src: str, pos: Pos, parse_float: ParseFloat |
670 | 694 | ) -> tuple[Pos, Any]: |
@@ -703,6 +727,25 @@ def parse_value( |
703 | 727 | if char == "{": |
704 | 728 | return parse_inline_table(src, pos, parse_float) |
705 | 729 |
|
| 730 | + global _REGEX_IMPORTED, RE_DATETIME, RE_LOCALTIME, RE_NUMBER |
| 731 | + global match_to_datetime, match_to_localtime, match_to_number |
| 732 | + if not _REGEX_IMPORTED: |
| 733 | + # Simple number parser avoiding regex |
| 734 | + if char in _DECDIGIT_CHARS: |
| 735 | + res = _parse_simple_number(src, pos) |
| 736 | + if res is not None: |
| 737 | + return res |
| 738 | + |
| 739 | + from ._re import ( |
| 740 | + RE_DATETIME, |
| 741 | + RE_LOCALTIME, |
| 742 | + RE_NUMBER, |
| 743 | + match_to_datetime, |
| 744 | + match_to_localtime, |
| 745 | + match_to_number, |
| 746 | + ) |
| 747 | + _REGEX_IMPORTED = True |
| 748 | + |
706 | 749 | # Dates and times |
707 | 750 | datetime_match = RE_DATETIME.match(src, pos) |
708 | 751 | if datetime_match: |
|
0 commit comments