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