Keep signs when evaluating the expr tree
continuous-integration/drone/push Build is passing Details

main
Clément FRÉVILLE 2 years ago
parent 83a48440b4
commit 8199cffdf1

@ -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![

@ -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<Token>) {
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;

@ -5,8 +5,9 @@ use std::iter::Peekable;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expression {
Digit(u64),
Digit(i64),
Parentheses(Box<Expression>),
Unary(Operator, Box<Expression>),
Binary(Operator, Box<Expression>, Box<Expression>),
}
@ -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<impl Iterator<Item = &'a Token>>,
) -> Result<Expression, ()> {
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<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)]
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))
)),
)],
);
}
}

Loading…
Cancel
Save