shape_value/value.rs
1//! Core value types for Shape
2//!
3//! This module defines supporting types used throughout the Shape runtime and VM.
4//! The canonical runtime value representation is `ValueWord` (8-byte NaN-boxed).
5
6use std::collections::HashMap;
7use std::sync::{Arc, RwLock};
8
9use crate::value_word::ValueWord;
10
11/// Type alias for array storage — ValueWord elements for 9x memory reduction.
12pub type VMArray = Arc<Vec<ValueWord>>;
13
14/// Upvalue - a captured variable that can be shared between closures and their enclosing scope.
15///
16/// Most captures are immutable (the compiler never emits `StoreClosure`), so the
17/// `Immutable` variant stores the ValueWord directly — no Arc, no RwLock, just an
18/// 8-byte clone. The `Mutable` variant preserves the old `Arc<RwLock<ValueWord>>`
19/// path for the rare case where a capture is written to.
20#[derive(Debug, Clone)]
21pub enum Upvalue {
22 /// Direct value — no lock, no Arc overhead. Clone is 8 bytes (or Arc bump for heap values).
23 Immutable(ValueWord),
24 /// Shared mutable capture — used if `StoreClosure` is ever emitted.
25 Mutable(Arc<RwLock<ValueWord>>),
26}
27
28impl Upvalue {
29 /// Create a new **immutable** upvalue (fast path — default for all captures).
30 #[inline]
31 pub fn new(value: ValueWord) -> Self {
32 Upvalue::Immutable(value)
33 }
34
35 /// Create a new **mutable** upvalue (slow path — only when writes are needed).
36 #[inline]
37 pub fn new_mutable(value: ValueWord) -> Self {
38 Upvalue::Mutable(Arc::new(RwLock::new(value)))
39 }
40
41 /// Get a clone of the contained ValueWord value.
42 #[inline]
43 pub fn get(&self) -> ValueWord {
44 match self {
45 Upvalue::Immutable(nb) => nb.clone(),
46 Upvalue::Mutable(arc) => arc.read().unwrap().clone(),
47 }
48 }
49
50 /// Set the contained value.
51 ///
52 /// If the upvalue is `Immutable`, it is upgraded to `Mutable` on the first write.
53 /// This requires `&mut self`.
54 pub fn set(&mut self, value: ValueWord) {
55 match self {
56 Upvalue::Mutable(arc) => {
57 *arc.write().unwrap() = value;
58 }
59 Upvalue::Immutable(_) => {
60 // Upgrade to mutable on first write
61 *self = Upvalue::Mutable(Arc::new(RwLock::new(value)));
62 }
63 }
64 }
65}
66
67/// Print result with structured spans for reformattable output
68#[derive(Debug, Clone)]
69pub struct PrintResult {
70 /// Rendered output string (cached)
71 pub rendered: String,
72
73 /// Structured spans with metadata for reformatting
74 pub spans: Vec<PrintSpan>,
75}
76
77/// A span in print output (literal text or formatted value)
78#[derive(Debug, Clone)]
79pub enum PrintSpan {
80 /// Literal text span
81 Literal {
82 text: String,
83 start: usize,
84 end: usize,
85 span_id: String,
86 },
87
88 /// Value span with formatting metadata
89 Value {
90 text: String,
91 start: usize,
92 end: usize,
93 span_id: String,
94 variable_name: Option<String>,
95 raw_value: Box<ValueWord>,
96 type_name: String,
97 current_format: String,
98 format_params: HashMap<String, ValueWord>,
99 },
100}
101
102/// A callable closure backed by native Rust code with captured state.
103/// Used by extension modules to return objects with callable methods (e.g., DbTable.filter).
104#[derive(Clone)]
105pub struct HostCallable {
106 inner: Arc<dyn Fn(&[ValueWord]) -> Result<ValueWord, String> + Send + Sync>,
107}
108
109impl HostCallable {
110 /// Create a new HostCallable from a closure
111 pub fn new(
112 f: impl Fn(&[ValueWord]) -> Result<ValueWord, String> + Send + Sync + 'static,
113 ) -> Self {
114 Self { inner: Arc::new(f) }
115 }
116
117 /// Call the host closure with the given arguments
118 pub fn call(&self, args: &[ValueWord]) -> Result<ValueWord, String> {
119 (self.inner)(args)
120 }
121}
122
123impl std::fmt::Debug for HostCallable {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 f.write_str("<host_closure>")
126 }
127}
128
129/// Comparison operator for filter expressions
130#[derive(Debug, Clone, PartialEq)]
131pub enum FilterOp {
132 Eq,
133 Neq,
134 Gt,
135 Gte,
136 Lt,
137 Lte,
138}
139
140/// A literal value in a filter expression (for SQL generation)
141#[derive(Debug, Clone, PartialEq)]
142pub enum FilterLiteral {
143 Int(i64),
144 Float(f64),
145 String(String),
146 Bool(bool),
147 Null,
148}
149
150/// Filter expression tree for SQL pushdown.
151/// Built from ExprProxy comparisons and logical operations.
152#[derive(Debug, Clone, PartialEq)]
153pub enum FilterNode {
154 /// Column compared to a literal value
155 Compare {
156 column: String,
157 op: FilterOp,
158 value: FilterLiteral,
159 },
160 /// Logical AND of two filter nodes
161 And(Box<FilterNode>, Box<FilterNode>),
162 /// Logical OR of two filter nodes
163 Or(Box<FilterNode>, Box<FilterNode>),
164 /// Logical NOT of a filter node
165 Not(Box<FilterNode>),
166}
167
168/// Virtual method table for trait objects.
169///
170/// Maps method names to function IDs for dynamic dispatch.
171/// Created when a concrete value is boxed into a `dyn Trait`.
172#[derive(Debug, Clone)]
173pub struct VTable {
174 /// Trait names this vtable implements
175 pub trait_names: Vec<String>,
176 /// Map from method name to function ID (bytecode offset or closure)
177 pub methods: HashMap<String, VTableEntry>,
178}
179
180/// An entry in a vtable — how to dispatch a method call
181#[derive(Debug, Clone)]
182pub enum VTableEntry {
183 /// A compiled function by ID
184 FunctionId(u16),
185 /// A closure with captured upvalues
186 Closure {
187 function_id: u16,
188 upvalues: Vec<Upvalue>,
189 },
190}
191
192/// Create a VMArray from an iterator of ValueWord values.
193pub fn vmarray_from_value_words(iter: impl IntoIterator<Item = ValueWord>) -> VMArray {
194 Arc::new(iter.into_iter().collect())
195}
196
197/// Backward-compatibility alias.
198pub fn vmarray_from_nanboxed(iter: impl IntoIterator<Item = ValueWord>) -> VMArray {
199 vmarray_from_value_words(iter)
200}