Keep signs when evaluating the expr tree
This commit is contained in:
@@ -10,6 +10,13 @@ pub fn calculate(expr: &Expression) -> f64 {
|
|||||||
match expr {
|
match expr {
|
||||||
Expression::Digit(value) => *value as f64,
|
Expression::Digit(value) => *value as f64,
|
||||||
Expression::Parentheses(expr) => calculate(expr),
|
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) => {
|
Expression::Binary(operator, left, right) => {
|
||||||
let left = calculate(left);
|
let left = calculate(left);
|
||||||
let right = calculate(right);
|
let right = calculate(right);
|
||||||
@@ -180,6 +187,32 @@ mod tests {
|
|||||||
assert!(!are_valid_expressions(&expr));
|
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]
|
#[test]
|
||||||
fn shunting_yard_sample() {
|
fn shunting_yard_sample() {
|
||||||
let tokens = vec![
|
let tokens = vec![
|
||||||
|
@@ -3,7 +3,7 @@ use std::fmt;
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
NumberLiteral(u64),
|
NumberLiteral(i64),
|
||||||
Operator(Operator),
|
Operator(Operator),
|
||||||
LeftParen,
|
LeftParen,
|
||||||
RightParen,
|
RightParen,
|
||||||
@@ -46,13 +46,13 @@ pub fn lexer_reuse(input: &[Tile], result: &mut Vec<Token>) {
|
|||||||
if digit.has_left_parenthesis {
|
if digit.has_left_parenthesis {
|
||||||
result.push(Token::LeftParen);
|
result.push(Token::LeftParen);
|
||||||
}
|
}
|
||||||
let mut value = digit.value as u64;
|
let mut value = digit.value as i64;
|
||||||
it.next();
|
it.next();
|
||||||
while let Some(&Tile::Digit(digit)) = it.peek() {
|
while let Some(&Tile::Digit(digit)) = it.peek() {
|
||||||
if digit.has_left_parenthesis {
|
if digit.has_left_parenthesis {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
value = value * 10 + digit.value as u64;
|
value = value * 10 + digit.value as i64;
|
||||||
it.next();
|
it.next();
|
||||||
if digit.has_right_parenthesis {
|
if digit.has_right_parenthesis {
|
||||||
has_right_parenthesis = true;
|
has_right_parenthesis = true;
|
||||||
|
@@ -5,8 +5,9 @@ use std::iter::Peekable;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Digit(u64),
|
Digit(i64),
|
||||||
Parentheses(Box<Expression>),
|
Parentheses(Box<Expression>),
|
||||||
|
Unary(Operator, Box<Expression>),
|
||||||
Binary(Operator, Box<Expression>, Box<Expression>),
|
Binary(Operator, Box<Expression>, Box<Expression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ impl fmt::Display for Expression {
|
|||||||
match self {
|
match self {
|
||||||
Expression::Digit(value) => write!(f, "{value}"),
|
Expression::Digit(value) => write!(f, "{value}"),
|
||||||
Expression::Parentheses(expr) => write!(f, "({expr})"),
|
Expression::Parentheses(expr) => write!(f, "({expr})"),
|
||||||
|
Expression::Unary(operator, expr) => write!(f, "{operator}{expr}"),
|
||||||
Expression::Binary(operator, left, right) => {
|
Expression::Binary(operator, left, right) => {
|
||||||
write!(f, "{left} {operator} {right}")
|
write!(f, "{left} {operator} {right}")
|
||||||
}
|
}
|
||||||
@@ -50,11 +52,11 @@ fn parse_expression<'a>(
|
|||||||
fn parse_term<'a>(
|
fn parse_term<'a>(
|
||||||
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
|
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
|
||||||
) -> Result<Expression, ()> {
|
) -> Result<Expression, ()> {
|
||||||
let mut left = parse_factor(tokens)?;
|
let mut left = parse_unary(tokens)?;
|
||||||
while let Some(Token::Operator(operator)) = tokens.peek() {
|
while let Some(Token::Operator(operator)) = tokens.peek() {
|
||||||
let operator = *operator;
|
let operator = *operator;
|
||||||
tokens.next();
|
tokens.next();
|
||||||
let right = parse_factor(tokens)?;
|
let right = parse_unary(tokens)?;
|
||||||
left = Expression::Binary(operator, Box::new(left), Box::new(right));
|
left = Expression::Binary(operator, Box::new(left), Box::new(right));
|
||||||
}
|
}
|
||||||
Ok(left)
|
Ok(left)
|
||||||
@@ -77,6 +79,18 @@ fn parse_factor<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_unary<'a>(
|
||||||
|
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
|
||||||
|
) -> Result<Expression, ()> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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))
|
||||||
|
)),
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user