|
1 | | -use rustpython_unicode as unicode; |
2 | 1 | use rustpython_wtf8::Wtf8; |
3 | 2 |
|
4 | 3 | #[derive(Debug, Clone, Copy)] |
@@ -333,70 +332,142 @@ const fn utf8_is_cont_byte(byte: u8) -> bool { |
333 | 332 | /// Mask of the value bits of a continuation byte. |
334 | 333 | const CONT_MASK: u8 = 0b0011_1111; |
335 | 334 |
|
| 335 | +// Character-class and case predicates for the SRE engine. |
| 336 | +// |
| 337 | +// Every predicate takes a raw `u32` code point (SRE decodes strings into `u32`s, |
| 338 | +// including lone surrogates) and returns whether it belongs to the class. |
| 339 | +// ASCII-mode predicates only ever consider byte values; Unicode-mode predicates |
| 340 | +// consult the shared property tables in `rustpython_unicode::classify`. |
| 341 | + |
| 342 | +const UNDERSCORE: u32 = '_' as u32; |
| 343 | + |
| 344 | +const fn is_py_ascii_whitespace(b: u8) -> bool { |
| 345 | + matches!(b, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ' | b'\x0B') |
| 346 | +} |
| 347 | + |
336 | 348 | #[inline] |
337 | 349 | pub(crate) fn is_word(ch: u32) -> bool { |
338 | | - unicode::regex::is_word(ch) |
| 350 | + ch == UNDERSCORE || u8::try_from(ch).is_ok_and(|x| x.is_ascii_alphanumeric()) |
339 | 351 | } |
| 352 | + |
340 | 353 | #[inline] |
341 | 354 | pub(crate) fn is_space(ch: u32) -> bool { |
342 | | - unicode::regex::is_space(ch) |
| 355 | + u8::try_from(ch).is_ok_and(is_py_ascii_whitespace) |
343 | 356 | } |
| 357 | + |
344 | 358 | #[inline] |
345 | 359 | pub(crate) fn is_digit(ch: u32) -> bool { |
346 | | - unicode::regex::is_digit(ch) |
| 360 | + u8::try_from(ch).is_ok_and(|x| x.is_ascii_digit()) |
347 | 361 | } |
| 362 | + |
348 | 363 | #[inline] |
349 | 364 | pub(crate) fn is_loc_alnum(ch: u32) -> bool { |
350 | | - unicode::regex::is_loc_alnum(ch) |
| 365 | + // FIXME: Ignore the locales |
| 366 | + u8::try_from(ch).is_ok_and(|x| x.is_ascii_alphanumeric()) |
351 | 367 | } |
| 368 | + |
352 | 369 | #[inline] |
353 | 370 | pub(crate) fn is_loc_word(ch: u32) -> bool { |
354 | | - ch == '_' as u32 || is_loc_alnum(ch) |
| 371 | + ch == UNDERSCORE || is_loc_alnum(ch) |
355 | 372 | } |
| 373 | + |
356 | 374 | #[inline] |
357 | 375 | pub(crate) const fn is_linebreak(ch: u32) -> bool { |
358 | | - unicode::regex::is_linebreak(ch) |
| 376 | + ch == '\n' as u32 |
359 | 377 | } |
| 378 | + |
360 | 379 | #[inline] |
361 | 380 | #[must_use] |
362 | 381 | pub fn lower_ascii(ch: u32) -> u32 { |
363 | | - unicode::regex::lower_ascii(ch) |
| 382 | + u8::try_from(ch).map_or(ch, |x| x.to_ascii_lowercase() as u32) |
364 | 383 | } |
| 384 | + |
365 | 385 | #[inline] |
366 | 386 | pub(crate) fn lower_locate(ch: u32) -> u32 { |
367 | | - unicode::regex::lower_locate(ch) |
| 387 | + // FIXME: Ignore the locales |
| 388 | + lower_ascii(ch) |
368 | 389 | } |
| 390 | + |
369 | 391 | #[inline] |
370 | 392 | pub(crate) fn upper_locate(ch: u32) -> u32 { |
371 | | - unicode::regex::upper_locate(ch) |
| 393 | + // FIXME: Ignore the locales |
| 394 | + u8::try_from(ch).map_or(ch, |x| x.to_ascii_uppercase() as u32) |
372 | 395 | } |
| 396 | + |
373 | 397 | #[inline] |
374 | 398 | pub(crate) fn is_uni_digit(ch: u32) -> bool { |
375 | | - unicode::regex::is_uni_digit(ch) |
| 399 | + // SRE_UNI_IS_DIGIT matches Unicode decimal digits (Py_UNICODE_ISDECIMAL), |
| 400 | + // not just ASCII 0-9. |
| 401 | + char::try_from(ch).is_ok_and(rustpython_unicode::classify::is_decimal) |
376 | 402 | } |
| 403 | + |
377 | 404 | #[inline] |
378 | 405 | pub(crate) fn is_uni_space(ch: u32) -> bool { |
379 | | - unicode::regex::is_uni_space(ch) |
| 406 | + // TODO: check with cpython |
| 407 | + is_space(ch) |
| 408 | + || matches!( |
| 409 | + ch, |
| 410 | + 0x0009 |
| 411 | + | 0x000A |
| 412 | + | 0x000B |
| 413 | + | 0x000C |
| 414 | + | 0x000D |
| 415 | + | 0x001C |
| 416 | + | 0x001D |
| 417 | + | 0x001E |
| 418 | + | 0x001F |
| 419 | + | 0x0020 |
| 420 | + | 0x0085 |
| 421 | + | 0x00A0 |
| 422 | + | 0x1680 |
| 423 | + | 0x2000 |
| 424 | + | 0x2001 |
| 425 | + | 0x2002 |
| 426 | + | 0x2003 |
| 427 | + | 0x2004 |
| 428 | + | 0x2005 |
| 429 | + | 0x2006 |
| 430 | + | 0x2007 |
| 431 | + | 0x2008 |
| 432 | + | 0x2009 |
| 433 | + | 0x200A |
| 434 | + | 0x2028 |
| 435 | + | 0x2029 |
| 436 | + | 0x202F |
| 437 | + | 0x205F |
| 438 | + | 0x3000 |
| 439 | + ) |
380 | 440 | } |
| 441 | + |
381 | 442 | #[inline] |
382 | 443 | pub(crate) const fn is_uni_linebreak(ch: u32) -> bool { |
383 | | - unicode::regex::is_uni_linebreak(ch) |
| 444 | + matches!( |
| 445 | + ch, |
| 446 | + 0x000A | 0x000B | 0x000C | 0x000D | 0x001C | 0x001D | 0x001E | 0x0085 | 0x2028 | 0x2029 |
| 447 | + ) |
384 | 448 | } |
| 449 | + |
385 | 450 | #[inline] |
386 | 451 | pub(crate) fn is_uni_alnum(ch: u32) -> bool { |
387 | | - unicode::regex::is_uni_alnum(ch) |
| 452 | + // TODO: check with cpython |
| 453 | + char::try_from(ch).is_ok_and(rustpython_unicode::classify::is_alnum) |
388 | 454 | } |
| 455 | + |
389 | 456 | #[inline] |
390 | 457 | pub(crate) fn is_uni_word(ch: u32) -> bool { |
391 | | - ch == '_' as u32 || is_uni_alnum(ch) |
| 458 | + ch == UNDERSCORE || is_uni_alnum(ch) |
392 | 459 | } |
| 460 | + |
393 | 461 | #[inline] |
394 | 462 | #[must_use] |
395 | 463 | pub fn lower_unicode(ch: u32) -> u32 { |
396 | | - unicode::regex::lower_unicode(ch) |
| 464 | + // TODO: check with cpython |
| 465 | + char::try_from(ch).map_or(ch, |x| x.to_lowercase().next().unwrap() as u32) |
397 | 466 | } |
| 467 | + |
398 | 468 | #[inline] |
399 | 469 | #[must_use] |
400 | 470 | pub fn upper_unicode(ch: u32) -> u32 { |
401 | | - unicode::regex::upper_unicode(ch) |
| 471 | + // TODO: check with cpython |
| 472 | + char::try_from(ch).map_or(ch, |x| x.to_uppercase().next().unwrap() as u32) |
402 | 473 | } |
0 commit comments