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