rpn/
lib.rs

1/// Evaluates an RPL expression.
2///
3/// # Examples
4/// ````
5/// extern crate rpn;
6///
7/// let result:f32 = rpn::evaluate("5 2 +").unwrap();
8/// ````
9///
10/// # Errors
11/// This function will return an error in case of bad expression:
12///
13/// - if it includes an unrecognized operator (recognized ones are +, -, * and /
14/// - if it misses an operand (i.e. value)
15/// - if it misses an operator
16pub fn evaluate(expr: &str) -> Result<f32, &str> {
17  let mut stack:Vec<f32> = Vec::new();
18  for token in expr.split_whitespace() {
19    let wrapped_operand = token.parse::<f32>();
20    let is_operator = wrapped_operand.is_err();
21    if is_operator {
22      if stack.len() < 2 {
23        return Err("Unsufficient operands before operator");
24      }
25      // xxxFlorent: Any way to destructure like this? Not sure of what I am
26      // doing below
27      // let [ operand1, operand2 ] = stack.drain((token.len() - 3)..).collect();
28      let operand2 = stack.pop().expect("expected f32 values in stack");
29      let operand1 = stack.pop().expect("expected f32 values in stack");
30      let result = match token {
31        "+" => operand1 + operand2,
32        "-" => operand1 - operand2,
33        "*" => operand1 * operand2,
34        "/" => operand1 / operand2,
35        _ => return Err("Unsupported operator")
36      };
37      stack.push(result);
38    } else {
39      stack.push(wrapped_operand.unwrap());
40    }
41  }
42
43  if stack.len() != 1 {
44    return Err("Remaining untreated operands. Probably missing operator.");
45  }
46  return Ok(stack.pop().expect("expected a f32 value remaining in stack"));
47}
48
49#[test]
50fn it_adds() {
51  let result = evaluate("1 2 +");
52  assert_eq!(result.unwrap(), 3.0);
53}
54
55#[test]
56fn it_substracts() {
57  let result = evaluate("1 2 -");
58  assert_eq!(result.unwrap(), -1.0);
59}
60
61#[test]
62fn it_multiplies() {
63  let result = evaluate("6 7 *");
64  assert_eq!(result.unwrap(), 42.0);
65}
66
67#[test]
68fn it_divides() {
69  let result = evaluate("1 2 /");
70  assert_eq!(result.unwrap(), 0.5);
71}
72
73#[test]
74fn it_evaluates_complex_expressions() {
75  // ((1+2) * 8 / (5-1) - 1) / 2
76  let result = evaluate("1 2 + 8 * 5 1 - / 1 - 2 /");
77  assert_eq!(result.unwrap(), 2.5);
78}
79
80#[test]
81fn it_allows_multiple_shitespaces() {
82  let result = evaluate("1  2 +\t3 -");
83  assert_eq!(result.unwrap(), 0.0);
84}
85
86#[test]
87fn it_panics_for_unsupported_characters() {
88  let result = evaluate("1 2 t");
89  assert_eq!(result.unwrap_err(), "Unsupported operator");
90}
91
92#[test]
93fn it_panics_for_unsufficient_operands() {
94  let result = evaluate("1 +");
95  assert_eq!(result.unwrap_err(), "Unsufficient operands before operator");
96}
97
98#[test]
99fn it_panics_for_unsufficient_operators() {
100  let result = evaluate("1 2 3 +");
101  assert_eq!(result.unwrap_err(),
102    "Remaining untreated operands. Probably missing operator.");
103}