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