// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// The Unicode general categories a pre-tokenizer rule refers to, as the
/// patterns are written in terms of `\p{L}`, `\p{N}` and `\p{M}`.
///
/// Decoding a code point twice for one character would show up in the profile
/// of any tokenizer, so the ASCII range is answered from a table instead.
fn is_letter(c : Char) -> Bool {
  let n = c.to_int()
  if n < 0x80 {
    return (n >= 0x41 && n <= 0x5A) || (n >= 0x61 && n <= 0x7A)
  }
  match @ucd.category(c) {
    Lu | Ll | Lt | Lm | Lo => true
    _ => false
  }
}

///|
fn is_number(c : Char) -> Bool {
  let n = c.to_int()
  if n < 0x80 {
    return n >= 0x30 && n <= 0x39
  }
  match @ucd.category(c) {
    Nd | Nl | No => true
    _ => false
  }
}

///|
/// The `\s` of the patterns. Rust's `regex` crate reads it as the
/// `White_Space` property rather than as the `Z` category, and the two differ on
/// U+0009 to U+000D and U+0085.
fn is_space(c : Char) -> Bool {
  let n = c.to_int()
  if n < 0x80 {
    return n == 0x20 || (n >= 0x09 && n <= 0x0D)
  }
  @ucd.has(c, @ucd.WhiteSpace)
}

///|
/// `[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]`, the leading class of the case-aware
/// rules.
fn is_upper_class(c : Char) -> Bool {
  let n = c.to_int()
  if n < 0x80 {
    return n >= 0x41 && n <= 0x5A
  }
  match @ucd.category(c) {
    Lu | Lt | Lm | Lo | Mn | Mc | Me => true
    _ => false
  }
}

///|
/// `[\p{Ll}\p{Lm}\p{Lo}\p{M}]`, the trailing class of the case-aware rules.
fn is_lower_class(c : Char) -> Bool {
  let n = c.to_int()
  if n < 0x80 {
    return n >= 0x61 && n <= 0x7A
  }
  match @ucd.category(c) {
    Ll | Lm | Lo | Mn | Mc | Me => true
    _ => false
  }
}

///|
fn is_cr(c : Char) -> Bool {
  c == '\r'
}

///|
fn is_lf(c : Char) -> Bool {
  c == '\n'
}