use super::harness::*; #[test] fn quotation_on_stack() { // Quotation should be pushable to stack let f = forth(); let result = f.evaluate("( 1 2 + )", &default_ctx()); assert!(result.is_ok()); } #[test] fn when_true_executes() { let f = run("( 42 ) 1 ?"); assert_eq!(stack_int(&f), 42); } #[test] fn when_false_skips() { let f = run("99 ( 42 ) 0 ?"); // Stack should still have 99, quotation not executed assert_eq!(stack_int(&f), 99); } #[test] fn when_with_arithmetic() { let f = run("10 ( 5 + ) 1 ?"); assert_eq!(stack_int(&f), 15); } #[test] fn when_with_every() { // iter=0, every 2 executes quotation let ctx = ctx_with(|c| c.iter = 0); let f = run_ctx("( 100 ) 2 every", &ctx); assert_eq!(stack_int(&f), 100); // iter=1, every 2 skips quotation let ctx = ctx_with(|c| c.iter = 1); let f = run_ctx("50 ( 100 ) 2 every", &ctx); assert_eq!(stack_int(&f), 50); // quotation not executed } #[test] fn when_with_chance_deterministic() { // 1.0 chance always executes quotation let f = run("( 42 ) 1.0 chance"); assert_eq!(stack_int(&f), 42); // 0.0 chance never executes quotation let f = run("99 ( 42 ) 0.0 chance"); assert_eq!(stack_int(&f), 99); } #[test] fn nested_quotations() { let f = run("( ( 42 ) 1 ? ) 1 ?"); assert_eq!(stack_int(&f), 42); } #[test] fn quotation_with_param() { let outputs = expect_outputs(r#""kick" snd ( 2 distort ) 1 ? ."#, 1); assert!(outputs[0].contains("distort/2")); } #[test] fn quotation_skips_param() { let outputs = expect_outputs(r#""kick" snd ( 2 distort ) 0 ? ."#, 1); assert!(!outputs[0].contains("distort")); } #[test] fn quotation_with_emit() { // When true, . should fire let outputs = expect_outputs(r#""kick" snd ( . ) 1 ?"#, 1); assert!(outputs[0].contains("kick")); } #[test] fn quotation_skips_emit() { // When false, . should not fire let f = forth(); let outputs = f .evaluate(r#""kick" snd ( . ) 0 ?"#, &default_ctx()) .unwrap(); // No output since . was skipped and no implicit emit assert_eq!(outputs.len(), 0); } #[test] fn missing_quotation_error() { expect_error("42 1 ?", "expected quotation"); } #[test] fn unclosed_quotation_error() { expect_error("( 1 2", "missing )"); } #[test] fn unexpected_close_error() { expect_error("1 2 )", "unexpected )"); } #[test] fn every_with_quotation_integration() { // ( 2 distort ) 2 every — on even iterations, distort is applied for iter in 0..4 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); let outputs = f .evaluate(r#""kick" snd ( 2 distort ) 2 every ."#, &ctx) .unwrap(); if iter % 2 == 0 { assert!( outputs[0].contains("distort/2"), "iter {} should have distort", iter ); } else { assert!( !outputs[0].contains("distort"), "iter {} should not have distort", iter ); } } } // bjork with sound #[test] fn bjork_with_sound() { let ctx = ctx_with(|c| c.runs = 2); // position 2 is a hit for (3,8) let f = forth(); let outputs = f .evaluate(r#""kick" snd ( 2 distort ) 3 8 bjork ."#, &ctx) .unwrap(); assert!(outputs[0].contains("distort/2")); } // Unless (!?) tests #[test] fn unless_false_executes() { let f = run("( 42 ) 0 !?"); assert_eq!(stack_int(&f), 42); } #[test] fn unless_true_skips() { let f = run("99 ( 42 ) 1 !?"); assert_eq!(stack_int(&f), 99); } #[test] fn when_and_unless_complementary() { // Using iter mod + ?/!? for if-else behavior (every no longer pushes bool) for iter in 0..4 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); let outputs = f .evaluate( r#""kick" snd ( 2 distort ) iter 2 mod 0 = ? ( 4 distort ) iter 2 mod 0 = !? ."#, &ctx, ) .unwrap(); if iter % 2 == 0 { assert!( outputs[0].contains("distort/2"), "iter {} should have distort/2", iter ); } else { assert!( outputs[0].contains("distort/4"), "iter {} should have distort/4", iter ); } } }