use metadol::ast::{BinaryOp, Expr, Literal, MatchArm, Pattern, UnaryOp};
use metadol::typechecker::{Type, TypeChecker};
fn int_lit(n: i64) -> Expr {
Expr::Literal(Literal::Int(n))
}
fn float_lit(n: f64) -> Expr {
Expr::Literal(Literal::Float(n))
}
fn bool_lit(b: bool) -> Expr {
Expr::Literal(Literal::Bool(b))
}
fn string_lit(s: &str) -> Expr {
Expr::Literal(Literal::String(s.to_string()))
}
fn ident(name: &str) -> Expr {
Expr::Identifier(name.to_string())
}
#[test]
fn test_quote_literal() {
let expr = Expr::Quote(Box::new(int_lit(42)));
match expr {
Expr::Quote(inner) => {
assert!(matches!(*inner, Expr::Literal(Literal::Int(42))));
}
_ => panic!("Expected Quote expression"),
}
}
#[test]
fn test_quote_binary_expr() {
let expr = Expr::Quote(Box::new(Expr::Binary {
op: BinaryOp::Add,
left: Box::new(int_lit(1)),
right: Box::new(int_lit(2)),
}));
match expr {
Expr::Quote(inner) => match *inner {
Expr::Binary { op, .. } => assert_eq!(op, BinaryOp::Add),
_ => panic!("Expected Binary expression inside Quote"),
},
_ => panic!("Expected Quote expression"),
}
}
#[test]
fn test_nested_quote() {
let expr = Expr::Quote(Box::new(Expr::Quote(Box::new(int_lit(42)))));
match expr {
Expr::Quote(inner) => {
assert!(matches!(*inner, Expr::Quote(_)));
}
_ => panic!("Expected nested Quote"),
}
}
#[test]
fn test_quote_expr_construction() {
let expr = Expr::Quote(Box::new(bool_lit(true)));
if let Expr::Quote(inner) = expr {
if let Expr::Literal(Literal::Bool(b)) = *inner {
assert!(b);
} else {
panic!("Expected Bool literal");
}
} else {
panic!("Expected Quote");
}
}
#[test]
fn test_eval_expr_construction() {
let quoted = Expr::Quote(Box::new(string_lit("hello")));
let expr = Expr::Eval(Box::new(quoted));
if let Expr::Eval(inner) = expr {
if let Expr::Quote(_) = *inner {
} else {
panic!("Expected Quote inside Eval");
}
} else {
panic!("Expected Eval");
}
}
#[test]
fn test_quote_type_is_quoted() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(int_lit(42)));
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert_eq!(args[0], Type::Int64);
}
_ => panic!("Expected Quoted type, got {:?}", ty),
}
}
#[test]
fn test_quote_preserves_inner_type() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(float_lit(1.5)));
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert_eq!(args[0], Type::Float64);
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_quote_bool_type() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(bool_lit(true)));
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert_eq!(args[0], Type::Bool);
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_quote_string_type() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(string_lit("hello")));
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert_eq!(args[0], Type::String);
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_eval_type_unwraps_quoted() {
let mut checker = TypeChecker::new();
let expr = Expr::Eval(Box::new(Expr::Quote(Box::new(int_lit(42)))));
let ty = checker.infer(&expr).unwrap();
assert_eq!(ty, Type::Int64);
}
#[test]
fn test_eval_type_unwraps_quoted_float() {
let mut checker = TypeChecker::new();
let expr = Expr::Eval(Box::new(Expr::Quote(Box::new(float_lit(1.5)))));
let ty = checker.infer(&expr).unwrap();
assert_eq!(ty, Type::Float64);
}
#[test]
fn test_eval_type_unwraps_quoted_bool() {
let mut checker = TypeChecker::new();
let expr = Expr::Eval(Box::new(Expr::Quote(Box::new(bool_lit(true)))));
let ty = checker.infer(&expr).unwrap();
assert_eq!(ty, Type::Bool);
}
#[test]
#[ignore = "Requires public access to TypeChecker environment"]
fn test_quote_identifier() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(ident("x")));
let _ty = checker.infer(&expr);
}
#[test]
fn test_quote_lambda() {
let mut checker = TypeChecker::new();
let lambda = Expr::Lambda {
params: vec![("x".to_string(), None)],
body: Box::new(Expr::Binary {
op: BinaryOp::Add,
left: Box::new(ident("x")),
right: Box::new(int_lit(1)),
}),
return_type: None,
};
let expr = Expr::Quote(Box::new(lambda));
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
match &args[0] {
Type::Function { .. } => {}
_ => panic!("Expected function type inside Quoted"),
}
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_nested_quote_type() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(Expr::Quote(Box::new(int_lit(42)))));
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic {
name: outer_name,
args: outer_args,
} => {
assert_eq!(outer_name, "Quoted");
assert_eq!(outer_args.len(), 1);
match &outer_args[0] {
Type::Generic {
name: inner_name,
args: inner_args,
} => {
assert_eq!(inner_name, "Quoted");
assert_eq!(inner_args.len(), 1);
assert_eq!(inner_args[0], Type::Int64);
}
_ => panic!("Expected nested Quoted type"),
}
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_quote_eval_roundtrip_type() {
let mut checker = TypeChecker::new();
let inner = Expr::Binary {
op: BinaryOp::Add,
left: Box::new(int_lit(1)),
right: Box::new(int_lit(2)),
};
let original_type = checker.infer(&inner).unwrap();
let quoted_then_evaled = Expr::Eval(Box::new(Expr::Quote(Box::new(inner.clone()))));
let roundtrip_type = checker.infer("ed_then_evaled).unwrap();
assert_eq!(original_type, roundtrip_type);
}
#[test]
fn test_quote_eval_roundtrip_literal() {
let mut checker = TypeChecker::new();
let literal = int_lit(42);
let original_type = checker.infer(&literal).unwrap();
let quoted_then_evaled = Expr::Eval(Box::new(Expr::Quote(Box::new(literal.clone()))));
let roundtrip_type = checker.infer("ed_then_evaled).unwrap();
assert_eq!(original_type, roundtrip_type);
}
#[test]
fn test_double_quote_double_eval_roundtrip() {
let mut checker = TypeChecker::new();
let literal = int_lit(42);
let original_type = checker.infer(&literal).unwrap();
let double_quoted = Expr::Quote(Box::new(Expr::Quote(Box::new(literal.clone()))));
let double_evaled = Expr::Eval(Box::new(Expr::Eval(Box::new(double_quoted))));
let roundtrip_type = checker.infer(&double_evaled).unwrap();
assert_eq!(original_type, roundtrip_type);
}
#[test]
fn test_quote_complex_expression() {
let expr = Expr::Quote(Box::new(Expr::If {
condition: Box::new(ident("x")),
then_branch: Box::new(int_lit(1)),
else_branch: Some(Box::new(int_lit(2))),
}));
match expr {
Expr::Quote(inner) => {
assert!(matches!(*inner, Expr::If { .. }));
}
_ => panic!("Expected Quote"),
}
}
#[test]
fn test_quote_match_expression() {
let expr = Expr::Quote(Box::new(Expr::Match {
scrutinee: Box::new(ident("x")),
arms: vec![
MatchArm {
pattern: Pattern::Literal(Literal::Int(1)),
guard: None,
body: Box::new(string_lit("one")),
},
MatchArm {
pattern: Pattern::Wildcard,
guard: None,
body: Box::new(string_lit("other")),
},
],
}));
match expr {
Expr::Quote(inner) => {
assert!(matches!(*inner, Expr::Match { .. }));
}
_ => panic!("Expected Quote"),
}
}
#[test]
fn test_quote_pipeline() {
let pipeline = Expr::Binary {
op: BinaryOp::Pipe,
left: Box::new(Expr::Binary {
op: BinaryOp::Pipe,
left: Box::new(ident("a")),
right: Box::new(ident("b")),
}),
right: Box::new(ident("c")),
};
let expr = Expr::Quote(Box::new(pipeline));
match expr {
Expr::Quote(inner) => match *inner {
Expr::Binary {
op: BinaryOp::Pipe, ..
} => {}
_ => panic!("Expected pipe expression inside quote"),
},
_ => panic!("Expected Quote"),
}
}
#[test]
fn test_quote_composition() {
let composition = Expr::Binary {
op: BinaryOp::Compose,
left: Box::new(Expr::Binary {
op: BinaryOp::Compose,
left: Box::new(ident("f")),
right: Box::new(ident("g")),
}),
right: Box::new(ident("h")),
};
let expr = Expr::Quote(Box::new(composition));
match expr {
Expr::Quote(inner) => match *inner {
Expr::Binary {
op: BinaryOp::Compose,
..
} => {}
_ => panic!("Expected compose expression inside quote"),
},
_ => panic!("Expected Quote"),
}
}
#[test]
fn test_quote_nested_lambdas() {
let nested_lambda = Expr::Lambda {
params: vec![("x".to_string(), None)],
body: Box::new(Expr::Lambda {
params: vec![("y".to_string(), None)],
body: Box::new(Expr::Binary {
op: BinaryOp::Add,
left: Box::new(ident("x")),
right: Box::new(ident("y")),
}),
return_type: None,
}),
return_type: None,
};
let expr = Expr::Quote(Box::new(nested_lambda));
match expr {
Expr::Quote(inner) => match *inner {
Expr::Lambda { .. } => {}
_ => panic!("Expected lambda inside quote"),
},
_ => panic!("Expected Quote"),
}
}
#[test]
fn test_quote_call_expression() {
let call = Expr::Call {
callee: Box::new(ident("f")),
args: vec![ident("x"), ident("y"), ident("z")],
};
let expr = Expr::Quote(Box::new(call));
match expr {
Expr::Quote(inner) => match *inner {
Expr::Call { args, .. } => {
assert_eq!(args.len(), 3);
}
_ => panic!("Expected call expression inside quote"),
},
_ => panic!("Expected Quote"),
}
}
#[test]
fn test_eval_non_quoted_fails() {
let mut checker = TypeChecker::new();
let expr = Expr::Eval(Box::new(int_lit(42)));
let result = checker.infer(&expr);
match result {
Ok(Type::Error) => {}
Ok(other) => panic!("Expected Error type, got {:?}", other),
Err(e) => panic!("Expected Ok(Error), got Err: {:?}", e),
}
assert!(!checker.is_ok(), "Expected type checker to have errors");
}
#[test]
fn test_eval_unknown_type() {
let mut checker = TypeChecker::new();
let expr = Expr::Eval(Box::new(ident("unknown")));
let result = checker.infer(&expr);
assert!(result.is_err());
}
#[test]
fn test_unary_quote_operator() {
let mut checker = TypeChecker::new();
let expr = Expr::Unary {
op: UnaryOp::Quote,
operand: Box::new(int_lit(42)),
};
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert_eq!(args[0], Type::Int64);
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_unary_quote_complex_expr() {
let mut checker = TypeChecker::new();
let expr = Expr::Unary {
op: UnaryOp::Quote,
operand: Box::new(Expr::Binary {
op: BinaryOp::Mul,
left: Box::new(int_lit(5)),
right: Box::new(int_lit(10)),
}),
};
let ty = checker.infer(&expr).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert!(args[0].is_integer());
}
_ => panic!("Expected Quoted type"),
}
}
#[test]
fn test_quoted_expr_from_expr() {
use metadol::ast::QuotedExpr;
let expr = int_lit(42);
let quoted = QuotedExpr::from_expr(&expr);
match quoted {
QuotedExpr::Literal(Literal::Int(42)) => {}
_ => panic!("Expected literal in QuotedExpr"),
}
}
#[test]
fn test_quoted_expr_to_expr() {
use metadol::ast::QuotedExpr;
let quoted = QuotedExpr::Literal(Literal::Int(42));
let expr = quoted.to_expr();
match expr {
Expr::Literal(Literal::Int(42)) => {}
_ => panic!("Expected literal in Expr"),
}
}
#[test]
fn test_quoted_expr_roundtrip() {
use metadol::ast::QuotedExpr;
let original = Expr::Binary {
op: BinaryOp::Add,
left: Box::new(int_lit(1)),
right: Box::new(int_lit(2)),
};
let quoted = QuotedExpr::from_expr(&original);
let converted_back = quoted.to_expr();
match converted_back {
Expr::Binary {
op: BinaryOp::Add, ..
} => {}
_ => panic!("Expected binary addition after roundtrip"),
}
}
#[test]
fn test_quoted_expr_nested_quote() {
use metadol::ast::QuotedExpr;
let expr = Expr::Quote(Box::new(int_lit(42)));
let quoted = QuotedExpr::from_expr(&expr);
match quoted {
QuotedExpr::Quote(_) => {}
_ => panic!("Expected Quote in QuotedExpr"),
}
}
#[test]
fn test_quote_preserves_all_literal_types() {
let mut checker = TypeChecker::new();
let test_cases = vec![
(int_lit(42), Type::Int64),
(float_lit(1.5), Type::Float64),
(bool_lit(true), Type::Bool),
(string_lit("test"), Type::String),
];
for (expr, expected_inner) in test_cases {
let quoted = Expr::Quote(Box::new(expr));
let ty = checker.infer("ed).unwrap();
match ty {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert_eq!(args[0], expected_inner);
}
_ => panic!("Expected Quoted type for literal"),
}
}
}
#[test]
fn test_arithmetic_in_quoted_context() {
let mut checker = TypeChecker::new();
let expr = Expr::Quote(Box::new(Expr::Binary {
op: BinaryOp::Add,
left: Box::new(int_lit(1)),
right: Box::new(Expr::Binary {
op: BinaryOp::Mul,
left: Box::new(int_lit(2)),
right: Box::new(int_lit(3)),
}),
}));
let result = checker.infer(&expr);
assert!(result.is_ok());
match result.unwrap() {
Type::Generic { name, args } => {
assert_eq!(name, "Quoted");
assert_eq!(args.len(), 1);
assert!(args[0].is_integer());
}
_ => panic!("Expected Quoted type"),
}
}