Skip to main content

cjc_runtime/
value.rs

1//! The universal runtime value type for the CJC interpreter.
2//!
3//! This module defines [`Value`], the tagged union that represents every
4//! runtime value in both the AST interpreter (`cjc-eval`) and the MIR
5//! executor (`cjc-mir-exec`). It also provides the [`Bf16`] brain-float
6//! type and [`FnValue`] for function references.
7//!
8//! # Memory Model
9//!
10//! - **Scalars** (`Int`, `Float`, `Bool`, `U8`, `Bf16`, `F16`, `Complex`) are
11//!   stored inline -- no heap allocation.
12//! - **Heap types** (`String`, `Array`, `Tuple`, `Map`, `Bytes`, `Tensor`)
13//!   use `Rc<...>` for O(1) clone with copy-on-write mutation semantics.
14//! - **Type-erased objects** (`GradGraph`, `OptimizerState`, `TidyView`,
15//!   `VizorPlot`, `QuantumState`) use `Rc<dyn Any>` or `Rc<RefCell<dyn Any>>`
16//!   to avoid circular crate dependencies.
17//!
18//! # NA Semantics
19//!
20//! [`Value::Na`] is the missing-value sentinel. It propagates through
21//! arithmetic (`NA + x -> NA`) and compares unequal to everything including
22//! itself (`NA == NA -> false`). Test with `is_na()`.
23
24use std::any::Any;
25use std::collections::BTreeMap;
26use std::fmt;
27use std::rc::Rc;
28use std::cell::RefCell;
29
30use crate::aligned_pool::AlignedByteSlice;
31use crate::complex;
32use crate::det_map::DetMap;
33use crate::gc::GcRef;
34use crate::paged_kv::PagedKvCache;
35use crate::scratchpad::Scratchpad;
36use crate::sparse::SparseCsr;
37use crate::tensor::Tensor;
38
39// ---------------------------------------------------------------------------
40// 7. Value enum for the interpreter
41// ---------------------------------------------------------------------------
42
43/// Brain-float 16-bit floating-point type (bf16).
44///
45/// Stores a `u16` that represents the upper 16 bits of an IEEE 754 `f32`.
46/// All arithmetic is performed by widening to `f32`, computing, then
47/// narrowing back -- this ensures deterministic results regardless of
48/// hardware bf16 support.
49///
50/// # Determinism
51///
52/// Conversion uses truncation (not rounding) of the lower 16 mantissa bits,
53/// guaranteeing identical results across all platforms.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub struct Bf16(pub u16);
56
57impl Bf16 {
58    /// Convert f32 to bf16 by truncating lower 16 mantissa bits.
59    pub fn from_f32(v: f32) -> Self {
60        Bf16((v.to_bits() >> 16) as u16)
61    }
62
63    /// Convert bf16 to f32 by shifting left 16 bits.
64    pub fn to_f32(self) -> f32 {
65        f32::from_bits((self.0 as u32) << 16)
66    }
67
68    /// Add two bf16 values (widen to f32, add, narrow back).
69    pub fn add(self, rhs: Self) -> Self {
70        Self::from_f32(self.to_f32() + rhs.to_f32())
71    }
72
73    /// Subtract two bf16 values (widen to f32, subtract, narrow back).
74    pub fn sub(self, rhs: Self) -> Self {
75        Self::from_f32(self.to_f32() - rhs.to_f32())
76    }
77
78    /// Multiply two bf16 values (widen to f32, multiply, narrow back).
79    pub fn mul(self, rhs: Self) -> Self {
80        Self::from_f32(self.to_f32() * rhs.to_f32())
81    }
82
83    /// Divide two bf16 values (widen to f32, divide, narrow back).
84    pub fn div(self, rhs: Self) -> Self {
85        Self::from_f32(self.to_f32() / rhs.to_f32())
86    }
87
88    /// Negate a bf16 value (widen to f32, negate, narrow back).
89    pub fn neg(self) -> Self {
90        Self::from_f32(-self.to_f32())
91    }
92}
93
94impl fmt::Display for Bf16 {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "{}", self.to_f32())
97    }
98}
99
100/// A CJC function value — either a named function or a closure.
101#[derive(Debug, Clone)]
102pub struct FnValue {
103    /// Function name (or `"<lambda>"` for anonymous closures).
104    pub name: String,
105    /// Number of parameters.
106    pub arity: usize,
107    /// Opaque identifier used by the interpreter to locate the function body.
108    pub body_id: usize,
109}
110
111/// The universal value type for the CJC interpreter.
112///
113/// Every runtime value -- from scalars to tensors to closures -- is
114/// represented as a variant of this enum. Both the AST interpreter
115/// (`cjc-eval`) and the MIR executor (`cjc-mir-exec`) operate on `Value`.
116///
117/// # Clone Semantics
118///
119/// Cloning a `Value` is cheap for heap-backed variants: `String`, `Array`,
120/// `Tuple`, `Map`, `Bytes`, `ByteSlice`, and `Tensor` all use `Rc`
121/// internally, so `clone()` increments a refcount without copying data.
122/// Mutation (e.g., `array_push`) returns a *new* `Value` (functional
123/// immutability), or triggers COW when the `Rc` refcount is 1.
124///
125/// # Display
126///
127/// All variants implement [`fmt::Display`] for user-facing output.
128/// The format matches CJC's `print()` builtin behavior.
129#[derive(Debug, Clone)]
130pub enum Value {
131    /// 64-bit signed integer.
132    Int(i64),
133    /// 64-bit IEEE 754 float.
134    Float(f64),
135    /// Boolean value.
136    Bool(bool),
137    /// Heap-allocated string with COW sharing via `Rc`.
138    String(Rc<String>),
139    /// Owning byte buffer.
140    Bytes(Rc<RefCell<Vec<u8>>>),
141    /// Non-owning byte slice view. In the interpreter this is an owning
142    /// snapshot (Vec<u8>) since we can't borrow across eval boundaries.
143    /// The compiler/MIR path can use true zero-copy slices.
144    ByteSlice(Rc<Vec<u8>>),
145    /// Validated UTF-8 string view (same representation as ByteSlice but
146    /// guaranteed valid UTF-8).
147    StrView(Rc<Vec<u8>>),
148    /// Single byte value (u8).
149    U8(u8),
150    /// N-dimensional tensor backed by [`Buffer<f64>`](crate::buffer::Buffer).
151    Tensor(Tensor),
152    /// Sparse matrix in CSR (Compressed Sparse Row) format.
153    SparseTensor(SparseCsr),
154    /// Deterministic hash map with interior mutability. Iteration order is
155    /// fixed by [`DetMap`]'s MurmurHash3-based bucket ordering.
156    Map(Rc<RefCell<DetMap>>),
157    /// Copy-on-write array. `Rc` provides O(1) clone; `Rc::make_mut()`
158    /// triggers a deep copy only when the array is mutated and shared.
159    Array(Rc<Vec<Value>>),
160    /// Named struct with ordered fields. Field order is deterministic
161    /// because fields are stored in a [`BTreeMap`].
162    Struct {
163        /// The struct type name (e.g., `"Point"`).
164        name: String,
165        /// Field name -> value mapping, ordered alphabetically.
166        fields: BTreeMap<String, Value>,
167    },
168    /// Copy-on-write tuple. Same COW semantics as Array.
169    Tuple(Rc<Vec<Value>>),
170    /// Reference to a GC-managed class instance in the [`GcHeap`](crate::gc::GcHeap).
171    ClassRef(GcRef),
172    /// A named function reference (not a closure -- no captured environment).
173    Fn(FnValue),
174    /// A closure: a function name + captured environment values.
175    Closure {
176        fn_name: String,
177        env: Vec<Value>,
178        /// Arity of the *original* lambda params (not including captures).
179        arity: usize,
180    },
181    /// Enum variant value: `Some(42)`, `None`, `Ok(v)`, `Err(e)`
182    Enum {
183        enum_name: String,
184        variant: String,
185        fields: Vec<Value>,
186    },
187    /// Compiled regex pattern: (pattern, flags)
188    Regex { pattern: String, flags: String },
189    /// bf16 brain-float: u16-backed, deterministic f32 conversions
190    Bf16(Bf16),
191    /// f16 half-precision: u16-backed IEEE 754 binary16
192    F16(crate::f16::F16),
193    /// Complex f64: deterministic fixed-sequence arithmetic
194    Complex(complex::ComplexF64),
195    /// Pre-allocated KV-cache scratchpad for zero-allocation inference.
196    ///
197    /// **Runtime-only:** No corresponding `Type::Scratchpad` exists. Created
198    /// via `Scratchpad.new()` builtin. Type-checking treats it as opaque.
199    Scratchpad(Rc<RefCell<Scratchpad>>),
200    /// Block-paged KV-cache (vLLM-style).
201    ///
202    /// **Runtime-only:** No corresponding `Type::PagedKvCache` exists. Created
203    /// via `PagedKvCache.new()` builtin. Type-checking treats it as opaque.
204    PagedKvCache(Rc<RefCell<PagedKvCache>>),
205    /// Aligned byte slice with 16-byte alignment guarantee.
206    ///
207    /// **Runtime-only:** No corresponding `Type::AlignedBytes` exists. Created
208    /// via `AlignedByteSlice.from_bytes()` builtin. Type-checking treats it
209    /// as opaque.
210    AlignedBytes(AlignedByteSlice),
211    /// Type-erased reverse-mode AD graph. Concrete type: `cjc_ad::GradGraph`.
212    /// Uses `Rc<RefCell<dyn Any>>` because cjc-runtime cannot depend on cjc-ad.
213    /// Construction and method dispatch happen in cjc-eval and cjc-mir-exec.
214    GradGraph(Rc<RefCell<dyn Any>>),
215    /// Type-erased optimizer state. Concrete types: AdamState or SgdState (from ml.rs).
216    /// Uses `Rc<RefCell<dyn Any>>` for interior mutability (step updates internal state).
217    OptimizerState(Rc<RefCell<dyn Any>>),
218    /// Type-erased tidy data view. Concrete type is `cjc_data::TidyView`.
219    /// Wrapped in `Rc<dyn Any>` for cheap cloning without circular deps
220    /// (cjc-runtime cannot depend on cjc-data).
221    ///
222    /// Dispatch is handled by `cjc_data::tidy_dispatch::dispatch_tidy_method`.
223    TidyView(Rc<dyn Any>),
224    /// Type-erased grouped tidy view. Concrete type is
225    /// `cjc_data::GroupedTidyView`. Same erasure strategy as TidyView.
226    GroupedTidyView(Rc<dyn Any>),
227    /// Type-erased Vizor plot specification. Concrete type is
228    /// `cjc_vizor::spec::PlotSpec`. Same erasure strategy as TidyView.
229    ///
230    /// Dispatch is handled by `cjc_vizor::dispatch::dispatch_vizor_method`.
231    VizorPlot(Rc<dyn Any>),
232    /// Type-erased quantum state (circuit or statevector).
233    /// Concrete type: `cjc_quantum::Circuit` or `cjc_quantum::Statevector`.
234    /// Uses `Rc<RefCell<dyn Any>>` for interior mutability (measurement collapses state).
235    /// Construction and method dispatch happen in cjc-eval and cjc-mir-exec.
236    QuantumState(Rc<RefCell<dyn Any>>),
237    /// Missing value sentinel. Propagates through arithmetic (NA + x → NA),
238    /// compares unequal to everything including itself (NA == NA → false).
239    /// Test with `is_na()`. This is the only way to detect NA.
240    Na,
241    Void,
242}
243
244impl Value {
245    /// Return a human-readable type name string for error messages and debugging.
246    ///
247    /// The returned string matches the CJC type system names (e.g., `"Int"`,
248    /// `"Float"`, `"Tensor"`, `"Array"`, `"Struct"`, `"Closure"`).
249    pub fn type_name(&self) -> &str {
250        match self {
251            Value::Int(_) => "Int",
252            Value::Float(_) => "Float",
253            Value::Bool(_) => "Bool",
254            Value::String(_) => "String",
255            Value::Bytes(_) => "Bytes",
256            Value::ByteSlice(_) => "ByteSlice",
257            Value::StrView(_) => "StrView",
258            Value::U8(_) => "u8",
259            Value::Tensor(_) => "Tensor",
260            Value::SparseTensor(_) => "SparseTensor",
261            Value::Map(_) => "Map",
262            Value::Array(_) => "Array",
263            Value::Tuple(_) => "Tuple",
264            Value::Struct { .. } => "Struct",
265            Value::Enum { .. } => "Enum",
266            Value::ClassRef(_) => "ClassRef",
267            Value::Fn(_) => "Fn",
268            Value::Closure { .. } => "Closure",
269            Value::Regex { .. } => "Regex",
270            Value::Bf16(_) => "Bf16",
271            Value::F16(_) => "F16",
272            Value::Complex(_) => "Complex",
273            Value::Scratchpad(_) => "Scratchpad",
274            Value::PagedKvCache(_) => "PagedKvCache",
275            Value::AlignedBytes(_) => "AlignedBytes",
276            Value::GradGraph(_) => "GradGraph",
277            Value::OptimizerState(_) => "OptimizerState",
278            Value::TidyView(_) => "TidyView",
279            Value::GroupedTidyView(_) => "GroupedTidyView",
280            Value::VizorPlot(_) => "VizorPlot",
281            Value::QuantumState(_) => "QuantumState",
282            Value::Na => "Na",
283            Value::Void => "Void",
284        }
285    }
286}
287
288impl fmt::Display for Value {
289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290        match self {
291            Value::Int(v) => write!(f, "{v}"),
292            Value::Float(v) => write!(f, "{v}"),
293            Value::Bool(v) => write!(f, "{v}"),
294            Value::String(v) => write!(f, "{v}"),
295            Value::Bytes(b) => {
296                let b = b.borrow();
297                write!(f, "Bytes([")?;
298                for (i, byte) in b.iter().enumerate() {
299                    if i > 0 { write!(f, ", ")?; }
300                    write!(f, "{byte}")?;
301                }
302                write!(f, "])")
303            }
304            Value::ByteSlice(b) => {
305                // Try to display as UTF-8, fall back to hex
306                match std::str::from_utf8(b) {
307                    Ok(s) => write!(f, "b\"{s}\""),
308                    Err(_) => {
309                        write!(f, "b\"")?;
310                        for &byte in b.iter() {
311                            if byte.is_ascii_graphic() || byte == b' ' {
312                                write!(f, "{}", byte as char)?;
313                            } else {
314                                write!(f, "\\x{byte:02x}")?;
315                            }
316                        }
317                        write!(f, "\"")
318                    }
319                }
320            }
321            Value::StrView(b) => {
322                // StrView is guaranteed valid UTF-8
323                let s = std::str::from_utf8(b).unwrap_or("<invalid utf8>");
324                write!(f, "{s}")
325            }
326            Value::U8(v) => write!(f, "{v}"),
327            Value::Tensor(t) => write!(f, "{t}"),
328            Value::SparseTensor(s) => write!(f, "SparseTensor({}x{}, nnz={})", s.nrows, s.ncols, s.nnz()),
329            Value::Map(m) => {
330                let m = m.borrow();
331                write!(f, "Map({{")?;
332                for (i, (k, v)) in m.iter().enumerate() {
333                    if i > 0 {
334                        write!(f, ", ")?;
335                    }
336                    write!(f, "{k}: {v}")?;
337                }
338                write!(f, "}})")
339            }
340            Value::Array(arr) => {
341                write!(f, "[")?;
342                for (i, v) in arr.iter().enumerate() {
343                    if i > 0 {
344                        write!(f, ", ")?;
345                    }
346                    write!(f, "{v}")?;
347                }
348                write!(f, "]")
349            }
350            Value::Tuple(elems) => {
351                write!(f, "(")?;
352                for (i, v) in elems.iter().enumerate() {
353                    if i > 0 {
354                        write!(f, ", ")?;
355                    }
356                    write!(f, "{v}")?;
357                }
358                write!(f, ")")
359            }
360            Value::Struct { name, fields } => {
361                write!(f, "{name} {{ ")?;
362                for (i, (k, v)) in fields.iter().enumerate() {
363                    if i > 0 {
364                        write!(f, ", ")?;
365                    }
366                    write!(f, "{k}: {v}")?;
367                }
368                write!(f, " }}")
369            }
370            Value::Enum {
371                enum_name: _,
372                variant,
373                fields,
374            } => {
375                write!(f, "{variant}")?;
376                if !fields.is_empty() {
377                    write!(f, "(")?;
378                    for (i, v) in fields.iter().enumerate() {
379                        if i > 0 {
380                            write!(f, ", ")?;
381                        }
382                        write!(f, "{v}")?;
383                    }
384                    write!(f, ")")?;
385                }
386                Ok(())
387            }
388            Value::Regex { pattern, flags } => {
389                write!(f, "/{pattern}/")?;
390                if !flags.is_empty() {
391                    write!(f, "{flags}")?;
392                }
393                Ok(())
394            }
395            Value::Bf16(v) => write!(f, "{}", v.to_f32()),
396            Value::F16(v) => write!(f, "{}", v.to_f64()),
397            Value::Complex(z) => write!(f, "{z}"),
398            Value::ClassRef(r) => write!(f, "<object@{}>", r.index),
399            Value::Fn(fv) => write!(f, "<fn {}({})>", fv.name, fv.arity),
400            Value::Closure {
401                fn_name, arity, ..
402            } => write!(f, "<closure {}({})>", fn_name, arity),
403            Value::Scratchpad(s) => write!(f, "{}", s.borrow()),
404            Value::PagedKvCache(c) => write!(f, "{}", c.borrow()),
405            Value::AlignedBytes(a) => write!(f, "{}", a),
406            Value::GradGraph(_) => write!(f, "<GradGraph>"),
407            Value::OptimizerState(_) => write!(f, "<OptimizerState>"),
408            Value::TidyView(_) => write!(f, "<TidyView>"),
409            Value::GroupedTidyView(_) => write!(f, "<GroupedTidyView>"),
410            Value::VizorPlot(_) => write!(f, "<VizorPlot>"),
411            Value::QuantumState(_) => write!(f, "<QuantumState>"),
412            Value::Na => write!(f, "NA"),
413            Value::Void => write!(f, "void"),
414        }
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421    use std::rc::Rc;
422
423    #[test]
424    fn int_display() {
425        assert_eq!(format!("{}", Value::Int(42)), "42");
426        assert_eq!(format!("{}", Value::Int(-1)), "-1");
427    }
428
429    #[test]
430    fn float_display() {
431        let s = format!("{}", Value::Float(3.14));
432        assert!(s.starts_with("3.14"), "got: {s}");
433    }
434
435    #[test]
436    fn bool_display() {
437        assert_eq!(format!("{}", Value::Bool(true)), "true");
438        assert_eq!(format!("{}", Value::Bool(false)), "false");
439    }
440
441    #[test]
442    fn string_display() {
443        let v = Value::String(Rc::new("hello".to_string()));
444        assert_eq!(format!("{v}"), "hello");
445    }
446
447    #[test]
448    fn void_display() {
449        assert_eq!(format!("{}", Value::Void), "void");
450    }
451
452    #[test]
453    fn type_name_coverage() {
454        assert_eq!(Value::Int(0).type_name(), "Int");
455        assert_eq!(Value::Float(0.0).type_name(), "Float");
456        assert_eq!(Value::Bool(true).type_name(), "Bool");
457        assert_eq!(Value::String(Rc::new(String::new())).type_name(), "String");
458        assert_eq!(Value::Void.type_name(), "Void");
459    }
460
461    #[test]
462    fn tuple_display() {
463        let t = Value::Tuple(Rc::new(vec![
464            Value::Int(1),
465            Value::Bool(true),
466        ]));
467        let s = format!("{t}");
468        assert!(s.contains("1"), "tuple should contain 1, got: {s}");
469        assert!(s.contains("true"), "tuple should contain true, got: {s}");
470    }
471
472    #[test]
473    fn array_display() {
474        let a = Value::Array(Rc::new(vec![
475            Value::Int(10),
476            Value::Int(20),
477        ]));
478        let s = format!("{a}");
479        assert!(s.contains("10"), "array should contain 10, got: {s}");
480        assert!(s.contains("20"), "array should contain 20, got: {s}");
481    }
482
483    #[test]
484    fn struct_value_display() {
485        let mut fields = std::collections::BTreeMap::new();
486        fields.insert("x".to_string(), Value::Int(1));
487        fields.insert("y".to_string(), Value::Int(2));
488        let sv = Value::Struct {
489            name: "Point".to_string(),
490            fields,
491        };
492        let s = format!("{sv}");
493        assert!(s.contains("Point"), "struct display should contain name, got: {s}");
494    }
495
496    #[test]
497    fn enum_value_display() {
498        let ev = Value::Enum {
499            enum_name: "Option".to_string(),
500            variant: "Some".to_string(),
501            fields: vec![Value::Int(42)],
502        };
503        let s = format!("{ev}");
504        assert!(s.contains("Some"), "enum display should contain variant, got: {s}");
505    }
506
507    #[test]
508    fn map_display() {
509        let m = Value::Map(Rc::new(std::cell::RefCell::new(crate::det_map::DetMap::new())));
510        let s = format!("{m}");
511        assert!(s.contains("{") || s.contains("Map"), "map display should be readable, got: {s}");
512    }
513}
514