Skip to main content

rexlang_engine/
value.rs

1//! Core value representation for Rex.
2
3use std::collections::{BTreeMap, HashSet};
4use std::ops::Deref;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, Mutex};
7
8use chrono::{DateTime, Utc};
9use rexlang_ast::expr::{Symbol, sym, sym_eq};
10use rexlang_typesystem::{BuiltinTypeId, Type, TypedExpr};
11use uuid::Uuid;
12
13use crate::EngineError;
14use crate::Env;
15use crate::engine::{NativeFn, OverloadedFn};
16
17#[derive(Default)]
18struct HeapState {
19    slots: Vec<HeapSlot>,
20    free_list: Vec<u32>,
21}
22
23#[derive(Clone)]
24struct HeapSlot {
25    generation: u32,
26    value: Option<Arc<Value>>,
27}
28
29#[derive(Clone)]
30pub struct Heap {
31    id: u64,
32    state: Arc<Mutex<HeapState>>,
33}
34
35impl Default for Heap {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl Heap {
42    /// Create a heap for a lexical scope.
43    pub fn scoped<R>(f: impl FnOnce(&Heap) -> R) -> R {
44        let heap = Heap::new();
45        f(&heap)
46    }
47
48    pub fn new() -> Self {
49        static NEXT_HEAP_ID: AtomicU64 = AtomicU64::new(1);
50        let id = NEXT_HEAP_ID.fetch_add(1, Ordering::Relaxed);
51        Self {
52            id,
53            state: Arc::new(Mutex::new(HeapState::default())),
54        }
55    }
56
57    fn invalid_pointer(heap_id: u64, index: u32, generation: u32) -> EngineError {
58        EngineError::Internal(format!(
59            "invalid heap pointer (heap_id={}, index={}, generation={})",
60            heap_id, index, generation
61        ))
62    }
63
64    fn wrong_heap_pointer(
65        pointer_heap_id: u64,
66        heap_id: u64,
67        index: u32,
68        generation: u32,
69    ) -> EngineError {
70        EngineError::Internal(format!(
71            "heap pointer belongs to different heap (pointer_heap_id={}, heap_id={}, index={}, generation={})",
72            pointer_heap_id, heap_id, index, generation
73        ))
74    }
75
76    fn alloc_slot(&self, value: Value) -> Result<Pointer, EngineError> {
77        let (index, generation) = self.alloc_slot_raw(value)?;
78        Ok(Pointer {
79            heap_id: self.id,
80            index,
81            generation,
82        })
83    }
84
85    pub fn get(&self, pointer: &Pointer) -> Result<ValueRef, EngineError> {
86        if pointer.heap_id != self.id {
87            return Err(Self::wrong_heap_pointer(
88                pointer.heap_id,
89                self.id,
90                pointer.index,
91                pointer.generation,
92            ));
93        }
94        self.read_slot(pointer.index, pointer.generation)
95            .map(ValueRef::from_arc)
96    }
97
98    pub fn type_name(&self, pointer: &Pointer) -> Result<&'static str, EngineError> {
99        self.get(pointer)
100            .map(|value| self.type_name_of_value(value.as_ref()))
101    }
102
103    pub(crate) fn type_name_of_value(&self, value: &Value) -> &'static str {
104        value.value_type_name()
105    }
106
107    pub fn pointer_as_bool(&self, pointer: &Pointer) -> Result<bool, EngineError> {
108        self.get(pointer)?.as_ref().value_as_bool()
109    }
110
111    pub fn pointer_as_u8(&self, pointer: &Pointer) -> Result<u8, EngineError> {
112        self.get(pointer)?.as_ref().value_as_u8()
113    }
114
115    pub fn pointer_as_u16(&self, pointer: &Pointer) -> Result<u16, EngineError> {
116        self.get(pointer)?.as_ref().value_as_u16()
117    }
118
119    pub fn pointer_as_u32(&self, pointer: &Pointer) -> Result<u32, EngineError> {
120        self.get(pointer)?.as_ref().value_as_u32()
121    }
122
123    pub fn pointer_as_u64(&self, pointer: &Pointer) -> Result<u64, EngineError> {
124        self.get(pointer)?.as_ref().value_as_u64()
125    }
126
127    pub fn pointer_as_i8(&self, pointer: &Pointer) -> Result<i8, EngineError> {
128        self.get(pointer)?.as_ref().value_as_i8()
129    }
130
131    pub fn pointer_as_i16(&self, pointer: &Pointer) -> Result<i16, EngineError> {
132        self.get(pointer)?.as_ref().value_as_i16()
133    }
134
135    pub fn pointer_as_i32(&self, pointer: &Pointer) -> Result<i32, EngineError> {
136        self.get(pointer)?.as_ref().value_as_i32()
137    }
138
139    pub fn pointer_as_i64(&self, pointer: &Pointer) -> Result<i64, EngineError> {
140        self.get(pointer)?.as_ref().value_as_i64()
141    }
142
143    pub fn pointer_as_f32(&self, pointer: &Pointer) -> Result<f32, EngineError> {
144        self.get(pointer)?.as_ref().value_as_f32()
145    }
146
147    pub fn pointer_as_f64(&self, pointer: &Pointer) -> Result<f64, EngineError> {
148        self.get(pointer)?.as_ref().value_as_f64()
149    }
150
151    pub fn pointer_as_string(&self, pointer: &Pointer) -> Result<String, EngineError> {
152        self.get(pointer)?.as_ref().value_as_string()
153    }
154
155    pub fn pointer_as_uuid(&self, pointer: &Pointer) -> Result<Uuid, EngineError> {
156        self.get(pointer)?.as_ref().value_as_uuid()
157    }
158
159    pub fn pointer_as_datetime(&self, pointer: &Pointer) -> Result<DateTime<Utc>, EngineError> {
160        self.get(pointer)?.as_ref().value_as_datetime()
161    }
162
163    pub fn pointer_as_tuple(&self, pointer: &Pointer) -> Result<Vec<Pointer>, EngineError> {
164        self.get(pointer)?.as_ref().value_as_tuple()
165    }
166
167    pub fn pointer_as_array(&self, pointer: &Pointer) -> Result<Vec<Pointer>, EngineError> {
168        self.get(pointer)?.as_ref().value_as_array()
169    }
170
171    pub fn pointer_as_dict(
172        &self,
173        pointer: &Pointer,
174    ) -> Result<BTreeMap<Symbol, Pointer>, EngineError> {
175        self.get(pointer)?.as_ref().value_as_dict()
176    }
177
178    pub fn pointer_as_adt(&self, pointer: &Pointer) -> Result<(Symbol, Vec<Pointer>), EngineError> {
179        self.get(pointer)?.as_ref().value_as_adt()
180    }
181
182    pub fn pointer_as_uninitialized(&self, pointer: &Pointer) -> Result<Symbol, EngineError> {
183        self.get(pointer)?.as_ref().value_as_uninitialized()
184    }
185
186    pub fn pointer_as_closure(&self, pointer: &Pointer) -> Result<Closure, EngineError> {
187        self.get(pointer)?.as_ref().value_as_closure()
188    }
189
190    pub fn pointer_as_native(&self, pointer: &Pointer) -> Result<NativeFn, EngineError> {
191        self.get(pointer)?.as_ref().value_as_native()
192    }
193
194    pub fn pointer_as_overloaded(&self, pointer: &Pointer) -> Result<OverloadedFn, EngineError> {
195        self.get(pointer)?.as_ref().value_as_overloaded()
196    }
197
198    pub fn alloc_bool(&self, value: bool) -> Result<Pointer, EngineError> {
199        self.alloc_slot(Value::Bool(value))
200    }
201
202    pub fn alloc_u8(&self, value: u8) -> Result<Pointer, EngineError> {
203        self.alloc_slot(Value::U8(value))
204    }
205
206    pub fn alloc_u16(&self, value: u16) -> Result<Pointer, EngineError> {
207        self.alloc_slot(Value::U16(value))
208    }
209
210    pub fn alloc_u32(&self, value: u32) -> Result<Pointer, EngineError> {
211        self.alloc_slot(Value::U32(value))
212    }
213
214    pub fn alloc_u64(&self, value: u64) -> Result<Pointer, EngineError> {
215        self.alloc_slot(Value::U64(value))
216    }
217
218    pub fn alloc_i8(&self, value: i8) -> Result<Pointer, EngineError> {
219        self.alloc_slot(Value::I8(value))
220    }
221
222    pub fn alloc_i16(&self, value: i16) -> Result<Pointer, EngineError> {
223        self.alloc_slot(Value::I16(value))
224    }
225
226    pub fn alloc_i32(&self, value: i32) -> Result<Pointer, EngineError> {
227        self.alloc_slot(Value::I32(value))
228    }
229
230    pub fn alloc_i64(&self, value: i64) -> Result<Pointer, EngineError> {
231        self.alloc_slot(Value::I64(value))
232    }
233
234    pub fn alloc_f32(&self, value: f32) -> Result<Pointer, EngineError> {
235        self.alloc_slot(Value::F32(value))
236    }
237
238    pub fn alloc_f64(&self, value: f64) -> Result<Pointer, EngineError> {
239        self.alloc_slot(Value::F64(value))
240    }
241
242    pub fn alloc_string(&self, value: String) -> Result<Pointer, EngineError> {
243        self.alloc_slot(Value::String(value))
244    }
245
246    pub fn alloc_uuid(&self, value: Uuid) -> Result<Pointer, EngineError> {
247        self.alloc_slot(Value::Uuid(value))
248    }
249
250    pub fn alloc_datetime(&self, value: DateTime<Utc>) -> Result<Pointer, EngineError> {
251        self.alloc_slot(Value::DateTime(value))
252    }
253
254    pub fn alloc_value(&self, value: Value) -> Result<Pointer, EngineError> {
255        self.alloc_slot(value)
256    }
257
258    pub(crate) fn alloc_uninitialized(&self, name: Symbol) -> Result<Pointer, EngineError> {
259        self.alloc_slot(Value::Uninitialized(name))
260    }
261
262    pub fn alloc_tuple(&self, values: Vec<Pointer>) -> Result<Pointer, EngineError> {
263        self.alloc_slot(Value::Tuple(values))
264    }
265
266    pub fn alloc_array(&self, values: Vec<Pointer>) -> Result<Pointer, EngineError> {
267        self.alloc_slot(Value::Array(values))
268    }
269
270    pub fn alloc_dict(&self, values: BTreeMap<Symbol, Pointer>) -> Result<Pointer, EngineError> {
271        self.alloc_slot(Value::Dict(values))
272    }
273
274    pub fn alloc_adt(&self, name: Symbol, args: Vec<Pointer>) -> Result<Pointer, EngineError> {
275        self.alloc_slot(Value::Adt(name, args))
276    }
277
278    pub fn alloc_closure(
279        &self,
280        env: Env,
281        param: Symbol,
282        param_ty: Type,
283        typ: Type,
284        body: Arc<TypedExpr>,
285    ) -> Result<Pointer, EngineError> {
286        self.alloc_slot(Value::Closure(Closure {
287            env,
288            param,
289            param_ty,
290            typ,
291            body,
292        }))
293    }
294
295    #[allow(clippy::too_many_arguments)]
296    pub(crate) fn alloc_native(
297        &self,
298        native_id: u64,
299        name: Symbol,
300        arity: usize,
301        typ: Type,
302        gas_cost: u64,
303        applied: Vec<Pointer>,
304        applied_types: Vec<Type>,
305    ) -> Result<Pointer, EngineError> {
306        self.alloc_slot(Value::Native(NativeFn::from_parts(
307            native_id,
308            name,
309            arity,
310            typ,
311            gas_cost,
312            applied,
313            applied_types,
314        )))
315    }
316
317    pub fn alloc_overloaded(
318        &self,
319        name: Symbol,
320        typ: Type,
321        applied: Vec<Pointer>,
322        applied_types: Vec<Type>,
323    ) -> Result<Pointer, EngineError> {
324        self.alloc_slot(Value::Overloaded(OverloadedFn::from_parts(
325            name,
326            typ,
327            applied,
328            applied_types,
329        )))
330    }
331
332    pub(crate) fn overwrite(&self, pointer: &Pointer, value: Value) -> Result<(), EngineError> {
333        if pointer.heap_id != self.id {
334            return Err(Self::wrong_heap_pointer(
335                pointer.heap_id,
336                self.id,
337                pointer.index,
338                pointer.generation,
339            ));
340        }
341
342        let mut state = self
343            .state
344            .lock()
345            .map_err(|_| EngineError::Internal("heap state poisoned".into()))?;
346        let slot = state
347            .slots
348            .get_mut(pointer.index as usize)
349            .ok_or_else(|| Heap::invalid_pointer(self.id, pointer.index, pointer.generation))?;
350        if slot.generation != pointer.generation {
351            return Err(Heap::invalid_pointer(
352                self.id,
353                pointer.index,
354                pointer.generation,
355            ));
356        }
357        slot.value = Some(Arc::new(value));
358        Ok(())
359    }
360
361    fn alloc_slot_raw(&self, value: Value) -> Result<(u32, u32), EngineError> {
362        let mut state = self
363            .state
364            .lock()
365            .map_err(|_| EngineError::Internal("heap state poisoned".into()))?;
366
367        if let Some(index) = state.free_list.pop() {
368            let slot = state
369                .slots
370                .get_mut(index as usize)
371                .ok_or_else(|| EngineError::Internal("heap free-list corruption".into()))?;
372            slot.value = Some(Arc::new(value));
373            return Ok((index, slot.generation));
374        }
375
376        let index = u32::try_from(state.slots.len())
377            .map_err(|_| EngineError::Internal("heap exhausted: too many slots".into()))?;
378        state.slots.push(HeapSlot {
379            generation: 0,
380            value: Some(Arc::new(value)),
381        });
382        Ok((index, 0))
383    }
384
385    fn read_slot(&self, index: u32, generation: u32) -> Result<Arc<Value>, EngineError> {
386        let state = self
387            .state
388            .lock()
389            .map_err(|_| EngineError::Internal("heap state poisoned".into()))?;
390        let slot = state
391            .slots
392            .get(index as usize)
393            .ok_or_else(|| Heap::invalid_pointer(self.id, index, generation))?;
394        if slot.generation != generation {
395            return Err(Heap::invalid_pointer(self.id, index, generation));
396        }
397        slot.value
398            .as_ref()
399            .cloned()
400            .ok_or_else(|| Heap::invalid_pointer(self.id, index, generation))
401    }
402}
403
404#[derive(Clone)]
405pub struct ValueRef {
406    value: Arc<Value>,
407}
408
409impl ValueRef {
410    fn from_arc(value: Arc<Value>) -> Self {
411        Self { value }
412    }
413}
414
415impl AsRef<Value> for ValueRef {
416    fn as_ref(&self) -> &Value {
417        self.value.as_ref()
418    }
419}
420
421impl Deref for ValueRef {
422    type Target = Value;
423
424    fn deref(&self) -> &Self::Target {
425        self.value.as_ref()
426    }
427}
428
429#[derive(Clone)]
430pub struct Closure {
431    pub env: Env,
432    pub param: Symbol,
433    pub param_ty: Type,
434    pub typ: Type,
435    pub body: Arc<TypedExpr>,
436}
437
438#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
439pub struct Pointer {
440    heap_id: u64,
441    index: u32,
442    generation: u32,
443}
444
445#[derive(Clone)]
446pub enum Value {
447    Bool(bool),
448    U8(u8),
449    U16(u16),
450    U32(u32),
451    U64(u64),
452    I8(i8),
453    I16(i16),
454    I32(i32),
455    I64(i64),
456    F32(f32),
457    F64(f64),
458    String(String),
459    Uuid(Uuid),
460    DateTime(DateTime<Utc>),
461    Tuple(Vec<Pointer>),
462    Array(Vec<Pointer>),
463    Dict(BTreeMap<Symbol, Pointer>),
464    Adt(Symbol, Vec<Pointer>),
465    Uninitialized(Symbol),
466    Closure(Closure),
467    Native(NativeFn),
468    Overloaded(OverloadedFn),
469}
470
471impl Value {
472    pub fn value_type_name(&self) -> &'static str {
473        match self {
474            Value::Bool(..) => "bool",
475            Value::U8(..) => "u8",
476            Value::U16(..) => "u16",
477            Value::U32(..) => "u32",
478            Value::U64(..) => "u64",
479            Value::I8(..) => "i8",
480            Value::I16(..) => "i16",
481            Value::I32(..) => "i32",
482            Value::I64(..) => "i64",
483            Value::F32(..) => "f32",
484            Value::F64(..) => "f64",
485            Value::String(..) => "string",
486            Value::Uuid(..) => "uuid",
487            Value::DateTime(..) => "datetime",
488            Value::Tuple(..) => "tuple",
489            Value::Array(..) => "array",
490            Value::Dict(..) => "dict",
491            Value::Adt(name, ..) if sym_eq(name, "Empty") || sym_eq(name, "Cons") => "list",
492            Value::Adt(..) => "adt",
493            Value::Uninitialized(..) => "uninitialized",
494            Value::Closure(..) => "closure",
495            Value::Native(..) => "native",
496            Value::Overloaded(..) => "overloaded",
497        }
498    }
499
500    fn value_type_error(&self, expected: &'static str) -> EngineError {
501        EngineError::NativeType {
502            expected: expected.to_string(),
503            got: self.value_type_name().to_string(),
504        }
505    }
506
507    pub fn value_as_bool(&self) -> Result<bool, EngineError> {
508        match self {
509            Value::Bool(v) => Ok(*v),
510            _ => Err(self.value_type_error("bool")),
511        }
512    }
513
514    pub fn value_as_u8(&self) -> Result<u8, EngineError> {
515        match self {
516            Value::U8(v) => Ok(*v),
517            _ => Err(self.value_type_error("u8")),
518        }
519    }
520
521    pub fn value_as_u16(&self) -> Result<u16, EngineError> {
522        match self {
523            Value::U16(v) => Ok(*v),
524            _ => Err(self.value_type_error("u16")),
525        }
526    }
527
528    pub fn value_as_u32(&self) -> Result<u32, EngineError> {
529        match self {
530            Value::U32(v) => Ok(*v),
531            _ => Err(self.value_type_error("u32")),
532        }
533    }
534
535    pub fn value_as_u64(&self) -> Result<u64, EngineError> {
536        match self {
537            Value::U64(v) => Ok(*v),
538            _ => Err(self.value_type_error("u64")),
539        }
540    }
541
542    pub fn value_as_i8(&self) -> Result<i8, EngineError> {
543        match self {
544            Value::I8(v) => Ok(*v),
545            _ => Err(self.value_type_error("i8")),
546        }
547    }
548
549    pub fn value_as_i16(&self) -> Result<i16, EngineError> {
550        match self {
551            Value::I16(v) => Ok(*v),
552            _ => Err(self.value_type_error("i16")),
553        }
554    }
555
556    pub fn value_as_i32(&self) -> Result<i32, EngineError> {
557        match self {
558            Value::I32(v) => Ok(*v),
559            _ => Err(self.value_type_error("i32")),
560        }
561    }
562
563    pub fn value_as_i64(&self) -> Result<i64, EngineError> {
564        match self {
565            Value::I64(v) => Ok(*v),
566            _ => Err(self.value_type_error("i64")),
567        }
568    }
569
570    pub fn value_as_f32(&self) -> Result<f32, EngineError> {
571        match self {
572            Value::F32(v) => Ok(*v),
573            _ => Err(self.value_type_error("f32")),
574        }
575    }
576
577    pub fn value_as_f64(&self) -> Result<f64, EngineError> {
578        match self {
579            Value::F64(v) => Ok(*v),
580            _ => Err(self.value_type_error("f64")),
581        }
582    }
583
584    pub fn value_as_string(&self) -> Result<String, EngineError> {
585        match self {
586            Value::String(v) => Ok(v.clone()),
587            _ => Err(self.value_type_error("string")),
588        }
589    }
590
591    pub fn value_as_uuid(&self) -> Result<Uuid, EngineError> {
592        match self {
593            Value::Uuid(v) => Ok(*v),
594            _ => Err(self.value_type_error("uuid")),
595        }
596    }
597
598    pub fn value_as_datetime(&self) -> Result<DateTime<Utc>, EngineError> {
599        match self {
600            Value::DateTime(v) => Ok(*v),
601            _ => Err(self.value_type_error("datetime")),
602        }
603    }
604
605    pub fn value_as_tuple(&self) -> Result<Vec<Pointer>, EngineError> {
606        match self {
607            Value::Tuple(v) => Ok(v.clone()),
608            _ => Err(self.value_type_error("tuple")),
609        }
610    }
611
612    pub fn value_as_array(&self) -> Result<Vec<Pointer>, EngineError> {
613        match self {
614            Value::Array(v) => Ok(v.clone()),
615            _ => Err(self.value_type_error("array")),
616        }
617    }
618
619    pub fn value_as_dict(&self) -> Result<BTreeMap<Symbol, Pointer>, EngineError> {
620        match self {
621            Value::Dict(v) => Ok(v.clone()),
622            _ => Err(self.value_type_error("dict")),
623        }
624    }
625
626    pub fn value_as_adt(&self) -> Result<(Symbol, Vec<Pointer>), EngineError> {
627        match self {
628            Value::Adt(name, args) => Ok((name.clone(), args.clone())),
629            _ => Err(self.value_type_error("adt")),
630        }
631    }
632
633    pub fn value_as_uninitialized(&self) -> Result<Symbol, EngineError> {
634        match self {
635            Value::Uninitialized(name) => Ok(name.clone()),
636            _ => Err(self.value_type_error("uninitialized")),
637        }
638    }
639
640    pub fn value_as_closure(&self) -> Result<Closure, EngineError> {
641        match self {
642            Value::Closure(v) => Ok(v.clone()),
643            _ => Err(self.value_type_error("closure")),
644        }
645    }
646
647    pub fn value_as_native(&self) -> Result<NativeFn, EngineError> {
648        match self {
649            Value::Native(v) => Ok(v.clone()),
650            _ => Err(self.value_type_error("native")),
651        }
652    }
653
654    pub fn value_as_overloaded(&self) -> Result<OverloadedFn, EngineError> {
655        match self {
656            Value::Overloaded(v) => Ok(v.clone()),
657            _ => Err(self.value_type_error("overloaded")),
658        }
659    }
660}
661
662type PointerKey = (u64, u32, u32);
663type PointerPairKey = (PointerKey, PointerKey);
664
665#[derive(Clone, Copy, Debug, PartialEq, Eq)]
666pub struct ValueDisplayOptions {
667    pub include_numeric_suffixes: bool,
668    pub strip_internal_snippet_qualifiers: bool,
669}
670
671impl Default for ValueDisplayOptions {
672    fn default() -> Self {
673        Self::docs()
674    }
675}
676
677impl ValueDisplayOptions {
678    pub fn unsanitized() -> Self {
679        Self {
680            include_numeric_suffixes: true,
681            strip_internal_snippet_qualifiers: false,
682        }
683    }
684
685    pub fn docs() -> Self {
686        Self {
687            include_numeric_suffixes: false,
688            strip_internal_snippet_qualifiers: true,
689        }
690    }
691}
692
693fn maybe_strip_snippet_qualifier(name: &str, opts: ValueDisplayOptions) -> String {
694    if !opts.strip_internal_snippet_qualifiers || !name.starts_with("@snippet") {
695        return name.to_string();
696    }
697    if let Some((_, tail)) = name.rsplit_once('.') {
698        return tail.to_string();
699    }
700    name.to_string()
701}
702
703fn pointer_key(pointer: &Pointer) -> PointerKey {
704    (pointer.heap_id, pointer.index, pointer.generation)
705}
706
707fn canonical_pointer_pair(lhs: PointerKey, rhs: PointerKey) -> PointerPairKey {
708    if lhs <= rhs { (lhs, rhs) } else { (rhs, lhs) }
709}
710
711fn pointer_debug_inner(
712    heap: &Heap,
713    pointer: &Pointer,
714    active: &mut HashSet<PointerKey>,
715) -> Result<String, EngineError> {
716    let key = pointer_key(pointer);
717    if !active.insert(key) {
718        return Ok(format!("<cycle:{}:{}>", pointer.index, pointer.generation));
719    }
720    let value = heap.get(pointer)?;
721    let out = value_debug_inner(heap, &value, active);
722    active.remove(&key);
723    out
724}
725
726fn pointer_display_inner(
727    heap: &Heap,
728    pointer: &Pointer,
729    active: &mut HashSet<PointerKey>,
730    opts: ValueDisplayOptions,
731) -> Result<String, EngineError> {
732    let key = pointer_key(pointer);
733    if !active.insert(key) {
734        return Ok(format!("<cycle:{}:{}>", pointer.index, pointer.generation));
735    }
736    let value = heap.get(pointer)?;
737    let out = value_display_inner(heap, &value, active, opts);
738    active.remove(&key);
739    out
740}
741
742fn env_debug_inner(
743    heap: &Heap,
744    env: &Env,
745    active: &mut HashSet<PointerKey>,
746) -> Result<String, EngineError> {
747    let mut bindings = env.bindings().iter().collect::<Vec<_>>();
748    bindings.sort_by(|(lhs, _), (rhs, _)| lhs.as_ref().cmp(rhs.as_ref()));
749
750    let mut rendered = Vec::with_capacity(bindings.len());
751    for (name, pointer) in bindings {
752        rendered.push(format!(
753            "{} = {}",
754            name,
755            pointer_debug_inner(heap, pointer, active)?
756        ));
757    }
758
759    let frame = format!("{{{}}}", rendered.join(", "));
760    match env.parent() {
761        Some(parent) => Ok(format!(
762            "{frame} :: {}",
763            env_debug_inner(heap, parent, active)?
764        )),
765        None => Ok(frame),
766    }
767}
768
769fn closure_debug_inner(
770    heap: &Heap,
771    closure: &Closure,
772    active: &mut HashSet<PointerKey>,
773) -> Result<String, EngineError> {
774    Ok(format!(
775        "Closure {{ env: {}, param: {}, param_ty: {}, typ: {}, body: {:?} }}",
776        env_debug_inner(heap, &closure.env, active)?,
777        closure.param,
778        closure.param_ty,
779        closure.typ,
780        closure.body
781    ))
782}
783
784fn value_debug_inner(
785    heap: &Heap,
786    value: &Value,
787    active: &mut HashSet<PointerKey>,
788) -> Result<String, EngineError> {
789    Ok(match value {
790        Value::Bool(v) => v.to_string(),
791        Value::U8(v) => format!("{v}u8"),
792        Value::U16(v) => format!("{v}u16"),
793        Value::U32(v) => format!("{v}u32"),
794        Value::U64(v) => format!("{v}u64"),
795        Value::I8(v) => format!("{v}i8"),
796        Value::I16(v) => format!("{v}i16"),
797        Value::I32(v) => format!("{v}i32"),
798        Value::I64(v) => format!("{v}i64"),
799        Value::F32(v) => format!("{v}f32"),
800        Value::F64(v) => format!("{v}f64"),
801        Value::String(v) => format!("{v:?}"),
802        Value::Uuid(v) => v.to_string(),
803        Value::DateTime(v) => v.to_string(),
804        Value::Tuple(values) => {
805            let items = values
806                .iter()
807                .map(|pointer| pointer_debug_inner(heap, pointer, active))
808                .collect::<Result<Vec<_>, _>>()?;
809            format!("({})", items.join(", "))
810        }
811        Value::Array(values) => {
812            let items = values
813                .iter()
814                .map(|pointer| pointer_debug_inner(heap, pointer, active))
815                .collect::<Result<Vec<_>, _>>()?;
816            format!("<array {}>", items.join(", "))
817        }
818        Value::Dict(values) => {
819            let mut items = values.iter().collect::<Vec<_>>();
820            items.sort_by(|(lhs, _), (rhs, _)| lhs.as_ref().cmp(rhs.as_ref()));
821            let items = items
822                .into_iter()
823                .map(|(name, pointer)| {
824                    Ok(format!(
825                        "{} = {}",
826                        name,
827                        pointer_debug_inner(heap, pointer, active)?
828                    ))
829                })
830                .collect::<Result<Vec<_>, EngineError>>()?;
831            format!("{{{}}}", items.join(", "))
832        }
833        Value::Adt(name, args) => {
834            if let Some(values) = list_to_vec_opt(heap, value)? {
835                let items = values
836                    .iter()
837                    .map(|pointer| pointer_debug_inner(heap, pointer, active))
838                    .collect::<Result<Vec<_>, _>>()?;
839                format!("[{}]", items.join(", "))
840            } else {
841                let mut rendered = vec![name.to_string()];
842                for pointer in args {
843                    rendered.push(pointer_debug_inner(heap, pointer, active)?);
844                }
845                rendered.join(" ")
846            }
847        }
848        Value::Uninitialized(name) => format!("<uninitialized:{name}>"),
849        Value::Closure(closure) => closure_debug_inner(heap, closure, active)?,
850        Value::Native(native) => format!("<native:{}>", native.name()),
851        Value::Overloaded(over) => format!("<overloaded:{}>", over.name()),
852    })
853}
854
855fn value_display_inner(
856    heap: &Heap,
857    value: &Value,
858    active: &mut HashSet<PointerKey>,
859    opts: ValueDisplayOptions,
860) -> Result<String, EngineError> {
861    Ok(match value {
862        Value::Bool(v) => v.to_string(),
863        Value::U8(v) => {
864            if opts.include_numeric_suffixes {
865                format!("{v}u8")
866            } else {
867                v.to_string()
868            }
869        }
870        Value::U16(v) => {
871            if opts.include_numeric_suffixes {
872                format!("{v}u16")
873            } else {
874                v.to_string()
875            }
876        }
877        Value::U32(v) => {
878            if opts.include_numeric_suffixes {
879                format!("{v}u32")
880            } else {
881                v.to_string()
882            }
883        }
884        Value::U64(v) => {
885            if opts.include_numeric_suffixes {
886                format!("{v}u64")
887            } else {
888                v.to_string()
889            }
890        }
891        Value::I8(v) => {
892            if opts.include_numeric_suffixes {
893                format!("{v}i8")
894            } else {
895                v.to_string()
896            }
897        }
898        Value::I16(v) => {
899            if opts.include_numeric_suffixes {
900                format!("{v}i16")
901            } else {
902                v.to_string()
903            }
904        }
905        Value::I32(v) => {
906            if opts.include_numeric_suffixes {
907                format!("{v}i32")
908            } else {
909                v.to_string()
910            }
911        }
912        Value::I64(v) => {
913            if opts.include_numeric_suffixes {
914                format!("{v}i64")
915            } else {
916                v.to_string()
917            }
918        }
919        Value::F32(v) => {
920            if opts.include_numeric_suffixes {
921                format!("{v}f32")
922            } else {
923                v.to_string()
924            }
925        }
926        Value::F64(v) => {
927            if opts.include_numeric_suffixes {
928                format!("{v}f64")
929            } else {
930                v.to_string()
931            }
932        }
933        Value::String(v) => format!("{v:?}"),
934        Value::Uuid(v) => v.to_string(),
935        Value::DateTime(v) => v.to_string(),
936        Value::Tuple(values) => {
937            let items = values
938                .iter()
939                .map(|pointer| pointer_display_inner(heap, pointer, active, opts))
940                .collect::<Result<Vec<_>, _>>()?;
941            format!("({})", items.join(", "))
942        }
943        Value::Array(values) => {
944            let items = values
945                .iter()
946                .map(|pointer| pointer_display_inner(heap, pointer, active, opts))
947                .collect::<Result<Vec<_>, _>>()?;
948            format!("<array {}>", items.join(", "))
949        }
950        Value::Dict(values) => {
951            let mut items = values.iter().collect::<Vec<_>>();
952            items.sort_by(|(lhs, _), (rhs, _)| lhs.as_ref().cmp(rhs.as_ref()));
953            let items = items
954                .into_iter()
955                .map(|(name, pointer)| {
956                    Ok(format!(
957                        "{} = {}",
958                        name,
959                        pointer_display_inner(heap, pointer, active, opts)?
960                    ))
961                })
962                .collect::<Result<Vec<_>, EngineError>>()?;
963            format!("{{{}}}", items.join(", "))
964        }
965        Value::Adt(name, args) => {
966            if let Some(values) = list_to_vec_opt(heap, value)? {
967                let items = values
968                    .iter()
969                    .map(|pointer| pointer_display_inner(heap, pointer, active, opts))
970                    .collect::<Result<Vec<_>, _>>()?;
971                format!("[{}]", items.join(", "))
972            } else {
973                let mut rendered = vec![maybe_strip_snippet_qualifier(name.as_ref(), opts)];
974                for pointer in args {
975                    rendered.push(pointer_display_inner(heap, pointer, active, opts)?);
976                }
977                rendered.join(" ")
978            }
979        }
980        Value::Uninitialized(name) => format!("<uninitialized:{name}>"),
981        Value::Closure(..) => "<closure>".to_string(),
982        Value::Native(native) => format!("<native:{}>", native.name()),
983        Value::Overloaded(over) => format!("<overloaded:{}>", over.name()),
984    })
985}
986
987pub fn value_debug(heap: &Heap, value: &Value) -> Result<String, EngineError> {
988    let mut active = HashSet::new();
989    value_debug_inner(heap, value, &mut active)
990}
991
992pub fn pointer_display(heap: &Heap, pointer: &Pointer) -> Result<String, EngineError> {
993    pointer_display_with(heap, pointer, ValueDisplayOptions::default())
994}
995
996pub fn pointer_display_with(
997    heap: &Heap,
998    pointer: &Pointer,
999    opts: ValueDisplayOptions,
1000) -> Result<String, EngineError> {
1001    let mut active = HashSet::new();
1002    pointer_display_inner(heap, pointer, &mut active, opts)
1003}
1004
1005pub fn closure_debug(heap: &Heap, closure: &Closure) -> Result<String, EngineError> {
1006    let mut active = HashSet::new();
1007    closure_debug_inner(heap, closure, &mut active)
1008}
1009
1010fn pointer_eq_inner(
1011    heap: &Heap,
1012    lhs: &Pointer,
1013    rhs: &Pointer,
1014    seen: &mut HashSet<PointerPairKey>,
1015) -> Result<bool, EngineError> {
1016    let lhs_key = pointer_key(lhs);
1017    let rhs_key = pointer_key(rhs);
1018    if lhs_key == rhs_key {
1019        return Ok(true);
1020    }
1021    let pair = canonical_pointer_pair(lhs_key, rhs_key);
1022    if !seen.insert(pair) {
1023        return Ok(true);
1024    }
1025    let lhs_value = heap.get(lhs)?;
1026    let rhs_value = heap.get(rhs)?;
1027    value_eq_inner(heap, &lhs_value, &rhs_value, seen)
1028}
1029
1030fn env_eq_inner(
1031    heap: &Heap,
1032    lhs: &Env,
1033    rhs: &Env,
1034    seen: &mut HashSet<PointerPairKey>,
1035) -> Result<bool, EngineError> {
1036    if lhs.bindings().len() != rhs.bindings().len() {
1037        return Ok(false);
1038    }
1039    for (name, lhs_pointer) in lhs.bindings() {
1040        let Some(rhs_pointer) = rhs.bindings().get(name) else {
1041            return Ok(false);
1042        };
1043        if !pointer_eq_inner(heap, lhs_pointer, rhs_pointer, seen)? {
1044            return Ok(false);
1045        }
1046    }
1047    match (lhs.parent(), rhs.parent()) {
1048        (Some(lhs_parent), Some(rhs_parent)) => env_eq_inner(heap, lhs_parent, rhs_parent, seen),
1049        (None, None) => Ok(true),
1050        _ => Ok(false),
1051    }
1052}
1053
1054fn closure_eq_inner(
1055    heap: &Heap,
1056    lhs: &Closure,
1057    rhs: &Closure,
1058    seen: &mut HashSet<PointerPairKey>,
1059) -> Result<bool, EngineError> {
1060    if lhs.param != rhs.param
1061        || lhs.param_ty != rhs.param_ty
1062        || lhs.typ != rhs.typ
1063        || lhs.body != rhs.body
1064    {
1065        return Ok(false);
1066    }
1067    env_eq_inner(heap, &lhs.env, &rhs.env, seen)
1068}
1069
1070fn value_eq_inner(
1071    heap: &Heap,
1072    lhs: &Value,
1073    rhs: &Value,
1074    seen: &mut HashSet<PointerPairKey>,
1075) -> Result<bool, EngineError> {
1076    match (lhs, rhs) {
1077        (Value::Bool(lhs), Value::Bool(rhs)) => Ok(lhs == rhs),
1078        (Value::U8(lhs), Value::U8(rhs)) => Ok(lhs == rhs),
1079        (Value::U16(lhs), Value::U16(rhs)) => Ok(lhs == rhs),
1080        (Value::U32(lhs), Value::U32(rhs)) => Ok(lhs == rhs),
1081        (Value::U64(lhs), Value::U64(rhs)) => Ok(lhs == rhs),
1082        (Value::I8(lhs), Value::I8(rhs)) => Ok(lhs == rhs),
1083        (Value::I16(lhs), Value::I16(rhs)) => Ok(lhs == rhs),
1084        (Value::I32(lhs), Value::I32(rhs)) => Ok(lhs == rhs),
1085        (Value::I64(lhs), Value::I64(rhs)) => Ok(lhs == rhs),
1086        (Value::F32(lhs), Value::F32(rhs)) => Ok(lhs == rhs),
1087        (Value::F64(lhs), Value::F64(rhs)) => Ok(lhs == rhs),
1088        (Value::String(lhs), Value::String(rhs)) => Ok(lhs == rhs),
1089        (Value::Uuid(lhs), Value::Uuid(rhs)) => Ok(lhs == rhs),
1090        (Value::DateTime(lhs), Value::DateTime(rhs)) => Ok(lhs == rhs),
1091        (Value::Tuple(lhs), Value::Tuple(rhs)) | (Value::Array(lhs), Value::Array(rhs)) => {
1092            if lhs.len() != rhs.len() {
1093                return Ok(false);
1094            }
1095            for (lhs, rhs) in lhs.iter().zip(rhs.iter()) {
1096                if !pointer_eq_inner(heap, lhs, rhs, seen)? {
1097                    return Ok(false);
1098                }
1099            }
1100            Ok(true)
1101        }
1102        (Value::Dict(lhs), Value::Dict(rhs)) => {
1103            if lhs.len() != rhs.len() {
1104                return Ok(false);
1105            }
1106            for (name, lhs_pointer) in lhs {
1107                let Some(rhs_pointer) = rhs.get(name) else {
1108                    return Ok(false);
1109                };
1110                if !pointer_eq_inner(heap, lhs_pointer, rhs_pointer, seen)? {
1111                    return Ok(false);
1112                }
1113            }
1114            Ok(true)
1115        }
1116        (Value::Adt(lhs_name, lhs_args), Value::Adt(rhs_name, rhs_args)) => {
1117            if lhs_name != rhs_name || lhs_args.len() != rhs_args.len() {
1118                return Ok(false);
1119            }
1120            for (lhs, rhs) in lhs_args.iter().zip(rhs_args.iter()) {
1121                if !pointer_eq_inner(heap, lhs, rhs, seen)? {
1122                    return Ok(false);
1123                }
1124            }
1125            Ok(true)
1126        }
1127        (Value::Uninitialized(lhs), Value::Uninitialized(rhs)) => Ok(lhs == rhs),
1128        (Value::Closure(lhs), Value::Closure(rhs)) => closure_eq_inner(heap, lhs, rhs, seen),
1129        (Value::Native(lhs), Value::Native(rhs)) => Ok(lhs == rhs),
1130        (Value::Overloaded(lhs), Value::Overloaded(rhs)) => Ok(lhs == rhs),
1131        _ => Ok(false),
1132    }
1133}
1134
1135pub fn value_eq(heap: &Heap, lhs: &Value, rhs: &Value) -> Result<bool, EngineError> {
1136    let mut seen = HashSet::new();
1137    value_eq_inner(heap, lhs, rhs, &mut seen)
1138}
1139
1140pub fn pointer_eq(heap: &Heap, lhs: &Pointer, rhs: &Pointer) -> Result<bool, EngineError> {
1141    let mut seen = HashSet::new();
1142    pointer_eq_inner(heap, lhs, rhs, &mut seen)
1143}
1144
1145pub fn closure_eq(heap: &Heap, lhs: &Closure, rhs: &Closure) -> Result<bool, EngineError> {
1146    let mut seen = HashSet::new();
1147    closure_eq_inner(heap, lhs, rhs, &mut seen)
1148}
1149
1150fn list_to_vec_opt(heap: &Heap, value: &Value) -> Result<Option<Vec<Pointer>>, EngineError> {
1151    enum Cursor<'a> {
1152        Borrowed(&'a Value),
1153        Owned(ValueRef),
1154    }
1155
1156    let mut out = Vec::new();
1157    let mut cursor = Cursor::Borrowed(value);
1158    loop {
1159        let cur = match &cursor {
1160            Cursor::Borrowed(v) => *v,
1161            Cursor::Owned(v) => v.as_ref(),
1162        };
1163
1164        match cur {
1165            Value::Adt(tag, args) if sym_eq(tag, "Empty") && args.is_empty() => {
1166                return Ok(Some(out));
1167            }
1168            Value::Adt(tag, args) if sym_eq(tag, "Cons") && args.len() == 2 => {
1169                out.push(args[0]);
1170                cursor = Cursor::Owned(heap.get(&args[1])?);
1171            }
1172            _ => return Ok(None),
1173        }
1174    }
1175}
1176
1177pub(crate) fn list_to_vec(heap: &Heap, value: &Value) -> Result<Vec<Pointer>, EngineError> {
1178    enum Cursor<'a> {
1179        Borrowed(&'a Value),
1180        Owned(ValueRef),
1181    }
1182
1183    let mut out = Vec::new();
1184    let mut cursor = Cursor::Borrowed(value);
1185    loop {
1186        let cur = match &cursor {
1187            Cursor::Borrowed(v) => *v,
1188            Cursor::Owned(v) => v.as_ref(),
1189        };
1190
1191        match cur {
1192            Value::Adt(tag, args) if sym_eq(tag, "Empty") && args.is_empty() => return Ok(out),
1193            Value::Adt(tag, args) if sym_eq(tag, "Cons") && args.len() == 2 => {
1194                out.push(args[0]);
1195                cursor = Cursor::Owned(heap.get(&args[1])?);
1196            }
1197            _ => {
1198                return Err(EngineError::NativeType {
1199                    expected: "list".into(),
1200                    got: heap.type_name_of_value(cur).into(),
1201                });
1202            }
1203        }
1204    }
1205}
1206
1207pub trait IntoPointer {
1208    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError>;
1209}
1210
1211pub trait FromPointer: Sized {
1212    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError>;
1213}
1214
1215pub trait RexType {
1216    fn rex_type() -> Type;
1217}
1218
1219impl IntoPointer for Value {
1220    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1221        heap.alloc_value(self)
1222    }
1223}
1224
1225impl IntoPointer for &Value {
1226    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1227        heap.alloc_value(self.clone())
1228    }
1229}
1230
1231impl IntoPointer for Pointer {
1232    fn into_pointer(self, _heap: &Heap) -> Result<Pointer, EngineError> {
1233        Ok(self)
1234    }
1235}
1236
1237impl IntoPointer for &Pointer {
1238    fn into_pointer(self, _heap: &Heap) -> Result<Pointer, EngineError> {
1239        Ok(*self)
1240    }
1241}
1242
1243impl IntoPointer for bool {
1244    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1245        heap.alloc_bool(self)
1246    }
1247}
1248
1249impl IntoPointer for u8 {
1250    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1251        heap.alloc_u8(self)
1252    }
1253}
1254
1255impl IntoPointer for u16 {
1256    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1257        heap.alloc_u16(self)
1258    }
1259}
1260
1261impl IntoPointer for u32 {
1262    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1263        heap.alloc_u32(self)
1264    }
1265}
1266
1267impl IntoPointer for u64 {
1268    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1269        heap.alloc_u64(self)
1270    }
1271}
1272
1273impl IntoPointer for i8 {
1274    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1275        heap.alloc_i8(self)
1276    }
1277}
1278
1279impl IntoPointer for i16 {
1280    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1281        heap.alloc_i16(self)
1282    }
1283}
1284
1285impl IntoPointer for i32 {
1286    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1287        heap.alloc_i32(self)
1288    }
1289}
1290
1291impl IntoPointer for i64 {
1292    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1293        heap.alloc_i64(self)
1294    }
1295}
1296
1297impl IntoPointer for f32 {
1298    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1299        heap.alloc_f32(self)
1300    }
1301}
1302
1303impl IntoPointer for f64 {
1304    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1305        heap.alloc_f64(self)
1306    }
1307}
1308
1309impl IntoPointer for String {
1310    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1311        heap.alloc_string(self)
1312    }
1313}
1314
1315impl IntoPointer for &str {
1316    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1317        heap.alloc_string(self.to_string())
1318    }
1319}
1320
1321impl<T: IntoPointer> IntoPointer for Vec<T> {
1322    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1323        let ptrs = self
1324            .into_iter()
1325            .map(|v| v.into_pointer(heap))
1326            .collect::<Result<Vec<_>, _>>()?;
1327        heap.alloc_array(ptrs)
1328    }
1329}
1330
1331impl<T: IntoPointer> IntoPointer for Option<T> {
1332    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1333        match self {
1334            Some(v) => {
1335                let ptr = v.into_pointer(heap)?;
1336                heap.alloc_adt(sym("Some"), vec![ptr])
1337            }
1338            None => heap.alloc_adt(sym("None"), vec![]),
1339        }
1340    }
1341}
1342
1343impl IntoPointer for Uuid {
1344    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1345        heap.alloc_uuid(self)
1346    }
1347}
1348
1349impl IntoPointer for DateTime<Utc> {
1350    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1351        heap.alloc_datetime(self)
1352    }
1353}
1354
1355impl RexType for bool {
1356    fn rex_type() -> Type {
1357        Type::builtin(BuiltinTypeId::Bool)
1358    }
1359}
1360
1361impl RexType for u8 {
1362    fn rex_type() -> Type {
1363        Type::builtin(BuiltinTypeId::U8)
1364    }
1365}
1366
1367impl RexType for u16 {
1368    fn rex_type() -> Type {
1369        Type::builtin(BuiltinTypeId::U16)
1370    }
1371}
1372
1373impl RexType for u32 {
1374    fn rex_type() -> Type {
1375        Type::builtin(BuiltinTypeId::U32)
1376    }
1377}
1378
1379impl RexType for u64 {
1380    fn rex_type() -> Type {
1381        Type::builtin(BuiltinTypeId::U64)
1382    }
1383}
1384
1385impl RexType for i8 {
1386    fn rex_type() -> Type {
1387        Type::builtin(BuiltinTypeId::I8)
1388    }
1389}
1390
1391impl RexType for i16 {
1392    fn rex_type() -> Type {
1393        Type::builtin(BuiltinTypeId::I16)
1394    }
1395}
1396
1397impl RexType for i32 {
1398    fn rex_type() -> Type {
1399        Type::builtin(BuiltinTypeId::I32)
1400    }
1401}
1402
1403impl RexType for i64 {
1404    fn rex_type() -> Type {
1405        Type::builtin(BuiltinTypeId::I64)
1406    }
1407}
1408
1409impl RexType for f32 {
1410    fn rex_type() -> Type {
1411        Type::builtin(BuiltinTypeId::F32)
1412    }
1413}
1414
1415impl RexType for f64 {
1416    fn rex_type() -> Type {
1417        Type::builtin(BuiltinTypeId::F64)
1418    }
1419}
1420
1421impl RexType for String {
1422    fn rex_type() -> Type {
1423        Type::builtin(BuiltinTypeId::String)
1424    }
1425}
1426
1427impl RexType for &str {
1428    fn rex_type() -> Type {
1429        Type::builtin(BuiltinTypeId::String)
1430    }
1431}
1432
1433impl RexType for Uuid {
1434    fn rex_type() -> Type {
1435        Type::builtin(BuiltinTypeId::Uuid)
1436    }
1437}
1438
1439impl RexType for DateTime<Utc> {
1440    fn rex_type() -> Type {
1441        Type::builtin(BuiltinTypeId::DateTime)
1442    }
1443}
1444
1445impl<T: RexType> RexType for Vec<T> {
1446    fn rex_type() -> Type {
1447        Type::app(Type::builtin(BuiltinTypeId::Array), T::rex_type())
1448    }
1449}
1450
1451impl<T: RexType> RexType for Option<T> {
1452    fn rex_type() -> Type {
1453        Type::app(Type::builtin(BuiltinTypeId::Option), T::rex_type())
1454    }
1455}
1456
1457impl FromPointer for bool {
1458    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1459        heap.pointer_as_bool(pointer)
1460    }
1461}
1462
1463macro_rules! impl_from_pointer_num {
1464    ($t:ty, $pointer_as:ident) => {
1465        impl FromPointer for $t {
1466            fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1467                heap.$pointer_as(pointer).map(|v| v as $t)
1468            }
1469        }
1470    };
1471}
1472
1473impl_from_pointer_num!(u8, pointer_as_u8);
1474impl_from_pointer_num!(u16, pointer_as_u16);
1475impl_from_pointer_num!(u32, pointer_as_u32);
1476impl_from_pointer_num!(u64, pointer_as_u64);
1477impl_from_pointer_num!(i8, pointer_as_i8);
1478impl_from_pointer_num!(i16, pointer_as_i16);
1479impl_from_pointer_num!(i32, pointer_as_i32);
1480impl_from_pointer_num!(i64, pointer_as_i64);
1481impl_from_pointer_num!(f32, pointer_as_f32);
1482impl_from_pointer_num!(f64, pointer_as_f64);
1483
1484impl FromPointer for String {
1485    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1486        heap.pointer_as_string(pointer)
1487    }
1488}
1489
1490impl FromPointer for Uuid {
1491    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1492        heap.pointer_as_uuid(pointer)
1493    }
1494}
1495
1496impl FromPointer for DateTime<Utc> {
1497    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1498        heap.pointer_as_datetime(pointer)
1499    }
1500}
1501
1502impl FromPointer for Value {
1503    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1504        heap.get(pointer).map(|value| value.as_ref().clone())
1505    }
1506}
1507
1508impl<T> FromPointer for Vec<T>
1509where
1510    T: FromPointer,
1511{
1512    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1513        let xs = heap.pointer_as_array(pointer)?;
1514        let mut ys = Vec::with_capacity(xs.len());
1515        for x in &xs {
1516            ys.push(T::from_pointer(heap, x)?);
1517        }
1518        Ok(ys)
1519    }
1520}
1521
1522impl<T> FromPointer for Option<T>
1523where
1524    T: FromPointer,
1525{
1526    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1527        let (tag, args) = heap.pointer_as_adt(pointer)?;
1528        if sym_eq(&tag, "Some") && args.len() == 1 {
1529            return Ok(Some(T::from_pointer(heap, &args[0])?));
1530        }
1531        if sym_eq(&tag, "None") && args.is_empty() {
1532            return Ok(None);
1533        }
1534        Err(EngineError::NativeType {
1535            expected: "vec".into(),
1536            got: heap.type_name(pointer)?.into(),
1537        })
1538    }
1539}
1540
1541impl<T: IntoPointer, E: IntoPointer> IntoPointer for Result<T, E> {
1542    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1543        match self {
1544            Ok(v) => {
1545                let ptr = v.into_pointer(heap)?;
1546                heap.alloc_adt(sym("Ok"), vec![ptr])
1547            }
1548            Err(e) => {
1549                let ptr = e.into_pointer(heap)?;
1550                heap.alloc_adt(sym("Err"), vec![ptr])
1551            }
1552        }
1553    }
1554}
1555
1556impl<T: RexType, E: RexType> RexType for Result<T, E> {
1557    fn rex_type() -> Type {
1558        Type::app(
1559            Type::app(Type::builtin(BuiltinTypeId::Result), E::rex_type()),
1560            T::rex_type(),
1561        )
1562    }
1563}
1564
1565impl<T, E> FromPointer for Result<T, E>
1566where
1567    T: FromPointer,
1568    E: FromPointer,
1569{
1570    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1571        let (tag, args) = heap.pointer_as_adt(pointer)?;
1572        if sym_eq(&tag, "Ok") && args.len() == 1 {
1573            return Ok(Ok(T::from_pointer(heap, &args[0])?));
1574        }
1575        if sym_eq(&tag, "Err") && args.len() == 1 {
1576            return Ok(Err(E::from_pointer(heap, &args[0])?));
1577        }
1578        Err(EngineError::NativeType {
1579            expected: "result".into(),
1580            got: heap.type_name(pointer)?.into(),
1581        })
1582    }
1583}
1584
1585impl RexType for () {
1586    fn rex_type() -> Type {
1587        Type::tuple(vec![])
1588    }
1589}
1590
1591impl IntoPointer for () {
1592    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1593        heap.alloc_tuple(vec![])
1594    }
1595}
1596
1597impl FromPointer for () {
1598    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1599        let items = heap.pointer_as_tuple(pointer)?;
1600        if items.is_empty() {
1601            Ok(())
1602        } else {
1603            Err(EngineError::NativeType {
1604                expected: "tuple".into(),
1605                got: heap.type_name(pointer)?.into(),
1606            })
1607        }
1608    }
1609}
1610
1611macro_rules! impl_tuple_traits {
1612    ($($name:ident),+) => {
1613        impl<$($name: RexType),+> RexType for ($($name,)+) {
1614            fn rex_type() -> Type {
1615                Type::tuple(vec![$($name::rex_type()),+])
1616            }
1617        }
1618
1619        impl<$($name: IntoPointer),+> IntoPointer for ($($name,)+) {
1620            #[allow(non_snake_case)]
1621            fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1622                let ($($name,)+) = self;
1623                let ptrs = vec![$($name.into_pointer(heap)?),+];
1624                heap.alloc_tuple(ptrs)
1625            }
1626        }
1627
1628        impl<$($name: FromPointer),+> FromPointer for ($($name,)+) {
1629            #[allow(non_snake_case)]
1630            fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1631                let items = heap.pointer_as_tuple(pointer)?;
1632                match items.as_slice() {
1633                    [$($name),+] => {
1634                        Ok(($(<$name as FromPointer>::from_pointer(heap, $name)?),+,))
1635                    }
1636                    _ => Err(EngineError::NativeType {
1637                        expected: "tuple".into(),
1638                        got: heap.type_name(pointer)?.into(),
1639                    }),
1640                }
1641            }
1642        }
1643    };
1644}
1645
1646impl_tuple_traits!(A0);
1647impl_tuple_traits!(A0, A1);
1648impl_tuple_traits!(A0, A1, A2);
1649impl_tuple_traits!(A0, A1, A2, A3);
1650impl_tuple_traits!(A0, A1, A2, A3, A4);
1651impl_tuple_traits!(A0, A1, A2, A3, A4, A5);
1652impl_tuple_traits!(A0, A1, A2, A3, A4, A5, A6);
1653impl_tuple_traits!(A0, A1, A2, A3, A4, A5, A6, A7);
1654
1655impl RexType for serde_json::Value {
1656    fn rex_type() -> Type {
1657        Type::con("serde_json::Value", 0)
1658    }
1659}
1660
1661impl IntoPointer for serde_json::Value {
1662    fn into_pointer(self, heap: &Heap) -> Result<Pointer, EngineError> {
1663        let json_string = serde_json::to_string(&self)
1664            .map_err(|e| EngineError::Internal(format!("failed to serialize JSON: {}", e)))?;
1665        let string_ptr = heap.alloc_string(json_string)?;
1666        heap.alloc_adt(sym("serde_json::Value"), vec![string_ptr])
1667    }
1668}
1669
1670impl FromPointer for serde_json::Value {
1671    fn from_pointer(heap: &Heap, pointer: &Pointer) -> Result<Self, EngineError> {
1672        let (tag, args) = heap.pointer_as_adt(pointer)?;
1673        if !sym_eq(&tag, "serde_json::Value") {
1674            return Err(EngineError::NativeType {
1675                expected: "serde_json::Value".into(),
1676                got: heap.type_name(pointer)?.into(),
1677            });
1678        }
1679        if args.len() != 1 {
1680            return Err(EngineError::Internal(format!(
1681                "serde_json::Value ADT should have 1 field, got {}",
1682                args.len()
1683            )));
1684        }
1685        let json_string = heap.pointer_as_string(&args[0])?;
1686        serde_json::from_str(&json_string)
1687            .map_err(|e| EngineError::Internal(format!("failed to deserialize JSON: {}", e)))
1688    }
1689}
1690
1691#[cfg(test)]
1692mod tests {
1693    use super::*;
1694
1695    #[test]
1696    fn heap_rejects_pointer_from_different_heap() {
1697        let heap_a = Heap::new();
1698        let heap_b = Heap::new();
1699        let pointer = heap_a.alloc_i32(42).expect("alloc_i32 should succeed");
1700
1701        let err = match heap_b.get(&pointer) {
1702            Ok(_) => panic!("cross-heap pointer use should fail"),
1703            Err(err) => err,
1704        };
1705        let EngineError::Internal(msg) = err else {
1706            panic!("expected internal error for cross-heap pointer");
1707        };
1708        assert!(msg.contains("different heap"), "unexpected error: {msg}");
1709    }
1710
1711    #[test]
1712    fn scoped_heap_allocates_and_reads() {
1713        Heap::scoped(|heap| {
1714            let pointer = heap.alloc_i32(7).expect("alloc_i32 should succeed");
1715            let value = heap.get(&pointer).expect("pointer should resolve");
1716            assert!(matches!(value.as_ref(), Value::I32(7)));
1717        });
1718    }
1719
1720    #[test]
1721    fn value_as_reports_mismatch_with_value_type_error() {
1722        let value = Value::Bool(true);
1723        let err = value
1724            .value_as_i32()
1725            .expect_err("bool should not coerce to i32");
1726        match err {
1727            EngineError::NativeType { expected, got } => {
1728                assert_eq!(expected, "i32");
1729                assert_eq!(got, "bool");
1730            }
1731            other => panic!("unexpected error variant: {other:?}"),
1732        }
1733    }
1734
1735    #[test]
1736    fn pointer_as_reports_mismatch_with_value_type_error() {
1737        let heap = Heap::new();
1738        let pointer = heap.alloc_bool(true).expect("alloc_bool should succeed");
1739        let err = heap
1740            .pointer_as_i32(&pointer)
1741            .expect_err("bool pointer should not coerce to i32");
1742        match err {
1743            EngineError::NativeType { expected, got } => {
1744                assert_eq!(expected, "i32");
1745                assert_eq!(got, "bool");
1746            }
1747            other => panic!("unexpected error variant: {other:?}"),
1748        }
1749    }
1750
1751    #[test]
1752    fn pointer_as_returns_payload_on_match() {
1753        let heap = Heap::new();
1754        let pointer = heap.alloc_i32(42).expect("alloc_i32 should succeed");
1755        let value = heap
1756            .pointer_as_i32(&pointer)
1757            .expect("i32 pointer should decode");
1758        assert_eq!(value, 42);
1759    }
1760
1761    #[test]
1762    fn value_display_default_keeps_suffixes_and_names() {
1763        let heap = Heap::new();
1764        let num = heap.alloc_i32(2).expect("alloc i32");
1765        let got_num = pointer_display(&heap, &num).expect("display i32");
1766        assert_eq!(got_num, "2");
1767
1768        let ctor = heap
1769            .alloc_adt(sym("@snippetabc.A"), vec![])
1770            .expect("alloc adt");
1771        let got_ctor = pointer_display(&heap, &ctor).expect("display adt");
1772        assert_eq!(got_ctor, "A");
1773    }
1774
1775    #[test]
1776    fn value_display_unsanitized_keeps_suffixes_and_names() {
1777        let heap = Heap::new();
1778        let opts = ValueDisplayOptions::unsanitized();
1779        let num = heap.alloc_i32(2).expect("alloc i32");
1780        let got_num = pointer_display_with(&heap, &num, opts).expect("display i32");
1781        assert_eq!(got_num, "2i32");
1782
1783        let ctor = heap
1784            .alloc_adt(sym("@snippetabc.A"), vec![])
1785            .expect("alloc adt");
1786        let got_ctor = pointer_display_with(&heap, &ctor, opts).expect("display adt");
1787        assert_eq!(got_ctor, "@snippetabc.A");
1788    }
1789
1790    #[test]
1791    fn value_display_docs_mode_strips_internal_noise() {
1792        let heap = Heap::new();
1793        let opts = ValueDisplayOptions::docs();
1794        let num = heap.alloc_i32(2).expect("alloc i32");
1795        let got_num = pointer_display_with(&heap, &num, opts).expect("display i32 docs");
1796        assert_eq!(got_num, "2");
1797
1798        let ctor = heap
1799            .alloc_adt(sym("@snippetabc.A"), vec![])
1800            .expect("alloc adt");
1801        let got_ctor = pointer_display_with(&heap, &ctor, opts).expect("display adt docs");
1802        assert_eq!(got_ctor, "A");
1803
1804        let non_snippet = heap.alloc_adt(sym("pkg.A"), vec![]).expect("alloc adt");
1805        let got_non_snippet =
1806            pointer_display_with(&heap, &non_snippet, opts).expect("display non-snippet adt docs");
1807        assert_eq!(got_non_snippet, "pkg.A");
1808    }
1809}