1
use nomiscript::{Expr, Fraction, Program, Reader};
2

            
3
128
fn parse(input: &str) -> Program {
4
128
    Reader::parse(input).unwrap()
5
128
}
6

            
7
119
fn parse_one(input: &str) -> Expr {
8
119
    let program = parse(input);
9
119
    assert_eq!(program.exprs.len(), 1, "expected single expression");
10
119
    program.exprs.into_iter().next().unwrap()
11
119
}
12

            
13
52
fn num(n: i64) -> Expr {
14
52
    Expr::Number(Fraction::from_integer(n))
15
52
}
16

            
17
9
fn frac(n: i64, d: i64) -> Expr {
18
9
    Expr::Number(Fraction::new(n, d))
19
9
}
20

            
21
96
fn sym(s: &str) -> Expr {
22
96
    Expr::Symbol(s.into())
23
96
}
24

            
25
61
fn list(items: Vec<Expr>) -> Expr {
26
61
    Expr::List(items)
27
61
}
28

            
29
mod atoms {
30
    use super::*;
31

            
32
    #[test]
33
1
    fn test_nil_variations() {
34
1
        let cases = [("nil", Expr::Nil), ("  nil  ", Expr::Nil)];
35

            
36
2
        for (input, expected) in cases {
37
2
            assert_eq!(parse_one(input), expected, "input: {input:?}");
38
        }
39
1
    }
40

            
41
    #[test]
42
1
    fn test_booleans() {
43
1
        let cases = [
44
1
            ("#t", Expr::Bool(true)),
45
1
            ("#f", Expr::Nil),
46
1
            ("  #t  ", Expr::Bool(true)),
47
1
            ("  #f  ", Expr::Nil),
48
1
        ];
49

            
50
4
        for (input, expected) in cases {
51
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
52
        }
53
1
    }
54
}
55

            
56
mod numbers {
57
    use super::*;
58

            
59
    #[test]
60
1
    fn test_integers() {
61
1
        let cases = [
62
1
            ("0", num(0)),
63
1
            ("1", num(1)),
64
1
            ("42", num(42)),
65
1
            ("123456789", num(123456789)),
66
1
            ("-1", num(-1)),
67
1
            ("-42", num(-42)),
68
1
            ("+1", num(1)),
69
1
            ("+42", num(42)),
70
1
        ];
71

            
72
8
        for (input, expected) in cases {
73
8
            assert_eq!(parse_one(input), expected, "input: {input:?}");
74
        }
75
1
    }
76

            
77
    #[test]
78
1
    fn test_decimals() {
79
1
        let cases = [
80
1
            ("0.1", frac(1, 10)),
81
1
            ("0.01", frac(1, 100)),
82
1
            ("0.001", frac(1, 1000)),
83
1
            ("1.5", frac(3, 2)),
84
1
            ("3.14", frac(314, 100)),
85
1
            ("10.25", frac(1025, 100)),
86
1
            ("-0.5", frac(-1, 2)),
87
1
            ("-1.25", frac(-5, 4)),
88
1
        ];
89

            
90
8
        for (input, expected) in cases {
91
8
            assert_eq!(parse_one(input), expected, "input: {input:?}");
92
        }
93
1
    }
94

            
95
    #[test]
96
1
    fn test_decimal_normalization() {
97
1
        let half = parse_one("0.5");
98
1
        let also_half = parse_one("0.50");
99
1
        assert_eq!(half, also_half);
100

            
101
1
        let quarter = parse_one("0.25");
102
1
        assert_eq!(quarter, frac(1, 4));
103
1
    }
104
}
105

            
106
mod strings {
107
    use super::*;
108

            
109
    #[test]
110
1
    fn test_simple_strings() {
111
1
        let cases = [
112
1
            (r#""""#, Expr::String(String::new())),
113
1
            (r#""hello""#, Expr::String("hello".into())),
114
1
            (r#""hello world""#, Expr::String("hello world".into())),
115
1
            (r#""123""#, Expr::String("123".into())),
116
1
        ];
117

            
118
4
        for (input, expected) in cases {
119
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
120
        }
121
1
    }
122

            
123
    #[test]
124
1
    fn test_escape_sequences() {
125
1
        let cases = [
126
1
            (r#""hello\nworld""#, Expr::String("hello\nworld".into())),
127
1
            (r#""tab\there""#, Expr::String("tab\there".into())),
128
1
            (r#""return\rhere""#, Expr::String("return\rhere".into())),
129
1
            (r#""slash\\here""#, Expr::String("slash\\here".into())),
130
1
            (r#""quote\"here""#, Expr::String("quote\"here".into())),
131
1
            (r#""multi\n\tline""#, Expr::String("multi\n\tline".into())),
132
1
        ];
133

            
134
6
        for (input, expected) in cases {
135
6
            assert_eq!(parse_one(input), expected, "input: {input:?}");
136
        }
137
1
    }
138

            
139
    #[test]
140
1
    fn test_triple_quoted_strings() {
141
1
        let cases = [
142
1
            (r#""""""""#, Expr::String(String::new())),
143
1
            (r#""""hello""""#, Expr::String("hello".into())),
144
1
            (
145
1
                r#""""line1
146
1
line2""""#,
147
1
                Expr::String("line1\nline2".into()),
148
1
            ),
149
1
            (
150
1
                r#""""contains "quotes" inside""""#,
151
1
                Expr::String("contains \"quotes\" inside".into()),
152
1
            ),
153
1
        ];
154

            
155
4
        for (input, expected) in cases {
156
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
157
        }
158
1
    }
159
}
160

            
161
mod symbols {
162
    use super::*;
163

            
164
    #[test]
165
1
    fn test_simple_symbols() {
166
1
        let cases = [
167
1
            ("x", sym("X")),
168
1
            ("foo", sym("FOO")),
169
1
            ("hello-world", sym("HELLO-WORLD")),
170
1
            ("snake_case", sym("SNAKE_CASE")),
171
1
            ("CamelCase", sym("CAMELCASE")),
172
1
        ];
173

            
174
5
        for (input, expected) in cases {
175
5
            assert_eq!(parse_one(input), expected, "input: {input:?}");
176
        }
177
1
    }
178

            
179
    #[test]
180
1
    fn test_operator_symbols() {
181
1
        let cases = [
182
1
            ("+", sym("+")),
183
1
            ("-", sym("-")),
184
1
            ("*", sym("*")),
185
1
            ("/", sym("/")),
186
1
            ("=", sym("=")),
187
1
            ("<", sym("<")),
188
1
            (">", sym(">")),
189
1
            ("<=", sym("<=")),
190
1
            (">=", sym(">=")),
191
1
            ("!=", sym("!=")),
192
1
            ("++", sym("++")),
193
1
            ("->", sym("->")),
194
1
            ("=>", sym("=>")),
195
1
        ];
196

            
197
13
        for (input, expected) in cases {
198
13
            assert_eq!(parse_one(input), expected, "input: {input:?}");
199
        }
200
1
    }
201

            
202
    #[test]
203
1
    fn test_special_symbols() {
204
1
        let cases = [
205
1
            ("define", sym("DEFINE")),
206
1
            ("lambda", sym("LAMBDA")),
207
1
            ("if", sym("IF")),
208
1
            ("cond", sym("COND")),
209
1
            ("let", sym("LET")),
210
1
            ("let*", sym("LET*")),
211
1
            ("set!", sym("SET!")),
212
1
            ("begin", sym("BEGIN")),
213
1
            ("car", sym("CAR")),
214
1
            ("cdr", sym("CDR")),
215
1
            ("cons", sym("CONS")),
216
1
            ("list?", sym("LIST?")),
217
1
            ("null?", sym("NULL?")),
218
1
            ("pair?", sym("PAIR?")),
219
1
        ];
220

            
221
14
        for (input, expected) in cases {
222
14
            assert_eq!(parse_one(input), expected, "input: {input:?}");
223
        }
224
1
    }
225

            
226
    #[test]
227
1
    fn test_case_insensitive() {
228
1
        assert_eq!(parse_one("foo"), parse_one("FOO"));
229
1
        assert_eq!(parse_one("foo"), parse_one("Foo"));
230
1
        assert_eq!(parse_one("Sum"), sym("SUM"));
231
1
    }
232

            
233
    #[test]
234
1
    fn test_nil_as_symbol_variant() {
235
1
        assert_eq!(parse_one("nIl"), Expr::Nil);
236
1
        assert_eq!(parse_one("nIL"), Expr::Nil);
237
1
    }
238
}
239

            
240
mod lists {
241
    use super::*;
242

            
243
    #[test]
244
1
    fn test_empty_list() {
245
1
        assert_eq!(parse_one("()"), list(vec![]));
246
1
        assert_eq!(parse_one("(  )"), list(vec![]));
247
1
    }
248

            
249
    #[test]
250
1
    fn test_simple_lists() {
251
1
        let cases = [
252
1
            ("(1)", list(vec![num(1)])),
253
1
            ("(1 2)", list(vec![num(1), num(2)])),
254
1
            ("(1 2 3)", list(vec![num(1), num(2), num(3)])),
255
1
            ("(a b c)", list(vec![sym("A"), sym("B"), sym("C")])),
256
1
        ];
257

            
258
4
        for (input, expected) in cases {
259
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
260
        }
261
1
    }
262

            
263
    #[test]
264
1
    fn test_nested_lists() {
265
1
        let cases = [
266
1
            ("(())", list(vec![list(vec![])])),
267
1
            ("((1))", list(vec![list(vec![num(1)])])),
268
1
            (
269
1
                "((1 2) (3 4))",
270
1
                list(vec![list(vec![num(1), num(2)]), list(vec![num(3), num(4)])]),
271
1
            ),
272
1
            (
273
1
                "(a (b (c d)))",
274
1
                list(vec![
275
1
                    sym("A"),
276
1
                    list(vec![sym("B"), list(vec![sym("C"), sym("D")])]),
277
1
                ]),
278
1
            ),
279
1
        ];
280

            
281
4
        for (input, expected) in cases {
282
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
283
        }
284
1
    }
285

            
286
    #[test]
287
1
    fn test_mixed_lists() {
288
1
        let cases = [
289
1
            ("(+ 1 2)", list(vec![sym("+"), num(1), num(2)])),
290
1
            (
291
1
                r#"(print "hello")"#,
292
1
                list(vec![sym("PRINT"), Expr::String("hello".into())]),
293
1
            ),
294
1
            (
295
1
                "(if #t 1 0)",
296
1
                list(vec![sym("IF"), Expr::Bool(true), num(1), num(0)]),
297
1
            ),
298
1
        ];
299

            
300
3
        for (input, expected) in cases {
301
3
            assert_eq!(parse_one(input), expected, "input: {input:?}");
302
        }
303
1
    }
304
}
305

            
306
mod quotes {
307
    use super::*;
308

            
309
    #[test]
310
1
    fn test_quoted_atoms() {
311
1
        let cases = [
312
1
            ("'x", Expr::Quote(Box::new(sym("X")))),
313
1
            ("'42", Expr::Quote(Box::new(num(42)))),
314
1
            ("'nil", Expr::Quote(Box::new(Expr::Nil))),
315
1
            ("'#t", Expr::Quote(Box::new(Expr::Bool(true)))),
316
1
        ];
317

            
318
4
        for (input, expected) in cases {
319
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
320
        }
321
1
    }
322

            
323
    #[test]
324
1
    fn test_quoted_lists() {
325
1
        let cases = [
326
1
            ("'()", Expr::Quote(Box::new(list(vec![])))),
327
1
            (
328
1
                "'(1 2 3)",
329
1
                Expr::Quote(Box::new(list(vec![num(1), num(2), num(3)]))),
330
1
            ),
331
1
            (
332
1
                "'(a b c)",
333
1
                Expr::Quote(Box::new(list(vec![sym("A"), sym("B"), sym("C")]))),
334
1
            ),
335
1
        ];
336

            
337
3
        for (input, expected) in cases {
338
3
            assert_eq!(parse_one(input), expected, "input: {input:?}");
339
        }
340
1
    }
341

            
342
    #[test]
343
1
    fn test_nested_quotes() {
344
1
        let input = "''x";
345
1
        let expected = Expr::Quote(Box::new(Expr::Quote(Box::new(sym("X")))));
346
1
        assert_eq!(parse_one(input), expected);
347
1
    }
348
}
349

            
350
mod programs {
351
    use super::*;
352

            
353
    #[test]
354
1
    fn test_empty_program() {
355
1
        let program = parse("");
356
1
        assert!(program.exprs.is_empty());
357

            
358
1
        let program = parse("   ");
359
1
        assert!(program.exprs.is_empty());
360

            
361
1
        let program = parse("\n\n");
362
1
        assert!(program.exprs.is_empty());
363
1
    }
364

            
365
    #[test]
366
1
    fn test_multiple_expressions() {
367
1
        let program = parse("1 2 3");
368
1
        assert_eq!(program.exprs.len(), 3);
369
1
        assert_eq!(program.exprs[0], num(1));
370
1
        assert_eq!(program.exprs[1], num(2));
371
1
        assert_eq!(program.exprs[2], num(3));
372
1
    }
373

            
374
    #[test]
375
1
    fn test_scheme_like_programs() {
376
1
        let cases = [
377
1
            (
378
1
                "(define x 10)",
379
1
                vec![list(vec![sym("DEFINE"), sym("X"), num(10)])],
380
1
            ),
381
1
            (
382
1
                "(define (square x) (* x x))",
383
1
                vec![list(vec![
384
1
                    sym("DEFINE"),
385
1
                    list(vec![sym("SQUARE"), sym("X")]),
386
1
                    list(vec![sym("*"), sym("X"), sym("X")]),
387
1
                ])],
388
1
            ),
389
1
            (
390
1
                "(define x 10) (+ x 5)",
391
1
                vec![
392
1
                    list(vec![sym("DEFINE"), sym("X"), num(10)]),
393
1
                    list(vec![sym("+"), sym("X"), num(5)]),
394
1
                ],
395
1
            ),
396
1
            (
397
1
                "(if (> x 0) x (- x))",
398
1
                vec![list(vec![
399
1
                    sym("IF"),
400
1
                    list(vec![sym(">"), sym("X"), num(0)]),
401
1
                    sym("X"),
402
1
                    list(vec![sym("-"), sym("X")]),
403
1
                ])],
404
1
            ),
405
1
        ];
406

            
407
4
        for (input, expected) in cases {
408
4
            let program = parse(input);
409
4
            assert_eq!(program.exprs, expected, "input: {input:?}");
410
        }
411
1
    }
412

            
413
    #[test]
414
1
    fn test_lambda_expressions() {
415
1
        let cases = [
416
1
            (
417
1
                "(lambda (x) x)",
418
1
                list(vec![sym("LAMBDA"), list(vec![sym("X")]), sym("X")]),
419
1
            ),
420
1
            (
421
1
                "(lambda (x y) (+ x y))",
422
1
                list(vec![
423
1
                    sym("LAMBDA"),
424
1
                    list(vec![sym("X"), sym("Y")]),
425
1
                    list(vec![sym("+"), sym("X"), sym("Y")]),
426
1
                ]),
427
1
            ),
428
1
            (
429
1
                "((lambda (x) (* x x)) 5)",
430
1
                list(vec![
431
1
                    list(vec![
432
1
                        sym("LAMBDA"),
433
1
                        list(vec![sym("X")]),
434
1
                        list(vec![sym("*"), sym("X"), sym("X")]),
435
1
                    ]),
436
1
                    num(5),
437
1
                ]),
438
1
            ),
439
1
        ];
440

            
441
3
        for (input, expected) in cases {
442
3
            assert_eq!(parse_one(input), expected, "input: {input:?}");
443
        }
444
1
    }
445

            
446
    #[test]
447
1
    fn test_let_expressions() {
448
1
        let cases = [
449
1
            (
450
1
                "(let ((x 1)) x)",
451
1
                list(vec![
452
1
                    sym("LET"),
453
1
                    list(vec![list(vec![sym("X"), num(1)])]),
454
1
                    sym("X"),
455
1
                ]),
456
1
            ),
457
1
            (
458
1
                "(let ((x 1) (y 2)) (+ x y))",
459
1
                list(vec![
460
1
                    sym("LET"),
461
1
                    list(vec![
462
1
                        list(vec![sym("X"), num(1)]),
463
1
                        list(vec![sym("Y"), num(2)]),
464
1
                    ]),
465
1
                    list(vec![sym("+"), sym("X"), sym("Y")]),
466
1
                ]),
467
1
            ),
468
1
        ];
469

            
470
2
        for (input, expected) in cases {
471
2
            assert_eq!(parse_one(input), expected, "input: {input:?}");
472
        }
473
1
    }
474

            
475
    #[test]
476
1
    fn test_cond_expressions() {
477
1
        let input = "(cond ((< x 0) -1) ((= x 0) 0) (#t 1))";
478
1
        let expected = list(vec![
479
1
            sym("COND"),
480
1
            list(vec![list(vec![sym("<"), sym("X"), num(0)]), num(-1)]),
481
1
            list(vec![list(vec![sym("="), sym("X"), num(0)]), num(0)]),
482
1
            list(vec![Expr::Bool(true), num(1)]),
483
        ]);
484
1
        assert_eq!(parse_one(input), expected);
485
1
    }
486
}
487

            
488
mod whitespace {
489
    use super::*;
490

            
491
    #[test]
492
1
    fn test_various_whitespace() {
493
1
        let inputs = [
494
1
            "(+ 1 2)",
495
1
            "( + 1 2 )",
496
1
            "(  +  1  2  )",
497
1
            "(\n+\n1\n2\n)",
498
1
            "(\t+\t1\t2\t)",
499
1
            "( \n\t + \n\t 1 \n\t 2 \n\t )",
500
1
        ];
501

            
502
1
        let expected = list(vec![sym("+"), num(1), num(2)]);
503

            
504
6
        for input in inputs {
505
6
            assert_eq!(parse_one(input), expected, "input: {input:?}");
506
        }
507
1
    }
508

            
509
    #[test]
510
1
    fn test_leading_trailing_whitespace() {
511
1
        let inputs = ["42", " 42", "42 ", " 42 ", "\n42\n", "\t42\t"];
512

            
513
6
        for input in inputs {
514
6
            assert_eq!(parse_one(input), num(42), "input: {input:?}");
515
        }
516
1
    }
517
}
518

            
519
mod edge_cases {
520
    use super::*;
521

            
522
    #[test]
523
1
    fn test_deeply_nested() {
524
1
        let input = "((((((1))))))";
525
1
        let mut expr = num(1);
526
6
        for _ in 0..6 {
527
6
            expr = list(vec![expr]);
528
6
        }
529
1
        assert_eq!(parse_one(input), expr);
530
1
    }
531

            
532
    #[test]
533
1
    fn test_long_symbol() {
534
1
        let input = "a".repeat(1000);
535
1
        let expected = "A".repeat(1000);
536
1
        assert_eq!(parse_one(&input), sym(&expected));
537
1
    }
538

            
539
    #[test]
540
1
    fn test_many_list_elements() {
541
1
        let input = format!(
542
            "({})",
543
1
            (1..=100)
544
100
                .map(|n| n.to_string())
545
1
                .collect::<Vec<_>>()
546
1
                .join(" ")
547
        );
548
1
        let program = parse(&input);
549
1
        assert_eq!(program.exprs.len(), 1);
550
1
        if let Expr::List(items) = &program.exprs[0] {
551
1
            assert_eq!(items.len(), 100);
552
        } else {
553
            panic!("expected list");
554
        }
555
1
    }
556
}