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}