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