use super::types::*;
pub fn tokenize(function: String) -> LexResult {
let mut rhs: Vec<Token> = vec![];
let chars = function.to_lowercase().chars().collect::<Vec<_>>();
let mut i = 0;
let simple_token = [
('+', TokenType::Operator),
('-', TokenType::Operator),
('/', TokenType::Operator),
('*', TokenType::Operator),
('(', TokenType::ParaOpen),
(')', TokenType::ParaClose),
(',', TokenType::Comma),
];
while i < chars.len() {
if chars[i].is_whitespace() {
i += 1;
continue;
}
if let Some((token, n)) = try_number(&chars, i) {
rhs.push(token);
i += n;
continue;
}
if let Some((token, n)) = try_identifiers(&chars, i) {
rhs.push(token);
i += n;
continue;
}
if let Some(t_type) = simple_token
.iter()
.find(|(ch, _)| *ch == chars[i])
.map(|p| p.1)
{
rhs.push(Token::new(t_type, chars[i].to_string(), i));
i += 1;
continue;
}
rhs.push(Token::new(
TokenType::Unimplemented,
chars[i].to_string(),
i,
));
i += 1;
}
LexResult { rhs }
}
fn try_number(chars: &[char], mut i: usize) -> Option<(Token, usize)> {
let start = i;
if !chars[i].is_ascii_digit() {
return None;
}
while i < chars.len() && (chars[i].is_ascii_digit() || chars[i] == '.') {
i += 1;
}
let token = Token {
token_type: TokenType::Number,
text: chars[start..i].iter().collect(),
position: start,
};
Some((token, i - start))
}
fn try_identifiers(chars: &[char], mut i: usize) -> Option<(Token, usize)> {
let start = i;
if !chars[i].is_ascii_alphabetic() {
return None;
}
while i < chars.len() && chars[i].is_ascii_alphanumeric() {
i += 1;
}
let token = Token {
token_type: TokenType::Identifier,
text: chars[start..i].iter().collect(),
position: start,
};
Some((token, i - start))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_try_number() {
assert_eq!(
try_number(&['1', '2'], 0),
Some((Token::new(TokenType::Number, "12".to_string(), 0), 2))
);
assert_eq!(
try_number(&['1', '.', '2'], 0),
Some((Token::new(TokenType::Number, "1.2".to_string(), 0), 3))
);
assert_eq!(
try_number(&['1', '1', '.', '2'], 0),
Some((Token::new(TokenType::Number, "11.2".to_string(), 0), 4))
);
assert_eq!(
try_number(&['1', '.', '2'], 2),
Some((Token::new(TokenType::Number, "2".to_string(), 2), 1))
);
assert_eq!(
try_number(&['1', '+', '2'], 0),
Some((Token::new(TokenType::Number, "1".to_string(), 0), 1))
);
assert_eq!(
try_number(&['1', ' ', '2'], 0),
Some((Token::new(TokenType::Number, "1".to_string(), 0), 1))
);
}
}