calculator_parser/
lib.rs

1enum Operator {
2    Add,
3    Multiply,
4    Divide,
5    Subtract,
6    Exponent,
7}
8enum Token {
9    Number(f64),
10    Operator(Operator),
11}
12struct OperationVector {
13    add: Option<fn(&Vec<Token>, &mut usize, &mut Vec<Token>)>,
14    subtract: Option<fn(&Vec<Token>, &mut usize, &mut Vec<Token>)>,
15    divide: Option<fn(&Vec<Token>, &mut usize, &mut Vec<Token>)>,
16    multiply: Option<fn(&Vec<Token>, &mut usize, &mut Vec<Token>)>,
17    exponent: Option<fn(&Vec<Token>, &mut usize, &mut Vec<Token>)>,
18}
19
20#[derive(Debug)]
21pub struct CalculationError {
22    message: String
23}
24
25fn process_pass(result_array: &Vec<Token>, operations: OperationVector) -> Vec<Token> {
26    let mut index: usize = 0;
27    let mut new_array: Vec<Token> = Vec::new();
28    while index < result_array.len() {
29        match result_array[index] {
30            Token::Number(number) => new_array.push(Token::Number(number)),
31            Token::Operator(Operator::Add) => {
32                match operations.add {
33                    Some(func) => func(result_array, &mut index, &mut new_array),
34                    None => new_array.push(Token::Operator(Operator::Add)),
35                };
36            }
37            Token::Operator(Operator::Subtract) => {
38                match operations.subtract {
39                    Some(func) => func(result_array, &mut index, &mut new_array),
40                    None => new_array.push(Token::Operator(Operator::Subtract)),
41                };
42            }
43            Token::Operator(Operator::Multiply) => {
44                match operations.multiply {
45                    Some(func) => func(result_array, &mut index, &mut new_array),
46                    None => new_array.push(Token::Operator(Operator::Multiply)),
47                };
48            }
49            Token::Operator(Operator::Divide) => {
50                match operations.divide {
51                    Some(func) => func(result_array, &mut index, &mut new_array),
52                    None => new_array.push(Token::Operator(Operator::Divide)),
53                };
54            }
55            Token::Operator(Operator::Exponent) => {
56                match operations.exponent {
57                    Some(func) => func(result_array, &mut index, &mut new_array),
58                    None => new_array.push(Token::Operator(Operator::Exponent)),
59                };
60            }
61        }
62        index += 1
63    }
64    return new_array;
65}
66// Resolve a string of calculations to the resulting number
67pub fn calculate(to_calculate: &str) -> Result<f64,CalculationError> {
68    let mut result_array: Vec<Token> = Vec::new();
69    let mut number = String::from("");
70    for character in to_calculate.chars() {
71        if let '+' | '-' | '*' | '/' | '^' = character {
72            result_array.push(Token::Number(number.clone().parse::<f64>().unwrap()));
73            number = "".to_string();
74        }
75        match character {
76            '*' => result_array.push(Token::Operator(Operator::Multiply)),
77            '/' => result_array.push(Token::Operator(Operator::Divide)),
78            '-' => result_array.push(Token::Operator(Operator::Subtract)),
79            '+' => result_array.push(Token::Operator(Operator::Add)),
80            '^' => result_array.push(Token::Operator(Operator::Exponent)),
81            _ => {
82                if character.is_ascii_digit() || character == '.' {
83                    number.push(character)
84                } else if !character.is_ascii_whitespace() {
85                    return Err(CalculationError{
86                        message: String::from(format!("Invalid character: {}",character))
87                    })
88                }
89            }
90        }
91    }
92    if number != "".to_string() {
93        result_array.push(Token::Number(number.clone().parse::<f64>().unwrap()));
94    }
95    result_array = process_pass(
96        &result_array,
97        OperationVector {
98            add: None,
99            subtract: None,
100            multiply: None,
101            divide: None,
102            exponent: Some(
103                |result_array: &Vec<Token>, index: &mut usize, new_array: &mut Vec<Token>| {
104                    if let Token::Number(number) = result_array[*index + (1 as usize)] {
105                        if let Token::Number(last_number) = *new_array.last().unwrap() {
106                            *new_array.last_mut().unwrap() =
107                                Token::Number(f64::powf(last_number, number));
108                            *index += 1 as usize;
109                        }
110                    }
111                },
112            ),
113        },
114    );
115    result_array = process_pass(
116        &result_array,
117        OperationVector {
118            add: None,
119            subtract: None,
120            multiply: Some(
121                |result_array: &Vec<Token>, index: &mut usize, new_array: &mut Vec<Token>| {
122                    if let Token::Number(number) = result_array[*index + (1 as usize)] {
123                        if let Token::Number(last_number) = *new_array.last().unwrap() {
124                            *new_array.last_mut().unwrap() = Token::Number(last_number * number);
125                            *index += 1 as usize;
126                        }
127                    }
128                },
129            ),
130            divide: Some(
131                |result_array: &Vec<Token>, index: &mut usize, new_array: &mut Vec<Token>| {
132                    if let Token::Number(number) = result_array[*index + (1 as usize)] {
133                        if let Token::Number(last_number) = *new_array.last().unwrap() {
134                            *new_array.last_mut().unwrap() = Token::Number(last_number / number);
135                            *index += 1 as usize;
136                        }
137                    }
138                },
139            ),
140            exponent: None,
141        },
142    );
143    result_array = process_pass(
144        &result_array,
145        OperationVector {
146            add: Some(
147                |result_array: &Vec<Token>, index: &mut usize, new_array: &mut Vec<Token>| {
148                    if let Token::Number(number) = result_array[*index + (1 as usize)] {
149                        if let Token::Number(last_number) = *new_array.last().unwrap() {
150                            *new_array.last_mut().unwrap() = Token::Number(last_number + number);
151                            *index += 1 as usize;
152                        }
153                    }
154                },
155            ),
156            subtract: Some(
157                |result_array: &Vec<Token>, index: &mut usize, new_array: &mut Vec<Token>| {
158                    if let Token::Number(number) = result_array[*index + (1 as usize)] {
159                        if let Token::Number(last_number) = *new_array.last().unwrap() {
160                            *new_array.last_mut().unwrap() = Token::Number(last_number - number);
161                            *index += 1 as usize;
162                        }
163                    }
164                },
165            ),
166            divide: None,
167            multiply: None,
168            exponent: None,
169        },
170    );
171    if let Token::Number(value) = result_array[0] {
172        return Ok(value);
173    }
174    return Ok(0.0);
175}
176#[cfg(test)] // Only compiles when running tests
177mod tests { // Separates tests from code
178    use crate::calculate;
179    #[test]
180    fn test_basic() {
181        match calculate("2+2"){
182            Ok(value)=>{assert_eq!(value,4.0)},
183            Err(value)=>{assert!(false,value)}
184        }
185    }
186    #[test]
187    fn test_all() {
188        match calculate("2+7*4^2-5"){
189            Ok(value)=>{assert_eq!(value,109.0)},
190            Err(value)=>{assert!(false,value)}
191        }
192    }
193    #[test]
194    fn test_decimal() {
195        match calculate("2.1*2+5.35"){
196            Ok(value)=>{assert_eq!(value,9.55)},
197            Err(value)=>{assert!(false,value)}
198        }
199    }
200    #[test]
201    fn test_invalid() {
202        match calculate("invalid"){
203            Ok(value)=>{assert!(false,"Invalid character returned number: {}",value)},
204            Err(value)=>{assert!(true,value)}
205        }
206    }
207}