state-macro 0.1.1

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

// Test program using the #[stateful] attribute
// This implements the program described in NEW_TESTS.md
#[stateful(&StackState)]
fn test_program(x: i32, y: i32) -> i32 {
    ::push(x);
    ::push(y);
    ::add();
    ::pop()
}

#[test]
fn test_basic_stateful() {
    // Test that the stateful attribute works correctly with a basic case
    let stack = new_stack();

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

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

// Module for testing namespaced functions with stateful attribute
mod operations {
    use super::*;

    // A namespaced stateful function
    #[stateful(&StackState)]
    pub fn multiply(x: i32, y: i32) -> i32 {
        // Implement multiplication as repeated addition
        let mut result = 0;
        for _ in 0..y {
            ::push(x);
            ::push(result);
            ::add();
            result = ::pop();
        }

        result
    }
}

#[test]
fn test_stateful_with_namespaced_functions() {
    // Test that the stateful attribute works with namespaced functions
    let stack = new_stack();

    // Using a namespaced stateful function
    let result = operations::multiply(&stack, 5, 4);

    // Should calculate 5 * 4 = 20
    assert_eq!(result, 20);
    assert_eq!(stack.borrow().len(), 0);
}

// A stateful function that calls another stateful function
#[stateful(&StackState)]
fn nested_calls(x: i32, y: i32) -> i32 {
    // Call the test_program function first
    let sum = ::test_program(x, y);

    // Then use the result in another calculation
    ::push(sum);
    ::push(sum);
    ::add();
    ::pop() // Returns sum * 2
}

#[test]
fn test_stateful_nested_calls() {
    // Test that nested stateful function calls work
    let stack = new_stack();

    let result = nested_calls(&stack, 5, 7);

    // Should call test_program(5, 7) = 12, then double it to 24
    assert_eq!(result, 24);
    assert_eq!(stack.borrow().len(), 0);
}

// A stateful function with expressions, blocks, conditionals
#[stateful(&StackState)]
fn complex_stateful(x: i32) -> i32 {
    // Use a block expression
    let doubled = {
        ::push(x);
        ::push(x);
        ::add();
        ::pop()
    };

    // Use conditionals
    if doubled > 10 {
        ::push(doubled);
        ::push(5);
        ::add();
    } else {
        ::push(doubled);
        ::push(2);
        ::add();
    }

    ::pop()
}

#[test]
fn test_stateful_with_expressions_and_conditionals() {
    // Test stateful with expressions, blocks, conditionals
    let stack = new_stack();

    // For x = 3: doubled = 6, which is <= 10, so we add 2
    let result1 = complex_stateful(&stack, 3);
    assert_eq!(result1, 8);

    // For x = 8: doubled = 16, which is > 10, so we add 5
    let result2 = complex_stateful(&stack, 8);
    assert_eq!(result2, 21);

    assert_eq!(stack.borrow().len(), 0);
}