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