#[cfg(test)]
mod tests {
use finx::lexer::Token;
use finx::parser::{Expr, Parser, Stmt};
use logos::Logos;
fn lex(input: &str) -> Vec<Token> {
Token::lexer(input)
.map(|t| t.unwrap_or(Token::Error))
.filter(|t| *t != Token::Error)
.collect()
}
#[test]
fn test_parse_let() {
let tokens = lex("let a = 42;");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::Let {
name: "a".to_string(),
value: Expr::Number(42.0),
}]
);
}
#[test]
fn test_parse_print() {
let tokens = lex("print(\"hi\");");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("hi".to_string())],
})]
);
}
#[test]
fn test_parse_expr_stmt() {
let tokens = lex("a;");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(ast, vec![Stmt::Expr(Expr::Identifier("a".to_string()))]);
}
#[test]
fn test_variable_declaration_and_redefinition() {
let tokens = lex("let a = 1; let b = \"a\"; let c = false;");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![
Stmt::Let {
name: "a".to_string(),
value: Expr::Number(1.0)
},
Stmt::Let {
name: "b".to_string(),
value: Expr::String("a".to_string())
},
Stmt::Let {
name: "c".to_string(),
value: Expr::Bool(false)
},
]
);
}
#[test]
fn test_variable_declaration_with_other_variable() {
let tokens = lex("let a = 1; let b = a;");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![
Stmt::Let {
name: "a".to_string(),
value: Expr::Number(1.0)
},
Stmt::Let {
name: "b".to_string(),
value: Expr::Identifier("a".to_string())
},
]
);
}
#[test]
fn test_function_definition_and_call() {
let tokens = lex("fn yap(msg) { return msg; } yap(\"Hello, World!\");");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![
Stmt::Fn {
name: "yap".to_string(),
params: vec!["msg".to_string()],
body: vec![Stmt::Return(Expr::Identifier("msg".to_string())),],
},
Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("yap".to_string())),
args: vec![Expr::String("Hello, World!".to_string())],
})
]
);
}
#[test]
fn test_if_number_condition() {
let tokens = lex("if 1 { print(\"yes\"); }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::If {
cond: Expr::Number(1.0),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("yes".to_string())],
})],
else_branch: None,
}]
);
}
#[test]
fn test_if_else_number_condition() {
let tokens = lex("if 1 { print(\"yes\"); } else { print(\"no\"); }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::If {
cond: Expr::Number(1.0),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("yes".to_string())],
})],
else_branch: Some(Box::new(Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("no".to_string())],
}))),
}]
);
}
#[test]
fn test_if_else_if_condition() {
let tokens =
lex("if 1 { print(\"yes\"); } else if 2 { print(\"maybe\"); } else { print(\"no\"); }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::If {
cond: Expr::Number(1.0),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("yes".to_string())],
})],
else_branch: Some(Box::new(Stmt::If {
cond: Expr::Number(2.0),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("maybe".to_string())],
})],
else_branch: Some(Box::new(Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("no".to_string())],
}))),
}))
}]
);
}
#[test]
fn test_if_variable_truthy() {
let tokens = lex("if a { print(\"a exists and is not null\"); }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::If {
cond: Expr::Identifier("a".to_string()),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("a exists and is not null".to_string())],
})],
else_branch: None
}]
);
}
#[test]
fn test_if_bool_condition() {
let tokens = lex("if false { print(\"no\"); }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::If {
cond: Expr::Bool(false),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("no".to_string())],
})],
else_branch: None
}]
);
}
#[test]
fn test_if_nested() {
let tokens = lex("if a { if b { print(\"x\"); } }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::If {
cond: Expr::Identifier("a".to_string()),
then_branch: vec![Stmt::If {
cond: Expr::Identifier("b".to_string()),
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("x".to_string())],
})],
else_branch: None
}],
else_branch: None
}]
);
}
#[test]
fn test_if_with_operators() {
let tokens = lex("if 1 == 2 { print(\"no\"); } if a != b { print(\"yes\"); }");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![
Stmt::If {
cond: Expr::Binary {
left: Box::new(Expr::Number(1.0)),
op: Token::EqEq,
right: Box::new(Expr::Number(2.0)),
},
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("no".to_string())],
})],
else_branch: None
},
Stmt::If {
cond: Expr::Binary {
left: Box::new(Expr::Identifier("a".to_string())),
op: Token::BangEq,
right: Box::new(Expr::Identifier("b".to_string())),
},
then_branch: vec![Stmt::Expr(Expr::Call {
callee: Box::new(Expr::Identifier("print".to_string())),
args: vec![Expr::String("yes".to_string())],
})],
else_branch: None
},
]
);
}
#[test]
fn test_arithmetic_order_of_operations() {
let tokens = lex("let x = 1 + 2 * 3 - 4 / 2;");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::Let {
name: "x".to_string(),
value: Expr::Binary {
left: Box::new(Expr::Binary {
left: Box::new(Expr::Number(1.0)),
op: Token::Plus,
right: Box::new(Expr::Binary {
left: Box::new(Expr::Number(2.0)),
op: Token::Star,
right: Box::new(Expr::Number(3.0)),
}),
}),
op: Token::Minus,
right: Box::new(Expr::Binary {
left: Box::new(Expr::Number(4.0)),
op: Token::Slash,
right: Box::new(Expr::Number(2.0)),
}),
},
}]
);
}
#[test]
fn test_arithmetic_parentheses() {
let tokens = lex("let y = (1 + 2) * (3 - 4) / 2;");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::Let {
name: "y".to_string(),
value: Expr::Binary {
left: Box::new(Expr::Binary {
left: Box::new(Expr::Binary {
left: Box::new(Expr::Number(1.0)),
op: Token::Plus,
right: Box::new(Expr::Number(2.0)),
}),
op: Token::Star,
right: Box::new(Expr::Binary {
left: Box::new(Expr::Number(3.0)),
op: Token::Minus,
right: Box::new(Expr::Number(4.0)),
}),
}),
op: Token::Slash,
right: Box::new(Expr::Number(2.0)),
},
}]
);
}
#[test]
fn test_empty_input() {
let tokens = lex("");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(ast, vec![]);
}
#[test]
fn test_let_missing_semicolon() {
let tokens = lex("let a = 1");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(
ast,
vec![Stmt::Let {
name: "a".to_string(),
value: Expr::Number(1.0)
}]
);
}
#[test]
fn test_let_missing_value() {
let tokens = lex("let a = ;");
let mut parser = Parser::new(&tokens);
let result = parser.parse();
assert!(result.is_err());
}
#[test]
fn test_function_call_exact_args() {
let tokens = lex("fn f(x, y) { print(x); } f(1, 2);");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(ast.len(), 2);
}
#[test]
fn test_function_call_zero_args() {
let tokens = lex("fn f() { print(1); } f();");
let mut parser = Parser::new(&tokens);
let ast = parser.parse().unwrap();
assert_eq!(ast.len(), 2);
}
#[test]
fn test_function_call_missing_paren() {
let tokens = lex("fn f(x) { print(x); } f(1;");
let mut parser = Parser::new(&tokens);
let result = parser.parse();
assert!(result.is_err() || result.is_ok()); }
}