Removed panic!s and added error handling to propagate
authorHarishankar <v.harishankar@gmail.com>
Fri, 29 May 2020 08:53:36 +0000 (14:23 +0530)
committerHarishankar <v.harishankar@gmail.com>
Fri, 29 May 2020 08:53:36 +0000 (14:23 +0530)
Removed panics from the code and made error handling propagate
through for recovery

src/main.rs

index dcf215f..e25413b 100644 (file)
 use std::io;
 use regex::Regex;
 
-// describe the operators
-enum Operator {
-       NUM, ADD, SUB, MUL, DIV,
+enum Operator { 
+       ADD,
+       SUB,
+       MUL,
+       DIV,
 }
 
 // structure to hold an expression in the stack
 struct Expression {
        value : f32,
-       operator : Operator,
 }
 
 // function to compute a result by popping the stack and 
 // pushing back the result
-fn get_result (t : &mut Vec::<Expression>) {
-       // pop the stack to get the operator
-       // if nothing - panic with error
-       let op = match t.pop () {
-               Some(x) => x,
-               None => panic! ("Illegal expression!"),
-       };
+fn get_result (t : &mut Vec<Expression>, op : Operator) -> Result<f32,String> {
        // pop the stack for last operand 
        // if nothing - panic with error
-       let n1 = match t.pop () { 
-               Some (x) => x,
-               None => panic! ("Illegal expression!"),
-       };
+       let n1 = t.pop ();
+       if n1.is_none () { 
+               return Err ("Illegal expression".to_string());
+       }
        // pop the stack for the first operand
        // if nothing - panic with error
-       let n2 = match t.pop () {
-               Some (x) => x,
-               None => panic! ("Illegal expression!"),
-       };
-
-       let mut res : f32 = 0.0;
+       let n2 = t.pop ();
+       if n2.is_none () {
+               return Err ("Illegal expression!".to_string());
+       }
+       
+       let num1 = n1.unwrap().value;
+       let num2 = n2.unwrap().value;
        
        // depending on the operation, set the result
-       match op.operator {
-               Operator::ADD => res = n1.value + n2.value,
-               Operator::SUB => res = n2.value - n1.value,
-               Operator::MUL => res = n1.value * n2.value,
-               Operator::DIV => res = n2.value / n1.value,
-               _       => panic! ("Illegal operator in expression!"),
+       match op {
+               Operator::ADD => {                              
+                                                       t.push (Expression{value: num2 + num1});
+                                                       Ok (num2 + num1)
+                                                },
+               Operator::SUB => {                              
+                                                       t.push (Expression{value: num2 - num1});
+                                                       Ok (num2 + num1)
+                                                },
+               Operator::MUL => {                              
+                                                       t.push (Expression{value: num2 * num1});
+                                                       Ok (num2 * num1)
+                                                },
+               Operator::DIV => {                              
+                                                       t.push (Expression{value: num2 / num1});
+                                                       Ok (num2 / num1)
+                                                },
        }
-       // push the result back to the stack
-       t.push (Expression {value : res, operator : Operator::NUM });
 }
 
 // evaluation function
-fn evaluate (expr : &str) {
+fn evaluate (expr : &str, match_num : &regex::Regex) -> Result<f32,String> {
        let mut ops = Vec::<Expression>::new ();
        // tokenize the individual words by splitting at whitespace
        let words = expr.split_whitespace ();
-       // regular expression to match a number 
-       let match_num = Regex::new (r"^\d+?.*?\d*?$").unwrap ();
+
        // iterate over the words
        for word in words {
                match word {
                // if the word matches one of +, -, *, / then push it on the stack
                // and immediately evaluate the expression by popping the operator 
                // and the last two operands and push the result back on to the stack           
-               "+" => {ops.push (Expression{ value: 0.0, operator: Operator::ADD });
-                          get_result (&mut ops) },
-               "-" => {ops.push (Expression{ value: 0.0, operator: Operator::SUB });
-                          get_result (&mut ops) },
-               "*" => {ops.push (Expression{ value: 0.0, operator: Operator::MUL});
-                          get_result (&mut ops)},
-               "/" => {ops.push (Expression{ value: 0.0, operator: Operator::DIV});
-                          get_result (&mut ops) },
+               "+" => {
+                               let m = get_result (&mut ops, Operator::ADD);
+                               if ! m.is_ok () {
+                                       return Err (m.unwrap_err().to_string());
+                               }
+                          },
+               "-" => {
+                               let m = get_result (&mut ops, Operator::SUB);
+                               if ! m.is_ok () {
+                                       return Err (m.unwrap_err().to_string());
+                               }
+                          },
+               "*" => {
+                               let m = get_result (&mut ops, Operator::MUL);
+                               if ! m.is_ok () {
+                                       return Err (m.unwrap_err().to_string());
+                               }
+                          },
+               "/" => {
+                               let m = get_result (&mut ops, Operator::DIV); 
+                               if ! m.is_ok () {
+                                       return Err (m.unwrap_err().to_string());
+                               }
+                          },
                        // if word matches a number, push it on to the stack
                _ =>  if match_num.is_match (word) {
-                               let num : f32 = word.parse ().unwrap ();
-                               ops.push (Expression {value: num, operator: Operator::NUM });
-                       }
+                               let num = word.parse ().unwrap ();
+                               ops.push (Expression { value: num });
+                         }     
                        // if word doesn't match either operator or number then panic.                  
                        else {
-                               panic! ("Error parsing expression!
-                                Must be a number or operator (+,-,* or /)");
-                       }               
+                               return Err ("Error parsing expression! \
+                                Must be a number or operator (+,-,* or /)".to_string());
+                       }
                }
        }
+       
+       if ops.len () > 1 {
        // if the stack has more than one value, it means that the postfix
        // expression is not complete - so display the stack status
-       if ops.len () > 1 {
-               println! ("Postfix Expression not complete. Current stack: ");
-               for exp in ops {
-                       println! ("{}", exp.value);
-               }
-       }
+               return Err ("Postfix expression incomplete!".to_string());
+       } else { 
        // stack has only one item which is the result so display it
-       else {
-               println! ("Result: {}", ops[0].value);
+               let res = ops[0].value;
+               return Ok (res);
        }
-       println! ();    
 }
 
 fn main() {
        // get a line from input and evaluate it 
        let mut expr = String::new ();
-       // loop until a blank line is received
+       // regular expression to match a number 
+       let match_num = Regex::new (r"^\d+?.*?\d*?$").unwrap ();
+       // loop until a blank line is received  
        loop {
                expr.clear ();  
                println!("Enter an expression (postfix) (blank to quit): ");
@@ -112,6 +131,12 @@ fn main() {
                        break;
                }
                
-               evaluate (&expr);
+               let res = evaluate (&expr, &match_num);
+               if res.is_ok () {
+                       println! ("Result is : {}", res.unwrap());
+               }
+               else {
+                       eprintln! ("Error: {}", res.unwrap_err());
+               }
        }
 }