1use std::any::Any;
2use std::cell::{Cell, RefCell};
3use std::collections::BTreeMap;
4use std::fmt;
5use std::hash::{Hash, Hasher};
6use std::rc::Rc;
7
8use hashbrown::HashMap as SpurMap;
9use lasso::{Rodeo, Spur};
10
11use crate::error::SemaError;
12use crate::EvalContext;
13
14#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
18compile_error!("sema-core NaN-boxed Value requires a 64-bit platform (or wasm32)");
19
20thread_local! {
23 static INTERNER: RefCell<Rodeo> = RefCell::new(Rodeo::default());
24}
25
26pub fn intern(s: &str) -> Spur {
28 INTERNER.with(|r| r.borrow_mut().get_or_intern(s))
29}
30
31pub fn resolve(spur: Spur) -> String {
33 INTERNER.with(|r| r.borrow().resolve(&spur).to_string())
34}
35
36pub fn with_resolved<F, R>(spur: Spur, f: F) -> R
38where
39 F: FnOnce(&str) -> R,
40{
41 INTERNER.with(|r| {
42 let interner = r.borrow();
43 f(interner.resolve(&spur))
44 })
45}
46
47pub fn interner_stats() -> (usize, usize) {
49 INTERNER.with(|r| {
50 let interner = r.borrow();
51 let count = interner.len();
52 let bytes = count * 16; (count, bytes)
54 })
55}
56
57thread_local! {
60 static GENSYM_COUNTER: Cell<u64> = const { Cell::new(0) };
61}
62
63pub fn next_gensym(prefix: &str) -> String {
67 GENSYM_COUNTER.with(|c| {
68 let val = c.get();
69 c.set(val.wrapping_add(1));
70 format!("{prefix}__{val}")
71 })
72}
73
74pub fn compare_spurs(a: Spur, b: Spur) -> std::cmp::Ordering {
76 if a == b {
77 return std::cmp::Ordering::Equal;
78 }
79 INTERNER.with(|r| {
80 let interner = r.borrow();
81 interner.resolve(&a).cmp(interner.resolve(&b))
82 })
83}
84
85pub type NativeFnInner = dyn Fn(&EvalContext, &[Value]) -> Result<Value, SemaError>;
89
90pub struct NativeFn {
91 pub name: String,
92 pub func: Box<NativeFnInner>,
93 pub payload: Option<Rc<dyn Any>>,
94}
95
96impl NativeFn {
97 pub fn simple(
98 name: impl Into<String>,
99 f: impl Fn(&[Value]) -> Result<Value, SemaError> + 'static,
100 ) -> Self {
101 Self {
102 name: name.into(),
103 func: Box::new(move |_ctx, args| f(args)),
104 payload: None,
105 }
106 }
107
108 pub fn with_ctx(
109 name: impl Into<String>,
110 f: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
111 ) -> Self {
112 Self {
113 name: name.into(),
114 func: Box::new(f),
115 payload: None,
116 }
117 }
118
119 pub fn with_payload(
120 name: impl Into<String>,
121 payload: Rc<dyn Any>,
122 f: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
123 ) -> Self {
124 Self {
125 name: name.into(),
126 func: Box::new(f),
127 payload: Some(payload),
128 }
129 }
130}
131
132impl fmt::Debug for NativeFn {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "<native-fn {}>", self.name)
135 }
136}
137
138#[derive(Debug, Clone)]
140pub struct Lambda {
141 pub params: Vec<Spur>,
142 pub rest_param: Option<Spur>,
143 pub body: Vec<Value>,
144 pub env: Env,
145 pub name: Option<Spur>,
146}
147
148#[derive(Debug, Clone)]
150pub struct Macro {
151 pub params: Vec<Spur>,
152 pub rest_param: Option<Spur>,
153 pub body: Vec<Value>,
154 pub name: Spur,
155}
156
157pub struct Thunk {
159 pub body: Value,
160 pub forced: RefCell<Option<Value>>,
161}
162
163impl fmt::Debug for Thunk {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 if self.forced.borrow().is_some() {
166 write!(f, "<promise (forced)>")
167 } else {
168 write!(f, "<promise>")
169 }
170 }
171}
172
173impl Clone for Thunk {
174 fn clone(&self) -> Self {
175 Thunk {
176 body: self.body.clone(),
177 forced: RefCell::new(self.forced.borrow().clone()),
178 }
179 }
180}
181
182#[derive(Debug, Clone, PartialEq, Eq)]
184pub enum PromiseState {
185 Pending,
186 Resolved(Value),
187 Rejected(String),
188}
189
190pub struct AsyncPromise {
192 pub state: RefCell<PromiseState>,
193 pub task_id: Cell<u64>,
194}
195
196impl fmt::Debug for AsyncPromise {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 match &*self.state.borrow() {
199 PromiseState::Pending => write!(f, "<async-promise pending>"),
200 PromiseState::Resolved(_) => write!(f, "<async-promise resolved>"),
201 PromiseState::Rejected(e) => write!(f, "<async-promise rejected: {e}>"),
202 }
203 }
204}
205
206impl Clone for AsyncPromise {
207 fn clone(&self) -> Self {
208 AsyncPromise {
209 state: RefCell::new(self.state.borrow().clone()),
210 task_id: Cell::new(self.task_id.get()),
211 }
212 }
213}
214
215pub struct Channel {
217 pub buffer: RefCell<std::collections::VecDeque<Value>>,
218 pub capacity: usize,
219 pub closed: Cell<bool>,
220}
221
222impl fmt::Debug for Channel {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 let len = self.buffer.borrow().len();
225 write!(f, "<channel {len}/{}>", self.capacity)
226 }
227}
228
229impl Clone for Channel {
230 fn clone(&self) -> Self {
231 Channel {
232 buffer: RefCell::new(self.buffer.borrow().clone()),
233 capacity: self.capacity,
234 closed: Cell::new(self.closed.get()),
235 }
236 }
237}
238
239#[derive(Debug, Clone)]
241pub struct Record {
242 pub type_tag: Spur,
243 pub fields: Vec<Value>,
244}
245
246#[derive(Debug, Clone, PartialEq, Eq)]
248pub enum Role {
249 System,
250 User,
251 Assistant,
252 Tool,
253}
254
255impl fmt::Display for Role {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 match self {
258 Role::System => write!(f, "system"),
259 Role::User => write!(f, "user"),
260 Role::Assistant => write!(f, "assistant"),
261 Role::Tool => write!(f, "tool"),
262 }
263 }
264}
265
266#[derive(Debug, Clone)]
268pub struct ImageAttachment {
269 pub data: String,
270 pub media_type: String,
271}
272
273#[derive(Debug, Clone)]
275pub struct Message {
276 pub role: Role,
277 pub content: String,
278 pub images: Vec<ImageAttachment>,
280}
281
282#[derive(Debug, Clone)]
284pub struct Prompt {
285 pub messages: Vec<Message>,
286}
287
288#[derive(Debug, Clone)]
290pub struct Conversation {
291 pub messages: Vec<Message>,
292 pub model: String,
293 pub metadata: BTreeMap<String, String>,
294}
295
296#[derive(Debug, Clone)]
298pub struct ToolDefinition {
299 pub name: String,
300 pub description: String,
301 pub parameters: Value,
302 pub handler: Value,
303}
304
305#[derive(Debug, Clone)]
307pub struct Agent {
308 pub name: String,
309 pub system: String,
310 pub tools: Vec<Value>,
311 pub max_turns: usize,
312 pub model: String,
313}
314
315pub struct MultiMethod {
318 pub name: Spur,
319 pub dispatch_fn: Value,
320 pub methods: RefCell<BTreeMap<Value, Value>>,
321 pub default: RefCell<Option<Value>>,
322}
323
324impl fmt::Debug for MultiMethod {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 write!(f, "<multimethod {}>", resolve(self.name))
327 }
328}
329
330pub trait SemaStream: fmt::Debug {
333 fn read(&self, buf: &mut [u8]) -> Result<usize, SemaError>;
334 fn write(&self, data: &[u8]) -> Result<usize, SemaError>;
335 fn available(&self) -> Result<bool, SemaError> {
336 Ok(false)
337 }
338 fn flush(&self) -> Result<(), SemaError> {
339 Ok(())
340 }
341 fn close(&self) -> Result<(), SemaError> {
342 Ok(())
343 }
344 fn is_readable(&self) -> bool {
345 true
346 }
347 fn is_writable(&self) -> bool {
348 true
349 }
350 fn stream_type(&self) -> &'static str;
351 fn as_any(&self) -> &dyn std::any::Any;
352}
353
354pub struct StreamBox {
357 inner: RefCell<Box<dyn SemaStream>>,
358 closed: Cell<bool>,
359}
360
361impl StreamBox {
362 pub fn new(s: impl SemaStream + 'static) -> Self {
363 StreamBox {
364 inner: RefCell::new(Box::new(s)),
365 closed: Cell::new(false),
366 }
367 }
368
369 pub fn read(&self, buf: &mut [u8]) -> Result<usize, SemaError> {
370 if self.closed.get() {
371 return Err(SemaError::eval("stream/read: stream is closed"));
372 }
373 self.inner.borrow().read(buf)
374 }
375
376 pub fn write(&self, data: &[u8]) -> Result<usize, SemaError> {
377 if self.closed.get() {
378 return Err(SemaError::eval("stream/write: stream is closed"));
379 }
380 self.inner.borrow().write(data)
381 }
382
383 pub fn flush(&self) -> Result<(), SemaError> {
384 if self.closed.get() {
385 return Err(SemaError::eval("stream/flush: stream is closed"));
386 }
387 self.inner.borrow().flush()
388 }
389
390 pub fn close(&self) -> Result<(), SemaError> {
391 if self.closed.get() {
392 return Ok(()); }
394 self.inner.borrow().close()?;
395 self.closed.set(true);
396 Ok(())
397 }
398
399 pub fn is_closed(&self) -> bool {
400 self.closed.get()
401 }
402
403 pub fn is_readable(&self) -> bool {
404 !self.closed.get() && self.inner.borrow().is_readable()
405 }
406
407 pub fn is_writable(&self) -> bool {
408 !self.closed.get() && self.inner.borrow().is_writable()
409 }
410
411 pub fn available(&self) -> Result<bool, SemaError> {
412 if self.closed.get() {
413 return Ok(false);
414 }
415 self.inner.borrow().available()
416 }
417
418 pub fn stream_type(&self) -> &'static str {
419 self.inner.borrow().stream_type()
420 }
421
422 pub fn borrow_inner(&self) -> std::cell::Ref<'_, Box<dyn SemaStream>> {
423 self.inner.borrow()
424 }
425}
426
427impl fmt::Debug for StreamBox {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 write!(f, "<stream:{}>", self.stream_type())
430 }
431}
432
433impl Clone for MultiMethod {
434 fn clone(&self) -> Self {
435 MultiMethod {
436 name: self.name,
437 dispatch_fn: self.dispatch_fn.clone(),
438 methods: RefCell::new(self.methods.borrow().clone()),
439 default: RefCell::new(self.default.borrow().clone()),
440 }
441 }
442}
443
444const BOX_MASK: u64 = 0xFFF8_0000_0000_0000;
456
457const PAYLOAD_MASK: u64 = (1u64 << 45) - 1; const INT_SIGN_BIT: u64 = 1u64 << 44;
462
463const TAG_MASK_6BIT: u64 = 0x3F;
465
466const CANONICAL_NAN: u64 = 0x7FF8_0000_0000_0000;
468
469const TAG_NIL: u64 = 0;
471const TAG_FALSE: u64 = 1;
472const TAG_TRUE: u64 = 2;
473const TAG_INT_SMALL: u64 = 3;
474const TAG_CHAR: u64 = 4;
475const TAG_SYMBOL: u64 = 5;
476const TAG_KEYWORD: u64 = 6;
477const TAG_INT_BIG: u64 = 7;
478const TAG_STRING: u64 = 8;
479const TAG_LIST: u64 = 9;
480const TAG_VECTOR: u64 = 10;
481const TAG_MAP: u64 = 11;
482const TAG_HASHMAP: u64 = 12;
483const TAG_LAMBDA: u64 = 13;
484const TAG_MACRO: u64 = 14;
485pub const TAG_NATIVE_FN: u64 = 15;
486const TAG_PROMPT: u64 = 16;
487const TAG_MESSAGE: u64 = 17;
488const TAG_CONVERSATION: u64 = 18;
489const TAG_TOOL_DEF: u64 = 19;
490const TAG_AGENT: u64 = 20;
491const TAG_THUNK: u64 = 21;
492const TAG_RECORD: u64 = 22;
493const TAG_BYTEVECTOR: u64 = 23;
494const TAG_MULTIMETHOD: u64 = 24;
495const TAG_STREAM: u64 = 25;
496const TAG_F64_ARRAY: u64 = 26;
497const TAG_I64_ARRAY: u64 = 27;
498const TAG_ASYNC_PROMISE: u64 = 28;
499const TAG_CHANNEL: u64 = 29;
500
501const SMALL_INT_MIN: i64 = -(1i64 << 44);
503const SMALL_INT_MAX: i64 = (1i64 << 44) - 1;
504
505pub const NAN_TAG_MASK: u64 = BOX_MASK | (TAG_MASK_6BIT << 45); pub const NAN_INT_SMALL_PATTERN: u64 = BOX_MASK | (TAG_INT_SMALL << 45);
512
513pub const NAN_PAYLOAD_MASK: u64 = PAYLOAD_MASK;
515
516pub const NAN_INT_SIGN_BIT: u64 = INT_SIGN_BIT;
518
519pub const NAN_PAYLOAD_BITS: u32 = 45;
521
522#[inline(always)]
525fn make_boxed(tag: u64, payload: u64) -> u64 {
526 BOX_MASK | (tag << 45) | (payload & PAYLOAD_MASK)
527}
528
529#[inline(always)]
530fn is_boxed(bits: u64) -> bool {
531 (bits & BOX_MASK) == BOX_MASK
532}
533
534#[inline(always)]
535fn get_tag(bits: u64) -> u64 {
536 (bits >> 45) & TAG_MASK_6BIT
537}
538
539#[inline(always)]
540fn get_payload(bits: u64) -> u64 {
541 bits & PAYLOAD_MASK
542}
543
544#[inline(always)]
545fn ptr_to_payload(ptr: *const u8) -> u64 {
546 let raw = ptr as u64;
547 debug_assert!(raw & 0x7 == 0, "pointer not 8-byte aligned: 0x{:x}", raw);
548 debug_assert!(
549 raw >> 48 == 0,
550 "pointer exceeds 48-bit VA space: 0x{:x}",
551 raw
552 );
553 raw >> 3
554}
555
556#[inline(always)]
557fn payload_to_ptr(payload: u64) -> *const u8 {
558 (payload << 3) as *const u8
559}
560
561pub enum ValueView {
566 Nil,
567 Bool(bool),
568 Int(i64),
569 Float(f64),
570 String(Rc<String>),
571 Symbol(Spur),
572 Keyword(Spur),
573 Char(char),
574 List(Rc<Vec<Value>>),
575 Vector(Rc<Vec<Value>>),
576 Map(Rc<BTreeMap<Value, Value>>),
577 HashMap(Rc<hashbrown::HashMap<Value, Value>>),
578 Lambda(Rc<Lambda>),
579 Macro(Rc<Macro>),
580 NativeFn(Rc<NativeFn>),
581 Prompt(Rc<Prompt>),
582 Message(Rc<Message>),
583 Conversation(Rc<Conversation>),
584 ToolDef(Rc<ToolDefinition>),
585 Agent(Rc<Agent>),
586 Thunk(Rc<Thunk>),
587 Record(Rc<Record>),
588 Bytevector(Rc<Vec<u8>>),
589 MultiMethod(Rc<MultiMethod>),
590 Stream(Rc<StreamBox>),
591 F64Array(Rc<Vec<f64>>),
592 I64Array(Rc<Vec<i64>>),
593 AsyncPromise(Rc<AsyncPromise>),
594 Channel(Rc<Channel>),
595}
596
597#[repr(transparent)]
603pub struct Value(u64);
604
605impl Value {
608 pub const NIL: Value = Value(make_boxed_const(TAG_NIL, 0));
611 pub const TRUE: Value = Value(make_boxed_const(TAG_TRUE, 0));
612 pub const FALSE: Value = Value(make_boxed_const(TAG_FALSE, 0));
613
614 #[inline(always)]
615 pub fn nil() -> Value {
616 Value::NIL
617 }
618
619 #[inline(always)]
620 pub fn bool(b: bool) -> Value {
621 if b {
622 Value::TRUE
623 } else {
624 Value::FALSE
625 }
626 }
627
628 #[inline(always)]
629 pub fn int(n: i64) -> Value {
630 if (SMALL_INT_MIN..=SMALL_INT_MAX).contains(&n) {
631 let payload = (n as u64) & PAYLOAD_MASK;
633 Value(make_boxed(TAG_INT_SMALL, payload))
634 } else {
635 let rc = Rc::new(n);
637 let ptr = Rc::into_raw(rc) as *const u8;
638 Value(make_boxed(TAG_INT_BIG, ptr_to_payload(ptr)))
639 }
640 }
641
642 #[inline(always)]
643 pub fn float(f: f64) -> Value {
644 let bits = f.to_bits();
645 if f.is_nan() {
646 Value(CANONICAL_NAN)
648 } else {
649 debug_assert!(
656 !is_boxed(bits),
657 "non-NaN float collides with boxed pattern: {:?} = 0x{:016x}",
658 f,
659 bits
660 );
661 Value(bits)
662 }
663 }
664
665 #[inline(always)]
666 pub fn char(c: char) -> Value {
667 Value(make_boxed(TAG_CHAR, c as u64))
668 }
669
670 #[inline(always)]
671 pub fn symbol_from_spur(spur: Spur) -> Value {
672 let bits: u32 = unsafe { std::mem::transmute(spur) };
673 Value(make_boxed(TAG_SYMBOL, bits as u64))
674 }
675
676 pub fn symbol(s: &str) -> Value {
677 Value::symbol_from_spur(intern(s))
678 }
679
680 #[inline(always)]
681 pub fn keyword_from_spur(spur: Spur) -> Value {
682 let bits: u32 = unsafe { std::mem::transmute(spur) };
683 Value(make_boxed(TAG_KEYWORD, bits as u64))
684 }
685
686 pub fn keyword(s: &str) -> Value {
687 Value::keyword_from_spur(intern(s))
688 }
689
690 fn from_rc_ptr<T>(tag: u64, rc: Rc<T>) -> Value {
693 let ptr = Rc::into_raw(rc) as *const u8;
694 Value(make_boxed(tag, ptr_to_payload(ptr)))
695 }
696
697 pub fn string(s: &str) -> Value {
698 Value::from_rc_ptr(TAG_STRING, Rc::new(s.to_string()))
699 }
700
701 pub fn string_from_rc(rc: Rc<String>) -> Value {
702 Value::from_rc_ptr(TAG_STRING, rc)
703 }
704
705 pub fn list(v: Vec<Value>) -> Value {
706 Value::from_rc_ptr(TAG_LIST, Rc::new(v))
707 }
708
709 pub fn list_from_rc(rc: Rc<Vec<Value>>) -> Value {
710 Value::from_rc_ptr(TAG_LIST, rc)
711 }
712
713 pub fn vector(v: Vec<Value>) -> Value {
714 Value::from_rc_ptr(TAG_VECTOR, Rc::new(v))
715 }
716
717 pub fn vector_from_rc(rc: Rc<Vec<Value>>) -> Value {
718 Value::from_rc_ptr(TAG_VECTOR, rc)
719 }
720
721 pub fn map(m: BTreeMap<Value, Value>) -> Value {
722 Value::from_rc_ptr(TAG_MAP, Rc::new(m))
723 }
724
725 pub fn map_from_rc(rc: Rc<BTreeMap<Value, Value>>) -> Value {
726 Value::from_rc_ptr(TAG_MAP, rc)
727 }
728
729 pub fn hashmap(entries: Vec<(Value, Value)>) -> Value {
730 let map: hashbrown::HashMap<Value, Value> = entries.into_iter().collect();
731 Value::from_rc_ptr(TAG_HASHMAP, Rc::new(map))
732 }
733
734 pub fn hashmap_from_rc(rc: Rc<hashbrown::HashMap<Value, Value>>) -> Value {
735 Value::from_rc_ptr(TAG_HASHMAP, rc)
736 }
737
738 pub fn lambda(l: Lambda) -> Value {
739 Value::from_rc_ptr(TAG_LAMBDA, Rc::new(l))
740 }
741
742 pub fn lambda_from_rc(rc: Rc<Lambda>) -> Value {
743 Value::from_rc_ptr(TAG_LAMBDA, rc)
744 }
745
746 pub fn macro_val(m: Macro) -> Value {
747 Value::from_rc_ptr(TAG_MACRO, Rc::new(m))
748 }
749
750 pub fn macro_from_rc(rc: Rc<Macro>) -> Value {
751 Value::from_rc_ptr(TAG_MACRO, rc)
752 }
753
754 pub fn native_fn(f: NativeFn) -> Value {
755 Value::from_rc_ptr(TAG_NATIVE_FN, Rc::new(f))
756 }
757
758 pub fn native_fn_from_rc(rc: Rc<NativeFn>) -> Value {
759 Value::from_rc_ptr(TAG_NATIVE_FN, rc)
760 }
761
762 pub fn prompt(p: Prompt) -> Value {
763 Value::from_rc_ptr(TAG_PROMPT, Rc::new(p))
764 }
765
766 pub fn prompt_from_rc(rc: Rc<Prompt>) -> Value {
767 Value::from_rc_ptr(TAG_PROMPT, rc)
768 }
769
770 pub fn message(m: Message) -> Value {
771 Value::from_rc_ptr(TAG_MESSAGE, Rc::new(m))
772 }
773
774 pub fn message_from_rc(rc: Rc<Message>) -> Value {
775 Value::from_rc_ptr(TAG_MESSAGE, rc)
776 }
777
778 pub fn conversation(c: Conversation) -> Value {
779 Value::from_rc_ptr(TAG_CONVERSATION, Rc::new(c))
780 }
781
782 pub fn conversation_from_rc(rc: Rc<Conversation>) -> Value {
783 Value::from_rc_ptr(TAG_CONVERSATION, rc)
784 }
785
786 pub fn tool_def(t: ToolDefinition) -> Value {
787 Value::from_rc_ptr(TAG_TOOL_DEF, Rc::new(t))
788 }
789
790 pub fn tool_def_from_rc(rc: Rc<ToolDefinition>) -> Value {
791 Value::from_rc_ptr(TAG_TOOL_DEF, rc)
792 }
793
794 pub fn agent(a: Agent) -> Value {
795 Value::from_rc_ptr(TAG_AGENT, Rc::new(a))
796 }
797
798 pub fn agent_from_rc(rc: Rc<Agent>) -> Value {
799 Value::from_rc_ptr(TAG_AGENT, rc)
800 }
801
802 pub fn thunk(t: Thunk) -> Value {
803 Value::from_rc_ptr(TAG_THUNK, Rc::new(t))
804 }
805
806 pub fn thunk_from_rc(rc: Rc<Thunk>) -> Value {
807 Value::from_rc_ptr(TAG_THUNK, rc)
808 }
809
810 pub fn record(r: Record) -> Value {
811 Value::from_rc_ptr(TAG_RECORD, Rc::new(r))
812 }
813
814 pub fn record_from_rc(rc: Rc<Record>) -> Value {
815 Value::from_rc_ptr(TAG_RECORD, rc)
816 }
817
818 pub fn bytevector(bytes: Vec<u8>) -> Value {
819 Value::from_rc_ptr(TAG_BYTEVECTOR, Rc::new(bytes))
820 }
821
822 pub fn bytevector_from_rc(rc: Rc<Vec<u8>>) -> Value {
823 Value::from_rc_ptr(TAG_BYTEVECTOR, rc)
824 }
825
826 pub fn f64_array(data: Vec<f64>) -> Value {
827 Value::from_rc_ptr(TAG_F64_ARRAY, Rc::new(data))
828 }
829
830 pub fn f64_array_from_rc(rc: Rc<Vec<f64>>) -> Value {
831 Value::from_rc_ptr(TAG_F64_ARRAY, rc)
832 }
833
834 pub fn i64_array(data: Vec<i64>) -> Value {
835 Value::from_rc_ptr(TAG_I64_ARRAY, Rc::new(data))
836 }
837
838 pub fn i64_array_from_rc(rc: Rc<Vec<i64>>) -> Value {
839 Value::from_rc_ptr(TAG_I64_ARRAY, rc)
840 }
841
842 pub fn multimethod(m: MultiMethod) -> Value {
843 Value::from_rc_ptr(TAG_MULTIMETHOD, Rc::new(m))
844 }
845
846 pub fn multimethod_from_rc(rc: Rc<MultiMethod>) -> Value {
847 Value::from_rc_ptr(TAG_MULTIMETHOD, rc)
848 }
849
850 pub fn stream(s: impl SemaStream + 'static) -> Value {
851 Value::from_rc_ptr(TAG_STREAM, Rc::new(StreamBox::new(s)))
852 }
853
854 pub fn stream_from_rc(rc: Rc<StreamBox>) -> Value {
855 Value::from_rc_ptr(TAG_STREAM, rc)
856 }
857
858 pub fn async_promise(promise: AsyncPromise) -> Value {
859 Value::from_rc_ptr(TAG_ASYNC_PROMISE, Rc::new(promise))
860 }
861 pub fn async_promise_from_rc(rc: Rc<AsyncPromise>) -> Value {
862 Value::from_rc_ptr(TAG_ASYNC_PROMISE, rc)
863 }
864 pub fn channel(ch: Channel) -> Value {
865 Value::from_rc_ptr(TAG_CHANNEL, Rc::new(ch))
866 }
867 pub fn channel_from_rc(rc: Rc<Channel>) -> Value {
868 Value::from_rc_ptr(TAG_CHANNEL, rc)
869 }
870}
871
872const fn make_boxed_const(tag: u64, payload: u64) -> u64 {
874 BOX_MASK | (tag << 45) | (payload & PAYLOAD_MASK)
875}
876
877impl Value {
880 #[inline(always)]
882 pub fn raw_bits(&self) -> u64 {
883 self.0
884 }
885
886 #[inline(always)]
895 pub unsafe fn from_raw_bits(bits: u64) -> Value {
896 Value(bits)
897 }
898
899 #[inline(always)]
902 pub fn raw_tag(&self) -> Option<u64> {
903 if is_boxed(self.0) {
904 Some(get_tag(self.0))
905 } else {
906 None
907 }
908 }
909
910 #[inline(always)]
913 pub fn as_native_fn_ref(&self) -> Option<&NativeFn> {
914 if is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN {
915 Some(unsafe { self.borrow_ref::<NativeFn>() })
916 } else {
917 None
918 }
919 }
920
921 #[inline(always)]
923 pub fn is_float(&self) -> bool {
924 !is_boxed(self.0)
925 }
926
927 #[inline(always)]
930 unsafe fn get_rc<T>(&self) -> Rc<T> {
931 let payload = get_payload(self.0);
932 let ptr = payload_to_ptr(payload) as *const T;
933 Rc::increment_strong_count(ptr);
934 Rc::from_raw(ptr)
935 }
936
937 #[inline(always)]
940 unsafe fn borrow_ref<T>(&self) -> &T {
941 let payload = get_payload(self.0);
942 let ptr = payload_to_ptr(payload) as *const T;
943 &*ptr
944 }
945
946 pub fn view(&self) -> ValueView {
949 if !is_boxed(self.0) {
950 return ValueView::Float(f64::from_bits(self.0));
951 }
952 let tag = get_tag(self.0);
953 match tag {
954 TAG_NIL => ValueView::Nil,
955 TAG_FALSE => ValueView::Bool(false),
956 TAG_TRUE => ValueView::Bool(true),
957 TAG_INT_SMALL => {
958 let payload = get_payload(self.0);
959 let val = if payload & INT_SIGN_BIT != 0 {
960 (payload | !PAYLOAD_MASK) as i64
961 } else {
962 payload as i64
963 };
964 ValueView::Int(val)
965 }
966 TAG_CHAR => {
967 let payload = get_payload(self.0);
968 ValueView::Char(unsafe { char::from_u32_unchecked(payload as u32) })
969 }
970 TAG_SYMBOL => {
971 let payload = get_payload(self.0);
972 let spur: Spur = unsafe { std::mem::transmute(payload as u32) };
973 ValueView::Symbol(spur)
974 }
975 TAG_KEYWORD => {
976 let payload = get_payload(self.0);
977 let spur: Spur = unsafe { std::mem::transmute(payload as u32) };
978 ValueView::Keyword(spur)
979 }
980 TAG_INT_BIG => {
981 let val = unsafe { *self.borrow_ref::<i64>() };
982 ValueView::Int(val)
983 }
984 TAG_STRING => ValueView::String(unsafe { self.get_rc::<String>() }),
985 TAG_LIST => ValueView::List(unsafe { self.get_rc::<Vec<Value>>() }),
986 TAG_VECTOR => ValueView::Vector(unsafe { self.get_rc::<Vec<Value>>() }),
987 TAG_MAP => ValueView::Map(unsafe { self.get_rc::<BTreeMap<Value, Value>>() }),
988 TAG_HASHMAP => {
989 ValueView::HashMap(unsafe { self.get_rc::<hashbrown::HashMap<Value, Value>>() })
990 }
991 TAG_LAMBDA => ValueView::Lambda(unsafe { self.get_rc::<Lambda>() }),
992 TAG_MACRO => ValueView::Macro(unsafe { self.get_rc::<Macro>() }),
993 TAG_NATIVE_FN => ValueView::NativeFn(unsafe { self.get_rc::<NativeFn>() }),
994 TAG_PROMPT => ValueView::Prompt(unsafe { self.get_rc::<Prompt>() }),
995 TAG_MESSAGE => ValueView::Message(unsafe { self.get_rc::<Message>() }),
996 TAG_CONVERSATION => ValueView::Conversation(unsafe { self.get_rc::<Conversation>() }),
997 TAG_TOOL_DEF => ValueView::ToolDef(unsafe { self.get_rc::<ToolDefinition>() }),
998 TAG_AGENT => ValueView::Agent(unsafe { self.get_rc::<Agent>() }),
999 TAG_THUNK => ValueView::Thunk(unsafe { self.get_rc::<Thunk>() }),
1000 TAG_RECORD => ValueView::Record(unsafe { self.get_rc::<Record>() }),
1001 TAG_BYTEVECTOR => ValueView::Bytevector(unsafe { self.get_rc::<Vec<u8>>() }),
1002 TAG_MULTIMETHOD => ValueView::MultiMethod(unsafe { self.get_rc::<MultiMethod>() }),
1003 TAG_STREAM => ValueView::Stream(unsafe { self.get_rc::<StreamBox>() }),
1004 TAG_F64_ARRAY => ValueView::F64Array(unsafe { self.get_rc::<Vec<f64>>() }),
1005 TAG_I64_ARRAY => ValueView::I64Array(unsafe { self.get_rc::<Vec<i64>>() }),
1006 TAG_ASYNC_PROMISE => ValueView::AsyncPromise(unsafe { self.get_rc::<AsyncPromise>() }),
1007 TAG_CHANNEL => ValueView::Channel(unsafe { self.get_rc::<Channel>() }),
1008 _ => unreachable!("invalid NaN-boxed tag: {}", tag),
1009 }
1010 }
1011
1012 pub fn type_name(&self) -> &'static str {
1015 if !is_boxed(self.0) {
1016 return "float";
1017 }
1018 match get_tag(self.0) {
1019 TAG_NIL => "nil",
1020 TAG_FALSE | TAG_TRUE => "bool",
1021 TAG_INT_SMALL | TAG_INT_BIG => "int",
1022 TAG_CHAR => "char",
1023 TAG_SYMBOL => "symbol",
1024 TAG_KEYWORD => "keyword",
1025 TAG_STRING => "string",
1026 TAG_LIST => "list",
1027 TAG_VECTOR => "vector",
1028 TAG_MAP => "map",
1029 TAG_HASHMAP => "hashmap",
1030 TAG_LAMBDA => "lambda",
1031 TAG_MACRO => "macro",
1032 TAG_NATIVE_FN => "native-fn",
1033 TAG_PROMPT => "prompt",
1034 TAG_MESSAGE => "message",
1035 TAG_CONVERSATION => "conversation",
1036 TAG_TOOL_DEF => "tool",
1037 TAG_AGENT => "agent",
1038 TAG_THUNK => "promise",
1039 TAG_RECORD => "record",
1040 TAG_BYTEVECTOR => "bytevector",
1041 TAG_MULTIMETHOD => "multimethod",
1042 TAG_STREAM => "stream",
1043 TAG_F64_ARRAY => "f64-array",
1044 TAG_I64_ARRAY => "i64-array",
1045 TAG_ASYNC_PROMISE => "async-promise",
1046 TAG_CHANNEL => "channel",
1047 _ => "unknown",
1048 }
1049 }
1050
1051 #[inline(always)]
1052 pub fn is_nil(&self) -> bool {
1053 self.0 == Value::NIL.0
1054 }
1055
1056 #[inline(always)]
1057 pub fn is_truthy(&self) -> bool {
1058 self.0 != Value::NIL.0 && self.0 != Value::FALSE.0
1059 }
1060
1061 #[inline(always)]
1062 pub fn is_falsy(&self) -> bool {
1063 !self.is_truthy()
1064 }
1065
1066 #[inline(always)]
1067 pub fn is_bool(&self) -> bool {
1068 self.0 == Value::TRUE.0 || self.0 == Value::FALSE.0
1069 }
1070
1071 #[inline(always)]
1072 pub fn is_int(&self) -> bool {
1073 is_boxed(self.0) && matches!(get_tag(self.0), TAG_INT_SMALL | TAG_INT_BIG)
1074 }
1075
1076 #[inline(always)]
1077 pub fn is_symbol(&self) -> bool {
1078 is_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL
1079 }
1080
1081 #[inline(always)]
1082 pub fn is_keyword(&self) -> bool {
1083 is_boxed(self.0) && get_tag(self.0) == TAG_KEYWORD
1084 }
1085
1086 #[inline(always)]
1087 pub fn is_string(&self) -> bool {
1088 is_boxed(self.0) && get_tag(self.0) == TAG_STRING
1089 }
1090
1091 #[inline(always)]
1092 pub fn is_list(&self) -> bool {
1093 is_boxed(self.0) && get_tag(self.0) == TAG_LIST
1094 }
1095
1096 #[inline(always)]
1097 pub fn is_pair(&self) -> bool {
1098 if let Some(items) = self.as_list() {
1099 !items.is_empty()
1100 } else {
1101 false
1102 }
1103 }
1104
1105 #[inline(always)]
1106 pub fn is_vector(&self) -> bool {
1107 is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR
1108 }
1109
1110 #[inline(always)]
1111 pub fn is_map(&self) -> bool {
1112 is_boxed(self.0) && matches!(get_tag(self.0), TAG_MAP | TAG_HASHMAP)
1113 }
1114
1115 #[inline(always)]
1116 pub fn is_lambda(&self) -> bool {
1117 is_boxed(self.0) && get_tag(self.0) == TAG_LAMBDA
1118 }
1119
1120 #[inline(always)]
1121 pub fn is_native_fn(&self) -> bool {
1122 is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN
1123 }
1124
1125 #[inline(always)]
1126 pub fn is_thunk(&self) -> bool {
1127 is_boxed(self.0) && get_tag(self.0) == TAG_THUNK
1128 }
1129
1130 #[inline(always)]
1131 pub fn is_async_promise(&self) -> bool {
1132 is_boxed(self.0) && get_tag(self.0) == TAG_ASYNC_PROMISE
1133 }
1134 #[inline(always)]
1135 pub fn is_channel(&self) -> bool {
1136 is_boxed(self.0) && get_tag(self.0) == TAG_CHANNEL
1137 }
1138
1139 #[inline(always)]
1140 pub fn is_record(&self) -> bool {
1141 is_boxed(self.0) && get_tag(self.0) == TAG_RECORD
1142 }
1143
1144 #[inline(always)]
1145 pub fn as_int(&self) -> Option<i64> {
1146 if !is_boxed(self.0) {
1147 return None;
1148 }
1149 match get_tag(self.0) {
1150 TAG_INT_SMALL => {
1151 let payload = get_payload(self.0);
1152 let val = if payload & INT_SIGN_BIT != 0 {
1153 (payload | !PAYLOAD_MASK) as i64
1154 } else {
1155 payload as i64
1156 };
1157 Some(val)
1158 }
1159 TAG_INT_BIG => Some(unsafe { *self.borrow_ref::<i64>() }),
1160 _ => None,
1161 }
1162 }
1163
1164 #[inline(always)]
1165 pub fn as_float(&self) -> Option<f64> {
1166 if !is_boxed(self.0) {
1167 return Some(f64::from_bits(self.0));
1168 }
1169 match get_tag(self.0) {
1170 TAG_INT_SMALL => {
1171 let payload = get_payload(self.0);
1172 let val = if payload & INT_SIGN_BIT != 0 {
1173 (payload | !PAYLOAD_MASK) as i64
1174 } else {
1175 payload as i64
1176 };
1177 Some(val as f64)
1178 }
1179 TAG_INT_BIG => Some(unsafe { *self.borrow_ref::<i64>() } as f64),
1180 _ => None,
1181 }
1182 }
1183
1184 #[inline(always)]
1185 pub fn as_bool(&self) -> Option<bool> {
1186 if self.0 == Value::TRUE.0 {
1187 Some(true)
1188 } else if self.0 == Value::FALSE.0 {
1189 Some(false)
1190 } else {
1191 None
1192 }
1193 }
1194
1195 pub fn as_str(&self) -> Option<&str> {
1196 if is_boxed(self.0) && get_tag(self.0) == TAG_STRING {
1197 Some(unsafe { self.borrow_ref::<String>() })
1198 } else {
1199 None
1200 }
1201 }
1202
1203 pub fn as_string_rc(&self) -> Option<Rc<String>> {
1204 if is_boxed(self.0) && get_tag(self.0) == TAG_STRING {
1205 Some(unsafe { self.get_rc::<String>() })
1206 } else {
1207 None
1208 }
1209 }
1210
1211 pub fn as_symbol(&self) -> Option<String> {
1212 self.as_symbol_spur().map(resolve)
1213 }
1214
1215 pub fn as_symbol_spur(&self) -> Option<Spur> {
1216 if is_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL {
1217 let payload = get_payload(self.0);
1218 Some(unsafe { std::mem::transmute::<u32, Spur>(payload as u32) })
1219 } else {
1220 None
1221 }
1222 }
1223
1224 pub fn as_keyword(&self) -> Option<String> {
1225 self.as_keyword_spur().map(resolve)
1226 }
1227
1228 pub fn as_keyword_spur(&self) -> Option<Spur> {
1229 if is_boxed(self.0) && get_tag(self.0) == TAG_KEYWORD {
1230 let payload = get_payload(self.0);
1231 Some(unsafe { std::mem::transmute::<u32, Spur>(payload as u32) })
1232 } else {
1233 None
1234 }
1235 }
1236
1237 pub fn as_char(&self) -> Option<char> {
1238 if is_boxed(self.0) && get_tag(self.0) == TAG_CHAR {
1239 let payload = get_payload(self.0);
1240 char::from_u32(payload as u32)
1241 } else {
1242 None
1243 }
1244 }
1245
1246 pub fn as_list(&self) -> Option<&[Value]> {
1247 if is_boxed(self.0) && get_tag(self.0) == TAG_LIST {
1248 Some(unsafe { self.borrow_ref::<Vec<Value>>() })
1249 } else {
1250 None
1251 }
1252 }
1253
1254 pub fn as_list_rc(&self) -> Option<Rc<Vec<Value>>> {
1255 if is_boxed(self.0) && get_tag(self.0) == TAG_LIST {
1256 Some(unsafe { self.get_rc::<Vec<Value>>() })
1257 } else {
1258 None
1259 }
1260 }
1261
1262 pub fn as_seq(&self) -> Option<&[Value]> {
1264 self.as_list().or_else(|| self.as_vector())
1265 }
1266
1267 pub fn as_vector(&self) -> Option<&[Value]> {
1268 if is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR {
1269 Some(unsafe { self.borrow_ref::<Vec<Value>>() })
1270 } else {
1271 None
1272 }
1273 }
1274
1275 pub fn as_vector_rc(&self) -> Option<Rc<Vec<Value>>> {
1276 if is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR {
1277 Some(unsafe { self.get_rc::<Vec<Value>>() })
1278 } else {
1279 None
1280 }
1281 }
1282
1283 pub fn as_map_rc(&self) -> Option<Rc<BTreeMap<Value, Value>>> {
1284 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
1285 Some(unsafe { self.get_rc::<BTreeMap<Value, Value>>() })
1286 } else {
1287 None
1288 }
1289 }
1290
1291 pub fn as_hashmap_rc(&self) -> Option<Rc<hashbrown::HashMap<Value, Value>>> {
1292 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
1293 Some(unsafe { self.get_rc::<hashbrown::HashMap<Value, Value>>() })
1294 } else {
1295 None
1296 }
1297 }
1298
1299 #[inline(always)]
1301 pub fn as_hashmap_ref(&self) -> Option<&hashbrown::HashMap<Value, Value>> {
1302 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
1303 Some(unsafe { self.borrow_ref::<hashbrown::HashMap<Value, Value>>() })
1304 } else {
1305 None
1306 }
1307 }
1308
1309 #[inline(always)]
1311 pub fn as_map_ref(&self) -> Option<&BTreeMap<Value, Value>> {
1312 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
1313 Some(unsafe { self.borrow_ref::<BTreeMap<Value, Value>>() })
1314 } else {
1315 None
1316 }
1317 }
1318
1319 #[inline(always)]
1323 pub fn with_hashmap_mut_if_unique<R>(
1324 &self,
1325 f: impl FnOnce(&mut hashbrown::HashMap<Value, Value>) -> R,
1326 ) -> Option<R> {
1327 if !is_boxed(self.0) || get_tag(self.0) != TAG_HASHMAP {
1328 return None;
1329 }
1330 let payload = get_payload(self.0);
1331 let ptr = payload_to_ptr(payload) as *const hashbrown::HashMap<Value, Value>;
1332 let rc = std::mem::ManuallyDrop::new(unsafe { Rc::from_raw(ptr) });
1333 if Rc::strong_count(&rc) != 1 {
1334 return None;
1335 }
1336 let ptr_mut = ptr as *mut hashbrown::HashMap<Value, Value>;
1338 Some(f(unsafe { &mut *ptr_mut }))
1339 }
1340
1341 #[inline(always)]
1344 pub fn with_map_mut_if_unique<R>(
1345 &self,
1346 f: impl FnOnce(&mut BTreeMap<Value, Value>) -> R,
1347 ) -> Option<R> {
1348 if !is_boxed(self.0) || get_tag(self.0) != TAG_MAP {
1349 return None;
1350 }
1351 let payload = get_payload(self.0);
1352 let ptr = payload_to_ptr(payload) as *const BTreeMap<Value, Value>;
1353 let rc = std::mem::ManuallyDrop::new(unsafe { Rc::from_raw(ptr) });
1354 if Rc::strong_count(&rc) != 1 {
1355 return None;
1356 }
1357 let ptr_mut = ptr as *mut BTreeMap<Value, Value>;
1358 Some(f(unsafe { &mut *ptr_mut }))
1359 }
1360
1361 pub fn into_hashmap_rc(self) -> Result<Rc<hashbrown::HashMap<Value, Value>>, Value> {
1364 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
1365 let payload = get_payload(self.0);
1366 let ptr = payload_to_ptr(payload) as *const hashbrown::HashMap<Value, Value>;
1367 std::mem::forget(self);
1369 Ok(unsafe { Rc::from_raw(ptr) })
1370 } else {
1371 Err(self)
1372 }
1373 }
1374
1375 pub fn into_map_rc(self) -> Result<Rc<BTreeMap<Value, Value>>, Value> {
1378 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
1379 let payload = get_payload(self.0);
1380 let ptr = payload_to_ptr(payload) as *const BTreeMap<Value, Value>;
1381 std::mem::forget(self);
1382 Ok(unsafe { Rc::from_raw(ptr) })
1383 } else {
1384 Err(self)
1385 }
1386 }
1387
1388 pub fn as_lambda_rc(&self) -> Option<Rc<Lambda>> {
1389 if is_boxed(self.0) && get_tag(self.0) == TAG_LAMBDA {
1390 Some(unsafe { self.get_rc::<Lambda>() })
1391 } else {
1392 None
1393 }
1394 }
1395
1396 pub fn as_macro_rc(&self) -> Option<Rc<Macro>> {
1397 if is_boxed(self.0) && get_tag(self.0) == TAG_MACRO {
1398 Some(unsafe { self.get_rc::<Macro>() })
1399 } else {
1400 None
1401 }
1402 }
1403
1404 pub fn as_native_fn_rc(&self) -> Option<Rc<NativeFn>> {
1405 if is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN {
1406 Some(unsafe { self.get_rc::<NativeFn>() })
1407 } else {
1408 None
1409 }
1410 }
1411
1412 pub fn as_thunk_rc(&self) -> Option<Rc<Thunk>> {
1413 if is_boxed(self.0) && get_tag(self.0) == TAG_THUNK {
1414 Some(unsafe { self.get_rc::<Thunk>() })
1415 } else {
1416 None
1417 }
1418 }
1419
1420 pub fn as_record(&self) -> Option<&Record> {
1421 if is_boxed(self.0) && get_tag(self.0) == TAG_RECORD {
1422 Some(unsafe { self.borrow_ref::<Record>() })
1423 } else {
1424 None
1425 }
1426 }
1427
1428 pub fn as_record_rc(&self) -> Option<Rc<Record>> {
1429 if is_boxed(self.0) && get_tag(self.0) == TAG_RECORD {
1430 Some(unsafe { self.get_rc::<Record>() })
1431 } else {
1432 None
1433 }
1434 }
1435
1436 pub fn as_bytevector(&self) -> Option<&[u8]> {
1437 if is_boxed(self.0) && get_tag(self.0) == TAG_BYTEVECTOR {
1438 Some(unsafe { self.borrow_ref::<Vec<u8>>() })
1439 } else {
1440 None
1441 }
1442 }
1443
1444 pub fn as_bytevector_rc(&self) -> Option<Rc<Vec<u8>>> {
1445 if is_boxed(self.0) && get_tag(self.0) == TAG_BYTEVECTOR {
1446 Some(unsafe { self.get_rc::<Vec<u8>>() })
1447 } else {
1448 None
1449 }
1450 }
1451
1452 pub fn as_f64_array(&self) -> Option<&[f64]> {
1453 if is_boxed(self.0) && get_tag(self.0) == TAG_F64_ARRAY {
1454 Some(unsafe { self.borrow_ref::<Vec<f64>>() })
1455 } else {
1456 None
1457 }
1458 }
1459
1460 pub fn as_f64_array_rc(&self) -> Option<Rc<Vec<f64>>> {
1461 if is_boxed(self.0) && get_tag(self.0) == TAG_F64_ARRAY {
1462 Some(unsafe { self.get_rc::<Vec<f64>>() })
1463 } else {
1464 None
1465 }
1466 }
1467
1468 pub fn as_i64_array(&self) -> Option<&[i64]> {
1469 if is_boxed(self.0) && get_tag(self.0) == TAG_I64_ARRAY {
1470 Some(unsafe { self.borrow_ref::<Vec<i64>>() })
1471 } else {
1472 None
1473 }
1474 }
1475
1476 pub fn as_i64_array_rc(&self) -> Option<Rc<Vec<i64>>> {
1477 if is_boxed(self.0) && get_tag(self.0) == TAG_I64_ARRAY {
1478 Some(unsafe { self.get_rc::<Vec<i64>>() })
1479 } else {
1480 None
1481 }
1482 }
1483
1484 pub fn as_stream(&self) -> Option<&StreamBox> {
1485 if is_boxed(self.0) && get_tag(self.0) == TAG_STREAM {
1486 Some(unsafe { self.borrow_ref::<StreamBox>() })
1487 } else {
1488 None
1489 }
1490 }
1491
1492 pub fn as_stream_rc(&self) -> Option<Rc<StreamBox>> {
1493 if is_boxed(self.0) && get_tag(self.0) == TAG_STREAM {
1494 Some(unsafe { self.get_rc::<StreamBox>() })
1495 } else {
1496 None
1497 }
1498 }
1499
1500 pub fn as_prompt_rc(&self) -> Option<Rc<Prompt>> {
1501 if is_boxed(self.0) && get_tag(self.0) == TAG_PROMPT {
1502 Some(unsafe { self.get_rc::<Prompt>() })
1503 } else {
1504 None
1505 }
1506 }
1507
1508 pub fn as_message_rc(&self) -> Option<Rc<Message>> {
1509 if is_boxed(self.0) && get_tag(self.0) == TAG_MESSAGE {
1510 Some(unsafe { self.get_rc::<Message>() })
1511 } else {
1512 None
1513 }
1514 }
1515
1516 pub fn as_conversation_rc(&self) -> Option<Rc<Conversation>> {
1517 if is_boxed(self.0) && get_tag(self.0) == TAG_CONVERSATION {
1518 Some(unsafe { self.get_rc::<Conversation>() })
1519 } else {
1520 None
1521 }
1522 }
1523
1524 pub fn as_tool_def_rc(&self) -> Option<Rc<ToolDefinition>> {
1525 if is_boxed(self.0) && get_tag(self.0) == TAG_TOOL_DEF {
1526 Some(unsafe { self.get_rc::<ToolDefinition>() })
1527 } else {
1528 None
1529 }
1530 }
1531
1532 pub fn as_agent_rc(&self) -> Option<Rc<Agent>> {
1533 if is_boxed(self.0) && get_tag(self.0) == TAG_AGENT {
1534 Some(unsafe { self.get_rc::<Agent>() })
1535 } else {
1536 None
1537 }
1538 }
1539
1540 pub fn as_multimethod_rc(&self) -> Option<Rc<MultiMethod>> {
1541 if is_boxed(self.0) && get_tag(self.0) == TAG_MULTIMETHOD {
1542 Some(unsafe { self.get_rc::<MultiMethod>() })
1543 } else {
1544 None
1545 }
1546 }
1547}
1548
1549impl Clone for Value {
1552 #[inline(always)]
1553 fn clone(&self) -> Self {
1554 if !is_boxed(self.0) {
1555 return Value(self.0);
1557 }
1558 let tag = get_tag(self.0);
1559 match tag {
1560 TAG_NIL | TAG_FALSE | TAG_TRUE | TAG_INT_SMALL | TAG_CHAR | TAG_SYMBOL
1562 | TAG_KEYWORD => Value(self.0),
1563 _ => {
1565 let payload = get_payload(self.0);
1566 let ptr = payload_to_ptr(payload);
1567 unsafe {
1569 match tag {
1570 TAG_INT_BIG => Rc::increment_strong_count(ptr as *const i64),
1571 TAG_STRING => Rc::increment_strong_count(ptr as *const String),
1572 TAG_LIST | TAG_VECTOR => {
1573 Rc::increment_strong_count(ptr as *const Vec<Value>)
1574 }
1575 TAG_MAP => Rc::increment_strong_count(ptr as *const BTreeMap<Value, Value>),
1576 TAG_HASHMAP => Rc::increment_strong_count(
1577 ptr as *const hashbrown::HashMap<Value, Value>,
1578 ),
1579 TAG_LAMBDA => Rc::increment_strong_count(ptr as *const Lambda),
1580 TAG_MACRO => Rc::increment_strong_count(ptr as *const Macro),
1581 TAG_NATIVE_FN => Rc::increment_strong_count(ptr as *const NativeFn),
1582 TAG_PROMPT => Rc::increment_strong_count(ptr as *const Prompt),
1583 TAG_MESSAGE => Rc::increment_strong_count(ptr as *const Message),
1584 TAG_CONVERSATION => Rc::increment_strong_count(ptr as *const Conversation),
1585 TAG_TOOL_DEF => Rc::increment_strong_count(ptr as *const ToolDefinition),
1586 TAG_AGENT => Rc::increment_strong_count(ptr as *const Agent),
1587 TAG_THUNK => Rc::increment_strong_count(ptr as *const Thunk),
1588 TAG_RECORD => Rc::increment_strong_count(ptr as *const Record),
1589 TAG_BYTEVECTOR => Rc::increment_strong_count(ptr as *const Vec<u8>),
1590 TAG_MULTIMETHOD => Rc::increment_strong_count(ptr as *const MultiMethod),
1591 TAG_STREAM => Rc::increment_strong_count(ptr as *const StreamBox),
1592 TAG_F64_ARRAY => Rc::increment_strong_count(ptr as *const Vec<f64>),
1593 TAG_I64_ARRAY => Rc::increment_strong_count(ptr as *const Vec<i64>),
1594 TAG_ASYNC_PROMISE => Rc::increment_strong_count(ptr as *const AsyncPromise),
1595 TAG_CHANNEL => Rc::increment_strong_count(ptr as *const Channel),
1596 _ => unreachable!("invalid heap tag in clone: {}", tag),
1597 }
1598 }
1599 Value(self.0)
1600 }
1601 }
1602 }
1603}
1604
1605impl Drop for Value {
1608 #[inline(always)]
1609 fn drop(&mut self) {
1610 if !is_boxed(self.0) {
1611 return; }
1613 let tag = get_tag(self.0);
1614 match tag {
1615 TAG_NIL | TAG_FALSE | TAG_TRUE | TAG_INT_SMALL | TAG_CHAR | TAG_SYMBOL
1617 | TAG_KEYWORD => {}
1618 _ => {
1620 let payload = get_payload(self.0);
1621 let ptr = payload_to_ptr(payload);
1622 unsafe {
1623 match tag {
1624 TAG_INT_BIG => drop(Rc::from_raw(ptr as *const i64)),
1625 TAG_STRING => drop(Rc::from_raw(ptr as *const String)),
1626 TAG_LIST | TAG_VECTOR => drop(Rc::from_raw(ptr as *const Vec<Value>)),
1627 TAG_MAP => drop(Rc::from_raw(ptr as *const BTreeMap<Value, Value>)),
1628 TAG_HASHMAP => {
1629 drop(Rc::from_raw(ptr as *const hashbrown::HashMap<Value, Value>))
1630 }
1631 TAG_LAMBDA => drop(Rc::from_raw(ptr as *const Lambda)),
1632 TAG_MACRO => drop(Rc::from_raw(ptr as *const Macro)),
1633 TAG_NATIVE_FN => drop(Rc::from_raw(ptr as *const NativeFn)),
1634 TAG_PROMPT => drop(Rc::from_raw(ptr as *const Prompt)),
1635 TAG_MESSAGE => drop(Rc::from_raw(ptr as *const Message)),
1636 TAG_CONVERSATION => drop(Rc::from_raw(ptr as *const Conversation)),
1637 TAG_TOOL_DEF => drop(Rc::from_raw(ptr as *const ToolDefinition)),
1638 TAG_AGENT => drop(Rc::from_raw(ptr as *const Agent)),
1639 TAG_THUNK => drop(Rc::from_raw(ptr as *const Thunk)),
1640 TAG_RECORD => drop(Rc::from_raw(ptr as *const Record)),
1641 TAG_BYTEVECTOR => drop(Rc::from_raw(ptr as *const Vec<u8>)),
1642 TAG_MULTIMETHOD => drop(Rc::from_raw(ptr as *const MultiMethod)),
1643 TAG_STREAM => drop(Rc::from_raw(ptr as *const StreamBox)),
1644 TAG_F64_ARRAY => drop(Rc::from_raw(ptr as *const Vec<f64>)),
1645 TAG_I64_ARRAY => drop(Rc::from_raw(ptr as *const Vec<i64>)),
1646 TAG_ASYNC_PROMISE => drop(Rc::from_raw(ptr as *const AsyncPromise)),
1647 TAG_CHANNEL => drop(Rc::from_raw(ptr as *const Channel)),
1648 _ => {} }
1650 }
1651 }
1652 }
1653 }
1654}
1655
1656impl PartialEq for Value {
1659 fn eq(&self, other: &Self) -> bool {
1660 if self.0 == other.0 {
1662 if !is_boxed(self.0) {
1666 let f = f64::from_bits(self.0);
1667 if f.is_nan() {
1669 return false;
1670 }
1671 return true;
1672 }
1673 return true;
1674 }
1675 match (self.view(), other.view()) {
1677 (ValueView::Nil, ValueView::Nil) => true,
1678 (ValueView::Bool(a), ValueView::Bool(b)) => a == b,
1679 (ValueView::Int(a), ValueView::Int(b)) => a == b,
1680 (ValueView::Float(a), ValueView::Float(b)) => a == b,
1681 (ValueView::String(a), ValueView::String(b)) => a == b,
1682 (ValueView::Symbol(a), ValueView::Symbol(b)) => a == b,
1683 (ValueView::Keyword(a), ValueView::Keyword(b)) => a == b,
1684 (ValueView::Char(a), ValueView::Char(b)) => a == b,
1685 (ValueView::List(a), ValueView::List(b)) => a == b,
1686 (ValueView::Vector(a), ValueView::Vector(b)) => a == b,
1687 (ValueView::Map(a), ValueView::Map(b)) => a == b,
1688 (ValueView::HashMap(a), ValueView::HashMap(b)) => a == b,
1689 (ValueView::Record(a), ValueView::Record(b)) => {
1690 a.type_tag == b.type_tag && a.fields == b.fields
1691 }
1692 (ValueView::Bytevector(a), ValueView::Bytevector(b)) => a == b,
1693 (ValueView::F64Array(a), ValueView::F64Array(b)) => {
1694 a.len() == b.len()
1695 && a.iter()
1696 .zip(b.iter())
1697 .all(|(x, y)| x.to_bits() == y.to_bits())
1698 }
1699 (ValueView::I64Array(a), ValueView::I64Array(b)) => a == b,
1700 (ValueView::Stream(a), ValueView::Stream(b)) => Rc::ptr_eq(&a, &b),
1701 (ValueView::AsyncPromise(a), ValueView::AsyncPromise(b)) => Rc::ptr_eq(&a, &b),
1702 (ValueView::Channel(a), ValueView::Channel(b)) => Rc::ptr_eq(&a, &b),
1703 _ => false,
1704 }
1705 }
1706}
1707
1708impl Eq for Value {}
1709
1710impl Hash for Value {
1713 fn hash<H: Hasher>(&self, state: &mut H) {
1714 match self.view() {
1715 ValueView::Nil => 0u8.hash(state),
1716 ValueView::Bool(b) => {
1717 1u8.hash(state);
1718 b.hash(state);
1719 }
1720 ValueView::Int(n) => {
1721 2u8.hash(state);
1722 n.hash(state);
1723 }
1724 ValueView::Float(f) => {
1725 3u8.hash(state);
1726 let bits = if f == 0.0 { 0u64 } else { f.to_bits() };
1728 bits.hash(state);
1729 }
1730 ValueView::String(s) => {
1731 4u8.hash(state);
1732 s.hash(state);
1733 }
1734 ValueView::Symbol(s) => {
1735 5u8.hash(state);
1736 s.hash(state);
1737 }
1738 ValueView::Keyword(s) => {
1739 6u8.hash(state);
1740 s.hash(state);
1741 }
1742 ValueView::Char(c) => {
1743 7u8.hash(state);
1744 c.hash(state);
1745 }
1746 ValueView::List(l) => {
1747 8u8.hash(state);
1748 l.hash(state);
1749 }
1750 ValueView::Vector(v) => {
1751 9u8.hash(state);
1752 v.hash(state);
1753 }
1754 ValueView::Record(r) => {
1755 10u8.hash(state);
1756 r.type_tag.hash(state);
1757 r.fields.hash(state);
1758 }
1759 ValueView::Bytevector(bv) => {
1760 11u8.hash(state);
1761 bv.hash(state);
1762 }
1763 ValueView::F64Array(arr) => {
1764 26u8.hash(state);
1765 for v in arr.iter() {
1766 v.to_bits().hash(state);
1767 }
1768 }
1769 ValueView::I64Array(arr) => {
1770 27u8.hash(state);
1771 arr.hash(state);
1772 }
1773 ValueView::Stream(s) => {
1774 25u8.hash(state);
1775 (Rc::as_ptr(&s) as usize).hash(state);
1776 }
1777 ValueView::AsyncPromise(p) => {
1778 28u8.hash(state);
1779 (Rc::as_ptr(&p) as usize).hash(state);
1780 }
1781 ValueView::Channel(c) => {
1782 29u8.hash(state);
1783 (Rc::as_ptr(&c) as usize).hash(state);
1784 }
1785 _ => {}
1786 }
1787 }
1788}
1789
1790impl PartialOrd for Value {
1793 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1794 Some(self.cmp(other))
1795 }
1796}
1797
1798impl Ord for Value {
1799 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1800 use std::cmp::Ordering;
1801 fn type_order(v: &Value) -> u8 {
1802 match v.view() {
1803 ValueView::Nil => 0,
1804 ValueView::Bool(_) => 1,
1805 ValueView::Int(_) => 2,
1806 ValueView::Float(_) => 3,
1807 ValueView::Char(_) => 4,
1808 ValueView::String(_) => 5,
1809 ValueView::Symbol(_) => 6,
1810 ValueView::Keyword(_) => 7,
1811 ValueView::List(_) => 8,
1812 ValueView::Vector(_) => 9,
1813 ValueView::Map(_) => 10,
1814 ValueView::HashMap(_) => 11,
1815 ValueView::Record(_) => 12,
1816 ValueView::Bytevector(_) => 13,
1817 ValueView::F64Array(_) => 14,
1818 ValueView::I64Array(_) => 15,
1819 ValueView::Stream(_) => 16,
1820 _ => 17,
1821 }
1822 }
1823 match (self.view(), other.view()) {
1824 (ValueView::Nil, ValueView::Nil) => Ordering::Equal,
1825 (ValueView::Bool(a), ValueView::Bool(b)) => a.cmp(&b),
1826 (ValueView::Int(a), ValueView::Int(b)) => a.cmp(&b),
1827 (ValueView::Float(a), ValueView::Float(b)) => a.total_cmp(&b),
1828 (ValueView::String(a), ValueView::String(b)) => a.cmp(&b),
1829 (ValueView::Symbol(a), ValueView::Symbol(b)) => compare_spurs(a, b),
1830 (ValueView::Keyword(a), ValueView::Keyword(b)) => compare_spurs(a, b),
1831 (ValueView::Char(a), ValueView::Char(b)) => a.cmp(&b),
1832 (ValueView::List(a), ValueView::List(b)) => a.cmp(&b),
1833 (ValueView::Vector(a), ValueView::Vector(b)) => a.cmp(&b),
1834 (ValueView::Record(a), ValueView::Record(b)) => {
1835 compare_spurs(a.type_tag, b.type_tag).then_with(|| a.fields.cmp(&b.fields))
1836 }
1837 (ValueView::Bytevector(a), ValueView::Bytevector(b)) => a.cmp(&b),
1838 (ValueView::I64Array(a), ValueView::I64Array(b)) => a.cmp(&b),
1839 (ValueView::F64Array(a), ValueView::F64Array(b)) => a
1840 .iter()
1841 .zip(b.iter())
1842 .map(|(x, y)| x.total_cmp(y))
1843 .find(|o| *o != std::cmp::Ordering::Equal)
1844 .unwrap_or_else(|| a.len().cmp(&b.len())),
1845 _ => type_order(self).cmp(&type_order(other)),
1846 }
1847 }
1848}
1849
1850fn truncate(s: &str, max: usize) -> String {
1853 let mut iter = s.chars();
1854 let prefix: String = iter.by_ref().take(max).collect();
1855 if iter.next().is_none() {
1856 prefix
1857 } else {
1858 format!("{prefix}...")
1859 }
1860}
1861
1862impl fmt::Display for Value {
1863 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1864 match self.view() {
1865 ValueView::Nil => write!(f, "nil"),
1866 ValueView::Bool(true) => write!(f, "#t"),
1867 ValueView::Bool(false) => write!(f, "#f"),
1868 ValueView::Int(n) => write!(f, "{n}"),
1869 ValueView::Float(n) => {
1870 if n.fract() == 0.0 {
1871 write!(f, "{n:.1}")
1872 } else {
1873 write!(f, "{n}")
1874 }
1875 }
1876 ValueView::String(s) => write!(f, "\"{s}\""),
1877 ValueView::Symbol(s) => with_resolved(s, |name| write!(f, "{name}")),
1878 ValueView::Keyword(s) => with_resolved(s, |name| write!(f, ":{name}")),
1879 ValueView::Char(c) => match c {
1880 ' ' => write!(f, "#\\space"),
1881 '\n' => write!(f, "#\\newline"),
1882 '\t' => write!(f, "#\\tab"),
1883 '\r' => write!(f, "#\\return"),
1884 '\0' => write!(f, "#\\nul"),
1885 _ => write!(f, "#\\{c}"),
1886 },
1887 ValueView::List(items) => {
1888 write!(f, "(")?;
1889 for (i, item) in items.iter().enumerate() {
1890 if i > 0 {
1891 write!(f, " ")?;
1892 }
1893 write!(f, "{item}")?;
1894 }
1895 write!(f, ")")
1896 }
1897 ValueView::Vector(items) => {
1898 write!(f, "[")?;
1899 for (i, item) in items.iter().enumerate() {
1900 if i > 0 {
1901 write!(f, " ")?;
1902 }
1903 write!(f, "{item}")?;
1904 }
1905 write!(f, "]")
1906 }
1907 ValueView::Map(map) => {
1908 write!(f, "{{")?;
1909 for (i, (k, v)) in map.iter().enumerate() {
1910 if i > 0 {
1911 write!(f, " ")?;
1912 }
1913 write!(f, "{k} {v}")?;
1914 }
1915 write!(f, "}}")
1916 }
1917 ValueView::HashMap(map) => {
1918 let mut entries: Vec<_> = map.iter().collect();
1919 entries.sort_by_key(|(k1, _)| *k1);
1920 write!(f, "{{")?;
1921 for (i, (k, v)) in entries.iter().enumerate() {
1922 if i > 0 {
1923 write!(f, " ")?;
1924 }
1925 write!(f, "{k} {v}")?;
1926 }
1927 write!(f, "}}")
1928 }
1929 ValueView::Lambda(l) => {
1930 if let Some(name) = &l.name {
1931 with_resolved(*name, |n| write!(f, "<lambda {n}>"))
1932 } else {
1933 write!(f, "<lambda>")
1934 }
1935 }
1936 ValueView::Macro(m) => with_resolved(m.name, |n| write!(f, "<macro {n}>")),
1937 ValueView::NativeFn(n) => write!(f, "<native-fn {}>", n.name),
1938 ValueView::Prompt(p) => write!(f, "<prompt {} messages>", p.messages.len()),
1939 ValueView::Message(m) => {
1940 write!(f, "<message {} \"{}\">", m.role, truncate(&m.content, 40))
1941 }
1942 ValueView::Conversation(c) => {
1943 write!(f, "<conversation {} messages>", c.messages.len())
1944 }
1945 ValueView::ToolDef(t) => write!(f, "<tool {}>", t.name),
1946 ValueView::Agent(a) => write!(f, "<agent {}>", a.name),
1947 ValueView::Thunk(t) => {
1948 if t.forced.borrow().is_some() {
1949 write!(f, "<promise (forced)>")
1950 } else {
1951 write!(f, "<promise>")
1952 }
1953 }
1954 ValueView::Record(r) => {
1955 with_resolved(r.type_tag, |tag| write!(f, "#<record {tag}"))?;
1956 for field in &r.fields {
1957 write!(f, " {field}")?;
1958 }
1959 write!(f, ">")
1960 }
1961 ValueView::Bytevector(bv) => {
1962 write!(f, "#u8(")?;
1963 for (i, byte) in bv.iter().enumerate() {
1964 if i > 0 {
1965 write!(f, " ")?;
1966 }
1967 write!(f, "{byte}")?;
1968 }
1969 write!(f, ")")
1970 }
1971 ValueView::F64Array(arr) => {
1972 write!(f, "#f64(")?;
1973 for (i, v) in arr.iter().enumerate() {
1974 if i > 0 {
1975 write!(f, " ")?;
1976 }
1977 write!(f, "{v}")?;
1978 }
1979 write!(f, ")")
1980 }
1981 ValueView::I64Array(arr) => {
1982 write!(f, "#i64(")?;
1983 for (i, v) in arr.iter().enumerate() {
1984 if i > 0 {
1985 write!(f, " ")?;
1986 }
1987 write!(f, "{v}")?;
1988 }
1989 write!(f, ")")
1990 }
1991 ValueView::MultiMethod(m) => with_resolved(m.name, |n| write!(f, "<multimethod {n}>")),
1992 ValueView::Stream(s) => write!(f, "<stream:{}>", s.stream_type()),
1993 ValueView::AsyncPromise(p) => match &*p.state.borrow() {
1994 PromiseState::Pending => write!(f, "<async-promise pending>"),
1995 PromiseState::Resolved(v) => write!(f, "<async-promise resolved: {v}>"),
1996 PromiseState::Rejected(e) => write!(f, "<async-promise rejected: {e}>"),
1997 },
1998 ValueView::Channel(c) => {
1999 let len = c.buffer.borrow().len();
2000 if c.closed.get() {
2001 write!(f, "<channel {len}/{} closed>", c.capacity)
2002 } else {
2003 write!(f, "<channel {len}/{}>", c.capacity)
2004 }
2005 }
2006 }
2007 }
2008}
2009
2010pub fn pretty_print(value: &Value, max_width: usize) -> String {
2016 let compact = format!("{value}");
2017 if compact.len() <= max_width {
2018 return compact;
2019 }
2020 let mut buf = String::new();
2021 pp_value(value, 0, max_width, &mut buf);
2022 buf
2023}
2024
2025fn pp_value(value: &Value, indent: usize, max_width: usize, buf: &mut String) {
2029 let compact = format!("{value}");
2030 let remaining = max_width.saturating_sub(indent);
2031 if compact.len() <= remaining {
2032 buf.push_str(&compact);
2033 return;
2034 }
2035
2036 match value.view() {
2037 ValueView::List(items) => {
2038 pp_seq(items.iter(), '(', ')', indent, max_width, buf);
2039 }
2040 ValueView::Vector(items) => {
2041 pp_seq(items.iter(), '[', ']', indent, max_width, buf);
2042 }
2043 ValueView::Map(map) => {
2044 pp_map(
2045 map.iter().map(|(k, v)| (k.clone(), v.clone())),
2046 indent,
2047 max_width,
2048 buf,
2049 );
2050 }
2051 ValueView::HashMap(map) => {
2052 let mut entries: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
2053 entries.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
2054 pp_map(entries.into_iter(), indent, max_width, buf);
2055 }
2056 _ => buf.push_str(&compact),
2057 }
2058}
2059
2060fn pp_seq<'a>(
2062 items: impl Iterator<Item = &'a Value>,
2063 open: char,
2064 close: char,
2065 indent: usize,
2066 max_width: usize,
2067 buf: &mut String,
2068) {
2069 buf.push(open);
2070 let child_indent = indent + 1;
2071 let pad = " ".repeat(child_indent);
2072 for (i, item) in items.enumerate() {
2073 if i > 0 {
2074 buf.push('\n');
2075 buf.push_str(&pad);
2076 }
2077 pp_value(item, child_indent, max_width, buf);
2078 }
2079 buf.push(close);
2080}
2081
2082fn pp_map(
2084 entries: impl Iterator<Item = (Value, Value)>,
2085 indent: usize,
2086 max_width: usize,
2087 buf: &mut String,
2088) {
2089 buf.push('{');
2090 let child_indent = indent + 1;
2091 let pad = " ".repeat(child_indent);
2092 for (i, (k, v)) in entries.enumerate() {
2093 if i > 0 {
2094 buf.push('\n');
2095 buf.push_str(&pad);
2096 }
2097 let key_str = format!("{k}");
2099 buf.push_str(&key_str);
2100
2101 let inline_indent = child_indent + key_str.len() + 1;
2103 let compact_val = format!("{v}");
2104 let remaining = max_width.saturating_sub(inline_indent);
2105
2106 if compact_val.len() <= remaining {
2107 buf.push(' ');
2109 buf.push_str(&compact_val);
2110 } else if is_compound(&v) {
2111 let nested_indent = child_indent + 2;
2113 let nested_pad = " ".repeat(nested_indent);
2114 buf.push('\n');
2115 buf.push_str(&nested_pad);
2116 pp_value(&v, nested_indent, max_width, buf);
2117 } else {
2118 buf.push(' ');
2120 buf.push_str(&compact_val);
2121 }
2122 }
2123 buf.push('}');
2124}
2125
2126fn is_compound(value: &Value) -> bool {
2128 matches!(
2129 value.view(),
2130 ValueView::List(_) | ValueView::Vector(_) | ValueView::Map(_) | ValueView::HashMap(_)
2131 )
2132}
2133
2134impl fmt::Debug for Value {
2137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2138 match self.view() {
2139 ValueView::Nil => write!(f, "Nil"),
2140 ValueView::Bool(b) => write!(f, "Bool({b})"),
2141 ValueView::Int(n) => write!(f, "Int({n})"),
2142 ValueView::Float(n) => write!(f, "Float({n})"),
2143 ValueView::String(s) => write!(f, "String({:?})", &**s),
2144 ValueView::Symbol(s) => write!(f, "Symbol({})", resolve(s)),
2145 ValueView::Keyword(s) => write!(f, "Keyword({})", resolve(s)),
2146 ValueView::Char(c) => write!(f, "Char({c:?})"),
2147 ValueView::List(items) => write!(f, "List({items:?})"),
2148 ValueView::Vector(items) => write!(f, "Vector({items:?})"),
2149 ValueView::Map(map) => write!(f, "Map({map:?})"),
2150 ValueView::HashMap(map) => write!(f, "HashMap({map:?})"),
2151 ValueView::Lambda(l) => write!(f, "{l:?}"),
2152 ValueView::Macro(m) => write!(f, "{m:?}"),
2153 ValueView::NativeFn(n) => write!(f, "{n:?}"),
2154 ValueView::Prompt(p) => write!(f, "{p:?}"),
2155 ValueView::Message(m) => write!(f, "{m:?}"),
2156 ValueView::Conversation(c) => write!(f, "{c:?}"),
2157 ValueView::ToolDef(t) => write!(f, "{t:?}"),
2158 ValueView::Agent(a) => write!(f, "{a:?}"),
2159 ValueView::Thunk(t) => write!(f, "{t:?}"),
2160 ValueView::Record(r) => write!(f, "{r:?}"),
2161 ValueView::Bytevector(bv) => write!(f, "Bytevector({bv:?})"),
2162 ValueView::F64Array(arr) => write!(f, "F64Array({arr:?})"),
2163 ValueView::I64Array(arr) => write!(f, "I64Array({arr:?})"),
2164 ValueView::MultiMethod(m) => write!(f, "{m:?}"),
2165 ValueView::Stream(s) => write!(f, "Stream({:?})", s.stream_type()),
2166 ValueView::AsyncPromise(p) => write!(f, "{p:?}"),
2167 ValueView::Channel(c) => write!(f, "{c:?}"),
2168 }
2169 }
2170}
2171
2172#[derive(Debug, Clone)]
2176pub struct Env {
2177 pub bindings: Rc<RefCell<SpurMap<Spur, Value>>>,
2178 pub parent: Option<Rc<Env>>,
2179 pub version: Cell<u64>,
2180}
2181
2182impl Env {
2183 pub fn new() -> Self {
2184 Env {
2185 bindings: Rc::new(RefCell::new(SpurMap::new())),
2186 parent: None,
2187 version: Cell::new(0),
2188 }
2189 }
2190
2191 pub fn with_parent(parent: Rc<Env>) -> Self {
2192 Env {
2193 bindings: Rc::new(RefCell::new(SpurMap::new())),
2194 parent: Some(parent),
2195 version: Cell::new(0),
2196 }
2197 }
2198
2199 fn bump_version(&self) {
2200 self.version.set(self.version.get().wrapping_add(1));
2201 }
2202
2203 pub fn get(&self, name: Spur) -> Option<Value> {
2204 if let Some(val) = self.bindings.borrow().get(&name) {
2205 Some(val.clone())
2206 } else if let Some(parent) = &self.parent {
2207 parent.get(name)
2208 } else {
2209 None
2210 }
2211 }
2212
2213 pub fn get_str(&self, name: &str) -> Option<Value> {
2214 self.get(intern(name))
2215 }
2216
2217 pub fn set(&self, name: Spur, val: Value) {
2218 self.bindings.borrow_mut().insert(name, val);
2219 self.bump_version();
2220 }
2221
2222 pub fn set_str(&self, name: &str, val: Value) {
2223 self.set(intern(name), val);
2224 }
2225
2226 pub fn update(&self, name: Spur, val: Value) {
2228 let mut bindings = self.bindings.borrow_mut();
2229 if let Some(entry) = bindings.get_mut(&name) {
2230 *entry = val;
2231 } else {
2232 bindings.insert(name, val);
2233 }
2234 drop(bindings);
2235 self.bump_version();
2236 }
2237
2238 pub fn take(&self, name: Spur) -> Option<Value> {
2240 let result = self.bindings.borrow_mut().remove(&name);
2241 if result.is_some() {
2242 self.bump_version();
2243 }
2244 result
2245 }
2246
2247 pub fn take_anywhere(&self, name: Spur) -> Option<Value> {
2249 if let Some(val) = self.bindings.borrow_mut().remove(&name) {
2250 self.bump_version();
2251 Some(val)
2252 } else if let Some(parent) = &self.parent {
2253 parent.take_anywhere(name)
2254 } else {
2255 None
2256 }
2257 }
2258
2259 pub fn set_existing(&self, name: Spur, val: Value) -> bool {
2261 let mut bindings = self.bindings.borrow_mut();
2262 if let Some(entry) = bindings.get_mut(&name) {
2263 *entry = val;
2264 drop(bindings);
2265 self.bump_version();
2266 true
2267 } else {
2268 drop(bindings);
2269 if let Some(parent) = &self.parent {
2270 parent.set_existing(name, val)
2271 } else {
2272 false
2273 }
2274 }
2275 }
2276
2277 pub fn all_names(&self) -> Vec<Spur> {
2279 let mut names: Vec<Spur> = self.bindings.borrow().keys().copied().collect();
2280 if let Some(parent) = &self.parent {
2281 names.extend(parent.all_names());
2282 }
2283 names.sort_unstable();
2284 names.dedup();
2285 names
2286 }
2287
2288 pub fn iter_bindings(&self, mut f: impl FnMut(Spur, &Value)) {
2290 let bindings = self.bindings.borrow();
2291 for (&spur, value) in bindings.iter() {
2292 f(spur, value);
2293 }
2294 }
2295
2296 pub fn get_local(&self, name: Spur) -> Option<Value> {
2298 self.bindings.borrow().get(&name).cloned()
2299 }
2300
2301 pub fn replace_bindings(&self, new_bindings: impl IntoIterator<Item = (Spur, Value)>) {
2304 let mut bindings = self.bindings.borrow_mut();
2305 bindings.clear();
2306 for (spur, value) in new_bindings {
2307 bindings.insert(spur, value);
2308 }
2309 drop(bindings);
2310 self.bump_version();
2311 }
2312}
2313
2314impl Default for Env {
2315 fn default() -> Self {
2316 Self::new()
2317 }
2318}
2319
2320#[cfg(test)]
2323mod tests {
2324 use super::*;
2325
2326 #[test]
2327 fn test_size_of_value() {
2328 assert_eq!(std::mem::size_of::<Value>(), 8);
2329 }
2330
2331 #[test]
2332 fn test_nil() {
2333 let v = Value::nil();
2334 assert!(v.is_nil());
2335 assert!(!v.is_truthy());
2336 assert_eq!(v.type_name(), "nil");
2337 assert_eq!(format!("{v}"), "nil");
2338 }
2339
2340 #[test]
2341 fn test_bool() {
2342 let t = Value::bool(true);
2343 let f = Value::bool(false);
2344 assert!(t.is_truthy());
2345 assert!(!f.is_truthy());
2346 assert_eq!(t.as_bool(), Some(true));
2347 assert_eq!(f.as_bool(), Some(false));
2348 assert_eq!(format!("{t}"), "#t");
2349 assert_eq!(format!("{f}"), "#f");
2350 }
2351
2352 #[test]
2353 fn test_small_int() {
2354 let v = Value::int(42);
2355 assert_eq!(v.as_int(), Some(42));
2356 assert_eq!(v.type_name(), "int");
2357 assert_eq!(format!("{v}"), "42");
2358
2359 let neg = Value::int(-100);
2360 assert_eq!(neg.as_int(), Some(-100));
2361 assert_eq!(format!("{neg}"), "-100");
2362
2363 let zero = Value::int(0);
2364 assert_eq!(zero.as_int(), Some(0));
2365 }
2366
2367 #[test]
2368 fn test_small_int_boundaries() {
2369 let max = Value::int(SMALL_INT_MAX);
2370 assert_eq!(max.as_int(), Some(SMALL_INT_MAX));
2371
2372 let min = Value::int(SMALL_INT_MIN);
2373 assert_eq!(min.as_int(), Some(SMALL_INT_MIN));
2374 }
2375
2376 #[test]
2377 fn test_big_int() {
2378 let big = Value::int(i64::MAX);
2379 assert_eq!(big.as_int(), Some(i64::MAX));
2380 assert_eq!(big.type_name(), "int");
2381
2382 let big_neg = Value::int(i64::MIN);
2383 assert_eq!(big_neg.as_int(), Some(i64::MIN));
2384
2385 let just_over = Value::int(SMALL_INT_MAX + 1);
2387 assert_eq!(just_over.as_int(), Some(SMALL_INT_MAX + 1));
2388 }
2389
2390 #[test]
2391 fn test_float() {
2392 let v = Value::float(3.14);
2393 assert_eq!(v.as_float(), Some(3.14));
2394 assert_eq!(v.type_name(), "float");
2395
2396 let neg = Value::float(-0.5);
2397 assert_eq!(neg.as_float(), Some(-0.5));
2398
2399 let inf = Value::float(f64::INFINITY);
2400 assert_eq!(inf.as_float(), Some(f64::INFINITY));
2401
2402 let neg_inf = Value::float(f64::NEG_INFINITY);
2403 assert_eq!(neg_inf.as_float(), Some(f64::NEG_INFINITY));
2404 }
2405
2406 #[test]
2407 fn test_float_nan() {
2408 let nan = Value::float(f64::NAN);
2409 let f = nan.as_float().unwrap();
2410 assert!(f.is_nan());
2411 }
2412
2413 #[test]
2414 fn test_string() {
2415 let v = Value::string("hello");
2416 assert_eq!(v.as_str(), Some("hello"));
2417 assert_eq!(v.type_name(), "string");
2418 assert_eq!(format!("{v}"), "\"hello\"");
2419 }
2420
2421 #[test]
2422 fn test_symbol() {
2423 let v = Value::symbol("foo");
2424 assert!(v.as_symbol_spur().is_some());
2425 assert_eq!(v.as_symbol(), Some("foo".to_string()));
2426 assert_eq!(v.type_name(), "symbol");
2427 assert_eq!(format!("{v}"), "foo");
2428 }
2429
2430 #[test]
2431 fn test_keyword() {
2432 let v = Value::keyword("bar");
2433 assert!(v.as_keyword_spur().is_some());
2434 assert_eq!(v.as_keyword(), Some("bar".to_string()));
2435 assert_eq!(v.type_name(), "keyword");
2436 assert_eq!(format!("{v}"), ":bar");
2437 }
2438
2439 #[test]
2440 fn test_char() {
2441 let v = Value::char('λ');
2442 assert_eq!(v.as_char(), Some('λ'));
2443 assert_eq!(v.type_name(), "char");
2444 }
2445
2446 #[test]
2447 fn test_list() {
2448 let v = Value::list(vec![Value::int(1), Value::int(2), Value::int(3)]);
2449 assert_eq!(v.as_list().unwrap().len(), 3);
2450 assert_eq!(v.type_name(), "list");
2451 assert_eq!(format!("{v}"), "(1 2 3)");
2452 }
2453
2454 #[test]
2455 fn test_clone_immediate() {
2456 let v = Value::int(42);
2457 let v2 = v.clone();
2458 assert_eq!(v.as_int(), v2.as_int());
2459 }
2460
2461 #[test]
2462 fn test_clone_heap() {
2463 let v = Value::string("hello");
2464 let v2 = v.clone();
2465 assert_eq!(v.as_str(), v2.as_str());
2466 assert_eq!(format!("{v}"), format!("{v2}"));
2468 }
2469
2470 #[test]
2471 fn test_equality() {
2472 assert_eq!(Value::int(42), Value::int(42));
2473 assert_ne!(Value::int(42), Value::int(43));
2474 assert_eq!(Value::nil(), Value::nil());
2475 assert_eq!(Value::bool(true), Value::bool(true));
2476 assert_ne!(Value::bool(true), Value::bool(false));
2477 assert_eq!(Value::string("a"), Value::string("a"));
2478 assert_ne!(Value::string("a"), Value::string("b"));
2479 assert_eq!(Value::symbol("x"), Value::symbol("x"));
2480 }
2481
2482 #[test]
2483 fn test_big_int_equality() {
2484 assert_eq!(Value::int(i64::MAX), Value::int(i64::MAX));
2485 assert_ne!(Value::int(i64::MAX), Value::int(i64::MIN));
2486 }
2487
2488 #[test]
2489 fn test_view_pattern_matching() {
2490 let v = Value::int(42);
2491 match v.view() {
2492 ValueView::Int(n) => assert_eq!(n, 42),
2493 _ => panic!("expected int"),
2494 }
2495
2496 let v = Value::string("hello");
2497 match v.view() {
2498 ValueView::String(s) => assert_eq!(&**s, "hello"),
2499 _ => panic!("expected string"),
2500 }
2501 }
2502
2503 #[test]
2504 fn test_env() {
2505 let env = Env::new();
2506 env.set_str("x", Value::int(42));
2507 assert_eq!(env.get_str("x"), Some(Value::int(42)));
2508 }
2509
2510 #[test]
2511 fn test_native_fn_simple() {
2512 let f = NativeFn::simple("add1", |args| Ok(args[0].clone()));
2513 let ctx = EvalContext::new();
2514 assert!((f.func)(&ctx, &[Value::int(42)]).is_ok());
2515 }
2516
2517 #[test]
2518 fn test_native_fn_with_ctx() {
2519 let f = NativeFn::with_ctx("get-depth", |ctx, _args| {
2520 Ok(Value::int(ctx.eval_depth.get() as i64))
2521 });
2522 let ctx = EvalContext::new();
2523 assert_eq!((f.func)(&ctx, &[]).unwrap(), Value::int(0));
2524 }
2525
2526 #[test]
2527 fn test_drop_doesnt_leak() {
2528 for _ in 0..10000 {
2530 let _ = Value::string("test");
2531 let _ = Value::list(vec![Value::int(1), Value::int(2)]);
2532 let _ = Value::int(i64::MAX); }
2534 }
2535
2536 #[test]
2537 fn test_is_truthy() {
2538 assert!(!Value::nil().is_truthy());
2539 assert!(!Value::bool(false).is_truthy());
2540 assert!(Value::bool(true).is_truthy());
2541 assert!(Value::int(0).is_truthy());
2542 assert!(Value::int(1).is_truthy());
2543 assert!(Value::string("").is_truthy());
2544 assert!(Value::list(vec![]).is_truthy());
2545 }
2546
2547 #[test]
2548 fn test_as_float_from_int() {
2549 assert_eq!(Value::int(42).as_float(), Some(42.0));
2550 assert_eq!(Value::float(3.14).as_float(), Some(3.14));
2551 }
2552
2553 #[test]
2554 fn test_next_gensym_unique() {
2555 let a = next_gensym("x");
2556 let b = next_gensym("x");
2557 let c = next_gensym("y");
2558 assert_ne!(a, b);
2559 assert_ne!(a, c);
2560 assert_ne!(b, c);
2561 assert!(a.starts_with("x__"));
2562 assert!(b.starts_with("x__"));
2563 assert!(c.starts_with("y__"));
2564 }
2565
2566 #[test]
2567 fn test_next_gensym_counter_does_not_panic_near_max() {
2568 GENSYM_COUNTER.with(|c| c.set(u64::MAX - 1));
2570 let a = next_gensym("z");
2571 assert!(a.contains(&(u64::MAX - 1).to_string()));
2572 let b = next_gensym("z");
2574 assert!(b.contains(&u64::MAX.to_string()));
2575 let c = next_gensym("z");
2577 assert!(c.contains("__0"));
2578 }
2579
2580 #[derive(Debug)]
2583 struct TestStream {
2584 data: RefCell<Vec<u8>>,
2585 readable: bool,
2586 writable: bool,
2587 }
2588
2589 impl TestStream {
2590 fn new(readable: bool, writable: bool) -> Self {
2591 TestStream {
2592 data: RefCell::new(Vec::new()),
2593 readable,
2594 writable,
2595 }
2596 }
2597 }
2598
2599 impl SemaStream for TestStream {
2600 fn read(&self, buf: &mut [u8]) -> Result<usize, SemaError> {
2601 let mut data = self.data.borrow_mut();
2602 let n = buf.len().min(data.len());
2603 buf[..n].copy_from_slice(&data[..n]);
2604 data.drain(..n);
2605 Ok(n)
2606 }
2607
2608 fn write(&self, data: &[u8]) -> Result<usize, SemaError> {
2609 self.data.borrow_mut().extend_from_slice(data);
2610 Ok(data.len())
2611 }
2612
2613 fn flush(&self) -> Result<(), SemaError> {
2614 Ok(())
2615 }
2616
2617 fn close(&self) -> Result<(), SemaError> {
2618 Ok(())
2619 }
2620
2621 fn available(&self) -> Result<bool, SemaError> {
2622 Ok(!self.data.borrow().is_empty())
2623 }
2624
2625 fn is_readable(&self) -> bool {
2626 self.readable
2627 }
2628
2629 fn is_writable(&self) -> bool {
2630 self.writable
2631 }
2632
2633 fn stream_type(&self) -> &'static str {
2634 "test"
2635 }
2636
2637 fn as_any(&self) -> &dyn std::any::Any {
2638 self
2639 }
2640 }
2641
2642 #[test]
2643 fn streambox_read_writes_data() {
2644 let sb = StreamBox::new(TestStream::new(true, true));
2645 sb.write(b"hello").unwrap();
2646 let mut buf = [0u8; 5];
2647 let n = sb.read(&mut buf).unwrap();
2648 assert_eq!(n, 5);
2649 assert_eq!(&buf, b"hello");
2650 }
2651
2652 #[test]
2653 fn streambox_close_prevents_read() {
2654 let sb = StreamBox::new(TestStream::new(true, true));
2655 sb.close().unwrap();
2656 let mut buf = [0u8; 5];
2657 let err = sb.read(&mut buf).unwrap_err();
2658 assert!(err.to_string().contains("closed"));
2659 }
2660
2661 #[test]
2662 fn streambox_close_prevents_write() {
2663 let sb = StreamBox::new(TestStream::new(true, true));
2664 sb.close().unwrap();
2665 let err = sb.write(b"data").unwrap_err();
2666 assert!(err.to_string().contains("closed"));
2667 }
2668
2669 #[test]
2670 fn streambox_close_prevents_flush() {
2671 let sb = StreamBox::new(TestStream::new(true, true));
2672 sb.close().unwrap();
2673 let err = sb.flush().unwrap_err();
2674 assert!(err.to_string().contains("closed"));
2675 }
2676
2677 #[test]
2678 fn streambox_double_close_is_noop() {
2679 let sb = StreamBox::new(TestStream::new(true, true));
2680 sb.close().unwrap();
2681 sb.close().unwrap(); }
2683
2684 #[test]
2685 fn streambox_is_closed() {
2686 let sb = StreamBox::new(TestStream::new(true, true));
2687 assert!(!sb.is_closed());
2688 sb.close().unwrap();
2689 assert!(sb.is_closed());
2690 }
2691
2692 #[test]
2693 fn streambox_is_readable() {
2694 let sb = StreamBox::new(TestStream::new(true, false));
2695 assert!(sb.is_readable());
2696 sb.close().unwrap();
2697 assert!(!sb.is_readable());
2698 }
2699
2700 #[test]
2701 fn streambox_is_writable() {
2702 let sb = StreamBox::new(TestStream::new(false, true));
2703 assert!(sb.is_writable());
2704 sb.close().unwrap();
2705 assert!(!sb.is_writable());
2706 }
2707
2708 #[test]
2709 fn streambox_available_when_closed() {
2710 let sb = StreamBox::new(TestStream::new(true, true));
2711 sb.close().unwrap();
2712 assert_eq!(sb.available().unwrap(), false);
2713 }
2714
2715 #[test]
2716 fn streambox_stream_type() {
2717 let sb = StreamBox::new(TestStream::new(true, true));
2718 assert_eq!(sb.stream_type(), "test");
2719 }
2720}