From 0312bb8037d5b8092abfb8593884be32f2a1d295 Mon Sep 17 00:00:00 2001 From: NerdyPepper Date: Sat, 23 Mar 2019 14:52:11 +0530 Subject: implement error handling in operators and functions --- src/lex/mod.rs | 58 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'src/lex') 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 @@ use std::collections::HashMap; +use crate::error::{ + CalcError, + Math +}; + #[derive(Debug, Copy, Clone, PartialEq)] pub struct Operator { token: char, @@ -9,7 +14,10 @@ pub struct Operator { } impl Operator { - fn token_from_op(token: char, operation: fn(f64, f64) -> f64, precedence: u8, is_left_associative: bool) -> Token { + fn token_from_op(token: char, + operation: fn(f64, f64) -> f64, + precedence: u8, + is_left_associative: bool) -> Token { Token::Operator( Operator { token, @@ -19,8 +27,12 @@ impl Operator { } ) } - pub fn operate(self, x: f64, y: f64) -> f64 { - (self.operation)(x, y) + pub fn operate(self, x: f64, y: f64) -> Result { + if self.token == '/' && y == 0. { + return Err(CalcError::Math(Math::DivideByZero)) + } else { + Ok((self.operation)(x, y)) + } } } @@ -39,8 +51,13 @@ impl Function { } ) } - pub fn apply(self, arg: f64) -> f64 { - (self.relation)(arg) + pub fn apply(self, arg: f64) -> Result { + let result = (self.relation)(arg); + if !result.is_finite() { + return Err(CalcError::Math(Math::OutOfBounds)); + } else { + Ok(result) + } } } @@ -84,7 +101,7 @@ fn get_operators() -> HashMap { ].iter().cloned().collect(); } -pub fn lexer(input: &str) -> Result, String> { +pub fn lexer(input: &str) -> Result, CalcError> { let functions: HashMap<&str, Token> = get_functions(); let operators: HashMap = get_operators(); @@ -97,8 +114,7 @@ pub fn lexer(input: &str) -> Result, String> { match letter { '0'...'9' | '.' => { num_vec.push(letter); - last_char_is_op = false; // 5 - 6 - // ^---- binary + last_char_is_op = false; }, 'a'...'z' | 'A'...'Z' => { let parse_num = num_vec.parse::().ok(); @@ -108,7 +124,7 @@ pub fn lexer(input: &str) -> Result, String> { num_vec.clear(); } char_vec.push(letter); - last_char_is_op = false; // + or - can never be encountered right after a character + last_char_is_op = false; }, '+' | '-' => { let op_token = operators.get(&letter).unwrap().clone(); @@ -120,7 +136,7 @@ pub fn lexer(input: &str) -> Result, String> { last_char_is_op = true; } result.push(op_token); - } else if last_char_is_op { // act as unary only if an operator was parsed previously + } else if last_char_is_op { result.push(Token::LParen); result.push(Token::Num((letter.to_string() + "1").parse::().unwrap())); result.push(Token::RParen); @@ -131,17 +147,14 @@ pub fn lexer(input: &str) -> Result, String> { drain_num_stack(&mut num_vec, &mut result); let operator_token: Token = operators.get(&letter).unwrap().clone(); result.push(operator_token); - last_char_is_op = true; // 5 / -5 - // ^---- unary - // TODO: parse right associative followed by unary properly - // 2^+5 is parsed as 2^1*5 = 10 + last_char_is_op = true; }, '(' => { if char_vec.len() > 0 { if let Some(res) = functions.get(&char_vec[..]) { result.push(res.clone()); } else { - return Err(format!("Unexpected function {}", char_vec)) + return Err(CalcError::Syntax(format!("Unknown function '{}'", char_vec))) } char_vec.clear(); } else { @@ -162,21 +175,16 @@ pub fn lexer(input: &str) -> Result, String> { }; } result.push(Token::LParen); - last_char_is_op = true; // unary + or - if a lparen was encountered - // (-5 + 6) or (+6 + 7) - // ^ ^ - // `-----------`----unary + last_char_is_op = true; }, ')' => { drain_num_stack(&mut num_vec, &mut result); result.push(Token::RParen); - last_char_is_op = false; // binary + or - if rparen was encountered - // (1 + 2) - (3 + 4) - // ^ binary minus - } - ' ' => {} + last_char_is_op = false; + }, + ' ' => {}, _ => { - return Err(format!("Unexpected character: {}", letter)) + return Err(CalcError::Syntax(format!("Unexpected character: '{}'", letter))) } } } -- cgit v1.2.3