diff --git a/src/error/mod.rs b/src/error/mod.rs index 7f3e133..9e6933a 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -29,6 +29,12 @@ pub enum Error { /// A value has the wrong type. TypeError, + + /// An opening brace without a matching closing brace was found. + UnmatchedLBrace, + + /// A closing brace without a matching opening brace was found. + UnmatchedRBrace, } impl Error { diff --git a/src/lib.rs b/src/lib.rs index dfb3c62..d0166ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,9 @@ mod test { assert_eq!(eval("true"), Ok(Value::Boolean(true))); assert_eq!(eval("false"), Ok(Value::Boolean(false))); assert_eq!(eval("blub"), Err(Error::IdentifierNotFound)); + assert_eq!(eval("-3"), Ok(Value::Int(-3))); + assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6))); + assert_eq!(eval("----3"), Ok(Value::Int(3))); } #[test] @@ -41,6 +44,14 @@ mod test { assert_eq!(eval("5-3.0"), Ok(Value::Float(2.0))); assert_eq!(eval("5 / 4.0"), Ok(Value::Float(1.25))); assert_eq!(eval("5.0 *3"), Ok(Value::Float(15.0))); + assert_eq!(eval("5.0 *-3"), Ok(Value::Float(-15.0))); + assert_eq!(eval("5.0 *- 3"), Ok(Value::Float(-15.0))); + assert_eq!(eval("5.0 * -3"), Ok(Value::Float(-15.0))); + assert_eq!(eval("5.0 * - 3"), Ok(Value::Float(-15.0))); + assert_eq!(eval("-5.0 *-3"), Ok(Value::Float(15.0))); + assert_eq!(eval("3+-1"), Ok(Value::Int(2))); + assert_eq!(eval("-3-5"), Ok(Value::Int(-8))); + assert_eq!(eval("-5--3"), Ok(Value::Int(-2))); } #[test] @@ -52,6 +63,36 @@ mod test { assert_eq!(eval("5 / 4*2"), Ok(Value::Int(2))); assert_eq!(eval("1-5 *3/15"), Ok(Value::Int(0))); assert_eq!(eval("15/7/2.0"), Ok(Value::Float(1.0))); - assert_eq!(eval("15.0/7/2"), Ok(Value::Float(15.0/7.0/2.0))); + assert_eq!(eval("15.0/7/2"), Ok(Value::Float(15.0 / 7.0 / 2.0))); + assert_eq!(eval("15.0/-7/2"), Ok(Value::Float(15.0 / -7.0 / 2.0))); + assert_eq!(eval("-15.0/7/2"), Ok(Value::Float(-15.0 / 7.0 / 2.0))); + assert_eq!(eval("-15.0/7/-2"), Ok(Value::Float(-15.0 / 7.0 / -2.0))); } + + #[test] + fn test_braced_examples() { + assert_eq!(eval("(1)"), Ok(Value::Int(1))); + assert_eq!(eval("( 1.0 )"), Ok(Value::Float(1.0))); + assert_eq!(eval("( true)"), Ok(Value::Boolean(true))); + assert_eq!(eval("( -1 )"), Ok(Value::Int(-1))); + assert_eq!(eval("-(1)"), Ok(Value::Int(-1))); + assert_eq!(eval("-(1 + 3) * 7"), Ok(Value::Int(-28))); + assert_eq!(eval("(1 * 1) - 3"), Ok(Value::Int(-2))); + assert_eq!(eval("4 / (2 * 2)"), Ok(Value::Int(1))); + assert_eq!(eval("7/(7/(7/(7/(7/(7)))))"), Ok(Value::Int(1))); + } + + #[test] + fn test_type_errors() { + assert_eq!( + eval("-true"), + Err(Error::expected_number(Value::Boolean(true))) + ); + assert_eq!( + eval("1-true"), + Err(Error::expected_number(Value::Boolean(true))) + ); + assert_eq!(eval("true-"), Err(Error::wrong_argument_amount(1, 2))); + } + } diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 2f60237..350c153 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -23,8 +23,10 @@ pub trait Operator { pub struct RootNode; pub struct Add; pub struct Sub; +pub struct Neg; pub struct Mul; pub struct Div; +pub struct Braced; pub struct Const { value: Value, @@ -75,9 +77,13 @@ impl Operator for Add { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int(arguments[0].as_int().unwrap() + arguments[1].as_int().unwrap())) + Ok(Value::Int( + arguments[0].as_int().unwrap() + arguments[1].as_int().unwrap(), + )) } else { - Ok(Value::Float(arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap())) + Ok(Value::Float( + arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap(), + )) } } } @@ -97,9 +103,34 @@ impl Operator for Sub { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int(arguments[0].as_int().unwrap() - arguments[1].as_int().unwrap())) + Ok(Value::Int( + arguments[0].as_int().unwrap() - arguments[1].as_int().unwrap(), + )) } else { - Ok(Value::Float(arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap())) + Ok(Value::Float( + arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap(), + )) + } + } +} + +impl Operator for Neg { + fn precedence(&self) -> i32 { + 110 + } + + fn argument_amount(&self) -> usize { + 1 + } + + fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + expect_argument_amount(arguments.len(), 1)?; + expect_number(&arguments[0])?; + + if arguments[0].is_int() { + Ok(Value::Int(-arguments[0].as_int().unwrap())) + } else { + Ok(Value::Float(-arguments[0].as_float().unwrap())) } } } @@ -119,9 +150,13 @@ impl Operator for Mul { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int(arguments[0].as_int().unwrap() * arguments[1].as_int().unwrap())) + Ok(Value::Int( + arguments[0].as_int().unwrap() * arguments[1].as_int().unwrap(), + )) } else { - Ok(Value::Float(arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap())) + Ok(Value::Float( + arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap(), + )) } } } @@ -141,9 +176,13 @@ impl Operator for Div { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int(arguments[0].as_int().unwrap() / arguments[1].as_int().unwrap())) + Ok(Value::Int( + arguments[0].as_int().unwrap() / arguments[1].as_int().unwrap(), + )) } else { - Ok(Value::Float(arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap())) + Ok(Value::Float( + arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap(), + )) } } } @@ -179,3 +218,18 @@ impl Operator for Identifier { configuration.get_value(&self.identifier) } } + +impl Operator for Braced { + fn precedence(&self) -> i32 { + 200 + } + + fn argument_amount(&self) -> usize { + 1 + } + + fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result { + expect_argument_amount(arguments.len(), 1)?; + Ok(arguments[0].clone()) + } +} diff --git a/src/token/mod.rs b/src/token/mod.rs index 7b85819..7605e85 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -1,4 +1,4 @@ -use value::{IntType, FloatType}; +use value::{FloatType, IntType}; #[derive(Clone, PartialEq)] pub enum Token { @@ -7,6 +7,8 @@ pub enum Token { Minus, Star, Slash, + LBrace, + RBrace, Whitespace, // Complex tokens @@ -28,6 +30,8 @@ fn char_to_token(c: char) -> PartialToken { '-' => PartialToken::Token(Token::Minus), '*' => PartialToken::Token(Token::Star), '/' => PartialToken::Token(Token::Slash), + '(' => PartialToken::Token(Token::LBrace), + ')' => PartialToken::Token(Token::RBrace), c => { if c.is_whitespace() { PartialToken::Token(Token::Whitespace) @@ -38,6 +42,25 @@ fn char_to_token(c: char) -> PartialToken { } } +impl Token { + // Make this a const fn as soon as match gets stable (issue #57563) + pub fn is_value(&self) -> bool { + match self { + Token::Plus => false, + Token::Minus => false, + Token::Star => false, + Token::Slash => false, + Token::LBrace => false, + Token::RBrace => true, + Token::Whitespace => false, + Token::Identifier(_) => true, + Token::Float(_) => true, + Token::Int(_) => true, + Token::Boolean(_) => true, + } + } +} + /// Converts a string to a vector of partial tokens. fn str_to_tokens(string: &str) -> Vec { let mut result = Vec::new(); diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 67d4c8f..e10e230 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -7,18 +7,19 @@ pub struct Node { } impl Node { - fn new(operator: Box) -> Self { + fn new(operator: T) -> Self { Self { children: Vec::new(), - operator, + operator: Box::new(operator), } } fn root_node() -> Self { - Self { - children: Vec::new(), - operator: Box::new(RootNode), - } + Self::new(RootNode) + } + + fn braced_node() -> Self { + Self::new(Braced) } pub fn eval(&self, configuration: &Configuration) -> Result { @@ -41,32 +42,31 @@ impl Node { self.children().len() == self.operator().argument_amount() } - fn insert_back_prioritized(&mut self, operator: Box) -> Result<(), Error> { - if self.operator().precedence() < operator.precedence() { + fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> Result<(), Error> { + if self.operator().precedence() < node.operator().precedence() || is_root_node { if self.operator().is_leaf() { Err(Error::AppendedToLeafNode) } else if self.has_correct_amount_of_children() { - if self.children.last_mut().unwrap().operator().precedence() < operator.precedence() + if self.children.last_mut().unwrap().operator().precedence() < node.operator().precedence() { self.children .last_mut() .unwrap() - .insert_back_prioritized(operator) + .insert_back_prioritized(node, false) } else { - let new_node = Node::new(operator); let last_child = self.children.pop().unwrap(); - self.children.push(new_node); - let new_node = self.children.last_mut().unwrap(); + self.children.push(node); + let node = self.children.last_mut().unwrap(); - if new_node.operator().is_leaf() { + if node.operator().is_leaf() { Err(Error::AppendedToLeafNode) } else { - new_node.children.push(last_child); + node.children.push(last_child); Ok(()) } } } else { - self.children.push(Node::new(operator)); + self.children.push(node); Ok(()) } } else { @@ -76,29 +76,62 @@ impl Node { } pub fn tokens_to_operator_tree(tokens: Vec) -> Result { - let mut root = Node::root_node(); + let mut root = vec![Node::root_node()]; + let mut last_non_whitespace_token_is_value = false; for token in tokens { - let operator: Option> = match token { - Token::Plus => Some(Box::new(Add)), - Token::Minus => Some(Box::new(Sub)), - Token::Star => Some(Box::new(Mul)), - Token::Slash => Some(Box::new(Div)), + let node = match token.clone() { + Token::Plus => Some(Node::new(Add)), + Token::Minus => { + if last_non_whitespace_token_is_value { + Some(Node::new(Sub)) + } else { + Some(Node::new(Neg)) + } + } + Token::Star => Some(Node::new(Mul)), + Token::Slash => Some(Node::new(Div)), + Token::LBrace => { + root.push(Node::braced_node()); + None + }, + Token::RBrace => { + if root.len() < 2 { + return Err(Error::UnmatchedRBrace); + } else { + root.pop() + } + } Token::Whitespace => None, - Token::Identifier(identifier) => Some(Box::new(Identifier::new(identifier))), - Token::Float(number) => Some(Box::new(Const::new(Value::Float(number)))), - Token::Int(number) => Some(Box::new(Const::new(Value::Int(number)))), - Token::Boolean(boolean) => Some(Box::new(Const::new(Value::Boolean(boolean)))), + Token::Identifier(identifier) => Some(Node::new(Identifier::new(identifier))), + Token::Float(number) => Some(Node::new(Const::new(Value::Float(number)))), + Token::Int(number) => Some(Node::new(Const::new(Value::Int(number)))), + Token::Boolean(boolean) => Some(Node::new(Const::new(Value::Boolean(boolean)))), }; - if let Some(operator) = operator { - root.insert_back_prioritized(operator)?; + if let Some(node) = node { + if let Some(root) = root.last_mut() { + root.insert_back_prioritized(node, true)?; + } else { + return Err(Error::UnmatchedRBrace); + } + } + + if token != Token::Whitespace { + last_non_whitespace_token_is_value = token.is_value(); } } - if root.children().len() == 1 { - Ok(root.children.pop().unwrap()) + if root.len() > 1 { + Err(Error::UnmatchedLBrace) + } else if root.len() == 0 { + Err(Error::UnmatchedRBrace) } else { - Err(Error::EmptyExpression) + let mut root = root.pop().unwrap(); + if root.children().len() == 1 { + Ok(root.children.pop().unwrap()) + } else { + Err(Error::EmptyExpression) + } } } diff --git a/src/value/mod.rs b/src/value/mod.rs index 482b18f..170ab0e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -41,9 +41,3 @@ impl Value { } } } - -#[derive(Clone, Debug, PartialEq)] -pub enum Number { - Float(FloatType), - Int(IntType), -} \ No newline at end of file