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