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::Bool(true), "bool"),
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
        let falsy = [Value::Nil, Value::Bool(false)];
40

            
41
1
        let truthy = [
42
1
            Value::Bool(true),
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
2
        for val in falsy {
54
2
            assert!(!val.is_truthy(), "{val:?} should be falsy");
55
        }
56

            
57
9
        for val in truthy {
58
9
            assert!(val.is_truthy(), "{val:?} should be truthy");
59
        }
60
1
    }
61
}
62

            
63
mod fractions {
64
    use super::*;
65

            
66
    #[test]
67
1
    fn test_fraction_equality() {
68
1
        assert_eq!(frac(1, 2), frac(2, 4));
69
1
        assert_eq!(frac(3, 6), frac(1, 2));
70
1
        assert_eq!(frac(100, 100), num(1));
71
1
    }
72

            
73
    #[test]
74
1
    fn test_fraction_normalization() {
75
1
        let half = Fraction::new(1, 2);
76
1
        let also_half = Fraction::new(50, 100);
77
1
        assert_eq!(half, also_half);
78

            
79
1
        let negative = Fraction::new(-1, 2);
80
1
        let also_negative = Fraction::new(1, -2);
81
1
        assert_eq!(negative, also_negative);
82
1
    }
83
}
84

            
85
mod pairs {
86
    use super::*;
87

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

            
100
    #[test]
101
1
    fn test_nested_pairs() {
102
1
        let inner = Pair::cons(num(2), num(3));
103
1
        let outer = Pair::cons(num(1), inner);
104

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

            
121
mod lists {
122
    use super::*;
123

            
124
    #[test]
125
1
    fn test_empty_list() {
126
1
        let list = Value::Nil;
127
1
        let vec = list_to_vec(&list).unwrap();
128
1
        assert!(vec.is_empty());
129

            
130
1
        let back = vec_to_list(vec);
131
1
        assert_eq!(back, Value::Nil);
132
1
    }
133

            
134
    #[test]
135
1
    fn test_single_element_list() {
136
1
        let list = Pair::cons(num(42), Value::Nil);
137
1
        let vec = list_to_vec(&list).unwrap();
138
1
        assert_eq!(vec, vec![num(42)]);
139

            
140
1
        let back = vec_to_list(vec);
141
1
        assert_eq!(back, list);
142
1
    }
143

            
144
    #[test]
145
1
    fn test_multi_element_list() {
146
1
        let list = Pair::cons(num(1), Pair::cons(num(2), Pair::cons(num(3), Value::Nil)));
147

            
148
1
        let vec = list_to_vec(&list).unwrap();
149
1
        assert_eq!(vec, vec![num(1), num(2), num(3)]);
150

            
151
1
        let back = vec_to_list(vec);
152
1
        assert_eq!(back, list);
153
1
    }
154

            
155
    #[test]
156
1
    fn test_improper_list() {
157
1
        let improper = Pair::cons(num(1), num(2));
158
1
        assert!(list_to_vec(&improper).is_none());
159

            
160
1
        let improper = Pair::cons(num(1), Pair::cons(num(2), num(3)));
161
1
        assert!(list_to_vec(&improper).is_none());
162
1
    }
163

            
164
    #[test]
165
1
    fn test_heterogeneous_list() {
166
1
        let list = Pair::cons(
167
1
            num(1),
168
1
            Pair::cons(
169
1
                Value::String("hello".into()),
170
1
                Pair::cons(Value::Bool(true), Value::Nil),
171
            ),
172
        );
173

            
174
1
        let vec = list_to_vec(&list).unwrap();
175
1
        assert_eq!(vec.len(), 3);
176
1
        assert_eq!(vec[0], num(1));
177
1
        assert_eq!(vec[1], Value::String("hello".into()));
178
1
        assert_eq!(vec[2], Value::Bool(true));
179
1
    }
180

            
181
    #[test]
182
1
    fn test_nested_list() {
183
1
        let inner = Pair::cons(num(2), Pair::cons(num(3), Value::Nil));
184
1
        let outer = Pair::cons(num(1), Pair::cons(inner.clone(), Value::Nil));
185

            
186
1
        let vec = list_to_vec(&outer).unwrap();
187
1
        assert_eq!(vec.len(), 2);
188
1
        assert_eq!(vec[0], num(1));
189
1
        assert_eq!(vec[1], inner);
190
1
    }
191

            
192
    #[test]
193
1
    fn test_roundtrip_preserves_structure() {
194
1
        let original = vec![
195
1
            num(1),
196
1
            Value::String("test".into()),
197
1
            Value::Bool(false),
198
1
            frac(1, 3),
199
        ];
200

            
201
1
        let as_list = vec_to_list(original.clone());
202
1
        let back = list_to_vec(&as_list).unwrap();
203

            
204
1
        assert_eq!(original, back);
205
1
    }
206
}
207

            
208
mod symbols {
209
    use super::*;
210

            
211
    #[test]
212
1
    fn test_symbol_creation() {
213
1
        let sym = Symbol::new("foo", SymbolKind::Variable);
214
1
        assert_eq!(sym.name(), "foo");
215
1
        assert_eq!(sym.kind(), SymbolKind::Variable);
216
1
    }
217

            
218
    #[test]
219
1
    fn test_symbol_table() {
220
1
        let mut table = SymbolTable::new();
221
1
        table.define(Symbol::new("+", SymbolKind::Operator));
222
1
        table.define(Symbol::new("car", SymbolKind::Native));
223

            
224
1
        assert!(table.contains("+"));
225
1
        assert!(table.contains("car"));
226
1
        assert!(!table.contains("undefined"));
227

            
228
1
        assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
229
1
        assert_eq!(table.lookup("car").unwrap().kind(), SymbolKind::Native);
230
1
    }
231

            
232
    #[test]
233
1
    fn test_symbol_table_builtins() {
234
1
        let table = SymbolTable::with_builtins();
235

            
236
1
        assert!(table.contains("+"));
237
1
        assert!(table.contains("IF"));
238
1
        assert!(table.contains("CAR"));
239

            
240
1
        assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
241
1
        assert_eq!(table.lookup("IF").unwrap().kind(), SymbolKind::SpecialForm);
242
1
        assert_eq!(table.lookup("CAR").unwrap().kind(), SymbolKind::Native);
243
1
    }
244
}
245

            
246
mod closures {
247
    use super::*;
248

            
249
    #[test]
250
1
    fn test_closure_creation() {
251
1
        let closure = Closure::new(42, vec![num(1), num(2)]);
252
1
        assert_eq!(closure.code_id, 42);
253
1
        assert_eq!(closure.env, vec![num(1), num(2)]);
254
1
    }
255

            
256
    #[test]
257
1
    fn test_empty_closure() {
258
1
        let closure = Closure::new(0, vec![]);
259
1
        assert_eq!(closure.code_id, 0);
260
1
        assert!(closure.env.is_empty());
261
1
    }
262
}
263

            
264
mod large_structures {
265
    use super::*;
266

            
267
    #[test]
268
1
    fn test_long_list() {
269
1
        let values: Vec<Value> = (0..1000).map(num).collect();
270
1
        let list = vec_to_list(values.clone());
271
1
        let back = list_to_vec(&list).unwrap();
272
1
        assert_eq!(values, back);
273
1
    }
274

            
275
    #[test]
276
1
    fn test_deeply_nested_pairs() {
277
1
        let mut val = num(0);
278
99
        for i in 1..100 {
279
99
            val = Pair::cons(num(i), val);
280
99
        }
281

            
282
1
        match &val {
283
1
            Value::Pair(p) => assert_eq!(p.car, num(99)),
284
            _ => panic!("expected pair"),
285
        }
286
1
    }
287

            
288
    #[test]
289
1
    fn test_large_vector() {
290
1
        let vec: Vec<Value> = (0..10000).map(num).collect();
291
1
        let val = Value::Vector(vec.clone());
292

            
293
1
        match val {
294
1
            Value::Vector(v) => assert_eq!(v.len(), 10000),
295
            _ => panic!("expected vector"),
296
        }
297
1
    }
298
}