diff options
author | NerdyPepper <[email protected]> | 2019-03-23 09:22:11 +0000 |
---|---|---|
committer | NerdyPepper <[email protected]> | 2019-03-23 09:22:11 +0000 |
commit | 0312bb8037d5b8092abfb8593884be32f2a1d295 (patch) | |
tree | df3d46f017da9677231f064ae4400582121bddc0 | |
parent | 5ec753f3d4c96690ef7ea63267138acad9c0c6b2 (diff) |
implement error handling in operators and functions
-rw-r--r-- | src/lex/mod.rs | 58 |
1 files changed, 33 insertions, 25 deletions
diff --git a/src/lex/mod.rs b/src/lex/mod.rs index 9912ca3..e12f327 100644 --- a/src/lex/mod.rs +++ b/src/lex/mod.rs | |||
@@ -1,5 +1,10 @@ | |||
1 | use std::collections::HashMap; | 1 | use std::collections::HashMap; |
2 | 2 | ||
3 | use crate::error::{ | ||
4 | CalcError, | ||
5 | Math | ||
6 | }; | ||
7 | |||
3 | #[derive(Debug, Copy, Clone, PartialEq)] | 8 | #[derive(Debug, Copy, Clone, PartialEq)] |
4 | pub struct Operator { | 9 | pub struct Operator { |
5 | token: char, | 10 | token: char, |
@@ -9,7 +14,10 @@ pub struct Operator { | |||
9 | } | 14 | } |
10 | 15 | ||
11 | impl Operator { | 16 | impl Operator { |
12 | fn token_from_op(token: char, operation: fn(f64, f64) -> f64, precedence: u8, is_left_associative: bool) -> Token { | 17 | fn token_from_op(token: char, |
18 | operation: fn(f64, f64) -> f64, | ||
19 | precedence: u8, | ||
20 | is_left_associative: bool) -> Token { | ||
13 | Token::Operator( | 21 | Token::Operator( |
14 | Operator { | 22 | Operator { |
15 | token, | 23 | token, |
@@ -19,8 +27,12 @@ impl Operator { | |||
19 | } | 27 | } |
20 | ) | 28 | ) |
21 | } | 29 | } |
22 | pub fn operate(self, x: f64, y: f64) -> f64 { | 30 | pub fn operate(self, x: f64, y: f64) -> Result<f64, CalcError> { |
23 | (self.operation)(x, y) | 31 | if self.token == '/' && y == 0. { |
32 | return Err(CalcError::Math(Math::DivideByZero)) | ||
33 | } else { | ||
34 | Ok((self.operation)(x, y)) | ||
35 | } | ||
24 | } | 36 | } |
25 | } | 37 | } |
26 | 38 | ||
@@ -39,8 +51,13 @@ impl Function { | |||
39 | } | 51 | } |
40 | ) | 52 | ) |
41 | } | 53 | } |
42 | pub fn apply(self, arg: f64) -> f64 { | 54 | pub fn apply(self, arg: f64) -> Result<f64, CalcError> { |
43 | (self.relation)(arg) | 55 | let result = (self.relation)(arg); |
56 | if !result.is_finite() { | ||
57 | return Err(CalcError::Math(Math::OutOfBounds)); | ||
58 | } else { | ||
59 | Ok(result) | ||
60 | } | ||
44 | } | 61 | } |
45 | } | 62 | } |
46 | 63 | ||
@@ -84,7 +101,7 @@ fn get_operators() -> HashMap<char, Token> { | |||
84 | ].iter().cloned().collect(); | 101 | ].iter().cloned().collect(); |
85 | } | 102 | } |
86 | 103 | ||
87 | pub fn lexer(input: &str) -> Result<Vec<Token>, String> { | 104 | pub fn lexer(input: &str) -> Result<Vec<Token>, CalcError> { |
88 | let functions: HashMap<&str, Token> = get_functions(); | 105 | let functions: HashMap<&str, Token> = get_functions(); |
89 | let operators: HashMap<char, Token> = get_operators(); | 106 | let operators: HashMap<char, Token> = get_operators(); |
90 | 107 | ||
@@ -97,8 +114,7 @@ pub fn lexer(input: &str) -> Result<Vec<Token>, String> { | |||
97 | match letter { | 114 | match letter { |
98 | '0'...'9' | '.' => { | 115 | '0'...'9' | '.' => { |
99 | num_vec.push(letter); | 116 | num_vec.push(letter); |
100 | last_char_is_op = false; // 5 - 6 | 117 | last_char_is_op = false; |
101 | // ^---- binary | ||
102 | }, | 118 | }, |
103 | 'a'...'z' | 'A'...'Z' => { | 119 | 'a'...'z' | 'A'...'Z' => { |
104 | let parse_num = num_vec.parse::<f64>().ok(); | 120 | let parse_num = num_vec.parse::<f64>().ok(); |
@@ -108,7 +124,7 @@ pub fn lexer(input: &str) -> Result<Vec<Token>, String> { | |||
108 | num_vec.clear(); | 124 | num_vec.clear(); |
109 | } | 125 | } |
110 | char_vec.push(letter); | 126 | char_vec.push(letter); |
111 | last_char_is_op = false; // + or - can never be encountered right after a character | 127 | last_char_is_op = false; |
112 | }, | 128 | }, |
113 | '+' | '-' => { | 129 | '+' | '-' => { |
114 | let op_token = operators.get(&letter).unwrap().clone(); | 130 | let op_token = operators.get(&letter).unwrap().clone(); |
@@ -120,7 +136,7 @@ pub fn lexer(input: &str) -> Result<Vec<Token>, String> { | |||
120 | last_char_is_op = true; | 136 | last_char_is_op = true; |
121 | } | 137 | } |
122 | result.push(op_token); | 138 | result.push(op_token); |
123 | } else if last_char_is_op { // act as unary only if an operator was parsed previously | 139 | } else if last_char_is_op { |
124 | result.push(Token::LParen); | 140 | result.push(Token::LParen); |
125 | result.push(Token::Num((letter.to_string() + "1").parse::<f64>().unwrap())); | 141 | result.push(Token::Num((letter.to_string() + "1").parse::<f64>().unwrap())); |
126 | result.push(Token::RParen); | 142 | result.push(Token::RParen); |
@@ -131,17 +147,14 @@ pub fn lexer(input: &str) -> Result<Vec<Token>, String> { | |||
131 | drain_num_stack(&mut num_vec, &mut result); | 147 | drain_num_stack(&mut num_vec, &mut result); |
132 | let operator_token: Token = operators.get(&letter).unwrap().clone(); | 148 | let operator_token: Token = operators.get(&letter).unwrap().clone(); |
133 | result.push(operator_token); | 149 | result.push(operator_token); |
134 | last_char_is_op = true; // 5 / -5 | 150 | last_char_is_op = true; |
135 | // ^---- unary | ||
136 | // TODO: parse right associative followed by unary properly | ||
137 | // 2^+5 is parsed as 2^1*5 = 10 | ||
138 | }, | 151 | }, |
139 | '(' => { | 152 | '(' => { |
140 | if char_vec.len() > 0 { | 153 | if char_vec.len() > 0 { |
141 | if let Some(res) = functions.get(&char_vec[..]) { | 154 | if let Some(res) = functions.get(&char_vec[..]) { |
142 | result.push(res.clone()); | 155 | result.push(res.clone()); |
143 | } else { | 156 | } else { |
144 | return Err(format!("Unexpected function {}", char_vec)) | 157 | return Err(CalcError::Syntax(format!("Unknown function '{}'", char_vec))) |
145 | } | 158 | } |
146 | char_vec.clear(); | 159 | char_vec.clear(); |
147 | } else { | 160 | } else { |
@@ -162,21 +175,16 @@ pub fn lexer(input: &str) -> Result<Vec<Token>, String> { | |||
162 | }; | 175 | }; |
163 | } | 176 | } |
164 | result.push(Token::LParen); | 177 | result.push(Token::LParen); |
165 | last_char_is_op = true; // unary + or - if a lparen was encountered | 178 | last_char_is_op = true; |
166 | // (-5 + 6) or (+6 + 7) | ||
167 | // ^ ^ | ||
168 | // `-----------`----unary | ||
169 | }, | 179 | }, |
170 | ')' => { | 180 | ')' => { |
171 | drain_num_stack(&mut num_vec, &mut result); | 181 | drain_num_stack(&mut num_vec, &mut result); |
172 | result.push(Token::RParen); | 182 | result.push(Token::RParen); |
173 | last_char_is_op = false; // binary + or - if rparen was encountered | 183 | last_char_is_op = false; |
174 | // (1 + 2) - (3 + 4) | 184 | }, |
175 | // ^ binary minus | 185 | ' ' => {}, |
176 | } | ||
177 | ' ' => {} | ||
178 | _ => { | 186 | _ => { |
179 | return Err(format!("Unexpected character: {}", letter)) | 187 | return Err(CalcError::Syntax(format!("Unexpected character: '{}'", letter))) |
180 | } | 188 | } |
181 | } | 189 | } |
182 | } | 190 | } |