use super::bracket::{is_close_bracket, is_open_bracket, matching_close};
use super::delimiter::is_delimiter;
use super::keyword::{self, Keyword};
use super::token::{Token, TokenKind};
use crate::options::Options;
pub(crate) fn tokenize(input: &str, _options: &Options) -> Vec<Token> {
let chars: Vec<char> = input.chars().collect();
let mut pos = 0usize;
let mut tokens = Vec::new();
while let Some(token) = next_token(&chars, &mut pos) {
tokens.push(token);
}
process_tokens(&mut tokens);
tokens
}
fn is_text_char(ch: char) -> bool {
!is_open_bracket(ch) && !is_close_bracket(ch) && !is_delimiter(ch)
}
fn is_word_boundary(ch: char) -> bool {
!is_text_char(ch)
}
fn next_token(chars: &[char], pos: &mut usize) -> Option<Token> {
let ch = chars.get(*pos).copied()?;
if is_open_bracket(ch) {
return Some(Token {
kind: TokenKind::OpenBracket,
value: take(chars, pos, 1),
..Token::default()
});
}
if is_close_bracket(ch) {
return Some(Token {
kind: TokenKind::CloseBracket,
value: take(chars, pos, 1),
..Token::default()
});
}
if is_delimiter(ch) {
return Some(Token {
kind: TokenKind::Delimiter,
value: take(chars, pos, 1),
..Token::default()
});
}
if let Some((value, keyword)) = take_keyword(chars, pos) {
return Some(Token {
kind: TokenKind::Keyword,
value,
keyword: Some(keyword),
..Token::default()
});
}
Some(Token {
kind: TokenKind::Text,
value: take_text(chars, pos),
..Token::default()
})
}
fn process_tokens(tokens: &mut [Token]) {
let mut expected_close: Option<char> = None;
let mut position: usize = 0;
for token in tokens.iter_mut() {
match token.kind {
TokenKind::OpenBracket if expected_close.is_none() => {
expected_close = token.value.chars().next().and_then(matching_close);
}
TokenKind::CloseBracket if expected_close == token.value.chars().next() => {
expected_close = None;
}
TokenKind::OpenBracket | TokenKind::CloseBracket => {}
_ => token.is_enclosed = expected_close.is_some(),
}
token.position = position;
position += token.value.chars().count();
if token.kind == TokenKind::Text {
token.is_number = token.value.chars().all(|c| c.is_ascii_digit());
}
}
}
fn take(chars: &[char], pos: &mut usize, n: usize) -> String {
let taken: String = chars.iter().skip(*pos).take(n).collect();
*pos += taken.chars().count();
taken
}
fn take_text(chars: &[char], pos: &mut usize) -> String {
let n = chars
.iter()
.skip(*pos)
.take_while(|&&c| is_text_char(c))
.count();
take(chars, pos, n)
}
fn take_keyword(chars: &[char], pos: &mut usize) -> Option<(String, Keyword)> {
let key = find_key(chars, *pos)?;
let n = key.chars().count();
let keyword = keyword::get(&key)?;
if !is_keyword_boundary(chars, pos.saturating_add(n), &keyword) {
return None;
}
Some((take(chars, pos, n), keyword))
}
fn find_key(chars: &[char], start: usize) -> Option<String> {
let remaining_len = chars.len().saturating_sub(start);
let mut key = None;
for n in 1..=remaining_len {
let prefix: String = chars.iter().skip(start).take(n).collect();
if keyword::get(&prefix).is_some() {
key = Some(prefix.clone());
}
if !keyword::has_prefix(&prefix) {
break;
}
}
key
}
fn is_keyword_boundary(chars: &[char], start: usize, keyword: &Keyword) -> bool {
if keyword.subword {
return true;
}
let Some(next) = chars.get(start).copied() else {
return true;
};
if is_word_boundary(next) {
return true;
}
if keyword.prefix_for_number {
return next.is_ascii_digit();
}
if keyword.prefix_for_other {
return find_key(chars, start).is_some();
}
false
}