From 8199cffdf1663da6c91af66dadf5bcd3e4d12a02 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Tue, 21 Mar 2023 20:20:56 +0100 Subject: [PATCH] Keep signs when evaluating the expr tree --- board-shared/src/expr.rs | 33 +++++++++++++++ board-shared/src/lexer.rs | 6 +-- board-shared/src/parser.rs | 86 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/board-shared/src/expr.rs b/board-shared/src/expr.rs index bdaefec..31e8cd4 100644 --- a/board-shared/src/expr.rs +++ b/board-shared/src/expr.rs @@ -10,6 +10,13 @@ pub fn calculate(expr: &Expression) -> f64 { match expr { Expression::Digit(value) => *value as f64, Expression::Parentheses(expr) => calculate(expr), + Expression::Unary(operator, expr) => { + let value = calculate(expr); + match operator { + Operator::Subtract => -value, + _ => panic!("Invalid unary operator"), + } + } Expression::Binary(operator, left, right) => { let left = calculate(left); let right = calculate(right); @@ -180,6 +187,32 @@ mod tests { assert!(!are_valid_expressions(&expr)); } + #[test] + fn test_are_valid_expressions_negative() { + let expr = vec![ + Expression::Binary( + Operator::Add, + Box::new(Expression::Digit(2)), + Box::new(Expression::Digit(-5)), + ), + Expression::Binary( + Operator::Subtract, + Box::new(Expression::Digit(-4)), + Box::new(Expression::Digit(-1)), + ), + ]; + assert!(are_valid_expressions(&expr)); + } + + #[test] + fn test_are_valid_expressions_unary_negative() { + let expr = vec![ + Expression::Unary(Operator::Subtract, Box::new(Expression::Digit(8))), + Expression::Digit(-8), + ]; + assert!(are_valid_expressions(&expr)); + } + #[test] fn shunting_yard_sample() { let tokens = vec![ diff --git a/board-shared/src/lexer.rs b/board-shared/src/lexer.rs index e8eb994..777cc1d 100644 --- a/board-shared/src/lexer.rs +++ b/board-shared/src/lexer.rs @@ -3,7 +3,7 @@ use std::fmt; #[derive(Debug, PartialEq, Clone)] pub enum Token { - NumberLiteral(u64), + NumberLiteral(i64), Operator(Operator), LeftParen, RightParen, @@ -46,13 +46,13 @@ pub fn lexer_reuse(input: &[Tile], result: &mut Vec) { if digit.has_left_parenthesis { result.push(Token::LeftParen); } - let mut value = digit.value as u64; + let mut value = digit.value as i64; it.next(); while let Some(&Tile::Digit(digit)) = it.peek() { if digit.has_left_parenthesis { break; } - value = value * 10 + digit.value as u64; + value = value * 10 + digit.value as i64; it.next(); if digit.has_right_parenthesis { has_right_parenthesis = true; diff --git a/board-shared/src/parser.rs b/board-shared/src/parser.rs index cbae922..1348ee7 100644 --- a/board-shared/src/parser.rs +++ b/board-shared/src/parser.rs @@ -5,8 +5,9 @@ use std::iter::Peekable; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expression { - Digit(u64), + Digit(i64), Parentheses(Box), + Unary(Operator, Box), Binary(Operator, Box, Box), } @@ -15,6 +16,7 @@ impl fmt::Display for Expression { match self { Expression::Digit(value) => write!(f, "{value}"), Expression::Parentheses(expr) => write!(f, "({expr})"), + Expression::Unary(operator, expr) => write!(f, "{operator}{expr}"), Expression::Binary(operator, left, right) => { write!(f, "{left} {operator} {right}") } @@ -50,11 +52,11 @@ fn parse_expression<'a>( fn parse_term<'a>( tokens: &mut Peekable>, ) -> Result { - let mut left = parse_factor(tokens)?; + let mut left = parse_unary(tokens)?; while let Some(Token::Operator(operator)) = tokens.peek() { let operator = *operator; tokens.next(); - let right = parse_factor(tokens)?; + let right = parse_unary(tokens)?; left = Expression::Binary(operator, Box::new(left), Box::new(right)); } Ok(left) @@ -77,6 +79,18 @@ fn parse_factor<'a>( } } +fn parse_unary<'a>( + tokens: &mut Peekable>, +) -> Result { + if let Some(Token::Operator(Operator::Subtract)) = tokens.peek() { + tokens.next(); + let expression = parse_unary(tokens)?; + Ok(Expression::Unary(Operator::Subtract, Box::new(expression))) + } else { + parse_factor(tokens) + } +} + #[cfg(test)] mod tests { use super::*; @@ -150,4 +164,70 @@ mod tests { ], ); } + + #[test] + fn test_parse_unary_and_binary_minus() { + let tokens = vec![ + Token::Operator(Operator::Subtract), + Token::NumberLiteral(1), + Token::Operator(Operator::Subtract), + Token::NumberLiteral(2), + ]; + let expression = parse(&tokens).unwrap(); + assert_eq!( + expression, + vec![Expression::Binary( + Operator::Subtract, + Box::new(Expression::Unary( + Operator::Subtract, + Box::new(Expression::Digit(1)), + )), + Box::new(Expression::Digit(2)), + )], + ); + } + + #[test] + fn test_parse_unary_before_parenthesis() { + let tokens = vec![ + Token::Operator(Operator::Subtract), + Token::LeftParen, + Token::NumberLiteral(9), + Token::Operator(Operator::Multiply), + Token::NumberLiteral(3), + Token::RightParen, + ]; + let expression = parse(&tokens).unwrap(); + assert_eq!( + expression, + vec![Expression::Unary( + Operator::Subtract, + Box::new(Expression::Parentheses(Box::new(Expression::Binary( + Operator::Multiply, + Box::new(Expression::Digit(9)), + Box::new(Expression::Digit(3)), + )))), + )], + ); + } + + #[test] + fn test_double_unary() { + let tokens = vec![ + Token::Operator(Operator::Subtract), + Token::Operator(Operator::Subtract), + Token::NumberLiteral(7), + ]; + let expression = parse(&tokens).unwrap(); + assert_eq!( + expression, + vec![Expression::Unary( + Operator::Subtract, + Box::new(Expression::Unary( + Operator::Subtract, + Box::new(Expression::Digit(7)) + )), + )], + ); + } }