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}