use crate::js::chars::{is_line_end, is_space, is_word, unit, unit_at};
pub(crate) const END: usize = 0;
pub(crate) const EOF: usize = 1;
pub(crate) const SPACELINE: usize = 2;
pub(crate) const NL: usize = 3;
pub(crate) const ISHIKAWA: usize = 4;
pub(crate) const SPACELIST: usize = 5;
pub(crate) const TEXT: usize = 6;
pub(crate) const TOKEN_NAMES: [&str; 7] = [
"$end",
"EOF",
"SPACELINE",
"NL",
"ISHIKAWA",
"SPACELIST",
"TEXT",
];
#[derive(Clone, Debug, Default)]
pub(crate) struct Tokens {
pub(crate) types: Vec<usize>,
pub(crate) starts: Vec<usize>,
pub(crate) ends: Vec<usize>,
}
fn keyword(src: &str, at: usize) -> usize {
let word = b"ishikawa-beta";
let mut k = 0;
while k < 13 {
let c = unit(src, at + k);
let w = i32::from(word[k]);
if c != w && (c + 32 != w || !(65..=90).contains(&c)) {
break;
}
k += 1;
}
if k == 13 && !is_word(unit(src, at + 13)) {
return 13;
}
if k >= 8 && !is_word(unit(src, at + 8)) {
8
} else {
0
}
}
pub(crate) fn tokenize(src: &str) -> Tokens {
let mut t = Tokens::default();
let n = src.len();
let mut i = 0;
while i < n {
let mut j = i;
let mut last_break = None;
loop {
let (c, size) = unit_at(src, j);
if !is_space(c) {
break;
}
if c == 10 {
last_break = Some(j);
}
j += size;
}
let (kind, end);
if unit(src, j) == 37 && unit(src, j + 1) == 37 {
kind = SPACELINE;
let mut e = j + 2;
loop {
let (c, size) = unit_at(src, e);
if size == 0 || is_line_end(c) {
break;
}
e += size;
}
end = e;
} else if j == i {
let length = keyword(src, i);
if length > 0 {
kind = ISHIKAWA;
end = i + length;
} else {
kind = TEXT;
end = src[i..].find('\n').map_or(n, |at| i + at);
}
} else if last_break.is_some_and(|b| b > i) {
kind = SPACELINE;
end = last_break.unwrap_or(i) + 1;
} else if last_break == Some(i) {
kind = NL;
end = i + 1;
} else {
kind = SPACELIST;
end = j;
}
t.types.push(kind);
t.starts.push(i);
t.ends.push(end);
i = end;
}
t.types.extend([EOF, END]);
t.starts.extend([n, n]);
t.ends.extend([n, n]);
t
}