const STLC_MAX_DEPTH: usize = 62;
use crate::logic::grammar::Grammar;
use crate::logic::partial::MetaParser;
use crate::logic::typing::core::{Context, TreeStatus};
use crate::logic::typing::eval::check_tree;
use crate::logic::typing::Type;
use crate::validation::completable::load_example_grammar;
fn stlc() -> Grammar {
load_example_grammar("stlc")
}
#[test]
fn test_context_extend_rejects_duplicate() {
let ctx = Context::new();
let ctx = ctx
.extend("x".to_string(), Type::Raw("Int".to_string()))
.expect("First binding should succeed");
let result = ctx.extend("x".to_string(), Type::Raw("Bool".to_string()));
assert!(
result.is_err(),
"Context should reject duplicate variable 'x'"
);
assert!(
result
.unwrap_err()
.contains("already contains binding for 'x'"),
"Error message should mention duplicate binding"
);
}
#[test]
fn test_context_extend_allows_different_variables() {
let ctx = Context::new();
let ctx = ctx
.extend("x".to_string(), Type::Raw("Int".to_string()))
.expect("First binding should succeed");
let ctx = ctx
.extend("y".to_string(), Type::Raw("Bool".to_string()))
.expect("Second binding with different name should succeed");
let ctx = ctx
.extend("z".to_string(), Type::Raw("String".to_string()))
.expect("Third binding with different name should succeed");
assert!(ctx.lookup("x").is_some());
assert!(ctx.lookup("y").is_some());
assert!(ctx.lookup("z").is_some());
}
#[test]
fn test_nested_lambda_same_variable_rejected() {
let g = stlc();
let input = "λx : Int. λx : Bool. x";
let mut p = MetaParser::new(g.clone()).with_max_depth(STLC_MAX_DEPTH);
let ast = p.partial(input).unwrap();
let completes = ast.completes();
for tree in completes {
let status = check_tree(&tree, &g);
match status {
TreeStatus::Malformed => {
return;
}
TreeStatus::Valid(_) | TreeStatus::Partial(_) => {
panic!(
"Expected Malformed status for nested lambda with duplicate variable name, got: {:?}",
status
);
}
TreeStatus::TooDeep => {
panic!("Unexpected TooDeep status");
}
}
}
}
#[test]
fn test_nested_lambda_different_variables_accepted() {
let g = stlc();
let input = "λx : Int. λy : Bool. x";
let mut p = MetaParser::new(g.clone()).with_max_depth(STLC_MAX_DEPTH);
let ast = p.partial(input).unwrap();
let completes = ast.completes();
assert!(
!completes.is_empty(),
"Should have at least one complete parse"
);
for tree in completes {
let status = check_tree(&tree, &g);
match status {
TreeStatus::Valid(_) | TreeStatus::Partial(_) => {
return;
}
TreeStatus::Malformed => {
panic!(
"Expected Valid/Partial status for nested lambda with different variable names, got Malformed"
);
}
TreeStatus::TooDeep => {
panic!("Unexpected TooDeep status");
}
}
}
}
#[test]
fn test_triple_nested_lambda_duplicate_rejected() {
let g = stlc();
let input = "λx : Int. λy : Bool. λx : String. x";
let mut p = MetaParser::new(g.clone()).with_max_depth(STLC_MAX_DEPTH);
let ast = p.partial(input).unwrap();
let completes = ast.completes();
for tree in completes {
let status = check_tree(&tree, &g);
match status {
TreeStatus::Malformed => {
return;
}
TreeStatus::Valid(_) | TreeStatus::Partial(_) => {
panic!(
"Expected Malformed status for triple nested lambda with duplicate 'x', got: {:?}",
status
);
}
TreeStatus::TooDeep => {
panic!("Unexpected TooDeep status");
}
}
}
}
#[test]
fn test_triple_nested_lambda_all_different_accepted() {
let g = stlc();
let input = "λx : Int. λy : Bool. λz : String. x";
let mut p = MetaParser::new(g.clone()).with_max_depth(STLC_MAX_DEPTH);
let ast = p.partial(input).unwrap();
let completes = ast.completes();
assert!(
!completes.is_empty(),
"Should have at least one complete parse"
);
for tree in completes {
let status = check_tree(&tree, &g);
match status {
TreeStatus::Valid(_) | TreeStatus::Partial(_) => {
return;
}
TreeStatus::Malformed => {
panic!(
"Expected Valid/Partial status for triple nested lambda with all different variables, got Malformed"
);
}
TreeStatus::TooDeep => {
panic!("Unexpected TooDeep status");
}
}
}
}
#[test]
fn test_lambda_with_application_duplicate_rejected() {
let g = stlc();
let input = "λx : Int. (λx : Bool. x) x";
let mut p = MetaParser::new(g.clone()).with_max_depth(STLC_MAX_DEPTH);
let ast = p.partial(input).unwrap();
let completes = ast.completes();
for tree in completes {
let status = check_tree(&tree, &g);
match status {
TreeStatus::Malformed => {
return;
}
TreeStatus::Valid(_) | TreeStatus::Partial(_) => {
panic!(
"Expected Malformed status for lambda application with duplicate binding, got: {:?}",
status
);
}
TreeStatus::TooDeep => {
panic!("Unexpected TooDeep status");
}
}
}
}
#[test]
fn test_context_multiple_duplicates() {
let ctx = Context::new();
let ctx = ctx
.extend("x".to_string(), Type::Raw("Int".to_string()))
.expect("First binding should succeed");
assert!(ctx
.extend("x".to_string(), Type::Raw("Bool".to_string()))
.is_err());
assert!(ctx
.extend("x".to_string(), Type::Raw("String".to_string()))
.is_err());
assert!(ctx
.extend("x".to_string(), Type::Atom("A".to_string()))
.is_err());
match ctx.lookup("x") {
Some(Type::Raw(s)) if s == "Int" => {} other => panic!("Expected Raw(Int), got: {:?}", other),
}
}
#[test]
fn test_sequential_scopes_same_variable_rejected() {
let ctx = Context::new();
let ctx1 = ctx
.extend("x".to_string(), Type::Raw("Int".to_string()))
.expect("First x should work");
let ctx2 = ctx1
.extend("y".to_string(), Type::Raw("Bool".to_string()))
.expect("y should work");
let result = ctx2.extend("x".to_string(), Type::Raw("String".to_string()));
assert!(
result.is_err(),
"Should not be able to rebind x in cumulative context"
);
}