1use 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 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}