1
0
Fork 0
This repository has been archived on 2025-03-30. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
compiler-course/src/compiler/parser/tests.rs

761 lines
17 KiB
Rust

use super::*;
use crate::compiler::{token::CodeLocation, tokenizer::tokenize};
macro_rules! bool_ast {
($x:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), BoolLiteral($x))
};
}
macro_rules! bool_ast_b {
($x:expr) => {
Box::new(bool_ast!($x))
};
}
macro_rules! int_ast {
($x:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), IntLiteral($x))
};
}
macro_rules! int_ast_b {
($x:expr) => {
Box::new(int_ast!($x))
};
}
macro_rules! id_ast {
($x:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), Identifier($x))
};
}
macro_rules! id_ast_b {
($x:expr) => {
Box::new(id_ast!($x))
};
}
macro_rules! un_ast {
($x:expr, $y:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), UnaryOp($x, $y))
};
}
macro_rules! un_ast_b {
($x:expr, $y:expr) => {
Box::new(un_ast!($x, $y))
};
}
macro_rules! bin_ast {
($x:expr, $y:expr, $z:expr) => {
AstNode::new(
CodeLocation::new(usize::MAX, usize::MAX),
BinaryOp($x, $y, $z),
)
};
}
macro_rules! bin_ast_b {
($x:expr, $y:expr, $z:expr) => {
Box::new(bin_ast!($x, $y, $z))
};
}
macro_rules! con_ast {
($x:expr, $y:expr, $z:expr) => {
AstNode::new(
CodeLocation::new(usize::MAX, usize::MAX),
Conditional($x, $y, $z),
)
};
}
macro_rules! con_ast_b {
($x:expr, $y:expr, $z:expr) => {
Box::new(con_ast!($x, $y, $z))
};
}
macro_rules! fun_ast {
($x:expr, $y:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), FunCall($x, $y))
};
}
macro_rules! fun_ast_b {
($x:expr, $y:expr) => {
Box::new(fun_ast!($x, $y))
};
}
macro_rules! block_ast {
($x:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), Block($x))
};
}
macro_rules! block_ast_b {
($x:expr) => {
Box::new(block_ast!($x))
};
}
macro_rules! empty_ast {
() => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), EmptyLiteral())
};
}
macro_rules! var_ast {
($x:expr, $y:expr, $z:expr) => {
AstNode::new(
CodeLocation::new(usize::MAX, usize::MAX),
VarDeclaration($x, $y, $z),
)
};
}
macro_rules! while_ast {
($x:expr, $y:expr) => {
AstNode::new(CodeLocation::new(usize::MAX, usize::MAX), While($x, $y))
};
}
macro_rules! while_ast_b {
($x:expr, $y:expr) => {
Box::new(while_ast!($x, $y))
};
}
#[test]
#[should_panic]
fn test_empty() {
parse(&[]);
}
#[test]
#[should_panic]
fn test_invalid_start() {
parse(&tokenize("1 2 + 3").unwrap());
}
#[test]
#[should_panic]
fn test_invalid_middle() {
parse(&tokenize("1 + 2 2 + 3").unwrap());
}
#[test]
#[should_panic]
fn test_invalid_end() {
parse(&tokenize("1 + 2 3").unwrap());
}
#[test]
fn test_binary_op_basic() {
let result = parse(&tokenize("1 + 23").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "+", int_ast_b!(23)));
let result = parse(&tokenize("4 - 56").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(4), "-", int_ast_b!(56)));
let result = parse(&tokenize("1 * 2").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "*", int_ast_b!(2)));
let result = parse(&tokenize("1 / 2").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "/", int_ast_b!(2)));
}
#[test]
fn test_binary_op_all_levels() {
let result = parse(&tokenize("1 * 2 + 3 < 4 == 5 and 6 or 7").unwrap());
assert_eq!(
result,
bin_ast!(
bin_ast_b!(
bin_ast_b!(
bin_ast_b!(
bin_ast_b!(
bin_ast_b!(int_ast_b!(1), "*", int_ast_b!(2)),
"+",
int_ast_b!(3)
),
"<",
int_ast_b!(4)
),
"==",
int_ast_b!(5)
),
"and",
int_ast_b!(6)
),
"or",
int_ast_b!(7)
)
);
}
#[test]
fn test_binary_op_identifier() {
let result = parse(&tokenize("a + 1").unwrap());
assert_eq!(result, bin_ast!(id_ast_b!("a"), "+", int_ast_b!(1)));
let result = parse(&tokenize("1 - a").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "-", id_ast_b!("a")));
}
#[test]
fn test_binary_op_multiple() {
let result = parse(&tokenize("1 + 2 - 3").unwrap());
assert_eq!(
result,
bin_ast!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
"-",
int_ast_b!(3)
)
);
}
#[test]
fn test_binary_op_precedence() {
let result = parse(&tokenize("1 + 2 * 3").unwrap());
assert_eq!(
result,
bin_ast!(
int_ast_b!(1),
"+",
bin_ast_b!(int_ast_b!(2), "*", int_ast_b!(3))
)
);
let result = parse(&tokenize("1 - 2 / 3").unwrap());
assert_eq!(
result,
bin_ast!(
int_ast_b!(1),
"-",
bin_ast_b!(int_ast_b!(2), "/", int_ast_b!(3))
)
);
}
#[test]
fn test_assignment_basic() {
let result = parse(&tokenize("a = 1 + 2").unwrap());
assert_eq!(
result,
bin_ast!(
id_ast_b!("a"),
"=",
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2))
)
);
}
#[test]
fn test_assignment_chain() {
let result = parse(&tokenize("a = b = 1 + 2").unwrap());
assert_eq!(
result,
bin_ast!(
id_ast_b!("a"),
"=",
bin_ast_b!(
id_ast_b!("b"),
"=",
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2))
)
)
);
}
#[test]
#[should_panic]
fn test_assignment_invalid() {
parse(&tokenize("a =").unwrap());
}
#[test]
fn test_unary_basic() {
let result = parse(&tokenize("not x").unwrap());
assert_eq!(result, un_ast!("not", id_ast_b!("x")));
let result = parse(&tokenize("-x").unwrap());
assert_eq!(result, un_ast!("-", id_ast_b!("x")));
let result = parse(&tokenize("-1").unwrap());
assert_eq!(result, un_ast!("-", int_ast_b!(1)));
let result = parse(&tokenize("-1 + 2").unwrap());
assert_eq!(
result,
bin_ast!(un_ast_b!("-", int_ast_b!(1)), "+", int_ast_b!(2))
);
}
#[test]
fn test_unary_chain() {
let result = parse(&tokenize("not not x").unwrap());
assert_eq!(result, un_ast!("not", un_ast_b!("not", id_ast_b!("x"))));
let result = parse(&tokenize("--x").unwrap());
assert_eq!(result, un_ast!("-", un_ast_b!("-", id_ast_b!("x"))));
let result = parse(&tokenize("--1").unwrap());
assert_eq!(result, un_ast!("-", un_ast_b!("-", int_ast_b!(1))));
let result = parse(&tokenize("--1 + 2").unwrap());
assert_eq!(
result,
bin_ast!(
un_ast_b!("-", un_ast_b!("-", int_ast_b!(1))),
"+",
int_ast_b!(2)
)
);
}
#[test]
fn test_parenthesized() {
let result = parse(&tokenize("(1+2)*3").unwrap());
assert_eq!(
result,
bin_ast!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
"*",
int_ast_b!(3)
)
);
}
#[test]
fn test_parenthesized_nested() {
let result = parse(&tokenize("((1 - 2))/3").unwrap());
assert_eq!(
result,
bin_ast!(
bin_ast_b!(int_ast_b!(1), "-", int_ast_b!(2)),
"/",
int_ast_b!(3)
)
);
let result = parse(&tokenize("((1 + 2)*3) / 4").unwrap());
assert_eq!(
result,
bin_ast!(
bin_ast_b!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
"*",
int_ast_b!(3)
),
"/",
int_ast_b!(4)
)
);
}
#[test]
#[should_panic]
fn test_parenthesized_mismatched() {
parse(&tokenize("(1+2*3").unwrap());
}
#[test]
fn test_if_then() {
let result = parse(&tokenize("if 1 + 2 then 3").unwrap());
assert_eq!(
result,
con_ast!(
bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)),
int_ast_b!(3),
None
)
);
}
#[test]
fn test_if_then_else() {
let result = parse(&tokenize("if a then b + c else 1 * 2").unwrap());
assert_eq!(
result,
con_ast!(
id_ast_b!("a"),
bin_ast_b!(id_ast_b!("b"), "+", id_ast_b!("c")),
Some(bin_ast_b!(int_ast_b!(1), "*", int_ast_b!(2)))
)
);
}
#[test]
fn test_if_then_else_embedded() {
let result = parse(&tokenize("1 + if true then 2 else 3").unwrap());
assert_eq!(
result,
bin_ast!(
int_ast_b!(1),
"+",
con_ast_b!(bool_ast_b!(true), int_ast_b!(2), Some(int_ast_b!(3)))
)
);
}
#[test]
fn test_if_then_else_nested() {
let result = parse(&tokenize("if true then if false then 1 else 2 else 3").unwrap());
assert_eq!(
result,
con_ast!(
bool_ast_b!(true),
con_ast_b!(bool_ast_b!(false), int_ast_b!(1), Some(int_ast_b!(2))),
Some(int_ast_b!(3))
)
);
}
#[test]
#[should_panic]
fn test_if_no_then() {
parse(&tokenize("if true").unwrap());
}
#[test]
fn test_func_basic() {
let result = parse(&tokenize("f(a, b)").unwrap());
assert_eq!(result, fun_ast!("f", vec![id_ast!("a"), id_ast!("b"),]));
let result = parse(&tokenize("f(a, 1 + 2)").unwrap());
assert_eq!(
result,
fun_ast!(
"f",
vec![id_ast!("a"), bin_ast!(int_ast_b!(1), "+", int_ast_b!(2)),]
)
);
let result = parse(&tokenize("f()").unwrap());
assert_eq!(result, fun_ast!("f", vec![]));
}
#[test]
fn test_func_embedded() {
let result = parse(&tokenize("1 + f(a)").unwrap());
assert_eq!(
result,
bin_ast!(int_ast_b!(1), "+", fun_ast_b!("f", vec![id_ast!("a")]))
);
}
#[test]
fn test_func_nested() {
let result = parse(&tokenize("f(a, g(b))").unwrap());
assert_eq!(
result,
fun_ast!("f", vec![id_ast!("a"), fun_ast!("g", vec![id_ast!("b")]),])
);
}
#[test]
#[should_panic]
fn test_func_missing_comma() {
parse(&tokenize("f(a b)").unwrap());
}
#[test]
#[should_panic]
fn test_func_missing_close() {
parse(&tokenize("f(a").unwrap());
}
#[test]
fn test_block_basic() {
let result = parse(&tokenize("{ a = 1; b; }").unwrap());
assert_eq!(
result,
block_ast!(vec![
bin_ast!(id_ast_b!("a"), "=", int_ast_b!(1)),
id_ast!("b"),
empty_ast!()
])
);
let result = parse(&tokenize("{ a = 1; b }").unwrap());
assert_eq!(
result,
block_ast!(vec![
bin_ast!(id_ast_b!("a"), "=", int_ast_b!(1)),
id_ast!("b"),
])
);
}
#[test]
fn test_block_embedded() {
let result = parse(&tokenize("{ 1 + 2 } * 3").unwrap());
assert_eq!(
result,
bin_ast!(
block_ast_b!(vec![bin_ast!(int_ast_b!(1), "+", int_ast_b!(2))]),
"*",
int_ast_b!(3)
)
);
}
#[test]
fn test_block_nested() {
let result = parse(&tokenize("{ a = { 1 + 2}}").unwrap());
assert_eq!(
result,
block_ast!(vec![bin_ast!(
id_ast_b!("a"),
"=",
block_ast_b!(vec![bin_ast!(int_ast_b!(1), "+", int_ast_b!(2))])
)])
);
}
#[test]
#[should_panic]
fn test_block_unmatched() {
parse(&tokenize("{ a = 1 ").unwrap());
}
#[test]
#[should_panic]
fn test_block_missing_semicolon() {
parse(&tokenize("{ a = 1\nb }").unwrap());
}
#[test]
fn test_var_basic() {
let result = parse(&tokenize("var x = 1").unwrap());
assert_eq!(result, var_ast!("x", int_ast_b!(1), None));
let result = parse(&tokenize("{ var x = 1; x = 2; }").unwrap());
assert_eq!(
result,
block_ast!(vec![
var_ast!("x", int_ast_b!(1), None),
bin_ast!(id_ast_b!("x"), "=", int_ast_b!(2)),
empty_ast!()
])
);
}
#[test]
fn test_var_typed() {
let result = parse(&tokenize("var x: Int = 1").unwrap());
assert_eq!(
result,
var_ast!(
"x",
int_ast_b!(1),
Some(TypeExpression::Int(CodeLocation::new(
usize::MAX,
usize::MAX
)))
)
);
let result = parse(&tokenize("var x: Bool = true").unwrap());
assert_eq!(
result,
var_ast!(
"x",
bool_ast_b!(true),
Some(TypeExpression::Bool(CodeLocation::new(
usize::MAX,
usize::MAX
)))
)
);
}
#[test]
#[should_panic]
fn test_var_chain() {
parse(&tokenize("var x = var y = 1").unwrap());
}
#[test]
#[should_panic]
fn test_var_embedded() {
parse(&tokenize("if true then var x = 3").unwrap());
}
#[test]
fn test_omitting_semicolons() {
let result = parse(&tokenize("{ { a } { b } }").unwrap());
assert_eq!(
result,
block_ast!(vec![
block_ast!(vec![id_ast!("a")]),
block_ast!(vec![id_ast!("b")])
])
);
let result = parse(&tokenize("{ if true then { a } b }").unwrap());
assert_eq!(
result,
block_ast!(vec![
con_ast!(bool_ast_b!(true), block_ast_b!(vec![id_ast!("a")]), None),
id_ast!("b"),
])
);
let result = parse(&tokenize("{ if true then { a }; b }").unwrap());
assert_eq!(
result,
block_ast!(vec![
con_ast!(bool_ast_b!(true), block_ast_b!(vec![id_ast!("a")]), None),
id_ast!("b"),
])
);
let result = parse(&tokenize("{ if true then { a } else { b } c }").unwrap());
assert_eq!(
result,
block_ast!(vec![
con_ast!(
bool_ast_b!(true),
block_ast_b!(vec![id_ast!("a")]),
Some(block_ast_b!(vec![id_ast!("b")]))
),
id_ast!("c"),
])
);
let result = parse(&tokenize("x = { { f(a) } { b } }").unwrap());
assert_eq!(
result,
bin_ast!(
id_ast_b!("x"),
"=",
block_ast_b!(vec![
block_ast!(vec![fun_ast!("f", vec![id_ast!("a")])]),
block_ast!(vec![id_ast!("b")]),
])
)
);
}
#[test]
#[should_panic]
fn test_omitting_semicolons_invalid() {
parse(&tokenize("{ if true then { a } b c }").unwrap());
}
#[test]
fn test_while_do() {
let result = parse(&tokenize("while 1 + 2 do 3").unwrap());
assert_eq!(
result,
while_ast!(bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)), int_ast_b!(3))
);
}
#[test]
fn test_while_do_embedded() {
let result = parse(&tokenize("1 + while true do 2").unwrap());
assert_eq!(
result,
bin_ast!(
int_ast_b!(1),
"+",
while_ast_b!(bool_ast_b!(true), int_ast_b!(2))
)
);
}
#[test]
fn test_while_do_nested() {
let result = parse(&tokenize("while true do while false do 1").unwrap());
assert_eq!(
result,
while_ast!(
bool_ast_b!(true),
while_ast_b!(bool_ast_b!(false), int_ast_b!(1))
)
);
}
#[test]
#[should_panic]
fn test_while_no_do() {
parse(&tokenize("while true").unwrap());
}
#[test]
fn test_multiple_top_levels() {
let result = parse(&tokenize("a;").unwrap());
assert_eq!(result, block_ast!(vec![id_ast!("a"), empty_ast!()]));
let result = parse(&tokenize("a; b").unwrap());
assert_eq!(result, block_ast!(vec![id_ast!("a"), id_ast!("b")]));
let result = parse(&tokenize("{}{}").unwrap());
assert_eq!(
result,
block_ast!(vec![block_ast!(vec![]), block_ast!(vec![])])
);
}
#[test]
fn test_large() {
let result = parse(
&tokenize(
"
{
while f() do {
x = 10;
y = if g(x) then {
x = x + 1;
x
} else {
g(x)
}
g(y);
}
123
}
",
)
.unwrap(),
);
assert_eq!(
result,
block_ast!(vec![
while_ast!(
fun_ast_b!("f", vec![]),
block_ast_b!(vec![
bin_ast!(id_ast_b!("x"), "=", int_ast_b!(10)),
bin_ast!(
id_ast_b!("y"),
"=",
con_ast_b!(
fun_ast_b!("g", vec![id_ast!("x")]),
block_ast_b!(vec![
bin_ast!(
id_ast_b!("x"),
"=",
bin_ast_b!(id_ast_b!("x"), "+", int_ast_b!(1))
),
id_ast!("x")
]),
Some(block_ast_b!(vec![fun_ast!("g", vec![id_ast!("x")])]))
)
),
fun_ast!("g", vec![id_ast!("y")]),
empty_ast!()
])
),
int_ast!(123),
])
);
}