use crate::charset::CharSet;
use rexact_syntax::{Dialect, Flags};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Alphabet {
Unicode,
Utf16,
Bytes,
}
impl Alphabet {
pub const fn name(self) -> &'static str {
match self {
Alphabet::Unicode => "unicode",
Alphabet::Utf16 => "utf16",
Alphabet::Bytes => "bytes",
}
}
pub fn default_for(dialect: Dialect, flags: Flags) -> Alphabet {
match dialect {
Dialect::Ecma if !flags.u => Alphabet::Utf16,
Dialect::Pcre2 if !flags.u => Alphabet::Bytes,
_ => Alphabet::Unicode,
}
}
pub fn universe(self) -> CharSet {
match self {
Alphabet::Unicode => CharSet::from_ranges([(0, 0xD7FF), (0xE000, 0x10FFFF)]),
Alphabet::Utf16 => CharSet::range(0, 0xFFFF),
Alphabet::Bytes => CharSet::range(0, 0xFF),
}
}
pub fn encode(self, s: &str) -> Vec<u32> {
match self {
Alphabet::Unicode => s.chars().map(u32::from).collect(),
Alphabet::Utf16 => s.encode_utf16().map(u32::from).collect(),
Alphabet::Bytes => s.bytes().map(u32::from).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encoding_of_a_non_bmp_character_depends_on_the_mode() {
assert_eq!(Alphabet::Unicode.encode("😀"), vec![0x1F600]);
assert_eq!(Alphabet::Utf16.encode("😀"), vec![0xD83D, 0xDE00]);
assert_eq!(Alphabet::Bytes.encode("é"), vec![0xC3, 0xA9]);
}
#[test]
fn unicode_universe_excludes_surrogates() {
let u = Alphabet::Unicode.universe();
assert!(
u.contains(0xD7FF) && !u.contains(0xD800) && !u.contains(0xDFFF) && u.contains(0xE000)
);
assert_eq!(u.count(), 0x110000 - 0x800);
}
#[test]
fn default_mode_follows_dialect_and_unicode_flag() {
let u = Flags {
u: true,
..Flags::none()
};
assert_eq!(
Alphabet::default_for(Dialect::Ecma, Flags::none()),
Alphabet::Utf16
);
assert_eq!(Alphabet::default_for(Dialect::Ecma, u), Alphabet::Unicode);
assert_eq!(
Alphabet::default_for(Dialect::Python, Flags::none()),
Alphabet::Unicode
);
}
}