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