Tweak precedence handling in the parser
This commit is contained in:
@@ -284,4 +284,18 @@ mod tests {
|
|||||||
];
|
];
|
||||||
assert!(!is_valid_guess_of_tokens(&tokens));
|
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 tokens = tokens.iter().peekable();
|
||||||
let mut expressions = Vec::new();
|
let mut expressions = Vec::new();
|
||||||
while tokens.peek().is_some() {
|
while tokens.peek().is_some() {
|
||||||
expressions.push(parse_expression(&mut tokens)?);
|
expressions.push(parse_term(&mut tokens)?);
|
||||||
tokens.next();
|
tokens.next();
|
||||||
}
|
}
|
||||||
Ok(expressions)
|
Ok(expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expression<'a>(
|
fn parse_primary<'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_term(tokens)?;
|
if let Some(Token::NumberLiteral(value)) = tokens.peek() {
|
||||||
while let Some(Token::Operator(operator)) = tokens.peek() {
|
|
||||||
let operator = *operator;
|
|
||||||
tokens.next();
|
tokens.next();
|
||||||
let right = parse_term(tokens)?;
|
Ok(Expression::Digit(*value))
|
||||||
left = Expression::Binary(operator, Box::new(left), Box::new(right));
|
} else if let Some(Token::LeftParen) = tokens.peek() {
|
||||||
|
tokens.next();
|
||||||
|
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>(
|
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_unary(tokens)?;
|
let mut expr = parse_factor(tokens)?;
|
||||||
while let Some(Token::Operator(operator)) = tokens.peek() {
|
while let Some(Token::Operator(operator)) = tokens.peek() {
|
||||||
let operator = *operator;
|
if !matches!(operator, Operator::Add | Operator::Subtract) {
|
||||||
tokens.next();
|
break;
|
||||||
let right = parse_unary(tokens)?;
|
|
||||||
left = Expression::Binary(operator, Box::new(left), Box::new(right));
|
|
||||||
}
|
}
|
||||||
Ok(left)
|
tokens.next();
|
||||||
|
let right = parse_factor(tokens)?;
|
||||||
|
expr = Expression::Binary(*operator, Box::new(expr), Box::new(right));
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_factor<'a>(
|
fn parse_factor<'a>(
|
||||||
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
|
tokens: &mut Peekable<impl Iterator<Item = &'a Token>>,
|
||||||
) -> Result<Expression, ()> {
|
) -> Result<Expression, ()> {
|
||||||
match tokens.next() {
|
let mut expr = parse_unary(tokens)?;
|
||||||
Some(Token::NumberLiteral(value)) => Ok(Expression::Digit(*value)),
|
while let Some(Token::Operator(operator)) = tokens.peek() {
|
||||||
Some(Token::LeftParen) => {
|
if !matches!(operator, Operator::Multiply | Operator::Divide) {
|
||||||
let expression = parse_expression(tokens)?;
|
break;
|
||||||
if let Some(Token::RightParen) = tokens.next() {
|
|
||||||
Ok(Expression::Parentheses(Box::new(expression)))
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
|
tokens.next();
|
||||||
|
let right = parse_unary(tokens)?;
|
||||||
|
expr = Expression::Binary(*operator, Box::new(expr), Box::new(right));
|
||||||
}
|
}
|
||||||
_ => Err(()),
|
Ok(expr)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unary<'a>(
|
fn parse_unary<'a>(
|
||||||
@@ -87,7 +94,7 @@ fn parse_unary<'a>(
|
|||||||
let expression = parse_unary(tokens)?;
|
let expression = parse_unary(tokens)?;
|
||||||
Ok(Expression::Unary(Operator::Subtract, Box::new(expression)))
|
Ok(Expression::Unary(Operator::Subtract, Box::new(expression)))
|
||||||
} else {
|
} else {
|
||||||
parse_factor(tokens)
|
parse_primary(tokens)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,13 +115,37 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
expression,
|
expression,
|
||||||
vec![Expression::Binary(
|
vec![Expression::Binary(
|
||||||
Operator::Multiply,
|
|
||||||
Box::new(Expression::Binary(
|
|
||||||
Operator::Add,
|
Operator::Add,
|
||||||
Box::new(Expression::Digit(1)),
|
Box::new(Expression::Digit(1)),
|
||||||
|
Box::new(Expression::Binary(
|
||||||
|
Operator::Multiply,
|
||||||
Box::new(Expression::Digit(2)),
|
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)),
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user