Removed panic!s and added error handling to propagate
[evpf.git] / src / main.rs
1 use std::io;
2 use regex::Regex;
3
4 enum Operator {
5 ADD,
6 SUB,
7 MUL,
8 DIV,
9 }
10
11 // structure to hold an expression in the stack
12 struct Expression {
13 value : f32,
14 }
15
16 // function to compute a result by popping the stack and
17 // pushing back the result
18 fn get_result (t : &mut Vec<Expression>, op : Operator) -> Result<f32,String> {
19 // pop the stack for last operand
20 // if nothing - panic with error
21 let n1 = t.pop ();
22 if n1.is_none () {
23 return Err ("Illegal expression".to_string());
24 }
25 // pop the stack for the first operand
26 // if nothing - panic with error
27 let n2 = t.pop ();
28 if n2.is_none () {
29 return Err ("Illegal expression!".to_string());
30 }
31
32 let num1 = n1.unwrap().value;
33 let num2 = n2.unwrap().value;
34
35 // depending on the operation, set the result
36 match op {
37 Operator::ADD => {
38 t.push (Expression{value: num2 + num1});
39 Ok (num2 + num1)
40 },
41 Operator::SUB => {
42 t.push (Expression{value: num2 - num1});
43 Ok (num2 + num1)
44 },
45 Operator::MUL => {
46 t.push (Expression{value: num2 * num1});
47 Ok (num2 * num1)
48 },
49 Operator::DIV => {
50 t.push (Expression{value: num2 / num1});
51 Ok (num2 / num1)
52 },
53 }
54 }
55
56 // evaluation function
57 fn evaluate (expr : &str, match_num : &regex::Regex) -> Result<f32,String> {
58 let mut ops = Vec::<Expression>::new ();
59 // tokenize the individual words by splitting at whitespace
60 let words = expr.split_whitespace ();
61
62 // iterate over the words
63 for word in words {
64 match word {
65 // if the word matches one of +, -, *, / then push it on the stack
66 // and immediately evaluate the expression by popping the operator
67 // and the last two operands and push the result back on to the stack
68 "+" => {
69 let m = get_result (&mut ops, Operator::ADD);
70 if ! m.is_ok () {
71 return Err (m.unwrap_err().to_string());
72 }
73 },
74 "-" => {
75 let m = get_result (&mut ops, Operator::SUB);
76 if ! m.is_ok () {
77 return Err (m.unwrap_err().to_string());
78 }
79 },
80 "*" => {
81 let m = get_result (&mut ops, Operator::MUL);
82 if ! m.is_ok () {
83 return Err (m.unwrap_err().to_string());
84 }
85 },
86 "/" => {
87 let m = get_result (&mut ops, Operator::DIV);
88 if ! m.is_ok () {
89 return Err (m.unwrap_err().to_string());
90 }
91 },
92 // if word matches a number, push it on to the stack
93 _ => if match_num.is_match (word) {
94 let num = word.parse ().unwrap ();
95 ops.push (Expression { value: num });
96 }
97 // if word doesn't match either operator or number then panic.
98 else {
99 return Err ("Error parsing expression! \
100 Must be a number or operator (+,-,* or /)".to_string());
101 }
102 }
103 }
104
105 if ops.len () > 1 {
106 // if the stack has more than one value, it means that the postfix
107 // expression is not complete - so display the stack status
108 return Err ("Postfix expression incomplete!".to_string());
109 } else {
110 // stack has only one item which is the result so display it
111 let res = ops[0].value;
112 return Ok (res);
113 }
114 }
115
116 fn main() {
117 // get a line from input and evaluate it
118 let mut expr = String::new ();
119 // regular expression to match a number
120 let match_num = Regex::new (r"^\d+?.*?\d*?$").unwrap ();
121 // loop until a blank line is received
122 loop {
123 expr.clear ();
124 println!("Enter an expression (postfix) (blank to quit): ");
125 // read a line of text
126 io::stdin().read_line (&mut expr).expect ("Error reading line!");
127 // trim the text
128 let expr = expr.trim ();
129 // quit if the expression is blank
130 if expr == "" {
131 break;
132 }
133
134 let res = evaluate (&expr, &match_num);
135 if res.is_ok () {
136 println! ("Result is : {}", res.unwrap());
137 }
138 else {
139 eprintln! ("Error: {}", res.unwrap_err());
140 }
141 }
142 }