use myc::ast::environment::Environment; use myc::ast::types::Value; #[test] fn test_closure_modification_from_source() { let source = r#" (do (def x 10) (def f (fn [] (assign x 20))) (f) x ) "#; let env = Environment::new(); let compiled = env .compile(source) .into_result() .expect("Failed to compile"); let linked = env.link(compiled); let result = env.create_vm().run(&linked); match result { Ok(Value::Int(20)) => (), Ok(val) => panic!("Expected Int(20), got {:?}", val), Err(e) => panic!("VM Error: {}", e), } } #[test] fn test_closure_reassignment_optimization_bug() { let env = Environment::new(); // Regression: optimizer inlined a function whose parameters were assigned to // by an inner lambda. Such functions must NOT be inlined. let source = "(do (def f (fn [[x y]] (fn [] (assign x (+ x y))))) ((f [1 2])))"; let res = env.run_script(source); assert_eq!(format!("{}", res.unwrap()), "3"); } #[test] fn test_optimizer_upvalue_inlining_bug_repro() { let env = Environment::new(); let source = r#" (do (def make-counter (fn [init] (do (def val init) { :inc (fn [] (assign val (+ val 1))) :get (fn [] val) }))) (def c (make-counter 10)) ((.inc c)) ((.get c))) "#; let res = env.run_script(source); assert!(res.is_ok(), "Optimizer bug triggered: {:?}", res.err()); assert_eq!(format!("{}", res.unwrap()), "11"); } #[test] #[should_panic(expected = "'again' is only allowed in tail position to avoid dead code.")] fn test_again_non_tail_panic() { let env = Environment::new(); let source = "(do (def f (fn [x] (do (again (- x 1)) x))) (f 5))"; let _ = env.run_script(source); } #[test] fn test_debug_mode_logging() { let mut env = Environment::new(); env.optimization = false; let source = "(+ 10 20)"; let result = env.run_debug(source).expect("Failed to run debug"); let (val, logs) = result; match val { Ok(Value::Int(30)) => (), _ => panic!("Expected Int(30), got {:?}", val), } assert!(!logs.is_empty(), "Logs should not be empty"); let has_call = logs.iter().any(|l| l.contains("CALL")); let has_const = logs.iter().any(|l| l.contains("CONST(10)")); let has_result = logs.iter().any(|l| l.contains("} -> 30")); assert!(has_call, "Logs should contain CALL"); assert!(has_const, "Logs should contain CONST(10)"); assert!(has_result, "Logs should contain result 30"); }