dazzle_core/scheme/
primitives.rs

1//! Scheme primitive procedures
2//!
3//! Corresponds to OpenJade's `primitive.cxx` (~5,700 lines).
4//!
5//! ## Organization
6//!
7//! Primitives are organized by category:
8//! - **List operations**: car, cdr, cons, list, append, length, etc.
9//! - **Arithmetic**: +, -, *, /, <, >, =, etc.
10//! - **String operations**: string-append, string-length, etc.
11//! - **Type predicates**: pair?, list?, number?, string?, etc.
12//! - **DSSSL-specific**: grove queries, processing functions
13//!
14//! ## OpenJade Correspondence
15//!
16//! | Category          | OpenJade Count | Dazzle Status |
17//! |-------------------|----------------|---------------|
18//! | R4RS standard     | ~90            | In progress   |
19//! | DSSSL grove       | ~50            | TODO          |
20//! | DSSSL processing  | ~20            | TODO          |
21//! | DSSSL types       | ~30            | Stubs         |
22//! | Extensions        | ~20            | TODO          |
23//! | **Total**         | **~236**       |               |
24
25use crate::scheme::value::Value;
26use crate::grove::{EmptyNodeList, Node};
27
28/// Result type for primitive procedures
29pub type PrimitiveResult = Result<Value, String>;
30
31// =============================================================================
32// List Primitives (R4RS)
33// =============================================================================
34
35/// (car pair) → value
36///
37/// Returns the car (first element) of a pair.
38///
39/// **R4RS**: Required procedure
40pub fn prim_car(args: &[Value]) -> PrimitiveResult {
41    if args.len() != 1 {
42        return Err("car requires exactly 1 argument".to_string());
43    }
44
45    if let Value::Pair(ref p) = args[0] {
46        let pair = p.borrow();
47        Ok(pair.car.clone())
48    } else {
49        Err(format!("car: not a pair: {:?}", args[0]))
50    }
51}
52
53/// (cdr pair) → value
54///
55/// Returns the cdr (rest) of a pair.
56///
57/// **R4RS**: Required procedure
58pub fn prim_cdr(args: &[Value]) -> PrimitiveResult {
59    if args.len() != 1 {
60        return Err("cdr requires exactly 1 argument".to_string());
61    }
62
63    if let Value::Pair(ref p) = args[0] {
64        let pair = p.borrow();
65        Ok(pair.cdr.clone())
66    } else {
67        Err(format!("cdr: not a pair: {:?}", args[0]))
68    }
69}
70
71/// (cons obj1 obj2) → pair
72///
73/// Returns a newly allocated pair whose car is obj1 and cdr is obj2.
74///
75/// **R4RS**: Required procedure
76pub fn prim_cons(args: &[Value]) -> PrimitiveResult {
77    if args.len() != 2 {
78        return Err("cons requires exactly 2 arguments".to_string());
79    }
80
81    Ok(Value::cons(args[0].clone(), args[1].clone()))
82}
83
84/// (list obj ...) → list
85///
86/// Returns a newly allocated list of its arguments.
87///
88/// **R4RS**: Required procedure
89pub fn prim_list(args: &[Value]) -> PrimitiveResult {
90    let mut result = Value::Nil;
91    for arg in args.iter().rev() {
92        result = Value::cons(arg.clone(), result);
93    }
94    Ok(result)
95}
96
97/// (null? obj) → boolean
98///
99/// Returns #t if obj is the empty list, otherwise #f.
100///
101/// **R4RS**: Required procedure
102pub fn prim_null_p(args: &[Value]) -> PrimitiveResult {
103    if args.len() != 1 {
104        return Err("null? requires exactly 1 argument".to_string());
105    }
106
107    Ok(Value::bool(args[0].is_nil()))
108}
109
110/// (pair? obj) → boolean
111///
112/// Returns #t if obj is a pair, otherwise #f.
113///
114/// **R4RS**: Required procedure
115pub fn prim_pair_p(args: &[Value]) -> PrimitiveResult {
116    if args.len() != 1 {
117        return Err("pair? requires exactly 1 argument".to_string());
118    }
119
120    Ok(Value::bool(args[0].is_pair()))
121}
122
123/// (list? obj) → boolean
124///
125/// Returns #t if obj is a list (nil or pair), otherwise #f.
126///
127/// **R4RS**: Required procedure
128pub fn prim_list_p(args: &[Value]) -> PrimitiveResult {
129    if args.len() != 1 {
130        return Err("list? requires exactly 1 argument".to_string());
131    }
132
133    Ok(Value::bool(args[0].is_list()))
134}
135
136/// (length list) → integer
137///
138/// Returns the length of list.
139///
140/// **R4RS**: Required procedure
141pub fn prim_length(args: &[Value]) -> PrimitiveResult {
142    if args.len() != 1 {
143        return Err("length requires exactly 1 argument".to_string());
144    }
145
146    let mut len = 0;
147    let mut current = args[0].clone();
148
149    loop {
150        match current {
151            Value::Nil => break,
152            Value::Pair(ref p) => {
153                len += 1;
154                let pair = p.borrow();
155                let cdr = pair.cdr.clone();
156                drop(pair);
157                current = cdr;
158            }
159            _ => return Err("length: not a proper list".to_string()),
160        }
161    }
162
163    Ok(Value::integer(len))
164}
165
166/// (append list ...) → list
167///
168/// Returns a list consisting of the elements of the first list followed by
169/// the elements of the other lists.
170///
171/// **R4RS**: Required procedure
172pub fn prim_append(args: &[Value]) -> PrimitiveResult {
173    if args.is_empty() {
174        return Ok(Value::Nil);
175    }
176
177    if args.len() == 1 {
178        return Ok(args[0].clone());
179    }
180
181    // Convert all but last list to vectors
182    let mut all_elements = Vec::new();
183
184    for i in 0..args.len() - 1 {
185        let mut current = args[i].clone();
186        loop {
187            match current {
188                Value::Nil => break,
189                Value::Pair(ref p) => {
190                    let pair = p.borrow();
191                    all_elements.push(pair.car.clone());
192                    let cdr = pair.cdr.clone();
193                    drop(pair);
194                    current = cdr;
195                }
196                _ => return Err("append: not a proper list".to_string()),
197            }
198        }
199    }
200
201    // Build result list, ending with the last argument
202    let mut result = args[args.len() - 1].clone();
203    for elem in all_elements.iter().rev() {
204        result = Value::cons(elem.clone(), result);
205    }
206
207    Ok(result)
208}
209
210/// (reverse list) → list
211///
212/// Returns a newly allocated list consisting of the elements of list in reverse order.
213///
214/// **R4RS**: Required procedure
215pub fn prim_reverse(args: &[Value]) -> PrimitiveResult {
216    if args.len() != 1 {
217        return Err("reverse requires exactly 1 argument".to_string());
218    }
219
220    let mut result = Value::Nil;
221    let mut current = args[0].clone();
222
223    loop {
224        match current {
225            Value::Nil => break,
226            Value::Pair(ref p) => {
227                let pair = p.borrow();
228                result = Value::cons(pair.car.clone(), result);
229                let cdr = pair.cdr.clone();
230                drop(pair);
231                current = cdr;
232            }
233            _ => return Err("reverse: not a proper list".to_string()),
234        }
235    }
236
237    Ok(result)
238}
239
240/// (list-tail list k) → value
241///
242/// Returns the sublist of list obtained by omitting the first k elements.
243///
244/// **R4RS**: Required procedure
245pub fn prim_list_tail(args: &[Value]) -> PrimitiveResult {
246    if args.len() != 2 {
247        return Err("list-tail requires exactly 2 arguments".to_string());
248    }
249
250    let k = match args[1] {
251        Value::Integer(n) if n >= 0 => n as usize,
252        _ => return Err("list-tail: second argument must be a non-negative integer".to_string()),
253    };
254
255    let mut current = args[0].clone();
256    for _ in 0..k {
257        match current {
258            Value::Pair(ref p) => {
259                let pair = p.borrow();
260                let cdr = pair.cdr.clone();
261                drop(pair);
262                current = cdr;
263            }
264            _ => return Err("list-tail: list too short".to_string()),
265        }
266    }
267
268    Ok(current)
269}
270
271/// (list-ref list k) → value
272///
273/// Returns the kth element of list (zero-indexed).
274///
275/// **R4RS**: Required procedure
276pub fn prim_list_ref(args: &[Value]) -> PrimitiveResult {
277    if args.len() != 2 {
278        return Err("list-ref requires exactly 2 arguments".to_string());
279    }
280
281    let k = match args[1] {
282        Value::Integer(n) if n >= 0 => n as usize,
283        _ => return Err("list-ref: second argument must be a non-negative integer".to_string()),
284    };
285
286    let mut current = args[0].clone();
287    for _ in 0..k {
288        match current {
289            Value::Pair(ref p) => {
290                let pair = p.borrow();
291                let cdr = pair.cdr.clone();
292                drop(pair);
293                current = cdr;
294            }
295            _ => return Err("list-ref: list too short".to_string()),
296        }
297    }
298
299    match current {
300        Value::Pair(ref p) => {
301            let pair = p.borrow();
302            Ok(pair.car.clone())
303        }
304        _ => Err("list-ref: index out of bounds".to_string()),
305    }
306}
307
308// =============================================================================
309// Number Primitives (R4RS)
310// =============================================================================
311
312/// (+ number ...) → number
313///
314/// Returns the sum of its arguments.
315///
316/// **R4RS**: Required procedure
317pub fn prim_add(args: &[Value]) -> PrimitiveResult {
318    let mut int_sum = 0i64;
319    let mut real_sum = 0.0f64;
320    let mut has_real = false;
321
322    for arg in args {
323        match arg {
324            Value::Integer(n) => {
325                int_sum += n;
326                real_sum += *n as f64;
327            }
328            Value::Real(r) => {
329                has_real = true;
330                real_sum += r;
331            }
332            _ => return Err(format!("+: not a number: {:?}", arg)),
333        }
334    }
335
336    if has_real {
337        Ok(Value::real(real_sum))
338    } else {
339        Ok(Value::integer(int_sum))
340    }
341}
342
343/// (- number number* ...) → number
344///
345/// With one argument, returns the negation. With multiple arguments, returns
346/// the first argument minus the sum of the remaining arguments.
347///
348/// **R4RS**: Required procedure
349pub fn prim_subtract(args: &[Value]) -> PrimitiveResult {
350    if args.is_empty() {
351        return Err("-: requires at least 1 argument".to_string());
352    }
353
354    if args.len() == 1 {
355        // Negation
356        match args[0] {
357            Value::Integer(n) => Ok(Value::integer(-n)),
358            Value::Real(r) => Ok(Value::real(-r)),
359            _ => Err(format!("-: not a number: {:?}", args[0])),
360        }
361    } else {
362        // Subtraction
363        let mut result_int: i64;
364        let mut result_real: f64;
365        let mut has_real = false;
366
367        // First argument
368        match args[0] {
369            Value::Integer(n) => {
370                result_int = n;
371                result_real = n as f64;
372            }
373            Value::Real(r) => {
374                has_real = true;
375                result_int = 0; // dummy value
376                result_real = r;
377            }
378            _ => return Err(format!("-: not a number: {:?}", args[0])),
379        }
380
381        // Subtract remaining arguments
382        for arg in &args[1..] {
383            match arg {
384                Value::Integer(n) => {
385                    result_int -= n;
386                    result_real -= *n as f64;
387                }
388                Value::Real(r) => {
389                    has_real = true;
390                    result_real -= r;
391                }
392                _ => return Err(format!("-: not a number: {:?}", arg)),
393            }
394        }
395
396        if has_real {
397            Ok(Value::real(result_real))
398        } else {
399            Ok(Value::integer(result_int))
400        }
401    }
402}
403
404/// (* number ...) → number
405///
406/// Returns the product of its arguments.
407///
408/// **R4RS**: Required procedure
409pub fn prim_multiply(args: &[Value]) -> PrimitiveResult {
410    let mut int_product = 1i64;
411    let mut real_product = 1.0f64;
412    let mut has_real = false;
413
414    for arg in args {
415        match arg {
416            Value::Integer(n) => {
417                int_product *= n;
418                real_product *= *n as f64;
419            }
420            Value::Real(r) => {
421                has_real = true;
422                real_product *= r;
423            }
424            _ => return Err(format!("*: not a number: {:?}", arg)),
425        }
426    }
427
428    if has_real {
429        Ok(Value::real(real_product))
430    } else {
431        Ok(Value::integer(int_product))
432    }
433}
434
435/// (/ number number* ...) → number
436///
437/// With one argument, returns the reciprocal. With multiple arguments, returns
438/// the first argument divided by the product of the remaining arguments.
439///
440/// **R4RS**: Required procedure
441pub fn prim_divide(args: &[Value]) -> PrimitiveResult {
442    if args.is_empty() {
443        return Err("/: requires at least 1 argument".to_string());
444    }
445
446    if args.len() == 1 {
447        // Reciprocal
448        match args[0] {
449            Value::Integer(n) => {
450                if n == 0 {
451                    return Err("/: division by zero".to_string());
452                }
453                Ok(Value::real(1.0 / n as f64))
454            }
455            Value::Real(r) => {
456                if r == 0.0 {
457                    return Err("/: division by zero".to_string());
458                }
459                Ok(Value::real(1.0 / r))
460            }
461            _ => Err(format!("/: not a number: {:?}", args[0])),
462        }
463    } else {
464        // Division - always returns real
465        let mut result = match args[0] {
466            Value::Integer(n) => n as f64,
467            Value::Real(r) => r,
468            _ => return Err(format!("/: not a number: {:?}", args[0])),
469        };
470
471        for arg in &args[1..] {
472            match arg {
473                Value::Integer(n) => {
474                    if *n == 0 {
475                        return Err("/: division by zero".to_string());
476                    }
477                    result /= *n as f64;
478                }
479                Value::Real(r) => {
480                    if *r == 0.0 {
481                        return Err("/: division by zero".to_string());
482                    }
483                    result /= r;
484                }
485                _ => return Err(format!("/: not a number: {:?}", arg)),
486            }
487        }
488
489        Ok(Value::real(result))
490    }
491}
492
493/// (quotient n1 n2) → integer
494///
495/// Returns the quotient of n1 and n2 (integer division).
496///
497/// **R4RS**: Required procedure
498pub fn prim_quotient(args: &[Value]) -> PrimitiveResult {
499    if args.len() != 2 {
500        return Err("quotient requires exactly 2 arguments".to_string());
501    }
502
503    let n1 = match args[0] {
504        Value::Integer(n) => n,
505        _ => return Err(format!("quotient: not an integer: {:?}", args[0])),
506    };
507
508    let n2 = match args[1] {
509        Value::Integer(n) => n,
510        _ => return Err(format!("quotient: not an integer: {:?}", args[1])),
511    };
512
513    if n2 == 0 {
514        return Err("quotient: division by zero".to_string());
515    }
516
517    Ok(Value::integer(n1 / n2))
518}
519
520/// (remainder n1 n2) → integer
521///
522/// Returns the remainder of n1 divided by n2.
523///
524/// **R4RS**: Required procedure
525pub fn prim_remainder(args: &[Value]) -> PrimitiveResult {
526    if args.len() != 2 {
527        return Err("remainder requires exactly 2 arguments".to_string());
528    }
529
530    let n1 = match args[0] {
531        Value::Integer(n) => n,
532        _ => return Err(format!("remainder: not an integer: {:?}", args[0])),
533    };
534
535    let n2 = match args[1] {
536        Value::Integer(n) => n,
537        _ => return Err(format!("remainder: not an integer: {:?}", args[1])),
538    };
539
540    if n2 == 0 {
541        return Err("remainder: division by zero".to_string());
542    }
543
544    Ok(Value::integer(n1 % n2))
545}
546
547/// (modulo n1 n2) → integer
548///
549/// Returns n1 modulo n2.
550///
551/// **R4RS**: Required procedure
552pub fn prim_modulo(args: &[Value]) -> PrimitiveResult {
553    if args.len() != 2 {
554        return Err("modulo requires exactly 2 arguments".to_string());
555    }
556
557    let n1 = match args[0] {
558        Value::Integer(n) => n,
559        _ => return Err(format!("modulo: not an integer: {:?}", args[0])),
560    };
561
562    let n2 = match args[1] {
563        Value::Integer(n) => n,
564        _ => return Err(format!("modulo: not an integer: {:?}", args[1])),
565    };
566
567    if n2 == 0 {
568        return Err("modulo: division by zero".to_string());
569    }
570
571    // Euclidean modulo (always non-negative result)
572    let result = ((n1 % n2) + n2) % n2;
573    Ok(Value::integer(result))
574}
575
576/// (= number1 number2 number3 ...) → boolean
577///
578/// Returns #t if all arguments are numerically equal, otherwise #f.
579///
580/// **R4RS**: Required procedure
581pub fn prim_num_eq(args: &[Value]) -> PrimitiveResult {
582    if args.len() < 2 {
583        return Err("=: requires at least 2 arguments".to_string());
584    }
585
586    let first_val = match args[0] {
587        Value::Integer(n) => n as f64,
588        Value::Real(r) => r,
589        _ => return Err(format!("=: not a number: {:?}", args[0])),
590    };
591
592    for arg in &args[1..] {
593        let val = match arg {
594            Value::Integer(n) => *n as f64,
595            Value::Real(r) => *r,
596            _ => return Err(format!("=: not a number: {:?}", arg)),
597        };
598
599        if (first_val - val).abs() > f64::EPSILON {
600            return Ok(Value::bool(false));
601        }
602    }
603
604    Ok(Value::bool(true))
605}
606
607/// (< number1 number2 number3 ...) → boolean
608///
609/// Returns #t if arguments are in strictly increasing order, otherwise #f.
610///
611/// **R4RS**: Required procedure
612pub fn prim_num_lt(args: &[Value]) -> PrimitiveResult {
613    if args.len() < 2 {
614        return Err("<: requires at least 2 arguments".to_string());
615    }
616
617    for i in 0..args.len() - 1 {
618        let v1 = match args[i] {
619            Value::Integer(n) => n as f64,
620            Value::Real(r) => r,
621            _ => return Err(format!("<: not a number: {:?}", args[i])),
622        };
623
624        let v2 = match args[i + 1] {
625            Value::Integer(n) => n as f64,
626            Value::Real(r) => r,
627            _ => return Err(format!("<: not a number: {:?}", args[i + 1])),
628        };
629
630        if v1 >= v2 {
631            return Ok(Value::bool(false));
632        }
633    }
634
635    Ok(Value::bool(true))
636}
637
638/// (> number1 number2 number3 ...) → boolean
639///
640/// Returns #t if arguments are in strictly decreasing order, otherwise #f.
641///
642/// **R4RS**: Required procedure
643pub fn prim_num_gt(args: &[Value]) -> PrimitiveResult {
644    if args.len() < 2 {
645        return Err(">: requires at least 2 arguments".to_string());
646    }
647
648    for i in 0..args.len() - 1 {
649        let v1 = match args[i] {
650            Value::Integer(n) => n as f64,
651            Value::Real(r) => r,
652            _ => return Err(format!(">: not a number: {:?}", args[i])),
653        };
654
655        let v2 = match args[i + 1] {
656            Value::Integer(n) => n as f64,
657            Value::Real(r) => r,
658            _ => return Err(format!(">: not a number: {:?}", args[i + 1])),
659        };
660
661        if v1 <= v2 {
662            return Ok(Value::bool(false));
663        }
664    }
665
666    Ok(Value::bool(true))
667}
668
669/// (<= number1 number2 number3 ...) → boolean
670///
671/// Returns #t if arguments are in non-decreasing order, otherwise #f.
672///
673/// **R4RS**: Required procedure
674pub fn prim_num_le(args: &[Value]) -> PrimitiveResult {
675    if args.len() < 2 {
676        return Err("<=: requires at least 2 arguments".to_string());
677    }
678
679    for i in 0..args.len() - 1 {
680        let v1 = match args[i] {
681            Value::Integer(n) => n as f64,
682            Value::Real(r) => r,
683            _ => return Err(format!("<=: not a number: {:?}", args[i])),
684        };
685
686        let v2 = match args[i + 1] {
687            Value::Integer(n) => n as f64,
688            Value::Real(r) => r,
689            _ => return Err(format!("<=: not a number: {:?}", args[i + 1])),
690        };
691
692        if v1 > v2 {
693            return Ok(Value::bool(false));
694        }
695    }
696
697    Ok(Value::bool(true))
698}
699
700/// (>= number1 number2 number3 ...) → boolean
701///
702/// Returns #t if arguments are in non-increasing order, otherwise #f.
703///
704/// **R4RS**: Required procedure
705pub fn prim_num_ge(args: &[Value]) -> PrimitiveResult {
706    if args.len() < 2 {
707        return Err(">=: requires at least 2 arguments".to_string());
708    }
709
710    for i in 0..args.len() - 1 {
711        let v1 = match args[i] {
712            Value::Integer(n) => n as f64,
713            Value::Real(r) => r,
714            _ => return Err(format!(">=: not a number: {:?}", args[i])),
715        };
716
717        let v2 = match args[i + 1] {
718            Value::Integer(n) => n as f64,
719            Value::Real(r) => r,
720            _ => return Err(format!(">=: not a number: {:?}", args[i + 1])),
721        };
722
723        if v1 < v2 {
724            return Ok(Value::bool(false));
725        }
726    }
727
728    Ok(Value::bool(true))
729}
730
731/// (number? obj) → boolean
732///
733/// Returns #t if obj is a number, otherwise #f.
734///
735/// **R4RS**: Required procedure
736pub fn prim_number_p(args: &[Value]) -> PrimitiveResult {
737    if args.len() != 1 {
738        return Err("number?: requires exactly 1 argument".to_string());
739    }
740
741    Ok(Value::bool(matches!(args[0], Value::Integer(_) | Value::Real(_))))
742}
743
744/// (integer? obj) → boolean
745///
746/// Returns #t if obj is an integer, otherwise #f.
747///
748/// **R4RS**: Required procedure
749pub fn prim_integer_p(args: &[Value]) -> PrimitiveResult {
750    if args.len() != 1 {
751        return Err("integer?: requires exactly 1 argument".to_string());
752    }
753
754    Ok(Value::bool(matches!(args[0], Value::Integer(_))))
755}
756
757/// (real? obj) → boolean
758///
759/// Returns #t if obj is a real number, otherwise #f.
760///
761/// **R4RS**: Required procedure
762pub fn prim_real_p(args: &[Value]) -> PrimitiveResult {
763    if args.len() != 1 {
764        return Err("real?: requires exactly 1 argument".to_string());
765    }
766
767    Ok(Value::bool(matches!(args[0], Value::Real(_))))
768}
769
770/// (zero? number) → boolean
771///
772/// Returns #t if number is zero, otherwise #f.
773///
774/// **R4RS**: Required procedure
775pub fn prim_zero_p(args: &[Value]) -> PrimitiveResult {
776    if args.len() != 1 {
777        return Err("zero?: requires exactly 1 argument".to_string());
778    }
779
780    match args[0] {
781        Value::Integer(n) => Ok(Value::bool(n == 0)),
782        Value::Real(r) => Ok(Value::bool(r.abs() < f64::EPSILON)),
783        _ => Err(format!("zero?: not a number: {:?}", args[0])),
784    }
785}
786
787/// (positive? number) → boolean
788///
789/// Returns #t if number is positive, otherwise #f.
790///
791/// **R4RS**: Required procedure
792pub fn prim_positive_p(args: &[Value]) -> PrimitiveResult {
793    if args.len() != 1 {
794        return Err("positive?: requires exactly 1 argument".to_string());
795    }
796
797    match args[0] {
798        Value::Integer(n) => Ok(Value::bool(n > 0)),
799        Value::Real(r) => Ok(Value::bool(r > 0.0)),
800        _ => Err(format!("positive?: not a number: {:?}", args[0])),
801    }
802}
803
804/// (negative? number) → boolean
805///
806/// Returns #t if number is negative, otherwise #f.
807///
808/// **R4RS**: Required procedure
809pub fn prim_negative_p(args: &[Value]) -> PrimitiveResult {
810    if args.len() != 1 {
811        return Err("negative?: requires exactly 1 argument".to_string());
812    }
813
814    match args[0] {
815        Value::Integer(n) => Ok(Value::bool(n < 0)),
816        Value::Real(r) => Ok(Value::bool(r < 0.0)),
817        _ => Err(format!("negative?: not a number: {:?}", args[0])),
818    }
819}
820
821/// (odd? integer) → boolean
822///
823/// Returns #t if integer is odd, otherwise #f.
824///
825/// **R4RS**: Required procedure
826pub fn prim_odd_p(args: &[Value]) -> PrimitiveResult {
827    if args.len() != 1 {
828        return Err("odd?: requires exactly 1 argument".to_string());
829    }
830
831    match args[0] {
832        Value::Integer(n) => Ok(Value::bool(n % 2 != 0)),
833        _ => Err(format!("odd?: not an integer: {:?}", args[0])),
834    }
835}
836
837/// (even? integer) → boolean
838///
839/// Returns #t if integer is even, otherwise #f.
840///
841/// **R4RS**: Required procedure
842pub fn prim_even_p(args: &[Value]) -> PrimitiveResult {
843    if args.len() != 1 {
844        return Err("even?: requires exactly 1 argument".to_string());
845    }
846
847    match args[0] {
848        Value::Integer(n) => Ok(Value::bool(n % 2 == 0)),
849        _ => Err(format!("even?: not an integer: {:?}", args[0])),
850    }
851}
852
853/// (abs number) → number
854///
855/// Returns the absolute value of number.
856///
857/// **R4RS**: Required procedure
858pub fn prim_abs(args: &[Value]) -> PrimitiveResult {
859    if args.len() != 1 {
860        return Err("abs: requires exactly 1 argument".to_string());
861    }
862
863    match args[0] {
864        Value::Integer(n) => Ok(Value::integer(n.abs())),
865        Value::Real(r) => Ok(Value::real(r.abs())),
866        _ => Err(format!("abs: not a number: {:?}", args[0])),
867    }
868}
869
870/// (max number number* ...) → number
871///
872/// Returns the maximum of its arguments.
873///
874/// **R4RS**: Required procedure
875pub fn prim_max(args: &[Value]) -> PrimitiveResult {
876    if args.is_empty() {
877        return Err("max: requires at least 1 argument".to_string());
878    }
879
880    let mut max_val = match args[0] {
881        Value::Integer(n) => n as f64,
882        Value::Real(r) => r,
883        _ => return Err(format!("max: not a number: {:?}", args[0])),
884    };
885
886    let mut has_real = matches!(args[0], Value::Real(_));
887
888    for arg in &args[1..] {
889        let val = match arg {
890            Value::Integer(n) => *n as f64,
891            Value::Real(r) => {
892                has_real = true;
893                *r
894            }
895            _ => return Err(format!("max: not a number: {:?}", arg)),
896        };
897
898        if val > max_val {
899            max_val = val;
900        }
901    }
902
903    if has_real {
904        Ok(Value::real(max_val))
905    } else {
906        Ok(Value::integer(max_val as i64))
907    }
908}
909
910/// (min number number* ...) → number
911///
912/// Returns the minimum of its arguments.
913///
914/// **R4RS**: Required procedure
915pub fn prim_min(args: &[Value]) -> PrimitiveResult {
916    if args.is_empty() {
917        return Err("min: requires at least 1 argument".to_string());
918    }
919
920    let mut min_val = match args[0] {
921        Value::Integer(n) => n as f64,
922        Value::Real(r) => r,
923        _ => return Err(format!("min: not a number: {:?}", args[0])),
924    };
925
926    let mut has_real = matches!(args[0], Value::Real(_));
927
928    for arg in &args[1..] {
929        let val = match arg {
930            Value::Integer(n) => *n as f64,
931            Value::Real(r) => {
932                has_real = true;
933                *r
934            }
935            _ => return Err(format!("min: not a number: {:?}", arg)),
936        };
937
938        if val < min_val {
939            min_val = val;
940        }
941    }
942
943    if has_real {
944        Ok(Value::real(min_val))
945    } else {
946        Ok(Value::integer(min_val as i64))
947    }
948}
949
950/// (gcd n1 n2 ...) → integer
951///
952/// Returns the greatest common divisor of its arguments.
953/// If called with no arguments, returns 0.
954///
955/// **R4RS**: Required procedure
956pub fn prim_gcd(args: &[Value]) -> PrimitiveResult {
957    if args.is_empty() {
958        return Ok(Value::integer(0));
959    }
960
961    // Helper function for Euclidean algorithm
962    fn gcd_two(a: i64, b: i64) -> i64 {
963        let mut a = a.abs();
964        let mut b = b.abs();
965        while b != 0 {
966            let temp = b;
967            b = a % b;
968            a = temp;
969        }
970        a
971    }
972
973    let mut result = match args[0] {
974        Value::Integer(n) => n,
975        _ => return Err(format!("gcd: not an integer: {:?}", args[0])),
976    };
977
978    for arg in &args[1..] {
979        let n = match arg {
980            Value::Integer(n) => *n,
981            _ => return Err(format!("gcd: not an integer: {:?}", arg)),
982        };
983        result = gcd_two(result, n);
984    }
985
986    Ok(Value::integer(result))
987}
988
989/// (lcm n1 n2 ...) → integer
990///
991/// Returns the least common multiple of its arguments.
992/// If called with no arguments, returns 1.
993///
994/// **R4RS**: Required procedure
995pub fn prim_lcm(args: &[Value]) -> PrimitiveResult {
996    if args.is_empty() {
997        return Ok(Value::integer(1));
998    }
999
1000    // Helper function for gcd (Euclidean algorithm)
1001    fn gcd_two(a: i64, b: i64) -> i64 {
1002        let mut a = a.abs();
1003        let mut b = b.abs();
1004        while b != 0 {
1005            let temp = b;
1006            b = a % b;
1007            a = temp;
1008        }
1009        a
1010    }
1011
1012    // Helper function for lcm of two numbers
1013    fn lcm_two(a: i64, b: i64) -> i64 {
1014        if a == 0 || b == 0 {
1015            return 0;
1016        }
1017        (a.abs() / gcd_two(a, b)) * b.abs()
1018    }
1019
1020    let mut result = match args[0] {
1021        Value::Integer(n) => n,
1022        _ => return Err(format!("lcm: not an integer: {:?}", args[0])),
1023    };
1024
1025    for arg in &args[1..] {
1026        let n = match arg {
1027            Value::Integer(n) => *n,
1028            _ => return Err(format!("lcm: not an integer: {:?}", arg)),
1029        };
1030        result = lcm_two(result, n);
1031    }
1032
1033    Ok(Value::integer(result))
1034}
1035
1036/// (floor number) → integer
1037///
1038/// Returns the largest integer not greater than number.
1039///
1040/// **R4RS**: Required procedure
1041pub fn prim_floor(args: &[Value]) -> PrimitiveResult {
1042    if args.len() != 1 {
1043        return Err("floor: requires exactly 1 argument".to_string());
1044    }
1045
1046    match args[0] {
1047        Value::Integer(n) => Ok(Value::integer(n)),
1048        Value::Real(r) => Ok(Value::integer(r.floor() as i64)),
1049        _ => Err(format!("floor: not a number: {:?}", args[0])),
1050    }
1051}
1052
1053/// (ceiling number) → integer
1054///
1055/// Returns the smallest integer not less than number.
1056///
1057/// **R4RS**: Required procedure
1058pub fn prim_ceiling(args: &[Value]) -> PrimitiveResult {
1059    if args.len() != 1 {
1060        return Err("ceiling: requires exactly 1 argument".to_string());
1061    }
1062
1063    match args[0] {
1064        Value::Integer(n) => Ok(Value::integer(n)),
1065        Value::Real(r) => Ok(Value::integer(r.ceil() as i64)),
1066        _ => Err(format!("ceiling: not a number: {:?}", args[0])),
1067    }
1068}
1069
1070/// (truncate number) → integer
1071///
1072/// Returns the integer closest to number whose absolute value is not greater
1073/// than the absolute value of number.
1074///
1075/// **R4RS**: Required procedure
1076pub fn prim_truncate(args: &[Value]) -> PrimitiveResult {
1077    if args.len() != 1 {
1078        return Err("truncate: requires exactly 1 argument".to_string());
1079    }
1080
1081    match args[0] {
1082        Value::Integer(n) => Ok(Value::integer(n)),
1083        Value::Real(r) => Ok(Value::integer(r.trunc() as i64)),
1084        _ => Err(format!("truncate: not a number: {:?}", args[0])),
1085    }
1086}
1087
1088/// (round number) → integer
1089///
1090/// Returns the closest integer to number, rounding to even when number is halfway
1091/// between two integers.
1092///
1093/// **R4RS**: Required procedure
1094pub fn prim_round(args: &[Value]) -> PrimitiveResult {
1095    if args.len() != 1 {
1096        return Err("round: requires exactly 1 argument".to_string());
1097    }
1098
1099    match args[0] {
1100        Value::Integer(n) => Ok(Value::integer(n)),
1101        Value::Real(r) => Ok(Value::integer(r.round() as i64)),
1102        _ => Err(format!("round: not a number: {:?}", args[0])),
1103    }
1104}
1105
1106// =============================================================================
1107// String Primitives (R4RS)
1108// =============================================================================
1109
1110/// (string-length string) → integer
1111///
1112/// Returns the number of characters in string.
1113///
1114/// **R4RS**: Required procedure
1115pub fn prim_string_length(args: &[Value]) -> PrimitiveResult {
1116    if args.len() != 1 {
1117        return Err("string-length requires exactly 1 argument".to_string());
1118    }
1119
1120    match &args[0] {
1121        Value::String(s) => Ok(Value::integer(s.chars().count() as i64)),
1122        Value::Bool(false) => {
1123            // Treat #f as empty string (graceful handling)
1124            Ok(Value::integer(0))
1125        }
1126        Value::Unspecified => {
1127            // Treat unspecified as empty string (graceful handling)
1128            // This happens when case expressions have no matching clause
1129            Ok(Value::integer(0))
1130        }
1131        _ => Err(format!("string-length: not a string: {:?}", args[0])),
1132    }
1133}
1134
1135/// (string-ref string k) → char
1136///
1137/// Returns the kth character of string (zero-indexed).
1138///
1139/// **R4RS**: Required procedure
1140pub fn prim_string_ref(args: &[Value]) -> PrimitiveResult {
1141    if args.len() != 2 {
1142        return Err("string-ref requires exactly 2 arguments".to_string());
1143    }
1144
1145    let s = match &args[0] {
1146        Value::String(s) => s,
1147        _ => return Err(format!("string-ref: not a string: {:?}", args[0])),
1148    };
1149
1150    let k = match args[1] {
1151        Value::Integer(n) if n >= 0 => n as usize,
1152        _ => return Err("string-ref: second argument must be a non-negative integer".to_string()),
1153    };
1154
1155    let chars: Vec<char> = s.chars().collect();
1156    if k >= chars.len() {
1157        return Err("string-ref: index out of bounds".to_string());
1158    }
1159
1160    Ok(Value::char(chars[k]))
1161}
1162
1163/// (string-append string ...) → string
1164///
1165/// Returns a newly allocated string consisting of the concatenation of all arguments.
1166///
1167/// **R4RS**: Required procedure
1168pub fn prim_string_append(args: &[Value]) -> PrimitiveResult {
1169    let mut result = String::new();
1170
1171    for arg in args {
1172        match arg {
1173            Value::String(s) => result.push_str(s),
1174            Value::Unspecified => {
1175                // Treat unspecified as empty string (graceful handling)
1176                // This happens when template functions don't explicitly return values
1177            }
1178            Value::Bool(false) => {
1179                // Treat #f as empty string (graceful handling)
1180                // Common pattern in templates where #f indicates "no value"
1181            }
1182            _ => return Err(format!("string-append: not a string: {:?}", arg)),
1183        }
1184    }
1185
1186    Ok(Value::string(result))
1187}
1188
1189/// (substring string start end) → string
1190///
1191/// Returns a newly allocated string formed from the characters of string
1192/// beginning with index start (inclusive) and ending with index end (exclusive).
1193///
1194/// **R4RS**: Required procedure
1195pub fn prim_substring(args: &[Value]) -> PrimitiveResult {
1196    if args.len() != 3 {
1197        return Err("substring requires exactly 3 arguments".to_string());
1198    }
1199
1200    let s = match &args[0] {
1201        Value::String(s) => s,
1202        Value::Bool(false) => {
1203            return Err("substring: first argument is #f (expected string)".to_string());
1204        }
1205        Value::Unspecified => {
1206            // Treat unspecified as empty string (graceful handling)
1207            return Ok(Value::string(String::new()));
1208        }
1209        _ => return Err(format!("substring: not a string: {:?}", args[0])),
1210    };
1211
1212    let start = match args[1] {
1213        Value::Integer(n) if n >= 0 => n as usize,
1214        _ => return Err("substring: start must be a non-negative integer".to_string()),
1215    };
1216
1217    let end = match args[2] {
1218        Value::Integer(n) if n >= 0 => n as usize,
1219        _ => return Err("substring: end must be a non-negative integer".to_string()),
1220    };
1221
1222    let chars: Vec<char> = s.chars().collect();
1223    if start > end || end > chars.len() {
1224        return Err("substring: invalid range".to_string());
1225    }
1226
1227    let substring: String = chars[start..end].iter().collect();
1228    Ok(Value::string(substring))
1229}
1230
1231/// (string=? string1 string2) → boolean
1232///
1233/// Returns #t if the two strings are equal, otherwise #f.
1234///
1235/// **R4RS**: Required procedure
1236pub fn prim_string_eq(args: &[Value]) -> PrimitiveResult {
1237    if args.len() != 2 {
1238        return Err("string=? requires exactly 2 arguments".to_string());
1239    }
1240
1241    let s1 = match &args[0] {
1242        Value::String(s) => s,
1243        _ => return Err(format!("string=?: not a string: {:?}", args[0])),
1244    };
1245
1246    let s2 = match &args[1] {
1247        Value::String(s) => s,
1248        _ => return Err(format!("string=?: not a string: {:?}", args[1])),
1249    };
1250
1251    Ok(Value::bool(s1 == s2))
1252}
1253
1254/// (string<? string1 string2) → boolean
1255///
1256/// Returns #t if string1 is lexicographically less than string2, otherwise #f.
1257///
1258/// **R4RS**: Required procedure
1259pub fn prim_string_lt(args: &[Value]) -> PrimitiveResult {
1260    if args.len() != 2 {
1261        return Err("string<? requires exactly 2 arguments".to_string());
1262    }
1263
1264    let s1 = match &args[0] {
1265        Value::String(s) => s,
1266        _ => return Err(format!("string<?: not a string: {:?}", args[0])),
1267    };
1268
1269    let s2 = match &args[1] {
1270        Value::String(s) => s,
1271        _ => return Err(format!("string<?: not a string: {:?}", args[1])),
1272    };
1273
1274    Ok(Value::bool(s1 < s2))
1275}
1276
1277/// (string>? string1 string2) → boolean
1278///
1279/// Returns #t if string1 is lexicographically greater than string2, otherwise #f.
1280///
1281/// **R4RS**: Required procedure
1282pub fn prim_string_gt(args: &[Value]) -> PrimitiveResult {
1283    if args.len() != 2 {
1284        return Err("string>? requires exactly 2 arguments".to_string());
1285    }
1286
1287    let s1 = match &args[0] {
1288        Value::String(s) => s,
1289        _ => return Err(format!("string>?: not a string: {:?}", args[0])),
1290    };
1291
1292    let s2 = match &args[1] {
1293        Value::String(s) => s,
1294        _ => return Err(format!("string>?: not a string: {:?}", args[1])),
1295    };
1296
1297    Ok(Value::bool(s1 > s2))
1298}
1299
1300/// (string<=? string1 string2) → boolean
1301///
1302/// Returns #t if string1 is lexicographically less than or equal to string2, otherwise #f.
1303///
1304/// **R4RS**: Required procedure
1305pub fn prim_string_le(args: &[Value]) -> PrimitiveResult {
1306    if args.len() != 2 {
1307        return Err("string<=? requires exactly 2 arguments".to_string());
1308    }
1309
1310    let s1 = match &args[0] {
1311        Value::String(s) => s,
1312        _ => return Err(format!("string<=?: not a string: {:?}", args[0])),
1313    };
1314
1315    let s2 = match &args[1] {
1316        Value::String(s) => s,
1317        _ => return Err(format!("string<=?: not a string: {:?}", args[1])),
1318    };
1319
1320    Ok(Value::bool(s1 <= s2))
1321}
1322
1323/// (string>=? string1 string2) → boolean
1324///
1325/// Returns #t if string1 is lexicographically greater than or equal to string2, otherwise #f.
1326///
1327/// **R4RS**: Required procedure
1328pub fn prim_string_ge(args: &[Value]) -> PrimitiveResult {
1329    if args.len() != 2 {
1330        return Err("string>=? requires exactly 2 arguments".to_string());
1331    }
1332
1333    let s1 = match &args[0] {
1334        Value::String(s) => s,
1335        _ => return Err(format!("string>=?: not a string: {:?}", args[0])),
1336    };
1337
1338    let s2 = match &args[1] {
1339        Value::String(s) => s,
1340        _ => return Err(format!("string>=?: not a string: {:?}", args[1])),
1341    };
1342
1343    Ok(Value::bool(s1 >= s2))
1344}
1345
1346/// (string-ci=? string1 string2) → boolean
1347///
1348/// Returns #t if the two strings are equal ignoring case, otherwise #f.
1349///
1350/// **R4RS**: Required procedure
1351pub fn prim_string_ci_eq(args: &[Value]) -> PrimitiveResult {
1352    if args.len() != 2 {
1353        return Err("string-ci=? requires exactly 2 arguments".to_string());
1354    }
1355
1356    let s1 = match &args[0] {
1357        Value::String(s) => s.to_lowercase(),
1358        _ => return Err(format!("string-ci=?: not a string: {:?}", args[0])),
1359    };
1360
1361    let s2 = match &args[1] {
1362        Value::String(s) => s.to_lowercase(),
1363        _ => return Err(format!("string-ci=?: not a string: {:?}", args[1])),
1364    };
1365
1366    Ok(Value::bool(s1 == s2))
1367}
1368
1369/// (string-ci<? string1 string2) → boolean
1370///
1371/// Returns #t if string1 is lexicographically less than string2 (ignoring case), otherwise #f.
1372///
1373/// **R4RS**: Required procedure
1374pub fn prim_string_ci_lt(args: &[Value]) -> PrimitiveResult {
1375    if args.len() != 2 {
1376        return Err("string-ci<? requires exactly 2 arguments".to_string());
1377    }
1378
1379    let s1 = match &args[0] {
1380        Value::String(s) => s.to_lowercase(),
1381        _ => return Err(format!("string-ci<?: not a string: {:?}", args[0])),
1382    };
1383
1384    let s2 = match &args[1] {
1385        Value::String(s) => s.to_lowercase(),
1386        _ => return Err(format!("string-ci<?: not a string: {:?}", args[1])),
1387    };
1388
1389    Ok(Value::bool(s1 < s2))
1390}
1391
1392/// (string-ci>? string1 string2) → boolean
1393///
1394/// Returns #t if string1 is lexicographically greater than string2 (ignoring case), otherwise #f.
1395///
1396/// **R4RS**: Required procedure
1397pub fn prim_string_ci_gt(args: &[Value]) -> PrimitiveResult {
1398    if args.len() != 2 {
1399        return Err("string-ci>? requires exactly 2 arguments".to_string());
1400    }
1401
1402    let s1 = match &args[0] {
1403        Value::String(s) => s.to_lowercase(),
1404        _ => return Err(format!("string-ci>?: not a string: {:?}", args[0])),
1405    };
1406
1407    let s2 = match &args[1] {
1408        Value::String(s) => s.to_lowercase(),
1409        _ => return Err(format!("string-ci>?: not a string: {:?}", args[1])),
1410    };
1411
1412    Ok(Value::bool(s1 > s2))
1413}
1414
1415/// (string-ci<=? string1 string2) → boolean
1416///
1417/// Returns #t if string1 is lexicographically less than or equal to string2 (ignoring case), otherwise #f.
1418///
1419/// **R4RS**: Required procedure
1420pub fn prim_string_ci_le(args: &[Value]) -> PrimitiveResult {
1421    if args.len() != 2 {
1422        return Err("string-ci<=? requires exactly 2 arguments".to_string());
1423    }
1424
1425    let s1 = match &args[0] {
1426        Value::String(s) => s.to_lowercase(),
1427        _ => return Err(format!("string-ci<=?: not a string: {:?}", args[0])),
1428    };
1429
1430    let s2 = match &args[1] {
1431        Value::String(s) => s.to_lowercase(),
1432        _ => return Err(format!("string-ci<=?: not a string: {:?}", args[1])),
1433    };
1434
1435    Ok(Value::bool(s1 <= s2))
1436}
1437
1438/// (string-ci>=? string1 string2) → boolean
1439///
1440/// Returns #t if string1 is lexicographically greater than or equal to string2 (ignoring case), otherwise #f.
1441///
1442/// **R4RS**: Required procedure
1443pub fn prim_string_ci_ge(args: &[Value]) -> PrimitiveResult {
1444    if args.len() != 2 {
1445        return Err("string-ci>=? requires exactly 2 arguments".to_string());
1446    }
1447
1448    let s1 = match &args[0] {
1449        Value::String(s) => s.to_lowercase(),
1450        _ => return Err(format!("string-ci>=?: not a string: {:?}", args[0])),
1451    };
1452
1453    let s2 = match &args[1] {
1454        Value::String(s) => s.to_lowercase(),
1455        _ => return Err(format!("string-ci>=?: not a string: {:?}", args[1])),
1456    };
1457
1458    Ok(Value::bool(s1 >= s2))
1459}
1460
1461/// (string? obj) → boolean
1462///
1463/// Returns #t if obj is a string, otherwise #f.
1464///
1465/// **R4RS**: Required procedure
1466pub fn prim_string_p(args: &[Value]) -> PrimitiveResult {
1467    if args.len() != 1 {
1468        return Err("string? requires exactly 1 argument".to_string());
1469    }
1470
1471    Ok(Value::bool(matches!(args[0], Value::String(_))))
1472}
1473
1474/// (make-string k [char]) → string
1475///
1476/// Returns a newly allocated string of length k. If char is given,
1477/// then all elements of the string are initialized to char, otherwise
1478/// the contents are unspecified (we use space).
1479///
1480/// **R4RS**: Required procedure
1481pub fn prim_make_string(args: &[Value]) -> PrimitiveResult {
1482    if args.is_empty() || args.len() > 2 {
1483        return Err("make-string requires 1 or 2 arguments".to_string());
1484    }
1485
1486    let k = match args[0] {
1487        Value::Integer(n) if n >= 0 => n as usize,
1488        _ => return Err("make-string: first argument must be a non-negative integer".to_string()),
1489    };
1490
1491    let ch = if args.len() == 2 {
1492        match args[1] {
1493            Value::Char(c) => c,
1494            _ => return Err("make-string: second argument must be a character".to_string()),
1495        }
1496    } else {
1497        ' '
1498    };
1499
1500    Ok(Value::string(ch.to_string().repeat(k)))
1501}
1502
1503/// (string char ...) → string
1504///
1505/// Returns a newly allocated string composed of the arguments.
1506///
1507/// **R4RS**: Required procedure
1508pub fn prim_string(args: &[Value]) -> PrimitiveResult {
1509    let mut result = String::new();
1510
1511    for arg in args {
1512        match arg {
1513            Value::Char(c) => result.push(*c),
1514            _ => return Err(format!("string: not a character: {:?}", arg)),
1515        }
1516    }
1517
1518    Ok(Value::string(result))
1519}
1520
1521/// (string->list string) → list
1522///
1523/// Returns a newly allocated list of the characters of string.
1524///
1525/// **R4RS**: Required procedure
1526pub fn prim_string_to_list(args: &[Value]) -> PrimitiveResult {
1527    if args.len() != 1 {
1528        return Err("string->list requires exactly 1 argument".to_string());
1529    }
1530
1531    let s = match &args[0] {
1532        Value::String(s) => s,
1533        _ => return Err(format!("string->list: not a string: {:?}", args[0])),
1534    };
1535
1536    let mut result = Value::Nil;
1537    for ch in s.chars().rev() {
1538        result = Value::cons(Value::char(ch), result);
1539    }
1540
1541    Ok(result)
1542}
1543
1544/// (list->string list) → string
1545///
1546/// Returns a newly allocated string formed from the characters in list.
1547///
1548/// **R4RS**: Required procedure
1549pub fn prim_list_to_string(args: &[Value]) -> PrimitiveResult {
1550    if args.len() != 1 {
1551        return Err("list->string requires exactly 1 argument".to_string());
1552    }
1553
1554    let mut result = String::new();
1555    let mut current = args[0].clone();
1556
1557    loop {
1558        match current {
1559            Value::Nil => break,
1560            Value::Pair(ref p) => {
1561                let pair = p.borrow();
1562                match pair.car {
1563                    Value::Char(c) => result.push(c),
1564                    _ => return Err("list->string: list must contain only characters".to_string()),
1565                }
1566                let cdr = pair.cdr.clone();
1567                drop(pair);
1568                current = cdr;
1569            }
1570            _ => return Err("list->string: not a proper list".to_string()),
1571        }
1572    }
1573
1574    Ok(Value::string(result))
1575}
1576
1577/// (symbol->string symbol) → string
1578///
1579/// Returns the name of symbol as a string.
1580///
1581/// **R4RS**: Required procedure
1582pub fn prim_symbol_to_string(args: &[Value]) -> PrimitiveResult {
1583    if args.len() != 1 {
1584        return Err("symbol->string requires exactly 1 argument".to_string());
1585    }
1586
1587    match &args[0] {
1588        Value::Symbol(s) => Ok(Value::string(s.to_string())),
1589        _ => Err(format!("symbol->string: not a symbol: {:?}", args[0])),
1590    }
1591}
1592
1593/// (string->symbol string) → symbol
1594///
1595/// Returns the symbol whose name is string.
1596///
1597/// **R4RS**: Required procedure
1598pub fn prim_string_to_symbol(args: &[Value]) -> PrimitiveResult {
1599    if args.len() != 1 {
1600        return Err("string->symbol requires exactly 1 argument".to_string());
1601    }
1602
1603    match &args[0] {
1604        Value::String(s) => Ok(Value::symbol(s)),
1605        _ => Err(format!("string->symbol: not a string: {:?}", args[0])),
1606    }
1607}
1608
1609/// (symbol? obj) → boolean
1610///
1611/// Returns #t if obj is a symbol, otherwise #f.
1612///
1613/// **R4RS**: Required procedure
1614pub fn prim_symbol_p(args: &[Value]) -> PrimitiveResult {
1615    if args.len() != 1 {
1616        return Err("symbol? requires exactly 1 argument".to_string());
1617    }
1618
1619    Ok(Value::bool(matches!(args[0], Value::Symbol(_))))
1620}
1621
1622/// (char? obj) → boolean
1623///
1624/// Returns #t if obj is a character, otherwise #f.
1625///
1626/// **R4RS**: Required procedure
1627pub fn prim_char_p(args: &[Value]) -> PrimitiveResult {
1628    if args.len() != 1 {
1629        return Err("char? requires exactly 1 argument".to_string());
1630    }
1631
1632    Ok(Value::bool(matches!(args[0], Value::Char(_))))
1633}
1634
1635/// (char=? char1 char2) → boolean
1636///
1637/// Returns #t if the two characters are equal, otherwise #f.
1638///
1639/// **R4RS**: Required procedure
1640pub fn prim_char_eq(args: &[Value]) -> PrimitiveResult {
1641    if args.len() != 2 {
1642        return Err("char=? requires exactly 2 arguments".to_string());
1643    }
1644
1645    let c1 = match args[0] {
1646        Value::Char(c) => c,
1647        _ => return Err(format!("char=?: not a character: {:?}", args[0])),
1648    };
1649
1650    let c2 = match args[1] {
1651        Value::Char(c) => c,
1652        _ => return Err(format!("char=?: not a character: {:?}", args[1])),
1653    };
1654
1655    Ok(Value::bool(c1 == c2))
1656}
1657
1658/// (char<? char1 char2) → boolean
1659///
1660/// Returns #t if char1 is less than char2, otherwise #f.
1661///
1662/// **R4RS**: Required procedure
1663pub fn prim_char_lt(args: &[Value]) -> PrimitiveResult {
1664    if args.len() != 2 {
1665        return Err("char<? requires exactly 2 arguments".to_string());
1666    }
1667
1668    let c1 = match args[0] {
1669        Value::Char(c) => c,
1670        _ => return Err(format!("char<?: not a character: {:?}", args[0])),
1671    };
1672
1673    let c2 = match args[1] {
1674        Value::Char(c) => c,
1675        _ => return Err(format!("char<?: not a character: {:?}", args[1])),
1676    };
1677
1678    Ok(Value::bool(c1 < c2))
1679}
1680
1681/// (char>? char1 char2) → boolean
1682///
1683/// Returns #t if char1 is greater than char2, otherwise #f.
1684///
1685/// **R4RS**: Required procedure
1686pub fn prim_char_gt(args: &[Value]) -> PrimitiveResult {
1687    if args.len() != 2 {
1688        return Err("char>? requires exactly 2 arguments".to_string());
1689    }
1690
1691    let c1 = match args[0] {
1692        Value::Char(c) => c,
1693        _ => return Err(format!("char>?: not a character: {:?}", args[0])),
1694    };
1695
1696    let c2 = match args[1] {
1697        Value::Char(c) => c,
1698        _ => return Err(format!("char>?: not a character: {:?}", args[1])),
1699    };
1700
1701    Ok(Value::bool(c1 > c2))
1702}
1703
1704/// (char-upcase char) → char
1705///
1706/// Returns the uppercase equivalent of char.
1707///
1708/// **R4RS**: Required procedure
1709pub fn prim_char_upcase(args: &[Value]) -> PrimitiveResult {
1710    if args.len() != 1 {
1711        return Err("char-upcase requires exactly 1 argument".to_string());
1712    }
1713
1714    match args[0] {
1715        Value::Char(c) => Ok(Value::char(c.to_ascii_uppercase())),
1716        _ => Err(format!("char-upcase: not a character: {:?}", args[0])),
1717    }
1718}
1719
1720/// (char-downcase char) → char
1721///
1722/// Returns the lowercase equivalent of char.
1723///
1724/// **R4RS**: Required procedure
1725pub fn prim_char_downcase(args: &[Value]) -> PrimitiveResult {
1726    if args.len() != 1 {
1727        return Err("char-downcase requires exactly 1 argument".to_string());
1728    }
1729
1730    match args[0] {
1731        Value::Char(c) => Ok(Value::char(c.to_ascii_lowercase())),
1732        _ => Err(format!("char-downcase: not a character: {:?}", args[0])),
1733    }
1734}
1735
1736/// (char<=? char1 char2) → boolean
1737///
1738/// Returns #t if char1 is less than or equal to char2, otherwise #f.
1739///
1740/// **R4RS**: Required procedure
1741pub fn prim_char_le(args: &[Value]) -> PrimitiveResult {
1742    if args.len() != 2 {
1743        return Err("char<=? requires exactly 2 arguments".to_string());
1744    }
1745
1746    let c1 = match args[0] {
1747        Value::Char(c) => c,
1748        _ => return Err(format!("char<=?: not a character: {:?}", args[0])),
1749    };
1750
1751    let c2 = match args[1] {
1752        Value::Char(c) => c,
1753        _ => return Err(format!("char<=?: not a character: {:?}", args[1])),
1754    };
1755
1756    Ok(Value::bool(c1 <= c2))
1757}
1758
1759/// (char>=? char1 char2) → boolean
1760///
1761/// Returns #t if char1 is greater than or equal to char2, otherwise #f.
1762///
1763/// **R4RS**: Required procedure
1764pub fn prim_char_ge(args: &[Value]) -> PrimitiveResult {
1765    if args.len() != 2 {
1766        return Err("char>=? requires exactly 2 arguments".to_string());
1767    }
1768
1769    let c1 = match args[0] {
1770        Value::Char(c) => c,
1771        _ => return Err(format!("char>=?: not a character: {:?}", args[0])),
1772    };
1773
1774    let c2 = match args[1] {
1775        Value::Char(c) => c,
1776        _ => return Err(format!("char>=?: not a character: {:?}", args[1])),
1777    };
1778
1779    Ok(Value::bool(c1 >= c2))
1780}
1781
1782/// (char->integer char) → integer
1783///
1784/// Returns the Unicode/ASCII code point of the character.
1785///
1786/// **R4RS**: Required procedure
1787pub fn prim_char_to_integer(args: &[Value]) -> PrimitiveResult {
1788    if args.len() != 1 {
1789        return Err("char->integer requires exactly 1 argument".to_string());
1790    }
1791
1792    match args[0] {
1793        Value::Char(c) => Ok(Value::integer(c as i64)),
1794        _ => Err(format!("char->integer: not a character: {:?}", args[0])),
1795    }
1796}
1797
1798/// (integer->char n) → char
1799///
1800/// Returns the character with the given Unicode/ASCII code point.
1801///
1802/// **R4RS**: Required procedure
1803pub fn prim_integer_to_char(args: &[Value]) -> PrimitiveResult {
1804    if args.len() != 1 {
1805        return Err("integer->char requires exactly 1 argument".to_string());
1806    }
1807
1808    match args[0] {
1809        Value::Integer(n) => {
1810            if n < 0 || n > 0x10FFFF {
1811                return Err(format!("integer->char: invalid code point: {}", n));
1812            }
1813            match char::from_u32(n as u32) {
1814                Some(c) => Ok(Value::char(c)),
1815                None => Err(format!("integer->char: invalid Unicode code point: {}", n)),
1816            }
1817        }
1818        _ => Err(format!("integer->char: not an integer: {:?}", args[0])),
1819    }
1820}
1821
1822/// (char-alphabetic? char) → boolean
1823///
1824/// Returns #t if char is an alphabetic character, otherwise #f.
1825///
1826/// **R4RS**: Required procedure
1827pub fn prim_char_alphabetic_p(args: &[Value]) -> PrimitiveResult {
1828    if args.len() != 1 {
1829        return Err("char-alphabetic? requires exactly 1 argument".to_string());
1830    }
1831
1832    match args[0] {
1833        Value::Char(c) => Ok(Value::bool(c.is_alphabetic())),
1834        _ => Err(format!("char-alphabetic?: not a character: {:?}", args[0])),
1835    }
1836}
1837
1838/// (char-numeric? char) → boolean
1839///
1840/// Returns #t if char is a numeric character, otherwise #f.
1841///
1842/// **R4RS**: Required procedure
1843pub fn prim_char_numeric_p(args: &[Value]) -> PrimitiveResult {
1844    if args.len() != 1 {
1845        return Err("char-numeric? requires exactly 1 argument".to_string());
1846    }
1847
1848    match args[0] {
1849        Value::Char(c) => Ok(Value::bool(c.is_numeric())),
1850        _ => Err(format!("char-numeric?: not a character: {:?}", args[0])),
1851    }
1852}
1853
1854/// (char-whitespace? char) → boolean
1855///
1856/// Returns #t if char is a whitespace character, otherwise #f.
1857///
1858/// **R4RS**: Required procedure
1859pub fn prim_char_whitespace_p(args: &[Value]) -> PrimitiveResult {
1860    if args.len() != 1 {
1861        return Err("char-whitespace? requires exactly 1 argument".to_string());
1862    }
1863
1864    match args[0] {
1865        Value::Char(c) => Ok(Value::bool(c.is_whitespace())),
1866        _ => Err(format!("char-whitespace?: not a character: {:?}", args[0])),
1867    }
1868}
1869
1870/// (char-ci=? char1 char2) → boolean
1871///
1872/// Returns #t if the two characters are equal ignoring case, otherwise #f.
1873///
1874/// **R4RS**: Required procedure
1875pub fn prim_char_ci_eq(args: &[Value]) -> PrimitiveResult {
1876    if args.len() != 2 {
1877        return Err("char-ci=? requires exactly 2 arguments".to_string());
1878    }
1879
1880    let c1 = match args[0] {
1881        Value::Char(c) => c.to_ascii_lowercase(),
1882        _ => return Err(format!("char-ci=?: not a character: {:?}", args[0])),
1883    };
1884
1885    let c2 = match args[1] {
1886        Value::Char(c) => c.to_ascii_lowercase(),
1887        _ => return Err(format!("char-ci=?: not a character: {:?}", args[1])),
1888    };
1889
1890    Ok(Value::bool(c1 == c2))
1891}
1892
1893/// (char-ci<? char1 char2) → boolean
1894///
1895/// Returns #t if char1 is less than char2 ignoring case, otherwise #f.
1896///
1897/// **R4RS**: Required procedure
1898pub fn prim_char_ci_lt(args: &[Value]) -> PrimitiveResult {
1899    if args.len() != 2 {
1900        return Err("char-ci<? requires exactly 2 arguments".to_string());
1901    }
1902
1903    let c1 = match args[0] {
1904        Value::Char(c) => c.to_ascii_lowercase(),
1905        _ => return Err(format!("char-ci<?: not a character: {:?}", args[0])),
1906    };
1907
1908    let c2 = match args[1] {
1909        Value::Char(c) => c.to_ascii_lowercase(),
1910        _ => return Err(format!("char-ci<?: not a character: {:?}", args[1])),
1911    };
1912
1913    Ok(Value::bool(c1 < c2))
1914}
1915
1916/// (char-ci>? char1 char2) → boolean
1917///
1918/// Returns #t if char1 is greater than char2 ignoring case, otherwise #f.
1919///
1920/// **R4RS**: Required procedure
1921pub fn prim_char_ci_gt(args: &[Value]) -> PrimitiveResult {
1922    if args.len() != 2 {
1923        return Err("char-ci>? requires exactly 2 arguments".to_string());
1924    }
1925
1926    let c1 = match args[0] {
1927        Value::Char(c) => c.to_ascii_lowercase(),
1928        _ => return Err(format!("char-ci>?: not a character: {:?}", args[0])),
1929    };
1930
1931    let c2 = match args[1] {
1932        Value::Char(c) => c.to_ascii_lowercase(),
1933        _ => return Err(format!("char-ci>?: not a character: {:?}", args[1])),
1934    };
1935
1936    Ok(Value::bool(c1 > c2))
1937}
1938
1939/// (char-ci<=? char1 char2) → boolean
1940///
1941/// Returns #t if char1 is less than or equal to char2 ignoring case, otherwise #f.
1942///
1943/// **R4RS**: Required procedure
1944pub fn prim_char_ci_le(args: &[Value]) -> PrimitiveResult {
1945    if args.len() != 2 {
1946        return Err("char-ci<=? requires exactly 2 arguments".to_string());
1947    }
1948
1949    let c1 = match args[0] {
1950        Value::Char(c) => c.to_ascii_lowercase(),
1951        _ => return Err(format!("char-ci<=?: not a character: {:?}", args[0])),
1952    };
1953
1954    let c2 = match args[1] {
1955        Value::Char(c) => c.to_ascii_lowercase(),
1956        _ => return Err(format!("char-ci<=?: not a character: {:?}", args[1])),
1957    };
1958
1959    Ok(Value::bool(c1 <= c2))
1960}
1961
1962/// (char-ci>=? char1 char2) → boolean
1963///
1964/// Returns #t if char1 is greater than or equal to char2 ignoring case, otherwise #f.
1965///
1966/// **R4RS**: Required procedure
1967pub fn prim_char_ci_ge(args: &[Value]) -> PrimitiveResult {
1968    if args.len() != 2 {
1969        return Err("char-ci>=? requires exactly 2 arguments".to_string());
1970    }
1971
1972    let c1 = match args[0] {
1973        Value::Char(c) => c.to_ascii_lowercase(),
1974        _ => return Err(format!("char-ci>=?: not a character: {:?}", args[0])),
1975    };
1976
1977    let c2 = match args[1] {
1978        Value::Char(c) => c.to_ascii_lowercase(),
1979        _ => return Err(format!("char-ci>=?: not a character: {:?}", args[1])),
1980    };
1981
1982    Ok(Value::bool(c1 >= c2))
1983}
1984
1985// =============================================================================
1986// Boolean and Equality Primitives (R4RS)
1987// =============================================================================
1988
1989/// (not obj) → boolean
1990///
1991/// Returns #t if obj is false, otherwise #f.
1992///
1993/// **R4RS**: Required procedure
1994pub fn prim_not(args: &[Value]) -> PrimitiveResult {
1995    if args.len() != 1 {
1996        return Err("not requires exactly 1 argument".to_string());
1997    }
1998
1999    Ok(Value::bool(!args[0].is_true()))
2000}
2001
2002/// (boolean? obj) → boolean
2003///
2004/// Returns #t if obj is a boolean, otherwise #f.
2005///
2006/// **R4RS**: Required procedure
2007pub fn prim_boolean_p(args: &[Value]) -> PrimitiveResult {
2008    if args.len() != 1 {
2009        return Err("boolean? requires exactly 1 argument".to_string());
2010    }
2011
2012    Ok(Value::bool(matches!(args[0], Value::Bool(_))))
2013}
2014
2015/// (equal? obj1 obj2) → boolean
2016///
2017/// Returns #t if obj1 and obj2 are structurally equal, otherwise #f.
2018/// This is a deep comparison that recursively compares pairs and other structures.
2019///
2020/// **R4RS**: Required procedure
2021pub fn prim_equal_p(args: &[Value]) -> PrimitiveResult {
2022    if args.len() != 2 {
2023        return Err("equal? requires exactly 2 arguments".to_string());
2024    }
2025
2026    Ok(Value::bool(values_equal(&args[0], &args[1])))
2027}
2028
2029/// Helper function for deep structural equality
2030fn values_equal(v1: &Value, v2: &Value) -> bool {
2031    match (v1, v2) {
2032        (Value::Nil, Value::Nil) => true,
2033        (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
2034        (Value::Integer(n1), Value::Integer(n2)) => n1 == n2,
2035        (Value::Real(r1), Value::Real(r2)) => (r1 - r2).abs() < f64::EPSILON,
2036        (Value::Integer(n), Value::Real(r)) | (Value::Real(r), Value::Integer(n)) => {
2037            (*n as f64 - r).abs() < f64::EPSILON
2038        }
2039        (Value::Char(c1), Value::Char(c2)) => c1 == c2,
2040        (Value::String(s1), Value::String(s2)) => s1 == s2,
2041        (Value::Symbol(s1), Value::Symbol(s2)) => s1 == s2,
2042        (Value::Pair(p1), Value::Pair(p2)) => {
2043            let pair1 = p1.borrow();
2044            let pair2 = p2.borrow();
2045            values_equal(&pair1.car, &pair2.car) && values_equal(&pair1.cdr, &pair2.cdr)
2046        }
2047        _ => false,
2048    }
2049}
2050
2051/// (eqv? obj1 obj2) → boolean
2052///
2053/// Returns #t if obj1 and obj2 are equivalent, otherwise #f.
2054/// For most types, this is the same as equal?, but symbols are compared by identity.
2055///
2056/// **R4RS**: Required procedure
2057pub fn prim_eqv_p(args: &[Value]) -> PrimitiveResult {
2058    if args.len() != 2 {
2059        return Err("eqv? requires exactly 2 arguments".to_string());
2060    }
2061
2062    let result = match (&args[0], &args[1]) {
2063        (Value::Nil, Value::Nil) => true,
2064        (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
2065        (Value::Integer(n1), Value::Integer(n2)) => n1 == n2,
2066        (Value::Real(r1), Value::Real(r2)) => (r1 - r2).abs() < f64::EPSILON,
2067        (Value::Char(c1), Value::Char(c2)) => c1 == c2,
2068        (Value::Symbol(s1), Value::Symbol(s2)) => s1 == s2,
2069        // For pairs, strings, and procedures, eqv? checks object identity
2070        (Value::Pair(p1), Value::Pair(p2)) => gc::Gc::ptr_eq(p1, p2),
2071        (Value::String(s1), Value::String(s2)) => gc::Gc::ptr_eq(s1, s2),
2072        (Value::Procedure(pr1), Value::Procedure(pr2)) => gc::Gc::ptr_eq(pr1, pr2),
2073        _ => false,
2074    };
2075
2076    Ok(Value::bool(result))
2077}
2078
2079/// (eq? obj1 obj2) → boolean
2080///
2081/// Returns #t if obj1 and obj2 are the same object (pointer equality), otherwise #f.
2082///
2083/// **R4RS**: Required procedure
2084pub fn prim_eq_p(args: &[Value]) -> PrimitiveResult {
2085    if args.len() != 2 {
2086        return Err("eq? requires exactly 2 arguments".to_string());
2087    }
2088
2089    let result = match (&args[0], &args[1]) {
2090        (Value::Nil, Value::Nil) => true,
2091        (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
2092        (Value::Integer(n1), Value::Integer(n2)) => n1 == n2,
2093        (Value::Char(c1), Value::Char(c2)) => c1 == c2,
2094        (Value::Symbol(s1), Value::Symbol(s2)) => s1 == s2,
2095        // For heap-allocated objects, check pointer equality
2096        (Value::Pair(p1), Value::Pair(p2)) => gc::Gc::ptr_eq(p1, p2),
2097        (Value::String(s1), Value::String(s2)) => gc::Gc::ptr_eq(s1, s2),
2098        (Value::Procedure(pr1), Value::Procedure(pr2)) => gc::Gc::ptr_eq(pr1, pr2),
2099        _ => false,
2100    };
2101
2102    Ok(Value::bool(result))
2103}
2104
2105/// (procedure? obj) → boolean
2106///
2107/// Returns #t if obj is a procedure, otherwise #f.
2108///
2109/// **R4RS**: Required procedure
2110pub fn prim_procedure_p(args: &[Value]) -> PrimitiveResult {
2111    if args.len() != 1 {
2112        return Err("procedure? requires exactly 1 argument".to_string());
2113    }
2114
2115    Ok(Value::bool(matches!(args[0], Value::Procedure(_))))
2116}
2117
2118// =============================================================================
2119// I/O and Utility Primitives (R4RS / DSSSL)
2120// =============================================================================
2121
2122/// (error message obj ...) → does not return
2123///
2124/// Signals an error with the given message and objects.
2125/// In DSSSL/OpenJade, this is used for runtime error reporting.
2126///
2127/// **DSSSL**: Extension (OpenJade primitive)
2128pub fn prim_error(args: &[Value]) -> PrimitiveResult {
2129    if args.is_empty() {
2130        return Err("error: requires at least 1 argument".to_string());
2131    }
2132
2133    let message = match &args[0] {
2134        Value::String(s) => s.to_string(),
2135        Value::Symbol(s) => s.to_string(),
2136        other => format!("{:?}", other),
2137    };
2138
2139    // Collect additional objects
2140    let mut full_message = message;
2141    for arg in &args[1..] {
2142        full_message.push_str(&format!(" {:?}", arg));
2143    }
2144
2145    Err(full_message)
2146}
2147
2148/// (display obj) → unspecified
2149///
2150/// Writes obj to the current output port (stdout).
2151/// Used for debugging and output in DSSSL templates.
2152///
2153/// **R4RS**: Required procedure
2154pub fn prim_display(args: &[Value]) -> PrimitiveResult {
2155    if args.len() != 1 {
2156        return Err("display requires exactly 1 argument".to_string());
2157    }
2158
2159    let output = match &args[0] {
2160        Value::String(s) => s.to_string(),
2161        Value::Char(c) => c.to_string(),
2162        other => format!("{:?}", other),
2163    };
2164
2165    print!("{}", output);
2166    Ok(Value::Unspecified)
2167}
2168
2169/// (newline) → unspecified
2170///
2171/// Writes a newline to the current output port (stdout).
2172///
2173/// **R4RS**: Required procedure
2174pub fn prim_newline(args: &[Value]) -> PrimitiveResult {
2175    if !args.is_empty() {
2176        return Err("newline requires 0 arguments".to_string());
2177    }
2178
2179    println!();
2180    Ok(Value::Unspecified)
2181}
2182
2183/// (write obj) → unspecified
2184///
2185/// Writes obj to the current output port in a machine-readable format.
2186///
2187/// **R4RS**: Required procedure
2188pub fn prim_write(args: &[Value]) -> PrimitiveResult {
2189    if args.len() != 1 {
2190        return Err("write requires exactly 1 argument".to_string());
2191    }
2192
2193    print!("{:?}", args[0]);
2194    Ok(Value::Unspecified)
2195}
2196
2197// =============================================================================
2198// Conversion Primitives (R4RS)
2199// =============================================================================
2200
2201/// (number->string number) → string
2202///
2203/// Returns a string representation of number.
2204///
2205/// **R4RS**: Required procedure
2206pub fn prim_number_to_string(args: &[Value]) -> PrimitiveResult {
2207    if args.len() != 1 {
2208        return Err("number->string requires exactly 1 argument".to_string());
2209    }
2210
2211    match &args[0] {
2212        Value::Integer(n) => Ok(Value::string(n.to_string())),
2213        Value::Real(r) => Ok(Value::string(r.to_string())),
2214        _ => Err(format!("number->string: not a number: {:?}", args[0])),
2215    }
2216}
2217
2218/// (string->number string) → number or #f
2219///
2220/// Returns a number parsed from string, or #f if the string is not a valid number.
2221///
2222/// **R4RS**: Required procedure
2223pub fn prim_string_to_number(args: &[Value]) -> PrimitiveResult {
2224    if args.len() != 1 {
2225        return Err("string->number requires exactly 1 argument".to_string());
2226    }
2227
2228    let s = match &args[0] {
2229        Value::String(s) => s,
2230        _ => return Err(format!("string->number: not a string: {:?}", args[0])),
2231    };
2232
2233    // Try parsing as integer first
2234    if let Ok(n) = s.parse::<i64>() {
2235        return Ok(Value::integer(n));
2236    }
2237
2238    // Try parsing as real
2239    if let Ok(r) = s.parse::<f64>() {
2240        return Ok(Value::real(r));
2241    }
2242
2243    // Return #f if not a valid number
2244    Ok(Value::bool(false))
2245}
2246
2247// =============================================================================
2248// Keyword Primitives (DSSSL Extension)
2249// =============================================================================
2250
2251/// (keyword? obj) → boolean
2252///
2253/// Returns #t if obj is a keyword, otherwise #f.
2254///
2255/// **DSSSL**: Extension (OpenJade primitive)
2256pub fn prim_keyword_p(args: &[Value]) -> PrimitiveResult {
2257    if args.len() != 1 {
2258        return Err("keyword? requires exactly 1 argument".to_string());
2259    }
2260
2261    Ok(Value::bool(matches!(args[0], Value::Keyword(_))))
2262}
2263
2264/// (keyword->string keyword) → string
2265///
2266/// Returns the name of keyword as a string.
2267///
2268/// **DSSSL**: Extension (OpenJade primitive)
2269pub fn prim_keyword_to_string(args: &[Value]) -> PrimitiveResult {
2270    if args.len() != 1 {
2271        return Err("keyword->string requires exactly 1 argument".to_string());
2272    }
2273
2274    match &args[0] {
2275        Value::Keyword(k) => Ok(Value::string(k.to_string())),
2276        _ => Err(format!("keyword->string: not a keyword: {:?}", args[0])),
2277    }
2278}
2279
2280/// (string->keyword string) → keyword
2281///
2282/// Returns the keyword whose name is string.
2283///
2284/// **DSSSL**: Extension (OpenJade primitive)
2285pub fn prim_string_to_keyword(args: &[Value]) -> PrimitiveResult {
2286    if args.len() != 1 {
2287        return Err("string->keyword requires exactly 1 argument".to_string());
2288    }
2289
2290    match &args[0] {
2291        Value::String(s) => Ok(Value::keyword(s)),
2292        _ => Err(format!("string->keyword: not a string: {:?}", args[0])),
2293    }
2294}
2295
2296// =============================================================================
2297// Additional List Utilities (R4RS)
2298// =============================================================================
2299
2300/// (memq obj list) → list or #f
2301///
2302/// Returns the first sublist of list whose car is eq? to obj.
2303/// If obj does not occur in list, returns #f.
2304///
2305/// **R4RS**: Required procedure
2306pub fn prim_memq(args: &[Value]) -> PrimitiveResult {
2307    if args.len() != 2 {
2308        return Err("memq requires exactly 2 arguments".to_string());
2309    }
2310
2311    let obj = &args[0];
2312    let mut current = args[1].clone();
2313
2314    loop {
2315        match current {
2316            Value::Nil => return Ok(Value::bool(false)),
2317            Value::Pair(ref p) => {
2318                let pair = p.borrow();
2319                // Use eq? comparison
2320                if obj.eq(&pair.car) {
2321                    drop(pair);
2322                    return Ok(current);
2323                }
2324                let cdr = pair.cdr.clone();
2325                drop(pair);
2326                current = cdr;
2327            }
2328            _ => return Err("memq: not a proper list".to_string()),
2329        }
2330    }
2331}
2332
2333/// (memv obj list) → list or #f
2334///
2335/// Returns the first sublist of list whose car is eqv? to obj.
2336/// If obj does not occur in list, returns #f.
2337///
2338/// **R4RS**: Required procedure
2339pub fn prim_memv(args: &[Value]) -> PrimitiveResult {
2340    if args.len() != 2 {
2341        return Err("memv requires exactly 2 arguments".to_string());
2342    }
2343
2344    let obj = &args[0];
2345    let mut current = args[1].clone();
2346
2347    loop {
2348        match current {
2349            Value::Nil => return Ok(Value::bool(false)),
2350            Value::Pair(ref p) => {
2351                let pair = p.borrow();
2352                // Use eqv? comparison
2353                if obj.eqv(&pair.car) {
2354                    drop(pair);
2355                    return Ok(current);
2356                }
2357                let cdr = pair.cdr.clone();
2358                drop(pair);
2359                current = cdr;
2360            }
2361            _ => return Err("memv: not a proper list".to_string()),
2362        }
2363    }
2364}
2365
2366/// (member obj list) → list or #f
2367///
2368/// Returns the first sublist of list whose car is equal? to obj.
2369/// If obj does not occur in list, returns #f.
2370///
2371/// **R4RS**: Required procedure
2372pub fn prim_member(args: &[Value]) -> PrimitiveResult {
2373    if args.len() != 2 {
2374        return Err("member requires exactly 2 arguments".to_string());
2375    }
2376
2377    let obj = &args[0];
2378    let mut current = args[1].clone();
2379
2380    loop {
2381        match current {
2382            Value::Nil => return Ok(Value::bool(false)),
2383            Value::Pair(ref p) => {
2384                let pair = p.borrow();
2385                // Use equal? comparison
2386                if obj.equal(&pair.car) {
2387                    drop(pair);
2388                    return Ok(current);
2389                }
2390                let cdr = pair.cdr.clone();
2391                drop(pair);
2392                current = cdr;
2393            }
2394            _ => return Err("member: not a proper list".to_string()),
2395        }
2396    }
2397}
2398
2399/// (assq obj alist) → pair or #f
2400///
2401/// Returns the first pair in alist whose car is eq? to obj.
2402/// If no pair is found, returns #f.
2403///
2404/// **R4RS**: Required procedure
2405pub fn prim_assq(args: &[Value]) -> PrimitiveResult {
2406    if args.len() != 2 {
2407        return Err("assq requires exactly 2 arguments".to_string());
2408    }
2409
2410    let obj = &args[0];
2411    let mut current = args[1].clone();
2412
2413    loop {
2414        match current {
2415            Value::Nil => return Ok(Value::bool(false)),
2416            Value::Pair(ref p) => {
2417                let pair = p.borrow();
2418                // Check if car is a pair
2419                if let Value::Pair(ref inner_p) = pair.car {
2420                    let inner_pair = inner_p.borrow();
2421                    if obj.eq(&inner_pair.car) {
2422                        drop(inner_pair);
2423                        let result = pair.car.clone();
2424                        drop(pair);
2425                        return Ok(result);
2426                    }
2427                }
2428                let cdr = pair.cdr.clone();
2429                drop(pair);
2430                current = cdr;
2431            }
2432            _ => return Err("assq: not a proper list".to_string()),
2433        }
2434    }
2435}
2436
2437/// (assv obj alist) → pair or #f
2438///
2439/// Returns the first pair in alist whose car is eqv? to obj.
2440/// If no pair is found, returns #f.
2441///
2442/// **R4RS**: Required procedure
2443pub fn prim_assv(args: &[Value]) -> PrimitiveResult {
2444    if args.len() != 2 {
2445        return Err("assv requires exactly 2 arguments".to_string());
2446    }
2447
2448    let obj = &args[0];
2449    let mut current = args[1].clone();
2450
2451    loop {
2452        match current {
2453            Value::Nil => return Ok(Value::bool(false)),
2454            Value::Pair(ref p) => {
2455                let pair = p.borrow();
2456                // Check if car is a pair
2457                if let Value::Pair(ref inner_p) = pair.car {
2458                    let inner_pair = inner_p.borrow();
2459                    if obj.eqv(&inner_pair.car) {
2460                        drop(inner_pair);
2461                        let result = pair.car.clone();
2462                        drop(pair);
2463                        return Ok(result);
2464                    }
2465                }
2466                let cdr = pair.cdr.clone();
2467                drop(pair);
2468                current = cdr;
2469            }
2470            _ => return Err("assv: not a proper list".to_string()),
2471        }
2472    }
2473}
2474
2475/// (assoc obj alist) → pair or #f
2476///
2477/// Returns the first pair in alist whose car is equal? to obj.
2478/// If no pair is found, returns #f.
2479///
2480/// **R4RS**: Required procedure
2481pub fn prim_assoc(args: &[Value]) -> PrimitiveResult {
2482    if args.len() != 2 {
2483        return Err("assoc requires exactly 2 arguments".to_string());
2484    }
2485
2486    let obj = &args[0];
2487    let mut current = args[1].clone();
2488
2489    loop {
2490        match current {
2491            Value::Nil => return Ok(Value::bool(false)),
2492            Value::Pair(ref p) => {
2493                let pair = p.borrow();
2494                // Check if car is a pair
2495                if let Value::Pair(ref inner_p) = pair.car {
2496                    let inner_pair = inner_p.borrow();
2497                    if obj.equal(&inner_pair.car) {
2498                        drop(inner_pair);
2499                        let result = pair.car.clone();
2500                        drop(pair);
2501                        return Ok(result);
2502                    }
2503                }
2504                let cdr = pair.cdr.clone();
2505                drop(pair);
2506                current = cdr;
2507            }
2508            _ => return Err("assoc: not a proper list".to_string()),
2509        }
2510    }
2511}
2512
2513// =============================================================================
2514// DSSSL Type Stub Primitives (Document Formatting - Not Needed for Code Gen)
2515// =============================================================================
2516//
2517// These primitives are part of DSSSL but are primarily used for document
2518// formatting (print/screen output). For code generation templates, they're
2519// not typically needed, so we implement them as stubs that return dummy values.
2520//
2521// If a template actually uses these, they can be properly implemented later.
2522
2523/// (quantity? obj) → boolean
2524///
2525/// Returns #t if obj is a quantity (length/dimension), otherwise #f.
2526///
2527/// **DSSSL**: Type predicate (stub - quantities not implemented)
2528pub fn prim_quantity_p(args: &[Value]) -> PrimitiveResult {
2529    if args.len() != 1 {
2530        return Err("quantity? requires exactly 1 argument".to_string());
2531    }
2532    // Quantities not implemented - always return #f
2533    Ok(Value::bool(false))
2534}
2535
2536/// (color? obj) → boolean
2537///
2538/// Returns #t if obj is a color, otherwise #f.
2539///
2540/// **DSSSL**: Type predicate (stub - colors not implemented)
2541pub fn prim_color_p(args: &[Value]) -> PrimitiveResult {
2542    if args.len() != 1 {
2543        return Err("color? requires exactly 1 argument".to_string());
2544    }
2545    // Colors not implemented - always return #f
2546    Ok(Value::bool(false))
2547}
2548
2549/// (address? obj) → boolean
2550///
2551/// Returns #t if obj is an address, otherwise #f.
2552///
2553/// **DSSSL**: Type predicate (stub - addresses not implemented)
2554pub fn prim_address_p(args: &[Value]) -> PrimitiveResult {
2555    if args.len() != 1 {
2556        return Err("address? requires exactly 1 argument".to_string());
2557    }
2558    // Addresses not implemented - always return #f
2559    Ok(Value::bool(false))
2560}
2561
2562// =============================================================================
2563// Additional Utility Primitives
2564// =============================================================================
2565
2566/// (cadr list) → value
2567///
2568/// Equivalent to (car (cdr list)). Returns the second element of a list.
2569///
2570/// **R4RS**: Library procedure
2571pub fn prim_cadr(args: &[Value]) -> PrimitiveResult {
2572    if args.len() != 1 {
2573        return Err("cadr requires exactly 1 argument".to_string());
2574    }
2575
2576    // Get cdr
2577    let cdr = prim_cdr(args)?;
2578    // Get car of result
2579    prim_car(&[cdr])
2580}
2581
2582/// (caddr list) → value
2583///
2584/// Equivalent to (car (cdr (cdr list))). Returns the third element of a list.
2585///
2586/// **R4RS**: Library procedure
2587pub fn prim_caddr(args: &[Value]) -> PrimitiveResult {
2588    if args.len() != 1 {
2589        return Err("caddr requires exactly 1 argument".to_string());
2590    }
2591
2592    // Get cdr twice
2593    let cdr1 = prim_cdr(args)?;
2594    let cdr2 = prim_cdr(&[cdr1])?;
2595    // Get car of result
2596    prim_car(&[cdr2])
2597}
2598
2599/// (cadddr list) → value
2600///
2601/// Equivalent to (car (cdr (cdr (cdr list)))). Returns the fourth element.
2602///
2603/// **R4RS**: Library procedure
2604pub fn prim_cadddr(args: &[Value]) -> PrimitiveResult {
2605    if args.len() != 1 {
2606        return Err("cadddr requires exactly 1 argument".to_string());
2607    }
2608
2609    // Get cdr three times
2610    let cdr1 = prim_cdr(args)?;
2611    let cdr2 = prim_cdr(&[cdr1])?;
2612    let cdr3 = prim_cdr(&[cdr2])?;
2613    // Get car of result
2614    prim_car(&[cdr3])
2615}
2616
2617/// (caar list) → value
2618///
2619/// Equivalent to (car (car list)).
2620///
2621/// **R4RS**: Library procedure
2622pub fn prim_caar(args: &[Value]) -> PrimitiveResult {
2623    if args.len() != 1 {
2624        return Err("caar requires exactly 1 argument".to_string());
2625    }
2626
2627    let car = prim_car(args)?;
2628    prim_car(&[car])
2629}
2630
2631/// (cddr list) → value
2632///
2633/// Equivalent to (cdr (cdr list)).
2634///
2635/// **R4RS**: Library procedure
2636pub fn prim_cddr(args: &[Value]) -> PrimitiveResult {
2637    if args.len() != 1 {
2638        return Err("cddr requires exactly 1 argument".to_string());
2639    }
2640
2641    let cdr1 = prim_cdr(args)?;
2642    prim_cdr(&[cdr1])
2643}
2644
2645/// (cdar list) → value
2646///
2647/// Equivalent to (cdr (car list)).
2648///
2649/// **R4RS**: Library procedure
2650pub fn prim_cdar(args: &[Value]) -> PrimitiveResult {
2651    if args.len() != 1 {
2652        return Err("cdar requires exactly 1 argument".to_string());
2653    }
2654
2655    let car = prim_car(args)?;
2656    prim_cdr(&[car])
2657}
2658
2659/// (caaar list) → value
2660///
2661/// Equivalent to (car (car (car list))).
2662///
2663/// **R4RS**: Library procedure
2664pub fn prim_caaar(args: &[Value]) -> PrimitiveResult {
2665    if args.len() != 1 {
2666        return Err("caaar requires exactly 1 argument".to_string());
2667    }
2668
2669    let car1 = prim_car(args)?;
2670    let car2 = prim_car(&[car1])?;
2671    prim_car(&[car2])
2672}
2673
2674/// (cdaar list) → value
2675///
2676/// Equivalent to (cdr (car (car list))).
2677///
2678/// **R4RS**: Library procedure
2679pub fn prim_cdaar(args: &[Value]) -> PrimitiveResult {
2680    if args.len() != 1 {
2681        return Err("cdaar requires exactly 1 argument".to_string());
2682    }
2683
2684    let car1 = prim_car(args)?;
2685    let car2 = prim_car(&[car1])?;
2686    prim_cdr(&[car2])
2687}
2688
2689/// (cadar list) → value
2690///
2691/// Equivalent to (car (cdr (car list))).
2692///
2693/// **R4RS**: Library procedure
2694pub fn prim_cadar(args: &[Value]) -> PrimitiveResult {
2695    if args.len() != 1 {
2696        return Err("cadar requires exactly 1 argument".to_string());
2697    }
2698
2699    let car = prim_car(args)?;
2700    let cdr = prim_cdr(&[car])?;
2701    prim_car(&[cdr])
2702}
2703
2704/// (cddar list) → value
2705///
2706/// Equivalent to (cdr (cdr (car list))).
2707///
2708/// **R4RS**: Library procedure
2709pub fn prim_cddar(args: &[Value]) -> PrimitiveResult {
2710    if args.len() != 1 {
2711        return Err("cddar requires exactly 1 argument".to_string());
2712    }
2713
2714    let car = prim_car(args)?;
2715    let cdr1 = prim_cdr(&[car])?;
2716    prim_cdr(&[cdr1])
2717}
2718
2719/// (caadr list) → value
2720///
2721/// Equivalent to (car (car (cdr list))).
2722///
2723/// **R4RS**: Library procedure
2724pub fn prim_caadr(args: &[Value]) -> PrimitiveResult {
2725    if args.len() != 1 {
2726        return Err("caadr requires exactly 1 argument".to_string());
2727    }
2728
2729    let cdr = prim_cdr(args)?;
2730    let car = prim_car(&[cdr])?;
2731    prim_car(&[car])
2732}
2733
2734/// (cdadr list) → value
2735///
2736/// Equivalent to (cdr (car (cdr list))).
2737///
2738/// **R4RS**: Library procedure
2739pub fn prim_cdadr(args: &[Value]) -> PrimitiveResult {
2740    if args.len() != 1 {
2741        return Err("cdadr requires exactly 1 argument".to_string());
2742    }
2743
2744    let cdr = prim_cdr(args)?;
2745    let car = prim_car(&[cdr])?;
2746    prim_cdr(&[car])
2747}
2748
2749// =============================================================================
2750// Additional List Utilities
2751// =============================================================================
2752
2753/// (last pair) → value
2754///
2755/// Returns the last element of a list (or the last cdr for improper lists).
2756///
2757/// **Extension**: Useful utility
2758pub fn prim_last(args: &[Value]) -> PrimitiveResult {
2759    if args.len() != 1 {
2760        return Err("last requires exactly 1 argument".to_string());
2761    }
2762
2763    let mut current = args[0].clone();
2764    loop {
2765        match current {
2766            Value::Nil => return Err("last: empty list".to_string()),
2767            Value::Pair(ref p) => {
2768                let pair = p.borrow();
2769                match &pair.cdr {
2770                    Value::Nil => {
2771                        // This is the last pair, return its car
2772                        return Ok(pair.car.clone());
2773                    }
2774                    Value::Pair(_) => {
2775                        // Continue to next pair
2776                        let next = pair.cdr.clone();
2777                        drop(pair);
2778                        current = next;
2779                    }
2780                    _other => {
2781                        // Improper list - return the last element before the improper cdr
2782                        return Ok(pair.car.clone());
2783                    }
2784                }
2785            }
2786            _ => return Err(format!("last: not a list: {:?}", args[0])),
2787        }
2788    }
2789}
2790
2791/// (zero? n) → boolean (alias check)
2792///
2793/// Returns #t if n is zero. Already implemented, but commonly used.
2794///
2795/// This is already in number primitives, just documenting it here.
2796
2797/// (null? obj) → boolean (alias check)
2798///
2799/// Returns #t if obj is the empty list. Already implemented.
2800///
2801/// This is already in list primitives, just documenting it here.
2802
2803// =============================================================================
2804// Format/Number Formatting Primitives (DSSSL)
2805// =============================================================================
2806
2807/// (format-number n format) → string
2808///
2809/// Formats a number according to format string.
2810/// Simplified implementation for basic number formatting.
2811///
2812/// **DSSSL**: Processing primitive
2813pub fn prim_format_number(args: &[Value]) -> PrimitiveResult {
2814    if args.len() != 2 {
2815        return Err("format-number requires exactly 2 arguments".to_string());
2816    }
2817
2818    let n = match &args[0] {
2819        Value::Integer(i) => *i,
2820        Value::Real(r) => *r as i64,
2821        _ => return Err(format!("format-number: not a number: {:?}", args[0])),
2822    };
2823
2824    let format = match &args[1] {
2825        Value::String(s) => s.as_str(),
2826        Value::Symbol(s) => s.as_ref(),
2827        _ => return Err(format!("format-number: invalid format: {:?}", args[1])),
2828    };
2829
2830    // Simple format implementation
2831    let result = match format {
2832        "1" | "decimal" => n.to_string(),
2833        "I" | "roman-upper" => {
2834            // Simple Roman numeral conversion (up to 20 for simplicity)
2835            let roman_str = match n {
2836                1 => "I",
2837                2 => "II",
2838                3 => "III",
2839                4 => "IV",
2840                5 => "V",
2841                6 => "VI",
2842                7 => "VII",
2843                8 => "VIII",
2844                9 => "IX",
2845                10 => "X",
2846                11 => "XI",
2847                12 => "XII",
2848                13 => "XIII",
2849                14 => "XIV",
2850                15 => "XV",
2851                16 => "XVI",
2852                17 => "XVII",
2853                18 => "XVIII",
2854                19 => "XIX",
2855                20 => "XX",
2856                _ => return Ok(Value::string(n.to_string())),
2857            };
2858            roman_str.to_string()
2859        }
2860        "i" | "roman-lower" => {
2861            let roman_str = match n {
2862                1 => "i",
2863                2 => "ii",
2864                3 => "iii",
2865                4 => "iv",
2866                5 => "v",
2867                6 => "vi",
2868                7 => "vii",
2869                8 => "viii",
2870                9 => "ix",
2871                10 => "x",
2872                11 => "xi",
2873                12 => "xii",
2874                13 => "xiii",
2875                14 => "xiv",
2876                15 => "xv",
2877                16 => "xvi",
2878                17 => "xvii",
2879                18 => "xviii",
2880                19 => "xix",
2881                20 => "xx",
2882                _ => return Ok(Value::string(n.to_string())),
2883            };
2884            roman_str.to_string()
2885        }
2886        "a" | "alpha-lower" => {
2887            // Convert to lowercase letter (1=a, 2=b, etc.)
2888            if n >= 1 && n <= 26 {
2889                ((b'a' + (n as u8 - 1)) as char).to_string()
2890            } else {
2891                n.to_string()
2892            }
2893        }
2894        "A" | "alpha-upper" => {
2895            // Convert to uppercase letter (1=A, 2=B, etc.)
2896            if n >= 1 && n <= 26 {
2897                ((b'A' + (n as u8 - 1)) as char).to_string()
2898            } else {
2899                n.to_string()
2900            }
2901        }
2902        _ => n.to_string(), // Default to decimal
2903    };
2904
2905    Ok(Value::string(result))
2906}
2907
2908/// (format-number-list numlist format) → string
2909///
2910/// Formats a list of numbers as a compound number (e.g., "1.2.3").
2911///
2912/// **DSSSL**: Processing primitive
2913pub fn prim_format_number_list(args: &[Value]) -> PrimitiveResult {
2914    if args.is_empty() || args.len() > 2 {
2915        return Err("format-number-list requires 1 or 2 arguments".to_string());
2916    }
2917
2918    let separator = if args.len() == 2 {
2919        match &args[1] {
2920            Value::String(s) => s.as_str(),
2921            Value::Symbol(s) => s.as_ref(),
2922            _ => ".",
2923        }
2924    } else {
2925        "."
2926    };
2927
2928    let mut numbers = Vec::new();
2929    let mut current = args[0].clone();
2930
2931    loop {
2932        match current {
2933            Value::Nil => break,
2934            Value::Pair(ref p) => {
2935                let pair = p.borrow();
2936                match &pair.car {
2937                    Value::Integer(n) => numbers.push(n.to_string()),
2938                    Value::Real(r) => numbers.push((*r as i64).to_string()),
2939                    _ => {
2940                        return Err("format-number-list: list must contain only numbers".to_string())
2941                    }
2942                }
2943                let cdr = pair.cdr.clone();
2944                drop(pair);
2945                current = cdr;
2946            }
2947            _ => return Err("format-number-list: not a proper list".to_string()),
2948        }
2949    }
2950
2951    Ok(Value::string(numbers.join(separator)))
2952}
2953
2954// =============================================================================
2955// Grove Query Primitives (DSSSL)
2956// =============================================================================
2957//
2958// These primitives provide XML tree navigation and querying.
2959// They form the core of DSSSL's grove model for document processing.
2960//
2961// **Implementation Status**: Basic API defined. Full grove integration
2962// with libxml2 will connect these to actual XML documents.
2963
2964/// (current-node) → node
2965///
2966/// Returns the current node being processed.
2967///
2968/// In DSSSL, the current node is an implicit context variable that changes
2969/// as the evaluator processes the document tree. When processing children
2970/// or a node-list, the current-node changes to each node in turn.
2971///
2972/// **DSSSL**: Grove primitive (context-dependent)
2973pub fn prim_current_node(args: &[Value]) -> PrimitiveResult {
2974    if !args.is_empty() {
2975        return Err("current-node requires no arguments".to_string());
2976    }
2977
2978    // Get the current evaluator context
2979    let ctx = crate::scheme::evaluator::get_evaluator_context()
2980        .ok_or_else(|| "current-node: no evaluator context available".to_string())?;
2981
2982    // Get the current node from the context
2983    let node = ctx
2984        .current_node
2985        .ok_or_else(|| "current-node: no current node set".to_string())?;
2986
2987    Ok(Value::Node(node))
2988}
2989
2990/// (node-list? obj) → boolean
2991///
2992/// Returns #t if obj is a node-list.
2993///
2994/// **DSSSL**: Grove primitive
2995pub fn prim_node_list_p(args: &[Value]) -> PrimitiveResult {
2996    if args.len() != 1 {
2997        return Err("node-list? requires exactly 1 argument".to_string());
2998    }
2999
3000    Ok(Value::bool(matches!(args[0], Value::NodeList(_))))
3001}
3002
3003/// (empty-node-list) → node-list
3004///
3005/// Returns an empty node-list.
3006///
3007/// **DSSSL**: Grove primitive
3008pub fn prim_empty_node_list(args: &[Value]) -> PrimitiveResult {
3009    if !args.is_empty() {
3010        return Err("empty-node-list requires no arguments".to_string());
3011    }
3012
3013    Ok(Value::node_list(Box::new(EmptyNodeList::new())))
3014}
3015
3016/// (node-list-empty? nl) → boolean
3017///
3018/// Returns #t if the node-list is empty.
3019///
3020/// **DSSSL**: Grove primitive
3021pub fn prim_node_list_empty_p(args: &[Value]) -> PrimitiveResult {
3022    if args.len() != 1 {
3023        return Err("node-list-empty? requires exactly 1 argument".to_string());
3024    }
3025
3026    match &args[0] {
3027        Value::NodeList(nl) => {
3028            // Now implemented with real grove support
3029            Ok(Value::bool(nl.is_empty()))
3030        }
3031        _ => Err(format!("node-list-empty?: not a node-list: {:?}", args[0])),
3032    }
3033}
3034
3035/// (node-list-length nl) → integer
3036///
3037/// Returns the number of nodes in the node-list.
3038///
3039/// **DSSSL**: Grove primitive
3040pub fn prim_node_list_length(args: &[Value]) -> PrimitiveResult {
3041    if args.len() != 1 {
3042        return Err("node-list-length requires exactly 1 argument".to_string());
3043    }
3044
3045    match &args[0] {
3046        Value::NodeList(nl) => {
3047            // Now implemented with real grove support
3048            Ok(Value::integer(nl.length() as i64))
3049        }
3050        Value::Node(_) => {
3051            // DSSSL: A single node can be treated as a single-element node-list
3052            Ok(Value::integer(1))
3053        }
3054        _ => Err(format!("node-list-length: not a node-list: {:?}", args[0])),
3055    }
3056}
3057
3058/// (node-list-first nl) → node
3059///
3060/// Returns the first node in a node-list.
3061/// Returns #f if the node-list is empty.
3062///
3063/// **DSSSL**: Grove primitive
3064pub fn prim_node_list_first(args: &[Value]) -> PrimitiveResult {
3065    if args.len() != 1 {
3066        return Err("node-list-first requires exactly 1 argument".to_string());
3067    }
3068
3069    match &args[0] {
3070        Value::NodeList(nl) => {
3071            // Now implemented with real grove support
3072            if let Some(node) = nl.first() {
3073                Ok(Value::node(node))
3074            } else {
3075                Ok(Value::bool(false))
3076            }
3077        }
3078        Value::Node(_) => {
3079            // DSSSL: A single node can be treated as a single-element node-list
3080            // The first element is the node itself
3081            Ok(args[0].clone())
3082        }
3083        _ => Err(format!("node-list-first: not a node-list: {:?}", args[0])),
3084    }
3085}
3086
3087/// (node-list-last nl) → node
3088///
3089/// Returns the last node in a node-list.
3090/// Returns #f if the node-list is empty.
3091///
3092/// **DSSSL**: Grove primitive (OpenJade extension)
3093pub fn prim_node_list_last(args: &[Value]) -> PrimitiveResult {
3094    if args.len() != 1 {
3095        return Err("node-list-last requires exactly 1 argument".to_string());
3096    }
3097
3098    match &args[0] {
3099        Value::NodeList(nl) => {
3100            let len = nl.length();
3101            if len == 0 {
3102                Ok(Value::bool(false))
3103            } else {
3104                // Get the last element by index (length - 1)
3105                let mut current_nl = nl.clone();
3106                let mut last_node = None;
3107
3108                // Iterate through the node-list to find the last node
3109                while let Some(node) = current_nl.first() {
3110                    last_node = Some(node);
3111                    let rest = current_nl.rest();
3112                    if rest.is_empty() {
3113                        break;
3114                    }
3115                    current_nl = std::rc::Rc::new(rest);
3116                }
3117
3118                if let Some(node) = last_node {
3119                    Ok(Value::node(node))
3120                } else {
3121                    Ok(Value::bool(false))
3122                }
3123            }
3124        }
3125        Value::Node(_) => {
3126            // DSSSL: A single node can be treated as a single-element node-list
3127            // The last element is the node itself
3128            Ok(args[0].clone())
3129        }
3130        _ => Err(format!("node-list-last: not a node-list: {:?}", args[0])),
3131    }
3132}
3133
3134/// (node-list->list nl) → list
3135///
3136/// Converts a node-list to a Scheme list of nodes.
3137/// This is a utility function (not in DSSSL standard).
3138///
3139/// **DSSSL**: Extension (utility)
3140pub fn prim_node_list_to_list(args: &[Value]) -> PrimitiveResult {
3141    if args.len() != 1 {
3142        return Err("node-list->list requires exactly 1 argument".to_string());
3143    }
3144
3145    match &args[0] {
3146        Value::NodeList(nl) => {
3147            // Build list by recursively traversing node-list
3148            fn build_list(nl: &std::rc::Rc<Box<dyn crate::grove::NodeList>>) -> Value {
3149                if nl.is_empty() {
3150                    Value::Nil
3151                } else if let Some(first) = nl.first() {
3152                    let rest_nl = nl.rest();
3153                    let rest_list = build_list(&std::rc::Rc::new(rest_nl));
3154                    Value::cons(Value::node(first), rest_list)
3155                } else {
3156                    Value::Nil
3157                }
3158            }
3159
3160            Ok(build_list(nl))
3161        }
3162        Value::Nil => {
3163            // Empty list treated as empty node-list
3164            Ok(Value::Nil)
3165        }
3166        Value::Pair(_) => {
3167            // Already a regular Scheme list - return as-is
3168            // This handles the case where node-list-map returns a regular list
3169            Ok(args[0].clone())
3170        }
3171        _ => Err(format!("node-list->list: not a node-list: {:?}", args[0])),
3172    }
3173}
3174
3175/// (node-list-rest nl) → node-list
3176///
3177/// Returns a node-list containing all but the first node.
3178/// Returns an empty node-list if the input is empty or has only one element.
3179///
3180/// **DSSSL**: Grove primitive
3181pub fn prim_node_list_rest(args: &[Value]) -> PrimitiveResult {
3182    if args.len() != 1 {
3183        return Err("node-list-rest requires exactly 1 argument".to_string());
3184    }
3185
3186    match &args[0] {
3187        Value::NodeList(_nl) => {
3188            // Now implemented with real grove support
3189            let rest = _nl.rest();
3190            Ok(Value::node_list(rest))
3191        }
3192        Value::Node(_) => {
3193            // DSSSL: A single node can be treated as a single-element node-list
3194            // The rest of a single-element list is an empty node-list
3195            Ok(Value::node_list(Box::new(EmptyNodeList::new())))
3196        }
3197        _ => Err(format!("node-list-rest: not a node-list: {:?}", args[0])),
3198    }
3199}
3200
3201/// (node-list-ref nl index) → node
3202///
3203/// Returns the node at the given index in the node-list (0-based).
3204/// Returns #f if index is out of bounds.
3205///
3206/// **DSSSL**: Grove primitive
3207pub fn prim_node_list_ref(args: &[Value]) -> PrimitiveResult {
3208    if args.len() != 2 {
3209        return Err("node-list-ref requires exactly 2 arguments".to_string());
3210    }
3211
3212    match &args[0] {
3213        Value::NodeList(nl) => {
3214            // Check index is an integer
3215            match &args[1] {
3216                Value::Integer(idx) => {
3217                    // Now implemented with real grove support
3218                    if *idx < 0 {
3219                        return Err("node-list-ref: index must be non-negative".to_string());
3220                    }
3221                    if let Some(node) = nl.get(*idx as usize) {
3222                        Ok(Value::node(node))
3223                    } else {
3224                        Ok(Value::bool(false))
3225                    }
3226                }
3227                _ => Err(format!("node-list-ref: index not an integer: {:?}", args[1])),
3228            }
3229        }
3230        _ => Err(format!("node-list-ref: not a node-list: {:?}", args[0])),
3231    }
3232}
3233
3234/// (node-list-reverse nl) → node-list
3235///
3236/// Returns a node-list with the nodes in reverse order.
3237///
3238/// **DSSSL**: Grove primitive
3239pub fn prim_node_list_reverse(args: &[Value]) -> PrimitiveResult {
3240    if args.len() != 1 {
3241        return Err("node-list-reverse requires exactly 1 argument".to_string());
3242    }
3243
3244    match &args[0] {
3245        Value::NodeList(nl) => {
3246            // Collect all nodes into a Vec and reverse it
3247            let mut nodes: Vec<Box<dyn Node>> = Vec::new();
3248            let mut current = nl.clone();
3249            while let Some(node) = current.first() {
3250                nodes.push(node);
3251                current = std::rc::Rc::new(current.rest());
3252                if current.length() == 0 {
3253                    break;
3254                }
3255            }
3256            nodes.reverse();
3257
3258            // Create a new node-list from the reversed Vec
3259            use crate::grove::VecNodeList;
3260            Ok(Value::node_list(Box::new(VecNodeList::new(nodes))))
3261        }
3262        _ => Err(format!("node-list-reverse: not a node-list: {:?}", args[0])),
3263    }
3264}
3265
3266/// (node-list-remove-duplicates nl) → node-list
3267///
3268/// Returns a node-list with duplicate nodes removed.
3269/// Node identity is based on the underlying document node, not object equality.
3270///
3271/// **DSSSL**: Grove primitive
3272pub fn prim_node_list_remove_duplicates(args: &[Value]) -> PrimitiveResult {
3273    if args.len() != 1 {
3274        return Err("node-list-remove-duplicates requires exactly 1 argument".to_string());
3275    }
3276
3277    match &args[0] {
3278        Value::NodeList(nl) => {
3279            let mut unique_nodes = Vec::new();
3280
3281            // Iterate through the node-list and collect unique nodes
3282            let mut index = 0;
3283            loop {
3284                if let Some(node) = nl.get(index) {
3285                    // Check if this node is already in unique_nodes
3286                    let is_duplicate = unique_nodes.iter().any(|existing: &Box<dyn crate::grove::Node>| {
3287                        existing.node_eq(node.as_ref())
3288                    });
3289
3290                    if !is_duplicate {
3291                        unique_nodes.push(node);
3292                    }
3293                    index += 1;
3294                } else {
3295                    break;
3296                }
3297            }
3298
3299            Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(unique_nodes))))
3300        }
3301        Value::Node(_) => {
3302            // DSSSL: A single node can be treated as a single-element node-list
3303            // A single node has no duplicates, so return it as a single-element node-list
3304            Ok(args[0].clone())
3305        }
3306        Value::Bool(false) => {
3307            // #f → empty node-list (graceful handling)
3308            Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3309        }
3310        _ => Err(format!("node-list-remove-duplicates: not a node-list: {:?}", args[0])),
3311    }
3312}
3313
3314/// (node-list-count nl) → integer
3315///
3316/// Returns the number of unique nodes in the node-list.
3317/// Equivalent to (node-list-length (node-list-remove-duplicates nl))
3318///
3319/// **DSSSL**: Grove primitive (defined in DSSSL spec)
3320pub fn prim_node_list_count(args: &[Value]) -> PrimitiveResult {
3321    // Implementation: node-list-length(node-list-remove-duplicates(nl))
3322    let unique_nl = prim_node_list_remove_duplicates(args)?;
3323    prim_node_list_length(&[unique_nl])
3324}
3325
3326/// (node-list-contains? node-list node) → boolean
3327///
3328/// Returns #t if the node is in the node-list, #f otherwise.
3329/// Uses node equality (same identity) for comparison.
3330///
3331/// **DSSSL**: Grove primitive (extension)
3332pub fn prim_node_list_contains_p(args: &[Value]) -> PrimitiveResult {
3333    if args.len() != 2 {
3334        return Err("node-list-contains? requires exactly 2 arguments".to_string());
3335    }
3336
3337    let search_node = match &args[1] {
3338        Value::Node(n) => n,
3339        _ => return Err(format!("node-list-contains?: second argument not a node: {:?}", args[1])),
3340    };
3341
3342    match &args[0] {
3343        Value::NodeList(nl) => {
3344            // Iterate through the node-list checking for equality
3345            let mut index = 0;
3346            loop {
3347                if let Some(node) = nl.get(index) {
3348                    // Check if nodes are equal using node_eq
3349                    if node.node_eq(search_node.as_ref().as_ref()) {
3350                        return Ok(Value::bool(true));
3351                    }
3352                    index += 1;
3353                } else {
3354                    break;
3355                }
3356            }
3357
3358            // Not found
3359            Ok(Value::bool(false))
3360        }
3361        Value::Nil => {
3362            // Empty list treated as empty node-list
3363            Ok(Value::bool(false))
3364        }
3365        Value::Pair(_) => {
3366            // Regular Scheme list - iterate through it
3367            let mut current = args[0].clone();
3368            loop {
3369                match current {
3370                    Value::Nil => return Ok(Value::bool(false)),
3371                    Value::Pair(ref p) => {
3372                        let (car, cdr) = {
3373                            let pair_data = p.borrow();
3374                            (pair_data.car.clone(), pair_data.cdr.clone())
3375                        };
3376
3377                        // Check if car is the node we're looking for
3378                        if let Value::Node(ref n) = car {
3379                            if n.as_ref().node_eq(search_node.as_ref().as_ref()) {
3380                                return Ok(Value::bool(true));
3381                            }
3382                        }
3383                        // Continue with cdr
3384                        current = cdr;
3385                    }
3386                    _ => return Err(format!("node-list-contains?: malformed list: {:?}", current)),
3387                }
3388            }
3389        }
3390        _ => Err(format!("node-list-contains?: first argument not a node-list: {:?}", args[0])),
3391    }
3392}
3393
3394/// (node? obj) → boolean
3395///
3396/// Returns #t if obj is a node.
3397///
3398/// **DSSSL**: Grove primitive
3399pub fn prim_node_p(args: &[Value]) -> PrimitiveResult {
3400    if args.len() != 1 {
3401        return Err("node? requires exactly 1 argument".to_string());
3402    }
3403
3404    Ok(Value::bool(matches!(args[0], Value::Node(_))))
3405}
3406
3407/// (gi node) → string | #f
3408///
3409/// Returns the generic identifier (element name) of a node.
3410/// Returns #f if the node is not an element node.
3411///
3412/// **DSSSL**: Grove primitive
3413pub fn prim_gi(args: &[Value]) -> PrimitiveResult {
3414    if args.len() != 1 {
3415        return Err("gi requires exactly 1 argument".to_string());
3416    }
3417
3418    match &args[0] {
3419        Value::Node(node) => {
3420            if let Some(gi) = node.gi() {
3421                Ok(Value::string(gi))
3422            } else {
3423                Ok(Value::bool(false))
3424            }
3425        }
3426        Value::NodeList(nl) => {
3427            // Handle single-element node-lists (OpenJade compatibility)
3428            if nl.length() == 1 {
3429                if let Some(node) = nl.first() {
3430                    if let Some(gi) = node.gi() {
3431                        Ok(Value::string(gi))
3432                    } else {
3433                        Ok(Value::bool(false))
3434                    }
3435                } else {
3436                    Ok(Value::bool(false))
3437                }
3438            } else {
3439                Err(format!("gi: node-list must have exactly 1 element, got {}", nl.length()))
3440            }
3441        }
3442        Value::Bool(false) => Ok(Value::bool(false)), // #f → #f (graceful handling)
3443        _ => Err(format!("gi: not a node: {:?}", args[0])),
3444    }
3445}
3446
3447/// (data node) → string | #f
3448///
3449/// Returns the text content of a node.
3450/// For text nodes, returns the text. For elements, returns concatenated descendant text.
3451/// Returns #f if the node has no data.
3452///
3453/// **DSSSL**: Grove primitive
3454pub fn prim_data(args: &[Value]) -> PrimitiveResult {
3455    if args.len() != 1 {
3456        return Err("data requires exactly 1 argument".to_string());
3457    }
3458
3459    match &args[0] {
3460        Value::Node(node) => {
3461            if let Some(data) = node.data() {
3462                // OpenJade returns text content as-is, WITHOUT whitespace normalization
3463                // This preserves leading/trailing spaces and all internal whitespace
3464                if data.is_empty() {
3465                    Ok(Value::bool(false))
3466                } else {
3467                    Ok(Value::string(data))
3468                }
3469            } else {
3470                Ok(Value::bool(false))
3471            }
3472        }
3473        Value::NodeList(nl) => {
3474            // DSSSL: node property functions can be called on node-lists
3475            // OpenJade behavior: concatenates data from ALL nodes in the list
3476            // and normalizes whitespace (collapses sequences to single space, trims)
3477            let mut result = String::new();
3478            let mut current = nl.clone();
3479
3480            loop {
3481                if let Some(node) = current.first() {
3482                    if let Some(data) = node.data() {
3483                        result.push_str(&data);
3484                    }
3485                    current = std::rc::Rc::new(current.rest());
3486                    if current.length() == 0 {
3487                        break;
3488                    }
3489                } else {
3490                    break;
3491                }
3492            }
3493
3494            if result.is_empty() {
3495                Ok(Value::bool(false))
3496            } else {
3497                // OpenJade concatenates data as-is, WITHOUT whitespace normalization
3498                Ok(Value::string(result))
3499            }
3500        }
3501        Value::Bool(false) => Ok(Value::bool(false)), // #f → #f (graceful handling)
3502        _ => Err(format!("data: not a node: {:?}", args[0])),
3503    }
3504}
3505
3506/// (attribute-string name node) → string | #f
3507///
3508/// Returns the value of the attribute with the given name.
3509/// Returns #f if the attribute does not exist or if node is #f.
3510/// Includes DTD default values.
3511///
3512/// **DSSSL**: Grove primitive
3513/// **OpenJade**: Gracefully handles #f nodes (returns #f)
3514pub fn prim_attribute_string(args: &[Value]) -> PrimitiveResult {
3515    if args.len() != 2 {
3516        return Err("attribute-string requires exactly 2 arguments".to_string());
3517    }
3518
3519    let name = match &args[0] {
3520        Value::String(s) => s.as_str(),
3521        _ => return Err(format!("attribute-string: name not a string: {:?}", args[0])),
3522    };
3523
3524    match &args[1] {
3525        Value::Node(node) => {
3526            if let Some(value) = node.attribute_string(name) {
3527                Ok(Value::string(value))
3528            } else {
3529                Ok(Value::bool(false))
3530            }
3531        }
3532        Value::NodeList(nl) => {
3533            // Handle single-element node-lists (OpenJade compatibility)
3534            // Empty node-lists return #f (no node to get attribute from)
3535            if nl.length() == 0 {
3536                Ok(Value::bool(false))
3537            } else if nl.length() == 1 {
3538                if let Some(node) = nl.first() {
3539                    if let Some(value) = node.attribute_string(name) {
3540                        Ok(Value::string(value))
3541                    } else {
3542                        Ok(Value::bool(false))
3543                    }
3544                } else {
3545                    Ok(Value::bool(false))
3546                }
3547            } else {
3548                Err(format!("attribute-string: node-list must have exactly 1 element, got {}", nl.length()))
3549            }
3550        }
3551        Value::Bool(false) => Ok(Value::bool(false)), // #f → #f (graceful handling)
3552        _ => Err(format!("attribute-string: not a node: {:?}", args[1])),
3553    }
3554}
3555
3556/// (children node) → node-list
3557///
3558/// Returns the child nodes of a node.
3559/// In DSSSL, this returns only element children, not text nodes.
3560///
3561/// **DSSSL**: Grove primitive
3562pub fn prim_children(args: &[Value]) -> PrimitiveResult {
3563    if args.len() != 1 {
3564        return Err("children requires exactly 1 argument".to_string());
3565    }
3566
3567    match &args[0] {
3568        Value::Node(node) => {
3569            let children = node.children();
3570            Ok(Value::node_list(children))
3571        }
3572        Value::NodeList(nl) => {
3573            // OpenJade semantics: When children is called on a multi-node NodeList,
3574            // it maps children over ALL nodes and returns a flattened NodeList
3575            // (see OpenJade primitive.cxx:3989-3991 - returns MapNodeListObj)
3576
3577            // Check if this is a singleton node-list
3578            if nl.length() == 1 {
3579                // Singleton - just get children of the single node
3580                if let Some(node) = nl.first() {
3581                    let children = node.children();
3582                    Ok(Value::node_list(children))
3583                } else {
3584                    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3585                }
3586            } else {
3587                // Multi-node list - map children over all nodes and flatten
3588                let mut all_children: Vec<Box<dyn crate::grove::Node>> = Vec::new();
3589                let mut index = 0;
3590                while let Some(node) = nl.get(index) {
3591                    let children = node.children();
3592                    // Flatten all children into the result
3593                    let mut child_index = 0;
3594                    while let Some(child) = children.get(child_index) {
3595                        all_children.push(child);
3596                        child_index += 1;
3597                    }
3598                    index += 1;
3599                }
3600                Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(all_children))))
3601            }
3602        }
3603        Value::Bool(false) | Value::Unspecified => {
3604            // Graceful handling: treat #f and unspecified as having no children
3605            Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3606        }
3607        _ => Err(format!("children: not a node or node-list: {:?}", args[0])),
3608    }
3609}
3610
3611/// (select-children gi-string node) → node-list
3612///
3613/// Returns all child elements of node whose gi matches gi-string.
3614/// This is a filtered version of children that only returns elements with a specific tag name.
3615///
3616/// **DSSSL**: Grove primitive
3617pub fn prim_select_children(args: &[Value]) -> PrimitiveResult {
3618    if args.len() != 2 {
3619        return Err("select-children requires exactly 2 arguments".to_string());
3620    }
3621
3622    let gi_name = match &args[0] {
3623        Value::String(s) => s.as_str(),
3624        _ => return Err(format!("select-children: first argument not a string: {:?}", args[0])),
3625    };
3626
3627    match &args[1] {
3628        Value::Node(node) => {
3629            // Get all children
3630            let children = node.children();
3631
3632            // Filter by gi
3633            let mut matching = Vec::new();
3634            let mut current = children;
3635            while !current.is_empty() {
3636                if let Some(child) = current.first() {
3637                    if let Some(child_gi) = child.gi() {
3638                        if child_gi == gi_name {
3639                            matching.push(child);
3640                        }
3641                    }
3642                    current = current.rest();
3643                } else {
3644                    break;
3645                }
3646            }
3647
3648            Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(matching))))
3649        }
3650        Value::NodeList(nl) => {
3651            // DSSSL: node property functions can be called on node-lists
3652            // Operates on the first node of the list
3653            if let Some(node) = nl.first() {
3654                // Recurse with the first node
3655                prim_select_children(&[args[0].clone(), Value::node(node)])
3656            } else {
3657                // Empty node-list -> empty result
3658                Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3659            }
3660        }
3661        Value::Bool(false) => {
3662            // #f → empty node-list (graceful handling)
3663            Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3664        }
3665        _ => Err(format!("select-children: second argument not a node or node-list: {:?}", args[1])),
3666    }
3667}
3668
3669/// (parent node) → node | #f
3670///
3671/// Returns the parent node of a node.
3672/// Returns #f if the node has no parent (i.e., it's the root).
3673///
3674/// **DSSSL**: Grove primitive
3675pub fn prim_parent(args: &[Value]) -> PrimitiveResult {
3676    if args.len() != 1 {
3677        return Err("parent requires exactly 1 argument".to_string());
3678    }
3679
3680    let node: Box<dyn crate::grove::Node> = match &args[0] {
3681        Value::Node(n) => n.clone_node(),
3682        Value::NodeList(nl) => {
3683            // DSSSL: node property functions can operate on node-lists (first node)
3684            if let Some(n) = nl.first() {
3685                n
3686            } else {
3687                return Ok(Value::bool(false)); // Empty node-list -> #f
3688            }
3689        }
3690        Value::Bool(false) => return Ok(Value::bool(false)), // #f → #f (graceful handling)
3691        _ => return Err(format!("parent: not a node or node-list: {:?}", args[0])),
3692    };
3693
3694    if let Some(parent) = node.parent() {
3695        Ok(Value::node(parent))
3696    } else {
3697        Ok(Value::bool(false))
3698    }
3699}
3700
3701/// (tree-root node) → node
3702///
3703/// Returns the root node of the tree containing the given node.
3704/// Walks up the parent chain until reaching the topmost node (which has no parent).
3705///
3706/// **DSSSL**: Grove property
3707pub fn prim_tree_root(args: &[Value]) -> PrimitiveResult {
3708    if args.len() != 1 {
3709        return Err("tree-root requires exactly 1 argument".to_string());
3710    }
3711
3712    match &args[0] {
3713        Value::Node(node) => {
3714            // Walk up parent chain until we find the root
3715            let mut current = node.clone_node();
3716            while let Some(parent) = current.parent() {
3717                current = parent;
3718            }
3719            Ok(Value::node(current))
3720        }
3721        Value::Bool(false) => Ok(Value::bool(false)), // #f → #f (graceful handling)
3722        _ => Err(format!("tree-root: not a node: {:?}", args[0])),
3723    }
3724}
3725
3726/// (ancestors node) → node-list
3727///
3728/// Returns a node-list containing all ancestors of the given node,
3729/// from the immediate parent up to (but not including) the root.
3730/// The node-list is ordered from nearest to farthest ancestor.
3731///
3732/// **DSSSL**: Grove primitive (OpenJade extension)
3733pub fn prim_ancestors(args: &[Value]) -> PrimitiveResult {
3734    if args.len() != 1 {
3735        return Err("ancestors requires exactly 1 argument".to_string());
3736    }
3737
3738    match &args[0] {
3739        Value::Node(node) => {
3740            // Collect all ancestors by walking up the parent chain
3741            let mut ancestor_nodes = Vec::new();
3742            let mut current = node.clone_node();
3743
3744            while let Some(parent) = current.parent() {
3745                ancestor_nodes.push(parent.clone_node());
3746                current = parent;
3747            }
3748
3749            Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(ancestor_nodes))))
3750        }
3751        Value::Bool(false) => Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))), // #f → empty node-list (graceful handling)
3752        _ => Err(format!("ancestors: not a node: {:?}", args[0])),
3753    }
3754}
3755
3756/// (id node) → string | #f
3757///
3758/// Returns the ID attribute value of a node.
3759/// Returns #f if the node has no ID.
3760///
3761/// **DSSSL**: Grove primitive
3762pub fn prim_id(args: &[Value]) -> PrimitiveResult {
3763    if args.len() != 1 {
3764        return Err("id requires exactly 1 argument".to_string());
3765    }
3766
3767    match &args[0] {
3768        Value::Node(node) => {
3769            if let Some(id) = node.id() {
3770                Ok(Value::string(id))
3771            } else {
3772                Ok(Value::bool(false))
3773            }
3774        }
3775        _ => Err(format!("id: not a node: {:?}", args[0])),
3776    }
3777}
3778
3779/// (ancestor gi node) → node | #f
3780///
3781/// Returns the nearest ancestor element with the given generic identifier.
3782/// Walks up the parent chain from the starting node until a matching ancestor is found.
3783/// Returns #f if no such ancestor exists.
3784///
3785/// **DSSSL**: Grove primitive
3786pub fn prim_ancestor(args: &[Value]) -> PrimitiveResult {
3787    if args.len() != 2 {
3788        return Err("ancestor requires exactly 2 arguments".to_string());
3789    }
3790
3791    // First argument is the gi name
3792    let gi_name = match &args[0] {
3793        Value::String(s) => s.clone(),
3794        _ => return Err(format!("ancestor: first argument not a string: {:?}", args[0])),
3795    };
3796
3797    // Second argument is the starting node
3798    let starting_node: Box<dyn crate::grove::Node> = match &args[1] {
3799        Value::Node(n) => n.clone_node(),
3800        Value::NodeList(nl) => {
3801            // DSSSL: node property functions can operate on node-lists (first node)
3802            if let Some(n) = nl.first() {
3803                n
3804            } else {
3805                return Ok(Value::bool(false)); // Empty node-list -> #f
3806            }
3807        }
3808        Value::Bool(false) => return Ok(Value::bool(false)), // #f → #f (graceful handling)
3809        _ => return Err(format!("ancestor: second argument not a node or node-list: {:?}", args[1])),
3810    };
3811
3812    // Walk up the parent chain looking for an ancestor with matching gi
3813    let mut current = starting_node.parent();
3814    while let Some(parent_node) = current {
3815        if let Some(parent_gi) = parent_node.gi() {
3816            if parent_gi == gi_name.as_str() {
3817                return Ok(Value::node(parent_node));
3818            }
3819        }
3820        current = parent_node.parent();
3821    }
3822
3823    // No matching ancestor found
3824    Ok(Value::bool(false))
3825}
3826
3827/// (descendants node) → node-list
3828///
3829/// Returns all descendant nodes of the given node in document order.
3830/// In DSSSL, this returns only element descendants, not text nodes.
3831///
3832/// **DSSSL**: Grove primitive
3833pub fn prim_descendants(args: &[Value]) -> PrimitiveResult {
3834    if args.len() != 1 {
3835        return Err("descendants requires exactly 1 argument".to_string());
3836    }
3837
3838    match &args[0] {
3839        Value::Node(node) => {
3840            let mut descendants = Vec::new();
3841            // node is &Rc<Box<dyn Node>>
3842            // node.as_ref() gives &Box<dyn Node>
3843            // node.as_ref().as_ref() gives &dyn Node
3844            collect_descendants(node.as_ref().as_ref(), &mut descendants);
3845            Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(descendants))))
3846        }
3847        Value::NodeList(nl) => {
3848            // DSSSL: operate on first node of node-list
3849            if let Some(node) = nl.first() {
3850                let mut descendants = Vec::new();
3851                // node is Box<dyn Node>, so &*node gives &dyn Node
3852                collect_descendants(&*node, &mut descendants);
3853                Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(descendants))))
3854            } else {
3855                Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3856            }
3857        }
3858        _ => Err(format!("descendants: not a node or node-list: {:?}", args[0])),
3859    }
3860}
3861
3862/// Helper function to recursively collect all descendant elements
3863fn collect_descendants(node: &dyn crate::grove::Node, result: &mut Vec<Box<dyn crate::grove::Node>>) {
3864    let children = node.children();
3865    let len = children.length();
3866
3867    for i in 0..len {
3868        if let Some(child) = children.get(i) {
3869            // Add this child to results
3870            result.push(child.clone_node());
3871
3872            // Recursively collect descendants of this child
3873            // child is Box<dyn Node>, so &*child gives us &dyn Node
3874            collect_descendants(&*child, result);
3875        }
3876    }
3877}
3878
3879/// (follow node) → node-list
3880///
3881/// Returns all following sibling nodes.
3882///
3883/// **DSSSL**: Grove primitive (stub)
3884pub fn prim_follow(args: &[Value]) -> PrimitiveResult {
3885    if args.len() != 1 {
3886        return Err("follow requires exactly 1 argument".to_string());
3887    }
3888
3889    // TODO: Implement following siblings traversal
3890    // For now, return empty node-list (stub)
3891    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3892}
3893
3894/// (preced node) → node-list
3895///
3896/// Returns all preceding sibling nodes.
3897///
3898/// **DSSSL**: Grove primitive (stub)
3899pub fn prim_preced(args: &[Value]) -> PrimitiveResult {
3900    if args.len() != 1 {
3901        return Err("preced requires exactly 1 argument".to_string());
3902    }
3903
3904    // TODO: Implement preceding siblings traversal
3905    // For now, return empty node-list (stub)
3906    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3907}
3908
3909/// (attributes node) → node-list
3910///
3911/// Returns the attributes of the given node as a node-list.
3912///
3913/// **DSSSL**: Grove primitive (stub)
3914pub fn prim_attributes(args: &[Value]) -> PrimitiveResult {
3915    if args.len() != 1 {
3916        return Err("attributes requires exactly 1 argument".to_string());
3917    }
3918
3919    // TODO: Implement attribute node-list
3920    // For now, return empty node-list (stub)
3921    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())))
3922}
3923
3924/// (select-elements node-list gi) → node-list
3925///
3926/// Returns a node-list containing only elements with the given gi.
3927///
3928/// **DSSSL**: Grove primitive (stub)
3929pub fn prim_select_elements(args: &[Value]) -> PrimitiveResult {
3930    if args.len() != 2 {
3931        return Err("select-elements requires exactly 2 arguments".to_string());
3932    }
3933
3934    let node_list = match &args[0] {
3935        Value::NodeList(nl) => nl,
3936        _ => return Err(format!("select-elements: first argument not a node-list: {:?}", args[0])),
3937    };
3938
3939    let gi_name = match &args[1] {
3940        Value::String(s) => s.as_str(),
3941        _ => return Err(format!("select-elements: second argument not a string: {:?}", args[1])),
3942    };
3943
3944    // Filter the node-list to only include elements with the specified gi
3945    let mut result_nodes = Vec::new();
3946
3947    // Iterate through the node list and collect matching nodes
3948    let mut index = 0;
3949    loop {
3950        if let Some(node) = node_list.get(index) {
3951            // Check if this node has the matching gi
3952            if let Some(node_gi) = node.gi() {
3953                if node_gi == gi_name {
3954                    result_nodes.push(node);
3955                }
3956            }
3957            index += 1;
3958        } else {
3959            break;
3960        }
3961    }
3962
3963    // Return filtered node-list
3964    Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(result_nodes))))
3965}
3966
3967/// (element-with-id id) → node | #f
3968///
3969/// Returns the element with the given ID attribute.
3970/// Uses the DTD to determine which attributes are of type ID.
3971/// Returns #f if no element has that ID.
3972///
3973/// **DSSSL**: Grove primitive
3974pub fn prim_element_with_id(args: &[Value]) -> PrimitiveResult {
3975    if args.is_empty() || args.len() > 2 {
3976        return Err("element-with-id requires 1 or 2 arguments".to_string());
3977    }
3978
3979    // First argument is the ID string
3980    let id = match &args[0] {
3981        Value::String(s) => s.as_str(),
3982        Value::Bool(false) => return Ok(Value::bool(false)), // #f → #f (graceful handling)
3983        _ => return Err(format!("element-with-id: first argument not a string: {:?}", args[0])),
3984    };
3985
3986    // Get the grove from evaluator context
3987    let ctx = crate::scheme::evaluator::get_evaluator_context()
3988        .ok_or_else(|| "element-with-id: no evaluator context available".to_string())?;
3989
3990    let grove = ctx.grove
3991        .as_ref()
3992        .ok_or_else(|| "element-with-id: no grove available".to_string())?;
3993
3994    // Use the grove's element_with_id method
3995    match grove.element_with_id(id) {
3996        Some(node) => Ok(Value::node(node)),
3997        None => Ok(Value::bool(false)),
3998    }
3999}
4000
4001// =============================================================================
4002// Additional Math Primitives (R4RS/R5RS)
4003// =============================================================================
4004
4005/// (expt base exponent) → number
4006///
4007/// Returns base raised to the power of exponent.
4008///
4009/// **R4RS**: Math primitive
4010pub fn prim_expt(args: &[Value]) -> PrimitiveResult {
4011    if args.len() != 2 {
4012        return Err("expt requires exactly 2 arguments".to_string());
4013    }
4014
4015    let base = match &args[0] {
4016        Value::Integer(n) => *n as f64,
4017        Value::Real(r) => *r,
4018        _ => return Err(format!("expt: not a number: {:?}", args[0])),
4019    };
4020
4021    let exponent = match &args[1] {
4022        Value::Integer(n) => *n as f64,
4023        Value::Real(r) => *r,
4024        _ => return Err(format!("expt: not a number: {:?}", args[1])),
4025    };
4026
4027    let result = base.powf(exponent);
4028
4029    // Return integer if both inputs were integers and result is whole
4030    if matches!(args[0], Value::Integer(_)) && matches!(args[1], Value::Integer(_)) && result.fract() == 0.0 {
4031        Ok(Value::integer(result as i64))
4032    } else {
4033        Ok(Value::real(result))
4034    }
4035}
4036
4037/// (sqrt n) → number
4038///
4039/// Returns the square root of n.
4040///
4041/// **R4RS**: Math primitive
4042pub fn prim_sqrt(args: &[Value]) -> PrimitiveResult {
4043    if args.len() != 1 {
4044        return Err("sqrt requires exactly 1 argument".to_string());
4045    }
4046
4047    let n = match &args[0] {
4048        Value::Integer(i) => *i as f64,
4049        Value::Real(r) => *r,
4050        _ => return Err(format!("sqrt: not a number: {:?}", args[0])),
4051    };
4052
4053    if n < 0.0 {
4054        return Err("sqrt: negative argument".to_string());
4055    }
4056
4057    Ok(Value::real(n.sqrt()))
4058}
4059
4060/// (sin n) → number
4061///
4062/// Returns the sine of n (in radians).
4063///
4064/// **R5RS**: Math primitive
4065pub fn prim_sin(args: &[Value]) -> PrimitiveResult {
4066    if args.len() != 1 {
4067        return Err("sin requires exactly 1 argument".to_string());
4068    }
4069
4070    let n = match &args[0] {
4071        Value::Integer(i) => *i as f64,
4072        Value::Real(r) => *r,
4073        _ => return Err(format!("sin: not a number: {:?}", args[0])),
4074    };
4075
4076    Ok(Value::real(n.sin()))
4077}
4078
4079/// (cos n) → number
4080///
4081/// Returns the cosine of n (in radians).
4082///
4083/// **R5RS**: Math primitive
4084pub fn prim_cos(args: &[Value]) -> PrimitiveResult {
4085    if args.len() != 1 {
4086        return Err("cos requires exactly 1 argument".to_string());
4087    }
4088
4089    let n = match &args[0] {
4090        Value::Integer(i) => *i as f64,
4091        Value::Real(r) => *r,
4092        _ => return Err(format!("cos: not a number: {:?}", args[0])),
4093    };
4094
4095    Ok(Value::real(n.cos()))
4096}
4097
4098/// (tan n) → number
4099///
4100/// Returns the tangent of n (in radians).
4101///
4102/// **R5RS**: Math primitive
4103pub fn prim_tan(args: &[Value]) -> PrimitiveResult {
4104    if args.len() != 1 {
4105        return Err("tan requires exactly 1 argument".to_string());
4106    }
4107
4108    let n = match &args[0] {
4109        Value::Integer(i) => *i as f64,
4110        Value::Real(r) => *r,
4111        _ => return Err(format!("tan: not a number: {:?}", args[0])),
4112    };
4113
4114    Ok(Value::real(n.tan()))
4115}
4116
4117/// (atan n) → number
4118///
4119/// Returns the arctangent of n (in radians).
4120///
4121/// **R5RS**: Math primitive
4122pub fn prim_atan(args: &[Value]) -> PrimitiveResult {
4123    if args.len() != 1 {
4124        return Err("atan requires exactly 1 argument".to_string());
4125    }
4126
4127    let n = match &args[0] {
4128        Value::Integer(i) => *i as f64,
4129        Value::Real(r) => *r,
4130        _ => return Err(format!("atan: not a number: {:?}", args[0])),
4131    };
4132
4133    Ok(Value::real(n.atan()))
4134}
4135
4136/// (log n) → number
4137///
4138/// Returns the natural logarithm of n.
4139///
4140/// **R5RS**: Math primitive
4141pub fn prim_log(args: &[Value]) -> PrimitiveResult {
4142    if args.len() != 1 {
4143        return Err("log requires exactly 1 argument".to_string());
4144    }
4145
4146    let n = match &args[0] {
4147        Value::Integer(i) => *i as f64,
4148        Value::Real(r) => *r,
4149        _ => return Err(format!("log: not a number: {:?}", args[0])),
4150    };
4151
4152    if n <= 0.0 {
4153        return Err("log: argument must be positive".to_string());
4154    }
4155
4156    Ok(Value::real(n.ln()))
4157}
4158
4159/// (exp n) → number
4160///
4161/// Returns e raised to the power of n.
4162///
4163/// **R5RS**: Math primitive
4164pub fn prim_exp(args: &[Value]) -> PrimitiveResult {
4165    if args.len() != 1 {
4166        return Err("exp requires exactly 1 argument".to_string());
4167    }
4168
4169    let n = match &args[0] {
4170        Value::Integer(i) => *i as f64,
4171        Value::Real(r) => *r,
4172        _ => return Err(format!("exp: not a number: {:?}", args[0])),
4173    };
4174
4175    Ok(Value::real(n.exp()))
4176}
4177
4178/// (asin n) → number
4179///
4180/// Returns the arcsine of n (in radians).
4181///
4182/// **R5RS**: Math primitive
4183pub fn prim_asin(args: &[Value]) -> PrimitiveResult {
4184    if args.len() != 1 {
4185        return Err("asin requires exactly 1 argument".to_string());
4186    }
4187
4188    let n = match &args[0] {
4189        Value::Integer(i) => *i as f64,
4190        Value::Real(r) => *r,
4191        _ => return Err(format!("asin: not a number: {:?}", args[0])),
4192    };
4193
4194    if n < -1.0 || n > 1.0 {
4195        return Err("asin: argument must be in range [-1, 1]".to_string());
4196    }
4197
4198    Ok(Value::real(n.asin()))
4199}
4200
4201/// (acos n) → number
4202///
4203/// Returns the arccosine of n (in radians).
4204///
4205/// **R5RS**: Math primitive
4206pub fn prim_acos(args: &[Value]) -> PrimitiveResult {
4207    if args.len() != 1 {
4208        return Err("acos requires exactly 1 argument".to_string());
4209    }
4210
4211    let n = match &args[0] {
4212        Value::Integer(i) => *i as f64,
4213        Value::Real(r) => *r,
4214        _ => return Err(format!("acos: not a number: {:?}", args[0])),
4215    };
4216
4217    if n < -1.0 || n > 1.0 {
4218        return Err("acos: argument must be in range [-1, 1]".to_string());
4219    }
4220
4221    Ok(Value::real(n.acos()))
4222}
4223
4224/// (exact? n) → boolean
4225///
4226/// Returns #t if n is exact (integer), #f otherwise.
4227///
4228/// **R5RS**: Number predicate
4229pub fn prim_exact_p(args: &[Value]) -> PrimitiveResult {
4230    if args.len() != 1 {
4231        return Err("exact? requires exactly 1 argument".to_string());
4232    }
4233
4234    Ok(Value::bool(matches!(args[0], Value::Integer(_))))
4235}
4236
4237/// (inexact? n) → boolean
4238///
4239/// Returns #t if n is inexact (real), #f otherwise.
4240///
4241/// **R5RS**: Number predicate
4242pub fn prim_inexact_p(args: &[Value]) -> PrimitiveResult {
4243    if args.len() != 1 {
4244        return Err("inexact? requires exactly 1 argument".to_string());
4245    }
4246
4247    Ok(Value::bool(matches!(args[0], Value::Real(_))))
4248}
4249
4250/// (exact->inexact n) → number
4251///
4252/// Converts an exact number to inexact.
4253///
4254/// **R5RS**: Number conversion
4255pub fn prim_exact_to_inexact(args: &[Value]) -> PrimitiveResult {
4256    if args.len() != 1 {
4257        return Err("exact->inexact requires exactly 1 argument".to_string());
4258    }
4259
4260    match &args[0] {
4261        Value::Integer(i) => Ok(Value::real(*i as f64)),
4262        Value::Real(r) => Ok(Value::real(*r)), // Already inexact
4263        _ => Err(format!("exact->inexact: not a number: {:?}", args[0])),
4264    }
4265}
4266
4267/// (inexact->exact n) → number
4268///
4269/// Converts an inexact number to exact.
4270///
4271/// **R5RS**: Number conversion
4272pub fn prim_inexact_to_exact(args: &[Value]) -> PrimitiveResult {
4273    if args.len() != 1 {
4274        return Err("inexact->exact requires exactly 1 argument".to_string());
4275    }
4276
4277    match &args[0] {
4278        Value::Real(r) => {
4279            if r.fract() == 0.0 && r.is_finite() {
4280                Ok(Value::integer(*r as i64))
4281            } else {
4282                Err("inexact->exact: cannot convert non-integer to exact".to_string())
4283            }
4284        }
4285        Value::Integer(i) => Ok(Value::integer(*i)), // Already exact
4286        _ => Err(format!("inexact->exact: not a number: {:?}", args[0])),
4287    }
4288}
4289
4290// =============================================================================
4291// Entity and Notation Primitives (DSSSL) - Stubs
4292// =============================================================================
4293
4294/// (entity-system-id name) → string | #f
4295pub fn prim_entity_system_id(_args: &[Value]) -> PrimitiveResult {
4296    Ok(Value::bool(false)) // Stub
4297}
4298
4299/// (entity-public-id name) → string | #f
4300pub fn prim_entity_public_id(_args: &[Value]) -> PrimitiveResult {
4301    Ok(Value::bool(false)) // Stub
4302}
4303
4304/// (notation-system-id name) → string | #f
4305pub fn prim_notation_system_id(_args: &[Value]) -> PrimitiveResult {
4306    Ok(Value::bool(false)) // Stub
4307}
4308
4309/// (notation-public-id name) → string | #f
4310pub fn prim_notation_public_id(_args: &[Value]) -> PrimitiveResult {
4311    Ok(Value::bool(false)) // Stub
4312}
4313
4314// =============================================================================
4315// DSSSL Type Stubs (Quantities, Colors, Addresses, Glyphs, Spacing)
4316// =============================================================================
4317
4318/// (color color-space ...) → color
4319pub fn prim_color(_args: &[Value]) -> PrimitiveResult {
4320    Ok(Value::Unspecified) // Stub - return placeholder color
4321}
4322
4323/// (color-space name) → color-space
4324pub fn prim_color_space(_args: &[Value]) -> PrimitiveResult {
4325    Ok(Value::Unspecified) // Stub
4326}
4327
4328/// (color-space? obj) → boolean
4329pub fn prim_color_space_p(args: &[Value]) -> PrimitiveResult {
4330    if args.len() != 1 {
4331        return Err("color-space? requires exactly 1 argument".to_string());
4332    }
4333    Ok(Value::bool(false)) // Stub - no color-space type yet
4334}
4335
4336/// (display-space ...) → display-space
4337pub fn prim_display_space(_args: &[Value]) -> PrimitiveResult {
4338    Ok(Value::Unspecified) // Stub
4339}
4340
4341/// (display-space? obj) → boolean
4342pub fn prim_display_space_p(args: &[Value]) -> PrimitiveResult {
4343    if args.len() != 1 {
4344        return Err("display-space? requires exactly 1 argument".to_string());
4345    }
4346    Ok(Value::bool(false)) // Stub
4347}
4348
4349/// (inline-space ...) → inline-space
4350pub fn prim_inline_space(_args: &[Value]) -> PrimitiveResult {
4351    Ok(Value::Unspecified) // Stub
4352}
4353
4354/// (inline-space? obj) → boolean
4355pub fn prim_inline_space_p(args: &[Value]) -> PrimitiveResult {
4356    if args.len() != 1 {
4357        return Err("inline-space? requires exactly 1 argument".to_string());
4358    }
4359    Ok(Value::bool(false)) // Stub
4360}
4361
4362/// (glyph-id name) → glyph-id
4363pub fn prim_glyph_id(_args: &[Value]) -> PrimitiveResult {
4364    Ok(Value::Unspecified) // Stub
4365}
4366
4367/// (glyph-id? obj) → boolean
4368pub fn prim_glyph_id_p(args: &[Value]) -> PrimitiveResult {
4369    if args.len() != 1 {
4370        return Err("glyph-id? requires exactly 1 argument".to_string());
4371    }
4372    Ok(Value::bool(false)) // Stub
4373}
4374
4375/// (glyph-subst-table name) → glyph-subst-table
4376pub fn prim_glyph_subst_table(_args: &[Value]) -> PrimitiveResult {
4377    Ok(Value::Unspecified) // Stub
4378}
4379
4380/// (glyph-subst-table? obj) → boolean
4381pub fn prim_glyph_subst_table_p(args: &[Value]) -> PrimitiveResult {
4382    if args.len() != 1 {
4383        return Err("glyph-subst-table? requires exactly 1 argument".to_string());
4384    }
4385    Ok(Value::bool(false)) // Stub
4386}
4387
4388/// (glyph-subst table glyph) → glyph
4389pub fn prim_glyph_subst(_args: &[Value]) -> PrimitiveResult {
4390    Ok(Value::Unspecified) // Stub
4391}
4392
4393/// (current-node-address) → address
4394pub fn prim_current_node_address(_args: &[Value]) -> PrimitiveResult {
4395    Ok(Value::Unspecified) // Stub
4396}
4397
4398/// (address-local? addr) → boolean
4399pub fn prim_address_local_p(args: &[Value]) -> PrimitiveResult {
4400    if args.len() != 1 {
4401        return Err("address-local? requires exactly 1 argument".to_string());
4402    }
4403    Ok(Value::bool(false)) // Stub
4404}
4405
4406/// (address-visited? addr) → boolean
4407pub fn prim_address_visited_p(args: &[Value]) -> PrimitiveResult {
4408    if args.len() != 1 {
4409        return Err("address-visited? requires exactly 1 argument".to_string());
4410    }
4411    Ok(Value::bool(false)) // Stub
4412}
4413
4414// =============================================================================
4415// Node-list Utility Stubs
4416// =============================================================================
4417
4418/// (node-list node ...) → node-list
4419pub fn prim_node_list(_args: &[Value]) -> PrimitiveResult {
4420    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
4421}
4422
4423/// (node-list-map proc node-list) → list
4424///
4425/// Applies proc to each node in node-list and returns a list of the results.
4426/// Unlike map, this returns a regular list, not a node-list.
4427///
4428/// **DSSSL**: Grove primitive
4429/// **NOTE**: Implemented as a special form in the evaluator
4430pub fn prim_node_list_map(_args: &[Value]) -> PrimitiveResult {
4431    Err("node-list-map should be handled as a special form in the evaluator".to_string())
4432}
4433
4434/// (node-property prop-name node) → value
4435pub fn prim_node_property(_args: &[Value]) -> PrimitiveResult {
4436    Ok(Value::Unspecified) // Stub
4437}
4438
4439/// (match-element? pattern node) → boolean
4440pub fn prim_match_element_p(_args: &[Value]) -> PrimitiveResult {
4441    Ok(Value::bool(false)) // Stub
4442}
4443
4444/// (named-node-list? obj) → boolean
4445pub fn prim_named_node_list_p(args: &[Value]) -> PrimitiveResult {
4446    if args.len() != 1 {
4447        return Err("named-node-list? requires exactly 1 argument".to_string());
4448    }
4449    Ok(Value::bool(false)) // Stub
4450}
4451
4452/// (node-list=? nl1 nl2) → boolean
4453///
4454/// Returns #t if nl1 and nl2 contain the same nodes in the same order.
4455/// A single node is treated as a singleton node-list.
4456/// Comparison is by node identity (pointer equality).
4457///
4458/// **DSSSL**: Grove primitive
4459pub fn prim_node_list_eq(args: &[Value]) -> PrimitiveResult {
4460    if args.len() != 2 {
4461        return Err("node-list=? requires exactly 2 arguments".to_string());
4462    }
4463
4464    // Handle all combinations of Node and NodeList
4465    match (&args[0], &args[1]) {
4466        // Two single nodes: compare by node identity
4467        (Value::Node(n1), Value::Node(n2)) => {
4468            Ok(Value::bool(n1.as_ref().as_ref().node_eq(n2.as_ref().as_ref())))
4469        }
4470
4471        // Single node vs node-list: node-list must have length 1 and contain same node
4472        (Value::Node(n), Value::NodeList(nl)) | (Value::NodeList(nl), Value::Node(n)) => {
4473            if nl.length() != 1 {
4474                return Ok(Value::bool(false));
4475            }
4476            if let Some(nl_node) = nl.get(0) {
4477                // Compare by checking if the nodes are the same
4478                Ok(Value::bool(n.as_ref().as_ref().node_eq(nl_node.as_ref())))
4479            } else {
4480                Ok(Value::bool(false))
4481            }
4482        }
4483
4484        // Two node-lists: compare element by element
4485        (Value::NodeList(nl1), Value::NodeList(nl2)) => {
4486            let len1 = nl1.length();
4487            let len2 = nl2.length();
4488
4489            // Different lengths → not equal
4490            if len1 != len2 {
4491                return Ok(Value::bool(false));
4492            }
4493
4494            // Compare each node
4495            for i in 0..len1 {
4496                let node1 = nl1.get(i);
4497                let node2 = nl2.get(i);
4498
4499                match (node1, node2) {
4500                    (Some(n1), Some(n2)) => {
4501                        // Compare nodes by identity
4502                        if !n1.node_eq(n2.as_ref()) {
4503                            return Ok(Value::bool(false));
4504                        }
4505                    }
4506                    (None, None) => continue,
4507                    _ => return Ok(Value::bool(false)),
4508                }
4509            }
4510
4511            Ok(Value::bool(true))
4512        }
4513
4514        _ => Err(format!("node-list=?: arguments must be nodes or node-lists: {:?}, {:?}", args[0], args[1])),
4515    }
4516}
4517
4518// =============================================================================
4519// Grove Extended Primitives - Stubs
4520// =============================================================================
4521
4522/// (first-sibling? node) → boolean
4523///
4524/// Returns #t if the node is the first sibling with the same element name.
4525/// If no argument is provided, uses current-node.
4526///
4527/// **DSSSL**: Grove primitive
4528pub fn prim_first_sibling_p(args: &[Value]) -> PrimitiveResult {
4529    if args.len() > 1 {
4530        return Err("first-sibling? requires 0 or 1 arguments".to_string());
4531    }
4532
4533    // Get the node to check
4534    let node = if args.is_empty() {
4535        // Use current-node if no argument provided
4536        let ctx = crate::scheme::evaluator::get_evaluator_context()
4537            .ok_or_else(|| "first-sibling?: no evaluator context available".to_string())?;
4538        ctx.current_node
4539            .ok_or_else(|| "first-sibling?: no current node set".to_string())?
4540    } else {
4541        match &args[0] {
4542            Value::Node(n) => n.clone(),
4543            Value::NodeList(nl) => {
4544                // Single-element node-list
4545                if nl.length() != 1 {
4546                    return Err("first-sibling?: argument must be a single node".to_string());
4547                }
4548                if let Some(n) = nl.first() {
4549                    std::rc::Rc::new(n)
4550                } else {
4551                    return Err("first-sibling?: empty node-list".to_string());
4552                }
4553            }
4554            _ => return Err(format!("first-sibling?: not a node: {:?}", args[0])),
4555        }
4556    };
4557
4558    // Get the node's GI (element name)
4559    let gi = match node.gi() {
4560        Some(g) => g,
4561        None => return Ok(Value::bool(true)), // Non-elements are considered first siblings
4562    };
4563
4564    // Get parent to find all siblings
4565    let parent = match node.parent() {
4566        Some(p) => p,
4567        None => return Ok(Value::bool(true)), // Root node is always first sibling
4568    };
4569
4570    // Check all siblings before this node
4571    let siblings = parent.children();
4572    for i in 0..siblings.length() {
4573        if let Some(sibling) = siblings.get(i) {
4574            // If we found ourselves, we're the first sibling with this GI
4575            if sibling.node_eq(node.as_ref().as_ref()) {
4576                return Ok(Value::bool(true));
4577            }
4578
4579            // If we found an earlier sibling with the same GI, we're not first
4580            if let Some(sibling_gi) = sibling.gi() {
4581                if sibling_gi == gi {
4582                    return Ok(Value::bool(false));
4583                }
4584            }
4585        }
4586    }
4587
4588    // Should not reach here, but treat as first sibling
4589    Ok(Value::bool(true))
4590}
4591
4592/// (last-sibling? node) → boolean
4593///
4594/// Returns #t if the node is the last sibling among nodes with the same element name.
4595/// If called with no arguments, uses the current node from the context.
4596///
4597/// **DSSSL**: Grove primitive
4598/// **OpenJade**: Checks if node is last among siblings with same GI
4599pub fn prim_last_sibling_p(args: &[Value]) -> PrimitiveResult {
4600    if args.len() > 1 {
4601        return Err("last-sibling? requires 0 or 1 arguments".to_string());
4602    }
4603
4604    let node = if args.is_empty() {
4605        // Use current node (from context)
4606        return Err("last-sibling? with no arguments requires current-node context (not yet implemented)".to_string());
4607    } else {
4608        match &args[0] {
4609            Value::Node(n) => n.clone(),
4610            Value::NodeList(nl) => {
4611                if let Some(node) = nl.first() {
4612                    if nl.length() != 1 {
4613                        return Err(format!("last-sibling?: node-list must have exactly 1 element, got {}", nl.length()));
4614                    }
4615                    std::rc::Rc::new(node)
4616                } else {
4617                    return Ok(Value::bool(false)); // Empty node-list
4618                }
4619            }
4620            _ => return Err("last-sibling? requires node or singleton node-list".to_string()),
4621        }
4622    };
4623
4624    // Get parent and check if this node is the last child with the same element name
4625    if let Some(parent) = node.parent() {
4626        let my_gi = node.gi();
4627
4628        // Strategy: Find the last sibling with same GI, then check if it's us
4629        let siblings = parent.children();
4630        let mut current_nl = siblings;
4631        let mut last_with_same_gi: Option<Box<dyn crate::grove::Node>> = None;
4632
4633        // Find the last sibling with matching GI
4634        loop {
4635            if let Some(child) = current_nl.first() {
4636                if child.gi() == my_gi {
4637                    last_with_same_gi = Some(child);
4638                }
4639                current_nl = current_nl.rest();
4640            } else {
4641                break;
4642            }
4643        }
4644
4645        // Check if we are that last sibling using node_eq
4646        if let Some(last) = last_with_same_gi {
4647            Ok(Value::bool(node.node_eq(last.as_ref())))
4648        } else {
4649            Ok(Value::bool(false))
4650        }
4651    } else {
4652        // No parent - can't determine siblings
4653        Ok(Value::bool(false))
4654    }
4655}
4656
4657/// (child-number node) → integer
4658///
4659/// Returns the 1-based position of the node among siblings with the same element name.
4660/// If called with no arguments, uses the current node.
4661///
4662/// **DSSSL**: Grove primitive (DSSSL §9)
4663/// **OpenJade**: Returns 1-based count (internal childNumber is 0-based, but adds 1)
4664pub fn prim_child_number(args: &[Value]) -> PrimitiveResult {
4665    if args.is_empty() || args.len() > 1 {
4666        return Err("child-number requires 0 or 1 arguments".to_string());
4667    }
4668
4669    let node = if args.is_empty() {
4670        // Use current node (from context - but we don't have context here, so error)
4671        return Err("child-number with no arguments requires current-node context (not yet implemented)".to_string());
4672    } else {
4673        match &args[0] {
4674            Value::Node(n) => n.clone(),
4675            Value::NodeList(nl) => {
4676                if let Some(node) = nl.first() {
4677                    if nl.length() != 1 {
4678                        return Err(format!("child-number: node-list must have exactly 1 element, got {}", nl.length()));
4679                    }
4680                    std::rc::Rc::new(node)
4681                } else {
4682                    return Err("child-number: empty node-list".to_string());
4683                }
4684            }
4685            _ => return Err(format!("child-number: not a node: {:?}", args[0])),
4686        }
4687    };
4688
4689    // Get the element name of this node
4690    let gi = match node.gi() {
4691        Some(name) => name.to_string(),
4692        None => return Ok(Value::bool(false)), // Not an element
4693    };
4694
4695    // Get the parent to find siblings
4696    let parent = match node.parent() {
4697        Some(p) => p,
4698        None => return Ok(Value::integer(1)), // Document element → child-number = 1
4699    };
4700
4701    // Count siblings with the same GI that come before this node
4702    let mut count = 0;
4703    let mut current = parent.children();
4704
4705    loop {
4706        if let Some(sibling) = current.first() {
4707            // Check if this is the target node (use node_eq for proper identity comparison)
4708            if sibling.node_eq(&**node) {
4709                // Found our node, return count + 1 (1-based)
4710                return Ok(Value::integer((count + 1) as i64));
4711            }
4712
4713            // Not the target node, check if it has the same GI
4714            if let Some(sibling_gi) = sibling.gi() {
4715                if sibling_gi == gi {
4716                    count += 1;
4717                }
4718            }
4719
4720            // Move to next sibling
4721            current = current.rest();
4722            if current.length() == 0 {
4723                break;
4724            }
4725        } else {
4726            break;
4727        }
4728    }
4729
4730    // If we didn't find the node, something is wrong
4731    Err("child-number: node not found among siblings".to_string())
4732}
4733
4734/// (element-number node) → integer
4735pub fn prim_element_number(_args: &[Value]) -> PrimitiveResult {
4736    Ok(Value::integer(0)) // Stub
4737}
4738
4739/// (inherited-attribute-string name node) → string | #f
4740pub fn prim_inherited_attribute_string(_args: &[Value]) -> PrimitiveResult {
4741    Ok(Value::bool(false)) // Stub
4742}
4743
4744/// (absolute-first-sibling? node) → boolean
4745pub fn prim_absolute_first_sibling_p(_args: &[Value]) -> PrimitiveResult {
4746    Ok(Value::bool(false)) // Stub
4747}
4748
4749/// (absolute-last-sibling? node) → boolean
4750pub fn prim_absolute_last_sibling_p(_args: &[Value]) -> PrimitiveResult {
4751    Ok(Value::bool(false)) // Stub
4752}
4753
4754/// (ancestor-child-number gi node) → integer
4755pub fn prim_ancestor_child_number(_args: &[Value]) -> PrimitiveResult {
4756    Ok(Value::integer(0)) // Stub
4757}
4758
4759/// (element-number-list gi-list node) → list
4760pub fn prim_element_number_list(_args: &[Value]) -> PrimitiveResult {
4761    Ok(Value::Nil) // Stub
4762}
4763
4764/// (hierarchical-number gi-list node) → string
4765pub fn prim_hierarchical_number(_args: &[Value]) -> PrimitiveResult {
4766    Ok(Value::string("1".to_string())) // Stub
4767}
4768
4769/// (hierarchical-number-recursive gi-list node) → string
4770pub fn prim_hierarchical_number_recursive(_args: &[Value]) -> PrimitiveResult {
4771    Ok(Value::string("1".to_string())) // Stub
4772}
4773
4774/// (have-ancestor? gi node) → boolean
4775pub fn prim_have_ancestor_p(_args: &[Value]) -> PrimitiveResult {
4776    Ok(Value::bool(false)) // Stub
4777}
4778
4779/// (all-element-number node) → integer
4780pub fn prim_all_element_number(_args: &[Value]) -> PrimitiveResult {
4781    Ok(Value::integer(0)) // Stub
4782}
4783
4784// =============================================================================
4785// Character and Language Primitives
4786// =============================================================================
4787
4788/// (char-property prop-name char) → value
4789pub fn prim_char_property(_args: &[Value]) -> PrimitiveResult {
4790    Ok(Value::Unspecified) // Stub
4791}
4792
4793/// (char-script-case char script-case-list) → char
4794pub fn prim_char_script_case(_args: &[Value]) -> PrimitiveResult {
4795    Ok(Value::Char('a')) // Stub - return dummy char
4796}
4797
4798/// (language name country) → language
4799pub fn prim_language(_args: &[Value]) -> PrimitiveResult {
4800    Ok(Value::Unspecified) // Stub
4801}
4802
4803/// (with-language lang proc) → value
4804pub fn prim_with_language(_args: &[Value]) -> PrimitiveResult {
4805    Ok(Value::Unspecified) // Stub
4806}
4807
4808// =============================================================================
4809// Basic Grove Extended
4810// =============================================================================
4811
4812/// (first-child-gi node gi) → boolean
4813pub fn prim_first_child_gi(_args: &[Value]) -> PrimitiveResult {
4814    Ok(Value::bool(false)) // Stub
4815}
4816
4817/// (inherited-element-attribute-string gi name node) → string | #f
4818pub fn prim_inherited_element_attribute_string(_args: &[Value]) -> PrimitiveResult {
4819    Ok(Value::bool(false)) // Stub
4820}
4821
4822// =============================================================================
4823// Debug and External Procedures
4824// =============================================================================
4825
4826/// (debug obj) → obj
4827pub fn prim_debug(args: &[Value]) -> PrimitiveResult {
4828    if args.is_empty() {
4829        return Err("debug requires at least 1 argument".to_string());
4830    }
4831
4832    // OpenJade: (debug string) - outputs string to stdout and passes it through
4833    // Evaluate the argument and print it
4834    match &args[0] {
4835        Value::String(s) => {
4836            println!("{}", s);
4837        }
4838        other => {
4839            // For non-strings, print their representation
4840            println!("{:?}", other);
4841        }
4842    }
4843
4844    Ok(args[0].clone())
4845}
4846
4847/// (external-procedure name) → procedure
4848pub fn prim_external_procedure(_args: &[Value]) -> PrimitiveResult {
4849    Ok(Value::Unspecified) // Stub
4850}
4851
4852/// (read-entity name) → string
4853pub fn prim_read_entity(_args: &[Value]) -> PrimitiveResult {
4854    Ok(Value::string("".to_string())) // Stub
4855}
4856
4857// =============================================================================
4858// Entity/Notation Extended Stubs
4859// =============================================================================
4860
4861/// (entity-address name) → address
4862pub fn prim_entity_address(_args: &[Value]) -> PrimitiveResult {
4863    Ok(Value::Unspecified) // Stub
4864}
4865
4866/// (entity-attribute-string attr-name entity-name node) → string | #f
4867pub fn prim_entity_attribute_string(_args: &[Value]) -> PrimitiveResult {
4868    Ok(Value::bool(false)) // Stub
4869}
4870
4871/// (entity-generated-system-id name node) → string | #f
4872pub fn prim_entity_generated_system_id(_args: &[Value]) -> PrimitiveResult {
4873    Ok(Value::bool(false)) // Stub
4874}
4875
4876/// (entity-name-normalize name node) → string
4877pub fn prim_entity_name_normalize(_args: &[Value]) -> PrimitiveResult {
4878    Ok(Value::string("".to_string())) // Stub
4879}
4880
4881/// (entity-notation name node) → string | #f
4882pub fn prim_entity_notation(_args: &[Value]) -> PrimitiveResult {
4883    Ok(Value::bool(false)) // Stub
4884}
4885
4886/// (entity-text name node) → string | #f
4887pub fn prim_entity_text(_args: &[Value]) -> PrimitiveResult {
4888    Ok(Value::bool(false)) // Stub
4889}
4890
4891/// (entity-type name node) → symbol
4892pub fn prim_entity_type(_args: &[Value]) -> PrimitiveResult {
4893    Ok(Value::symbol("unknown")) // Stub
4894}
4895
4896/// (notation-generated-system-id name node) → string | #f
4897pub fn prim_notation_generated_system_id(_args: &[Value]) -> PrimitiveResult {
4898    Ok(Value::bool(false)) // Stub
4899}
4900
4901/// (general-name-normalize name node) → string
4902pub fn prim_general_name_normalize(_args: &[Value]) -> PrimitiveResult {
4903    Ok(Value::string("".to_string())) // Stub
4904}
4905
4906/// (sgml-document-address doc-name node-id) → address
4907pub fn prim_sgml_document_address(_args: &[Value]) -> PrimitiveResult {
4908    Ok(Value::Unspecified) // Stub
4909}
4910
4911/// (declaration node) → node | #f
4912pub fn prim_declaration(_args: &[Value]) -> PrimitiveResult {
4913    Ok(Value::bool(false)) // Stub
4914}
4915
4916/// (dtd node) → node | #f
4917pub fn prim_dtd(_args: &[Value]) -> PrimitiveResult {
4918    Ok(Value::bool(false)) // Stub
4919}
4920
4921/// (sgml-declaration node) → node | #f
4922pub fn prim_sgml_declaration(_args: &[Value]) -> PrimitiveResult {
4923    Ok(Value::bool(false)) // Stub
4924}
4925
4926/// (document-element node) → node | #f
4927pub fn prim_document_element(_args: &[Value]) -> PrimitiveResult {
4928    Ok(Value::bool(false)) // Stub
4929}
4930
4931/// (prolog node) → node-list
4932pub fn prim_prolog(_args: &[Value]) -> PrimitiveResult {
4933    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
4934}
4935
4936/// (epilog node) → node-list
4937pub fn prim_epilog(_args: &[Value]) -> PrimitiveResult {
4938    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
4939}
4940
4941// define-language is now a special form in evaluator.rs
4942
4943/// declare-default-language: Set default language (DSSSL declaration, no-op)
4944pub fn prim_declare_default_language(_args: &[Value]) -> PrimitiveResult {
4945    Ok(Value::Unspecified)
4946}
4947
4948// declare-flow-object-class is now a special form in evaluator.rs
4949
4950/// declare-characteristic: Declare a characteristic (DSSSL declaration, no-op)
4951pub fn prim_declare_characteristic(_args: &[Value]) -> PrimitiveResult {
4952    Ok(Value::Unspecified)
4953}
4954
4955/// (origin-to-subnode-rel-forest-addr node subnode) → address
4956pub fn prim_origin_to_subnode_rel_forest_addr(_args: &[Value]) -> PrimitiveResult {
4957    Ok(Value::Unspecified) // Stub
4958}
4959
4960// =============================================================================
4961// Node-list Extended Stubs
4962// =============================================================================
4963
4964/// (named-node name named-node-list) → node | #f
4965pub fn prim_named_node(_args: &[Value]) -> PrimitiveResult {
4966    Ok(Value::bool(false)) // Stub
4967}
4968
4969/// (named-node-list-names named-node-list) → list
4970pub fn prim_named_node_list_names(_args: &[Value]) -> PrimitiveResult {
4971    Ok(Value::Nil) // Stub
4972}
4973
4974/// (named-node-list-normalize named-node-list tree-root? node-list) → named-node-list
4975pub fn prim_named_node_list_normalize(_args: &[Value]) -> PrimitiveResult {
4976    Ok(Value::Unspecified) // Stub
4977}
4978
4979/// (node-list-address node-list) → address
4980pub fn prim_node_list_address(_args: &[Value]) -> PrimitiveResult {
4981    Ok(Value::Unspecified) // Stub
4982}
4983
4984/// (node-list-error msg node-list) → node-list
4985pub fn prim_node_list_error(_args: &[Value]) -> PrimitiveResult {
4986    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
4987}
4988
4989/// (node-list-no-order node-list) → node-list
4990pub fn prim_node_list_no_order(_args: &[Value]) -> PrimitiveResult {
4991    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
4992}
4993
4994/// (select-by-class class-name node-list) → node-list
4995pub fn prim_select_by_class(_args: &[Value]) -> PrimitiveResult {
4996    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
4997}
4998
4999/// (node-list-union nl1 nl2 ...) → node-list
5000///
5001/// Returns a node-list containing all unique nodes from all argument node-lists.
5002/// Node identity (not equality) is used to determine uniqueness.
5003/// The order is the document order of the first occurrence of each node.
5004///
5005/// **DSSSL**: Grove primitive
5006pub fn prim_node_list_union(args: &[Value]) -> PrimitiveResult {
5007    if args.is_empty() {
5008        return Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new())));
5009    }
5010
5011    // Collect all nodes from all node-lists, maintaining document order
5012    let mut seen_ids = std::collections::HashSet::new();
5013    let mut result_nodes = Vec::new();
5014
5015    for arg in args {
5016        match arg {
5017            Value::NodeList(nl) => {
5018                // Iterate through this node-list
5019                for i in 0..nl.length() {
5020                    if let Some(node) = nl.get(i) {
5021                        let node_id = node.node_id();
5022                        // Only add if we haven't seen this node before
5023                        if !seen_ids.contains(&node_id) {
5024                            seen_ids.insert(node_id);
5025                            result_nodes.push(node);
5026                        }
5027                    }
5028                }
5029            }
5030            Value::Node(n) => {
5031                // Single node treated as singleton node-list
5032                let node_id = n.as_ref().node_id();
5033                if !seen_ids.contains(&node_id) {
5034                    seen_ids.insert(node_id);
5035                    result_nodes.push(n.as_ref().clone_node());
5036                }
5037            }
5038            Value::Pair(_) => {
5039                // Regular Scheme list (from node-list-map) - iterate through it
5040                let mut current = arg.clone();
5041                loop {
5042                    match current {
5043                        Value::Nil => break,
5044                        Value::Pair(ref p) => {
5045                            let (car, cdr) = {
5046                                let pair_data = p.borrow();
5047                                (pair_data.car.clone(), pair_data.cdr.clone())
5048                            };
5049
5050                            // Extract the node from car
5051                            match car {
5052                                Value::Node(ref n) => {
5053                                    let node_id = n.as_ref().node_id();
5054                                    if !seen_ids.contains(&node_id) {
5055                                        seen_ids.insert(node_id);
5056                                        result_nodes.push(n.as_ref().clone_node());
5057                                    }
5058                                }
5059                                Value::Bool(false) => {
5060                                    // #f in the list - skip it (graceful handling)
5061                                }
5062                                _ => return Err(format!("node-list-union: list contains non-node: {:?}", car)),
5063                            }
5064
5065                            current = cdr;
5066                        }
5067                        _ => return Err(format!("node-list-union: malformed list: {:?}", current)),
5068                    }
5069                }
5070            }
5071            Value::Nil => {
5072                // Empty list - skip
5073            }
5074            _ => return Err(format!("node-list-union: argument not a node-list: {:?}", arg)),
5075        }
5076    }
5077
5078    Ok(Value::node_list(Box::new(crate::grove::VecNodeList::new(result_nodes))))
5079}
5080
5081/// (node-list-intersection nl1 nl2 ...) → node-list
5082pub fn prim_node_list_intersection(_args: &[Value]) -> PrimitiveResult {
5083    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
5084}
5085
5086/// (node-list-difference nl1 nl2 ...) → node-list
5087pub fn prim_node_list_difference(_args: &[Value]) -> PrimitiveResult {
5088    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
5089}
5090
5091/// (node-list-symmetrical-difference nl1 nl2) → node-list
5092pub fn prim_node_list_symmetrical_difference(_args: &[Value]) -> PrimitiveResult {
5093    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
5094}
5095
5096/// (node-list-union-map proc nl) → node-list
5097pub fn prim_node_list_union_map(_args: &[Value]) -> PrimitiveResult {
5098    Ok(Value::node_list(Box::new(crate::grove::EmptyNodeList::new()))) // Stub
5099}
5100
5101// =============================================================================
5102// Processing Extended Stubs
5103// =============================================================================
5104
5105/// (process-children-trim) → sosofo
5106pub fn prim_process_children_trim(_args: &[Value]) -> PrimitiveResult {
5107    Ok(Value::Sosofo) // Stub
5108}
5109
5110/// (process-element-with-id id) → sosofo
5111pub fn prim_process_element_with_id(_args: &[Value]) -> PrimitiveResult {
5112    Ok(Value::Sosofo) // Stub
5113}
5114
5115/// (process-first-descendant gi-list) → sosofo
5116pub fn prim_process_first_descendant(_args: &[Value]) -> PrimitiveResult {
5117    Ok(Value::Sosofo) // Stub
5118}
5119
5120/// (process-matching-children gi-list) → sosofo
5121pub fn prim_process_matching_children(_args: &[Value]) -> PrimitiveResult {
5122    Ok(Value::Sosofo) // Stub
5123}
5124
5125/// (merge-style styles...) → style
5126pub fn prim_merge_style(_args: &[Value]) -> PrimitiveResult {
5127    Ok(Value::Unspecified) // Stub
5128}
5129
5130/// (map-constructor constructor-name) → procedure
5131pub fn prim_map_constructor(_args: &[Value]) -> PrimitiveResult {
5132    Ok(Value::Unspecified) // Stub
5133}
5134
5135/// (with-mode mode proc) → sosofo
5136pub fn prim_with_mode(_args: &[Value]) -> PrimitiveResult {
5137    Ok(Value::Sosofo) // Stub
5138}
5139
5140/// (current-mode) → symbol | #f
5141pub fn prim_current_mode(_args: &[Value]) -> PrimitiveResult {
5142    Ok(Value::bool(false)) // Stub
5143}
5144
5145// =============================================================================
5146// Sosofo/Page Layout Extended Stubs
5147// =============================================================================
5148
5149/// (current-node-page-number-sosofo) → sosofo
5150pub fn prim_current_node_page_number_sosofo(_args: &[Value]) -> PrimitiveResult {
5151    Ok(Value::Sosofo) // Stub
5152}
5153
5154/// (page-number-sosofo) → sosofo
5155pub fn prim_page_number_sosofo(_args: &[Value]) -> PrimitiveResult {
5156    Ok(Value::Sosofo) // Stub
5157}
5158
5159/// (sosofo-discard-labeled label sosofo) → sosofo
5160pub fn prim_sosofo_discard_labeled(_args: &[Value]) -> PrimitiveResult {
5161    Ok(Value::Sosofo) // Stub
5162}
5163
5164/// (sosofo-label label sosofo) → sosofo
5165pub fn prim_sosofo_label(_args: &[Value]) -> PrimitiveResult {
5166    Ok(Value::Sosofo) // Stub
5167}
5168
5169/// (idref-address string node) → address
5170pub fn prim_idref_address(_args: &[Value]) -> PrimitiveResult {
5171    Ok(Value::Unspecified) // Stub
5172}
5173
5174/// (hytime-linkend node) → string | #f
5175pub fn prim_hytime_linkend(_args: &[Value]) -> PrimitiveResult {
5176    Ok(Value::bool(false)) // Stub
5177}
5178
5179/// (if-first-page then-sosofo else-sosofo) → sosofo
5180pub fn prim_if_first_page(_args: &[Value]) -> PrimitiveResult {
5181    Ok(Value::Sosofo) // Stub
5182}
5183
5184/// (if-front-page then-sosofo else-sosofo) → sosofo
5185pub fn prim_if_front_page(_args: &[Value]) -> PrimitiveResult {
5186    Ok(Value::Sosofo) // Stub
5187}
5188
5189/// (sosofo-contains-node? sosofo node) → boolean
5190pub fn prim_sosofo_contains_node_p(_args: &[Value]) -> PrimitiveResult {
5191    Ok(Value::bool(false)) // Stub
5192}
5193
5194/// (set-visited! address) → unspecified
5195pub fn prim_set_visited(_args: &[Value]) -> PrimitiveResult {
5196    Ok(Value::Unspecified) // Stub
5197}
5198
5199/// (label-length label) → length
5200pub fn prim_label_length(_args: &[Value]) -> PrimitiveResult {
5201    Ok(Value::integer(0)) // Stub
5202}
5203
5204/// (label-distance label) → length
5205pub fn prim_label_distance(_args: &[Value]) -> PrimitiveResult {
5206    Ok(Value::integer(0)) // Stub
5207}
5208
5209// =============================================================================
5210// Quantity/Dimension Stubs
5211// =============================================================================
5212
5213/// (quantity->number quantity) → number
5214pub fn prim_quantity_to_number(_args: &[Value]) -> PrimitiveResult {
5215    Ok(Value::integer(0)) // Stub
5216}
5217
5218/// (number->quantity number [unit]) → quantity
5219pub fn prim_number_to_quantity(_args: &[Value]) -> PrimitiveResult {
5220    Ok(Value::Unspecified) // Stub
5221}
5222
5223/// (quantity-convert quantity unit) → quantity
5224pub fn prim_quantity_convert(_args: &[Value]) -> PrimitiveResult {
5225    Ok(Value::Unspecified) // Stub
5226}
5227
5228/// (device-length quantity) → number
5229pub fn prim_device_length(_args: &[Value]) -> PrimitiveResult {
5230    Ok(Value::integer(0)) // Stub
5231}
5232
5233/// (quantity->string quantity) → string
5234pub fn prim_quantity_to_string(_args: &[Value]) -> PrimitiveResult {
5235    Ok(Value::string("0pt".to_string())) // Stub
5236}
5237
5238/// (table-unit n) → length
5239pub fn prim_table_unit(_args: &[Value]) -> PrimitiveResult {
5240    Ok(Value::Unspecified) // Stub
5241}
5242
5243/// (display-size) → (width . height)
5244pub fn prim_display_size(_args: &[Value]) -> PrimitiveResult {
5245    // Return a cons pair (width . height)
5246    Ok(Value::Unspecified) // Stub
5247}
5248
5249// =============================================================================
5250// SGML-specific Stubs
5251// =============================================================================
5252
5253/// (sgml-parse system-id) → grove
5254pub fn prim_sgml_parse(_args: &[Value]) -> PrimitiveResult {
5255    Ok(Value::Unspecified) // Stub
5256}
5257
5258// =============================================================================
5259// Time Utility Stubs
5260// =============================================================================
5261
5262/// (time<? t1 t2) → boolean
5263pub fn prim_time_lt(_args: &[Value]) -> PrimitiveResult {
5264    Ok(Value::bool(false)) // Stub
5265}
5266
5267/// (time<=? t1 t2) → boolean
5268pub fn prim_time_le(_args: &[Value]) -> PrimitiveResult {
5269    Ok(Value::bool(false)) // Stub
5270}
5271
5272/// (time>? t1 t2) → boolean
5273pub fn prim_time_gt(_args: &[Value]) -> PrimitiveResult {
5274    Ok(Value::bool(false)) // Stub
5275}
5276
5277/// (time>=? t1 t2) → boolean
5278pub fn prim_time_ge(_args: &[Value]) -> PrimitiveResult {
5279    Ok(Value::bool(false)) // Stub
5280}
5281
5282// =============================================================================
5283// Processing and Sosofo Extended Stubs
5284// =============================================================================
5285
5286/// (next-match) → sosofo
5287pub fn prim_next_match(_args: &[Value]) -> PrimitiveResult {
5288    Ok(Value::Sosofo) // Stub
5289}
5290
5291/// (style? obj) → boolean
5292pub fn prim_style_p(args: &[Value]) -> PrimitiveResult {
5293    if args.len() != 1 {
5294        return Err("style? requires exactly 1 argument".to_string());
5295    }
5296    Ok(Value::bool(false)) // Stub
5297}
5298
5299// =============================================================================
5300// Sosofo Primitives (DSSSL)
5301// =============================================================================
5302//
5303// Sosofo = Specification Of a Sequence Of Flow Objects
5304// Core abstraction for document transformation in DSSSL.
5305//
5306// **Implementation Status**: Basic API defined. Full implementation will
5307// generate actual output when connected to backends.
5308
5309/// (sosofo? obj) → boolean
5310///
5311/// Returns #t if obj is a sosofo.
5312///
5313/// **DSSSL**: Processing primitive
5314pub fn prim_sosofo_p(args: &[Value]) -> PrimitiveResult {
5315    if args.len() != 1 {
5316        return Err("sosofo? requires exactly 1 argument".to_string());
5317    }
5318
5319    Ok(Value::bool(matches!(args[0], Value::Sosofo)))
5320}
5321
5322/// (empty-sosofo) → sosofo
5323///
5324/// Returns an empty sosofo (generates no output).
5325///
5326/// **DSSSL**: Processing primitive
5327pub fn prim_empty_sosofo(args: &[Value]) -> PrimitiveResult {
5328    if !args.is_empty() {
5329        return Err("empty-sosofo requires no arguments".to_string());
5330    }
5331
5332    Ok(Value::Sosofo)
5333}
5334
5335/// (literal str) → sosofo
5336///
5337/// Creates a sosofo that outputs the given string.
5338/// Appends the string to the backend's current output buffer.
5339///
5340/// **DSSSL**: Processing primitive
5341pub fn prim_literal(args: &[Value]) -> PrimitiveResult {
5342    if args.len() != 1 {
5343        return Err("literal requires exactly 1 argument".to_string());
5344    }
5345
5346    match &args[0] {
5347        Value::String(s) => {
5348            // Get backend from evaluator context and append text
5349            if let Some(ctx) = crate::scheme::evaluator::get_evaluator_context() {
5350                if let Some(ref backend) = ctx.backend {
5351                    backend.borrow_mut()
5352                        .formatting_instruction(s)
5353                        .map_err(|e| format!("literal: backend error: {}", e))?;
5354                }
5355            }
5356            Ok(Value::Sosofo)
5357        }
5358        _ => Err(format!("literal: not a string: {:?}", args[0])),
5359    }
5360}
5361
5362/// (sosofo-append sosofo ...) → sosofo
5363///
5364/// Concatenates multiple sosofos into a single sosofo.
5365///
5366/// **DSSSL**: Processing primitive
5367pub fn prim_sosofo_append(args: &[Value]) -> PrimitiveResult {
5368    // Check all arguments are sosofos or unspecified (which we treat as empty sosofo)
5369    for arg in args {
5370        if !matches!(arg, Value::Sosofo | Value::Unspecified) {
5371            return Err(format!("sosofo-append: not a sosofo: {:?}", arg));
5372        }
5373    }
5374
5375    // Return a sosofo (placeholder for now)
5376    // In practice, sosofos are just markers - the actual output happens via backend
5377    Ok(Value::Sosofo)
5378}
5379
5380// =============================================================================
5381// Processing Primitives (DSSSL)
5382// =============================================================================
5383//
5384// These primitives handle document tree traversal and template application.
5385// They are central to DSSSL's processing model.
5386
5387/// (process-children) → sosofo
5388///
5389/// Processes the children of the current node.
5390///
5391/// In DSSSL, this looks up the appropriate rule for each child node
5392/// and evaluates it, then appends all the resulting sosofos.
5393///
5394/// **Implementation Note**: This is a simplified stub implementation.
5395/// Full DSSSL requires rule/mode matching which will be added later.
5396/// For now, this returns empty-sosofo.
5397///
5398/// **DSSSL**: Processing primitive
5399pub fn prim_process_children(args: &[Value]) -> PrimitiveResult {
5400    if !args.is_empty() {
5401        return Err("process-children requires no arguments".to_string());
5402    }
5403
5404    // TODO: Implement full processing model
5405    // This should:
5406    // 1. Get children of current node
5407    // 2. For each child, find matching rule/mode
5408    // 3. Evaluate the rule with child as current-node
5409    // 4. Append all resulting sosofos
5410
5411    // For now, return empty sosofo
5412    Ok(Value::Sosofo)
5413}
5414
5415/// (process-node-list node-list) → sosofo
5416///
5417/// Processes each node in the given node-list.
5418///
5419/// Similar to process-children, but processes an explicit node-list
5420/// rather than the current node's children.
5421///
5422/// **Implementation Note**: This is a simplified stub implementation.
5423/// Full DSSSL requires rule/mode matching which will be added later.
5424/// For now, this returns empty-sosofo.
5425///
5426/// **DSSSL**: Processing primitive
5427pub fn prim_process_node_list(args: &[Value]) -> PrimitiveResult {
5428    if args.len() != 1 {
5429        return Err("process-node-list requires exactly 1 argument".to_string());
5430    }
5431
5432    // Verify argument is a node-list
5433    if !matches!(args[0], Value::NodeList(_)) {
5434        return Err(format!("process-node-list: not a node-list: {:?}", args[0]));
5435    }
5436
5437    // TODO: Implement full processing model
5438    // This should:
5439    // 1. For each node in the list
5440    // 2. Find matching rule/mode
5441    // 3. Evaluate the rule with node as current-node
5442    // 4. Append all resulting sosofos
5443
5444    // For now, return empty sosofo
5445    Ok(Value::Sosofo)
5446}
5447
5448/// (make flow-object-type ...) → sosofo
5449///
5450/// Creates a flow object (output element).
5451///
5452/// This is the primary way templates generate output. Common flow objects:
5453/// - `entity` - Creates an output file (system-id:, content)
5454/// - `formatting-instruction` - Outputs raw text (data:)
5455/// - `sequence` - Groups flow objects
5456/// - `literal` - Outputs text (handled separately as a function)
5457///
5458/// **Implementation Note**: This is a stub implementation.
5459/// Full implementation requires FotBuilder integration in the evaluator.
5460/// For now, this returns a sosofo marker.
5461///
5462/// **DSSSL**: Flow object construction primitive
5463///
5464/// **Example**:
5465/// ```scheme
5466/// (make entity
5467///   system-id: "Output.java"
5468///   (literal "public class Foo { }"))
5469/// ```
5470pub fn prim_make(args: &[Value]) -> PrimitiveResult {
5471    if args.is_empty() {
5472        return Err("make requires at least a flow object type".to_string());
5473    }
5474
5475    // First argument should be a symbol (flow object type)
5476    match &args[0] {
5477        Value::Symbol(fo_type) => {
5478            // TODO: Implement actual flow object creation
5479            // This should:
5480            // 1. Parse keyword arguments (system-id:, data:, etc.)
5481            // 2. Evaluate body sosofo
5482            // 3. Call appropriate FotBuilder method
5483            // 4. Return sosofo
5484
5485            // For now, just validate it's a known type and return sosofo
5486            match fo_type.as_ref() {
5487                "entity" | "formatting-instruction" | "sequence" | "paragraph" => {
5488                    Ok(Value::Sosofo)
5489                }
5490                _ => Err(format!("make: unknown flow object type: {}", fo_type)),
5491            }
5492        }
5493        _ => Err(format!("make: first argument must be a symbol: {:?}", args[0])),
5494    }
5495}
5496
5497// =============================================================================
5498// String Utility Primitives (DSSSL Extensions)
5499// =============================================================================
5500
5501/// (string-equiv? str1 str2) → boolean
5502///
5503/// Case-insensitive string comparison.
5504/// Returns #t if strings are equal ignoring case.
5505///
5506/// **DSSSL**: Extension primitive
5507pub fn prim_string_equiv_p(args: &[Value]) -> PrimitiveResult {
5508    if args.len() != 2 {
5509        return Err("string-equiv? requires exactly 2 arguments".to_string());
5510    }
5511
5512    let s1 = match &args[0] {
5513        Value::String(s) => s.to_lowercase(),
5514        _ => return Err(format!("string-equiv?: not a string: {:?}", args[0])),
5515    };
5516
5517    let s2 = match &args[1] {
5518        Value::String(s) => s.to_lowercase(),
5519        _ => return Err(format!("string-equiv?: not a string: {:?}", args[1])),
5520    };
5521
5522    Ok(Value::bool(s1 == s2))
5523}
5524
5525// =============================================================================
5526// Time Primitives (DSSSL Extensions)
5527// =============================================================================
5528//
5529// Time values represent points in time. For now, these are stubs.
5530
5531/// (time) → time
5532///
5533/// Returns the current time.
5534/// Stub implementation - returns unspecified for now.
5535///
5536/// **DSSSL**: Extension primitive
5537pub fn prim_time(args: &[Value]) -> PrimitiveResult {
5538    if !args.is_empty() {
5539        return Err("time requires no arguments".to_string());
5540    }
5541
5542    // Stub: return unspecified
5543    // Real implementation would return a time object
5544    Ok(Value::Unspecified)
5545}
5546
5547/// (time->string t format) → string
5548///
5549/// Converts time to string according to format.
5550/// Stub implementation.
5551///
5552/// **DSSSL**: Extension primitive
5553pub fn prim_time_to_string(args: &[Value]) -> PrimitiveResult {
5554    if args.len() != 2 {
5555        return Err("time->string requires exactly 2 arguments".to_string());
5556    }
5557
5558    // Stub: return empty string
5559    Ok(Value::string("".to_string()))
5560}
5561
5562// =============================================================================
5563// Language Primitives (DSSSL)
5564// =============================================================================
5565//
5566// Language objects represent natural languages (for i18n).
5567// Stub implementations for now.
5568
5569/// (language? obj) → boolean
5570///
5571/// Returns #t if obj is a language.
5572/// Stub - no language type yet.
5573///
5574/// **DSSSL**: Type predicate
5575pub fn prim_language_p(args: &[Value]) -> PrimitiveResult {
5576    if args.len() != 1 {
5577        return Err("language? requires exactly 1 argument".to_string());
5578    }
5579
5580    // Stub: no language type, always return #f
5581    Ok(Value::bool(false))
5582}
5583
5584/// (current-language) → language
5585///
5586/// Returns the current language.
5587/// Stub implementation.
5588///
5589/// **DSSSL**: Extension primitive
5590pub fn prim_current_language(args: &[Value]) -> PrimitiveResult {
5591    if !args.is_empty() {
5592        return Err("current-language requires no arguments".to_string());
5593    }
5594
5595    // Stub: return unspecified
5596    Ok(Value::Unspecified)
5597}
5598
5599// =============================================================================
5600// Registration
5601// =============================================================================
5602
5603/// Register all list primitives in an environment
5604pub fn register_list_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5605    env.define("car", Value::primitive("car", prim_car));
5606    env.define("cdr", Value::primitive("cdr", prim_cdr));
5607    env.define("cons", Value::primitive("cons", prim_cons));
5608    env.define("list", Value::primitive("list", prim_list));
5609    env.define("null?", Value::primitive("null?", prim_null_p));
5610    env.define("pair?", Value::primitive("pair?", prim_pair_p));
5611    env.define("list?", Value::primitive("list?", prim_list_p));
5612    env.define("length", Value::primitive("length", prim_length));
5613    env.define("append", Value::primitive("append", prim_append));
5614    env.define("reverse", Value::primitive("reverse", prim_reverse));
5615    env.define("list-tail", Value::primitive("list-tail", prim_list_tail));
5616    env.define("list-ref", Value::primitive("list-ref", prim_list_ref));
5617    env.define("memq", Value::primitive("memq", prim_memq));
5618    env.define("memv", Value::primitive("memv", prim_memv));
5619    env.define("member", Value::primitive("member", prim_member));
5620    env.define("assq", Value::primitive("assq", prim_assq));
5621    env.define("assv", Value::primitive("assv", prim_assv));
5622    env.define("assoc", Value::primitive("assoc", prim_assoc));
5623    // cXXr combinations
5624    env.define("cadr", Value::primitive("cadr", prim_cadr));
5625    env.define("caddr", Value::primitive("caddr", prim_caddr));
5626    env.define("cadddr", Value::primitive("cadddr", prim_cadddr));
5627    env.define("caar", Value::primitive("caar", prim_caar));
5628    env.define("cddr", Value::primitive("cddr", prim_cddr));
5629    env.define("cdar", Value::primitive("cdar", prim_cdar));
5630    env.define("caaar", Value::primitive("caaar", prim_caaar));
5631    env.define("cdaar", Value::primitive("cdaar", prim_cdaar));
5632    env.define("cadar", Value::primitive("cadar", prim_cadar));
5633    env.define("cddar", Value::primitive("cddar", prim_cddar));
5634    env.define("caadr", Value::primitive("caadr", prim_caadr));
5635    env.define("cdadr", Value::primitive("cdadr", prim_cdadr));
5636    // Additional list utilities
5637    env.define("last", Value::primitive("last", prim_last));
5638}
5639
5640/// Register all number primitives in an environment
5641pub fn register_number_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5642    // Arithmetic
5643    env.define("+", Value::primitive("+", prim_add));
5644    env.define("-", Value::primitive("-", prim_subtract));
5645    env.define("*", Value::primitive("*", prim_multiply));
5646    env.define("/", Value::primitive("/", prim_divide));
5647    env.define("quotient", Value::primitive("quotient", prim_quotient));
5648    env.define("remainder", Value::primitive("remainder", prim_remainder));
5649    env.define("modulo", Value::primitive("modulo", prim_modulo));
5650
5651    // Comparison
5652    env.define("=", Value::primitive("=", prim_num_eq));
5653    env.define("<", Value::primitive("<", prim_num_lt));
5654    env.define(">", Value::primitive(">", prim_num_gt));
5655    env.define("<=", Value::primitive("<=", prim_num_le));
5656    env.define(">=", Value::primitive(">=", prim_num_ge));
5657
5658    // Type predicates
5659    env.define("number?", Value::primitive("number?", prim_number_p));
5660    env.define("integer?", Value::primitive("integer?", prim_integer_p));
5661    env.define("real?", Value::primitive("real?", prim_real_p));
5662    env.define("zero?", Value::primitive("zero?", prim_zero_p));
5663    env.define("positive?", Value::primitive("positive?", prim_positive_p));
5664    env.define("negative?", Value::primitive("negative?", prim_negative_p));
5665    env.define("odd?", Value::primitive("odd?", prim_odd_p));
5666    env.define("even?", Value::primitive("even?", prim_even_p));
5667
5668    // Math functions
5669    env.define("abs", Value::primitive("abs", prim_abs));
5670    env.define("max", Value::primitive("max", prim_max));
5671    env.define("min", Value::primitive("min", prim_min));
5672    env.define("gcd", Value::primitive("gcd", prim_gcd));
5673    env.define("lcm", Value::primitive("lcm", prim_lcm));
5674    env.define("floor", Value::primitive("floor", prim_floor));
5675    env.define("ceiling", Value::primitive("ceiling", prim_ceiling));
5676    env.define("truncate", Value::primitive("truncate", prim_truncate));
5677    env.define("round", Value::primitive("round", prim_round));
5678
5679    // Advanced math functions
5680    env.define("expt", Value::primitive("expt", prim_expt));
5681    env.define("sqrt", Value::primitive("sqrt", prim_sqrt));
5682    env.define("sin", Value::primitive("sin", prim_sin));
5683    env.define("cos", Value::primitive("cos", prim_cos));
5684    env.define("tan", Value::primitive("tan", prim_tan));
5685    env.define("atan", Value::primitive("atan", prim_atan));
5686    env.define("asin", Value::primitive("asin", prim_asin));
5687    env.define("acos", Value::primitive("acos", prim_acos));
5688    env.define("log", Value::primitive("log", prim_log));
5689    env.define("exp", Value::primitive("exp", prim_exp));
5690
5691    // Number conversions
5692    env.define("exact?", Value::primitive("exact?", prim_exact_p));
5693    env.define("inexact?", Value::primitive("inexact?", prim_inexact_p));
5694    env.define("exact->inexact", Value::primitive("exact->inexact", prim_exact_to_inexact));
5695    env.define("inexact->exact", Value::primitive("inexact->exact", prim_inexact_to_exact));
5696}
5697
5698/// Register all string primitives in an environment
5699pub fn register_string_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5700    // String operations
5701    env.define("string-length", Value::primitive("string-length", prim_string_length));
5702    env.define("string-ref", Value::primitive("string-ref", prim_string_ref));
5703    env.define("string-append", Value::primitive("string-append", prim_string_append));
5704    env.define("substring", Value::primitive("substring", prim_substring));
5705    env.define("make-string", Value::primitive("make-string", prim_make_string));
5706    env.define("string", Value::primitive("string", prim_string));
5707
5708    // String comparison
5709    env.define("string=?", Value::primitive("string=?", prim_string_eq));
5710    env.define("string<?", Value::primitive("string<?", prim_string_lt));
5711    env.define("string>?", Value::primitive("string>?", prim_string_gt));
5712    env.define("string<=?", Value::primitive("string<=?", prim_string_le));
5713    env.define("string>=?", Value::primitive("string>=?", prim_string_ge));
5714
5715    // Case-insensitive string comparison
5716    env.define("string-ci=?", Value::primitive("string-ci=?", prim_string_ci_eq));
5717    env.define("string-ci<?", Value::primitive("string-ci<?", prim_string_ci_lt));
5718    env.define("string-ci>?", Value::primitive("string-ci>?", prim_string_ci_gt));
5719    env.define("string-ci<=?", Value::primitive("string-ci<=?", prim_string_ci_le));
5720    env.define("string-ci>=?", Value::primitive("string-ci>=?", prim_string_ci_ge));
5721
5722    // String conversions
5723    env.define("string->list", Value::primitive("string->list", prim_string_to_list));
5724    env.define("list->string", Value::primitive("list->string", prim_list_to_string));
5725    env.define("string->symbol", Value::primitive("string->symbol", prim_string_to_symbol));
5726    env.define("symbol->string", Value::primitive("symbol->string", prim_symbol_to_string));
5727
5728    // Type predicates
5729    env.define("string?", Value::primitive("string?", prim_string_p));
5730    env.define("symbol?", Value::primitive("symbol?", prim_symbol_p));
5731    env.define("char?", Value::primitive("char?", prim_char_p));
5732
5733    // Character operations
5734    env.define("char=?", Value::primitive("char=?", prim_char_eq));
5735    env.define("char<?", Value::primitive("char<?", prim_char_lt));
5736    env.define("char>?", Value::primitive("char>?", prim_char_gt));
5737    env.define("char<=?", Value::primitive("char<=?", prim_char_le));
5738    env.define("char>=?", Value::primitive("char>=?", prim_char_ge));
5739    env.define("char-upcase", Value::primitive("char-upcase", prim_char_upcase));
5740    env.define("char-downcase", Value::primitive("char-downcase", prim_char_downcase));
5741    env.define("char->integer", Value::primitive("char->integer", prim_char_to_integer));
5742    env.define("integer->char", Value::primitive("integer->char", prim_integer_to_char));
5743    env.define("char-alphabetic?", Value::primitive("char-alphabetic?", prim_char_alphabetic_p));
5744    env.define("char-numeric?", Value::primitive("char-numeric?", prim_char_numeric_p));
5745    env.define("char-whitespace?", Value::primitive("char-whitespace?", prim_char_whitespace_p));
5746
5747    // Case-insensitive character operations
5748    env.define("char-ci=?", Value::primitive("char-ci=?", prim_char_ci_eq));
5749    env.define("char-ci<?", Value::primitive("char-ci<?", prim_char_ci_lt));
5750    env.define("char-ci>?", Value::primitive("char-ci>?", prim_char_ci_gt));
5751    env.define("char-ci<=?", Value::primitive("char-ci<=?", prim_char_ci_le));
5752    env.define("char-ci>=?", Value::primitive("char-ci>=?", prim_char_ci_ge));
5753}
5754
5755/// Register all boolean and equality primitives in an environment
5756pub fn register_boolean_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5757    env.define("not", Value::primitive("not", prim_not));
5758    env.define("boolean?", Value::primitive("boolean?", prim_boolean_p));
5759    env.define("equal?", Value::primitive("equal?", prim_equal_p));
5760    env.define("eqv?", Value::primitive("eqv?", prim_eqv_p));
5761    env.define("eq?", Value::primitive("eq?", prim_eq_p));
5762    env.define("procedure?", Value::primitive("procedure?", prim_procedure_p));
5763}
5764
5765/// Register all I/O and utility primitives in an environment
5766pub fn register_io_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5767    env.define("error", Value::primitive("error", prim_error));
5768    env.define("display", Value::primitive("display", prim_display));
5769    env.define("newline", Value::primitive("newline", prim_newline));
5770    env.define("write", Value::primitive("write", prim_write));
5771}
5772
5773/// Register all conversion primitives in an environment
5774pub fn register_conversion_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5775    env.define("number->string", Value::primitive("number->string", prim_number_to_string));
5776    env.define("string->number", Value::primitive("string->number", prim_string_to_number));
5777}
5778
5779/// Register all keyword primitives in an environment
5780pub fn register_keyword_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5781    env.define("keyword?", Value::primitive("keyword?", prim_keyword_p));
5782    env.define("keyword->string", Value::primitive("keyword->string", prim_keyword_to_string));
5783    env.define("string->keyword", Value::primitive("string->keyword", prim_string_to_keyword));
5784}
5785
5786/// Register DSSSL type stub primitives in an environment
5787pub fn register_dsssl_type_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5788    env.define("quantity?", Value::primitive("quantity?", prim_quantity_p));
5789    env.define("color?", Value::primitive("color?", prim_color_p));
5790    env.define("address?", Value::primitive("address?", prim_address_p));
5791
5792    // Color and color-space stubs
5793    env.define("color", Value::primitive("color", prim_color));
5794    env.define("color-space", Value::primitive("color-space", prim_color_space));
5795    env.define("color-space?", Value::primitive("color-space?", prim_color_space_p));
5796
5797    // Spacing stubs
5798    env.define("display-space", Value::primitive("display-space", prim_display_space));
5799    env.define("display-space?", Value::primitive("display-space?", prim_display_space_p));
5800    env.define("inline-space", Value::primitive("inline-space", prim_inline_space));
5801    env.define("inline-space?", Value::primitive("inline-space?", prim_inline_space_p));
5802
5803    // Glyph stubs
5804    env.define("glyph-id", Value::primitive("glyph-id", prim_glyph_id));
5805    env.define("glyph-id?", Value::primitive("glyph-id?", prim_glyph_id_p));
5806    env.define("glyph-subst-table", Value::primitive("glyph-subst-table", prim_glyph_subst_table));
5807    env.define("glyph-subst-table?", Value::primitive("glyph-subst-table?", prim_glyph_subst_table_p));
5808    env.define("glyph-subst", Value::primitive("glyph-subst", prim_glyph_subst));
5809
5810    // Address stubs
5811    env.define("address-local?", Value::primitive("address-local?", prim_address_local_p));
5812
5813    // Quantity/Dimension (stubs)
5814    env.define("quantity->number", Value::primitive("quantity->number", prim_quantity_to_number));
5815    env.define("number->quantity", Value::primitive("number->quantity", prim_number_to_quantity));
5816    env.define("quantity-convert", Value::primitive("quantity-convert", prim_quantity_convert));
5817    env.define("device-length", Value::primitive("device-length", prim_device_length));
5818}
5819
5820/// Register format primitives in an environment
5821pub fn register_format_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5822    env.define("format-number", Value::primitive("format-number", prim_format_number));
5823    env.define("format-number-list", Value::primitive("format-number-list", prim_format_number_list));
5824}
5825
5826/// Register grove query primitives in an environment
5827pub fn register_grove_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5828    // Context primitives
5829    env.define("current-node", Value::primitive("current-node", prim_current_node));
5830
5831    // Node primitives
5832    env.define("node?", Value::primitive("node?", prim_node_p));
5833    env.define("gi", Value::primitive("gi", prim_gi));
5834    env.define("data", Value::primitive("data", prim_data));
5835    env.define("id", Value::primitive("id", prim_id));
5836    env.define("attribute-string", Value::primitive("attribute-string", prim_attribute_string));
5837    env.define("children", Value::primitive("children", prim_children));
5838    env.define("select-children", Value::primitive("select-children", prim_select_children));
5839    env.define("parent", Value::primitive("parent", prim_parent));
5840    env.define("tree-root", Value::primitive("tree-root", prim_tree_root));
5841    env.define("ancestors", Value::primitive("ancestors", prim_ancestors));
5842
5843    // Node navigation (stubs for now)
5844    env.define("ancestor", Value::primitive("ancestor", prim_ancestor));
5845    env.define("descendants", Value::primitive("descendants", prim_descendants));
5846    env.define("follow", Value::primitive("follow", prim_follow));
5847    env.define("preced", Value::primitive("preced", prim_preced));
5848    env.define("attributes", Value::primitive("attributes", prim_attributes));
5849    env.define("select-elements", Value::primitive("select-elements", prim_select_elements));
5850    env.define("element-with-id", Value::primitive("element-with-id", prim_element_with_id));
5851
5852    // Node-list primitives
5853    env.define("node-list?", Value::primitive("node-list?", prim_node_list_p));
5854    env.define("empty-node-list", Value::primitive("empty-node-list", prim_empty_node_list));
5855    env.define("node-list-empty?", Value::primitive("node-list-empty?", prim_node_list_empty_p));
5856    env.define("node-list-length", Value::primitive("node-list-length", prim_node_list_length));
5857    env.define("node-list-remove-duplicates", Value::primitive("node-list-remove-duplicates", prim_node_list_remove_duplicates));
5858    env.define("node-list-first", Value::primitive("node-list-first", prim_node_list_first));
5859    env.define("node-list-last", Value::primitive("node-list-last", prim_node_list_last));
5860    env.define("node-list-rest", Value::primitive("node-list-rest", prim_node_list_rest));
5861    env.define("node-list-ref", Value::primitive("node-list-ref", prim_node_list_ref));
5862    env.define("node-list-reverse", Value::primitive("node-list-reverse", prim_node_list_reverse));
5863    env.define("node-list->list", Value::primitive("node-list->list", prim_node_list_to_list));
5864    env.define("node-list-contains?", Value::primitive("node-list-contains?", prim_node_list_contains_p));
5865
5866    // Node-list utilities (stubs)
5867    env.define("node-list", Value::primitive("node-list", prim_node_list));
5868    env.define("node-list-map", Value::primitive("node-list-map", prim_node_list_map));
5869    env.define("node-property", Value::primitive("node-property", prim_node_property));
5870    env.define("match-element?", Value::primitive("match-element?", prim_match_element_p));
5871    env.define("named-node-list?", Value::primitive("named-node-list?", prim_named_node_list_p));
5872    env.define("node-list=?", Value::primitive("node-list=?", prim_node_list_eq));
5873
5874    // Grove extended (stubs)
5875    env.define("first-sibling?", Value::primitive("first-sibling?", prim_first_sibling_p));
5876    env.define("last-sibling?", Value::primitive("last-sibling?", prim_last_sibling_p));
5877    env.define("child-number", Value::primitive("child-number", prim_child_number));
5878    env.define("element-number", Value::primitive("element-number", prim_element_number));
5879    env.define("inherited-attribute-string", Value::primitive("inherited-attribute-string", prim_inherited_attribute_string));
5880
5881    // Entity and notation (stubs)
5882    env.define("entity-system-id", Value::primitive("entity-system-id", prim_entity_system_id));
5883    env.define("entity-public-id", Value::primitive("entity-public-id", prim_entity_public_id));
5884    env.define("notation-system-id", Value::primitive("notation-system-id", prim_notation_system_id));
5885    env.define("notation-public-id", Value::primitive("notation-public-id", prim_notation_public_id));
5886
5887    // Grove Position/Numbering (stubs)
5888    env.define("absolute-first-sibling?", Value::primitive("absolute-first-sibling?", prim_absolute_first_sibling_p));
5889    env.define("absolute-last-sibling?", Value::primitive("absolute-last-sibling?", prim_absolute_last_sibling_p));
5890    env.define("ancestor-child-number", Value::primitive("ancestor-child-number", prim_ancestor_child_number));
5891    env.define("element-number-list", Value::primitive("element-number-list", prim_element_number_list));
5892    env.define("hierarchical-number", Value::primitive("hierarchical-number", prim_hierarchical_number));
5893    env.define("hierarchical-number-recursive", Value::primitive("hierarchical-number-recursive", prim_hierarchical_number_recursive));
5894    env.define("have-ancestor?", Value::primitive("have-ancestor?", prim_have_ancestor_p));
5895    env.define("all-element-number", Value::primitive("all-element-number", prim_all_element_number));
5896
5897    // Basic Grove Extended (stubs)
5898    env.define("first-child-gi", Value::primitive("first-child-gi", prim_first_child_gi));
5899    env.define("inherited-element-attribute-string", Value::primitive("inherited-element-attribute-string", prim_inherited_element_attribute_string));
5900
5901    // Entity/Notation Extended (stubs)
5902    env.define("entity-address", Value::primitive("entity-address", prim_entity_address));
5903    env.define("entity-generated-system-id", Value::primitive("entity-generated-system-id", prim_entity_generated_system_id));
5904    env.define("entity-type", Value::primitive("entity-type", prim_entity_type));
5905    env.define("declaration", Value::primitive("declaration", prim_declaration));
5906    env.define("dtd", Value::primitive("dtd", prim_dtd));
5907    env.define("sgml-declaration", Value::primitive("sgml-declaration", prim_sgml_declaration));
5908    env.define("document-element", Value::primitive("document-element", prim_document_element));
5909    env.define("prolog", Value::primitive("prolog", prim_prolog));
5910    env.define("epilog", Value::primitive("epilog", prim_epilog));
5911
5912    // DSSSL Language & Flow Object Class Declarations (define-language and declare-flow-object-class are special forms)
5913    env.define("declare-default-language", Value::primitive("declare-default-language", prim_declare_default_language));
5914    env.define("declare-characteristic", Value::primitive("declare-characteristic", prim_declare_characteristic));
5915    env.define("origin-to-subnode-rel-forest-addr", Value::primitive("origin-to-subnode-rel-forest-addr", prim_origin_to_subnode_rel_forest_addr));
5916
5917    // Node-list Extended (stubs)
5918    env.define("named-node", Value::primitive("named-node", prim_named_node));
5919    env.define("named-node-list-names", Value::primitive("named-node-list-names", prim_named_node_list_names));
5920    env.define("node-list-union", Value::primitive("node-list-union", prim_node_list_union));
5921    env.define("node-list-intersection", Value::primitive("node-list-intersection", prim_node_list_intersection));
5922    env.define("node-list-difference", Value::primitive("node-list-difference", prim_node_list_difference));
5923    env.define("node-list-symmetrical-difference", Value::primitive("node-list-symmetrical-difference", prim_node_list_symmetrical_difference));
5924    env.define("node-list-union-map", Value::primitive("node-list-union-map", prim_node_list_union_map));
5925
5926    // DSSSL: node-list-count = node-list-length(node-list-remove-duplicates(nl))
5927    env.define("node-list-count", Value::primitive("node-list-count", prim_node_list_count));
5928}
5929
5930/// Register sosofo primitives in an environment
5931pub fn register_sosofo_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5932    // Sosofo construction
5933    env.define("sosofo?", Value::primitive("sosofo?", prim_sosofo_p));
5934    env.define("style?", Value::primitive("style?", prim_style_p));
5935    env.define("next-match", Value::primitive("next-match", prim_next_match));
5936    env.define("empty-sosofo", Value::primitive("empty-sosofo", prim_empty_sosofo));
5937    env.define("literal", Value::primitive("literal", prim_literal));
5938    env.define("sosofo-append", Value::primitive("sosofo-append", prim_sosofo_append));
5939
5940    // Processing primitives
5941    env.define("process-children", Value::primitive("process-children", prim_process_children));
5942    env.define("process-node-list", Value::primitive("process-node-list", prim_process_node_list));
5943    env.define("make", Value::primitive("make", prim_make));
5944
5945    // Processing Extended (stubs)
5946    env.define("process-children-trim", Value::primitive("process-children-trim", prim_process_children_trim));
5947    env.define("process-first-descendant", Value::primitive("process-first-descendant", prim_process_first_descendant));
5948    env.define("process-matching-children", Value::primitive("process-matching-children", prim_process_matching_children));
5949    env.define("process-element-with-id", Value::primitive("process-element-with-id", prim_process_element_with_id));
5950    env.define("with-mode", Value::primitive("with-mode", prim_with_mode));
5951    env.define("current-mode", Value::primitive("current-mode", prim_current_mode));
5952
5953    // Sosofo/Page Layout Extended (stubs)
5954    env.define("current-node-page-number-sosofo", Value::primitive("current-node-page-number-sosofo", prim_current_node_page_number_sosofo));
5955    env.define("page-number-sosofo", Value::primitive("page-number-sosofo", prim_page_number_sosofo));
5956    env.define("sosofo-contains-node?", Value::primitive("sosofo-contains-node?", prim_sosofo_contains_node_p));
5957    env.define("current-node-address", Value::primitive("current-node-address", prim_current_node_address));
5958    env.define("address-visited?", Value::primitive("address-visited?", prim_address_visited_p));
5959    env.define("set-visited!", Value::primitive("set-visited!", prim_set_visited));
5960    env.define("label-length", Value::primitive("label-length", prim_label_length));
5961    env.define("label-distance", Value::primitive("label-distance", prim_label_distance));
5962}
5963
5964/// Register utility primitives in an environment
5965pub fn register_utility_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
5966    // String utilities
5967    env.define("string-equiv?", Value::primitive("string-equiv?", prim_string_equiv_p));
5968
5969    // Time primitives (stubs)
5970    env.define("time", Value::primitive("time", prim_time));
5971    env.define("time->string", Value::primitive("time->string", prim_time_to_string));
5972    env.define("time<?", Value::primitive("time<?", prim_time_lt));
5973    env.define("time<=?", Value::primitive("time<=?", prim_time_le));
5974    env.define("time>?", Value::primitive("time>?", prim_time_gt));
5975    env.define("time>=?", Value::primitive("time>=?", prim_time_ge));
5976
5977    // Language primitives (stubs)
5978    env.define("language?", Value::primitive("language?", prim_language_p));
5979    env.define("current-language", Value::primitive("current-language", prim_current_language));
5980
5981    // Character/Language Extended (stubs)
5982    env.define("char-property", Value::primitive("char-property", prim_char_property));
5983    env.define("char-script-case", Value::primitive("char-script-case", prim_char_script_case));
5984    env.define("language", Value::primitive("language", prim_language));
5985    env.define("with-language", Value::primitive("with-language", prim_with_language));
5986
5987    // Debug (actual implementation)
5988    env.define("debug", Value::primitive("debug", prim_debug));
5989    env.define("external-procedure", Value::primitive("external-procedure", prim_external_procedure));
5990    env.define("read-entity", Value::primitive("read-entity", prim_read_entity));
5991
5992    // SGML-specific (stub)
5993    env.define("sgml-parse", Value::primitive("sgml-parse", prim_sgml_parse));
5994}
5995
5996/// Register all primitives in an environment
5997///
5998/// This is a convenience function for testing and REPL use.
5999/// It registers all available primitives in the given environment.
6000pub fn register_all_primitives(env: &gc::Gc<crate::scheme::environment::Environment>) {
6001    register_list_primitives(env);
6002    register_number_primitives(env);
6003    register_string_primitives(env);
6004    register_boolean_primitives(env);
6005    register_io_primitives(env);
6006    register_conversion_primitives(env);
6007    register_keyword_primitives(env);
6008    register_dsssl_type_primitives(env);
6009    register_format_primitives(env);
6010    register_grove_primitives(env);
6011    register_sosofo_primitives(env);
6012    register_utility_primitives(env);
6013}
6014
6015// =============================================================================
6016// Tests
6017// =============================================================================
6018
6019#[cfg(test)]
6020mod tests {
6021    use super::*;
6022
6023    #[test]
6024    fn test_car() {
6025        let pair = Value::cons(Value::integer(1), Value::integer(2));
6026        let result = prim_car(&[pair]).unwrap();
6027        assert!(matches!(result, Value::Integer(1)));
6028    }
6029
6030    #[test]
6031    fn test_cdr() {
6032        let pair = Value::cons(Value::integer(1), Value::integer(2));
6033        let result = prim_cdr(&[pair]).unwrap();
6034        assert!(matches!(result, Value::Integer(2)));
6035    }
6036
6037    #[test]
6038    fn test_cons() {
6039        let result = prim_cons(&[Value::integer(1), Value::integer(2)]).unwrap();
6040        assert!(result.is_pair());
6041    }
6042
6043    #[test]
6044    fn test_list() {
6045        let result = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
6046        assert!(result.is_list());
6047
6048        // Check length
6049        let len = prim_length(&[result]).unwrap();
6050        assert!(matches!(len, Value::Integer(3)));
6051    }
6052
6053    #[test]
6054    fn test_null_p() {
6055        assert!(matches!(prim_null_p(&[Value::Nil]).unwrap(), Value::Bool(true)));
6056        assert!(matches!(
6057            prim_null_p(&[Value::integer(1)]).unwrap(),
6058            Value::Bool(false)
6059        ));
6060    }
6061
6062    #[test]
6063    fn test_pair_p() {
6064        let pair = Value::cons(Value::integer(1), Value::integer(2));
6065        assert!(matches!(prim_pair_p(&[pair]).unwrap(), Value::Bool(true)));
6066        assert!(matches!(
6067            prim_pair_p(&[Value::Nil]).unwrap(),
6068            Value::Bool(false)
6069        ));
6070    }
6071
6072    #[test]
6073    fn test_length() {
6074        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
6075        let result = prim_length(&[list]).unwrap();
6076        assert!(matches!(result, Value::Integer(3)));
6077
6078        let empty = prim_length(&[Value::Nil]).unwrap();
6079        assert!(matches!(empty, Value::Integer(0)));
6080    }
6081
6082    #[test]
6083    fn test_append() {
6084        let list1 = prim_list(&[Value::integer(1), Value::integer(2)]).unwrap();
6085        let list2 = prim_list(&[Value::integer(3), Value::integer(4)]).unwrap();
6086        let result = prim_append(&[list1, list2]).unwrap();
6087
6088        let len = prim_length(&[result]).unwrap();
6089        assert!(matches!(len, Value::Integer(4)));
6090    }
6091
6092    #[test]
6093    fn test_reverse() {
6094        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
6095        let result = prim_reverse(&[list]).unwrap();
6096
6097        // Check first element is 3
6098        let first = prim_car(&[result]).unwrap();
6099        assert!(matches!(first, Value::Integer(3)));
6100    }
6101
6102    #[test]
6103    fn test_list_ref() {
6104        let list = prim_list(&[Value::integer(10), Value::integer(20), Value::integer(30)]).unwrap();
6105
6106        let result = prim_list_ref(&[list.clone(), Value::integer(0)]).unwrap();
6107        assert!(matches!(result, Value::Integer(10)));
6108
6109        let result = prim_list_ref(&[list.clone(), Value::integer(1)]).unwrap();
6110        assert!(matches!(result, Value::Integer(20)));
6111
6112        let result = prim_list_ref(&[list, Value::integer(2)]).unwrap();
6113        assert!(matches!(result, Value::Integer(30)));
6114    }
6115
6116    #[test]
6117    fn test_list_tail() {
6118        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
6119
6120        let result = prim_list_tail(&[list, Value::integer(2)]).unwrap();
6121        let len = prim_length(&[result]).unwrap();
6122        assert!(matches!(len, Value::Integer(1)));
6123    }
6124
6125    // =========================================================================
6126    // Number primitive tests
6127    // =========================================================================
6128
6129    #[test]
6130    fn test_add() {
6131        let result = prim_add(&[]).unwrap();
6132        assert!(matches!(result, Value::Integer(0)));
6133
6134        let result = prim_add(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
6135        assert!(matches!(result, Value::Integer(6)));
6136
6137        let result = prim_add(&[Value::integer(1), Value::real(2.5)]).unwrap();
6138        assert!(matches!(result, Value::Real(r) if (r - 3.5).abs() < f64::EPSILON));
6139    }
6140
6141    #[test]
6142    fn test_subtract() {
6143        let result = prim_subtract(&[Value::integer(5)]).unwrap();
6144        assert!(matches!(result, Value::Integer(-5)));
6145
6146        let result = prim_subtract(&[Value::integer(10), Value::integer(3), Value::integer(2)]).unwrap();
6147        assert!(matches!(result, Value::Integer(5)));
6148
6149        let result = prim_subtract(&[Value::real(10.5), Value::integer(2)]).unwrap();
6150        assert!(matches!(result, Value::Real(r) if (r - 8.5).abs() < f64::EPSILON));
6151    }
6152
6153    #[test]
6154    fn test_multiply() {
6155        let result = prim_multiply(&[]).unwrap();
6156        assert!(matches!(result, Value::Integer(1)));
6157
6158        let result = prim_multiply(&[Value::integer(2), Value::integer(3), Value::integer(4)]).unwrap();
6159        assert!(matches!(result, Value::Integer(24)));
6160
6161        let result = prim_multiply(&[Value::integer(2), Value::real(1.5)]).unwrap();
6162        assert!(matches!(result, Value::Real(r) if (r - 3.0).abs() < f64::EPSILON));
6163    }
6164
6165    #[test]
6166    fn test_divide() {
6167        let result = prim_divide(&[Value::integer(2)]).unwrap();
6168        assert!(matches!(result, Value::Real(r) if (r - 0.5).abs() < f64::EPSILON));
6169
6170        let result = prim_divide(&[Value::integer(10), Value::integer(2)]).unwrap();
6171        assert!(matches!(result, Value::Real(r) if (r - 5.0).abs() < f64::EPSILON));
6172
6173        // Division by zero should error
6174        assert!(prim_divide(&[Value::integer(1), Value::integer(0)]).is_err());
6175    }
6176
6177    #[test]
6178    fn test_quotient() {
6179        let result = prim_quotient(&[Value::integer(10), Value::integer(3)]).unwrap();
6180        assert!(matches!(result, Value::Integer(3)));
6181
6182        let result = prim_quotient(&[Value::integer(-10), Value::integer(3)]).unwrap();
6183        assert!(matches!(result, Value::Integer(-3)));
6184    }
6185
6186    #[test]
6187    fn test_remainder() {
6188        let result = prim_remainder(&[Value::integer(10), Value::integer(3)]).unwrap();
6189        assert!(matches!(result, Value::Integer(1)));
6190
6191        let result = prim_remainder(&[Value::integer(-10), Value::integer(3)]).unwrap();
6192        assert!(matches!(result, Value::Integer(-1)));
6193    }
6194
6195    #[test]
6196    fn test_modulo() {
6197        let result = prim_modulo(&[Value::integer(10), Value::integer(3)]).unwrap();
6198        assert!(matches!(result, Value::Integer(1)));
6199
6200        let result = prim_modulo(&[Value::integer(-10), Value::integer(3)]).unwrap();
6201        assert!(matches!(result, Value::Integer(2))); // Euclidean modulo
6202    }
6203
6204    #[test]
6205    fn test_num_eq() {
6206        let result = prim_num_eq(&[Value::integer(5), Value::integer(5)]).unwrap();
6207        assert!(matches!(result, Value::Bool(true)));
6208
6209        let result = prim_num_eq(&[Value::integer(5), Value::integer(6)]).unwrap();
6210        assert!(matches!(result, Value::Bool(false)));
6211
6212        let result = prim_num_eq(&[Value::integer(5), Value::real(5.0)]).unwrap();
6213        assert!(matches!(result, Value::Bool(true)));
6214    }
6215
6216    #[test]
6217    fn test_num_lt() {
6218        let result = prim_num_lt(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
6219        assert!(matches!(result, Value::Bool(true)));
6220
6221        let result = prim_num_lt(&[Value::integer(1), Value::integer(3), Value::integer(2)]).unwrap();
6222        assert!(matches!(result, Value::Bool(false)));
6223    }
6224
6225    #[test]
6226    fn test_num_gt() {
6227        let result = prim_num_gt(&[Value::integer(3), Value::integer(2), Value::integer(1)]).unwrap();
6228        assert!(matches!(result, Value::Bool(true)));
6229
6230        let result = prim_num_gt(&[Value::integer(3), Value::integer(1), Value::integer(2)]).unwrap();
6231        assert!(matches!(result, Value::Bool(false)));
6232    }
6233
6234    #[test]
6235    fn test_num_le() {
6236        let result = prim_num_le(&[Value::integer(1), Value::integer(2), Value::integer(2)]).unwrap();
6237        assert!(matches!(result, Value::Bool(true)));
6238
6239        let result = prim_num_le(&[Value::integer(2), Value::integer(1)]).unwrap();
6240        assert!(matches!(result, Value::Bool(false)));
6241    }
6242
6243    #[test]
6244    fn test_num_ge() {
6245        let result = prim_num_ge(&[Value::integer(3), Value::integer(2), Value::integer(2)]).unwrap();
6246        assert!(matches!(result, Value::Bool(true)));
6247
6248        let result = prim_num_ge(&[Value::integer(1), Value::integer(2)]).unwrap();
6249        assert!(matches!(result, Value::Bool(false)));
6250    }
6251
6252    #[test]
6253    fn test_number_p() {
6254        assert!(matches!(prim_number_p(&[Value::integer(42)]).unwrap(), Value::Bool(true)));
6255        assert!(matches!(prim_number_p(&[Value::real(3.14)]).unwrap(), Value::Bool(true)));
6256        assert!(matches!(prim_number_p(&[Value::string("hello".to_string())]).unwrap(), Value::Bool(false)));
6257    }
6258
6259    #[test]
6260    fn test_integer_p() {
6261        assert!(matches!(prim_integer_p(&[Value::integer(42)]).unwrap(), Value::Bool(true)));
6262        assert!(matches!(prim_integer_p(&[Value::real(3.14)]).unwrap(), Value::Bool(false)));
6263    }
6264
6265    #[test]
6266    fn test_real_p() {
6267        assert!(matches!(prim_real_p(&[Value::real(3.14)]).unwrap(), Value::Bool(true)));
6268        assert!(matches!(prim_real_p(&[Value::integer(42)]).unwrap(), Value::Bool(false)));
6269    }
6270
6271    #[test]
6272    fn test_zero_p() {
6273        assert!(matches!(prim_zero_p(&[Value::integer(0)]).unwrap(), Value::Bool(true)));
6274        assert!(matches!(prim_zero_p(&[Value::integer(1)]).unwrap(), Value::Bool(false)));
6275        assert!(matches!(prim_zero_p(&[Value::real(0.0)]).unwrap(), Value::Bool(true)));
6276    }
6277
6278    #[test]
6279    fn test_positive_p() {
6280        assert!(matches!(prim_positive_p(&[Value::integer(5)]).unwrap(), Value::Bool(true)));
6281        assert!(matches!(prim_positive_p(&[Value::integer(-5)]).unwrap(), Value::Bool(false)));
6282        assert!(matches!(prim_positive_p(&[Value::integer(0)]).unwrap(), Value::Bool(false)));
6283    }
6284
6285    #[test]
6286    fn test_negative_p() {
6287        assert!(matches!(prim_negative_p(&[Value::integer(-5)]).unwrap(), Value::Bool(true)));
6288        assert!(matches!(prim_negative_p(&[Value::integer(5)]).unwrap(), Value::Bool(false)));
6289        assert!(matches!(prim_negative_p(&[Value::integer(0)]).unwrap(), Value::Bool(false)));
6290    }
6291
6292    #[test]
6293    fn test_odd_p() {
6294        assert!(matches!(prim_odd_p(&[Value::integer(3)]).unwrap(), Value::Bool(true)));
6295        assert!(matches!(prim_odd_p(&[Value::integer(4)]).unwrap(), Value::Bool(false)));
6296    }
6297
6298    #[test]
6299    fn test_even_p() {
6300        assert!(matches!(prim_even_p(&[Value::integer(4)]).unwrap(), Value::Bool(true)));
6301        assert!(matches!(prim_even_p(&[Value::integer(3)]).unwrap(), Value::Bool(false)));
6302    }
6303
6304    #[test]
6305    fn test_abs() {
6306        let result = prim_abs(&[Value::integer(-5)]).unwrap();
6307        assert!(matches!(result, Value::Integer(5)));
6308
6309        let result = prim_abs(&[Value::real(-3.14)]).unwrap();
6310        assert!(matches!(result, Value::Real(r) if (r - 3.14).abs() < f64::EPSILON));
6311    }
6312
6313    #[test]
6314    fn test_max() {
6315        let result = prim_max(&[Value::integer(1), Value::integer(5), Value::integer(3)]).unwrap();
6316        assert!(matches!(result, Value::Integer(5)));
6317
6318        let result = prim_max(&[Value::integer(1), Value::real(5.5), Value::integer(3)]).unwrap();
6319        assert!(matches!(result, Value::Real(r) if (r - 5.5).abs() < f64::EPSILON));
6320    }
6321
6322    #[test]
6323    fn test_min() {
6324        let result = prim_min(&[Value::integer(5), Value::integer(1), Value::integer(3)]).unwrap();
6325        assert!(matches!(result, Value::Integer(1)));
6326
6327        let result = prim_min(&[Value::integer(5), Value::real(0.5), Value::integer(3)]).unwrap();
6328        assert!(matches!(result, Value::Real(r) if (r - 0.5).abs() < f64::EPSILON));
6329    }
6330
6331    #[test]
6332    fn test_gcd() {
6333        // gcd of two numbers
6334        let result = prim_gcd(&[Value::integer(12), Value::integer(8)]).unwrap();
6335        assert!(matches!(result, Value::Integer(4)));
6336
6337        // gcd of multiple numbers
6338        let result = prim_gcd(&[Value::integer(12), Value::integer(18), Value::integer(24)]).unwrap();
6339        assert!(matches!(result, Value::Integer(6)));
6340
6341        // gcd with negative numbers
6342        let result = prim_gcd(&[Value::integer(-12), Value::integer(8)]).unwrap();
6343        assert!(matches!(result, Value::Integer(4)));
6344
6345        // gcd with zero
6346        let result = prim_gcd(&[Value::integer(0), Value::integer(5)]).unwrap();
6347        assert!(matches!(result, Value::Integer(5)));
6348
6349        // gcd with no arguments
6350        let result = prim_gcd(&[]).unwrap();
6351        assert!(matches!(result, Value::Integer(0)));
6352
6353        // Non-integer should error
6354        assert!(prim_gcd(&[Value::real(12.5), Value::integer(8)]).is_err());
6355    }
6356
6357    #[test]
6358    fn test_lcm() {
6359        // lcm of two numbers
6360        let result = prim_lcm(&[Value::integer(4), Value::integer(6)]).unwrap();
6361        assert!(matches!(result, Value::Integer(12)));
6362
6363        // lcm of multiple numbers
6364        let result = prim_lcm(&[Value::integer(2), Value::integer(3), Value::integer(4)]).unwrap();
6365        assert!(matches!(result, Value::Integer(12)));
6366
6367        // lcm with negative numbers
6368        let result = prim_lcm(&[Value::integer(-4), Value::integer(6)]).unwrap();
6369        assert!(matches!(result, Value::Integer(12)));
6370
6371        // lcm with zero
6372        let result = prim_lcm(&[Value::integer(0), Value::integer(5)]).unwrap();
6373        assert!(matches!(result, Value::Integer(0)));
6374
6375        // lcm with no arguments
6376        let result = prim_lcm(&[]).unwrap();
6377        assert!(matches!(result, Value::Integer(1)));
6378
6379        // Non-integer should error
6380        assert!(prim_lcm(&[Value::real(4.5), Value::integer(6)]).is_err());
6381    }
6382
6383    #[test]
6384    fn test_floor() {
6385        let result = prim_floor(&[Value::real(3.7)]).unwrap();
6386        assert!(matches!(result, Value::Integer(3)));
6387
6388        let result = prim_floor(&[Value::real(-3.7)]).unwrap();
6389        assert!(matches!(result, Value::Integer(-4)));
6390    }
6391
6392    #[test]
6393    fn test_ceiling() {
6394        let result = prim_ceiling(&[Value::real(3.2)]).unwrap();
6395        assert!(matches!(result, Value::Integer(4)));
6396
6397        let result = prim_ceiling(&[Value::real(-3.2)]).unwrap();
6398        assert!(matches!(result, Value::Integer(-3)));
6399    }
6400
6401    #[test]
6402    fn test_truncate() {
6403        let result = prim_truncate(&[Value::real(3.7)]).unwrap();
6404        assert!(matches!(result, Value::Integer(3)));
6405
6406        let result = prim_truncate(&[Value::real(-3.7)]).unwrap();
6407        assert!(matches!(result, Value::Integer(-3)));
6408    }
6409
6410    #[test]
6411    fn test_round() {
6412        let result = prim_round(&[Value::real(3.5)]).unwrap();
6413        assert!(matches!(result, Value::Integer(4)));
6414
6415        let result = prim_round(&[Value::real(3.4)]).unwrap();
6416        assert!(matches!(result, Value::Integer(3)));
6417    }
6418
6419    // =========================================================================
6420    // String primitive tests
6421    // =========================================================================
6422
6423    #[test]
6424    fn test_string_length() {
6425        let s = Value::string("hello".to_string());
6426        let result = prim_string_length(&[s]).unwrap();
6427        assert!(matches!(result, Value::Integer(5)));
6428
6429        let empty = Value::string(String::new());
6430        let result = prim_string_length(&[empty]).unwrap();
6431        assert!(matches!(result, Value::Integer(0)));
6432    }
6433
6434    #[test]
6435    fn test_string_ref() {
6436        let s = Value::string("hello".to_string());
6437        let result = prim_string_ref(&[s.clone(), Value::integer(0)]).unwrap();
6438        assert!(matches!(result, Value::Char('h')));
6439
6440        let result = prim_string_ref(&[s, Value::integer(4)]).unwrap();
6441        assert!(matches!(result, Value::Char('o')));
6442    }
6443
6444    #[test]
6445    fn test_string_append() {
6446        let s1 = Value::string("hello".to_string());
6447        let s2 = Value::string(" ".to_string());
6448        let s3 = Value::string("world".to_string());
6449        let result = prim_string_append(&[s1, s2, s3]).unwrap();
6450
6451        if let Value::String(ref s) = result {
6452            assert_eq!(&**s, "hello world");
6453        } else {
6454            panic!("Expected string");
6455        }
6456    }
6457
6458    #[test]
6459    fn test_substring() {
6460        let s = Value::string("hello".to_string());
6461        let result = prim_substring(&[s, Value::integer(1), Value::integer(4)]).unwrap();
6462
6463        if let Value::String(ref s) = result {
6464            assert_eq!(&**s, "ell");
6465        } else {
6466            panic!("Expected string");
6467        }
6468    }
6469
6470    #[test]
6471    fn test_string_eq() {
6472        let s1 = Value::string("hello".to_string());
6473        let s2 = Value::string("hello".to_string());
6474        let s3 = Value::string("world".to_string());
6475
6476        let result = prim_string_eq(&[s1.clone(), s2]).unwrap();
6477        assert!(matches!(result, Value::Bool(true)));
6478
6479        let result = prim_string_eq(&[s1, s3]).unwrap();
6480        assert!(matches!(result, Value::Bool(false)));
6481    }
6482
6483    #[test]
6484    fn test_string_lt() {
6485        let s1 = Value::string("abc".to_string());
6486        let s2 = Value::string("def".to_string());
6487
6488        let result = prim_string_lt(&[s1.clone(), s2.clone()]).unwrap();
6489        assert!(matches!(result, Value::Bool(true)));
6490
6491        let result = prim_string_lt(&[s2, s1]).unwrap();
6492        assert!(matches!(result, Value::Bool(false)));
6493    }
6494
6495    #[test]
6496    fn test_string_ci_eq() {
6497        // Case-insensitive equal
6498        let result = prim_string_ci_eq(&[
6499            Value::string("Hello".to_string()),
6500            Value::string("hello".to_string()),
6501        ]).unwrap();
6502        assert!(matches!(result, Value::Bool(true)));
6503
6504        let result = prim_string_ci_eq(&[
6505            Value::string("WORLD".to_string()),
6506            Value::string("world".to_string()),
6507        ]).unwrap();
6508        assert!(matches!(result, Value::Bool(true)));
6509
6510        let result = prim_string_ci_eq(&[
6511            Value::string("hello".to_string()),
6512            Value::string("world".to_string()),
6513        ]).unwrap();
6514        assert!(matches!(result, Value::Bool(false)));
6515    }
6516
6517    #[test]
6518    fn test_string_ci_lt() {
6519        // Case-insensitive less than
6520        let result = prim_string_ci_lt(&[
6521            Value::string("ABC".to_string()),
6522            Value::string("def".to_string()),
6523        ]).unwrap();
6524        assert!(matches!(result, Value::Bool(true)));
6525
6526        let result = prim_string_ci_lt(&[
6527            Value::string("abc".to_string()),
6528            Value::string("DEF".to_string()),
6529        ]).unwrap();
6530        assert!(matches!(result, Value::Bool(true)));
6531
6532        let result = prim_string_ci_lt(&[
6533            Value::string("def".to_string()),
6534            Value::string("ABC".to_string()),
6535        ]).unwrap();
6536        assert!(matches!(result, Value::Bool(false)));
6537    }
6538
6539    #[test]
6540    fn test_string_ci_gt() {
6541        // Case-insensitive greater than
6542        let result = prim_string_ci_gt(&[
6543            Value::string("DEF".to_string()),
6544            Value::string("abc".to_string()),
6545        ]).unwrap();
6546        assert!(matches!(result, Value::Bool(true)));
6547
6548        let result = prim_string_ci_gt(&[
6549            Value::string("def".to_string()),
6550            Value::string("ABC".to_string()),
6551        ]).unwrap();
6552        assert!(matches!(result, Value::Bool(true)));
6553
6554        let result = prim_string_ci_gt(&[
6555            Value::string("ABC".to_string()),
6556            Value::string("def".to_string()),
6557        ]).unwrap();
6558        assert!(matches!(result, Value::Bool(false)));
6559    }
6560
6561    #[test]
6562    fn test_string_ci_le() {
6563        // Case-insensitive less than or equal
6564        let result = prim_string_ci_le(&[
6565            Value::string("ABC".to_string()),
6566            Value::string("def".to_string()),
6567        ]).unwrap();
6568        assert!(matches!(result, Value::Bool(true)));
6569
6570        let result = prim_string_ci_le(&[
6571            Value::string("Hello".to_string()),
6572            Value::string("hello".to_string()),
6573        ]).unwrap();
6574        assert!(matches!(result, Value::Bool(true)));
6575
6576        let result = prim_string_ci_le(&[
6577            Value::string("def".to_string()),
6578            Value::string("ABC".to_string()),
6579        ]).unwrap();
6580        assert!(matches!(result, Value::Bool(false)));
6581    }
6582
6583    #[test]
6584    fn test_string_ci_ge() {
6585        // Case-insensitive greater than or equal
6586        let result = prim_string_ci_ge(&[
6587            Value::string("DEF".to_string()),
6588            Value::string("abc".to_string()),
6589        ]).unwrap();
6590        assert!(matches!(result, Value::Bool(true)));
6591
6592        let result = prim_string_ci_ge(&[
6593            Value::string("Hello".to_string()),
6594            Value::string("hello".to_string()),
6595        ]).unwrap();
6596        assert!(matches!(result, Value::Bool(true)));
6597
6598        let result = prim_string_ci_ge(&[
6599            Value::string("ABC".to_string()),
6600            Value::string("def".to_string()),
6601        ]).unwrap();
6602        assert!(matches!(result, Value::Bool(false)));
6603    }
6604
6605    #[test]
6606    fn test_make_string() {
6607        let result = prim_make_string(&[Value::integer(5), Value::char('a')]).unwrap();
6608
6609        if let Value::String(ref s) = result {
6610            assert_eq!(&**s, "aaaaa");
6611        } else {
6612            panic!("Expected string");
6613        }
6614    }
6615
6616    #[test]
6617    fn test_string() {
6618        let result = prim_string(&[
6619            Value::char('h'),
6620            Value::char('i'),
6621        ]).unwrap();
6622
6623        if let Value::String(ref s) = result {
6624            assert_eq!(&**s, "hi");
6625        } else {
6626            panic!("Expected string");
6627        }
6628    }
6629
6630    #[test]
6631    fn test_string_to_list() {
6632        let s = Value::string("hi".to_string());
6633        let result = prim_string_to_list(&[s]).unwrap();
6634
6635        // Should be a list of 2 characters
6636        let len = prim_length(&[result.clone()]).unwrap();
6637        assert!(matches!(len, Value::Integer(2)));
6638
6639        let first = prim_car(&[result]).unwrap();
6640        assert!(matches!(first, Value::Char('h')));
6641    }
6642
6643    #[test]
6644    fn test_list_to_string() {
6645        let list = prim_list(&[
6646            Value::char('h'),
6647            Value::char('i'),
6648        ]).unwrap();
6649
6650        let result = prim_list_to_string(&[list]).unwrap();
6651
6652        if let Value::String(ref s) = result {
6653            assert_eq!(&**s, "hi");
6654        } else {
6655            panic!("Expected string");
6656        }
6657    }
6658
6659    #[test]
6660    fn test_symbol_to_string() {
6661        let sym = Value::symbol("foo");
6662        let result = prim_symbol_to_string(&[sym]).unwrap();
6663
6664        if let Value::String(ref s) = result {
6665            assert_eq!(&**s, "foo");
6666        } else {
6667            panic!("Expected string");
6668        }
6669    }
6670
6671    #[test]
6672    fn test_string_to_symbol() {
6673        let s = Value::string("foo".to_string());
6674        let result = prim_string_to_symbol(&[s]).unwrap();
6675
6676        assert!(matches!(result, Value::Symbol(_)));
6677    }
6678
6679    #[test]
6680    fn test_string_p() {
6681        assert!(matches!(prim_string_p(&[Value::string("hello".to_string())]).unwrap(), Value::Bool(true)));
6682        assert!(matches!(prim_string_p(&[Value::integer(42)]).unwrap(), Value::Bool(false)));
6683    }
6684
6685    #[test]
6686    fn test_symbol_p() {
6687        assert!(matches!(prim_symbol_p(&[Value::symbol("foo")]).unwrap(), Value::Bool(true)));
6688        assert!(matches!(prim_symbol_p(&[Value::string("foo".to_string())]).unwrap(), Value::Bool(false)));
6689    }
6690
6691    #[test]
6692    fn test_char_p() {
6693        assert!(matches!(prim_char_p(&[Value::char('a')]).unwrap(), Value::Bool(true)));
6694        assert!(matches!(prim_char_p(&[Value::integer(65)]).unwrap(), Value::Bool(false)));
6695    }
6696
6697    #[test]
6698    fn test_char_eq() {
6699        let result = prim_char_eq(&[Value::char('a'), Value::char('a')]).unwrap();
6700        assert!(matches!(result, Value::Bool(true)));
6701
6702        let result = prim_char_eq(&[Value::char('a'), Value::char('b')]).unwrap();
6703        assert!(matches!(result, Value::Bool(false)));
6704    }
6705
6706    #[test]
6707    fn test_char_lt() {
6708        let result = prim_char_lt(&[Value::char('a'), Value::char('b')]).unwrap();
6709        assert!(matches!(result, Value::Bool(true)));
6710
6711        let result = prim_char_lt(&[Value::char('b'), Value::char('a')]).unwrap();
6712        assert!(matches!(result, Value::Bool(false)));
6713    }
6714
6715    #[test]
6716    fn test_char_upcase() {
6717        let result = prim_char_upcase(&[Value::char('a')]).unwrap();
6718        assert!(matches!(result, Value::Char('A')));
6719    }
6720
6721    #[test]
6722    fn test_char_downcase() {
6723        let result = prim_char_downcase(&[Value::char('Z')]).unwrap();
6724        assert!(matches!(result, Value::Char('z')));
6725    }
6726
6727    #[test]
6728    fn test_char_le() {
6729        let result = prim_char_le(&[Value::char('a'), Value::char('b')]).unwrap();
6730        assert!(matches!(result, Value::Bool(true)));
6731
6732        let result = prim_char_le(&[Value::char('a'), Value::char('a')]).unwrap();
6733        assert!(matches!(result, Value::Bool(true)));
6734
6735        let result = prim_char_le(&[Value::char('b'), Value::char('a')]).unwrap();
6736        assert!(matches!(result, Value::Bool(false)));
6737    }
6738
6739    #[test]
6740    fn test_char_ge() {
6741        let result = prim_char_ge(&[Value::char('b'), Value::char('a')]).unwrap();
6742        assert!(matches!(result, Value::Bool(true)));
6743
6744        let result = prim_char_ge(&[Value::char('a'), Value::char('a')]).unwrap();
6745        assert!(matches!(result, Value::Bool(true)));
6746
6747        let result = prim_char_ge(&[Value::char('a'), Value::char('b')]).unwrap();
6748        assert!(matches!(result, Value::Bool(false)));
6749    }
6750
6751    #[test]
6752    fn test_char_to_integer() {
6753        let result = prim_char_to_integer(&[Value::char('A')]).unwrap();
6754        assert!(matches!(result, Value::Integer(65)));
6755
6756        let result = prim_char_to_integer(&[Value::char('a')]).unwrap();
6757        assert!(matches!(result, Value::Integer(97)));
6758
6759        let result = prim_char_to_integer(&[Value::char('0')]).unwrap();
6760        assert!(matches!(result, Value::Integer(48)));
6761
6762        // Non-char should error
6763        assert!(prim_char_to_integer(&[Value::integer(65)]).is_err());
6764    }
6765
6766    #[test]
6767    fn test_integer_to_char() {
6768        let result = prim_integer_to_char(&[Value::integer(65)]).unwrap();
6769        assert!(matches!(result, Value::Char('A')));
6770
6771        let result = prim_integer_to_char(&[Value::integer(97)]).unwrap();
6772        assert!(matches!(result, Value::Char('a')));
6773
6774        let result = prim_integer_to_char(&[Value::integer(48)]).unwrap();
6775        assert!(matches!(result, Value::Char('0')));
6776
6777        // Invalid code point should error
6778        assert!(prim_integer_to_char(&[Value::integer(-1)]).is_err());
6779        assert!(prim_integer_to_char(&[Value::integer(0x200000)]).is_err());
6780
6781        // Non-integer should error
6782        assert!(prim_integer_to_char(&[Value::char('A')]).is_err());
6783    }
6784
6785    #[test]
6786    fn test_char_alphabetic_p() {
6787        let result = prim_char_alphabetic_p(&[Value::char('a')]).unwrap();
6788        assert!(matches!(result, Value::Bool(true)));
6789
6790        let result = prim_char_alphabetic_p(&[Value::char('Z')]).unwrap();
6791        assert!(matches!(result, Value::Bool(true)));
6792
6793        let result = prim_char_alphabetic_p(&[Value::char('5')]).unwrap();
6794        assert!(matches!(result, Value::Bool(false)));
6795
6796        let result = prim_char_alphabetic_p(&[Value::char(' ')]).unwrap();
6797        assert!(matches!(result, Value::Bool(false)));
6798
6799        // Non-char should error
6800        assert!(prim_char_alphabetic_p(&[Value::integer(97)]).is_err());
6801    }
6802
6803    #[test]
6804    fn test_char_numeric_p() {
6805        let result = prim_char_numeric_p(&[Value::char('5')]).unwrap();
6806        assert!(matches!(result, Value::Bool(true)));
6807
6808        let result = prim_char_numeric_p(&[Value::char('0')]).unwrap();
6809        assert!(matches!(result, Value::Bool(true)));
6810
6811        let result = prim_char_numeric_p(&[Value::char('a')]).unwrap();
6812        assert!(matches!(result, Value::Bool(false)));
6813
6814        let result = prim_char_numeric_p(&[Value::char(' ')]).unwrap();
6815        assert!(matches!(result, Value::Bool(false)));
6816
6817        // Non-char should error
6818        assert!(prim_char_numeric_p(&[Value::integer(5)]).is_err());
6819    }
6820
6821    #[test]
6822    fn test_char_whitespace_p() {
6823        let result = prim_char_whitespace_p(&[Value::char(' ')]).unwrap();
6824        assert!(matches!(result, Value::Bool(true)));
6825
6826        let result = prim_char_whitespace_p(&[Value::char('\t')]).unwrap();
6827        assert!(matches!(result, Value::Bool(true)));
6828
6829        let result = prim_char_whitespace_p(&[Value::char('\n')]).unwrap();
6830        assert!(matches!(result, Value::Bool(true)));
6831
6832        let result = prim_char_whitespace_p(&[Value::char('a')]).unwrap();
6833        assert!(matches!(result, Value::Bool(false)));
6834
6835        let result = prim_char_whitespace_p(&[Value::char('5')]).unwrap();
6836        assert!(matches!(result, Value::Bool(false)));
6837
6838        // Non-char should error
6839        assert!(prim_char_whitespace_p(&[Value::integer(32)]).is_err());
6840    }
6841
6842    #[test]
6843    fn test_char_ci_eq() {
6844        // Case-insensitive equal
6845        let result = prim_char_ci_eq(&[Value::char('a'), Value::char('A')]).unwrap();
6846        assert!(matches!(result, Value::Bool(true)));
6847
6848        let result = prim_char_ci_eq(&[Value::char('Z'), Value::char('z')]).unwrap();
6849        assert!(matches!(result, Value::Bool(true)));
6850
6851        let result = prim_char_ci_eq(&[Value::char('a'), Value::char('b')]).unwrap();
6852        assert!(matches!(result, Value::Bool(false)));
6853
6854        // Non-char should error
6855        assert!(prim_char_ci_eq(&[Value::integer(97), Value::char('a')]).is_err());
6856    }
6857
6858    #[test]
6859    fn test_char_ci_lt() {
6860        // Case-insensitive less than
6861        let result = prim_char_ci_lt(&[Value::char('A'), Value::char('b')]).unwrap();
6862        assert!(matches!(result, Value::Bool(true)));
6863
6864        let result = prim_char_ci_lt(&[Value::char('a'), Value::char('B')]).unwrap();
6865        assert!(matches!(result, Value::Bool(true)));
6866
6867        let result = prim_char_ci_lt(&[Value::char('B'), Value::char('a')]).unwrap();
6868        assert!(matches!(result, Value::Bool(false)));
6869
6870        let result = prim_char_ci_lt(&[Value::char('A'), Value::char('a')]).unwrap();
6871        assert!(matches!(result, Value::Bool(false)));
6872    }
6873
6874    #[test]
6875    fn test_char_ci_gt() {
6876        // Case-insensitive greater than
6877        let result = prim_char_ci_gt(&[Value::char('B'), Value::char('a')]).unwrap();
6878        assert!(matches!(result, Value::Bool(true)));
6879
6880        let result = prim_char_ci_gt(&[Value::char('b'), Value::char('A')]).unwrap();
6881        assert!(matches!(result, Value::Bool(true)));
6882
6883        let result = prim_char_ci_gt(&[Value::char('A'), Value::char('b')]).unwrap();
6884        assert!(matches!(result, Value::Bool(false)));
6885
6886        let result = prim_char_ci_gt(&[Value::char('A'), Value::char('a')]).unwrap();
6887        assert!(matches!(result, Value::Bool(false)));
6888    }
6889
6890    #[test]
6891    fn test_char_ci_le() {
6892        // Case-insensitive less than or equal
6893        let result = prim_char_ci_le(&[Value::char('A'), Value::char('b')]).unwrap();
6894        assert!(matches!(result, Value::Bool(true)));
6895
6896        let result = prim_char_ci_le(&[Value::char('A'), Value::char('a')]).unwrap();
6897        assert!(matches!(result, Value::Bool(true)));
6898
6899        let result = prim_char_ci_le(&[Value::char('B'), Value::char('a')]).unwrap();
6900        assert!(matches!(result, Value::Bool(false)));
6901    }
6902
6903    #[test]
6904    fn test_char_ci_ge() {
6905        // Case-insensitive greater than or equal
6906        let result = prim_char_ci_ge(&[Value::char('B'), Value::char('a')]).unwrap();
6907        assert!(matches!(result, Value::Bool(true)));
6908
6909        let result = prim_char_ci_ge(&[Value::char('A'), Value::char('a')]).unwrap();
6910        assert!(matches!(result, Value::Bool(true)));
6911
6912        let result = prim_char_ci_ge(&[Value::char('A'), Value::char('b')]).unwrap();
6913        assert!(matches!(result, Value::Bool(false)));
6914    }
6915
6916    // =========================================================================
6917    // Boolean and equality primitive tests
6918    // =========================================================================
6919
6920    #[test]
6921    fn test_not() {
6922        let result = prim_not(&[Value::bool(true)]).unwrap();
6923        assert!(matches!(result, Value::Bool(false)));
6924
6925        let result = prim_not(&[Value::bool(false)]).unwrap();
6926        assert!(matches!(result, Value::Bool(true)));
6927
6928        // #f is the only false value in Scheme
6929        let result = prim_not(&[Value::integer(0)]).unwrap();
6930        assert!(matches!(result, Value::Bool(false)));
6931    }
6932
6933    #[test]
6934    fn test_boolean_p() {
6935        assert!(matches!(prim_boolean_p(&[Value::bool(true)]).unwrap(), Value::Bool(true)));
6936        assert!(matches!(prim_boolean_p(&[Value::bool(false)]).unwrap(), Value::Bool(true)));
6937        assert!(matches!(prim_boolean_p(&[Value::integer(1)]).unwrap(), Value::Bool(false)));
6938        assert!(matches!(prim_boolean_p(&[Value::Nil]).unwrap(), Value::Bool(false)));
6939    }
6940
6941    #[test]
6942    fn test_equal_p() {
6943        // Numbers
6944        let result = prim_equal_p(&[Value::integer(42), Value::integer(42)]).unwrap();
6945        assert!(matches!(result, Value::Bool(true)));
6946
6947        let result = prim_equal_p(&[Value::integer(42), Value::integer(43)]).unwrap();
6948        assert!(matches!(result, Value::Bool(false)));
6949
6950        // Strings
6951        let result = prim_equal_p(&[Value::string("hello".to_string()), Value::string("hello".to_string())]).unwrap();
6952        assert!(matches!(result, Value::Bool(true)));
6953
6954        // Lists
6955        let list1 = prim_list(&[Value::integer(1), Value::integer(2)]).unwrap();
6956        let list2 = prim_list(&[Value::integer(1), Value::integer(2)]).unwrap();
6957        let result = prim_equal_p(&[list1, list2]).unwrap();
6958        assert!(matches!(result, Value::Bool(true)));
6959    }
6960
6961    #[test]
6962    fn test_eqv_p() {
6963        // Numbers
6964        let result = prim_eqv_p(&[Value::integer(42), Value::integer(42)]).unwrap();
6965        assert!(matches!(result, Value::Bool(true)));
6966
6967        // Symbols
6968        let sym1 = Value::symbol("foo");
6969        let sym2 = Value::symbol("foo");
6970        let result = prim_eqv_p(&[sym1, sym2]).unwrap();
6971        assert!(matches!(result, Value::Bool(true)));
6972
6973        // Different types
6974        let result = prim_eqv_p(&[Value::integer(42), Value::string("42".to_string())]).unwrap();
6975        assert!(matches!(result, Value::Bool(false)));
6976    }
6977
6978    #[test]
6979    fn test_eq_p() {
6980        // Numbers
6981        let result = prim_eq_p(&[Value::integer(42), Value::integer(42)]).unwrap();
6982        assert!(matches!(result, Value::Bool(true)));
6983
6984        // Symbols
6985        let sym1 = Value::symbol("foo");
6986        let sym2 = Value::symbol("foo");
6987        let result = prim_eq_p(&[sym1, sym2]).unwrap();
6988        assert!(matches!(result, Value::Bool(true)));
6989
6990        // Same pair should be eq?
6991        let pair = Value::cons(Value::integer(1), Value::integer(2));
6992        let result = prim_eq_p(&[pair.clone(), pair]).unwrap();
6993        assert!(matches!(result, Value::Bool(true)));
6994    }
6995
6996    #[test]
6997    fn test_procedure_p() {
6998        // Primitives are procedures
6999        let proc = Value::primitive("+", prim_add);
7000        assert!(matches!(prim_procedure_p(&[proc]).unwrap(), Value::Bool(true)));
7001
7002        // Non-procedures
7003        assert!(matches!(prim_procedure_p(&[Value::integer(42)]).unwrap(), Value::Bool(false)));
7004        assert!(matches!(prim_procedure_p(&[Value::string("hello".to_string())]).unwrap(), Value::Bool(false)));
7005    }
7006
7007    // =========================================================================
7008    // I/O and utility primitive tests
7009    // =========================================================================
7010
7011    #[test]
7012    fn test_error() {
7013        // Error with string message
7014        let result = prim_error(&[Value::string("test error".to_string())]);
7015        assert!(result.is_err());
7016        assert_eq!(result.unwrap_err(), "test error");
7017
7018        // Error with symbol message
7019        let result = prim_error(&[Value::symbol("error-symbol")]);
7020        assert!(result.is_err());
7021        assert!(result.unwrap_err().contains("error-symbol"));
7022
7023        // Error with additional objects
7024        let result = prim_error(&[
7025            Value::string("error:".to_string()),
7026            Value::integer(42),
7027            Value::string("foo".to_string()),
7028        ]);
7029        assert!(result.is_err());
7030        let msg = result.unwrap_err();
7031        assert!(msg.contains("error:"));
7032        assert!(msg.contains("42"));
7033    }
7034
7035    #[test]
7036    fn test_display() {
7037        // Display returns unspecified
7038        let result = prim_display(&[Value::string("test".to_string())]).unwrap();
7039        assert!(matches!(result, Value::Unspecified));
7040
7041        let result = prim_display(&[Value::integer(42)]).unwrap();
7042        assert!(matches!(result, Value::Unspecified));
7043
7044        let result = prim_display(&[Value::Char('x')]).unwrap();
7045        assert!(matches!(result, Value::Unspecified));
7046    }
7047
7048    #[test]
7049    fn test_newline() {
7050        let result = prim_newline(&[]).unwrap();
7051        assert!(matches!(result, Value::Unspecified));
7052
7053        // Should error with arguments
7054        let result = prim_newline(&[Value::integer(1)]);
7055        assert!(result.is_err());
7056    }
7057
7058    #[test]
7059    fn test_write() {
7060        let result = prim_write(&[Value::string("test".to_string())]).unwrap();
7061        assert!(matches!(result, Value::Unspecified));
7062
7063        let result = prim_write(&[Value::integer(42)]).unwrap();
7064        assert!(matches!(result, Value::Unspecified));
7065    }
7066
7067    // =========================================================================
7068    // Conversion primitive tests
7069    // =========================================================================
7070
7071    #[test]
7072    fn test_number_to_string() {
7073        let result = prim_number_to_string(&[Value::integer(42)]).unwrap();
7074        if let Value::String(ref s) = result {
7075            assert_eq!(&***s, "42");
7076        } else {
7077            panic!("Expected string");
7078        }
7079
7080        let result = prim_number_to_string(&[Value::real(3.14)]).unwrap();
7081        if let Value::String(ref s) = result {
7082            assert!(s.starts_with("3.14"));
7083        } else {
7084            panic!("Expected string");
7085        }
7086    }
7087
7088    #[test]
7089    fn test_string_to_number() {
7090        let result = prim_string_to_number(&[Value::string("42".to_string())]).unwrap();
7091        assert!(matches!(result, Value::Integer(42)));
7092
7093        let result = prim_string_to_number(&[Value::string("3.14".to_string())]).unwrap();
7094        assert!(matches!(result, Value::Real(_)));
7095
7096        // Invalid number returns #f
7097        let result = prim_string_to_number(&[Value::string("not-a-number".to_string())]).unwrap();
7098        assert!(matches!(result, Value::Bool(false)));
7099    }
7100
7101    // =========================================================================
7102    // Keyword primitive tests
7103    // =========================================================================
7104
7105    #[test]
7106    fn test_keyword_p() {
7107        let kw = Value::keyword("test");
7108        assert!(matches!(prim_keyword_p(&[kw]).unwrap(), Value::Bool(true)));
7109
7110        assert!(matches!(prim_keyword_p(&[Value::symbol("test")]).unwrap(), Value::Bool(false)));
7111        assert!(matches!(prim_keyword_p(&[Value::string("test".to_string())]).unwrap(), Value::Bool(false)));
7112    }
7113
7114    #[test]
7115    fn test_keyword_to_string() {
7116        let kw = Value::keyword("test");
7117        let result = prim_keyword_to_string(&[kw]).unwrap();
7118        if let Value::String(ref s) = result {
7119            assert_eq!(&***s, "test");
7120        } else {
7121            panic!("Expected string");
7122        }
7123    }
7124
7125    #[test]
7126    fn test_string_to_keyword() {
7127        let result = prim_string_to_keyword(&[Value::string("test".to_string())]).unwrap();
7128        assert!(matches!(result, Value::Keyword(_)));
7129    }
7130
7131    // =========================================================================
7132    // List utility primitive tests
7133    // =========================================================================
7134
7135    #[test]
7136    fn test_memq() {
7137        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
7138        let result = prim_memq(&[Value::integer(2), list.clone()]).unwrap();
7139        assert!(result.is_list());
7140
7141        // Not found
7142        let result = prim_memq(&[Value::integer(99), list]).unwrap();
7143        assert!(matches!(result, Value::Bool(false)));
7144    }
7145
7146    #[test]
7147    fn test_memv() {
7148        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
7149        let result = prim_memv(&[Value::integer(2), list.clone()]).unwrap();
7150        assert!(result.is_list());
7151
7152        // Not found
7153        let result = prim_memv(&[Value::integer(99), list]).unwrap();
7154        assert!(matches!(result, Value::Bool(false)));
7155    }
7156
7157    #[test]
7158    fn test_member() {
7159        let list = prim_list(&[
7160            Value::string("a".to_string()),
7161            Value::string("b".to_string()),
7162            Value::string("c".to_string()),
7163        ])
7164        .unwrap();
7165        let result = prim_member(&[Value::string("b".to_string()), list.clone()]).unwrap();
7166        assert!(result.is_list());
7167
7168        // Not found
7169        let result = prim_member(&[Value::string("z".to_string()), list]).unwrap();
7170        assert!(matches!(result, Value::Bool(false)));
7171    }
7172
7173    #[test]
7174    fn test_assq() {
7175        // Create association list: ((a . 1) (b . 2) (c . 3))
7176        let pair1 = Value::cons(Value::symbol("a"), Value::integer(1));
7177        let pair2 = Value::cons(Value::symbol("b"), Value::integer(2));
7178        let pair3 = Value::cons(Value::symbol("c"), Value::integer(3));
7179        let alist = prim_list(&[pair1, pair2, pair3]).unwrap();
7180
7181        let result = prim_assq(&[Value::symbol("b"), alist.clone()]).unwrap();
7182        assert!(result.is_pair());
7183
7184        // Not found
7185        let result = prim_assq(&[Value::symbol("z"), alist]).unwrap();
7186        assert!(matches!(result, Value::Bool(false)));
7187    }
7188
7189    #[test]
7190    fn test_assv() {
7191        // Create association list: ((1 . a) (2 . b) (3 . c))
7192        let pair1 = Value::cons(Value::integer(1), Value::symbol("a"));
7193        let pair2 = Value::cons(Value::integer(2), Value::symbol("b"));
7194        let pair3 = Value::cons(Value::integer(3), Value::symbol("c"));
7195        let alist = prim_list(&[pair1, pair2, pair3]).unwrap();
7196
7197        let result = prim_assv(&[Value::integer(2), alist.clone()]).unwrap();
7198        assert!(result.is_pair());
7199
7200        // Not found
7201        let result = prim_assv(&[Value::integer(99), alist]).unwrap();
7202        assert!(matches!(result, Value::Bool(false)));
7203    }
7204
7205    #[test]
7206    fn test_assoc() {
7207        // Create association list: (("a" . 1) ("b" . 2) ("c" . 3))
7208        let pair1 = Value::cons(Value::string("a".to_string()), Value::integer(1));
7209        let pair2 = Value::cons(Value::string("b".to_string()), Value::integer(2));
7210        let pair3 = Value::cons(Value::string("c".to_string()), Value::integer(3));
7211        let alist = prim_list(&[pair1, pair2, pair3]).unwrap();
7212
7213        let result = prim_assoc(&[Value::string("b".to_string()), alist.clone()]).unwrap();
7214        assert!(result.is_pair());
7215
7216        // Not found
7217        let result = prim_assoc(&[Value::string("z".to_string()), alist]).unwrap();
7218        assert!(matches!(result, Value::Bool(false)));
7219    }
7220
7221    // =========================================================================
7222    // cXXr combination tests
7223    // =========================================================================
7224
7225    #[test]
7226    fn test_cadr() {
7227        // (cadr '(1 2 3)) => 2
7228        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
7229        let result = prim_cadr(&[list]).unwrap();
7230        assert!(matches!(result, Value::Integer(2)));
7231    }
7232
7233    #[test]
7234    fn test_caddr() {
7235        // (caddr '(1 2 3 4)) => 3
7236        let list = prim_list(&[
7237            Value::integer(1),
7238            Value::integer(2),
7239            Value::integer(3),
7240            Value::integer(4),
7241        ])
7242        .unwrap();
7243        let result = prim_caddr(&[list]).unwrap();
7244        assert!(matches!(result, Value::Integer(3)));
7245    }
7246
7247    #[test]
7248    fn test_cadddr() {
7249        // (cadddr '(1 2 3 4 5)) => 4
7250        let list = prim_list(&[
7251            Value::integer(1),
7252            Value::integer(2),
7253            Value::integer(3),
7254            Value::integer(4),
7255            Value::integer(5),
7256        ])
7257        .unwrap();
7258        let result = prim_cadddr(&[list]).unwrap();
7259        assert!(matches!(result, Value::Integer(4)));
7260    }
7261
7262    #[test]
7263    fn test_caar() {
7264        // (caar '((1 2) 3)) => 1
7265        let inner = prim_list(&[Value::integer(1), Value::integer(2)]).unwrap();
7266        let outer = prim_list(&[inner, Value::integer(3)]).unwrap();
7267        let result = prim_caar(&[outer]).unwrap();
7268        assert!(matches!(result, Value::Integer(1)));
7269    }
7270
7271    #[test]
7272    fn test_cddr() {
7273        // (cddr '(1 2 3 4)) => (3 4)
7274        let list = prim_list(&[
7275            Value::integer(1),
7276            Value::integer(2),
7277            Value::integer(3),
7278            Value::integer(4),
7279        ])
7280        .unwrap();
7281        let result = prim_cddr(&[list]).unwrap();
7282        assert!(result.is_list());
7283        // First element should be 3
7284        let first = prim_car(&[result.clone()]).unwrap();
7285        assert!(matches!(first, Value::Integer(3)));
7286    }
7287
7288    #[test]
7289    fn test_cdar() {
7290        // (cdar '((1 2 3) 4)) => (2 3)
7291        let inner = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
7292        let outer = prim_list(&[inner, Value::integer(4)]).unwrap();
7293        let result = prim_cdar(&[outer]).unwrap();
7294        assert!(result.is_list());
7295        let first = prim_car(&[result]).unwrap();
7296        assert!(matches!(first, Value::Integer(2)));
7297    }
7298
7299    #[test]
7300    fn test_caaar() {
7301        // (caaar '(((1 2) 3) 4)) => 1
7302        let innermost = prim_list(&[Value::integer(1), Value::integer(2)]).unwrap();
7303        let middle = prim_list(&[innermost, Value::integer(3)]).unwrap();
7304        let outer = prim_list(&[middle, Value::integer(4)]).unwrap();
7305        let result = prim_caaar(&[outer]).unwrap();
7306        assert!(matches!(result, Value::Integer(1)));
7307    }
7308
7309    #[test]
7310    fn test_cdaar() {
7311        // (cdaar '(((1 2) 3) 4)) => (2)
7312        let innermost = prim_list(&[Value::integer(1), Value::integer(2)]).unwrap();
7313        let middle = prim_list(&[innermost, Value::integer(3)]).unwrap();
7314        let outer = prim_list(&[middle, Value::integer(4)]).unwrap();
7315        let result = prim_cdaar(&[outer]).unwrap();
7316        assert!(result.is_list());
7317        let first = prim_car(&[result]).unwrap();
7318        assert!(matches!(first, Value::Integer(2)));
7319    }
7320
7321    #[test]
7322    fn test_cadar() {
7323        // (cadar '((1 2 3) 4)) => 2
7324        let inner = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
7325        let outer = prim_list(&[inner, Value::integer(4)]).unwrap();
7326        let result = prim_cadar(&[outer]).unwrap();
7327        assert!(matches!(result, Value::Integer(2)));
7328    }
7329
7330    #[test]
7331    fn test_cddar() {
7332        // (cddar '((1 2 3 4) 5)) => (3 4)
7333        let inner = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3), Value::integer(4)]).unwrap();
7334        let outer = prim_list(&[inner, Value::integer(5)]).unwrap();
7335        let result = prim_cddar(&[outer]).unwrap();
7336        assert!(result.is_list());
7337        let first = prim_car(&[result]).unwrap();
7338        assert!(matches!(first, Value::Integer(3)));
7339    }
7340
7341    #[test]
7342    fn test_caadr() {
7343        // (caadr '(1 (2 3) 4)) => 2
7344        let inner = prim_list(&[Value::integer(2), Value::integer(3)]).unwrap();
7345        let outer = prim_list(&[Value::integer(1), inner, Value::integer(4)]).unwrap();
7346        let result = prim_caadr(&[outer]).unwrap();
7347        assert!(matches!(result, Value::Integer(2)));
7348    }
7349
7350    #[test]
7351    fn test_cdadr() {
7352        // (cdadr '(1 (2 3 4) 5)) => (3 4)
7353        let inner = prim_list(&[Value::integer(2), Value::integer(3), Value::integer(4)]).unwrap();
7354        let outer = prim_list(&[Value::integer(1), inner, Value::integer(5)]).unwrap();
7355        let result = prim_cdadr(&[outer]).unwrap();
7356        assert!(result.is_list());
7357        let first = prim_car(&[result]).unwrap();
7358        assert!(matches!(first, Value::Integer(3)));
7359    }
7360
7361    #[test]
7362    fn test_last() {
7363        // (last '(1 2 3 4)) => 4
7364        let list = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3), Value::integer(4)]).unwrap();
7365        let result = prim_last(&[list]).unwrap();
7366        assert!(matches!(result, Value::Integer(4)));
7367
7368        // (last '(1)) => 1
7369        let single = prim_list(&[Value::integer(1)]).unwrap();
7370        let result = prim_last(&[single]).unwrap();
7371        assert!(matches!(result, Value::Integer(1)));
7372
7373        // Empty list should error
7374        assert!(prim_last(&[Value::Nil]).is_err());
7375    }
7376
7377    // =========================================================================
7378    // DSSSL type stub tests
7379    // =========================================================================
7380
7381    #[test]
7382    fn test_quantity_p() {
7383        // All types should return #f since quantities are not implemented
7384        assert!(matches!(prim_quantity_p(&[Value::integer(1)]).unwrap(), Value::Bool(false)));
7385        assert!(matches!(prim_quantity_p(&[Value::string("10pt".to_string())]).unwrap(), Value::Bool(false)));
7386        assert!(matches!(prim_quantity_p(&[Value::symbol("quantity")]).unwrap(), Value::Bool(false)));
7387    }
7388
7389    #[test]
7390    fn test_color_p() {
7391        // All types should return #f since colors are not implemented
7392        assert!(matches!(prim_color_p(&[Value::string("red".to_string())]).unwrap(), Value::Bool(false)));
7393        assert!(matches!(prim_color_p(&[Value::integer(0)]).unwrap(), Value::Bool(false)));
7394        assert!(matches!(prim_color_p(&[Value::symbol("color")]).unwrap(), Value::Bool(false)));
7395    }
7396
7397    #[test]
7398    fn test_address_p() {
7399        // All types should return #f since addresses are not implemented
7400        assert!(matches!(prim_address_p(&[Value::string("addr".to_string())]).unwrap(), Value::Bool(false)));
7401        assert!(matches!(prim_address_p(&[Value::integer(0)]).unwrap(), Value::Bool(false)));
7402        assert!(matches!(prim_address_p(&[Value::symbol("address")]).unwrap(), Value::Bool(false)));
7403    }
7404
7405    // =========================================================================
7406    // Format function tests
7407    // =========================================================================
7408
7409    #[test]
7410    fn test_format_number_decimal() {
7411        let result = prim_format_number(&[Value::integer(42), Value::string("1".to_string())]).unwrap();
7412        if let Value::String(ref s) = result {
7413            assert_eq!(&***s, "42");
7414        } else {
7415            panic!("Expected string");
7416        }
7417    }
7418
7419    #[test]
7420    fn test_format_number_roman_upper() {
7421        let result = prim_format_number(&[Value::integer(5), Value::string("I".to_string())]).unwrap();
7422        if let Value::String(ref s) = result {
7423            assert_eq!(&***s, "V");
7424        } else {
7425            panic!("Expected string");
7426        }
7427
7428        let result = prim_format_number(&[Value::integer(10), Value::string("I".to_string())]).unwrap();
7429        if let Value::String(ref s) = result {
7430            assert_eq!(&***s, "X");
7431        } else {
7432            panic!("Expected string");
7433        }
7434    }
7435
7436    #[test]
7437    fn test_format_number_roman_lower() {
7438        let result = prim_format_number(&[Value::integer(3), Value::string("i".to_string())]).unwrap();
7439        if let Value::String(ref s) = result {
7440            assert_eq!(&***s, "iii");
7441        } else {
7442            panic!("Expected string");
7443        }
7444    }
7445
7446    #[test]
7447    fn test_format_number_alpha_upper() {
7448        let result = prim_format_number(&[Value::integer(1), Value::string("A".to_string())]).unwrap();
7449        if let Value::String(ref s) = result {
7450            assert_eq!(&***s, "A");
7451        } else {
7452            panic!("Expected string");
7453        }
7454
7455        let result = prim_format_number(&[Value::integer(26), Value::string("A".to_string())]).unwrap();
7456        if let Value::String(ref s) = result {
7457            assert_eq!(&***s, "Z");
7458        } else {
7459            panic!("Expected string");
7460        }
7461    }
7462
7463    #[test]
7464    fn test_format_number_alpha_lower() {
7465        let result = prim_format_number(&[Value::integer(1), Value::string("a".to_string())]).unwrap();
7466        if let Value::String(ref s) = result {
7467            assert_eq!(&***s, "a");
7468        } else {
7469            panic!("Expected string");
7470        }
7471
7472        let result = prim_format_number(&[Value::integer(3), Value::string("a".to_string())]).unwrap();
7473        if let Value::String(ref s) = result {
7474            assert_eq!(&***s, "c");
7475        } else {
7476            panic!("Expected string");
7477        }
7478    }
7479
7480    #[test]
7481    fn test_format_number_list() {
7482        let nums = prim_list(&[Value::integer(1), Value::integer(2), Value::integer(3)]).unwrap();
7483        let result = prim_format_number_list(&[nums.clone()]).unwrap();
7484        if let Value::String(ref s) = result {
7485            assert_eq!(&***s, "1.2.3");
7486        } else {
7487            panic!("Expected string");
7488        }
7489
7490        // With custom separator
7491        let result = prim_format_number_list(&[nums, Value::string("-".to_string())]).unwrap();
7492        if let Value::String(ref s) = result {
7493            assert_eq!(&***s, "1-2-3");
7494        } else {
7495            panic!("Expected string");
7496        }
7497    }
7498
7499    // =========================================================================
7500    // Grove query primitive tests
7501    // =========================================================================
7502
7503    #[test]
7504    fn test_node_list_p() {
7505        // NodeList is a node-list
7506        let nl = prim_empty_node_list(&[]).unwrap();
7507        assert!(matches!(prim_node_list_p(&[nl]).unwrap(), Value::Bool(true)));
7508
7509        // Other types are not node-lists
7510        assert!(matches!(prim_node_list_p(&[Value::integer(1)]).unwrap(), Value::Bool(false)));
7511        assert!(matches!(prim_node_list_p(&[Value::string("test".to_string())]).unwrap(), Value::Bool(false)));
7512        assert!(matches!(prim_node_list_p(&[Value::Nil]).unwrap(), Value::Bool(false)));
7513    }
7514
7515    #[test]
7516    fn test_empty_node_list() {
7517        let result = prim_empty_node_list(&[]).unwrap();
7518        assert!(matches!(result, Value::NodeList(_)));
7519    }
7520
7521    #[test]
7522    fn test_node_list_empty_p() {
7523        // Empty node-list is empty
7524        let nl = prim_empty_node_list(&[]).unwrap();
7525        let result = prim_node_list_empty_p(&[nl]).unwrap();
7526        assert!(matches!(result, Value::Bool(true)));
7527
7528        // Non-node-list should error
7529        assert!(prim_node_list_empty_p(&[Value::integer(1)]).is_err());
7530    }
7531
7532    #[test]
7533    fn test_node_list_length() {
7534        // Empty node-list has length 0
7535        let nl = prim_empty_node_list(&[]).unwrap();
7536        let result = prim_node_list_length(&[nl]).unwrap();
7537        assert!(matches!(result, Value::Integer(0)));
7538
7539        // Non-node-list should error
7540        assert!(prim_node_list_length(&[Value::integer(1)]).is_err());
7541    }
7542
7543    #[test]
7544    fn test_node_list_first() {
7545        // Empty node-list returns #f
7546        let nl = prim_empty_node_list(&[]).unwrap();
7547        let result = prim_node_list_first(&[nl]).unwrap();
7548        assert!(matches!(result, Value::Bool(false)));
7549
7550        // Non-node-list should error
7551        assert!(prim_node_list_first(&[Value::integer(1)]).is_err());
7552    }
7553
7554    #[test]
7555    fn test_node_list_rest() {
7556        // Rest of empty node-list is empty node-list
7557        let nl = prim_empty_node_list(&[]).unwrap();
7558        let result = prim_node_list_rest(&[nl]).unwrap();
7559        assert!(matches!(result, Value::NodeList(_)));
7560
7561        // Non-node-list should error
7562        assert!(prim_node_list_rest(&[Value::integer(1)]).is_err());
7563    }
7564
7565    #[test]
7566    fn test_node_list_ref() {
7567        // Out-of-bounds on empty node-list returns #f
7568        let nl = prim_empty_node_list(&[]).unwrap();
7569        let result = prim_node_list_ref(&[nl, Value::integer(0)]).unwrap();
7570        assert!(matches!(result, Value::Bool(false)));
7571
7572        // Non-node-list should error
7573        assert!(prim_node_list_ref(&[Value::integer(1), Value::integer(0)]).is_err());
7574
7575        // Non-integer index should error
7576        let nl = prim_empty_node_list(&[]).unwrap();
7577        assert!(prim_node_list_ref(&[nl, Value::string("x".to_string())]).is_err());
7578    }
7579
7580    #[test]
7581    fn test_node_list_reverse() {
7582        // Reverse of empty node-list is empty node-list
7583        let nl = prim_empty_node_list(&[]).unwrap();
7584        let result = prim_node_list_reverse(&[nl]).unwrap();
7585        assert!(matches!(result, Value::NodeList(_)));
7586
7587        // Non-node-list should error
7588        assert!(prim_node_list_reverse(&[Value::integer(1)]).is_err());
7589    }
7590
7591    // =========================================================================
7592    // Math primitive tests
7593    // =========================================================================
7594
7595    #[test]
7596    fn test_expt() {
7597        // Integer exponentiation
7598        let result = prim_expt(&[Value::integer(2), Value::integer(3)]).unwrap();
7599        assert!(matches!(result, Value::Integer(8)));
7600
7601        // Real exponentiation
7602        let result = prim_expt(&[Value::real(2.0), Value::integer(3)]).unwrap();
7603        assert!(matches!(result, Value::Real(r) if (r - 8.0).abs() < 0.001));
7604
7605        // Fractional exponent
7606        let result = prim_expt(&[Value::integer(4), Value::real(0.5)]).unwrap();
7607        assert!(matches!(result, Value::Real(r) if (r - 2.0).abs() < 0.001));
7608
7609        // Non-number should error
7610        assert!(prim_expt(&[Value::string("x".to_string()), Value::integer(2)]).is_err());
7611    }
7612
7613    #[test]
7614    fn test_sqrt() {
7615        // Square root of integer
7616        let result = prim_sqrt(&[Value::integer(16)]).unwrap();
7617        assert!(matches!(result, Value::Real(r) if (r - 4.0).abs() < 0.001));
7618
7619        // Square root of real
7620        let result = prim_sqrt(&[Value::real(2.0)]).unwrap();
7621        assert!(matches!(result, Value::Real(r) if (r - 1.414).abs() < 0.01));
7622
7623        // Negative should error
7624        assert!(prim_sqrt(&[Value::integer(-1)]).is_err());
7625
7626        // Non-number should error
7627        assert!(prim_sqrt(&[Value::string("x".to_string())]).is_err());
7628    }
7629
7630    #[test]
7631    fn test_sin() {
7632        // sin(0) = 0
7633        let result = prim_sin(&[Value::integer(0)]).unwrap();
7634        assert!(matches!(result, Value::Real(r) if r.abs() < 0.001));
7635
7636        // sin(π/2) ≈ 1
7637        let result = prim_sin(&[Value::real(std::f64::consts::PI / 2.0)]).unwrap();
7638        assert!(matches!(result, Value::Real(r) if (r - 1.0).abs() < 0.001));
7639
7640        // Non-number should error
7641        assert!(prim_sin(&[Value::string("x".to_string())]).is_err());
7642    }
7643
7644    #[test]
7645    fn test_cos() {
7646        // cos(0) = 1
7647        let result = prim_cos(&[Value::integer(0)]).unwrap();
7648        assert!(matches!(result, Value::Real(r) if (r - 1.0).abs() < 0.001));
7649
7650        // cos(π) ≈ -1
7651        let result = prim_cos(&[Value::real(std::f64::consts::PI)]).unwrap();
7652        assert!(matches!(result, Value::Real(r) if (r + 1.0).abs() < 0.001));
7653
7654        // Non-number should error
7655        assert!(prim_cos(&[Value::string("x".to_string())]).is_err());
7656    }
7657
7658    #[test]
7659    fn test_tan() {
7660        // tan(0) = 0
7661        let result = prim_tan(&[Value::integer(0)]).unwrap();
7662        assert!(matches!(result, Value::Real(r) if r.abs() < 0.001));
7663
7664        // tan(π/4) ≈ 1
7665        let result = prim_tan(&[Value::real(std::f64::consts::PI / 4.0)]).unwrap();
7666        assert!(matches!(result, Value::Real(r) if (r - 1.0).abs() < 0.001));
7667
7668        // Non-number should error
7669        assert!(prim_tan(&[Value::string("x".to_string())]).is_err());
7670    }
7671
7672    #[test]
7673    fn test_atan() {
7674        // atan(0) = 0
7675        let result = prim_atan(&[Value::integer(0)]).unwrap();
7676        assert!(matches!(result, Value::Real(r) if r.abs() < 0.001));
7677
7678        // atan(1) ≈ π/4
7679        let result = prim_atan(&[Value::integer(1)]).unwrap();
7680        assert!(matches!(result, Value::Real(r) if (r - std::f64::consts::PI / 4.0).abs() < 0.001));
7681
7682        // Non-number should error
7683        assert!(prim_atan(&[Value::string("x".to_string())]).is_err());
7684    }
7685
7686    #[test]
7687    fn test_log() {
7688        // log(e) ≈ 1
7689        let result = prim_log(&[Value::real(std::f64::consts::E)]).unwrap();
7690        assert!(matches!(result, Value::Real(r) if (r - 1.0).abs() < 0.001));
7691
7692        // log(1) = 0
7693        let result = prim_log(&[Value::integer(1)]).unwrap();
7694        assert!(matches!(result, Value::Real(r) if r.abs() < 0.001));
7695
7696        // Negative should error
7697        assert!(prim_log(&[Value::integer(-1)]).is_err());
7698
7699        // Non-number should error
7700        assert!(prim_log(&[Value::string("x".to_string())]).is_err());
7701    }
7702
7703    #[test]
7704    fn test_exp() {
7705        // exp(0) = 1
7706        let result = prim_exp(&[Value::integer(0)]).unwrap();
7707        assert!(matches!(result, Value::Real(r) if (r - 1.0).abs() < 0.001));
7708
7709        // exp(1) ≈ e
7710        let result = prim_exp(&[Value::integer(1)]).unwrap();
7711        assert!(matches!(result, Value::Real(r) if (r - std::f64::consts::E).abs() < 0.001));
7712
7713        // Non-number should error
7714        assert!(prim_exp(&[Value::string("x".to_string())]).is_err());
7715    }
7716
7717    // =========================================================================
7718    // Sosofo primitive tests
7719    // =========================================================================
7720
7721    #[test]
7722    fn test_sosofo_p() {
7723        // Sosofo is a sosofo
7724        assert!(matches!(prim_sosofo_p(&[Value::Sosofo]).unwrap(), Value::Bool(true)));
7725
7726        // Other types are not sosofos
7727        assert!(matches!(prim_sosofo_p(&[Value::integer(1)]).unwrap(), Value::Bool(false)));
7728        assert!(matches!(prim_sosofo_p(&[Value::string("test".to_string())]).unwrap(), Value::Bool(false)));
7729        let nl = prim_empty_node_list(&[]).unwrap();
7730        assert!(matches!(prim_sosofo_p(&[nl]).unwrap(), Value::Bool(false)));
7731    }
7732
7733    #[test]
7734    fn test_empty_sosofo() {
7735        let result = prim_empty_sosofo(&[]).unwrap();
7736        assert!(matches!(result, Value::Sosofo));
7737    }
7738
7739    #[test]
7740    fn test_literal() {
7741        let result = prim_literal(&[Value::string("hello".to_string())]).unwrap();
7742        assert!(matches!(result, Value::Sosofo));
7743
7744        // Non-string should error
7745        assert!(prim_literal(&[Value::integer(1)]).is_err());
7746    }
7747
7748    #[test]
7749    fn test_sosofo_append() {
7750        // Append sosofos
7751        let s1 = prim_empty_sosofo(&[]).unwrap();
7752        let s2 = prim_empty_sosofo(&[]).unwrap();
7753        let result = prim_sosofo_append(&[s1, s2]).unwrap();
7754        assert!(matches!(result, Value::Sosofo));
7755
7756        // Non-sosofo should error
7757        assert!(prim_sosofo_append(&[Value::Sosofo, Value::integer(1)]).is_err());
7758    }
7759
7760    // =========================================================================
7761    // String utility tests
7762    // =========================================================================
7763
7764    #[test]
7765    fn test_string_equiv_p() {
7766        // Case-insensitive comparison
7767        let result = prim_string_equiv_p(&[
7768            Value::string("Hello".to_string()),
7769            Value::string("hello".to_string()),
7770        ])
7771        .unwrap();
7772        assert!(matches!(result, Value::Bool(true)));
7773
7774        let result = prim_string_equiv_p(&[
7775            Value::string("HELLO".to_string()),
7776            Value::string("hello".to_string()),
7777        ])
7778        .unwrap();
7779        assert!(matches!(result, Value::Bool(true)));
7780
7781        let result = prim_string_equiv_p(&[
7782            Value::string("hello".to_string()),
7783            Value::string("world".to_string()),
7784        ])
7785        .unwrap();
7786        assert!(matches!(result, Value::Bool(false)));
7787
7788        // Non-string should error
7789        assert!(prim_string_equiv_p(&[Value::integer(1), Value::string("test".to_string())]).is_err());
7790    }
7791
7792    // =========================================================================
7793    // Time primitive tests
7794    // =========================================================================
7795
7796    #[test]
7797    fn test_time() {
7798        // Stub: returns unspecified
7799        let result = prim_time(&[]).unwrap();
7800        assert!(matches!(result, Value::Unspecified));
7801    }
7802
7803    #[test]
7804    fn test_time_to_string() {
7805        // Stub: returns empty string
7806        let result = prim_time_to_string(&[Value::Unspecified, Value::string("".to_string())]).unwrap();
7807        if let Value::String(ref s) = result {
7808            assert_eq!(&***s, "");
7809        } else {
7810            panic!("Expected string");
7811        }
7812    }
7813
7814    // =========================================================================
7815    // Language primitive tests
7816    // =========================================================================
7817
7818    #[test]
7819    fn test_language_p() {
7820        // Stub: no language type, always returns #f
7821        assert!(matches!(prim_language_p(&[Value::Unspecified]).unwrap(), Value::Bool(false)));
7822        assert!(matches!(prim_language_p(&[Value::string("en".to_string())]).unwrap(), Value::Bool(false)));
7823    }
7824
7825    #[test]
7826    fn test_current_language() {
7827        // Stub: returns unspecified
7828        let result = prim_current_language(&[]).unwrap();
7829        assert!(matches!(result, Value::Unspecified));
7830    }
7831}