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