#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum Class {
Space,
Newline,
IdentStart,
Digit,
Dot,
Quote,
Apostrophe,
Slash,
Backslash,
Punct,
Other,
}
pub(crate) static CLASS: [Class; 256] = build();
const fn build() -> [Class; 256] {
let mut t = [Class::Other; 256];
let mut i = 0;
while i < 256 {
let b = i as u8;
t[i] = match b {
b' ' | b'\t' | 0x0B | 0x0C => Class::Space,
b'\n' | b'\r' => Class::Newline,
b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'$' => Class::IdentStart,
0x80..=0xFF => Class::IdentStart,
b'0'..=b'9' => Class::Digit,
b'.' => Class::Dot,
b'"' => Class::Quote,
b'\'' => Class::Apostrophe,
b'/' => Class::Slash,
b'\\' => Class::Backslash,
b'[' | b']' | b'(' | b')' | b'{' | b'}' | b'-' | b'+' | b'&' | b'*' | b'~' | b'!'
| b'%' | b'<' | b'>' | b'=' | b'^' | b'|' | b'?' | b':' | b';' | b',' | b'#' => {
Class::Punct
}
_ => Class::Other,
};
i += 1;
}
t
}
#[inline]
pub(crate) const fn is_ident_continue(b: u8) -> bool {
matches!(CLASS[b as usize], Class::IdentStart | Class::Digit)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_table_agrees_with_what_c_says_about_each_byte() {
assert_eq!(CLASS[b'i' as usize], Class::IdentStart);
assert_eq!(CLASS[b'_' as usize], Class::IdentStart);
assert_eq!(CLASS[b'$' as usize], Class::IdentStart);
assert_eq!(CLASS[0xC3], Class::IdentStart);
assert_eq!(CLASS[b'7' as usize], Class::Digit);
assert_eq!(CLASS[b' ' as usize], Class::Space);
assert_eq!(CLASS[b'\n' as usize], Class::Newline);
assert_eq!(CLASS[b'`' as usize], Class::Other);
assert_eq!(CLASS[0], Class::Other);
assert_eq!(CLASS[b'@' as usize], Class::Other);
}
#[test]
fn identifier_continuation_takes_digits_but_no_punctuation() {
assert!(is_ident_continue(b'x'));
assert!(is_ident_continue(b'0'));
assert!(!is_ident_continue(b'-'));
assert!(!is_ident_continue(b' '));
}
}