1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
//! For making notable symbols and words out of text.

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Operator {
    Plus,
    Minus,
    Star,
    Slash,
    Percent,
    Caret,
    LParen,
    RParen,
}
use self::Operator::*;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Function {
    Sqrt,
    Sin,
    Cos,
    Tan,
    Log,
}
use self::Function::*;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Token {
    Number(f64),
    Operator(Operator),
    Function(Function),
}

#[derive(Debug, Clone, PartialEq)]
pub enum LexerError {
    InvalidCharacter(char),
    InvalidNumber(String),
    InvalidIdentifier(String),
}

/// Turn a string into a vector of tokens.
pub fn tokenize(input: &str) -> Result<Vec<Token>, LexerError> {
    let mut tokens = Vec::<Token>::new();

    let chars: Vec<char> = input.chars().collect();

    let mut i = 0usize;
    while i < chars.len() {
        match chars[i] {
            '+' => tokens.push(Token::Operator(Plus)),
            '-' => tokens.push(Token::Operator(Minus)),
            '*' | '•' | '×' => tokens.push(Token::Operator(Star)),
            '/' | '÷' => tokens.push(Token::Operator(Slash)),
            '%' => tokens.push(Token::Operator(Percent)),
            '^' => tokens.push(Token::Operator(Caret)),
            '(' => tokens.push(Token::Operator(LParen)),
            ')' => tokens.push(Token::Operator(RParen)),
            '√' => tokens.push(Token::Function(Sqrt)),
            c => {
                if c.is_whitespace() {
                    i += 1;
                    continue;
                } else if c.is_digit(10) || c == '.' {
                    let mut number_string = c.to_string(); // Like creating a new string and pushing the character.
                    
                    i += 1;
                    while i < chars.len() && (chars[i].is_digit(10) || chars[i] == '.') {
                        number_string.push(chars[i]);
                        i += 1;
                    }

                    match number_string.parse::<f64>() {
                        Ok(num) => tokens.push(Token::Number(num)),
                        _ => return Err(LexerError::InvalidNumber(number_string)),
                    }

                    continue; // We i += 1 at end of latest while.
                } else if c.is_alphabetic() {
                    let mut full_identifier = c.to_string();

                    i += 1; // Step over first character of identifier.
                    // While we're still reading alphabetical characters.
                    while i < chars.len() && chars[i].is_alphabetic() {
                        full_identifier.push(chars[i]);
                        i += 1;
                    }

                    match &full_identifier.to_lowercase()[..] {
                        "sqrt" => tokens.push(Token::Function(Sqrt)),
                        "sin" => tokens.push(Token::Function(Sin)),
                        "cos" => tokens.push(Token::Function(Cos)),
                        "tan" => tokens.push(Token::Function(Tan)),
                        "log" => tokens.push(Token::Function(Log)),
                        _ => return Err(LexerError::InvalidIdentifier(full_identifier)),
                    }

		            continue;
                } else {
                    return Err(LexerError::InvalidCharacter(c));
                }
            }
        }
        i += 1;
    }
    
    Ok(tokens)
}