blots_core/
do_block_tests.rs

1// Tests for do block shadowing behavior
2#[cfg(test)]
3mod do_block_shadowing_tests {
4    use crate::expressions::evaluate_pairs;
5    use crate::heap::Heap;
6    use crate::parser::{Rule, get_pairs};
7    use crate::values::Value;
8    use std::cell::RefCell;
9    use std::collections::HashMap;
10    use std::rc::Rc;
11
12    fn parse_and_evaluate(
13        code: &str,
14        heap: Option<Rc<RefCell<Heap>>>,
15        bindings: Option<Rc<RefCell<HashMap<String, Value>>>>,
16    ) -> Result<Value, anyhow::Error> {
17        let pairs = get_pairs(code)?;
18
19        let heap = heap.unwrap_or_else(|| Rc::new(RefCell::new(Heap::new())));
20        let bindings = bindings.unwrap_or_else(|| Rc::new(RefCell::new(HashMap::new())));
21
22        let mut result = Value::Null;
23        for pair in pairs {
24            match pair.as_rule() {
25                Rule::statement => {
26                    if let Some(inner_pair) = pair.into_inner().next() {
27                        match inner_pair.as_rule() {
28                            Rule::expression | Rule::assignment => {
29                                result = evaluate_pairs(
30                                    inner_pair.into_inner(),
31                                    Rc::clone(&heap),
32                                    Rc::clone(&bindings),
33                                    0,
34                                )?;
35                            }
36                            _ => {}
37                        }
38                    }
39                }
40                Rule::EOI => {}
41                _ => {}
42            }
43        }
44        Ok(result)
45    }
46
47    #[test]
48    fn test_do_block_allows_shadowing() {
49        // Test that do blocks can shadow outer variables
50        let heap = Rc::new(RefCell::new(Heap::new()));
51        let bindings = Rc::new(RefCell::new(HashMap::new()));
52
53        // Define outer g = 25
54        let _ = parse_and_evaluate("g = 25", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
55            .unwrap();
56
57        // Define function that shadows g in do block
58        let _ = parse_and_evaluate(
59            "make_f = x => do { g = y => x * y; return g }",
60            Some(Rc::clone(&heap)),
61            Some(Rc::clone(&bindings)),
62        )
63        .unwrap();
64
65        // Call make_f(5) and then call the result with 3
66        let f = parse_and_evaluate(
67            "make_f(5)",
68            Some(Rc::clone(&heap)),
69            Some(Rc::clone(&bindings)),
70        )
71        .unwrap();
72
73        // The function should work (g was successfully shadowed)
74        assert!(matches!(f, Value::Lambda(_)));
75
76        // Now call the returned function with 3
77        let result = parse_and_evaluate("make_f(5)(3)", Some(heap), Some(bindings)).unwrap();
78
79        assert_eq!(result, Value::Number(15.0)); // 5 * 3 = 15
80    }
81
82    #[test]
83    fn test_do_block_shadowing_preserves_outer() {
84        // Test that shadowing in do block doesn't affect outer variable
85        let heap = Rc::new(RefCell::new(Heap::new()));
86        let bindings = Rc::new(RefCell::new(HashMap::new()));
87
88        // Define outer x = 10
89        let _ = parse_and_evaluate("x = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
90            .unwrap();
91
92        // Do block that shadows x
93        let _ = parse_and_evaluate(
94            "result = do { x = 20; return x }",
95            Some(Rc::clone(&heap)),
96            Some(Rc::clone(&bindings)),
97        )
98        .unwrap();
99
100        // Outer x should still be 10
101        let outer_x =
102            parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
103        assert_eq!(outer_x, Value::Number(10.0));
104
105        // Result should be 20 (from the do block)
106        let result = parse_and_evaluate("result", Some(heap), Some(bindings)).unwrap();
107        assert_eq!(result, Value::Number(20.0));
108    }
109
110    #[test]
111    fn test_nested_do_blocks_shadowing() {
112        // Test that nested do blocks can each shadow variables
113        let heap = Rc::new(RefCell::new(Heap::new()));
114        let bindings = Rc::new(RefCell::new(HashMap::new()));
115
116        // Define outer z = 1
117        let _ = parse_and_evaluate("z = 1", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
118            .unwrap();
119
120        // Nested do blocks with different z values
121        let result = parse_and_evaluate(
122            "do { z = 10; return do { z = 100; return z } }",
123            Some(heap),
124            Some(bindings),
125        )
126        .unwrap();
127
128        assert_eq!(result, Value::Number(100.0));
129    }
130
131    #[test]
132    fn test_do_block_multiple_shadowings() {
133        // Test that do blocks can shadow multiple variables
134        let heap = Rc::new(RefCell::new(Heap::new()));
135        let bindings = Rc::new(RefCell::new(HashMap::new()));
136
137        // Define outer a and b
138        let _ = parse_and_evaluate("a = 1", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
139            .unwrap();
140        let _ = parse_and_evaluate("b = 2", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
141            .unwrap();
142
143        // Do block that shadows both
144        let result = parse_and_evaluate(
145            "do { a = 10; b = 20; return a + b }",
146            Some(heap),
147            Some(bindings),
148        )
149        .unwrap();
150
151        assert_eq!(result, Value::Number(30.0)); // 10 + 20
152    }
153
154    #[test]
155    fn test_do_block_function_capture_with_shadowing() {
156        // Test that functions defined in do blocks capture the shadowed value
157        let heap = Rc::new(RefCell::new(Heap::new()));
158        let bindings = Rc::new(RefCell::new(HashMap::new()));
159
160        // Define outer v = 5
161        let _ = parse_and_evaluate("v = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
162            .unwrap();
163
164        // Do block that shadows v and returns a function capturing it
165        let _ = parse_and_evaluate(
166            "get_func = do { v = 100; f = () => v; return f }",
167            Some(Rc::clone(&heap)),
168            Some(Rc::clone(&bindings)),
169        )
170        .unwrap();
171
172        // Call the function - should return the shadowed value
173        let result = parse_and_evaluate("get_func()", Some(heap), Some(bindings)).unwrap();
174
175        assert_eq!(result, Value::Number(100.0)); // Captured the shadowed v
176    }
177}