aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNerdyPepper <[email protected]>2019-03-23 09:22:11 +0000
committerNerdyPepper <[email protected]>2019-03-23 09:22:11 +0000
commit0312bb8037d5b8092abfb8593884be32f2a1d295 (patch)
treedf3d46f017da9677231f064ae4400582121bddc0
parent5ec753f3d4c96690ef7ea63267138acad9c0c6b2 (diff)
implement error handling in operators and functions
-rw-r--r--src/lex/mod.rs58
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 @@
1use std::collections::HashMap; 1use std::collections::HashMap;
2 2
3use crate::error::{
4 CalcError,
5 Math
6};
7
3#[derive(Debug, Copy, Clone, PartialEq)] 8#[derive(Debug, Copy, Clone, PartialEq)]
4pub struct Operator { 9pub struct Operator {
5 token: char, 10 token: char,
@@ -9,7 +14,10 @@ pub struct Operator {
9} 14}
10 15
11impl Operator { 16impl 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
87pub fn lexer(input: &str) -> Result<Vec<Token>, String> { 104pub 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 }