(position: number)
| 194 | } |
| 195 | |
| 196 | private readNumber(position: number): NumberToken | KeywordToken { |
| 197 | const start = this.scanner.position; |
| 198 | let hasDecimal = false; |
| 199 | let hasDigit = false; |
| 200 | let isNegative = false; |
| 201 | |
| 202 | // Handle leading sign |
| 203 | const firstByte = this.scanner.peek(); |
| 204 | |
| 205 | if (firstByte === CHAR_PLUS || firstByte === CHAR_MINUS) { |
| 206 | isNegative = firstByte === CHAR_MINUS; |
| 207 | this.scanner.advance(); |
| 208 | |
| 209 | // Handle double negative (lenient) - if multiple negatives, ignore all |
| 210 | // This matches PDFBox behavior: --5 → 5, ---5 → 5 |
| 211 | if (this.scanner.peek() === CHAR_MINUS) { |
| 212 | isNegative = false; |
| 213 | |
| 214 | while (this.scanner.peek() === CHAR_MINUS) { |
| 215 | this.scanner.advance(); |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | // Track where digits start (after signs) |
| 221 | const digitsStart = this.scanner.position; |
| 222 | |
| 223 | // Handle leading decimal |
| 224 | if (this.scanner.peek() === CHAR_PERIOD) { |
| 225 | hasDecimal = true; |
| 226 | this.scanner.advance(); |
| 227 | } |
| 228 | |
| 229 | // Read digits |
| 230 | while (true) { |
| 231 | const byte = this.scanner.peek(); |
| 232 | |
| 233 | if (isDigit(byte)) { |
| 234 | hasDigit = true; |
| 235 | this.scanner.advance(); |
| 236 | continue; |
| 237 | } |
| 238 | |
| 239 | if (byte === CHAR_PERIOD && !hasDecimal) { |
| 240 | hasDecimal = true; |
| 241 | this.scanner.advance(); |
| 242 | continue; |
| 243 | } |
| 244 | |
| 245 | break; |
| 246 | } |
| 247 | |
| 248 | // Read any trailing digits after decimal |
| 249 | if (hasDecimal) { |
| 250 | while (isDigit(this.scanner.peek())) { |
| 251 | hasDigit = true; |
| 252 | this.scanner.advance(); |
| 253 | } |
no test coverage detected