state-macro 0.1.1

Syntax sugar for stateful functions
Documentation
use crate::stack_machine::{StackState, add, new_stack, pop, push, swap};
use state_macro::with_state;

mod nested {
    use super::*;

    // Namespaced version of push for testing namespace resolution
    pub fn push(state: &StackState, value: i32) {
        // Just delegates to the main push function
        super::push(state, value * 2); // Multiplies by 2 to distinguish it
    }
}

// Basic test program using the stack machine
fn test_program(state: &StackState, x: i32, y: i32) -> i32 {
    with_state! { state;
        ::push(x);
        ::push(y);
        ::add();
        ::pop()
    }
}

#[test]
fn test_basic_with_state() {
    // Test that the with_state! macro works correctly with a basic case
    let stack = new_stack();

    let result = test_program(&stack, 3, 5);

    // Should push 3, push 5, add them, and pop the result (8)
    assert_eq!(result, 8);
    // Stack should be empty after test_program
    assert_eq!(stack.borrow().len(), 0);
}

#[test]
fn test_with_state_namespaced_functions() {
    // Test that the with_state! macro works with namespaced functions
    let stack = new_stack();

    // Test using both regular and namespaced push functions
    with_state! { &stack;
        ::push(10);
        ::nested::push(5);  // This will push 10 (5*2)
        ::add();
    }

    // Should have pushed 10, then 10 (5*2), then added them to get 20
    let result = pop(&stack);
    assert_eq!(result, 20);
    assert_eq!(stack.borrow().len(), 0);
}

#[test]
fn test_with_state_nested_function_calls() {
    // Test that nested function calls work correctly with with_state!
    let stack = new_stack();

    with_state! { &stack;
        ::push(10);
        ::push(::pop() + 5);  // Pop 10, add 5, push 15
        ::push(20);
        ::swap();  // Swap 15 and 20
        ::add();   // Add 15 + 20 = 35
    }

    let result = pop(&stack);
    assert_eq!(result, 35);
    assert_eq!(stack.borrow().len(), 0);
}

#[test]
fn test_with_state_conditional() {
    let stack = new_stack();
    push(&stack, 10);
    with_state! {
        if ::pop() > 0 { true } else { false };
    }
}

#[test]
fn test_with_state_nested_expressions() {
    // Test that nested expressions, blocks work with with_state!
    let stack = new_stack();

    with_state! { &stack;
        // Block expression
        let x = {
            ::push(5);
            ::push(7);
            ::pop() + ::pop()  // 7 + 5 = 12
        };

        ::push(x);

        // Conditional
        if ::pop() > 10 {
            ::push(100);
        } else {
            ::push(0);
        }
    }

    let result = pop(&stack);
    assert_eq!(result, 100); // Since 12 > 10, we pushed 100
    assert_eq!(stack.borrow().len(), 0);
}

#[test]
fn test_with_state_loops() {
    // Test that loops work correctly with with_state!
    let stack = new_stack();

    with_state! { &stack;
        ::push(0);  // Initialize sum

        // Loop to sum numbers 1 through 5
        for i in 1..=5 {
            ::push(i);
            ::add();  // Add to running sum
        }
    }

    let result = pop(&stack);
    assert_eq!(result, 15); // 1+2+3+4+5 = 15
    assert_eq!(stack.borrow().len(), 0);
}

#[test]
fn test_complex_program() {
    // Test a more complex program with the stack machine
    let stack = new_stack();

    with_state! { &stack;
        // Push some initial values
        ::push(10);
        ::push(20);

        // Swap them
        ::swap();

        // Push their product using a combination of pops and a calculation
        let product = {
            let a = ::pop();  // 20
            let b = ::pop();  // 10
            a * b  // 200
        };

        ::push(product); // stack = [200]

        // Use a loop to divide the product by 2 repeatedly until it's < 50
        let mut result = ::pop(); // stack = []
        while result >= 50 {
            result /= 2; // rseult = 100; 50
            ::push(result); // stack = [100]; [100, 50]
        }
    }

    // 200 ÷ 2 = 100, 100 ÷ 2 = 50, 50 ÷ 2 = 25 (< 50, so we stop)
    let result = pop(&stack);
    assert_eq!(result, 25);
    assert_eq!(stack.borrow().len(), 2);
}