pub const TOKEN_BASE: u32 = 0xe100;
pub const FIRST_TOKEN_BYTE: u8 = 0x84;
pub const LAST_TOKEN_BYTE: u8 = 0xa2;
#[inline]
pub fn is_token_char(c: char) -> bool {
let u = c as u32;
(TOKEN_BASE + FIRST_TOKEN_BYTE as u32..=TOKEN_BASE + LAST_TOKEN_BYTE as u32).contains(&u)
}
#[inline]
pub fn token_char_from_byte(b: u8) -> Option<char> {
(FIRST_TOKEN_BYTE..=LAST_TOKEN_BYTE)
.contains(&b)
.then(|| char::from_u32(TOKEN_BASE + b as u32).expect("PUA scalar"))
}
#[inline]
pub fn token_byte(c: char) -> Option<u8> {
is_token_char(c).then(|| (c as u32 - TOKEN_BASE) as u8)
}
#[inline]
pub fn itok_char(c: char) -> bool {
token_byte(c).is_some_and(crate::ported::ztype_h::itok)
}
pub fn untokenize_bytes(b: &[u8]) -> Vec<u8> {
let ztokens = crate::ported::glob::ZTOKENS.as_bytes();
let nularg = token_byte(crate::ported::zsh_h::Nularg).expect("Nularg is a token");
let mut out = Vec::with_capacity(b.len());
let mut i = 0;
while i < b.len() {
if let [0xee, 0x86, t, ..] = b[i..] {
if crate::ported::ztype_h::itok(t) {
if t != nularg {
out.push(ztokens[(t - FIRST_TOKEN_BYTE) as usize]);
}
i += 3;
continue;
}
}
out.push(b[i]);
i += 1;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn token_chars_round_trip_and_real_text_is_not_a_token() {
for b in FIRST_TOKEN_BYTE..=LAST_TOKEN_BYTE {
let c = token_char_from_byte(b).unwrap();
assert!(is_token_char(c));
assert_eq!(token_byte(c), Some(b));
}
for c in ['\u{a0}', '\u{a1}', '\u{a2}', '\u{85}', 'a', '$', '\u{e083}', '\u{e0a0}', '\u{e0a1}', '\u{e0a2}', '\u{e183}', '\u{e1a3}'] {
assert!(!is_token_char(c), "{c:?}");
}
assert_eq!(token_char_from_byte(0x83), None);
assert_eq!(token_char_from_byte(0xa3), None);
}
#[test]
fn constants_are_the_pua_images_of_the_c_bytes() {
use crate::ported::zsh_h::*;
assert_eq!(token_byte(Pound), Some(0x84));
assert_eq!(token_byte(Stringg), Some(0x85));
assert_eq!(token_byte(Bang), Some(0x9c));
assert_eq!(token_byte(Bnullkeep), Some(0xa0));
assert_eq!(token_byte(Nularg), Some(0xa1));
assert_eq!(token_byte(Marker), Some(0xa2));
}
}