tinysh 0.0.2

A tiny shell built for educational purposes
use std::str::Chars;

use super::token::Token;

pub fn is_an_allowed_character(character: char) -> bool {
    return character.is_alphanumeric()
        || character == '&'
        || character == '>'
        || character == '|'
        || character == '-'
        || character == '<'
        || character == '_'
        || character == '~'
        || character == '('
        || character == ')'
        || character == '.'
        || character == '"'
        || character == ' ';
}

#[derive(Clone, PartialEq)]
pub struct Lexer {
    pub str: String,
}

impl Lexer {
    fn lex_int(&self, chars: &mut Chars, current_char: char) -> i64 {
        let a = self.clone().lex_raddix(chars, Some(current_char));
        let err: Result<i64, std::num::ParseIntError> = a.parse();
        match err {
            Err(e) => {
                println!("{e}");
                0
            }
            Ok(t) => t,
        }
    }

    fn lex_raddix(self, chars: &mut Chars, mut current_char: Option<char>) -> String {
        let mut str: String = String::new();
        str += &*current_char.unwrap().to_string();
        let mut peekable = chars.clone().peekable();
        while current_char != None {
            current_char = peekable.next();
            if current_char == None {
                break;
            }
            if !current_char.unwrap().is_ascii_digit() {
                break;
            }
            current_char = chars.next();
            str += &*current_char.unwrap().to_string();
        }
        str
    }

    fn lex_float(self, whole_side: i64, chars: &mut Chars) -> f64 {
        let current_char_options = chars.next();
        let current_char = match current_char_options {
            Some(t) => t,
            None => '0',
        };
        let a = self.lex_raddix(chars, Some(current_char));
        let f = (&*(whole_side.to_string().as_str().to_owned() + "." + a.as_str())).parse();
        if f.is_err() {
            return f64::NAN;
        }
        f.unwrap()
    }
    fn lex_string(self, chars: &mut Chars, mut current_char: Option<char>) -> String {
        let mut str: String = String::new();
        str += &*current_char.unwrap().to_string();
        let mut peekable = chars.clone().peekable();
        while current_char != None {
            current_char = peekable.next();
            if current_char == None {
                break;
            }
            if !(current_char.unwrap().is_alphanumeric() || current_char.unwrap() == '_') {
                break;
            }
            current_char = chars.next();
            str += &*current_char.unwrap().to_string();
        }

        str
    }
    pub fn lex(&self) -> Vec<Token> {
        let mut char_iter = self.str.chars();
        let mut vec = Vec::new();
        let mut char = char_iter.next();
        let mut quote = 0;
        while char != None {
            let v = match char.unwrap() {
                p if !is_an_allowed_character(p) => Token::Null,
                '>' => Token::LeftRedirection,
                '<' => Token::RightRedirection,
                '|' => Token::Pipe,
                '~' => Token::Tilde,
                ')' => Token::RPar,
                '(' => Token::LPar,
                '-' => Token::Dash,
                '"' => {
                    quote += 1;
                    Token::Quote
                }
                ' ' => {
                    if quote % 2 == 1 {
                        Token::Whitespace
                    } else {
                        Token::Null
                    }
                }
                '&' => {
                    let v = vec.pop();
                    match v {
                        None => Token::PreAnd,
                        Some(Token::PreAnd) => Token::And,
                        Some(p) => {
                            vec.push(p);
                            Token::PreAnd
                        }
                    }
                }
                ch => {
                    if ch.is_numeric() {
                        let a = self.lex_int(&mut char_iter, ch);
                        let mut next = char_iter.clone().peekable();
                        match next.peek() {
                            Some(p) if *p == '.' => {
                                char_iter.next();
                                let f = self.clone().lex_float(a, &mut char_iter);
                                Token::Float(f)
                            }
                            _ => Token::Int(a),
                        }
                    } else if ch.is_alphabetic() {
                        let str = self.clone().lex_string(&mut char_iter, Some(ch));
                        match str.as_str() {
                            "false" => Token::Bool(false),
                            "true" => Token::Bool(true),
                            "or" => Token::Or,
                            "and" => Token::And,
                            _ => Token::Identifier(str),
                        }
                    } else if ch == '.' {
                        let f = self.clone().lex_float(0, &mut char_iter);
                        Token::Float(f)
                    } else {
                        Token::Null
                    }
                }
            };
            if v != Token::Null {
                vec.push(v)
            }
            char = char_iter.next();
        }
        let mut final_vec = Vec::new();
        vec.into_iter()
            .filter(|x| &Token::PreAnd != x)
            .for_each(|f| final_vec.push(f));
        final_vec
    }
}

