use std::io;
+use std::io::Write;
use regex::Regex;
+use colored::*;
+use std::env;
-// describe the operators
-enum Operator {
- NUM, ADD, SUB, MUL, DIV,
+const ILLEGAL_EXP : &'static str = "Illegal Expression";
+const ERR_PARSING_NOT_MATCH : &'static str = "Error parsing expression! \
+ Must be a number or operator (+,-,* or /)";
+const ERR_POSTFIX_INCOMPLETE : &'static str = "Postfix expression incomplete!";
+const ERR_FLUSHING : &'static str = "Error flushing!";
+const ERR_READING_LINE : &'static str = "Error reading line!";
+const HELP_TEXT : [&str ; 4] =
+ ["Type an expression in postfix style to evaluate.",
+ "Example: 4 5 + 12 -",
+ "Supported operators: +, -, *, /",
+ "Type q, Q to quit"
+ ];
+
+const RESULT : &'static str = "Result";
+const ERROR : &'static str = "Error";
+const ERROR_HELP : &'static str = "Type ? or h or H for help";
+
+// Describe an operator - one of add, subtract, multiply or divide
+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_EXP.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_EXP.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 : ®ex::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 (ERR_PARSING_NOT_MATCH.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 (ERR_POSTFIX_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
- loop {
- expr.clear ();
- println!("Enter an expression (postfix) (blank to quit): ");
- // read a line of text
- io::stdin().read_line (&mut expr).expect ("Error reading line!");
- // trim the text
- let expr = expr.trim ();
- // quit if the expression is blank
- if expr == "" {
- break;
+
+ let args : Vec<String> = env::args().collect ();
+ // regular expression to match a number
+ let match_num = Regex::new (r"^\d+?\.*?\d*?$").unwrap ();
+
+ if args.len () > 1 {
+ // if arguments are provided run in command line mode - i.e. print the
+ // result and exit
+ let mut expr = String::new ();
+ let mut i = 0;
+ // create the expression string to evaluate
+ for arg in args.iter() {
+ if i > 0 {
+ expr.push_str (&arg);
+ expr.push_str (" ");
+ }
+ i += 1;
}
+ // evaluate the result
+ let res = evaluate (&expr, &match_num);
+ // if Result is OK then print the result in green
+ if res.is_ok () {
+ let restxt = format! ("{}: {}", RESULT,
+ res.unwrap());
+ println! ("{}", restxt.green ());
+ } else {
+ // print the error in purple
+ let errtxt = format! ("{}: {}", ERROR,
+ res.unwrap_err());
+ eprintln! ("{}", errtxt.purple ());
+ }
+
+ } else {
+ // if arguments are not provided run in interactive mode -
+ // display a prompt and get the expression
+ // repeat until the user quits
- evaluate (&expr);
+ // get a line from input and evaluate it
+ let mut expr = String::new ();
+
+ // loop until a blank line is received
+ loop {
+ expr.clear ();
+ print!("{}", "evpf>".bold() );
+ io::stdout().flush ().expect (ERR_FLUSHING);
+ // read a line of text
+ io::stdin().read_line (&mut expr).expect (ERR_READING_LINE);
+ // trim the text
+ let expr = expr.trim ();
+
+ if expr == "q" || expr == "Q" {
+ // quit if the expression is q or Qs
+ break;
+ } else if expr == "?" || expr == "h" || expr == "H" {
+ // display help text
+ for text in HELP_TEXT.iter() {
+ println! ("{}", text.cyan() );
+ }
+
+ continue;
+ } else if expr == "" {
+ // continue without proceeding
+ continue;
+ }
+
+ // Evaluate result
+ let res = evaluate (&expr, &match_num);
+
+ // if Result is OK then print the result in green
+ if res.is_ok () {
+ let restxt = format! ("{}: {}", RESULT,
+ res.unwrap());
+ println! ("{}", restxt.green ());
+ } else {
+ // print the error in purple
+ let errtxt = format! ("{}: {}", ERROR,
+ res.unwrap_err());
+ eprintln! ("{}", errtxt.purple());
+ eprintln! ("{}", ERROR_HELP.purple());
+ }
+ }
}
}