use super::*;
#[cfg(test)]
fn fun_grammar() -> Grammar {
load_example_grammar("fun")
}
pub fn valid_expressions_cases() -> Vec<ParseTestCase> {
let cases = vec![
ParseTestCase::valid("integer literal", "42"),
ParseTestCase::valid("zero", "0"),
ParseTestCase::valid("float literal", "3.14"),
ParseTestCase::valid("boolean true", "true"),
ParseTestCase::valid("boolean false", "false"),
ParseTestCase::valid("int addition", "1 + 2"),
ParseTestCase::valid("int multiplication", "3 * 4"),
ParseTestCase::valid("float addition", "1.0 +. 2.5"),
ParseTestCase::valid("float division", "10.0 /. 2.0"),
ParseTestCase::valid("float op with completable int", "1.0 +. 2"),
ParseTestCase::valid("simple lambda", "(x: Int) => x + 1"),
ParseTestCase::valid("float lambda", "(x: Float) => x *. 2.0"),
ParseTestCase::valid("lambda application", "((x: Int) => x + 1)(41)"),
ParseTestCase::valid("simple let", "let n: Int = 12; n + 1"),
ParseTestCase::valid("int 1", "1"),
ParseTestCase::valid("int 2", "2"),
ParseTestCase::valid("int 3", "3"),
ParseTestCase::valid("int 10", "10"),
ParseTestCase::valid("large int", "1000"),
ParseTestCase::valid("float 1", "0.5"),
ParseTestCase::valid("float 2", "2.0"),
ParseTestCase::valid("float 3", "123.456"),
ParseTestCase::valid("true literal", "true"),
ParseTestCase::valid("false literal", "false"),
ParseTestCase::valid("sum chain 3", "1 + 2 + 3"),
ParseTestCase::valid("sum chain 4", "1 + 2 + 3 + 4"),
ParseTestCase::valid("mult and add", "1 + 2 * 3"),
ParseTestCase::valid("paren precedence", "(1 + 2) * 3"),
ParseTestCase::valid("nested parens", "((1 + 2) + 3) + 4"),
ParseTestCase::valid("mixed float ops", "1.0 +. 2.0 +. 3.0"),
ParseTestCase::valid("float-int mix with op", "1.0 +. 2"),
ParseTestCase::valid("single paren", "(1)"),
ParseTestCase::valid("double paren", "((1))"),
ParseTestCase::valid("paren around binop", "(1 + 2)"),
ParseTestCase::valid("deep paren nesting", "((((1 + 2))))"),
ParseTestCase::valid("identity lambda", "(x: Int) => x"),
ParseTestCase::valid("lambda add one", "(x: Int) => x + 1"),
ParseTestCase::valid("lambda multiply", "(x: Int) => x * 2"),
ParseTestCase::valid("lambda float op", "(x: Float) => x +. 1.0"),
ParseTestCase::valid("nested lambda simple", "(x: Int) => ((y: Int) => x + y)"),
ParseTestCase::valid(
"nested lambda applied",
"((x: Int) => ((y: Int) => x + y))(1)(2)",
),
ParseTestCase::valid("apply literal lambda", "((x: Int) => x)(5)"),
ParseTestCase::valid("apply expression lambda", "((x: Int) => x + 2)(3 + 4)"),
ParseTestCase::valid("higher-order return lambda", "(f: Int -> Int) => f(1)"),
ParseTestCase::valid("let then use", "let a: Int = 1; a"),
ParseTestCase::valid("let chain 2", "let a: Int = 1; let b: Int = a + 2; b"),
ParseTestCase::valid(
"let with lambda",
"let id: Int -> Int = (x: Int) => x; id(7)",
),
ParseTestCase::valid("let float", "let f: Float = 2.5; f +. 1.5"),
ParseTestCase::valid("let nested expr", "let x: Int = (1 + 2) * 3; x + 4"),
ParseTestCase::valid(
"let lambda body",
"let inc: Int -> Int = (x: Int) => x + 1; inc(9)",
),
ParseTestCase::valid(
"let and nested lambda",
"let mk: Int -> (Int -> Int) = (x: Int) => (y: Int) => x + y; mk(1)(2)",
),
ParseTestCase::valid("lambda returning lambda", "(x: Int) => (y: Int) => x * y"),
ParseTestCase::valid(
"sum function application",
"let sum: Int -> Int = (n: Int) => n + 0; sum(5)",
),
ParseTestCase::valid(
"compose simple",
"let f: Int -> Int = (x: Int) => x + 1; let g: Int -> Int = (y: Int) => y * 2; g(f(3))",
),
ParseTestCase::valid(
"perm 1",
"let x: Int = 1; let y: Int = x + 2; let z: Int = y * 3; z",
),
ParseTestCase::valid("perm 2", "let x: Int = 2; let y: Int = x * 4; y + 1"),
ParseTestCase::valid("perm 3", "let x: Int = (1 + 1) * (2 + 2); x"),
ParseTestCase::valid("spacing a", "1+2"),
ParseTestCase::valid("spacing b", "1 +2"),
ParseTestCase::valid("spacing c", "1+ 2"),
ParseTestCase::valid("app depth 1", "((x: Int) => x + 1)(1)"),
ParseTestCase::valid("app depth 2", "(((x: Int) => x + 1)(2))"),
ParseTestCase::valid("app depth chain", "((x: Int) => (y: Int) => x + y)(1)(2)"),
ParseTestCase::valid("float chain", "1.0 +. 2.0 +. 3.0 +. 4.0"),
ParseTestCase::valid("float mixed paren", "(1.0 +. 2.0) *. 3.0"),
ParseTestCase::valid(
"let using let result",
"let a: Int = 5; let b: Int = a + 3; let c: Int = b - 2; c",
),
ParseTestCase::valid(
"lambda capturing number literal",
"(x: Int) => (y: Int) => x + (y + 1)",
),
ParseTestCase::valid(
"prog 1",
"let a: Int = 1; let b: Int = 2; (a + b) * (a + b)",
),
ParseTestCase::valid("prog 2", "let x: Int = 10; let y: Int = x / 2; y"),
ParseTestCase::valid("prog 3", "let f: Int -> Int = (n: Int) => n * n; f(4)"),
ParseTestCase::valid("trivial 1", "5"),
ParseTestCase::valid("trivial 2", "6"),
ParseTestCase::valid("trivial 3", "7"),
ParseTestCase::valid("trivial 4", "8"),
ParseTestCase::valid("trivial 5", "9"),
ParseTestCase::valid("trivial 6", "11"),
ParseTestCase::valid("trivial 7", "12"),
ParseTestCase::valid("trivial 8", "13"),
ParseTestCase::valid("trivial 9", "14"),
ParseTestCase::valid("trivial 10", "15"),
ParseTestCase::valid("paren mix 1", "(1 + (2 * 3))"),
ParseTestCase::valid("paren mix 2", "((1 + 2) + (3 + 4))"),
ParseTestCase::valid("complex expr 1", "(1 + 2) * (3 + 4) + 5"),
ParseTestCase::valid("complex expr 2", "(1 + (2 + (3 + 4)))"),
ParseTestCase::valid("complex expr 3", "((1 + 2) * (3 + 4)) / 2"),
];
cases
}
pub fn invalid_expressions_cases() -> Vec<ParseTestCase> {
vec![
ParseTestCase::invalid("missing ':' in let", "let n Int = 12;"),
ParseTestCase::invalid("missing semicolon", "let n: Int = 12 n"),
ParseTestCase::invalid("bad identifier", "let 1x: Int = 3;"),
ParseTestCase::invalid("close paren first", ")"),
ParseTestCase::invalid("extra close paren", "(1))"),
ParseTestCase::invalid("at sign", "@"),
ParseTestCase::invalid("hash", "#"),
ParseTestCase::invalid("dollar", "$x"),
ParseTestCase::invalid("backslash", "\\x"),
ParseTestCase::invalid("leading plus", "+ 1"),
ParseTestCase::invalid("leading star", "* 2"),
ParseTestCase::invalid("double operator", "1 ++ 2"),
ParseTestCase::invalid("let no name", "let : Int = 1; 1"),
ParseTestCase::invalid("let double semi", "let x: Int = 1;; x"),
ParseTestCase::invalid("arrow without lambda", "=> 1"),
ParseTestCase::invalid("lambda missing arrow", "(x: Int) x"),
ParseTestCase::invalid("let missing rhs", "let x: Int = ; x"),
ParseTestCase::invalid("app missing close paren", "f(1"),
ParseTestCase::invalid("binary op missing rhs in parens", "(1 + )"),
ParseTestCase::invalid("int expected, float given", "let n: Int = 9.8; n"),
ParseTestCase::invalid("float expected, int given", "let x: Float = 1; x"),
ParseTestCase::invalid("let int declared bool value", "let x: Int = true; x"),
ParseTestCase::invalid("let int declared float value", "let x: Int = 1.0; x"),
ParseTestCase::invalid("int operator with float", "1 + 2.0"),
ParseTestCase::invalid("mixed operators", "1 +. 2.0"),
ParseTestCase::invalid("mixed operators", "3 +. 5"),
ParseTestCase::invalid("bool plus int", "true + 1"),
ParseTestCase::invalid("int plus bool", "1 + false"),
ParseTestCase::invalid("bool float op", "true +. 1.0"),
ParseTestCase::invalid("int with float op/.", "10 /. 2.0"),
ParseTestCase::invalid(
"closed int expr with float op",
"((1 + 2) * (3 + 4)) /. 2.0",
),
ParseTestCase::invalid("wrong argument type", "((x: Int) => x + 1)(2.0)"),
ParseTestCase::invalid("apply non-function", "1(2)"),
ParseTestCase::invalid("apply bool", "true(1)"),
ParseTestCase::invalid("unbound x", "x"),
ParseTestCase::invalid("unbound in expr", "x + 1"),
ParseTestCase::invalid("unbound func", "f(1)"),
ParseTestCase::invalid("unbound in let body", "let x: Int = 1; y"),
ParseTestCase::invalid("var outside scope", "let x: Int = y; x"),
ParseTestCase::invalid("bool let from int expr", "let b: Bool = 1 + 2; b"),
ParseTestCase::invalid("lambda body uses missing var", "(x: Int) => y"),
ParseTestCase::invalid("call returns bool used as int", "((x: Int) => true)(1) + 2"),
ParseTestCase::invalid("wrong arg type bool for int", "f(true)")
.with_context(vec![("f", "Int -> Int")]),
ParseTestCase::invalid("wrong arg type int for bool", "f(1)")
.with_context(vec![("f", "Bool -> Bool")]),
]
}
#[test]
fn valid_expressions_fun() {
let mut grammar = fun_grammar();
let cases = valid_expressions_cases();
println!("\n=== Fun Valid Expressions ({} cases) ===", cases.len());
let (res, _cases_json) = run_parse_batch(&mut grammar, &cases);
assert_eq!(res.failed, 0, "{}", res.format_failures());
println!(
"✓ All {} cases passed in {:?} (avg {:?})",
cases.len(),
res.total_duration,
res.avg_duration
);
}
#[test]
fn invalid_expressions_fun() {
let mut grammar = fun_grammar();
let cases = invalid_expressions_cases();
println!("\n=== Fun Invalid Expressions ({} cases) ===", cases.len());
let (res, _cases_json) = run_parse_batch(&mut grammar, &cases);
assert_eq!(res.failed, 0, "{}", res.format_failures());
println!(
"✓ All {} cases passed in {:?} (avg {:?})",
res.passed, res.total_duration, res.avg_duration
);
}
#[test]
#[ignore = "debug helper for investigating specific fun prefixes"]
fn debug_fun_failures() {
let mut grammar = fun_grammar();
crate::set_debug_level(crate::DebugLevel::Trace);
let ctx = crate::logic::typing::Context::new();
let cases = vec!["((x: Int) => x + 1)(", "(1 + 2) *", "(1.0 +. 2.0) *."];
for s in cases {
println!("\n--- Debug parse for: '{}' ---", s);
match grammar.tokenize(s) {
Ok(segs) => {
println!("Segments ({}):", segs.len());
for seg in segs.iter() {
println!(" '{}' @ {}", seg.as_str(), seg.index);
}
}
Err(e) => println!("Tokenize error: {}", e),
}
let mut synth = crate::logic::synth::Synthesizer::new(grammar.clone(), s);
match synth.parse_with(&ctx) {
Ok(ast) => println!("Parsed OK: roots={}", ast.root_ids().len()),
Err(e) => println!("Parse error: {}", e),
}
}
}