mssh 0.0.0

Mssh Simple SHell. Bash interpreter/compiler. Will not support all the functionalities.

use std::fmt;

impl Token {
    pub fn literal_value(&self) -> String {
        match &self.val {
            TokenVal::Var(word) | TokenVal::Const(word) => word.to_owned(),
            _ => unreachable!(
                "{}:{} should not be called in this kind of token",
                self.row, self.col
            ),
        }
    }
}

#[derive(Clone)]
pub struct Token {
    pub row: usize, // line number
    pub col: usize, // position on the line
    pub val: TokenVal,
}

#[derive(Clone)]
pub enum TokenVal {
    Var(String), //
    Const(String),
    SubCmd(Vec<TokenVal>), // a seq of tokens to be interpreted as a single line
    Pipe,                  // |
    OpenPar,               // (
    ClosePar,              // )
    OpenBra,               // {
    CloseBra,              // }
    Less,                  // <
    Equal,                 // =
    More,                  // >
    And,                   // &
                           /*

                               If,     // if
                               Fi,     // fi
                               For,    // for
                               While,  //While
                               Func,   // function
                               Do,     // do
                               Done,   // done
                               Export, // export
                           */
}

pub type Tokens = Vec<Token>;

pub fn print_tokens(tokens: &Tokens) {
    print!("{}", tokens_to_string(tokens));
}

pub fn tokens_to_string(tokens: &Tokens) -> String {
    let len = tokens.len();
    if len == 0 {
        return "(no tokens)".to_owned();
    }

    let mut row = 1;
    let mut res = String::new();
    for token in tokens {
        if token.row > row {
            row = token.row;
            let s = format!(" \n{row}: ");
            res.push_str(&s);
        }
        let s = format!("{} ", token.val);
        res.push_str(&s);
    }
    let nbr = res.len();
    res[1..nbr].to_string()
}

pub fn from_u8(char: u8) -> TokenVal {
    match char as char {
        '(' => TokenVal::OpenPar,
        ')' => TokenVal::ClosePar,
        '{' => TokenVal::OpenBra,
        '}' => TokenVal::CloseBra,
        '|' => TokenVal::Pipe,
        '&' => TokenVal::And,
        '<' => TokenVal::Less,
        '=' => TokenVal::Equal,
        '>' => TokenVal::More,
        _ => panic!("unreacheable should not be called on {}", char as char),
    }
}

impl fmt::Display for Token {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.val {
            TokenVal::Const(s) | TokenVal::Var(s) => {
                write!(f, "'{}' at {}:{}", s, self.row, self.col)
            }
            _ => unreachable!("should only be called on consts or vars"),
        }
    }
}
impl TokenVal {
    fn to_string(&self) -> String {
        match self {
            TokenVal::OpenPar => "(".to_string(),
            TokenVal::ClosePar => ")".to_string(),
            TokenVal::OpenBra => "{".to_string(),
            TokenVal::CloseBra => "}".to_string(),
            TokenVal::Pipe => "|".to_string(),
            TokenVal::And => "&".to_string(),
            TokenVal::Less => "<".to_string(),
            TokenVal::Equal => "=".to_string(),
            TokenVal::More => ">".to_string(),
            _ => panic!("unreachable called on unexpexted token"),
        }
    }

    pub fn var_or_const_from_u8(bytes: &[u8]) -> Self {
        let val = std::str::from_utf8(bytes).unwrap().to_owned();
        if val.contains("$") {
            TokenVal::Var(val)
        } else {
            TokenVal::Const(val)
        }
    }

    pub fn is_function(&self) -> bool {
        if let TokenVal::Const(s) = self {
            return s == "function";
        }
        return false;
    }

    pub fn get_word_content(&self) -> String {
        match self {
            TokenVal::Var(s) | TokenVal::Const(s) => s.to_owned(),
            _ => panic!("get_word_content must only be used after validating the token is a word"),
        }
    }
}

impl fmt::Display for TokenVal {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TokenVal::Var(s) => write!(f, "Var({})", s),
            TokenVal::Const(s) => write!(f, "Const({})", s),
            TokenVal::OpenPar
            | TokenVal::ClosePar
            | TokenVal::OpenBra
            | TokenVal::CloseBra
            | TokenVal::Less
            | TokenVal::Equal
            | TokenVal::More
            | TokenVal::And
            | TokenVal::Pipe => write!(f, "{}", self.to_string()),
            TokenVal::SubCmd(tokens) => {
                let mut res = String::new();
                for tok in tokens {
                    res = format!("{}{}, ", res, tok);
                }
                let n = res.len();
                write!(f, "SubCmd([{}])", &res[..n - 2])
            }
        }
    }
}