add operator precedences tests
This commit is contained in:
parent
d84878c323
commit
b9d9f5b87e
270
src/parser.rs
270
src/parser.rs
|
@ -418,8 +418,8 @@ mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Boolean, ExpressionStatement, Identifier, Infix, InfixOperator, IntegerLiteral, Let,
|
Boolean, DummyExpression, Expression, ExpressionStatement, Identifier, Infix,
|
||||||
Prefix, PrefixOperator, Program, Return, Statement,
|
InfixOperator, IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement,
|
||||||
},
|
},
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
token::{Token, TokenType},
|
token::{Token, TokenType},
|
||||||
|
@ -708,6 +708,272 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operator_precedences() {
|
||||||
|
struct Test {
|
||||||
|
input: String,
|
||||||
|
expression: Rc<dyn Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
fn check_parser_errors(parser: Parser) {
|
||||||
if parser.errors().len() == 0 {
|
if parser.errors().len() == 0 {
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue