1
use nomiscript::{
2
    Closure, Fraction, Pair, Symbol, SymbolKind, SymbolTable, Value, list_to_vec, vec_to_list,
3
};
4

            
5
11139
fn num(n: i64) -> Value {
6
11139
    Value::Number(Fraction::from_integer(n))
7
11139
}
8

            
9
6
fn frac(n: i64, d: i64) -> Value {
10
6
    Value::Number(Fraction::new(n, d))
11
6
}
12

            
13
mod value_types {
14
    use super::*;
15

            
16
    #[test]
17
1
    fn test_type_names() {
18
1
        let cases = [
19
1
            (Value::Nil, "nil"),
20
1
            (Value::T, "boolean"),
21
1
            (num(42), "number"),
22
1
            (Value::String("hello".into()), "string"),
23
1
            (Value::Symbol("x".into()), "symbol"),
24
1
            (
25
1
                Value::Pair(Box::new(Pair::new(Value::Nil, Value::Nil))),
26
1
                "pair",
27
1
            ),
28
1
            (Value::Vector(vec![]), "vector"),
29
1
            (Value::Closure(Closure::new(0, vec![])), "closure"),
30
1
        ];
31

            
32
8
        for (value, expected_type) in cases {
33
8
            assert_eq!(value.type_name(), expected_type);
34
        }
35
1
    }
36

            
37
    #[test]
38
1
    fn test_truthiness() {
39
1
        assert!(Value::Nil.is_nil());
40

            
41
1
        let truthy = [
42
1
            Value::T,
43
1
            num(0),
44
1
            num(1),
45
1
            Value::String(String::new()),
46
1
            Value::String("hello".into()),
47
1
            Value::Symbol("x".into()),
48
1
            Value::Pair(Box::new(Pair::new(Value::Nil, Value::Nil))),
49
1
            Value::Vector(vec![]),
50
1
            Value::Closure(Closure::new(0, vec![])),
51
1
        ];
52

            
53
9
        for val in truthy {
54
9
            assert!(!val.is_nil(), "{val:?} should be truthy");
55
        }
56
1
    }
57
}
58

            
59
mod fractions {
60
    use super::*;
61

            
62
    #[test]
63
1
    fn test_fraction_equality() {
64
1
        assert_eq!(frac(1, 2), frac(2, 4));
65
1
        assert_eq!(frac(3, 6), frac(1, 2));
66
1
        assert_eq!(frac(100, 100), num(1));
67
1
    }
68

            
69
    #[test]
70
1
    fn test_fraction_normalization() {
71
1
        let half = Fraction::new(1, 2);
72
1
        let also_half = Fraction::new(50, 100);
73
1
        assert_eq!(half, also_half);
74

            
75
1
        let negative = Fraction::new(-1, 2);
76
1
        let also_negative = Fraction::new(1, -2);
77
1
        assert_eq!(negative, also_negative);
78
1
    }
79
}
80

            
81
mod pairs {
82
    use super::*;
83

            
84
    #[test]
85
1
    fn test_cons() {
86
1
        let pair = Pair::cons(num(1), num(2));
87
1
        match pair {
88
1
            Value::Pair(p) => {
89
1
                assert_eq!(p.car, num(1));
90
1
                assert_eq!(p.cdr, num(2));
91
            }
92
            _ => panic!("expected pair"),
93
        }
94
1
    }
95

            
96
    #[test]
97
1
    fn test_nested_pairs() {
98
1
        let inner = Pair::cons(num(2), num(3));
99
1
        let outer = Pair::cons(num(1), inner);
100

            
101
1
        match outer {
102
1
            Value::Pair(p) => {
103
1
                assert_eq!(p.car, num(1));
104
1
                match p.cdr {
105
1
                    Value::Pair(inner_p) => {
106
1
                        assert_eq!(inner_p.car, num(2));
107
1
                        assert_eq!(inner_p.cdr, num(3));
108
                    }
109
                    _ => panic!("expected inner pair"),
110
                }
111
            }
112
            _ => panic!("expected outer pair"),
113
        }
114
1
    }
115
}
116

            
117
mod lists {
118
    use super::*;
119

            
120
    #[test]
121
1
    fn test_empty_list() {
122
1
        let list = Value::Nil;
123
1
        let vec = list_to_vec(&list).unwrap();
124
1
        assert!(vec.is_empty());
125

            
126
1
        let back = vec_to_list(vec);
127
1
        assert_eq!(back, Value::Nil);
128
1
    }
129

            
130
    #[test]
131
1
    fn test_single_element_list() {
132
1
        let list = Pair::cons(num(42), Value::Nil);
133
1
        let vec = list_to_vec(&list).unwrap();
134
1
        assert_eq!(vec, vec![num(42)]);
135

            
136
1
        let back = vec_to_list(vec);
137
1
        assert_eq!(back, list);
138
1
    }
139

            
140
    #[test]
141
1
    fn test_multi_element_list() {
142
1
        let list = Pair::cons(num(1), Pair::cons(num(2), Pair::cons(num(3), Value::Nil)));
143

            
144
1
        let vec = list_to_vec(&list).unwrap();
145
1
        assert_eq!(vec, vec![num(1), num(2), num(3)]);
146

            
147
1
        let back = vec_to_list(vec);
148
1
        assert_eq!(back, list);
149
1
    }
150

            
151
    #[test]
152
1
    fn test_improper_list() {
153
1
        let improper = Pair::cons(num(1), num(2));
154
1
        assert!(list_to_vec(&improper).is_none());
155

            
156
1
        let improper = Pair::cons(num(1), Pair::cons(num(2), num(3)));
157
1
        assert!(list_to_vec(&improper).is_none());
158
1
    }
159

            
160
    #[test]
161
1
    fn test_heterogeneous_list() {
162
1
        let list = Pair::cons(
163
1
            num(1),
164
1
            Pair::cons(
165
1
                Value::String("hello".into()),
166
1
                Pair::cons(Value::T, Value::Nil),
167
            ),
168
        );
169

            
170
1
        let vec = list_to_vec(&list).unwrap();
171
1
        assert_eq!(vec.len(), 3);
172
1
        assert_eq!(vec[0], num(1));
173
1
        assert_eq!(vec[1], Value::String("hello".into()));
174
1
        assert_eq!(vec[2], Value::T);
175
1
    }
176

            
177
    #[test]
178
1
    fn test_nested_list() {
179
1
        let inner = Pair::cons(num(2), Pair::cons(num(3), Value::Nil));
180
1
        let outer = Pair::cons(num(1), Pair::cons(inner.clone(), Value::Nil));
181

            
182
1
        let vec = list_to_vec(&outer).unwrap();
183
1
        assert_eq!(vec.len(), 2);
184
1
        assert_eq!(vec[0], num(1));
185
1
        assert_eq!(vec[1], inner);
186
1
    }
187

            
188
    #[test]
189
1
    fn test_roundtrip_preserves_structure() {
190
1
        let original = vec![num(1), Value::String("test".into()), Value::Nil, frac(1, 3)];
191

            
192
1
        let as_list = vec_to_list(original.clone());
193
1
        let back = list_to_vec(&as_list).unwrap();
194

            
195
1
        assert_eq!(original, back);
196
1
    }
197
}
198

            
199
mod symbols {
200
    use super::*;
201

            
202
    #[test]
203
1
    fn test_symbol_creation() {
204
1
        let sym = Symbol::new("foo", SymbolKind::Variable);
205
1
        assert_eq!(sym.name(), "foo");
206
1
        assert_eq!(sym.kind(), SymbolKind::Variable);
207
1
    }
208

            
209
    #[test]
210
1
    fn test_symbol_table() {
211
1
        let mut table = SymbolTable::new();
212
1
        table.define(Symbol::new("+", SymbolKind::Operator));
213
1
        table.define(Symbol::new("car", SymbolKind::Native));
214

            
215
1
        assert!(table.contains("+"));
216
1
        assert!(table.contains("car"));
217
1
        assert!(!table.contains("undefined"));
218

            
219
1
        assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
220
1
        assert_eq!(table.lookup("car").unwrap().kind(), SymbolKind::Native);
221
1
    }
222

            
223
    #[test]
224
1
    fn test_symbol_table_builtins() {
225
1
        let table = SymbolTable::with_builtins();
226

            
227
1
        assert!(table.contains("+"));
228
1
        assert!(table.contains("IF"));
229
1
        assert!(table.contains("CAR"));
230

            
231
1
        assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
232
1
        assert_eq!(table.lookup("IF").unwrap().kind(), SymbolKind::SpecialForm);
233
1
        assert_eq!(table.lookup("CAR").unwrap().kind(), SymbolKind::Native);
234
1
    }
235
}
236

            
237
mod closures {
238
    use super::*;
239

            
240
    #[test]
241
1
    fn test_closure_creation() {
242
1
        let closure = Closure::new(42, vec![num(1), num(2)]);
243
1
        assert_eq!(closure.code_id, 42);
244
1
        assert_eq!(closure.env, vec![num(1), num(2)]);
245
1
    }
246

            
247
    #[test]
248
1
    fn test_empty_closure() {
249
1
        let closure = Closure::new(0, vec![]);
250
1
        assert_eq!(closure.code_id, 0);
251
1
        assert!(closure.env.is_empty());
252
1
    }
253
}
254

            
255
mod large_structures {
256
    use super::*;
257

            
258
    #[test]
259
1
    fn test_long_list() {
260
1
        let values: Vec<Value> = (0..1000).map(num).collect();
261
1
        let list = vec_to_list(values.clone());
262
1
        let back = list_to_vec(&list).unwrap();
263
1
        assert_eq!(values, back);
264
1
    }
265

            
266
    #[test]
267
1
    fn test_deeply_nested_pairs() {
268
1
        let mut val = num(0);
269
99
        for i in 1..100 {
270
99
            val = Pair::cons(num(i), val);
271
99
        }
272

            
273
1
        match &val {
274
1
            Value::Pair(p) => assert_eq!(p.car, num(99)),
275
            _ => panic!("expected pair"),
276
        }
277
1
    }
278

            
279
    #[test]
280
1
    fn test_large_vector() {
281
1
        let vec: Vec<Value> = (0..10000).map(num).collect();
282
1
        let val = Value::Vector(vec.clone());
283

            
284
1
        match val {
285
1
            Value::Vector(v) => assert_eq!(v.len(), 10000),
286
            _ => panic!("expected vector"),
287
        }
288
1
    }
289
}