#[cfg(test)]
mod test {
    use crate::lexing::token::Token;

    use super::{is_an_allowed_character, Lexer};

    #[test]
    pub fn test_allowed() {
        let expected = vec![
            'c', 'l', 'm', '&', '|', '>', '<', '-', '_', '0', '~', '(', ')', '.', ' ', '"', ' ',
        ];
        let value = vec![
            'c', 'l', 'm', '&', '|', '>', '<', '-', '_', '0', '~', '^', '(', ')', '%', '.', ' ',
            '$', '"', ' ',
        ];
        let mut final_value = Vec::new();
        value
            .into_iter()
            .filter(|x| is_an_allowed_character(x.clone()))
            .for_each(|f| final_value.push(f));
        assert_eq!(final_value, expected)
    }

    #[test]
    pub fn test_lex_operators() {
        let expected = vec![
            Token::Pipe,
            Token::RPar,
            Token::LPar,
            Token::LeftRedirection,
            Token::RightRedirection,
            Token::Dash,
            Token::Tilde,
            Token::And,
            Token::Quote,
            Token::Whitespace,
            Token::Quote,
        ];
        let value = Lexer {
            str: "|)(><-~&&\" \" ".to_string(),
        };
        assert_eq!(value.lex(), expected);
    }

    #[test]
    pub fn test_lex_int() {
        let expected = vec![Token::Int(10), Token::And, Token::Int(11)];
        let value = Lexer {
            str: "10&&11".to_string(),
        };
        assert_eq!(value.lex(), expected)
    }

    #[test]
    pub fn test_lex_str() {
        let expected = vec![Token::Identifier("test".to_string())];
        let value = Lexer {
            str: "test".to_string(),
        };
        assert_eq!(value.lex(), expected)
    }

    #[test]
    pub fn test_bool() {
        let expected = vec![Token::Bool(true), Token::And, Token::Bool(false)];
        let value = Lexer {
            str: "true&&false".to_string(),
        };
        assert_eq!(value.lex(), expected)
    }

    #[test]
    pub fn test_or_and() {
        let expected = vec![Token::And, Token::Or];
        let value = Lexer {
            str: "and or".to_string(),
        };
        assert_eq!(value.lex(), expected);
    }

    #[test]
    pub fn remove_preand() {
        let expected = vec![];
        let value = Lexer {
            str: "&".to_string(),
        };
        assert_eq!(value.lex(), expected);
    }

    #[test]
    pub fn simple_float() {
        let expected = vec![Token::Float(0.1)];
        let value = Lexer {
            str: ".1".to_string(),
        };
        assert_eq!(value.lex(), expected);
    }

    #[test]
    pub fn harder_float() {
        let expected = vec![Token::Float(1.1)];
        let value = Lexer {
            str: "1.1".to_string(),
        };
        assert_eq!(value.lex(), expected);
    }

    #[test]
    pub fn float_in_expr() {
        let expected = vec![Token::Float(1.1), Token::And, Token::Float(2.2)];
        let value = Lexer {
            str: "1.1 && 2.2".to_string(),
        };
        assert_eq!(value.lex(), expected);
    }
}