dazzle_core/scheme/
value.rs

1//! Scheme value types
2//!
3//! This module defines the `Value` enum, which represents all Scheme values.
4//! Corresponds to OpenJade's `ELObj` class hierarchy.
5//!
6//! ## Design
7//!
8//! Like OpenJade, we use garbage collection for heap-allocated values:
9//! - OpenJade: Custom mark-and-sweep collector (`Collector` class)
10//! - Dazzle: Rust `gc` crate (conservative GC)
11//!
12//! ## Value Types
13//!
14//! **Basic types** (R4RS Scheme):
15//! - Nil: Empty list `()`
16//! - Bool: `#t` and `#f`
17//! - Integer: Exact integers (i64)
18//! - Real: Inexact reals (f64)
19//! - Char: Unicode characters
20//! - String: Immutable strings
21//! - Symbol: Interned identifiers
22//! - Pair: Cons cells (car, cdr)
23//! - Vector: Arrays
24//! - Procedure: Functions (built-in or user-defined)
25//!
26//! **DSSSL types** (code generation):
27//! - NodeList: Document tree node collections
28//! - Sosofo: Flow object sequences
29//!
30//! **DSSSL types** (document formatting - stubs for now):
31//! - Quantity, Color, Address, etc.
32
33// Suppress warnings from gc_derive macro (third-party crate issue)
34#![allow(non_local_definitions)]
35
36use gc::{Gc, GcCell};
37use std::fmt;
38use std::rc::Rc;
39
40/// A Scheme value
41///
42/// Corresponds to OpenJade's `ELObj` base class.
43///
44/// **Memory management**: Uses `Gc<T>` for heap-allocated values.
45/// The `gc` crate provides conservative garbage collection similar
46/// to OpenJade's `Collector`.
47///
48/// **Note**: We manually implement Trace/Finalize because Node and NodeList
49/// variants use Rc (not Gc) and contain trait objects.
50#[derive(Clone)]
51pub enum Value {
52    /// The empty list `()`
53    ///
54    /// OpenJade: `NilObj`
55    Nil,
56
57    /// Boolean: `#t` or `#f`
58    ///
59    /// OpenJade: `TrueObj` / `FalseObj`
60    Bool(bool),
61
62    /// Exact integer
63    ///
64    /// OpenJade: `IntegerObj` (long n_)
65    Integer(i64),
66
67    /// Inexact real number
68    ///
69    /// OpenJade: `RealObj` (double n_)
70    Real(f64),
71
72    /// Unicode character
73    ///
74    /// OpenJade: `CharObj` (Char ch_)
75    Char(char),
76
77    /// Immutable string
78    ///
79    /// OpenJade: `StringObj` (extends StringC)
80    ///
81    /// Using `Gc<String>` for garbage collection.
82    String(Gc<String>),
83
84    /// Interned symbol
85    ///
86    /// OpenJade: `SymbolObj` (StringObj* name_)
87    ///
88    /// Symbols are interned (shared) for efficiency.
89    /// Using `Rc<str>` since symbols are immutable and shared.
90    Symbol(Rc<str>),
91
92    /// Keyword (DSSSL extension)
93    ///
94    /// OpenJade: `KeywordObj`
95    ///
96    /// Keywords are like symbols but in a separate namespace.
97    Keyword(Rc<str>),
98
99    /// Cons cell (pair)
100    ///
101    /// OpenJade: `PairObj` (ELObj* car_, ELObj* cdr_)
102    ///
103    /// Using `Gc<PairData>` for garbage-collected pairs.
104    /// `GcCell` allows mutation (for set-car!/set-cdr!).
105    Pair(Gc<GcCell<PairData>>),
106
107    /// Vector (array)
108    ///
109    /// OpenJade: `VectorObj` (Vector<ELObj*>)
110    ///
111    /// Using `Gc<GcCell<Vec<Value>>>` for mutable vectors.
112    Vector(Gc<GcCell<Vec<Value>>>),
113
114    /// Procedure (function)
115    ///
116    /// OpenJade: `FunctionObj` (various subclasses)
117    ///
118    /// Can be:
119    /// - Built-in primitive (Rust function)
120    /// - User-defined lambda (compiled bytecode or AST)
121    Procedure(Gc<Procedure>),
122
123    // DSSSL types (grove and flow objects)
124    /// A node in the document grove
125    ///
126    /// Represents a node from the XML document tree.
127    /// Corresponds to OpenJade's node objects in the grove.
128    ///
129    /// **Phase 3**: Now fully implemented with libxml2 grove.
130    ///
131    /// Uses `Rc<Box<dyn Node>>` instead of Gc because:
132    /// - Nodes are owned by the grove, not the Scheme GC
133    /// - Grove lifetime is managed separately
134    /// - Trait objects can't derive Trace automatically
135    ///
136    /// NOTE: Managed by Rc, not GC - see manual Trace impl below
137    Node(Rc<Box<dyn crate::grove::Node>>),
138
139    /// Node list (grove query result)
140    ///
141    /// Represents a collection of nodes from grove queries.
142    /// Corresponds to DSSSL node-list objects.
143    ///
144    /// **Phase 3**: Now fully implemented with libxml2 grove.
145    ///
146    /// Uses `Rc<Box<dyn NodeList>>` for same reasons as Node.
147    ///
148    /// NOTE: Managed by Rc, not GC - see manual Trace impl below
149    NodeList(Rc<Box<dyn crate::grove::NodeList>>),
150
151    /// Sosofo (flow object sequence)
152    ///
153    /// Placeholder - will be properly implemented in Phase 4.
154    Sosofo,
155
156    /// Unspecified value
157    ///
158    /// Returned by expressions with unspecified results (like set!).
159    ///
160    /// OpenJade: `UnspecifiedObj`
161    Unspecified,
162
163    /// Error marker
164    ///
165    /// Used internally for error propagation.
166    ///
167    /// OpenJade: `ErrorObj`
168    Error,
169}
170
171/// Pair data (car and cdr)
172///
173/// Separated from `Value::Pair` to allow mutation via `GcCell`.
174#[derive(Clone, gc::Trace, gc::Finalize)]
175pub struct PairData {
176    pub car: Value,
177    pub cdr: Value,
178}
179
180impl PairData {
181    pub fn new(car: Value, cdr: Value) -> Self {
182        PairData { car, cdr }
183    }
184}
185
186/// Procedure (function)
187///
188/// Can be either a built-in primitive or user-defined lambda.
189#[derive(gc::Finalize)]
190pub enum Procedure {
191    /// Built-in primitive function
192    ///
193    /// Takes arguments and returns a result.
194    /// Primitives can fail (return Err) or succeed (return Ok(Value)).
195    Primitive {
196        name: &'static str,
197        func: fn(&[Value]) -> Result<Value, String>,
198    },
199
200    /// User-defined lambda
201    ///
202    /// Captures:
203    /// - `params`: Parameter names (formal parameters)
204    /// - `body`: Expression to evaluate when called
205    /// - `env`: Closure environment (captures lexical scope)
206    Lambda {
207        params: Gc<Vec<String>>,
208        body: Gc<Value>,
209        env: Gc<crate::scheme::environment::Environment>,
210    },
211}
212
213impl Clone for Procedure {
214    fn clone(&self) -> Self {
215        match self {
216            Procedure::Primitive { name, func } => Procedure::Primitive {
217                name,
218                func: *func,
219            },
220            Procedure::Lambda { params, body, env } => Procedure::Lambda {
221                params: params.clone(),
222                body: body.clone(),
223                env: env.clone(),
224            },
225        }
226    }
227}
228
229// Manual Trace implementation since function pointers don't need tracing
230unsafe impl gc::Trace for Procedure {
231    unsafe fn trace(&self) {
232        match self {
233            Procedure::Primitive { .. } => {
234                // Primitives don't have GC'd data
235            }
236            Procedure::Lambda { params, body, env } => {
237                // Trace the lambda's garbage-collected fields
238                params.trace();
239                body.trace();
240                env.trace();
241            }
242        }
243    }
244
245    unsafe fn root(&self) {
246        match self {
247            Procedure::Primitive { .. } => {}
248            Procedure::Lambda { params, body, env } => {
249                params.root();
250                body.root();
251                env.root();
252            }
253        }
254    }
255
256    unsafe fn unroot(&self) {
257        match self {
258            Procedure::Primitive { .. } => {}
259            Procedure::Lambda { params, body, env } => {
260                params.unroot();
261                body.unroot();
262                env.unroot();
263            }
264        }
265    }
266
267    fn finalize_glue(&self) {
268        gc::Finalize::finalize(self);
269    }
270}
271
272// =============================================================================
273// Value constructors (ergonomic API)
274// =============================================================================
275
276impl Value {
277    /// Create a boolean value
278    pub fn bool(b: bool) -> Self {
279        Value::Bool(b)
280    }
281
282    /// Create an integer value
283    pub fn integer(n: i64) -> Self {
284        Value::Integer(n)
285    }
286
287    /// Create a real value
288    pub fn real(n: f64) -> Self {
289        Value::Real(n)
290    }
291
292    /// Create a character value
293    pub fn char(ch: char) -> Self {
294        Value::Char(ch)
295    }
296
297    /// Create a string value
298    pub fn string(s: String) -> Self {
299        Value::String(Gc::new(s))
300    }
301
302    /// Create a symbol value
303    pub fn symbol(s: &str) -> Self {
304        Value::Symbol(Rc::from(s))
305    }
306
307    /// Create a keyword value
308    pub fn keyword(s: &str) -> Self {
309        Value::Keyword(Rc::from(s))
310    }
311
312    /// Create a cons cell (pair)
313    pub fn cons(car: Value, cdr: Value) -> Self {
314        Value::Pair(Gc::new(GcCell::new(PairData::new(car, cdr))))
315    }
316
317    /// Create a vector
318    pub fn vector(elements: Vec<Value>) -> Self {
319        Value::Vector(Gc::new(GcCell::new(elements)))
320    }
321
322    /// Create a built-in primitive procedure
323    pub fn primitive(name: &'static str, func: fn(&[Value]) -> Result<Value, String>) -> Self {
324        Value::Procedure(Gc::new(Procedure::Primitive { name, func }))
325    }
326
327    /// Create a user-defined lambda procedure
328    pub fn lambda(
329        params: Vec<String>,
330        body: Value,
331        env: Gc<crate::scheme::environment::Environment>,
332    ) -> Self {
333        Value::Procedure(Gc::new(Procedure::Lambda {
334            params: Gc::new(params),
335            body: Gc::new(body),
336            env,
337        }))
338    }
339
340    /// Create a node value
341    pub fn node(node: Box<dyn crate::grove::Node>) -> Self {
342        Value::Node(Rc::new(node))
343    }
344
345    /// Create a node list value
346    pub fn node_list(node_list: Box<dyn crate::grove::NodeList>) -> Self {
347        Value::NodeList(Rc::new(node_list))
348    }
349}
350
351// =============================================================================
352// Value equality (Scheme equal? and eqv?)
353// =============================================================================
354
355impl Value {
356    /// Scheme `equal?` - Deep structural equality
357    ///
358    /// Corresponds to OpenJade's `ELObj::equal()`
359    ///
360    /// Recursively compares:
361    /// - Lists and vectors: Element-wise comparison
362    /// - Strings: Content comparison
363    /// - Numbers: Numeric equality
364    /// - Everything else: Same as `eqv?`
365    pub fn equal(&self, other: &Value) -> bool {
366        match (self, other) {
367            // Structural equality for lists
368            (Value::Pair(p1), Value::Pair(p2)) => {
369                let pair1 = p1.borrow();
370                let pair2 = p2.borrow();
371                pair1.car.equal(&pair2.car) && pair1.cdr.equal(&pair2.cdr)
372            }
373
374            // Structural equality for vectors
375            (Value::Vector(v1), Value::Vector(v2)) => {
376                let vec1 = v1.borrow();
377                let vec2 = v2.borrow();
378                if vec1.len() != vec2.len() {
379                    return false;
380                }
381                vec1.iter().zip(vec2.iter()).all(|(a, b)| a.equal(b))
382            }
383
384            // String content comparison
385            (Value::String(s1), Value::String(s2)) => **s1 == **s2,
386
387            // For all other types, equal? is the same as eqv?
388            _ => self.eqv(other),
389        }
390    }
391
392    /// Scheme `eqv?` - Equivalence (same value, not necessarily same object)
393    ///
394    /// Corresponds to OpenJade's `ELObj::eqv()`
395    ///
396    /// Returns true if:
397    /// - Both are the same boolean value
398    /// - Both are the same number (integer or real)
399    /// - Both are the same character
400    /// - Both are the same symbol (symbols are interned)
401    /// - Both are the same keyword
402    /// - Both refer to the same pair/vector/procedure object
403    /// - Both are nil
404    pub fn eqv(&self, other: &Value) -> bool {
405        match (self, other) {
406            (Value::Nil, Value::Nil) => true,
407            (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
408            (Value::Integer(n1), Value::Integer(n2)) => n1 == n2,
409            (Value::Real(n1), Value::Real(n2)) => n1 == n2,
410            (Value::Char(c1), Value::Char(c2)) => c1 == c2,
411
412            // Symbols and keywords: compare by content
413            // NOTE: Currently uses string content comparison (O(n)).
414            // FUTURE OPTIMIZATION: Implement global symbol table (interner) to enable
415            // pointer-equality comparison (O(1)). This would require:
416            //   - SymbolTable in Evaluator to intern all symbols
417            //   - Parser and Environment using the interner
418            //   - Change to: Rc::ptr_eq(s1, s2)
419            // Current implementation is correct per R4RS, just not optimally fast.
420            (Value::Symbol(s1), Value::Symbol(s2)) => **s1 == **s2,
421            (Value::Keyword(k1), Value::Keyword(k2)) => **k1 == **k2,
422
423            // For heap-allocated objects, compare object identity
424            (Value::Pair(p1), Value::Pair(p2)) => Gc::ptr_eq(p1, p2),
425            (Value::Vector(v1), Value::Vector(v2)) => Gc::ptr_eq(v1, v2),
426            (Value::Procedure(proc1), Value::Procedure(proc2)) => Gc::ptr_eq(proc1, proc2),
427
428            // Strings are not compared by eqv? - use equal? or eq?
429            // (In Scheme, eqv? on strings is unspecified)
430            (Value::String(_), Value::String(_)) => false,
431
432            // Special types
433            (Value::Node(n1), Value::Node(n2)) => {
434                // Nodes are equal by pointer identity (same Rc)
435                Rc::ptr_eq(n1, n2)
436            }
437            (Value::NodeList(nl1), Value::NodeList(nl2)) => {
438                // NodeLists are equal by pointer identity (same object)
439                Rc::ptr_eq(nl1, nl2)
440            }
441            (Value::Sosofo, Value::Sosofo) => true,
442            (Value::Unspecified, Value::Unspecified) => true,
443            (Value::Error, Value::Error) => true,
444
445            // Different types are never eqv?
446            _ => false,
447        }
448    }
449
450    /// Scheme `eq?` - Object identity (same object in memory)
451    ///
452    /// For most types, same as `eqv?`. Symbols and keywords are interned,
453    /// so `eq?` and `eqv?` are equivalent for them.
454    pub fn eq(&self, other: &Value) -> bool {
455        // For our implementation, eq? is the same as eqv?
456        // since we intern symbols and use Gc pointers for heap objects
457        self.eqv(other)
458    }
459}
460
461// =============================================================================
462// Value predicates (type checking)
463// =============================================================================
464
465impl Value {
466    /// Is this the nil value?
467    pub fn is_nil(&self) -> bool {
468        matches!(self, Value::Nil)
469    }
470
471    /// Is this a boolean?
472    pub fn is_bool(&self) -> bool {
473        matches!(self, Value::Bool(_))
474    }
475
476    /// Is this true? (for conditionals)
477    ///
478    /// In Scheme, only `#f` is false; everything else (including nil) is true.
479    pub fn is_true(&self) -> bool {
480        !matches!(self, Value::Bool(false))
481    }
482
483    /// Is this an integer?
484    pub fn is_integer(&self) -> bool {
485        matches!(self, Value::Integer(_))
486    }
487
488    /// Is this a real number?
489    pub fn is_real(&self) -> bool {
490        matches!(self, Value::Real(_))
491    }
492
493    /// Is this a number (integer or real)?
494    pub fn is_number(&self) -> bool {
495        matches!(self, Value::Integer(_) | Value::Real(_))
496    }
497
498    /// Is this a character?
499    pub fn is_char(&self) -> bool {
500        matches!(self, Value::Char(_))
501    }
502
503    /// Is this a string?
504    pub fn is_string(&self) -> bool {
505        matches!(self, Value::String(_))
506    }
507
508    /// Is this a symbol?
509    pub fn is_symbol(&self) -> bool {
510        matches!(self, Value::Symbol(_))
511    }
512
513    /// Is this a pair?
514    pub fn is_pair(&self) -> bool {
515        matches!(self, Value::Pair(_))
516    }
517
518    /// Is this a list? (nil or pair)
519    pub fn is_list(&self) -> bool {
520        matches!(self, Value::Nil | Value::Pair(_))
521    }
522
523    /// Is this a vector?
524    pub fn is_vector(&self) -> bool {
525        matches!(self, Value::Vector(_))
526    }
527
528    /// Is this a procedure?
529    pub fn is_procedure(&self) -> bool {
530        matches!(self, Value::Procedure(_))
531    }
532
533    /// Is this a node?
534    pub fn is_node(&self) -> bool {
535        matches!(self, Value::Node(_))
536    }
537
538    /// Is this a node list?
539    pub fn is_node_list(&self) -> bool {
540        matches!(self, Value::NodeList(_))
541    }
542}
543
544// =============================================================================
545// Display / Debug
546// =============================================================================
547
548impl fmt::Debug for Value {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550        match self {
551            Value::Nil => write!(f, "()"),
552            Value::Bool(true) => write!(f, "#t"),
553            Value::Bool(false) => write!(f, "#f"),
554            Value::Integer(n) => write!(f, "{}", n),
555            Value::Real(n) => write!(f, "{}", n),
556            Value::Char(ch) => write!(f, "#\\{}", ch),
557            Value::String(s) => write!(f, "{:?}", **s),
558            Value::Symbol(s) => write!(f, "{}", s),
559            Value::Keyword(s) => write!(f, "#:{}", s),
560            Value::Pair(p) => {
561                let pair = p.borrow();
562                write!(f, "({:?} . {:?})", pair.car, pair.cdr)
563            }
564            Value::Vector(v) => {
565                let vec = v.borrow();
566                write!(f, "#(")?;
567                for (i, val) in vec.iter().enumerate() {
568                    if i > 0 {
569                        write!(f, " ")?;
570                    }
571                    write!(f, "{:?}", val)?;
572                }
573                write!(f, ")")
574            }
575            Value::Procedure(proc) => match &**proc {
576                Procedure::Primitive { name, .. } => write!(f, "#<primitive:{}>", name),
577                Procedure::Lambda { .. } => write!(f, "#<lambda>"),
578            },
579            Value::Node(node) => {
580                // Display node with its gi if available
581                if let Some(gi) = node.gi() {
582                    write!(f, "#<node:{}>", gi)
583                } else {
584                    write!(f, "#<node>")
585                }
586            }
587            Value::NodeList(nl) => write!(f, "#<node-list:{}>", nl.length()),
588            Value::Sosofo => write!(f, "#<sosofo>"),
589            Value::Unspecified => write!(f, "#<unspecified>"),
590            Value::Error => write!(f, "#<error>"),
591        }
592    }
593}
594
595// Implement Display to show values in a Scheme-readable way
596impl fmt::Display for Value {
597    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
598        write!(f, "{:?}", self)
599    }
600}
601
602// =============================================================================
603// Garbage Collection (Manual Trace Implementation)
604// =============================================================================
605
606/// Manual implementation of Trace for Value
607///
608/// We implement this manually because:
609/// - Node and NodeList use Rc<Box<dyn Trait>>, which doesn't implement Trace
610/// - These are managed by Rc, not the GC
611/// - All other variants use Gc and need proper tracing
612unsafe impl gc::Trace for Value {
613    unsafe fn trace(&self) {
614        match self {
615            Value::Nil => {}
616            Value::Bool(_) => {}
617            Value::Integer(_) => {}
618            Value::Real(_) => {}
619            Value::Char(_) => {}
620            Value::String(s) => s.trace(),
621            Value::Symbol(s) => s.trace(),
622            Value::Keyword(k) => k.trace(),
623            Value::Pair(p) => p.trace(),
624            Value::Vector(v) => v.trace(),
625            Value::Procedure(proc) => proc.trace(),
626            // Node and NodeList use Rc, not Gc - no tracing needed
627            Value::Node(_) => {}
628            Value::NodeList(_) => {}
629            Value::Sosofo => {}
630            Value::Unspecified => {}
631            Value::Error => {}
632        }
633    }
634
635    unsafe fn root(&self) {
636        match self {
637            Value::String(s) => s.root(),
638            Value::Symbol(s) => s.root(),
639            Value::Keyword(k) => k.root(),
640            Value::Pair(p) => p.root(),
641            Value::Vector(v) => v.root(),
642            Value::Procedure(proc) => proc.root(),
643            _ => {}
644        }
645    }
646
647    unsafe fn unroot(&self) {
648        match self {
649            Value::String(s) => s.unroot(),
650            Value::Symbol(s) => s.unroot(),
651            Value::Keyword(k) => k.unroot(),
652            Value::Pair(p) => p.unroot(),
653            Value::Vector(v) => v.unroot(),
654            Value::Procedure(proc) => proc.unroot(),
655            _ => {}
656        }
657    }
658
659    fn finalize_glue(&self) {
660        match self {
661            Value::String(s) => s.finalize_glue(),
662            Value::Symbol(s) => s.finalize_glue(),
663            Value::Keyword(k) => k.finalize_glue(),
664            Value::Pair(p) => p.finalize_glue(),
665            Value::Vector(v) => v.finalize_glue(),
666            Value::Procedure(proc) => proc.finalize_glue(),
667            _ => {}
668        }
669    }
670}
671
672/// Manual implementation of Finalize for Value
673///
674/// No finalization needed - all cleanup is handled by Drop impls
675impl gc::Finalize for Value {}
676
677#[cfg(test)]
678mod tests {
679    use super::*;
680
681    #[test]
682    fn test_value_constructors() {
683        assert!(Value::bool(true).is_bool());
684        assert!(Value::integer(42).is_integer());
685        assert!(Value::real(3.14).is_real());
686        assert!(Value::char('a').is_char());
687        assert!(Value::string("hello".to_string()).is_string());
688        assert!(Value::symbol("foo").is_symbol());
689        assert!(Value::Nil.is_nil());
690    }
691
692    #[test]
693    fn test_truth_values() {
694        assert!(!Value::Bool(false).is_true());
695        assert!(Value::Bool(true).is_true());
696        assert!(Value::Nil.is_true()); // nil is true in Scheme!
697        assert!(Value::integer(0).is_true()); // 0 is true in Scheme!
698    }
699
700    #[test]
701    fn test_cons() {
702        let pair = Value::cons(Value::integer(1), Value::integer(2));
703        assert!(pair.is_pair());
704        assert!(pair.is_list());
705    }
706
707    #[test]
708    fn test_vector() {
709        let vec = Value::vector(vec![Value::integer(1), Value::integer(2), Value::integer(3)]);
710        assert!(vec.is_vector());
711    }
712
713    #[test]
714    fn test_equality_simple() {
715        // Numbers
716        assert!(Value::integer(42).eqv(&Value::integer(42)));
717        assert!(!Value::integer(42).eqv(&Value::integer(43)));
718        assert!(Value::real(3.14).eqv(&Value::real(3.14)));
719
720        // Booleans
721        assert!(Value::bool(true).eqv(&Value::bool(true)));
722        assert!(!Value::bool(true).eqv(&Value::bool(false)));
723
724        // Characters
725        assert!(Value::char('a').eqv(&Value::char('a')));
726        assert!(!Value::char('a').eqv(&Value::char('b')));
727
728        // Symbols (compared by content for now)
729        let sym1 = Value::symbol("foo");
730        let sym2 = Value::symbol("foo");
731        assert!(sym1.eqv(&sym2)); // Same symbol content
732
733        // Nil
734        assert!(Value::Nil.eqv(&Value::Nil));
735    }
736
737    #[test]
738    fn test_equality_strings() {
739        let s1 = Value::string("hello".to_string());
740        let s2 = Value::string("hello".to_string());
741        let s3 = Value::string("world".to_string());
742
743        // eqv? is false for different string objects (even with same content)
744        assert!(!s1.eqv(&s2));
745
746        // equal? compares string content
747        assert!(s1.equal(&s2));
748        assert!(!s1.equal(&s3));
749    }
750
751    #[test]
752    fn test_equality_lists() {
753        let list1 = Value::cons(
754            Value::integer(1),
755            Value::cons(Value::integer(2), Value::Nil),
756        );
757        let list2 = Value::cons(
758            Value::integer(1),
759            Value::cons(Value::integer(2), Value::Nil),
760        );
761        let list3 = Value::cons(
762            Value::integer(1),
763            Value::cons(Value::integer(3), Value::Nil),
764        );
765
766        // eqv? is false for different pair objects
767        assert!(!list1.eqv(&list2));
768
769        // equal? compares list structure
770        assert!(list1.equal(&list2));
771        assert!(!list1.equal(&list3));
772    }
773
774    #[test]
775    fn test_equality_vectors() {
776        let vec1 = Value::vector(vec![Value::integer(1), Value::integer(2)]);
777        let vec2 = Value::vector(vec![Value::integer(1), Value::integer(2)]);
778        let vec3 = Value::vector(vec![Value::integer(1), Value::integer(3)]);
779
780        // eqv? is false for different vector objects
781        assert!(!vec1.eqv(&vec2));
782
783        // equal? compares vector contents
784        assert!(vec1.equal(&vec2));
785        assert!(!vec1.equal(&vec3));
786    }
787}