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