Skip to main content

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