From b9d9f5b87e74e528cd4fba23afdd70d8a42213a9 Mon Sep 17 00:00:00 2001 From: Victor Timofei Date: Sun, 10 Sep 2023 23:36:10 +0300 Subject: [PATCH] add operator precedences tests --- src/parser.rs | 270 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 2 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 827bda4..0da65ad 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -418,8 +418,8 @@ mod tests { use crate::{ ast::{ - Boolean, ExpressionStatement, Identifier, Infix, InfixOperator, IntegerLiteral, Let, - Prefix, PrefixOperator, Program, Return, Statement, + Boolean, DummyExpression, Expression, ExpressionStatement, Identifier, Infix, + InfixOperator, IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement, }, lexer::Lexer, token::{Token, TokenType}, @@ -708,6 +708,272 @@ mod tests { } } + #[test] + fn operator_precedences() { + struct Test { + input: String, + expression: Rc, + } + + let tests = vec![ + Test { + input: "-a * b".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(Prefix { + operator: PrefixOperator::Minus, + right: Rc::new(Identifier { + value: "a".into(), + token_type: TokenType::Ident, + }), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + }), + }, + Test { + input: "!-a".into(), + expression: Rc::new(Prefix { + operator: PrefixOperator::Bang, + right: Rc::new(Prefix { + operator: PrefixOperator::Minus, + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + }), + }), + }, + Test { + input: "a + b + c".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "c".into(), + }), + }), + }, + Test { + input: "a + b - c".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Minus, + left: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "c".into(), + }), + }), + }, + Test { + input: "a * b * c".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "c".into(), + }), + }), + }, + Test { + input: "a * b / c".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Slash, + left: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "c".into(), + }), + }), + }, + Test { + input: "a + b / c".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + right: Rc::new(Infix { + operator: InfixOperator::Slash, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "c".into(), + }), + }), + }), + }, + Test { + input: "a + b * c + d / e - f".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Minus, + left: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "a".into(), + }), + right: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "b".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "c".into(), + }), + }), + }), + right: Rc::new(Infix { + operator: InfixOperator::Slash, + left: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "d".into(), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "e".into(), + }), + }), + }), + right: Rc::new(Identifier { + token_type: TokenType::Ident, + value: "f".into(), + }), + }), + }, + Test { + input: "5 > 4 == 3 < 4".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Eq, + left: Rc::new(Infix { + operator: InfixOperator::Gt, + left: Rc::new(IntegerLiteral { value: 5 }), + right: Rc::new(IntegerLiteral { value: 4 }), + }), + right: Rc::new(Infix { + operator: InfixOperator::Lt, + left: Rc::new(IntegerLiteral { value: 3 }), + right: Rc::new(IntegerLiteral { value: 4 }), + }), + }), + }, + Test { + input: "5 < 4 != 3 > 4".into(), + expression: Rc::new(Infix { + operator: InfixOperator::NotEq, + left: Rc::new(Infix { + operator: InfixOperator::Lt, + left: Rc::new(IntegerLiteral { value: 5 }), + right: Rc::new(IntegerLiteral { value: 4 }), + }), + right: Rc::new(Infix { + operator: InfixOperator::Gt, + left: Rc::new(IntegerLiteral { value: 3 }), + right: Rc::new(IntegerLiteral { value: 4 }), + }), + }), + }, + Test { + input: "3 + 4 * 5 == 3 * 1 + 4 * 5".into(), + expression: Rc::new(Infix { + operator: InfixOperator::Eq, + left: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(IntegerLiteral { value: 3 }), + right: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(IntegerLiteral { value: 4 }), + right: Rc::new(IntegerLiteral { value: 5 }), + }), + }), + right: Rc::new(Infix { + operator: InfixOperator::Plus, + left: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(IntegerLiteral { value: 3 }), + right: Rc::new(IntegerLiteral { value: 1 }), + }), + right: Rc::new(Infix { + operator: InfixOperator::Asterisk, + left: Rc::new(IntegerLiteral { value: 4 }), + right: Rc::new(IntegerLiteral { value: 5 }), + }), + }), + }), + }, + ]; + + for test in tests { + let l = Lexer::new(test.input); + + let mut parser = Parser::new(l); + + let program = parser.parse().unwrap(); + check_parser_errors(parser); + + assert_eq!( + program, + Program { + statements: vec![Rc::new(ExpressionStatement { + expression: test.expression + })] + } + ); + } + } + fn check_parser_errors(parser: Parser) { if parser.errors().len() == 0 { return;