use myc::ast::environment::Environment; #[test] fn test_record_basics() { let env = Environment::new(); let source = r#" ((fn [user] [(.name user) (.age user)]) {:name "Alice" :age 30}) "#; let res = env.run_script(source).unwrap(); assert_eq!(format!("{}", res), "[\"Alice\" 30]"); } #[test] fn test_record_optimized_access() { let env = Environment::new(); let source_eval = "(.price {:id 1 :price 99.5})"; let res = env.run_script(source_eval).unwrap(); assert_eq!(format!("{}", res), "99.5"); // Verify optimization to GET_FIELD when the record contains non-constants let source_ast = "(fn [id] (.price {:id id :price 99.5}))"; let dump = env.dump_ast(source_ast).unwrap(); assert!( dump.contains("GetField: .price"), "Should be optimized to GetField. Dump:\n{}", dump ); } #[test] fn test_first_class_field_accessor() { let env = Environment::new(); let source = r#" (do (def get-name .name) (get-name {:name "Alice"})) "#; let res = env.run_script(source).unwrap(); assert_eq!(format!("{}", res), "\"Alice\""); } #[test] fn test_record_constant_folding() { let env = Environment::new(); // Optimizer should fold (.x {:x 10}) into 10 let source = "(.x {:x 10 :y 20})"; let dump = env.dump_ast(source).unwrap(); assert!( dump.contains("Constant: 10"), "Should evaluate GetField at compile time." ); } #[test] fn test_record_literal_constant_folding() { let env = Environment::new(); let source = " {:a 1 :b 2} "; let dump = env.dump_ast(source).unwrap(); assert!( dump.contains("Constant: {:a 1, :b 2}"), "Should transform a pure record literal into a constant value." ); assert!( !dump.contains("Record {"), "Should not leave a runtime Record node in the AST." ); } #[test] fn test_record_inlining_in_while_loop() { let env_ast = Environment::new(); let source = r#" (do (def loop-config {:start 0 :limit 10}) (def loop-idx 0) (while (< loop-idx (.limit loop-config)) (assign loop-idx (+ loop-idx 1))) loop-idx ) "#; let dump = env_ast.dump_ast(source).unwrap(); assert!( dump.contains("Constant: 10") || dump.contains("GetField: .limit"), "The limit should be resolved to a constant 10 or kept as GetField." ); let env_run = Environment::new(); let res = env_run.run_script(source).unwrap(); assert_eq!(format!("{}", res), "10"); } #[test] fn test_record_errors() { let env = Environment::new(); // 1. Missing field let res_missing = env.run_script("(.missing {:a 1})"); assert!(res_missing.is_err()); assert!(res_missing.unwrap_err().contains("no matching overload")); // 2. Not a record let res_not_rec = env.run_script("(.name 123)"); assert!(res_not_rec.is_err()); assert!(res_not_rec.unwrap_err().contains("no matching overload")); } #[test] fn test_record_layout_interning() { let env = Environment::new(); let source = r#" (do (def r1 {:a 1 :b 2}) (def r2 {:a 10 :b 20}) (= r1 r2)) "#; let res = env.run_script(source).unwrap(); assert_eq!(format!("{}", res), "false"); }