248 lines
5.1 KiB
Rust
248 lines
5.1 KiB
Rust
use super::*;
|
|
use crate::compiler::tokenizer::tokenize;
|
|
|
|
macro_rules! int_ast {
|
|
($x:expr) => {
|
|
Box::new(IntLiteral($x))
|
|
};
|
|
}
|
|
|
|
macro_rules! id_ast {
|
|
($x:expr) => {
|
|
Box::new(Identifier($x))
|
|
};
|
|
}
|
|
|
|
macro_rules! bin_ast {
|
|
($x:expr, $y:expr, $z:expr) => {
|
|
Box::new(BinaryOp($x, $y, $z))
|
|
};
|
|
}
|
|
|
|
macro_rules! bool_ast {
|
|
($x:expr) => {
|
|
Box::new(BoolLiteral($x))
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_empty() {
|
|
parse(&vec![]);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_invalid_start() {
|
|
parse(&tokenize("1 2 + 3"));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_invalid_middle() {
|
|
parse(&tokenize("1 + 2 2 + 3"));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_invalid_end() {
|
|
parse(&tokenize("1 + 2 3"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_binary_op_basic() {
|
|
let result = parse(&tokenize("1 + 23"));
|
|
assert_eq!(result, BinaryOp(int_ast!(1), "+", int_ast!(23)));
|
|
|
|
let result = parse(&tokenize("4 - 56"));
|
|
assert_eq!(result, BinaryOp(int_ast!(4), "-", int_ast!(56)));
|
|
|
|
let result = parse(&tokenize("1 * 2"));
|
|
assert_eq!(result, BinaryOp(int_ast!(1), "*", int_ast!(2)));
|
|
|
|
let result = parse(&tokenize("1 / 2"));
|
|
assert_eq!(result, BinaryOp(int_ast!(1), "/", int_ast!(2)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_binary_op_identifier() {
|
|
let result = parse(&tokenize("a + 1"));
|
|
assert_eq!(result, BinaryOp(id_ast!("a"), "+", int_ast!(1)));
|
|
|
|
let result = parse(&tokenize("1 - a"));
|
|
assert_eq!(result, BinaryOp(int_ast!(1), "-", id_ast!("a")));
|
|
}
|
|
|
|
#[test]
|
|
fn test_binary_op_multiple() {
|
|
let result = parse(&tokenize("1 + 2 - 3"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(bin_ast!(int_ast!(1), "+", int_ast!(2)), "-", int_ast!(3))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_binary_op_precedence() {
|
|
let result = parse(&tokenize("1 + 2 * 3"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(int_ast!(1), "+", bin_ast!(int_ast!(2), "*", int_ast!(3)),)
|
|
);
|
|
|
|
let result = parse(&tokenize("1 - 2 / 3"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(int_ast!(1), "-", bin_ast!(int_ast!(2), "/", int_ast!(3)),)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parenthesized() {
|
|
let result = parse(&tokenize("(1+2)*3"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(bin_ast!(int_ast!(1), "+", int_ast!(2)), "*", int_ast!(3),)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_parenthesized_nested() {
|
|
let result = parse(&tokenize("((1 - 2))/3"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(bin_ast!(int_ast!(1), "-", int_ast!(2)), "/", int_ast!(3),)
|
|
);
|
|
|
|
let result = parse(&tokenize("((1 + 2)*3) / 4"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(
|
|
bin_ast!(bin_ast!(int_ast!(1), "+", int_ast!(2)), "*", int_ast!(3)),
|
|
"/",
|
|
int_ast!(4)
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_parenthesized_mismatched() {
|
|
parse(&tokenize("(1+2*3"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_if_then() {
|
|
let result = parse(&tokenize("if 1 + 2 then 3"));
|
|
assert_eq!(
|
|
result,
|
|
Conditional(bin_ast!(int_ast!(1), "+", int_ast!(2)), int_ast!(3), None,)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_if_then_else() {
|
|
let result = parse(&tokenize("if a then b + c else 1 * 2"));
|
|
assert_eq!(
|
|
result,
|
|
Conditional(
|
|
id_ast!("a"),
|
|
bin_ast!(id_ast!("b"), "+", id_ast!("c")),
|
|
Some(bin_ast!(int_ast!(1), "*", int_ast!(2)))
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_if_then_else_embedded() {
|
|
let result = parse(&tokenize("1 + if true then 2 else 3"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(
|
|
int_ast!(1),
|
|
"+",
|
|
Box::new(Conditional(bool_ast!(true), int_ast!(2), Some(int_ast!(3))))
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_if_then_else_nested() {
|
|
let result = parse(&tokenize("if true then if false then 1 else 2 else 3"));
|
|
assert_eq!(
|
|
result,
|
|
Conditional(
|
|
bool_ast!(true),
|
|
Box::new(Conditional(
|
|
bool_ast!(false),
|
|
int_ast!(1),
|
|
Some(int_ast!(2))
|
|
)),
|
|
Some(int_ast!(3))
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_if_no_then() {
|
|
parse(&tokenize("if true"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_func_basic() {
|
|
let result = parse(&tokenize("f(a, b)"));
|
|
assert_eq!(
|
|
result,
|
|
FunCall("f", vec![Identifier("a"), Identifier("b"),])
|
|
);
|
|
|
|
let result = parse(&tokenize("f(a, 1 + 2)"));
|
|
assert_eq!(
|
|
result,
|
|
FunCall(
|
|
"f",
|
|
vec![Identifier("a"), BinaryOp(int_ast!(1), "+", int_ast!(2),),]
|
|
)
|
|
);
|
|
|
|
let result = parse(&tokenize("f()"));
|
|
assert_eq!(result, FunCall("f", vec![]));
|
|
}
|
|
|
|
#[test]
|
|
fn test_func_embedded() {
|
|
let result = parse(&tokenize("1 + f(a)"));
|
|
assert_eq!(
|
|
result,
|
|
BinaryOp(
|
|
int_ast!(1),
|
|
"+",
|
|
Box::new(FunCall("f", vec![Identifier("a")]))
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_func_nested() {
|
|
let result = parse(&tokenize("f(a, g(b))"));
|
|
assert_eq!(
|
|
result,
|
|
FunCall(
|
|
"f",
|
|
vec![Identifier("a"), FunCall("g", vec![Identifier("b")]),]
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_func_missing_comma() {
|
|
parse(&tokenize("f(a b)"));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_func_missing_close() {
|
|
parse(&tokenize("f(a"));
|
|
}
|