dc_ock/
lib.rs

1use std::{collections::VecDeque, fmt};
2use CalcType::{Add, Divide, Multiply, Power, Print, Subtract, Val};
3#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
4/// enum CalcType is a type containing operations used in the calculator.
5pub enum CalcType {
6    Add,
7    Subtract,
8    Multiply,
9    Divide,
10    Power,
11    Print,
12    Val(f64),
13}
14#[derive(Debug, Clone)]
15pub struct EvaluationError {
16    pub message: String,
17}
18
19impl fmt::Display for EvaluationError {
20    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21        write!(f, "Failed evaluation with reason: {}", self.message)
22    }
23}
24
25/// str_to_calc_type converts a string to an optional `CalcType`.
26pub fn str_to_calc_type(string: &str) -> Option<CalcType> {
27    let as_int = string.parse::<f64>();
28    let result = match as_int {
29        Ok(x) => Some(Val(x)),
30        Err(_) => None,
31    };
32
33    if result.is_some() {
34        return result;
35    }
36
37    match string {
38        "+" => Some(Add),
39        "-" => Some(Subtract),
40        "*" => Some(Multiply),
41        "/" => Some(Divide),
42        "^" => Some(Power),
43        "p" => Some(Print),
44
45        _ => None,
46    }
47}
48
49/// `eval` takes a `&str` and a `&mut VecDeque<f64>`, evaluates the expression,
50/// and prints the result, pushing the results onto the stack of type
51/// VecDeque<f64> provided as a parameter.
52///
53/// It does not return an error, but will instead silently fail if the expression is invalid.
54/// Additionally, it mutates the stack parameter, rather than returning a new one.
55/// Even if the stack is empty, it will still evaluate the expression.
56/// For this reason, eval is unsafe, and safe_eval or safe_eval_with_stack should be used.
57///
58pub fn eval(input: &str, stack: &mut VecDeque<f64>) {
59    //// Create a mutable copy of the inputted stack
60    //// let mut stack = stack_in.clone();
61
62    // Split the input into tokens.
63    let toks = input.split(' ').collect::<Vec<&str>>();
64    let mut ops: VecDeque<CalcType> = VecDeque::new();
65
66    for tok in &toks {
67        let x: CalcType = str_to_calc_type(tok).unwrap();
68
69        match x {
70            Add | Divide | Multiply | Power | Subtract | Print => ops.push_back(x),
71
72            Val(x_) => stack.push_back(x_),
73        }
74    }
75
76    for op in &ops {
77        match op {
78            Add => {
79                let y = &stack.pop_back().unwrap_or(0.0);
80                let x = &stack.pop_back().unwrap_or(0.0);
81                &stack.push_back(x + y)
82            }
83            Subtract => {
84                let y = &stack.pop_back().unwrap_or(0.0);
85                let x = &stack.pop_back().unwrap_or(0.0);
86                &stack.push_back(x - y)
87            }
88            Multiply => {
89                let y = &stack.pop_back().unwrap_or(0.0);
90                let x = &stack.pop_back().unwrap_or(0.0);
91                &stack.push_back(x * y)
92            }
93            Divide => {
94                let y = &stack.pop_back().unwrap_or(0.0);
95                let x = &stack.pop_back().unwrap_or(0.0);
96                &stack.push_back(x / y)
97            }
98            Power => {
99                let y = &stack.pop_back().unwrap_or(0.0);
100                let x = &stack.pop_back().unwrap_or(0.0);
101
102                let result = x.powf(*y);
103                &stack.push_back(result)
104            }
105            Print => &{ println!("{:#?}", stack.iter().last()) },
106            Val(_) => panic!("Unexpected value in the operator stack!"),
107        };
108    }
109
110    println!("{}", stack.iter().last().unwrap_or(&0.0));
111}
112
113/// `safe_eval` takes a `&str` evaluates the expression, returning the
114/// resulting stack as a Result if the expression is valid, or an Err if the
115/// expression is invalid or otherwise cannot be evaluated.
116///
117/// safe_eval is useful for testing the validity of an expression, and is safer
118/// than eval, as it does not mutate any inputted values.
119///
120/// # Examples  
121///
122/// ```
123/// use dc-ock::safe_eval;
124///
125/// fn main() {
126///     let expr = "1 2 +";
127///     match safe_eval(expr) {
128///         Ok(x) => println!("{:?}", x), // prints [3.0]
129///         Err(e) => println!("{}", e),  // prints an error message
130///     }
131/// }  
132/// ```
133pub fn safe_eval(input: &str) -> Result<VecDeque<f64>, EvaluationError> {
134    // Initialise the stack
135    let mut stack: VecDeque<f64> = VecDeque::new();
136
137    // Split the input into tokens.
138    let toks = input.split(' ').collect::<Vec<&str>>();
139    let mut ops: VecDeque<CalcType> = VecDeque::new();
140
141    for tok in &toks {
142        let x: CalcType = match str_to_calc_type(tok) {
143            Some(x) => x,
144            None => {
145                return Err(EvaluationError {
146                    message: format!("Invalid token: {}", tok),
147                })
148            }
149        };
150
151        match x {
152            Add | Divide | Multiply | Power | Subtract | Print => ops.push_back(x),
153
154            Val(x_) => stack.push_back(x_),
155        }
156    }
157
158    for op in &ops {
159        match op {
160            Add => {
161                let y = &stack.pop_back().ok_or(EvaluationError {
162                    message: "Stack is empty!".to_string(),
163                })?;
164                let x = &stack.pop_back().ok_or(EvaluationError {
165                    message: "Stack is empty!".to_string(),
166                })?;
167                &stack.push_back(x + y)
168            }
169            Subtract => {
170                let y = &stack.pop_back().ok_or(EvaluationError {
171                    message: "Stack is empty!".to_string(),
172                })?;
173                let x = &stack.pop_back().ok_or(EvaluationError {
174                    message: "Stack is empty!".to_string(),
175                })?;
176                &stack.push_back(x - y)
177            }
178            Multiply => {
179                let y = &stack.pop_back().ok_or(EvaluationError {
180                    message: "Stack is empty!".to_string(),
181                })?;
182                let x = &stack.pop_back().ok_or(EvaluationError {
183                    message: "Stack is empty!".to_string(),
184                })?;
185                &stack.push_back(x * y)
186            }
187            Divide => {
188                let y = &stack.pop_back().ok_or(EvaluationError {
189                    message: "Stack is empty!".to_string(),
190                })?;
191                let x = &stack.pop_back().ok_or(EvaluationError {
192                    message: "Stack is empty!".to_string(),
193                })?;
194                &stack.push_back(x / y)
195            }
196            Power => {
197                let y = &stack.pop_back().ok_or(EvaluationError {
198                    message: "Stack is empty!".to_string(),
199                })?;
200                let x = &stack.pop_back().ok_or(EvaluationError {
201                    message: "Stack is empty!".to_string(),
202                })?;
203
204                let result = x.powf(*y);
205                &stack.push_back(result)
206            }
207            Print => &{ println!("{:#?}", stack.iter().last()) },
208            Val(_) => panic!("Unexpected value in the operator stack!"),
209        };
210    }
211
212    Ok(stack)
213}
214
215/// `safe_eval_with_stack` takes an `&str` expression, and a stack `VecDeque<f64>`,
216/// and evaluates the expression, returning the resulting stack as a Result if the
217/// if the expression is valid, or an Err if the expression is invalid or
218/// otherwise cannot be evaluated.
219///
220/// safe_eval_with_stack is useful for testing the validity of an expression,
221/// and is safer than eval, as it does not mutate any inputted values.
222///
223/// It also allows you to specify a stack to use for the expression, rather than
224/// automatically creating a new stack internally. This is useful for persisting
225/// a stack between calls to safe_eval_with_stack.
226/// # Examples  
227///
228/// ```
229/// use dc-ock::safe_eval_with_stack;
230///
231/// fn main() {
232///     let mut stack: VecDeque<f64> = VecDeque::new();
233///     stack.push_back(2.);
234///     stack.push_back(7.5);
235///     stack.push_back(3.5);
236///
237///     stack = safe_eval_with_stack("+ +", stack).unwrap();
238///     println!("{:?}", stack); // prints [13.0]
239/// }
240/// ```
241pub fn safe_eval_with_stack(
242    input: &str,
243    initial_stack: VecDeque<f64>,
244) -> Result<VecDeque<f64>, EvaluationError> {
245    let mut stack = initial_stack;
246    // Split the input into tokens.
247    let toks = input.split(' ').collect::<Vec<&str>>();
248    let mut ops: VecDeque<CalcType> = VecDeque::new();
249
250    for tok in &toks {
251        let x: CalcType = match str_to_calc_type(tok) {
252            Some(x) => x,
253            None => {
254                return Err(EvaluationError {
255                    message: format!("Invalid token: {}", tok),
256                })
257            }
258        };
259
260        match x {
261            Add | Divide | Multiply | Power | Subtract | Print => ops.push_back(x),
262
263            Val(x_) => stack.push_back(x_),
264        }
265    }
266
267    for op in &ops {
268        match op {
269            Add => {
270                let y = &stack.pop_back().ok_or(EvaluationError {
271                    message: "Stack is empty!".to_string(),
272                })?;
273                let x = &stack.pop_back().ok_or(EvaluationError {
274                    message: "Stack is empty!".to_string(),
275                })?;
276                &stack.push_back(x + y)
277            }
278            Subtract => {
279                let y = &stack.pop_back().ok_or(EvaluationError {
280                    message: "Stack is empty!".to_string(),
281                })?;
282                let x = &stack.pop_back().ok_or(EvaluationError {
283                    message: "Stack is empty!".to_string(),
284                })?;
285                &stack.push_back(x - y)
286            }
287            Multiply => {
288                let y = &stack.pop_back().ok_or(EvaluationError {
289                    message: "Stack is empty!".to_string(),
290                })?;
291                let x = &stack.pop_back().ok_or(EvaluationError {
292                    message: "Stack is empty!".to_string(),
293                })?;
294                &stack.push_back(x * y)
295            }
296            Divide => {
297                let y = &stack.pop_back().ok_or(EvaluationError {
298                    message: "Stack is empty!".to_string(),
299                })?;
300                let x = &stack.pop_back().ok_or(EvaluationError {
301                    message: "Stack is empty!".to_string(),
302                })?;
303                &stack.push_back(x / y)
304            }
305            Power => {
306                let y = &stack.pop_back().ok_or(EvaluationError {
307                    message: "Stack is empty!".to_string(),
308                })?;
309                let x = &stack.pop_back().ok_or(EvaluationError {
310                    message: "Stack is empty!".to_string(),
311                })?;
312
313                let result = x.powf(*y);
314                &stack.push_back(result)
315            }
316            Print => &{ println!("{:#?}", stack.iter().last()) },
317            Val(_) => panic!("Unexpected value in the operator stack!"),
318        };
319    }
320
321    Ok(stack)
322}