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