(source: str)
| 125 | |
| 126 | |
| 127 | def tokenize(source: str) -> Iterator[Token]: |
| 128 | cursor = 0 |
| 129 | backslashes = 0 |
| 130 | buffer = [] |
| 131 | |
| 132 | def send_buffer() -> Iterator[Token]: |
| 133 | nonlocal backslashes |
| 134 | if not buffer: |
| 135 | return None |
| 136 | |
| 137 | value = ''.join(buffer) |
| 138 | kind = TokenKind.TEXT |
| 139 | if not backslashes: |
| 140 | for variation, kind in [ |
| 141 | (int, TokenKind.NUMBER), |
| 142 | (check_escaped_int, TokenKind.TEXT), |
| 143 | ]: |
| 144 | try: |
| 145 | value = variation(value) |
| 146 | except ValueError: |
| 147 | continue |
| 148 | else: |
| 149 | break |
| 150 | yield Token( |
| 151 | kind=kind, |
| 152 | value=value, |
| 153 | start=cursor - (len(buffer) + backslashes), |
| 154 | end=cursor, |
| 155 | ) |
| 156 | buffer.clear() |
| 157 | backslashes = 0 |
| 158 | |
| 159 | def can_advance() -> bool: |
| 160 | return cursor < len(source) |
| 161 | |
| 162 | while can_advance(): |
| 163 | index = source[cursor] |
| 164 | if index in OPERATORS: |
| 165 | yield from send_buffer() |
| 166 | yield Token(OPERATORS[index], index, cursor, cursor + 1) |
| 167 | elif index == BACKSLASH and can_advance(): |
| 168 | if source[cursor + 1] in SPECIAL_CHARS: |
| 169 | backslashes += 1 |
| 170 | else: |
| 171 | buffer.append(index) |
| 172 | buffer.append(source[cursor + 1]) |
| 173 | cursor += 1 |
| 174 | else: |
| 175 | buffer.append(index) |
| 176 | cursor += 1 |
| 177 | |
| 178 | yield from send_buffer() |
| 179 | |
| 180 | |
| 181 | def check_escaped_int(value: str) -> str: |
no test coverage detected