bomlamaal 0.1.0

Bømlamål as a programming language.
use super::token::Token;
use super::types::Type;
use super::value::Value;

pub struct Lexer {
    text: String,
    position: usize,
    current_char: Option<char>,
}

impl Lexer {
    pub fn new(text: String) -> Self {
        Self {
            current_char: Some(
                text.chars()
                    .nth(0)
                    .expect("Input to Lexer should have length > 0 "),
            ),
            text: text,
            position: 0,
        }
    }
    fn peek(&mut self) -> Option<char> {
        self.text.chars().nth(self.position + 1)
    }

    fn advance(&mut self) {
        self.position = self.position + 1;

        if self.position > (self.text.chars().count() - 2) {
            self.current_char = None
        } else {
            self.current_char = Some(
                self.text
                    .chars()
                    .nth(self.position)
                    .expect("Input to Interpreter should have length > 0 "),
            )
        }
    }

    fn id(&mut self) -> Token {
        let mut _id = String::new();
        while let Some(_char) = self.current_char {
            if !_char.is_alphabetic() {
                break;
            }
            _id.push(_char);
            self.advance();
        }

        if _id == "BYRJAFARR" {
            return Token::new(Type::BEGIN, None);
        }

        if _id == "SLUTTADÅ" {
            return Token::new(Type::END, None);
        }

        return Token::new(Type::VARIABLE, Some(Box::new(Value::STRING(_id))));
    }

    fn skip_whitespace(&mut self) {
        while self.current_char.is_some() && self.current_char.unwrap().is_whitespace() {
            self.advance();
        }
    }

    fn integer(&mut self) -> i32 {
        let mut result = String::new();

        while self.current_char.is_some() && self.current_char.unwrap().is_numeric() {
            result.push(self.current_char.unwrap());
            self.advance();
        }

        return result.parse::<i32>().unwrap();
    }

    pub fn get_next_token(&mut self) -> Token {
        /* LEXICAL ANALYZER (aka. TOKENIZER)

        The purpose of this method is to break text into tokens.
        */

        while let Some(_char) = self.current_char {
            if _char.is_whitespace() {
                self.skip_whitespace();
                continue;
            }

            if _char.is_numeric() {
                return Token::new(
                    Type::INTEGER,
                    Some(Box::new(Value::INTEGER(self.integer()))),
                );
            }

            if _char == ':' && self.peek() == Some('=') {
                self.advance();
                self.advance();
                return Token::new(Type::ASSIGN, None);
            }

            if _char == ';' {
                self.advance();
                return Token::new(Type::SEMI, None);
            }

            if _char == '.' {
                self.advance();
                return Token::new(Type::DOT, None);
            }

            if _char == ';' {
                self.advance();
                return Token::new(Type::SEMI, None);
            }

            if _char == '+' {
                self.advance();
                return Token::new(Type::PLUS, None);
            }

            if _char == '-' {
                self.advance();
                return Token::new(Type::MINUS, None);
            }

            if _char == '/' {
                self.advance();
                return Token::new(Type::DIVIDE, None);
            }

            if _char == '*' {
                self.advance();
                return Token::new(Type::MULTIPLY, None);
            }

            if _char == '(' {
                self.advance();
                return Token::new(Type::LPAREN, None);
            }

            if _char == ')' {
                self.advance();
                return Token::new(Type::RPAREN, None);
            }

            if _char.is_alphabetic() {
                return self.id();
            }

            self.error(_char);
        }

        Token::empty()
    }

    fn error(&self, _char: char) {
        panic!("Intepreter crashed due to invalid input, {}.", _char);
    }
}