Tweak precedence handling in the parser
continuous-integration/drone/push Build is passing Details

main
Clément FRÉVILLE 2 years ago
parent 55848c5b36
commit 1311897416

@ -284,4 +284,18 @@ mod tests {
];
assert!(!is_valid_guess_of_tokens(&tokens));
}
#[test]
fn is_valid_guess_of_tiles_precedence() {
let tokens = vec![
Token::NumberLiteral(5),
Token::Operator(Operator::Add),
Token::NumberLiteral(-2),
Token::Operator(Operator::Multiply),
Token::NumberLiteral(3),
Token::Equals,
Token::NumberLiteral(-1),
];
assert!(is_valid_guess_of_tokens(&tokens));
}
}

@ -30,53 +30,60 @@ pub fn parse(tokens: &[Token]) -> Result<Expressions, ()> {
let mut tokens = tokens.iter().peekable();
let mut expressions = Vec::new();
while tokens.peek().is_some() {
expressions.push(parse_expression(&mut tokens)?);
expressions.push(parse_term(&mut tokens)?);
tokens.next();
}
Ok(expressions)
}
fn parse_expression<'a>(
fn parse_primary<'a>(
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
) -> Result<Expression, ()> {
let mut left = parse_term(tokens)?;
while let Some(Token::Operator(operator)) = tokens.peek() {
let operator = *operator;
if let Some(Token::NumberLiteral(value)) = tokens.peek() {
tokens.next();
Ok(Expression::Digit(*value))
} else if let Some(Token::LeftParen) = tokens.peek() {
tokens.next();
let right = parse_term(tokens)?;
left = Expression::Binary(operator, Box::new(left), Box::new(right));
let expr = parse_term(tokens)?;
if let Some(Token::RightParen) = tokens.peek() {
tokens.next();
Ok(Expression::Parentheses(Box::new(expr)))
} else {
Err(())
}
} else {
Err(())
}
Ok(left)
}
fn parse_term<'a>(
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
) -> Result<Expression, ()> {
let mut left = parse_unary(tokens)?;
let mut expr = parse_factor(tokens)?;
while let Some(Token::Operator(operator)) = tokens.peek() {
let operator = *operator;
if !matches!(operator, Operator::Add | Operator::Subtract) {
break;
}
tokens.next();
let right = parse_unary(tokens)?;
left = Expression::Binary(operator, Box::new(left), Box::new(right));
let right = parse_factor(tokens)?;
expr = Expression::Binary(*operator, Box::new(expr), Box::new(right));
}
Ok(left)
Ok(expr)
}
fn parse_factor<'a>(
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
) -> Result<Expression, ()> {
match tokens.next() {
Some(Token::NumberLiteral(value)) => Ok(Expression::Digit(*value)),
Some(Token::LeftParen) => {
let expression = parse_expression(tokens)?;
if let Some(Token::RightParen) = tokens.next() {
Ok(Expression::Parentheses(Box::new(expression)))
} else {
Err(())
}
let mut expr = parse_unary(tokens)?;
while let Some(Token::Operator(operator)) = tokens.peek() {
if !matches!(operator, Operator::Multiply | Operator::Divide) {
break;
}
_ => Err(()),
tokens.next();
let right = parse_unary(tokens)?;
expr = Expression::Binary(*operator, Box::new(expr), Box::new(right));
}
Ok(expr)
}
fn parse_unary<'a>(
@ -87,7 +94,7 @@ fn parse_unary<'a>(
let expression = parse_unary(tokens)?;
Ok(Expression::Unary(Operator::Subtract, Box::new(expression)))
} else {
parse_factor(tokens)
parse_primary(tokens)
}
}
@ -108,13 +115,37 @@ mod tests {
assert_eq!(
expression,
vec![Expression::Binary(
Operator::Multiply,
Operator::Add,
Box::new(Expression::Digit(1)),
Box::new(Expression::Binary(
Operator::Add,
Box::new(Expression::Digit(1)),
Operator::Multiply,
Box::new(Expression::Digit(2)),
Box::new(Expression::Digit(3)),
)),
Box::new(Expression::Digit(3)),
)],
);
}
#[test]
fn test_parse_reverse() {
let tokens = vec![
Token::NumberLiteral(2),
Token::Operator(Operator::Multiply),
Token::NumberLiteral(3),
Token::Operator(Operator::Add),
Token::NumberLiteral(1),
];
let expression = parse(&tokens).unwrap();
assert_eq!(
expression,
vec![Expression::Binary(
Operator::Add,
Box::new(Expression::Binary(
Operator::Multiply,
Box::new(Expression::Digit(2)),
Box::new(Expression::Digit(3)),
)),
Box::new(Expression::Digit(1)),
)],
);
}

Loading…
Cancel
Save