use crate::parser::errors::ParseError;
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
Command(String),
Pipe,
PipeErr,
And,
Or,
Redirect,
Append,
Word(String),
}
pub fn lex(input: &str) -> Result<Vec<Token>, ParseError> {
let raw_tokens = split_respecting_parens(input);
let tokens = raw_tokens.into_iter().map(|s| classify(&s)).collect();
Ok(tokens)
}
fn classify(s: &str) -> Token {
match s {
"|" => Token::Pipe,
"|&" => Token::PipeErr,
"&&" => Token::And,
"||" => Token::Or,
">" => Token::Redirect,
">>" => Token::Append,
_ if s.starts_with('/') => Token::Command(s.to_string()),
_ => Token::Word(s.to_string()),
}
}
fn split_respecting_parens(input: &str) -> Vec<String> {
let mut tokens = Vec::new();
let mut current = String::new();
let mut depth: usize = 0;
for ch in input.chars() {
match ch {
'(' => {
depth += 1;
current.push(ch);
}
')' => {
depth = depth.saturating_sub(1);
current.push(ch);
}
c if c.is_ascii_whitespace() && depth == 0 => {
if !current.is_empty() {
tokens.push(std::mem::take(&mut current));
}
}
_ => {
current.push(ch);
}
}
}
if !current.is_empty() {
tokens.push(current);
}
tokens
}