use unicode_ident::{is_xid_continue, is_xid_start};
#[inline]
pub fn is_id_start(ch: char) -> bool {
is_xid_start(ch) || ch == '_' || ch == '$' || is_id_start_not_xid(ch)
}
#[inline]
pub fn is_id_continue(ch: char) -> bool {
is_xid_continue(ch) || ch == '$' || is_id_start_not_xid(ch)
}
#[inline]
fn is_id_start_not_xid(ch: char) -> bool {
matches!(
ch,
'\u{037A}' | '\u{0E33}' | '\u{0EB3}' | '\u{309B}'..='\u{309C}' | '\u{FC5E}'..='\u{FC63}' | '\u{FDFA}'..='\u{FDFB}' | '\u{FE70}' | '\u{FE72}' | '\u{FE74}' | '\u{FE76}'
| '\u{FE78}' | '\u{FE7A}' | '\u{FE7C}' | '\u{FE7E}' | '\u{FF9E}'..='\u{FF9F}' )
}
#[cfg(test)]
mod tests {
use super::*;
const ID_START_NOT_XID: &[char] = &[
'\u{037A}', '\u{0E33}', '\u{0EB3}', '\u{309B}', '\u{309C}', '\u{FC5E}', '\u{FC5F}',
'\u{FC60}', '\u{FC61}', '\u{FC62}', '\u{FC63}', '\u{FDFA}', '\u{FDFB}', '\u{FE70}',
'\u{FE72}', '\u{FE74}', '\u{FE76}', '\u{FE78}', '\u{FE7A}', '\u{FE7C}', '\u{FE7E}',
'\u{FF9E}', '\u{FF9F}',
];
const ID_CONTINUE_NOT_XID: &[char] = &[
'\u{037A}', '\u{309B}', '\u{309C}', '\u{FC5E}', '\u{FC5F}', '\u{FC60}', '\u{FC61}',
'\u{FC62}', '\u{FC63}', '\u{FDFA}', '\u{FDFB}', '\u{FE70}', '\u{FE72}', '\u{FE74}',
'\u{FE76}', '\u{FE78}', '\u{FE7A}', '\u{FE7C}', '\u{FE7E}',
];
#[test]
fn id_start_includes_non_xid_code_points() {
for &ch in ID_START_NOT_XID {
assert!(
is_id_start(ch),
"ID_Start should accept U+{:04X}",
ch as u32
);
assert!(
is_id_continue(ch),
"ID_Continue should accept U+{:04X}",
ch as u32
);
assert!(
!is_xid_start(ch),
"U+{:04X} unexpectedly entered XID_Start",
ch as u32
);
}
}
#[test]
fn id_continue_gap_is_exactly_the_non_xid_continue_set() {
for &ch in ID_CONTINUE_NOT_XID {
assert!(
!is_xid_continue(ch),
"U+{:04X} unexpectedly entered XID_Continue",
ch as u32
);
}
for ch in [' ', '\u{2E2F}', '0', '!'] {
assert!(
!is_id_start(ch),
"U+{:04X} must not be an identifier start",
ch as u32
);
}
assert!(is_id_continue('0') && !is_id_start('0'));
}
}