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

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

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

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

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

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

            
25
64
fn list(items: Vec<Expr>) -> Expr {
26
64
    Expr::List(items)
27
64
}
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
    #[test]
106
1
    fn test_percent_sugar() {
107
        // `15%` reads as `15/100` (reduced). The `%` rides any numeric body —
108
        // integer, decimal, or rational — and divides it by 100.
109
1
        let cases = [
110
1
            ("15%", frac(15, 100)),
111
1
            ("100%", num(1)),
112
1
            ("0.5%", frac(1, 200)),
113
1
            ("1/2%", frac(1, 200)),
114
1
            ("-50%", frac(-1, 2)),
115
1
        ];
116

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

            
122
    #[test]
123
1
    fn test_percent_equivalent_to_explicit_fraction() {
124
        // `15%` and `15/100` denote the same Scalar — and the same inside a form.
125
1
        assert_eq!(parse_one("15%"), parse_one("15/100"));
126
1
        assert_eq!(parse_one("(* amount 15%)"), parse_one("(* amount 15/100)"));
127
1
    }
128

            
129
    #[test]
130
1
    fn test_decimal_overflow_is_parse_error() {
131
        // Malformed decimals must be structured parse errors, never i64
132
        // overflow panics (debug) / wraps (release).
133
        // 19 fractional digits → 10^19 exceeds i64.
134
1
        assert!(Reader::parse("1.0000000000000000000").is_err());
135
        // 9 × 10^18 + 10^18-scale fraction overflows the scaled numerator.
136
1
        assert!(Reader::parse("9.999999999999999999").is_err());
137
1
    }
138

            
139
    #[test]
140
1
    fn test_percent_denominator_overflow_is_parse_error() {
141
        // `denom * 100` would overflow i64 — must be a structured parse error,
142
        // never an overflow panic (debug) or silent wrap (release).
143
1
        assert!(Reader::parse("1/9223372036854775807%").is_err());
144
1
    }
145

            
146
    #[test]
147
1
    fn test_bare_percent_is_still_a_symbol() {
148
        // `%` is delimiter-only for terminating a numeric token; a lone `%`
149
        // (no numeric body) still reads as a symbol.
150
1
        assert_eq!(parse_one("%"), sym("%"));
151
1
    }
152
}
153

            
154
mod strings {
155
    use super::*;
156

            
157
    #[test]
158
1
    fn test_simple_strings() {
159
1
        let cases = [
160
1
            (r#""""#, Expr::String(String::new())),
161
1
            (r#""hello""#, Expr::String("hello".into())),
162
1
            (r#""hello world""#, Expr::String("hello world".into())),
163
1
            (r#""123""#, Expr::String("123".into())),
164
1
        ];
165

            
166
4
        for (input, expected) in cases {
167
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
168
        }
169
1
    }
170

            
171
    #[test]
172
1
    fn test_escape_sequences() {
173
1
        let cases = [
174
1
            (r#""hello\nworld""#, Expr::String("hello\nworld".into())),
175
1
            (r#""tab\there""#, Expr::String("tab\there".into())),
176
1
            (r#""return\rhere""#, Expr::String("return\rhere".into())),
177
1
            (r#""slash\\here""#, Expr::String("slash\\here".into())),
178
1
            (r#""quote\"here""#, Expr::String("quote\"here".into())),
179
1
            (r#""multi\n\tline""#, Expr::String("multi\n\tline".into())),
180
1
        ];
181

            
182
6
        for (input, expected) in cases {
183
6
            assert_eq!(parse_one(input), expected, "input: {input:?}");
184
        }
185
1
    }
186

            
187
    #[test]
188
1
    fn test_triple_quoted_strings() {
189
1
        let cases = [
190
1
            (r#""""""""#, Expr::String(String::new())),
191
1
            (r#""""hello""""#, Expr::String("hello".into())),
192
1
            (
193
1
                r#""""line1
194
1
line2""""#,
195
1
                Expr::String("line1\nline2".into()),
196
1
            ),
197
1
            (
198
1
                r#""""contains "quotes" inside""""#,
199
1
                Expr::String("contains \"quotes\" inside".into()),
200
1
            ),
201
1
        ];
202

            
203
4
        for (input, expected) in cases {
204
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
205
        }
206
1
    }
207
}
208

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

            
212
    #[test]
213
1
    fn test_simple_symbols() {
214
1
        let cases = [
215
1
            ("x", sym("X")),
216
1
            ("foo", sym("FOO")),
217
1
            ("hello-world", sym("HELLO-WORLD")),
218
1
            ("snake_case", sym("SNAKE_CASE")),
219
1
            ("CamelCase", sym("CAMELCASE")),
220
1
        ];
221

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

            
227
    #[test]
228
1
    fn test_operator_symbols() {
229
1
        let cases = [
230
1
            ("+", sym("+")),
231
1
            ("-", sym("-")),
232
1
            ("*", sym("*")),
233
1
            ("/", sym("/")),
234
1
            ("=", sym("=")),
235
1
            ("<", sym("<")),
236
1
            (">", sym(">")),
237
1
            ("<=", sym("<=")),
238
1
            (">=", sym(">=")),
239
1
            ("!=", sym("!=")),
240
1
            ("++", sym("++")),
241
1
            ("->", sym("->")),
242
1
            ("=>", sym("=>")),
243
1
        ];
244

            
245
13
        for (input, expected) in cases {
246
13
            assert_eq!(parse_one(input), expected, "input: {input:?}");
247
        }
248
1
    }
249

            
250
    #[test]
251
1
    fn test_special_symbols() {
252
1
        let cases = [
253
1
            ("define", sym("DEFINE")),
254
1
            ("lambda", sym("LAMBDA")),
255
1
            ("if", sym("IF")),
256
1
            ("cond", sym("COND")),
257
1
            ("let", sym("LET")),
258
1
            ("let*", sym("LET*")),
259
1
            ("set!", sym("SET!")),
260
1
            ("begin", sym("BEGIN")),
261
1
            ("car", sym("CAR")),
262
1
            ("cdr", sym("CDR")),
263
1
            ("cons", sym("CONS")),
264
1
            ("list?", sym("LIST?")),
265
1
            ("null?", sym("NULL?")),
266
1
            ("pair?", sym("PAIR?")),
267
1
        ];
268

            
269
14
        for (input, expected) in cases {
270
14
            assert_eq!(parse_one(input), expected, "input: {input:?}");
271
        }
272
1
    }
273

            
274
    #[test]
275
1
    fn test_case_insensitive() {
276
1
        assert_eq!(parse_one("foo"), parse_one("FOO"));
277
1
        assert_eq!(parse_one("foo"), parse_one("Foo"));
278
1
        assert_eq!(parse_one("Sum"), sym("SUM"));
279
1
    }
280

            
281
    #[test]
282
1
    fn test_nil_as_symbol_variant() {
283
1
        assert_eq!(parse_one("nIl"), Expr::Nil);
284
1
        assert_eq!(parse_one("nIL"), Expr::Nil);
285
1
    }
286
}
287

            
288
mod lists {
289
    use super::*;
290

            
291
    #[test]
292
1
    fn test_empty_list() {
293
1
        assert_eq!(parse_one("()"), list(vec![]));
294
1
        assert_eq!(parse_one("(  )"), list(vec![]));
295
1
    }
296

            
297
    #[test]
298
1
    fn test_simple_lists() {
299
1
        let cases = [
300
1
            ("(1)", list(vec![num(1)])),
301
1
            ("(1 2)", list(vec![num(1), num(2)])),
302
1
            ("(1 2 3)", list(vec![num(1), num(2), num(3)])),
303
1
            ("(a b c)", list(vec![sym("A"), sym("B"), sym("C")])),
304
1
        ];
305

            
306
4
        for (input, expected) in cases {
307
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
308
        }
309
1
    }
310

            
311
    #[test]
312
1
    fn test_nested_lists() {
313
1
        let cases = [
314
1
            ("(())", list(vec![list(vec![])])),
315
1
            ("((1))", list(vec![list(vec![num(1)])])),
316
1
            (
317
1
                "((1 2) (3 4))",
318
1
                list(vec![list(vec![num(1), num(2)]), list(vec![num(3), num(4)])]),
319
1
            ),
320
1
            (
321
1
                "(a (b (c d)))",
322
1
                list(vec![
323
1
                    sym("A"),
324
1
                    list(vec![sym("B"), list(vec![sym("C"), sym("D")])]),
325
1
                ]),
326
1
            ),
327
1
        ];
328

            
329
4
        for (input, expected) in cases {
330
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
331
        }
332
1
    }
333

            
334
    #[test]
335
1
    fn test_mixed_lists() {
336
1
        let cases = [
337
1
            ("(+ 1 2)", list(vec![sym("+"), num(1), num(2)])),
338
1
            (
339
1
                r#"(print "hello")"#,
340
1
                list(vec![sym("PRINT"), Expr::String("hello".into())]),
341
1
            ),
342
1
            (
343
1
                "(if #t 1 0)",
344
1
                list(vec![sym("IF"), Expr::Bool(true), num(1), num(0)]),
345
1
            ),
346
1
        ];
347

            
348
3
        for (input, expected) in cases {
349
3
            assert_eq!(parse_one(input), expected, "input: {input:?}");
350
        }
351
1
    }
352
}
353

            
354
mod quotes {
355
    use super::*;
356

            
357
    #[test]
358
1
    fn test_quoted_atoms() {
359
1
        let cases = [
360
1
            ("'x", Expr::Quote(Box::new(sym("X")))),
361
1
            ("'42", Expr::Quote(Box::new(num(42)))),
362
1
            ("'nil", Expr::Quote(Box::new(Expr::Nil))),
363
1
            ("'#t", Expr::Quote(Box::new(Expr::Bool(true)))),
364
1
        ];
365

            
366
4
        for (input, expected) in cases {
367
4
            assert_eq!(parse_one(input), expected, "input: {input:?}");
368
        }
369
1
    }
370

            
371
    #[test]
372
1
    fn test_quoted_lists() {
373
1
        let cases = [
374
1
            ("'()", Expr::Quote(Box::new(list(vec![])))),
375
1
            (
376
1
                "'(1 2 3)",
377
1
                Expr::Quote(Box::new(list(vec![num(1), num(2), num(3)]))),
378
1
            ),
379
1
            (
380
1
                "'(a b c)",
381
1
                Expr::Quote(Box::new(list(vec![sym("A"), sym("B"), sym("C")]))),
382
1
            ),
383
1
        ];
384

            
385
3
        for (input, expected) in cases {
386
3
            assert_eq!(parse_one(input), expected, "input: {input:?}");
387
        }
388
1
    }
389

            
390
    #[test]
391
1
    fn test_nested_quotes() {
392
1
        let input = "''x";
393
1
        let expected = Expr::Quote(Box::new(Expr::Quote(Box::new(sym("X")))));
394
1
        assert_eq!(parse_one(input), expected);
395
1
    }
396
}
397

            
398
mod programs {
399
    use super::*;
400

            
401
    #[test]
402
1
    fn test_empty_program() {
403
1
        let program = parse("");
404
1
        assert!(program.exprs.is_empty());
405

            
406
1
        let program = parse("   ");
407
1
        assert!(program.exprs.is_empty());
408

            
409
1
        let program = parse("\n\n");
410
1
        assert!(program.exprs.is_empty());
411
1
    }
412

            
413
    #[test]
414
1
    fn test_multiple_expressions() {
415
1
        let program = parse("1 2 3");
416
1
        assert_eq!(program.exprs.len(), 3);
417
1
        assert_eq!(program.exprs[0], num(1));
418
1
        assert_eq!(program.exprs[1], num(2));
419
1
        assert_eq!(program.exprs[2], num(3));
420
1
    }
421

            
422
    #[test]
423
1
    fn test_scheme_like_programs() {
424
1
        let cases = [
425
1
            (
426
1
                "(define x 10)",
427
1
                vec![list(vec![sym("DEFINE"), sym("X"), num(10)])],
428
1
            ),
429
1
            (
430
1
                "(define (square x) (* x x))",
431
1
                vec![list(vec![
432
1
                    sym("DEFINE"),
433
1
                    list(vec![sym("SQUARE"), sym("X")]),
434
1
                    list(vec![sym("*"), sym("X"), sym("X")]),
435
1
                ])],
436
1
            ),
437
1
            (
438
1
                "(define x 10) (+ x 5)",
439
1
                vec![
440
1
                    list(vec![sym("DEFINE"), sym("X"), num(10)]),
441
1
                    list(vec![sym("+"), sym("X"), num(5)]),
442
1
                ],
443
1
            ),
444
1
            (
445
1
                "(if (> x 0) x (- x))",
446
1
                vec![list(vec![
447
1
                    sym("IF"),
448
1
                    list(vec![sym(">"), sym("X"), num(0)]),
449
1
                    sym("X"),
450
1
                    list(vec![sym("-"), sym("X")]),
451
1
                ])],
452
1
            ),
453
1
        ];
454

            
455
4
        for (input, expected) in cases {
456
4
            let program = parse(input);
457
4
            assert_eq!(program.exprs, expected, "input: {input:?}");
458
        }
459
1
    }
460

            
461
    #[test]
462
1
    fn test_lambda_expressions() {
463
1
        let cases = [
464
1
            (
465
1
                "(lambda (x) x)",
466
1
                list(vec![sym("LAMBDA"), list(vec![sym("X")]), sym("X")]),
467
1
            ),
468
1
            (
469
1
                "(lambda (x y) (+ x y))",
470
1
                list(vec![
471
1
                    sym("LAMBDA"),
472
1
                    list(vec![sym("X"), sym("Y")]),
473
1
                    list(vec![sym("+"), sym("X"), sym("Y")]),
474
1
                ]),
475
1
            ),
476
1
            (
477
1
                "((lambda (x) (* x x)) 5)",
478
1
                list(vec![
479
1
                    list(vec![
480
1
                        sym("LAMBDA"),
481
1
                        list(vec![sym("X")]),
482
1
                        list(vec![sym("*"), sym("X"), sym("X")]),
483
1
                    ]),
484
1
                    num(5),
485
1
                ]),
486
1
            ),
487
1
        ];
488

            
489
3
        for (input, expected) in cases {
490
3
            assert_eq!(parse_one(input), expected, "input: {input:?}");
491
        }
492
1
    }
493

            
494
    #[test]
495
1
    fn test_let_expressions() {
496
1
        let cases = [
497
1
            (
498
1
                "(let ((x 1)) x)",
499
1
                list(vec![
500
1
                    sym("LET"),
501
1
                    list(vec![list(vec![sym("X"), num(1)])]),
502
1
                    sym("X"),
503
1
                ]),
504
1
            ),
505
1
            (
506
1
                "(let ((x 1) (y 2)) (+ x y))",
507
1
                list(vec![
508
1
                    sym("LET"),
509
1
                    list(vec![
510
1
                        list(vec![sym("X"), num(1)]),
511
1
                        list(vec![sym("Y"), num(2)]),
512
1
                    ]),
513
1
                    list(vec![sym("+"), sym("X"), sym("Y")]),
514
1
                ]),
515
1
            ),
516
1
        ];
517

            
518
2
        for (input, expected) in cases {
519
2
            assert_eq!(parse_one(input), expected, "input: {input:?}");
520
        }
521
1
    }
522

            
523
    #[test]
524
1
    fn test_cond_expressions() {
525
1
        let input = "(cond ((< x 0) -1) ((= x 0) 0) (#t 1))";
526
1
        let expected = list(vec![
527
1
            sym("COND"),
528
1
            list(vec![list(vec![sym("<"), sym("X"), num(0)]), num(-1)]),
529
1
            list(vec![list(vec![sym("="), sym("X"), num(0)]), num(0)]),
530
1
            list(vec![Expr::Bool(true), num(1)]),
531
        ]);
532
1
        assert_eq!(parse_one(input), expected);
533
1
    }
534
}
535

            
536
mod whitespace {
537
    use super::*;
538

            
539
    #[test]
540
1
    fn test_various_whitespace() {
541
1
        let inputs = [
542
1
            "(+ 1 2)",
543
1
            "( + 1 2 )",
544
1
            "(  +  1  2  )",
545
1
            "(\n+\n1\n2\n)",
546
1
            "(\t+\t1\t2\t)",
547
1
            "( \n\t + \n\t 1 \n\t 2 \n\t )",
548
1
        ];
549

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

            
552
6
        for input in inputs {
553
6
            assert_eq!(parse_one(input), expected, "input: {input:?}");
554
        }
555
1
    }
556

            
557
    #[test]
558
1
    fn test_leading_trailing_whitespace() {
559
1
        let inputs = ["42", " 42", "42 ", " 42 ", "\n42\n", "\t42\t"];
560

            
561
6
        for input in inputs {
562
6
            assert_eq!(parse_one(input), num(42), "input: {input:?}");
563
        }
564
1
    }
565
}
566

            
567
mod edge_cases {
568
    use super::*;
569

            
570
    #[test]
571
1
    fn test_deeply_nested() {
572
1
        let input = "((((((1))))))";
573
1
        let mut expr = num(1);
574
6
        for _ in 0..6 {
575
6
            expr = list(vec![expr]);
576
6
        }
577
1
        assert_eq!(parse_one(input), expr);
578
1
    }
579

            
580
    #[test]
581
1
    fn test_long_symbol() {
582
1
        let input = "a".repeat(1000);
583
1
        let expected = "A".repeat(1000);
584
1
        assert_eq!(parse_one(&input), sym(&expected));
585
1
    }
586

            
587
    #[test]
588
1
    fn test_many_list_elements() {
589
1
        let input = format!(
590
            "({})",
591
1
            (1..=100)
592
100
                .map(|n| n.to_string())
593
1
                .collect::<Vec<_>>()
594
1
                .join(" ")
595
        );
596
1
        let program = parse(&input);
597
1
        assert_eq!(program.exprs.len(), 1);
598
1
        if let Expr::List(items) = &program.exprs[0] {
599
1
            assert_eq!(items.len(), 100);
600
        } else {
601
            panic!("expected list");
602
        }
603
1
    }
604
}
605

            
606
/// ADR-0029 colon-namespace grammar, plus the cross-crate regression guard
607
/// that the rpc envelope + transaction-plist shapes (which parse through this
608
/// same `Reader`) are unaffected by the grammar tightening.
609
mod namespaces {
610
    use super::*;
611

            
612
    #[test]
613
1
    fn qualified_symbol_single_and_double_colon() {
614
1
        assert_eq!(parse_one("finance:add-money"), sym("FINANCE:ADD-MONEY"));
615
        // `::` folds to the canonical single-colon key.
616
1
        assert_eq!(parse_one("finance::add-money"), sym("FINANCE:ADD-MONEY"));
617
1
    }
618

            
619
    #[test]
620
1
    fn qualified_symbol_in_call_position() {
621
1
        assert_eq!(
622
1
            parse_one("(split:list-for-transaction tx)"),
623
1
            list(vec![sym("SPLIT:LIST-FOR-TRANSACTION"), sym("TX")])
624
        );
625
1
    }
626

            
627
    #[test]
628
1
    fn leading_colon_remains_keyword() {
629
1
        assert_eq!(parse_one(":foo"), Expr::Keyword("FOO".into()));
630
1
    }
631

            
632
    #[test]
633
1
    fn malformed_qualified_symbols_rejected() {
634
7
        for bad in ["foo:", "foo::", "a:b:c", "a::b::c", "a:b::c", "::foo", ":"] {
635
7
            assert!(
636
7
                Reader::parse(bad).is_err(),
637
                "expected parse error for {bad:?}"
638
            );
639
        }
640
1
    }
641

            
642
    // Cross-crate guard: the rpc envelope (`(:id N :form …)`) and the
643
    // create-transaction plist payload (`(:post-date … :splits (…))`) ride
644
    // this same reader. The grammar change touches only symbol/leading-colon
645
    // parsing, so well-formed `:key value` plists must parse byte-unchanged.
646

            
647
    #[test]
648
1
    fn rpc_envelope_frame_parses_unchanged() {
649
1
        let frame = parse_one("(:id 42 :form (list-accounts))");
650
1
        assert_eq!(
651
            frame,
652
1
            list(vec![
653
1
                Expr::Keyword("ID".into()),
654
1
                num(42),
655
1
                Expr::Keyword("FORM".into()),
656
1
                list(vec![sym("LIST-ACCOUNTS")]),
657
            ])
658
        );
659
1
    }
660

            
661
    #[test]
662
1
    fn transaction_plist_payload_parses_unchanged() {
663
1
        let payload = parse_one(
664
1
            "(:post-date \"2026-02-01T00:00:00Z\" \
665
1
             :splits ((:account-id \"a\" :value -100) (:account-id \"b\" :value 100)))",
666
        );
667
1
        let Expr::List(items) = payload else {
668
            panic!("expected plist");
669
        };
670
1
        assert_eq!(items[0], Expr::Keyword("POST-DATE".into()));
671
1
        assert_eq!(items[1], Expr::String("2026-02-01T00:00:00Z".into()));
672
1
        assert_eq!(items[2], Expr::Keyword("SPLITS".into()));
673
1
    }
674
}