1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
use std::io; pub struct RPN { pos: usize, input: String, } impl RPN { fn new(input: String) -> Self { RPN { input, pos: 0 } } fn eof(&self) -> bool { self.pos >= self.input.len() } fn peek_char(&self) -> Option<char> { self.input[self.pos..].chars().next() } fn consume_char(&mut self) -> char { let mut iter = self.input[self.pos..].char_indices(); let (_, cur_char) = iter.next().unwrap(); let (next_pos, _) = iter.next().unwrap_or((1, ' ')); self.pos += next_pos; return cur_char; } fn consume_while<F>(&mut self, test: F) -> String where F: Fn(char) -> bool, { let mut result = String::new(); while !self.eof() && test(self.peek_char().unwrap()) { result.push(self.consume_char()) } result } fn consume_num(&mut self) -> String { self.consume_while(|c| c.is_digit(10)) } fn consume_whitespace(&mut self) { self.consume_while(|c| c.is_whitespace()); } fn parse(&mut self) -> Result<Vec<f64>, io::Error> { let mut nums = vec![]; while !self.eof() { self.consume_whitespace(); let peek_char = match self.peek_char() { Some(c) => c, None => { return Ok(nums); } }; if peek_char.is_digit(10) { let num = self.consume_num(); nums.push(num.parse().unwrap()); continue; } let l2 = nums.pop().ok_or(io::Error::new( io::ErrorKind::InvalidInput, "Found invalid input", ))?; let l1 = nums.pop().ok_or(io::Error::new( io::ErrorKind::InvalidInput, "Found invalid input", ))?; match peek_char { '+' => nums.push(l1 + l2), '-' => nums.push(l1 - l2), '*' => nums.push(l1 * l2), '/' => nums.push(l1 / l2), '%' => nums.push(l1 % l2), _ => {} } self.consume_char(); } Ok(nums) } pub fn calc(input: String) -> Result<f64, io::Error> { let nums = RPN::new(input).parse()?; if nums.len() == 1 { Ok(nums[0]) } else { Err(io::Error::new( io::ErrorKind::InvalidInput, "Input syntax could be wrong.", )) } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_calc() { assert_eq!(RPN::calc(String::from("1 2 +")).unwrap(), 3.0); assert_eq!(RPN::calc(String::from("1 2 -")).unwrap(), -1.0); assert_eq!(RPN::calc(String::from("100 1 +")).unwrap(), 101.0); assert_eq!( RPN::calc(String::from("15 7 1 1 + - / 3 * 2 1 1 + + -")).unwrap(), 5.0 ); assert_eq!(RPN::calc(String::from("1 2 /")).unwrap(), 0.5); assert!(RPN::calc(String::from("15 7")).is_err()); assert!(RPN::calc(String::from("a b +")).is_err()); assert!(RPN::calc(String::from("")).is_err()); } }