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 should be true 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 should be false 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" s { 2 distort } 1 ? ."#, 1); assert!(outputs[0].contains("distort/2")); } #[test] fn quotation_skips_param() { let outputs = expect_outputs(r#""kick" s { 2 distort } 0 ? ."#, 1); assert!(!outputs[0].contains("distort")); } #[test] fn quotation_with_emit() { // When true, . should fire let outputs = expect_outputs(r#""kick" s { . } 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" s { . } 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() { // Simulating: { 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" s { 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 ); } } } // 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 unless_with_every() { // iter=0, every 2 is true, so unless skips let ctx = ctx_with(|c| c.iter = 0); let f = run_ctx("50 { 100 } 2 every !?", &ctx); assert_eq!(stack_int(&f), 50); // iter=1, every 2 is false, so unless executes let ctx = ctx_with(|c| c.iter = 1); let f = run_ctx("{ 100 } 2 every !?", &ctx); assert_eq!(stack_int(&f), 100); } #[test] fn when_and_unless_complementary() { // Using both ? and !? for if-else like behavior for iter in 0..4 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); let outputs = f .evaluate( r#""kick" s { 2 distort } 2 every ? { 4 distort } 2 every !? ."#, &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 ); } } }