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};
10use num_bigint::BigInt;
11use num_rational::BigRational;
12use num_traits::ToPrimitive;
13
14use crate::error::SemaError;
15use crate::number::Complex as SemaComplex;
16use crate::number::SemaNumber;
17use crate::runtime::{NativeCallContext, NativeOutcome, NativeResult};
18use crate::EvalContext;
19
20#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
24compile_error!("sema-core NaN-boxed Value requires a 64-bit platform (or wasm32)");
25
26thread_local! {
29 static INTERNER: RefCell<Rodeo> = RefCell::new(Rodeo::default());
30}
31
32pub fn intern(s: &str) -> Spur {
34 INTERNER.with(|r| r.borrow_mut().get_or_intern(s))
35}
36
37pub fn resolve(spur: Spur) -> String {
39 INTERNER.with(|r| r.borrow().resolve(&spur).to_string())
40}
41
42const _: () = assert!(std::mem::size_of::<Spur>() == 4);
44
45#[inline(always)]
53pub fn spur_to_bits(spur: Spur) -> u32 {
54 spur.into_usize() as u32 + 1
57}
58
59#[inline(always)]
66pub fn bits_to_spur(bits: u32) -> Spur {
67 Spur::try_from_usize((bits - 1) as usize)
68 .expect("NaN-boxed symbol/keyword payload is not a valid interned key")
69}
70
71pub fn with_resolved<F, R>(spur: Spur, f: F) -> R
73where
74 F: FnOnce(&str) -> R,
75{
76 INTERNER.with(|r| {
77 let interner = r.borrow();
78 f(interner.resolve(&spur))
79 })
80}
81
82pub fn interner_stats() -> (usize, usize) {
84 INTERNER.with(|r| {
85 let interner = r.borrow();
86 let count = interner.len();
87 let bytes = count * 16; (count, bytes)
89 })
90}
91
92thread_local! {
95 static GENSYM_COUNTER: Cell<u64> = const { Cell::new(0) };
96}
97
98pub fn next_gensym(prefix: &str) -> String {
102 GENSYM_COUNTER.with(|c| {
103 let val = c.get();
104 c.set(val.wrapping_add(1));
105 format!("{prefix}__{val}")
106 })
107}
108
109pub fn compare_spurs(a: Spur, b: Spur) -> std::cmp::Ordering {
111 if a == b {
112 return std::cmp::Ordering::Equal;
113 }
114 INTERNER.with(|r| {
115 let interner = r.borrow();
116 interner.resolve(&a).cmp(interner.resolve(&b))
117 })
118}
119
120pub type NativeFnInner = dyn Fn(&EvalContext, &[Value]) -> Result<Value, SemaError>;
124type RuntimeNativeFnInner = dyn for<'a> Fn(&mut NativeCallContext<'a>, &[Value]) -> NativeResult;
125
126#[derive(Clone, Copy, Debug, Eq, PartialEq)]
133pub enum NativeSuspensionClass {
134 Inert,
137 CallbackDriven(&'static [usize]),
141 MaySuspend,
144}
145
146pub struct NativeFn {
147 pub name: String,
148 pub func: Box<NativeFnInner>,
154 pub payload: Option<Rc<dyn Any>>,
155 pub param_names: Option<Rc<[Spur]>>,
160 pub is_closure: bool,
166 runtime_func: Option<Box<RuntimeNativeFnInner>>,
172 runtime_only: bool,
180 escaping_args: &'static [usize],
184 suspension: Option<NativeSuspensionClass>,
187}
188
189impl NativeFn {
190 pub fn simple(
196 name: impl Into<String>,
197 f: impl Fn(&[Value]) -> Result<Value, SemaError> + 'static,
198 ) -> Self {
199 Self {
200 name: name.into(),
201 func: Box::new(move |_ctx, args| f(args)),
202 payload: None,
203 param_names: None,
204 is_closure: false,
205 runtime_func: None,
206 runtime_only: false,
207 escaping_args: &[],
208 suspension: None,
209 }
210 }
211
212 pub fn with_ctx(
218 name: impl Into<String>,
219 f: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
220 ) -> Self {
221 Self {
222 name: name.into(),
223 func: Box::new(f),
224 payload: None,
225 param_names: None,
226 is_closure: false,
227 runtime_func: None,
228 runtime_only: false,
229 escaping_args: &[],
230 suspension: None,
231 }
232 }
233
234 pub fn with_payload(
240 name: impl Into<String>,
241 payload: Rc<dyn Any>,
242 f: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
243 ) -> Self {
244 Self {
245 name: name.into(),
246 func: Box::new(f),
247 payload: Some(payload),
248 param_names: None,
249 is_closure: false,
250 runtime_func: None,
251 runtime_only: false,
252 escaping_args: &[],
253 suspension: None,
254 }
255 }
256
257 pub fn simple_result(
263 name: impl Into<String>,
264 f: impl Fn(&[Value]) -> NativeResult + 'static,
265 ) -> Self {
266 let name = name.into();
267 let error_name = name.clone();
268 Self {
269 name,
270 func: Box::new(move |_, _| {
271 Err(SemaError::eval(format!(
272 "internal error: runtime native function '{error_name}' requires runtime invocation"
273 )))
274 }),
275 runtime_func: Some(Box::new(move |_, args| f(args))),
276 payload: None,
277 param_names: None,
278 is_closure: false,
279 runtime_only: true,
280 escaping_args: &[],
281 suspension: None,
282 }
283 }
284
285 pub fn with_context_result(
292 name: impl Into<String>,
293 f: impl for<'a> Fn(&mut NativeCallContext<'a>, &[Value]) -> NativeResult + 'static,
294 ) -> Self {
295 let name = name.into();
296 let error_name = name.clone();
297 Self {
298 name,
299 func: Box::new(move |_, _| {
300 Err(SemaError::eval(format!(
301 "internal error: runtime native function '{error_name}' requires runtime invocation"
302 )))
303 }),
304 runtime_func: Some(Box::new(f)),
305 payload: None,
306 param_names: None,
307 is_closure: false,
308 runtime_only: true,
309 escaping_args: &[],
310 suspension: None,
311 }
312 }
313
314 pub fn with_payload_result<T: Any + 'static>(
322 name: impl Into<String>,
323 payload: Rc<T>,
324 f: for<'a> fn(&T, &mut NativeCallContext<'a>, &[Value]) -> NativeResult,
325 ) -> Self {
326 let name = name.into();
327 let error_name = name.clone();
328 let weak_payload = Rc::downgrade(&payload);
329 let payload: Rc<dyn Any> = payload;
330 Self {
331 name,
332 func: Box::new(move |_, _| {
333 Err(SemaError::eval(format!(
334 "internal error: runtime native function '{error_name}' requires runtime invocation"
335 )))
336 }),
337 payload: Some(payload),
338 param_names: None,
339 is_closure: false,
340 runtime_func: Some(Box::new(move |context, args| {
341 let payload = weak_payload.upgrade().ok_or_else(|| {
342 SemaError::eval("internal error: runtime native payload is unavailable")
343 })?;
344 f(&payload, context, args)
345 })),
346 runtime_only: true,
347 escaping_args: &[],
348 suspension: None,
349 }
350 }
351
352 pub fn with_payload_ctx_runtime<T: Any + 'static>(
360 name: impl Into<String>,
361 payload: Rc<T>,
362 func: fn(&T, &EvalContext, &[Value]) -> Result<Value, SemaError>,
363 runtime: for<'a> fn(&T, &mut NativeCallContext<'a>, &[Value]) -> NativeResult,
364 ) -> Self {
365 let legacy_payload = Rc::downgrade(&payload);
366 let runtime_payload = Rc::downgrade(&payload);
367 let payload: Rc<dyn Any> = payload;
368 Self {
369 name: name.into(),
370 func: Box::new(move |context, args| {
371 let payload = legacy_payload.upgrade().ok_or_else(|| {
372 SemaError::eval("internal error: native payload is unavailable")
373 })?;
374 func(&payload, context, args)
375 }),
376 payload: Some(payload),
377 param_names: None,
378 is_closure: false,
379 runtime_func: Some(Box::new(move |context, args| {
380 let payload = runtime_payload.upgrade().ok_or_else(|| {
381 SemaError::eval("internal error: runtime native payload is unavailable")
382 })?;
383 runtime(&payload, context, args)
384 })),
385 runtime_only: false,
386 escaping_args: &[],
387 suspension: None,
388 }
389 }
390
391 pub fn simple_with_runtime(
400 name: impl Into<String>,
401 func: impl Fn(&[Value]) -> Result<Value, SemaError> + 'static,
402 runtime: impl for<'a> Fn(&mut NativeCallContext<'a>, &[Value]) -> NativeResult + 'static,
403 ) -> Self {
404 Self {
405 name: name.into(),
406 func: Box::new(move |_ctx, args| func(args)),
407 runtime_func: Some(Box::new(runtime)),
408 payload: None,
409 param_names: None,
410 is_closure: false,
411 runtime_only: false,
412 escaping_args: &[],
413 suspension: None,
414 }
415 }
416
417 pub fn with_ctx_runtime(
427 name: impl Into<String>,
428 func: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
429 runtime: impl for<'a> Fn(&mut NativeCallContext<'a>, &[Value]) -> NativeResult + 'static,
430 ) -> Self {
431 Self {
432 name: name.into(),
433 func: Box::new(func),
434 runtime_func: Some(Box::new(runtime)),
435 payload: None,
436 param_names: None,
437 is_closure: false,
438 runtime_only: false,
439 escaping_args: &[],
440 suspension: None,
441 }
442 }
443
444 pub fn with_escaping_args(mut self, indices: &'static [usize]) -> Self {
448 self.escaping_args = indices;
449 self
450 }
451
452 pub fn escaping_args(&self) -> &'static [usize] {
454 self.escaping_args
455 }
456
457 pub fn with_callback_suspension(mut self, positions: &'static [usize]) -> Self {
461 self.suspension = Some(NativeSuspensionClass::CallbackDriven(positions));
462 self
463 }
464
465 pub fn suspension_class(&self) -> NativeSuspensionClass {
469 self.suspension.unwrap_or(if self.runtime_func.is_none() {
470 NativeSuspensionClass::Inert
471 } else {
472 NativeSuspensionClass::MaySuspend
473 })
474 }
475
476 #[doc(hidden)]
477 pub fn invoke_runtime(
480 &self,
481 runtime_context: &mut NativeCallContext<'_>,
482 args: &[Value],
483 ) -> NativeResult {
484 match &self.runtime_func {
485 Some(f) => f(runtime_context, args),
486 None => (self.func)(runtime_context.eval_context, args).map(NativeOutcome::Return),
487 }
488 }
489
490 pub fn is_runtime_only(&self) -> bool {
496 self.runtime_only
497 }
498}
499
500impl fmt::Debug for NativeFn {
501 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502 write!(f, "<native-fn {}>", self.name)
503 }
504}
505
506#[derive(Debug, Clone)]
508pub struct Lambda {
509 pub params: Vec<Spur>,
510 pub rest_param: Option<Spur>,
511 pub body: Vec<Value>,
512 pub env: Env,
513 pub name: Option<Spur>,
514}
515
516#[derive(Debug, Clone)]
524pub struct Macro {
525 pub params: Vec<Spur>,
526 pub rest_param: Option<Spur>,
527 pub body: Vec<Value>,
528 pub name: Spur,
529 pub syntax_rules: Option<Rc<SyntaxRules>>,
531}
532
533#[derive(Debug, Clone)]
538pub struct SyntaxRules {
539 pub literals: Vec<Spur>,
540 pub ellipsis: Spur,
541 pub rules: Vec<(Value, Value)>,
543}
544
545pub struct Thunk {
547 pub body: Value,
548 pub forced: RefCell<Option<Value>>,
549}
550
551impl fmt::Debug for Thunk {
552 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553 if self.forced.borrow().is_some() {
554 write!(f, "<promise (forced)>")
555 } else {
556 write!(f, "<promise>")
557 }
558 }
559}
560
561impl Clone for Thunk {
562 fn clone(&self) -> Self {
563 Thunk {
564 body: self.body.clone(),
565 forced: RefCell::new(self.forced.borrow().clone()),
566 }
567 }
568}
569
570#[derive(Debug, Clone, PartialEq, Eq)]
578pub enum PromiseState {
579 Pending,
580 Resolved(Value),
581 Rejected(String),
582 Cancelled,
583}
584
585#[derive(Clone, Copy)]
591pub struct AsyncPromise {
592 pub id: crate::runtime::PromiseId,
593}
594
595impl fmt::Debug for AsyncPromise {
596 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597 write!(f, "<async-promise>")
598 }
599}
600
601#[derive(Clone, Copy)]
608pub struct Channel {
609 pub id: crate::runtime::ChannelId,
610}
611
612impl fmt::Debug for Channel {
613 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614 f.write_str("<channel>")
615 }
616}
617
618#[derive(Debug, Clone)]
620pub struct Record {
621 pub type_tag: Spur,
622 pub field_names: Vec<Spur>,
623 pub fields: Vec<Value>,
624}
625
626pub struct MutableArray {
630 pub items: RefCell<Vec<Value>>,
631}
632
633impl fmt::Debug for MutableArray {
634 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635 match self.items.try_borrow() {
638 Ok(items) => write!(f, "<mutable-array {}>", items.len()),
639 Err(_) => write!(f, "<mutable-array (borrowed)>"),
640 }
641 }
642}
643
644impl Clone for MutableArray {
645 fn clone(&self) -> Self {
646 MutableArray {
647 items: RefCell::new(self.items.borrow().clone()),
648 }
649 }
650}
651
652pub struct MutableCell {
655 pub value: RefCell<Value>,
656}
657
658impl fmt::Debug for MutableCell {
659 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660 write!(f, "<mutable-cell>")
663 }
664}
665
666impl Clone for MutableCell {
667 fn clone(&self) -> Self {
668 MutableCell {
669 value: RefCell::new(self.value.borrow().clone()),
670 }
671 }
672}
673
674#[derive(Debug, Clone, PartialEq, Eq)]
676pub enum Role {
677 System,
678 User,
679 Assistant,
680 Tool,
681}
682
683impl fmt::Display for Role {
684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
685 match self {
686 Role::System => write!(f, "system"),
687 Role::User => write!(f, "user"),
688 Role::Assistant => write!(f, "assistant"),
689 Role::Tool => write!(f, "tool"),
690 }
691 }
692}
693
694#[derive(Debug, Clone)]
696pub struct ImageAttachment {
697 pub data: String,
698 pub media_type: String,
699}
700
701#[derive(Debug, Clone)]
703pub struct Message {
704 pub role: Role,
705 pub content: String,
706 pub images: Vec<ImageAttachment>,
708}
709
710#[derive(Debug, Clone)]
712pub struct Prompt {
713 pub messages: Vec<Message>,
714}
715
716#[derive(Debug, Clone)]
718pub struct Conversation {
719 pub messages: Vec<Message>,
720 pub model: String,
721 pub metadata: BTreeMap<String, String>,
722}
723
724#[derive(Debug, Clone)]
726pub struct ToolDefinition {
727 pub name: String,
728 pub description: String,
729 pub parameters: Value,
730 pub policy_subjects: Vec<ToolPolicySubject>,
731 pub handler: Value,
732}
733
734#[derive(Debug, Clone, PartialEq, Eq)]
739pub enum ToolPolicySubject {
740 File {
741 access: FileAccess,
742 path_arg: String,
743 },
744 NetworkRequest {
745 method: Option<String>,
746 url_arg: String,
747 },
748 Command {
749 command_arg: String,
750 },
751 ExternalAction {
752 action: String,
753 target_arg: Option<String>,
754 },
755}
756
757#[derive(Debug, Clone, Copy, PartialEq, Eq)]
759pub enum FileAccess {
760 Read,
761 Write,
762 Delete,
763}
764
765#[derive(Debug, Clone)]
767pub struct Agent {
768 pub name: String,
769 pub system: String,
770 pub tools: Vec<Value>,
771 pub max_turns: usize,
772 pub model: String,
773}
774
775pub struct MultiMethod {
778 pub name: Spur,
779 pub dispatch_fn: Value,
780 pub methods: RefCell<BTreeMap<Value, Value>>,
781 pub default: RefCell<Option<Value>>,
782}
783
784impl fmt::Debug for MultiMethod {
785 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
786 write!(f, "<multimethod {}>", resolve(self.name))
787 }
788}
789
790pub fn resolve_multimethod_handler(
795 ctx: &EvalContext,
796 mm: &MultiMethod,
797 args: &[Value],
798) -> Result<Value, SemaError> {
799 let dispatch_val = crate::call_callback(ctx, &mm.dispatch_fn, args)?;
800 select_multimethod_handler(mm, &dispatch_val)
801}
802
803pub fn select_multimethod_handler(
807 mm: &MultiMethod,
808 dispatch_val: &Value,
809) -> Result<Value, SemaError> {
810 let methods = mm.methods.borrow();
811 if let Some(handler) = methods.get(dispatch_val) {
812 Ok(handler.clone())
813 } else {
814 drop(methods);
815 let default = mm.default.borrow().clone();
816 default.ok_or_else(|| {
817 SemaError::eval(format!(
818 "no method in multimethod '{}' for dispatch value: {}",
819 resolve(mm.name),
820 dispatch_val
821 ))
822 .with_hint("add a (defmethod name :default handler) to handle unmatched values")
823 })
824 }
825}
826
827pub trait SemaStream: fmt::Debug {
830 fn read(&self, buf: &mut [u8]) -> Result<usize, SemaError>;
831 fn write(&self, data: &[u8]) -> Result<usize, SemaError>;
832 fn available(&self) -> Result<bool, SemaError> {
833 Ok(false)
834 }
835 fn flush(&self) -> Result<(), SemaError> {
836 Ok(())
837 }
838 fn close(&self) -> Result<(), SemaError> {
839 Ok(())
840 }
841 fn is_readable(&self) -> bool {
842 true
843 }
844 fn is_writable(&self) -> bool {
845 true
846 }
847 fn stream_type(&self) -> &'static str;
848 fn as_any(&self) -> &dyn std::any::Any;
849}
850
851pub struct StreamBox {
854 inner: RefCell<Box<dyn SemaStream>>,
855 closed: Cell<bool>,
856}
857
858impl StreamBox {
859 pub fn new(s: impl SemaStream + 'static) -> Self {
860 StreamBox {
861 inner: RefCell::new(Box::new(s)),
862 closed: Cell::new(false),
863 }
864 }
865
866 pub fn read(&self, buf: &mut [u8]) -> Result<usize, SemaError> {
867 if self.closed.get() {
868 return Err(SemaError::eval("stream/read: stream is closed"));
869 }
870 self.inner.borrow().read(buf)
871 }
872
873 pub fn write(&self, data: &[u8]) -> Result<usize, SemaError> {
874 if self.closed.get() {
875 return Err(SemaError::eval("stream/write: stream is closed"));
876 }
877 self.inner.borrow().write(data)
878 }
879
880 pub fn flush(&self) -> Result<(), SemaError> {
881 if self.closed.get() {
882 return Err(SemaError::eval("stream/flush: stream is closed"));
883 }
884 self.inner.borrow().flush()
885 }
886
887 pub fn close(&self) -> Result<(), SemaError> {
888 if self.closed.get() {
889 return Ok(()); }
891 self.inner.borrow().close()?;
892 self.closed.set(true);
893 Ok(())
894 }
895
896 pub fn is_closed(&self) -> bool {
897 self.closed.get()
898 }
899
900 pub fn is_readable(&self) -> bool {
901 !self.closed.get() && self.inner.borrow().is_readable()
902 }
903
904 pub fn is_writable(&self) -> bool {
905 !self.closed.get() && self.inner.borrow().is_writable()
906 }
907
908 pub fn available(&self) -> Result<bool, SemaError> {
909 if self.closed.get() {
910 return Ok(false);
911 }
912 self.inner.borrow().available()
913 }
914
915 pub fn stream_type(&self) -> &'static str {
916 self.inner.borrow().stream_type()
917 }
918
919 pub fn borrow_inner(&self) -> std::cell::Ref<'_, Box<dyn SemaStream>> {
920 self.inner.borrow()
921 }
922}
923
924impl fmt::Debug for StreamBox {
925 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
926 write!(f, "<stream:{}>", self.stream_type())
927 }
928}
929
930impl Clone for MultiMethod {
931 fn clone(&self) -> Self {
932 MultiMethod {
933 name: self.name,
934 dispatch_fn: self.dispatch_fn.clone(),
935 methods: RefCell::new(self.methods.borrow().clone()),
936 default: RefCell::new(self.default.borrow().clone()),
937 }
938 }
939}
940
941const BOX_MASK: u64 = 0xFFF8_0000_0000_0000;
953
954const PAYLOAD_MASK: u64 = (1u64 << 45) - 1; const INT_SIGN_BIT: u64 = 1u64 << 44;
959
960const TAG_MASK_6BIT: u64 = 0x3F;
962
963const CANONICAL_NAN: u64 = 0x7FF8_0000_0000_0000;
965
966const TAG_NIL: u64 = 0;
968const TAG_FALSE: u64 = 1;
969const TAG_TRUE: u64 = 2;
970const TAG_INT_SMALL: u64 = 3;
971const TAG_CHAR: u64 = 4;
972const TAG_SYMBOL: u64 = 5;
973const TAG_KEYWORD: u64 = 6;
974const TAG_INT_BIG: u64 = 7;
975const TAG_STRING: u64 = 8;
976const TAG_LIST: u64 = 9;
977const TAG_VECTOR: u64 = 10;
978const TAG_MAP: u64 = 11;
979const TAG_HASHMAP: u64 = 12;
980const TAG_LAMBDA: u64 = 13;
981const TAG_MACRO: u64 = 14;
982pub const TAG_NATIVE_FN: u64 = 15;
983const TAG_PROMPT: u64 = 16;
984const TAG_MESSAGE: u64 = 17;
985const TAG_CONVERSATION: u64 = 18;
986const TAG_TOOL_DEF: u64 = 19;
987const TAG_AGENT: u64 = 20;
988const TAG_THUNK: u64 = 21;
989const TAG_RECORD: u64 = 22;
990const TAG_BYTEVECTOR: u64 = 23;
991const TAG_MULTIMETHOD: u64 = 24;
992const TAG_STREAM: u64 = 25;
993const TAG_F64_ARRAY: u64 = 26;
994const TAG_I64_ARRAY: u64 = 27;
995const TAG_ASYNC_PROMISE: u64 = 28;
996const TAG_CHANNEL: u64 = 29;
997const TAG_BIGINT: u64 = 30;
998const TAG_RATIONAL: u64 = 31;
999const TAG_COMPLEX: u64 = 32;
1000const TAG_MUTABLE_ARRAY: u64 = 33;
1001const TAG_MUTABLE_CELL: u64 = 34;
1002
1003const SMALL_INT_MIN: i64 = -(1i64 << 44);
1005const SMALL_INT_MAX: i64 = (1i64 << 44) - 1;
1006
1007pub const NAN_TAG_MASK: u64 = BOX_MASK | (TAG_MASK_6BIT << 45); pub const NAN_INT_SMALL_PATTERN: u64 = BOX_MASK | (TAG_INT_SMALL << 45);
1014
1015pub const NAN_PAYLOAD_MASK: u64 = PAYLOAD_MASK;
1017
1018pub const NAN_INT_SIGN_BIT: u64 = INT_SIGN_BIT;
1020
1021pub const NAN_PAYLOAD_BITS: u32 = 45;
1023
1024#[inline(always)]
1027fn make_boxed(tag: u64, payload: u64) -> u64 {
1028 BOX_MASK | (tag << 45) | (payload & PAYLOAD_MASK)
1029}
1030
1031#[inline(always)]
1032fn is_boxed(bits: u64) -> bool {
1033 (bits & BOX_MASK) == BOX_MASK
1034}
1035
1036#[inline(always)]
1037fn get_tag(bits: u64) -> u64 {
1038 (bits >> 45) & TAG_MASK_6BIT
1039}
1040
1041#[inline(always)]
1042fn get_payload(bits: u64) -> u64 {
1043 bits & PAYLOAD_MASK
1044}
1045
1046#[inline(always)]
1047fn ptr_to_payload(ptr: *const u8) -> u64 {
1048 let raw = ptr as u64;
1049 debug_assert!(raw & 0x7 == 0, "pointer not 8-byte aligned: 0x{:x}", raw);
1050 debug_assert!(
1051 raw >> 48 == 0,
1052 "pointer exceeds 48-bit VA space: 0x{:x}",
1053 raw
1054 );
1055 raw >> 3
1056}
1057
1058#[inline(always)]
1059fn payload_to_ptr(payload: u64) -> *const u8 {
1060 (payload << 3) as *const u8
1061}
1062
1063pub enum ValueView {
1068 Nil,
1069 Bool(bool),
1070 Int(i64),
1071 BigInt(Rc<BigInt>),
1072 Rational(Rc<BigRational>),
1073 Complex(Rc<SemaComplex>),
1074 Float(f64),
1075 String(Rc<String>),
1076 Symbol(Spur),
1077 Keyword(Spur),
1078 Char(char),
1079 List(Rc<Vec<Value>>),
1080 Vector(Rc<Vec<Value>>),
1081 Map(Rc<BTreeMap<Value, Value>>),
1082 HashMap(Rc<hashbrown::HashMap<Value, Value>>),
1083 Lambda(Rc<Lambda>),
1084 Macro(Rc<Macro>),
1085 NativeFn(Rc<NativeFn>),
1086 Prompt(Rc<Prompt>),
1087 Message(Rc<Message>),
1088 Conversation(Rc<Conversation>),
1089 ToolDef(Rc<ToolDefinition>),
1090 Agent(Rc<Agent>),
1091 Thunk(Rc<Thunk>),
1092 Record(Rc<Record>),
1093 Bytevector(Rc<Vec<u8>>),
1094 MultiMethod(Rc<MultiMethod>),
1095 Stream(Rc<StreamBox>),
1096 F64Array(Rc<Vec<f64>>),
1097 I64Array(Rc<Vec<i64>>),
1098 AsyncPromise(Rc<AsyncPromise>),
1099 Channel(Rc<Channel>),
1100 MutableArray(Rc<MutableArray>),
1101 MutableCell(Rc<MutableCell>),
1102}
1103
1104pub enum ValueViewRef<'a> {
1108 Nil,
1109 Bool(bool),
1110 Int(i64),
1111 BigInt(&'a BigInt),
1112 Rational(&'a BigRational),
1113 Complex(&'a SemaComplex),
1114 Float(f64),
1115 String(&'a str),
1116 Symbol(Spur),
1117 Keyword(Spur),
1118 Char(char),
1119 List(&'a [Value]),
1120 Vector(&'a [Value]),
1121 Map(&'a BTreeMap<Value, Value>),
1122 HashMap(&'a hashbrown::HashMap<Value, Value>),
1123 Lambda(&'a Lambda),
1124 Macro(&'a Macro),
1125 NativeFn(&'a NativeFn),
1126 Prompt(&'a Prompt),
1127 Message(&'a Message),
1128 Conversation(&'a Conversation),
1129 ToolDef(&'a ToolDefinition),
1130 Agent(&'a Agent),
1131 Thunk(&'a Thunk),
1132 Record(&'a Record),
1133 Bytevector(&'a [u8]),
1134 MultiMethod(&'a MultiMethod),
1135 Stream(&'a StreamBox),
1136 F64Array(&'a [f64]),
1137 I64Array(&'a [i64]),
1138 AsyncPromise(&'a AsyncPromise),
1139 Channel(&'a Channel),
1140 MutableArray(&'a MutableArray),
1141 MutableCell(&'a MutableCell),
1142}
1143
1144#[repr(transparent)]
1150pub struct Value(u64);
1151
1152impl Value {
1155 pub const NIL: Value = Value(make_boxed_const(TAG_NIL, 0));
1158 pub const TRUE: Value = Value(make_boxed_const(TAG_TRUE, 0));
1159 pub const FALSE: Value = Value(make_boxed_const(TAG_FALSE, 0));
1160
1161 #[inline(always)]
1162 pub fn nil() -> Value {
1163 Value::NIL
1164 }
1165
1166 #[inline(always)]
1167 pub fn bool(b: bool) -> Value {
1168 if b {
1169 Value::TRUE
1170 } else {
1171 Value::FALSE
1172 }
1173 }
1174
1175 #[inline(always)]
1176 pub fn int(n: i64) -> Value {
1177 if (SMALL_INT_MIN..=SMALL_INT_MAX).contains(&n) {
1178 let payload = (n as u64) & PAYLOAD_MASK;
1180 Value(make_boxed(TAG_INT_SMALL, payload))
1181 } else {
1182 Value::from_rc_ptr(TAG_INT_BIG, Rc::new(n))
1185 }
1186 }
1187
1188 #[inline(always)]
1189 pub fn float(f: f64) -> Value {
1190 let bits = f.to_bits();
1191 if f.is_nan() {
1192 Value(CANONICAL_NAN)
1194 } else {
1195 debug_assert!(
1202 !is_boxed(bits),
1203 "non-NaN float collides with boxed pattern: {:?} = 0x{:016x}",
1204 f,
1205 bits
1206 );
1207 Value(bits)
1208 }
1209 }
1210
1211 #[inline(always)]
1212 pub fn char(c: char) -> Value {
1213 Value(make_boxed(TAG_CHAR, c as u64))
1214 }
1215
1216 #[inline(always)]
1217 pub fn symbol_from_spur(spur: Spur) -> Value {
1218 Value(make_boxed(TAG_SYMBOL, spur_to_bits(spur) as u64))
1219 }
1220
1221 pub fn symbol(s: &str) -> Value {
1222 Value::symbol_from_spur(intern(s))
1223 }
1224
1225 #[inline(always)]
1226 pub fn keyword_from_spur(spur: Spur) -> Value {
1227 Value(make_boxed(TAG_KEYWORD, spur_to_bits(spur) as u64))
1228 }
1229
1230 pub fn keyword(s: &str) -> Value {
1231 Value::keyword_from_spur(intern(s))
1232 }
1233
1234 fn from_rc_ptr<T>(tag: u64, rc: Rc<T>) -> Value {
1237 const { assert!(std::mem::align_of::<T>() <= RC_HEADER) };
1243 #[cfg(debug_assertions)]
1244 let count = Rc::strong_count(&rc);
1245 let ptr = Rc::into_raw(rc) as *const u8;
1246 #[cfg(debug_assertions)]
1249 unsafe {
1250 debug_assert_eq!(
1251 rc_strong_cell(ptr).get(),
1252 count,
1253 "RcBox header offset mismatch for {}",
1254 std::any::type_name::<T>()
1255 );
1256 }
1257 Value(make_boxed(tag, ptr_to_payload(ptr)))
1258 }
1259
1260 pub fn from_bigint(n: BigInt) -> Value {
1264 match n.to_i64() {
1265 Some(i) => Value::int(i),
1266 None => Value::from_rc_ptr(TAG_BIGINT, Rc::new(n)),
1267 }
1268 }
1269
1270 pub fn rational(r: BigRational) -> Value {
1273 if r.is_integer() {
1274 Value::from_bigint(r.to_integer())
1275 } else {
1276 Value::from_rc_ptr(TAG_RATIONAL, Rc::new(r))
1277 }
1278 }
1279
1280 pub fn complex(re: SemaNumber, im: SemaNumber) -> Value {
1283 Value::from_number(SemaNumber::Complex(Box::new(SemaComplex { re, im })))
1284 }
1285
1286 pub fn string(s: &str) -> Value {
1287 Value::from_rc_ptr(TAG_STRING, Rc::new(s.to_string()))
1288 }
1289
1290 pub fn string_owned(s: String) -> Value {
1294 Value::from_rc_ptr(TAG_STRING, Rc::new(s))
1295 }
1296
1297 pub fn string_from_rc(rc: Rc<String>) -> Value {
1298 Value::from_rc_ptr(TAG_STRING, rc)
1299 }
1300
1301 pub fn list(v: Vec<Value>) -> Value {
1302 Value::from_rc_ptr(TAG_LIST, Rc::new(v))
1303 }
1304
1305 pub fn list_from_rc(rc: Rc<Vec<Value>>) -> Value {
1306 Value::from_rc_ptr(TAG_LIST, rc)
1307 }
1308
1309 pub fn vector(v: Vec<Value>) -> Value {
1310 Value::from_rc_ptr(TAG_VECTOR, Rc::new(v))
1311 }
1312
1313 pub fn vector_from_rc(rc: Rc<Vec<Value>>) -> Value {
1314 Value::from_rc_ptr(TAG_VECTOR, rc)
1315 }
1316
1317 pub fn map(m: BTreeMap<Value, Value>) -> Value {
1318 Value::from_rc_ptr(TAG_MAP, Rc::new(m))
1319 }
1320
1321 pub fn map_from_rc(rc: Rc<BTreeMap<Value, Value>>) -> Value {
1322 Value::from_rc_ptr(TAG_MAP, rc)
1323 }
1324
1325 pub fn hashmap(entries: Vec<(Value, Value)>) -> Value {
1326 let map: hashbrown::HashMap<Value, Value> = entries.into_iter().collect();
1327 Value::from_rc_ptr(TAG_HASHMAP, Rc::new(map))
1328 }
1329
1330 pub fn hashmap_from_rc(rc: Rc<hashbrown::HashMap<Value, Value>>) -> Value {
1331 Value::from_rc_ptr(TAG_HASHMAP, rc)
1332 }
1333
1334 pub fn lambda(l: Lambda) -> Value {
1335 Value::from_rc_ptr(TAG_LAMBDA, Rc::new(l))
1336 }
1337
1338 pub fn lambda_from_rc(rc: Rc<Lambda>) -> Value {
1339 Value::from_rc_ptr(TAG_LAMBDA, rc)
1340 }
1341
1342 pub fn macro_val(m: Macro) -> Value {
1343 Value::from_rc_ptr(TAG_MACRO, Rc::new(m))
1344 }
1345
1346 pub fn macro_from_rc(rc: Rc<Macro>) -> Value {
1347 Value::from_rc_ptr(TAG_MACRO, rc)
1348 }
1349
1350 pub fn native_fn(f: NativeFn) -> Value {
1351 Value::from_rc_ptr(TAG_NATIVE_FN, Rc::new(f))
1352 }
1353
1354 pub fn native_fn_from_rc(rc: Rc<NativeFn>) -> Value {
1355 Value::from_rc_ptr(TAG_NATIVE_FN, rc)
1356 }
1357
1358 pub fn prompt(p: Prompt) -> Value {
1359 Value::from_rc_ptr(TAG_PROMPT, Rc::new(p))
1360 }
1361
1362 pub fn prompt_from_rc(rc: Rc<Prompt>) -> Value {
1363 Value::from_rc_ptr(TAG_PROMPT, rc)
1364 }
1365
1366 pub fn message(m: Message) -> Value {
1367 Value::from_rc_ptr(TAG_MESSAGE, Rc::new(m))
1368 }
1369
1370 pub fn message_from_rc(rc: Rc<Message>) -> Value {
1371 Value::from_rc_ptr(TAG_MESSAGE, rc)
1372 }
1373
1374 pub fn conversation(c: Conversation) -> Value {
1375 Value::from_rc_ptr(TAG_CONVERSATION, Rc::new(c))
1376 }
1377
1378 pub fn conversation_from_rc(rc: Rc<Conversation>) -> Value {
1379 Value::from_rc_ptr(TAG_CONVERSATION, rc)
1380 }
1381
1382 pub fn tool_def(t: ToolDefinition) -> Value {
1383 Value::from_rc_ptr(TAG_TOOL_DEF, Rc::new(t))
1384 }
1385
1386 pub fn tool_def_from_rc(rc: Rc<ToolDefinition>) -> Value {
1387 Value::from_rc_ptr(TAG_TOOL_DEF, rc)
1388 }
1389
1390 pub fn agent(a: Agent) -> Value {
1391 Value::from_rc_ptr(TAG_AGENT, Rc::new(a))
1392 }
1393
1394 pub fn agent_from_rc(rc: Rc<Agent>) -> Value {
1395 Value::from_rc_ptr(TAG_AGENT, rc)
1396 }
1397
1398 pub fn thunk(t: Thunk) -> Value {
1399 let rc = Rc::new(t);
1400 crate::cycle::register_candidate(crate::cycle::GcNode::Thunk(Rc::downgrade(&rc)));
1405 Value::from_rc_ptr(TAG_THUNK, rc)
1406 }
1407
1408 pub fn thunk_from_rc(rc: Rc<Thunk>) -> Value {
1409 Value::from_rc_ptr(TAG_THUNK, rc)
1410 }
1411
1412 pub fn record(r: Record) -> Value {
1413 Value::from_rc_ptr(TAG_RECORD, Rc::new(r))
1414 }
1415
1416 pub fn record_from_rc(rc: Rc<Record>) -> Value {
1417 Value::from_rc_ptr(TAG_RECORD, rc)
1418 }
1419
1420 pub fn bytevector(bytes: Vec<u8>) -> Value {
1421 Value::from_rc_ptr(TAG_BYTEVECTOR, Rc::new(bytes))
1422 }
1423
1424 pub fn bytevector_from_rc(rc: Rc<Vec<u8>>) -> Value {
1425 Value::from_rc_ptr(TAG_BYTEVECTOR, rc)
1426 }
1427
1428 pub fn f64_array(data: Vec<f64>) -> Value {
1429 Value::from_rc_ptr(TAG_F64_ARRAY, Rc::new(data))
1430 }
1431
1432 pub fn f64_array_from_rc(rc: Rc<Vec<f64>>) -> Value {
1433 Value::from_rc_ptr(TAG_F64_ARRAY, rc)
1434 }
1435
1436 pub fn i64_array(data: Vec<i64>) -> Value {
1437 Value::from_rc_ptr(TAG_I64_ARRAY, Rc::new(data))
1438 }
1439
1440 pub fn i64_array_from_rc(rc: Rc<Vec<i64>>) -> Value {
1441 Value::from_rc_ptr(TAG_I64_ARRAY, rc)
1442 }
1443
1444 pub fn multimethod(m: MultiMethod) -> Value {
1445 let rc = Rc::new(m);
1446 crate::cycle::register_candidate(crate::cycle::GcNode::MultiMethod(Rc::downgrade(&rc)));
1450 Value::from_rc_ptr(TAG_MULTIMETHOD, rc)
1451 }
1452
1453 pub fn multimethod_from_rc(rc: Rc<MultiMethod>) -> Value {
1454 Value::from_rc_ptr(TAG_MULTIMETHOD, rc)
1455 }
1456
1457 pub fn stream(s: impl SemaStream + 'static) -> Value {
1458 Value::from_rc_ptr(TAG_STREAM, Rc::new(StreamBox::new(s)))
1459 }
1460
1461 pub fn stream_from_rc(rc: Rc<StreamBox>) -> Value {
1462 Value::from_rc_ptr(TAG_STREAM, rc)
1463 }
1464
1465 pub fn async_promise(promise: AsyncPromise) -> Value {
1466 let rc = Rc::new(promise);
1475 crate::cycle::register_candidate(crate::cycle::GcNode::Promise {
1476 weak: Rc::downgrade(&rc),
1477 id: rc.id,
1478 });
1479 Value::from_rc_ptr(TAG_ASYNC_PROMISE, rc)
1480 }
1481 pub fn async_promise_id(id: crate::runtime::PromiseId) -> Value {
1484 Value::async_promise(AsyncPromise { id })
1485 }
1486 pub fn async_promise_from_rc(rc: Rc<AsyncPromise>) -> Value {
1490 Value::from_rc_ptr(TAG_ASYNC_PROMISE, rc)
1491 }
1492 pub fn channel(ch: Channel) -> Value {
1493 let rc = Rc::new(ch);
1502 crate::cycle::register_candidate(crate::cycle::GcNode::Channel {
1503 weak: Rc::downgrade(&rc),
1504 id: rc.id,
1505 });
1506 Value::from_rc_ptr(TAG_CHANNEL, rc)
1507 }
1508 pub fn channel_id(id: crate::runtime::ChannelId) -> Value {
1511 Value::channel(Channel { id })
1512 }
1513 pub fn channel_from_rc(rc: Rc<Channel>) -> Value {
1517 Value::from_rc_ptr(TAG_CHANNEL, rc)
1518 }
1519 pub fn mutable_array(items: Vec<Value>) -> Value {
1520 let rc = Rc::new(MutableArray {
1521 items: RefCell::new(items),
1522 });
1523 crate::cycle::register_candidate(crate::cycle::GcNode::MutableArray(Rc::downgrade(&rc)));
1527 Value::from_rc_ptr(TAG_MUTABLE_ARRAY, rc)
1528 }
1529 pub fn mutable_array_from_rc(rc: Rc<MutableArray>) -> Value {
1530 Value::from_rc_ptr(TAG_MUTABLE_ARRAY, rc)
1531 }
1532 pub fn mutable_cell(value: Value) -> Value {
1533 let rc = Rc::new(MutableCell {
1534 value: RefCell::new(value),
1535 });
1536 crate::cycle::register_candidate(crate::cycle::GcNode::MutableCell(Rc::downgrade(&rc)));
1539 Value::from_rc_ptr(TAG_MUTABLE_CELL, rc)
1540 }
1541 pub fn mutable_cell_from_rc(rc: Rc<MutableCell>) -> Value {
1542 Value::from_rc_ptr(TAG_MUTABLE_CELL, rc)
1543 }
1544}
1545
1546const fn make_boxed_const(tag: u64, payload: u64) -> u64 {
1548 BOX_MASK | (tag << 45) | (payload & PAYLOAD_MASK)
1549}
1550
1551impl Value {
1554 #[inline(always)]
1556 pub fn raw_bits(&self) -> u64 {
1557 self.0
1558 }
1559
1560 #[inline(always)]
1569 pub unsafe fn from_raw_bits(bits: u64) -> Value {
1570 Value(bits)
1571 }
1572
1573 #[inline(always)]
1576 pub fn raw_tag(&self) -> Option<u64> {
1577 if is_boxed(self.0) {
1578 Some(get_tag(self.0))
1579 } else {
1580 None
1581 }
1582 }
1583
1584 #[inline(always)]
1587 pub fn as_native_fn_ref(&self) -> Option<&NativeFn> {
1588 if is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN {
1589 Some(unsafe { self.borrow_ref::<NativeFn>() })
1590 } else {
1591 None
1592 }
1593 }
1594
1595 #[inline(always)]
1597 pub fn is_float(&self) -> bool {
1598 !is_boxed(self.0)
1599 }
1600
1601 #[inline(always)]
1604 unsafe fn get_rc<T>(&self) -> Rc<T> {
1605 let payload = get_payload(self.0);
1606 let ptr = payload_to_ptr(payload) as *const T;
1607 Rc::increment_strong_count(ptr);
1608 Rc::from_raw(ptr)
1609 }
1610
1611 #[inline(always)]
1614 unsafe fn borrow_ref<T>(&self) -> &T {
1615 let payload = get_payload(self.0);
1616 let ptr = payload_to_ptr(payload) as *const T;
1617 &*ptr
1618 }
1619
1620 pub fn view(&self) -> ValueView {
1623 if !is_boxed(self.0) {
1624 return ValueView::Float(f64::from_bits(self.0));
1625 }
1626 let tag = get_tag(self.0);
1627 match tag {
1628 TAG_NIL => ValueView::Nil,
1629 TAG_FALSE => ValueView::Bool(false),
1630 TAG_TRUE => ValueView::Bool(true),
1631 TAG_INT_SMALL => {
1632 let payload = get_payload(self.0);
1633 let val = if payload & INT_SIGN_BIT != 0 {
1634 (payload | !PAYLOAD_MASK) as i64
1635 } else {
1636 payload as i64
1637 };
1638 ValueView::Int(val)
1639 }
1640 TAG_CHAR => {
1641 let payload = get_payload(self.0);
1642 ValueView::Char(unsafe { char::from_u32_unchecked(payload as u32) })
1643 }
1644 TAG_SYMBOL => {
1645 let payload = get_payload(self.0);
1646 ValueView::Symbol(bits_to_spur(payload as u32))
1647 }
1648 TAG_KEYWORD => {
1649 let payload = get_payload(self.0);
1650 ValueView::Keyword(bits_to_spur(payload as u32))
1651 }
1652 TAG_INT_BIG => {
1653 let val = unsafe { *self.borrow_ref::<i64>() };
1654 ValueView::Int(val)
1655 }
1656 TAG_BIGINT => ValueView::BigInt(unsafe { self.get_rc::<BigInt>() }),
1657 TAG_RATIONAL => ValueView::Rational(unsafe { self.get_rc::<BigRational>() }),
1658 TAG_COMPLEX => ValueView::Complex(unsafe { self.get_rc::<SemaComplex>() }),
1659 TAG_STRING => ValueView::String(unsafe { self.get_rc::<String>() }),
1665 TAG_LIST => ValueView::List(unsafe { self.get_rc::<Vec<Value>>() }),
1666 TAG_VECTOR => ValueView::Vector(unsafe { self.get_rc::<Vec<Value>>() }),
1667 TAG_MAP => ValueView::Map(unsafe { self.get_rc::<BTreeMap<Value, Value>>() }),
1668 TAG_HASHMAP => {
1669 ValueView::HashMap(unsafe { self.get_rc::<hashbrown::HashMap<Value, Value>>() })
1670 }
1671 TAG_LAMBDA => ValueView::Lambda(unsafe { self.get_rc::<Lambda>() }),
1672 TAG_MACRO => ValueView::Macro(unsafe { self.get_rc::<Macro>() }),
1673 TAG_NATIVE_FN => ValueView::NativeFn(unsafe { self.get_rc::<NativeFn>() }),
1674 TAG_PROMPT => ValueView::Prompt(unsafe { self.get_rc::<Prompt>() }),
1675 TAG_MESSAGE => ValueView::Message(unsafe { self.get_rc::<Message>() }),
1676 TAG_CONVERSATION => ValueView::Conversation(unsafe { self.get_rc::<Conversation>() }),
1677 TAG_TOOL_DEF => ValueView::ToolDef(unsafe { self.get_rc::<ToolDefinition>() }),
1678 TAG_AGENT => ValueView::Agent(unsafe { self.get_rc::<Agent>() }),
1679 TAG_THUNK => ValueView::Thunk(unsafe { self.get_rc::<Thunk>() }),
1680 TAG_RECORD => ValueView::Record(unsafe { self.get_rc::<Record>() }),
1681 TAG_BYTEVECTOR => ValueView::Bytevector(unsafe { self.get_rc::<Vec<u8>>() }),
1682 TAG_MULTIMETHOD => ValueView::MultiMethod(unsafe { self.get_rc::<MultiMethod>() }),
1683 TAG_STREAM => ValueView::Stream(unsafe { self.get_rc::<StreamBox>() }),
1684 TAG_F64_ARRAY => ValueView::F64Array(unsafe { self.get_rc::<Vec<f64>>() }),
1685 TAG_I64_ARRAY => ValueView::I64Array(unsafe { self.get_rc::<Vec<i64>>() }),
1686 TAG_ASYNC_PROMISE => ValueView::AsyncPromise(unsafe { self.get_rc::<AsyncPromise>() }),
1687 TAG_CHANNEL => ValueView::Channel(unsafe { self.get_rc::<Channel>() }),
1688 TAG_MUTABLE_ARRAY => ValueView::MutableArray(unsafe { self.get_rc::<MutableArray>() }),
1689 TAG_MUTABLE_CELL => ValueView::MutableCell(unsafe { self.get_rc::<MutableCell>() }),
1690 _ => unreachable!("invalid NaN-boxed tag: {}", tag),
1691 }
1692 }
1693
1694 #[inline(always)]
1698 pub fn view_ref(&self) -> ValueViewRef<'_> {
1699 if !is_boxed(self.0) {
1700 return ValueViewRef::Float(f64::from_bits(self.0));
1701 }
1702 let tag = get_tag(self.0);
1703 match tag {
1704 TAG_NIL => ValueViewRef::Nil,
1705 TAG_FALSE => ValueViewRef::Bool(false),
1706 TAG_TRUE => ValueViewRef::Bool(true),
1707 TAG_INT_SMALL => {
1708 let payload = get_payload(self.0);
1709 let val = if payload & INT_SIGN_BIT != 0 {
1710 (payload | !PAYLOAD_MASK) as i64
1711 } else {
1712 payload as i64
1713 };
1714 ValueViewRef::Int(val)
1715 }
1716 TAG_CHAR => {
1717 let payload = get_payload(self.0);
1718 ValueViewRef::Char(unsafe { char::from_u32_unchecked(payload as u32) })
1719 }
1720 TAG_SYMBOL => {
1721 let payload = get_payload(self.0);
1722 ValueViewRef::Symbol(bits_to_spur(payload as u32))
1723 }
1724 TAG_KEYWORD => {
1725 let payload = get_payload(self.0);
1726 ValueViewRef::Keyword(bits_to_spur(payload as u32))
1727 }
1728 TAG_INT_BIG => {
1729 let val = unsafe { *self.borrow_ref::<i64>() };
1730 ValueViewRef::Int(val)
1731 }
1732 TAG_BIGINT => ValueViewRef::BigInt(unsafe { self.borrow_ref::<BigInt>() }),
1733 TAG_RATIONAL => ValueViewRef::Rational(unsafe { self.borrow_ref::<BigRational>() }),
1734 TAG_COMPLEX => ValueViewRef::Complex(unsafe { self.borrow_ref::<SemaComplex>() }),
1735 TAG_STRING => ValueViewRef::String(unsafe { self.borrow_ref::<String>() }),
1739 TAG_LIST => ValueViewRef::List(unsafe { self.borrow_ref::<Vec<Value>>() }),
1740 TAG_VECTOR => ValueViewRef::Vector(unsafe { self.borrow_ref::<Vec<Value>>() }),
1741 TAG_MAP => ValueViewRef::Map(unsafe { self.borrow_ref::<BTreeMap<Value, Value>>() }),
1742 TAG_HASHMAP => ValueViewRef::HashMap(unsafe {
1743 self.borrow_ref::<hashbrown::HashMap<Value, Value>>()
1744 }),
1745 TAG_LAMBDA => ValueViewRef::Lambda(unsafe { self.borrow_ref::<Lambda>() }),
1746 TAG_MACRO => ValueViewRef::Macro(unsafe { self.borrow_ref::<Macro>() }),
1747 TAG_NATIVE_FN => ValueViewRef::NativeFn(unsafe { self.borrow_ref::<NativeFn>() }),
1748 TAG_PROMPT => ValueViewRef::Prompt(unsafe { self.borrow_ref::<Prompt>() }),
1749 TAG_MESSAGE => ValueViewRef::Message(unsafe { self.borrow_ref::<Message>() }),
1750 TAG_CONVERSATION => {
1751 ValueViewRef::Conversation(unsafe { self.borrow_ref::<Conversation>() })
1752 }
1753 TAG_TOOL_DEF => ValueViewRef::ToolDef(unsafe { self.borrow_ref::<ToolDefinition>() }),
1754 TAG_AGENT => ValueViewRef::Agent(unsafe { self.borrow_ref::<Agent>() }),
1755 TAG_THUNK => ValueViewRef::Thunk(unsafe { self.borrow_ref::<Thunk>() }),
1756 TAG_RECORD => ValueViewRef::Record(unsafe { self.borrow_ref::<Record>() }),
1757 TAG_BYTEVECTOR => ValueViewRef::Bytevector(unsafe { self.borrow_ref::<Vec<u8>>() }),
1758 TAG_MULTIMETHOD => {
1759 ValueViewRef::MultiMethod(unsafe { self.borrow_ref::<MultiMethod>() })
1760 }
1761 TAG_STREAM => ValueViewRef::Stream(unsafe { self.borrow_ref::<StreamBox>() }),
1762 TAG_F64_ARRAY => ValueViewRef::F64Array(unsafe { self.borrow_ref::<Vec<f64>>() }),
1763 TAG_I64_ARRAY => ValueViewRef::I64Array(unsafe { self.borrow_ref::<Vec<i64>>() }),
1764 TAG_ASYNC_PROMISE => {
1765 ValueViewRef::AsyncPromise(unsafe { self.borrow_ref::<AsyncPromise>() })
1766 }
1767 TAG_CHANNEL => ValueViewRef::Channel(unsafe { self.borrow_ref::<Channel>() }),
1768 TAG_MUTABLE_ARRAY => {
1769 ValueViewRef::MutableArray(unsafe { self.borrow_ref::<MutableArray>() })
1770 }
1771 TAG_MUTABLE_CELL => {
1772 ValueViewRef::MutableCell(unsafe { self.borrow_ref::<MutableCell>() })
1773 }
1774 _ => unreachable!("invalid NaN-boxed tag: {}", tag),
1775 }
1776 }
1777
1778 pub(crate) fn heap_ptr(&self) -> Option<*const u8> {
1781 if !is_boxed(self.0) {
1782 return None;
1783 }
1784 if is_immediate_tag(get_tag(self.0)) {
1785 return None;
1786 }
1787 Some(payload_to_ptr(get_payload(self.0)))
1788 }
1789
1790 pub(crate) fn heap_strong_count(&self) -> Option<usize> {
1794 let ptr = self.heap_ptr()?;
1795 Some(unsafe { rc_strong_cell(ptr).get() })
1800 }
1801
1802 #[inline(always)]
1805 pub fn type_name(&self) -> &'static str {
1806 if !is_boxed(self.0) {
1807 return "float";
1808 }
1809 match get_tag(self.0) {
1810 TAG_NIL => "nil",
1811 TAG_FALSE | TAG_TRUE => "bool",
1812 TAG_INT_SMALL | TAG_INT_BIG | TAG_BIGINT => "int",
1813 TAG_RATIONAL => "rational",
1814 TAG_COMPLEX => "complex",
1815 TAG_CHAR => "char",
1816 TAG_SYMBOL => "symbol",
1817 TAG_KEYWORD => "keyword",
1818 TAG_STRING => "string",
1819 TAG_LIST => "list",
1820 TAG_VECTOR => "vector",
1821 TAG_MAP => "map",
1822 TAG_HASHMAP => "hashmap",
1823 TAG_LAMBDA => "lambda",
1824 TAG_MACRO => "macro",
1825 TAG_NATIVE_FN => "native-fn",
1826 TAG_PROMPT => "prompt",
1827 TAG_MESSAGE => "message",
1828 TAG_CONVERSATION => "conversation",
1829 TAG_TOOL_DEF => "tool",
1830 TAG_AGENT => "agent",
1831 TAG_THUNK => "promise",
1832 TAG_RECORD => "record",
1833 TAG_BYTEVECTOR => "bytevector",
1834 TAG_MULTIMETHOD => "multimethod",
1835 TAG_STREAM => "stream",
1836 TAG_F64_ARRAY => "f64-array",
1837 TAG_I64_ARRAY => "i64-array",
1838 TAG_ASYNC_PROMISE => "async-promise",
1839 TAG_CHANNEL => "channel",
1840 TAG_MUTABLE_ARRAY => "mutable-array",
1841 TAG_MUTABLE_CELL => "mutable-cell",
1842 _ => "unknown",
1843 }
1844 }
1845
1846 #[inline(always)]
1847 pub fn is_nil(&self) -> bool {
1848 self.0 == Value::NIL.0
1849 }
1850
1851 #[inline(always)]
1852 pub fn is_truthy(&self) -> bool {
1853 self.0 != Value::NIL.0 && self.0 != Value::FALSE.0
1854 }
1855
1856 #[inline(always)]
1857 pub fn is_falsy(&self) -> bool {
1858 !self.is_truthy()
1859 }
1860
1861 #[inline(always)]
1862 pub fn is_bool(&self) -> bool {
1863 self.0 == Value::TRUE.0 || self.0 == Value::FALSE.0
1864 }
1865
1866 #[inline(always)]
1867 pub fn is_int(&self) -> bool {
1868 is_boxed(self.0) && matches!(get_tag(self.0), TAG_INT_SMALL | TAG_INT_BIG)
1869 }
1870
1871 #[inline(always)]
1872 pub fn is_bigint(&self) -> bool {
1873 is_boxed(self.0) && get_tag(self.0) == TAG_BIGINT
1874 }
1875
1876 #[inline(always)]
1877 pub fn is_rational(&self) -> bool {
1878 is_boxed(self.0) && get_tag(self.0) == TAG_RATIONAL
1879 }
1880
1881 #[inline(always)]
1882 pub fn is_complex(&self) -> bool {
1883 is_boxed(self.0) && get_tag(self.0) == TAG_COMPLEX
1884 }
1885
1886 #[inline(always)]
1887 pub fn is_symbol(&self) -> bool {
1888 is_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL
1889 }
1890
1891 #[inline(always)]
1892 pub fn is_keyword(&self) -> bool {
1893 is_boxed(self.0) && get_tag(self.0) == TAG_KEYWORD
1894 }
1895
1896 #[inline(always)]
1897 pub fn is_string(&self) -> bool {
1898 is_boxed(self.0) && get_tag(self.0) == TAG_STRING
1899 }
1900
1901 #[inline(always)]
1902 pub fn is_list(&self) -> bool {
1903 is_boxed(self.0) && get_tag(self.0) == TAG_LIST
1904 }
1905
1906 #[inline(always)]
1907 pub fn is_pair(&self) -> bool {
1908 if let Some(items) = self.as_list() {
1909 !items.is_empty()
1910 } else {
1911 false
1912 }
1913 }
1914
1915 #[inline(always)]
1916 pub fn is_vector(&self) -> bool {
1917 is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR
1918 }
1919
1920 #[inline(always)]
1921 pub fn is_map(&self) -> bool {
1922 is_boxed(self.0) && matches!(get_tag(self.0), TAG_MAP | TAG_HASHMAP)
1923 }
1924
1925 #[inline(always)]
1926 pub fn is_lambda(&self) -> bool {
1927 is_boxed(self.0) && get_tag(self.0) == TAG_LAMBDA
1928 }
1929
1930 #[inline(always)]
1931 pub fn is_native_fn(&self) -> bool {
1932 is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN
1933 }
1934
1935 #[inline(always)]
1936 pub fn is_thunk(&self) -> bool {
1937 is_boxed(self.0) && get_tag(self.0) == TAG_THUNK
1938 }
1939
1940 #[inline(always)]
1941 pub fn is_async_promise(&self) -> bool {
1942 is_boxed(self.0) && get_tag(self.0) == TAG_ASYNC_PROMISE
1943 }
1944 #[inline(always)]
1945 pub fn is_channel(&self) -> bool {
1946 is_boxed(self.0) && get_tag(self.0) == TAG_CHANNEL
1947 }
1948
1949 #[inline(always)]
1950 pub fn is_record(&self) -> bool {
1951 is_boxed(self.0) && get_tag(self.0) == TAG_RECORD
1952 }
1953
1954 #[inline(always)]
1955 pub fn as_int(&self) -> Option<i64> {
1956 if !is_boxed(self.0) {
1957 return None;
1958 }
1959 match get_tag(self.0) {
1960 TAG_INT_SMALL => {
1961 let payload = get_payload(self.0);
1962 let val = if payload & INT_SIGN_BIT != 0 {
1963 (payload | !PAYLOAD_MASK) as i64
1964 } else {
1965 payload as i64
1966 };
1967 Some(val)
1968 }
1969 TAG_INT_BIG => Some(unsafe { *self.borrow_ref::<i64>() }),
1970 _ => None,
1971 }
1972 }
1973
1974 pub fn as_bigint(&self) -> Option<BigInt> {
1977 match self.view_ref() {
1978 ValueViewRef::Int(n) => Some(BigInt::from(n)),
1979 ValueViewRef::BigInt(n) => Some(n.clone()),
1980 _ => None,
1981 }
1982 }
1983
1984 pub fn as_rational(&self) -> Option<BigRational> {
1987 match self.view_ref() {
1988 ValueViewRef::Int(n) => Some(BigRational::from(BigInt::from(n))),
1989 ValueViewRef::BigInt(n) => Some(BigRational::from(n.clone())),
1990 ValueViewRef::Rational(r) => Some(r.clone()),
1991 _ => None,
1992 }
1993 }
1994
1995 pub fn as_number(&self) -> Option<SemaNumber> {
1998 match self.view_ref() {
1999 ValueViewRef::Int(n) => Some(SemaNumber::from_i64(n)),
2000 ValueViewRef::BigInt(n) => Some(SemaNumber::Integer(n.clone())),
2001 ValueViewRef::Rational(r) => Some(SemaNumber::Rational(r.clone())),
2002 ValueViewRef::Complex(c) => Some(SemaNumber::Complex(Box::new(c.clone()))),
2003 ValueViewRef::Float(f) => Some(SemaNumber::Real(f)),
2004 _ => None,
2005 }
2006 }
2007
2008 pub fn from_number(n: SemaNumber) -> Value {
2010 match n.normalize() {
2011 SemaNumber::Integer(big) => Value::from_bigint(big),
2012 SemaNumber::Rational(r) => Value::rational(r),
2013 SemaNumber::Real(f) => Value::float(f),
2014 SemaNumber::Complex(c) => Value::from_rc_ptr(TAG_COMPLEX, Rc::new(*c)),
2015 }
2016 }
2017
2018 pub fn as_complex(&self) -> Option<SemaComplex> {
2021 if let ValueViewRef::Complex(c) = self.view_ref() {
2022 Some(c.clone())
2023 } else {
2024 None
2025 }
2026 }
2027
2028 pub fn as_index(&self, name: &str) -> Result<usize, SemaError> {
2034 let n = self.as_int().ok_or_else(|| {
2035 SemaError::type_error("int", self.type_name())
2036 .with_hint(format!("{name}: argument must be an integer"))
2037 })?;
2038 if n < 0 {
2039 return Err(SemaError::eval(format!(
2040 "{name}: expected a non-negative integer, got {n}"
2041 ))
2042 .with_hint("pass 0 or a positive integer"));
2043 }
2044 Ok(n as usize)
2045 }
2046
2047 #[inline(always)]
2048 pub fn as_float(&self) -> Option<f64> {
2049 if !is_boxed(self.0) {
2050 return Some(f64::from_bits(self.0));
2051 }
2052 match get_tag(self.0) {
2053 TAG_INT_SMALL => {
2054 let payload = get_payload(self.0);
2055 let val = if payload & INT_SIGN_BIT != 0 {
2056 (payload | !PAYLOAD_MASK) as i64
2057 } else {
2058 payload as i64
2059 };
2060 Some(val as f64)
2061 }
2062 TAG_INT_BIG => Some(unsafe { *self.borrow_ref::<i64>() } as f64),
2063 _ => None,
2064 }
2065 }
2066
2067 #[inline(always)]
2068 pub fn as_bool(&self) -> Option<bool> {
2069 if self.0 == Value::TRUE.0 {
2070 Some(true)
2071 } else if self.0 == Value::FALSE.0 {
2072 Some(false)
2073 } else {
2074 None
2075 }
2076 }
2077
2078 #[inline(always)]
2079 pub fn as_str(&self) -> Option<&str> {
2080 if is_boxed(self.0) && get_tag(self.0) == TAG_STRING {
2081 Some(unsafe { self.borrow_ref::<String>() })
2082 } else {
2083 None
2084 }
2085 }
2086
2087 pub fn as_string_rc(&self) -> Option<Rc<String>> {
2088 if is_boxed(self.0) && get_tag(self.0) == TAG_STRING {
2089 Some(unsafe { self.get_rc::<String>() })
2090 } else {
2091 None
2092 }
2093 }
2094
2095 pub fn as_symbol(&self) -> Option<String> {
2096 self.as_symbol_spur().map(resolve)
2097 }
2098
2099 pub fn as_symbol_spur(&self) -> Option<Spur> {
2100 if is_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL {
2101 let payload = get_payload(self.0);
2102 Some(bits_to_spur(payload as u32))
2103 } else {
2104 None
2105 }
2106 }
2107
2108 pub fn as_keyword(&self) -> Option<String> {
2109 self.as_keyword_spur().map(resolve)
2110 }
2111
2112 pub fn as_keyword_spur(&self) -> Option<Spur> {
2113 if is_boxed(self.0) && get_tag(self.0) == TAG_KEYWORD {
2114 let payload = get_payload(self.0);
2115 Some(bits_to_spur(payload as u32))
2116 } else {
2117 None
2118 }
2119 }
2120
2121 pub fn as_char(&self) -> Option<char> {
2122 if is_boxed(self.0) && get_tag(self.0) == TAG_CHAR {
2123 let payload = get_payload(self.0);
2124 char::from_u32(payload as u32)
2125 } else {
2126 None
2127 }
2128 }
2129
2130 pub fn as_list(&self) -> Option<&[Value]> {
2131 if is_boxed(self.0) && get_tag(self.0) == TAG_LIST {
2132 Some(unsafe { self.borrow_ref::<Vec<Value>>() })
2133 } else {
2134 None
2135 }
2136 }
2137
2138 pub fn as_list_rc(&self) -> Option<Rc<Vec<Value>>> {
2139 if is_boxed(self.0) && get_tag(self.0) == TAG_LIST {
2140 Some(unsafe { self.get_rc::<Vec<Value>>() })
2141 } else {
2142 None
2143 }
2144 }
2145
2146 pub fn as_seq(&self) -> Option<&[Value]> {
2148 self.as_list().or_else(|| self.as_vector())
2149 }
2150
2151 pub fn as_vector(&self) -> Option<&[Value]> {
2152 if is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR {
2153 Some(unsafe { self.borrow_ref::<Vec<Value>>() })
2154 } else {
2155 None
2156 }
2157 }
2158
2159 pub fn as_vector_rc(&self) -> Option<Rc<Vec<Value>>> {
2160 if is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR {
2161 Some(unsafe { self.get_rc::<Vec<Value>>() })
2162 } else {
2163 None
2164 }
2165 }
2166
2167 pub fn as_map_rc(&self) -> Option<Rc<BTreeMap<Value, Value>>> {
2168 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
2169 Some(unsafe { self.get_rc::<BTreeMap<Value, Value>>() })
2170 } else {
2171 None
2172 }
2173 }
2174
2175 pub fn as_hashmap_rc(&self) -> Option<Rc<hashbrown::HashMap<Value, Value>>> {
2176 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
2177 Some(unsafe { self.get_rc::<hashbrown::HashMap<Value, Value>>() })
2178 } else {
2179 None
2180 }
2181 }
2182
2183 #[inline(always)]
2185 pub fn as_hashmap_ref(&self) -> Option<&hashbrown::HashMap<Value, Value>> {
2186 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
2187 Some(unsafe { self.borrow_ref::<hashbrown::HashMap<Value, Value>>() })
2188 } else {
2189 None
2190 }
2191 }
2192
2193 #[inline(always)]
2195 pub fn as_map_ref(&self) -> Option<&BTreeMap<Value, Value>> {
2196 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
2197 Some(unsafe { self.borrow_ref::<BTreeMap<Value, Value>>() })
2198 } else {
2199 None
2200 }
2201 }
2202
2203 #[inline(always)]
2207 pub fn with_hashmap_mut_if_unique<R>(
2208 &self,
2209 f: impl FnOnce(&mut hashbrown::HashMap<Value, Value>) -> R,
2210 ) -> Option<R> {
2211 if !is_boxed(self.0) || get_tag(self.0) != TAG_HASHMAP {
2212 return None;
2213 }
2214 let payload = get_payload(self.0);
2215 let ptr = payload_to_ptr(payload) as *const hashbrown::HashMap<Value, Value>;
2216 let rc = std::mem::ManuallyDrop::new(unsafe { Rc::from_raw(ptr) });
2217 if Rc::strong_count(&rc) != 1 {
2218 return None;
2219 }
2220 let ptr_mut = ptr as *mut hashbrown::HashMap<Value, Value>;
2222 Some(f(unsafe { &mut *ptr_mut }))
2223 }
2224
2225 #[inline(always)]
2228 pub fn with_map_mut_if_unique<R>(
2229 &self,
2230 f: impl FnOnce(&mut BTreeMap<Value, Value>) -> R,
2231 ) -> Option<R> {
2232 if !is_boxed(self.0) || get_tag(self.0) != TAG_MAP {
2233 return None;
2234 }
2235 let payload = get_payload(self.0);
2236 let ptr = payload_to_ptr(payload) as *const BTreeMap<Value, Value>;
2237 let rc = std::mem::ManuallyDrop::new(unsafe { Rc::from_raw(ptr) });
2238 if Rc::strong_count(&rc) != 1 {
2239 return None;
2240 }
2241 let ptr_mut = ptr as *mut BTreeMap<Value, Value>;
2242 Some(f(unsafe { &mut *ptr_mut }))
2243 }
2244
2245 pub fn into_hashmap_rc(self) -> Result<Rc<hashbrown::HashMap<Value, Value>>, Value> {
2248 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
2249 let payload = get_payload(self.0);
2250 let ptr = payload_to_ptr(payload) as *const hashbrown::HashMap<Value, Value>;
2251 std::mem::forget(self);
2253 Ok(unsafe { Rc::from_raw(ptr) })
2254 } else {
2255 Err(self)
2256 }
2257 }
2258
2259 pub fn into_map_rc(self) -> Result<Rc<BTreeMap<Value, Value>>, Value> {
2262 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
2263 let payload = get_payload(self.0);
2264 let ptr = payload_to_ptr(payload) as *const BTreeMap<Value, Value>;
2265 std::mem::forget(self);
2266 Ok(unsafe { Rc::from_raw(ptr) })
2267 } else {
2268 Err(self)
2269 }
2270 }
2271
2272 pub fn as_lambda_rc(&self) -> Option<Rc<Lambda>> {
2273 if is_boxed(self.0) && get_tag(self.0) == TAG_LAMBDA {
2274 Some(unsafe { self.get_rc::<Lambda>() })
2275 } else {
2276 None
2277 }
2278 }
2279
2280 pub fn as_macro_rc(&self) -> Option<Rc<Macro>> {
2281 if is_boxed(self.0) && get_tag(self.0) == TAG_MACRO {
2282 Some(unsafe { self.get_rc::<Macro>() })
2283 } else {
2284 None
2285 }
2286 }
2287
2288 pub fn as_native_fn_rc(&self) -> Option<Rc<NativeFn>> {
2289 if is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN {
2290 Some(unsafe { self.get_rc::<NativeFn>() })
2291 } else {
2292 None
2293 }
2294 }
2295
2296 pub fn as_thunk_rc(&self) -> Option<Rc<Thunk>> {
2297 if is_boxed(self.0) && get_tag(self.0) == TAG_THUNK {
2298 Some(unsafe { self.get_rc::<Thunk>() })
2299 } else {
2300 None
2301 }
2302 }
2303
2304 pub fn as_record(&self) -> Option<&Record> {
2305 if is_boxed(self.0) && get_tag(self.0) == TAG_RECORD {
2306 Some(unsafe { self.borrow_ref::<Record>() })
2307 } else {
2308 None
2309 }
2310 }
2311
2312 pub fn as_record_rc(&self) -> Option<Rc<Record>> {
2313 if is_boxed(self.0) && get_tag(self.0) == TAG_RECORD {
2314 Some(unsafe { self.get_rc::<Record>() })
2315 } else {
2316 None
2317 }
2318 }
2319
2320 pub fn as_bytevector(&self) -> Option<&[u8]> {
2321 if is_boxed(self.0) && get_tag(self.0) == TAG_BYTEVECTOR {
2322 Some(unsafe { self.borrow_ref::<Vec<u8>>() })
2323 } else {
2324 None
2325 }
2326 }
2327
2328 pub fn as_bytevector_rc(&self) -> Option<Rc<Vec<u8>>> {
2329 if is_boxed(self.0) && get_tag(self.0) == TAG_BYTEVECTOR {
2330 Some(unsafe { self.get_rc::<Vec<u8>>() })
2331 } else {
2332 None
2333 }
2334 }
2335
2336 pub fn as_f64_array(&self) -> Option<&[f64]> {
2337 if is_boxed(self.0) && get_tag(self.0) == TAG_F64_ARRAY {
2338 Some(unsafe { self.borrow_ref::<Vec<f64>>() })
2339 } else {
2340 None
2341 }
2342 }
2343
2344 pub fn as_f64_array_rc(&self) -> Option<Rc<Vec<f64>>> {
2345 if is_boxed(self.0) && get_tag(self.0) == TAG_F64_ARRAY {
2346 Some(unsafe { self.get_rc::<Vec<f64>>() })
2347 } else {
2348 None
2349 }
2350 }
2351
2352 pub fn as_i64_array(&self) -> Option<&[i64]> {
2353 if is_boxed(self.0) && get_tag(self.0) == TAG_I64_ARRAY {
2354 Some(unsafe { self.borrow_ref::<Vec<i64>>() })
2355 } else {
2356 None
2357 }
2358 }
2359
2360 pub fn as_i64_array_rc(&self) -> Option<Rc<Vec<i64>>> {
2361 if is_boxed(self.0) && get_tag(self.0) == TAG_I64_ARRAY {
2362 Some(unsafe { self.get_rc::<Vec<i64>>() })
2363 } else {
2364 None
2365 }
2366 }
2367
2368 pub fn as_stream(&self) -> Option<&StreamBox> {
2369 if is_boxed(self.0) && get_tag(self.0) == TAG_STREAM {
2370 Some(unsafe { self.borrow_ref::<StreamBox>() })
2371 } else {
2372 None
2373 }
2374 }
2375
2376 pub fn as_stream_rc(&self) -> Option<Rc<StreamBox>> {
2377 if is_boxed(self.0) && get_tag(self.0) == TAG_STREAM {
2378 Some(unsafe { self.get_rc::<StreamBox>() })
2379 } else {
2380 None
2381 }
2382 }
2383
2384 pub fn as_prompt_rc(&self) -> Option<Rc<Prompt>> {
2385 if is_boxed(self.0) && get_tag(self.0) == TAG_PROMPT {
2386 Some(unsafe { self.get_rc::<Prompt>() })
2387 } else {
2388 None
2389 }
2390 }
2391
2392 pub fn as_message_rc(&self) -> Option<Rc<Message>> {
2393 if is_boxed(self.0) && get_tag(self.0) == TAG_MESSAGE {
2394 Some(unsafe { self.get_rc::<Message>() })
2395 } else {
2396 None
2397 }
2398 }
2399
2400 pub fn as_conversation_rc(&self) -> Option<Rc<Conversation>> {
2401 if is_boxed(self.0) && get_tag(self.0) == TAG_CONVERSATION {
2402 Some(unsafe { self.get_rc::<Conversation>() })
2403 } else {
2404 None
2405 }
2406 }
2407
2408 pub fn as_tool_def_rc(&self) -> Option<Rc<ToolDefinition>> {
2409 if is_boxed(self.0) && get_tag(self.0) == TAG_TOOL_DEF {
2410 Some(unsafe { self.get_rc::<ToolDefinition>() })
2411 } else {
2412 None
2413 }
2414 }
2415
2416 pub fn as_agent_rc(&self) -> Option<Rc<Agent>> {
2417 if is_boxed(self.0) && get_tag(self.0) == TAG_AGENT {
2418 Some(unsafe { self.get_rc::<Agent>() })
2419 } else {
2420 None
2421 }
2422 }
2423
2424 pub fn as_multimethod_rc(&self) -> Option<Rc<MultiMethod>> {
2425 if is_boxed(self.0) && get_tag(self.0) == TAG_MULTIMETHOD {
2426 Some(unsafe { self.get_rc::<MultiMethod>() })
2427 } else {
2428 None
2429 }
2430 }
2431
2432 pub fn as_mutable_array(&self) -> Option<&MutableArray> {
2433 if is_boxed(self.0) && get_tag(self.0) == TAG_MUTABLE_ARRAY {
2434 Some(unsafe { self.borrow_ref::<MutableArray>() })
2435 } else {
2436 None
2437 }
2438 }
2439
2440 pub fn as_mutable_array_rc(&self) -> Option<Rc<MutableArray>> {
2441 if is_boxed(self.0) && get_tag(self.0) == TAG_MUTABLE_ARRAY {
2442 Some(unsafe { self.get_rc::<MutableArray>() })
2443 } else {
2444 None
2445 }
2446 }
2447
2448 pub fn as_mutable_cell(&self) -> Option<&MutableCell> {
2449 if is_boxed(self.0) && get_tag(self.0) == TAG_MUTABLE_CELL {
2450 Some(unsafe { self.borrow_ref::<MutableCell>() })
2451 } else {
2452 None
2453 }
2454 }
2455
2456 pub fn as_mutable_cell_rc(&self) -> Option<Rc<MutableCell>> {
2457 if is_boxed(self.0) && get_tag(self.0) == TAG_MUTABLE_CELL {
2458 Some(unsafe { self.get_rc::<MutableCell>() })
2459 } else {
2460 None
2461 }
2462 }
2463
2464 #[inline(always)]
2468 pub fn is_mutable_container(&self) -> bool {
2469 is_boxed(self.0) && matches!(get_tag(self.0), TAG_MUTABLE_ARRAY | TAG_MUTABLE_CELL)
2470 }
2471
2472 pub fn contains_mutable_container(&self) -> bool {
2480 fn scan(v: &Value, pending: &mut Vec<Value>) -> bool {
2481 if v.is_mutable_container() {
2482 return true;
2483 }
2484 match v.view_ref() {
2485 ValueViewRef::List(items) | ValueViewRef::Vector(items) => {
2486 pending.extend(items.iter().cloned());
2487 }
2488 ValueViewRef::Map(m) => {
2489 for (k, val) in m.iter() {
2490 pending.push(k.clone());
2491 pending.push(val.clone());
2492 }
2493 }
2494 ValueViewRef::HashMap(m) => {
2495 for (k, val) in m.iter() {
2496 pending.push(k.clone());
2497 pending.push(val.clone());
2498 }
2499 }
2500 ValueViewRef::Record(r) => pending.extend(r.fields.iter().cloned()),
2501 _ => {}
2502 }
2503 false
2504 }
2505 let mut pending = Vec::new();
2506 if scan(self, &mut pending) {
2507 return true;
2508 }
2509 while let Some(v) = pending.pop() {
2510 if scan(&v, &mut pending) {
2511 return true;
2512 }
2513 }
2514 false
2515 }
2516}
2517
2518const RC_HEADER: usize = 2 * std::mem::size_of::<usize>();
2536
2537#[inline(always)]
2544unsafe fn rc_strong_cell<'a>(ptr: *const u8) -> &'a Cell<usize> {
2545 &*(ptr.sub(RC_HEADER) as *const Cell<usize>)
2546}
2547
2548const DROP_DIRECT_RECURSION_BUDGET: u32 = 64;
2575
2576#[inline(never)]
2592unsafe fn drop_last_heap_ref(tag: u64, ptr: *const u8) {
2593 free_heap_value(tag, ptr, 0);
2594}
2595
2596unsafe fn free_heap_value(tag: u64, ptr: *const u8, depth: u32) {
2607 if depth > DROP_DIRECT_RECURSION_BUDGET {
2608 let mut worklist: Vec<Value> = Vec::new();
2628 free_heap_payload(tag, ptr, &mut worklist);
2629 drain_drop_worklist(worklist);
2630 return;
2631 }
2632 if !take_owned_children(tag, ptr, |child| drop_child_value(child, depth + 1)) {
2633 drop_leaf_heap_ref(tag, ptr);
2634 }
2635}
2636
2637unsafe fn take_owned_children(tag: u64, ptr: *const u8, mut sink: impl FnMut(Value)) -> bool {
2655 match tag {
2656 TAG_LIST | TAG_VECTOR => {
2657 let items = Rc::into_inner(Rc::from_raw(ptr as *const Vec<Value>))
2658 .expect("caller guarantees the last strong reference");
2659 for value in items {
2660 sink(value);
2661 }
2662 }
2663 TAG_MAP => {
2664 let map = Rc::into_inner(Rc::from_raw(ptr as *const BTreeMap<Value, Value>))
2665 .expect("caller guarantees the last strong reference");
2666 for (k, v) in map {
2667 sink(k);
2668 sink(v);
2669 }
2670 }
2671 TAG_HASHMAP => {
2672 let map = Rc::into_inner(Rc::from_raw(ptr as *const hashbrown::HashMap<Value, Value>))
2673 .expect("caller guarantees the last strong reference");
2674 for (k, v) in map {
2675 sink(k);
2676 sink(v);
2677 }
2678 }
2679 _ => return false,
2680 }
2681 true
2682}
2683
2684#[inline]
2690unsafe fn drop_child_value(value: Value, depth: u32) {
2691 drop_value_ref(value, |tag, ptr| free_heap_value(tag, ptr, depth));
2692}
2693
2694#[inline]
2701fn is_immediate_tag(tag: u64) -> bool {
2702 tag < TAG_INT_BIG
2703}
2704
2705#[inline]
2718unsafe fn drop_value_ref(value: Value, on_last: impl FnOnce(u64, *const u8)) {
2719 if is_boxed(value.0) {
2720 let tag = get_tag(value.0);
2721 if !is_immediate_tag(tag) {
2722 let ptr = payload_to_ptr(get_payload(value.0));
2723 let strong = rc_strong_cell(ptr);
2724 match strong.get() {
2725 1 => on_last(tag, ptr),
2726 n => strong.set(n - 1),
2727 }
2728 }
2729 }
2730 std::mem::forget(value);
2731}
2732
2733unsafe fn drain_drop_worklist(mut worklist: Vec<Value>) {
2740 while let Some(value) = worklist.pop() {
2741 drop_value_ref(value, |tag, ptr| free_heap_payload(tag, ptr, &mut worklist));
2742 }
2743}
2744
2745unsafe fn free_heap_payload(tag: u64, ptr: *const u8, worklist: &mut Vec<Value>) {
2750 if !take_owned_children(tag, ptr, |child| worklist.push(child)) {
2751 drop_leaf_heap_ref(tag, ptr);
2752 }
2753}
2754
2755unsafe fn drop_leaf_heap_ref(tag: u64, ptr: *const u8) {
2759 match tag {
2760 TAG_INT_BIG => drop(Rc::from_raw(ptr as *const i64)),
2761 TAG_BIGINT => drop(Rc::from_raw(ptr as *const BigInt)),
2762 TAG_RATIONAL => drop(Rc::from_raw(ptr as *const BigRational)),
2763 TAG_COMPLEX => drop(Rc::from_raw(ptr as *const SemaComplex)),
2764 TAG_STRING => drop(Rc::from_raw(ptr as *const String)),
2765 TAG_LIST | TAG_VECTOR => drop(Rc::from_raw(ptr as *const Vec<Value>)),
2766 TAG_MAP => drop(Rc::from_raw(ptr as *const BTreeMap<Value, Value>)),
2767 TAG_HASHMAP => drop(Rc::from_raw(ptr as *const hashbrown::HashMap<Value, Value>)),
2768 TAG_LAMBDA => drop(Rc::from_raw(ptr as *const Lambda)),
2769 TAG_MACRO => drop(Rc::from_raw(ptr as *const Macro)),
2770 TAG_NATIVE_FN => drop(Rc::from_raw(ptr as *const NativeFn)),
2771 TAG_PROMPT => drop(Rc::from_raw(ptr as *const Prompt)),
2772 TAG_MESSAGE => drop(Rc::from_raw(ptr as *const Message)),
2773 TAG_CONVERSATION => drop(Rc::from_raw(ptr as *const Conversation)),
2774 TAG_TOOL_DEF => drop(Rc::from_raw(ptr as *const ToolDefinition)),
2775 TAG_AGENT => drop(Rc::from_raw(ptr as *const Agent)),
2776 TAG_THUNK => drop(Rc::from_raw(ptr as *const Thunk)),
2777 TAG_RECORD => drop(Rc::from_raw(ptr as *const Record)),
2778 TAG_BYTEVECTOR => drop(Rc::from_raw(ptr as *const Vec<u8>)),
2779 TAG_MULTIMETHOD => drop(Rc::from_raw(ptr as *const MultiMethod)),
2780 TAG_STREAM => drop(Rc::from_raw(ptr as *const StreamBox)),
2781 TAG_F64_ARRAY => drop(Rc::from_raw(ptr as *const Vec<f64>)),
2782 TAG_I64_ARRAY => drop(Rc::from_raw(ptr as *const Vec<i64>)),
2783 TAG_ASYNC_PROMISE => drop(Rc::from_raw(ptr as *const AsyncPromise)),
2784 TAG_CHANNEL => drop(Rc::from_raw(ptr as *const Channel)),
2785 TAG_MUTABLE_ARRAY => drop(Rc::from_raw(ptr as *const MutableArray)),
2786 TAG_MUTABLE_CELL => drop(Rc::from_raw(ptr as *const MutableCell)),
2787 _ => {} }
2789}
2790
2791impl Clone for Value {
2792 #[inline(always)]
2793 fn clone(&self) -> Self {
2794 if !is_boxed(self.0) {
2795 return Value(self.0);
2797 }
2798 let tag = get_tag(self.0);
2799 if is_immediate_tag(tag) {
2801 return Value(self.0);
2802 }
2803 debug_assert!(
2807 (TAG_INT_BIG..=TAG_MUTABLE_CELL).contains(&tag),
2808 "invalid heap tag in clone: {tag}"
2809 );
2810 let ptr = payload_to_ptr(get_payload(self.0));
2811 unsafe {
2814 let strong = rc_strong_cell(ptr);
2815 let n = strong.get().wrapping_add(1);
2816 if n == 0 {
2817 std::process::abort();
2819 }
2820 strong.set(n);
2821 }
2822 Value(self.0)
2823 }
2824}
2825
2826impl Drop for Value {
2829 #[inline(always)]
2830 fn drop(&mut self) {
2831 if !is_boxed(self.0) {
2832 return; }
2834 let tag = get_tag(self.0);
2835 if is_immediate_tag(tag) {
2837 return;
2838 }
2839 debug_assert!(
2842 (TAG_INT_BIG..=TAG_MUTABLE_CELL).contains(&tag),
2843 "invalid heap tag in drop: {tag}"
2844 );
2845 let ptr = payload_to_ptr(get_payload(self.0));
2846 unsafe {
2850 let strong = rc_strong_cell(ptr);
2851 match strong.get() {
2852 1 => drop_last_heap_ref(tag, ptr),
2853 n => strong.set(n - 1),
2854 }
2855 }
2856 }
2857}
2858
2859thread_local! {
2862 static CMP_IN_FLIGHT: RefCell<Vec<(usize, usize)>> = const { RefCell::new(Vec::new()) };
2871}
2872
2873fn with_cycle_guard<T>(a: usize, b: usize, on_cycle: T, body: impl FnOnce() -> T) -> T {
2878 let already_in_flight = CMP_IN_FLIGHT.with(|s| {
2879 let mut s = s.borrow_mut();
2880 if s.contains(&(a, b)) {
2881 true
2882 } else {
2883 s.push((a, b));
2884 false
2885 }
2886 });
2887 if already_in_flight {
2888 return on_cycle;
2889 }
2890 struct PopGuard;
2893 impl Drop for PopGuard {
2894 fn drop(&mut self) {
2895 CMP_IN_FLIGHT.with(|s| {
2896 s.borrow_mut().pop();
2897 });
2898 }
2899 }
2900 let _guard = PopGuard;
2901 body()
2902}
2903
2904impl PartialEq for Value {
2905 fn eq(&self, other: &Self) -> bool {
2906 if self.0 == other.0 {
2908 if !is_boxed(self.0) {
2912 let f = f64::from_bits(self.0);
2913 if f.is_nan() {
2915 return false;
2916 }
2917 return true;
2918 }
2919 return true;
2920 }
2921 match (self.view_ref(), other.view_ref()) {
2923 (ValueViewRef::Nil, ValueViewRef::Nil) => true,
2924 (ValueViewRef::Bool(a), ValueViewRef::Bool(b)) => a == b,
2925 (ValueViewRef::Int(a), ValueViewRef::Int(b)) => a == b,
2926 (ValueViewRef::BigInt(a), ValueViewRef::BigInt(b)) => a == b,
2927 (ValueViewRef::Rational(a), ValueViewRef::Rational(b)) => a == b,
2928 (ValueViewRef::Complex(a), ValueViewRef::Complex(b)) => a.re == b.re && a.im == b.im,
2929 (ValueViewRef::Float(a), ValueViewRef::Float(b)) => a == b,
2930 (ValueViewRef::String(a), ValueViewRef::String(b)) => a == b,
2931 (ValueViewRef::Symbol(a), ValueViewRef::Symbol(b)) => a == b,
2932 (ValueViewRef::Keyword(a), ValueViewRef::Keyword(b)) => a == b,
2933 (ValueViewRef::Char(a), ValueViewRef::Char(b)) => a == b,
2934 (ValueViewRef::List(a), ValueViewRef::List(b)) => a == b,
2935 (ValueViewRef::Vector(a), ValueViewRef::Vector(b)) => a == b,
2936 (ValueViewRef::Map(a), ValueViewRef::Map(b)) => a == b,
2937 (ValueViewRef::HashMap(a), ValueViewRef::HashMap(b)) => a == b,
2938 (ValueViewRef::Record(a), ValueViewRef::Record(b)) => {
2939 a.type_tag == b.type_tag && a.fields == b.fields
2940 }
2941 (ValueViewRef::Bytevector(a), ValueViewRef::Bytevector(b)) => a == b,
2942 (ValueViewRef::F64Array(a), ValueViewRef::F64Array(b)) => {
2943 a.len() == b.len()
2944 && a.iter()
2945 .zip(b.iter())
2946 .all(|(x, y)| x.to_bits() == y.to_bits())
2947 }
2948 (ValueViewRef::I64Array(a), ValueViewRef::I64Array(b)) => a == b,
2949 (ValueViewRef::Stream(a), ValueViewRef::Stream(b)) => std::ptr::eq(a, b),
2950 (ValueViewRef::AsyncPromise(a), ValueViewRef::AsyncPromise(b)) => std::ptr::eq(a, b),
2951 (ValueViewRef::Channel(a), ValueViewRef::Channel(b)) => std::ptr::eq(a, b),
2952 (ValueViewRef::MutableArray(a), ValueViewRef::MutableArray(b)) => with_cycle_guard(
2957 a as *const MutableArray as usize,
2958 b as *const MutableArray as usize,
2959 true,
2960 || match (a.items.try_borrow(), b.items.try_borrow()) {
2961 (Ok(x), Ok(y)) => *x == *y,
2962 _ => false,
2963 },
2964 ),
2965 (ValueViewRef::MutableCell(a), ValueViewRef::MutableCell(b)) => with_cycle_guard(
2966 a as *const MutableCell as usize,
2967 b as *const MutableCell as usize,
2968 true,
2969 || match (a.value.try_borrow(), b.value.try_borrow()) {
2970 (Ok(x), Ok(y)) => *x == *y,
2971 _ => false,
2972 },
2973 ),
2974 _ => false,
2975 }
2976 }
2977}
2978
2979impl Eq for Value {}
2980
2981impl Hash for Value {
2984 fn hash<H: Hasher>(&self, state: &mut H) {
2985 match self.view_ref() {
2986 ValueViewRef::Nil => 0u8.hash(state),
2987 ValueViewRef::Bool(b) => {
2988 1u8.hash(state);
2989 b.hash(state);
2990 }
2991 ValueViewRef::Int(n) => {
2992 2u8.hash(state);
2993 n.hash(state);
2994 }
2995 ValueViewRef::BigInt(n) => {
2996 30u8.hash(state);
2997 n.hash(state);
2998 }
2999 ValueViewRef::Rational(r) => {
3000 31u8.hash(state);
3001 r.hash(state);
3002 }
3003 ValueViewRef::Complex(c) => {
3004 32u8.hash(state);
3005 c.re.hash(state);
3006 c.im.hash(state);
3007 }
3008 ValueViewRef::Float(f) => {
3009 3u8.hash(state);
3010 let bits = if f == 0.0 { 0u64 } else { f.to_bits() };
3011 bits.hash(state);
3012 }
3013 ValueViewRef::String(s) => {
3014 4u8.hash(state);
3015 s.hash(state);
3016 }
3017 ValueViewRef::Symbol(s) => {
3018 5u8.hash(state);
3019 s.hash(state);
3020 }
3021 ValueViewRef::Keyword(s) => {
3022 6u8.hash(state);
3023 s.hash(state);
3024 }
3025 ValueViewRef::Char(c) => {
3026 7u8.hash(state);
3027 c.hash(state);
3028 }
3029 ValueViewRef::List(l) => {
3030 8u8.hash(state);
3031 l.hash(state);
3032 }
3033 ValueViewRef::Vector(v) => {
3034 9u8.hash(state);
3035 v.hash(state);
3036 }
3037 ValueViewRef::Record(r) => {
3038 10u8.hash(state);
3039 r.type_tag.hash(state);
3040 r.fields.hash(state);
3041 }
3042 ValueViewRef::Bytevector(bv) => {
3043 11u8.hash(state);
3044 bv.hash(state);
3045 }
3046 ValueViewRef::F64Array(arr) => {
3047 26u8.hash(state);
3048 for v in arr.iter() {
3049 v.to_bits().hash(state);
3050 }
3051 }
3052 ValueViewRef::I64Array(arr) => {
3053 27u8.hash(state);
3054 arr.hash(state);
3055 }
3056 ValueViewRef::Stream(s) => {
3057 25u8.hash(state);
3058 (s as *const _ as usize).hash(state);
3059 }
3060 ValueViewRef::AsyncPromise(p) => {
3061 28u8.hash(state);
3062 (p as *const _ as usize).hash(state);
3063 }
3064 ValueViewRef::Channel(c) => {
3065 29u8.hash(state);
3066 (c as *const _ as usize).hash(state);
3067 }
3068 ValueViewRef::MutableArray(_) => 33u8.hash(state),
3074 ValueViewRef::MutableCell(_) => 34u8.hash(state),
3075 _ => {}
3076 }
3077 }
3078}
3079
3080impl PartialOrd for Value {
3083 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3084 Some(self.cmp(other))
3085 }
3086}
3087
3088impl Ord for Value {
3089 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3090 use std::cmp::Ordering;
3091 fn type_order(v: &Value) -> u8 {
3092 match v.view_ref() {
3093 ValueViewRef::Nil => 0,
3094 ValueViewRef::Bool(_) => 1,
3095 ValueViewRef::Int(_) | ValueViewRef::BigInt(_) => 2,
3096 ValueViewRef::Float(_) => 3,
3097 ValueViewRef::Char(_) => 4,
3098 ValueViewRef::String(_) => 5,
3099 ValueViewRef::Symbol(_) => 6,
3100 ValueViewRef::Keyword(_) => 7,
3101 ValueViewRef::List(_) => 8,
3102 ValueViewRef::Vector(_) => 9,
3103 ValueViewRef::Map(_) => 10,
3104 ValueViewRef::HashMap(_) => 11,
3105 ValueViewRef::Record(_) => 12,
3106 ValueViewRef::Bytevector(_) => 13,
3107 ValueViewRef::F64Array(_) => 14,
3108 ValueViewRef::I64Array(_) => 15,
3109 ValueViewRef::Stream(_) => 16,
3110 ValueViewRef::Rational(_) => 18,
3111 ValueViewRef::Complex(_) => 19,
3112 ValueViewRef::MutableArray(_) => 20,
3115 ValueViewRef::MutableCell(_) => 21,
3116 _ => 17,
3117 }
3118 }
3119 match (self.view_ref(), other.view_ref()) {
3120 (ValueViewRef::Nil, ValueViewRef::Nil) => Ordering::Equal,
3121 (ValueViewRef::Bool(a), ValueViewRef::Bool(b)) => a.cmp(&b),
3122 (ValueViewRef::Int(a), ValueViewRef::Int(b)) => a.cmp(&b),
3123 (ValueViewRef::BigInt(a), ValueViewRef::BigInt(b)) => a.cmp(b),
3124 (ValueViewRef::Int(a), ValueViewRef::BigInt(b)) => BigInt::from(a).cmp(b),
3125 (ValueViewRef::BigInt(a), ValueViewRef::Int(b)) => a.cmp(&BigInt::from(b)),
3126 (ValueViewRef::Rational(a), ValueViewRef::Rational(b)) => a.cmp(b),
3127 (ValueViewRef::Complex(a), ValueViewRef::Complex(b)) => {
3128 a.re.cmp(&b.re).then_with(|| a.im.cmp(&b.im))
3129 }
3130 (ValueViewRef::Float(a), ValueViewRef::Float(b)) => {
3131 let norm = |f: f64| if f == 0.0 { 0.0 } else { f };
3136 norm(a).total_cmp(&norm(b))
3137 }
3138 (ValueViewRef::String(a), ValueViewRef::String(b)) => a.cmp(b),
3139 (ValueViewRef::Symbol(a), ValueViewRef::Symbol(b)) => compare_spurs(a, b),
3140 (ValueViewRef::Keyword(a), ValueViewRef::Keyword(b)) => compare_spurs(a, b),
3141 (ValueViewRef::Char(a), ValueViewRef::Char(b)) => a.cmp(&b),
3142 (ValueViewRef::List(a), ValueViewRef::List(b)) => a.cmp(b),
3143 (ValueViewRef::Vector(a), ValueViewRef::Vector(b)) => a.cmp(b),
3144 (ValueViewRef::Record(a), ValueViewRef::Record(b)) => {
3145 compare_spurs(a.type_tag, b.type_tag).then_with(|| a.fields.cmp(&b.fields))
3146 }
3147 (ValueViewRef::Bytevector(a), ValueViewRef::Bytevector(b)) => a.cmp(b),
3148 (ValueViewRef::I64Array(a), ValueViewRef::I64Array(b)) => a.cmp(b),
3149 (ValueViewRef::F64Array(a), ValueViewRef::F64Array(b)) => a
3150 .iter()
3151 .zip(b.iter())
3152 .map(|(x, y)| x.total_cmp(y))
3153 .find(|o| *o != std::cmp::Ordering::Equal)
3154 .unwrap_or_else(|| a.len().cmp(&b.len())),
3155 (ValueViewRef::MutableArray(a), ValueViewRef::MutableArray(b)) => {
3165 let pa = a as *const MutableArray as usize;
3166 let pb = b as *const MutableArray as usize;
3167 with_cycle_guard(pa, pb, Ordering::Equal, || {
3168 match (a.items.try_borrow(), b.items.try_borrow()) {
3169 (Ok(x), Ok(y)) => (*x).cmp(&*y),
3170 _ => pa.cmp(&pb),
3171 }
3172 })
3173 }
3174 (ValueViewRef::MutableCell(a), ValueViewRef::MutableCell(b)) => {
3175 let pa = a as *const MutableCell as usize;
3176 let pb = b as *const MutableCell as usize;
3177 with_cycle_guard(pa, pb, Ordering::Equal, || {
3178 match (a.value.try_borrow(), b.value.try_borrow()) {
3179 (Ok(x), Ok(y)) => (*x).cmp(&y),
3180 _ => pa.cmp(&pb),
3181 }
3182 })
3183 }
3184 _ => type_order(self).cmp(&type_order(other)),
3185 }
3186 }
3187}
3188
3189fn truncate(s: &str, max: usize) -> String {
3192 let mut iter = s.chars();
3193 let prefix: String = iter.by_ref().take(max).collect();
3194 if iter.next().is_none() {
3195 prefix
3196 } else {
3197 format!("{prefix}...")
3198 }
3199}
3200
3201impl fmt::Display for Value {
3202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3203 crate::stack::maybe_grow(|| match self.view_ref() {
3206 ValueViewRef::Nil => write!(f, "nil"),
3207 ValueViewRef::Bool(true) => write!(f, "#t"),
3208 ValueViewRef::Bool(false) => write!(f, "#f"),
3209 ValueViewRef::Int(n) => write!(f, "{n}"),
3210 ValueViewRef::BigInt(n) => write!(f, "{n}"),
3211 ValueViewRef::Rational(r) => write!(f, "{}/{}", r.numer(), r.denom()),
3212 ValueViewRef::Complex(c) => {
3213 write!(f, "{}", SemaNumber::Complex(Box::new((*c).clone())))
3214 }
3215 ValueViewRef::Float(n) => {
3216 if n.fract() == 0.0 {
3217 write!(f, "{n:.1}")
3218 } else {
3219 write!(f, "{n}")
3220 }
3221 }
3222 ValueViewRef::String(s) => {
3223 write!(f, "\"")?;
3224 for c in s.chars() {
3225 match c {
3226 '"' => write!(f, "\\\"")?,
3227 '\\' => write!(f, "\\\\")?,
3228 '\n' => write!(f, "\\n")?,
3229 '\t' => write!(f, "\\t")?,
3230 '\r' => write!(f, "\\r")?,
3231 c => write!(f, "{c}")?,
3232 }
3233 }
3234 write!(f, "\"")
3235 }
3236 ValueViewRef::Symbol(s) => with_resolved(s, |name| write!(f, "{name}")),
3237 ValueViewRef::Keyword(s) => with_resolved(s, |name| write!(f, ":{name}")),
3238 ValueViewRef::Char(c) => match c {
3239 ' ' => write!(f, "#\\space"),
3240 '\n' => write!(f, "#\\newline"),
3241 '\t' => write!(f, "#\\tab"),
3242 '\r' => write!(f, "#\\return"),
3243 '\0' => write!(f, "#\\nul"),
3244 _ => write!(f, "#\\{c}"),
3245 },
3246 ValueViewRef::List(items) => {
3247 write!(f, "(")?;
3248 for (i, item) in items.iter().enumerate() {
3249 if i > 0 {
3250 write!(f, " ")?;
3251 }
3252 write!(f, "{item}")?;
3253 }
3254 write!(f, ")")
3255 }
3256 ValueViewRef::Vector(items) => {
3257 write!(f, "[")?;
3258 for (i, item) in items.iter().enumerate() {
3259 if i > 0 {
3260 write!(f, " ")?;
3261 }
3262 write!(f, "{item}")?;
3263 }
3264 write!(f, "]")
3265 }
3266 ValueViewRef::Map(map) => {
3267 write!(f, "{{")?;
3268 for (i, (k, v)) in map.iter().enumerate() {
3269 if i > 0 {
3270 write!(f, " ")?;
3271 }
3272 write!(f, "{k} {v}")?;
3273 }
3274 write!(f, "}}")
3275 }
3276 ValueViewRef::HashMap(map) => {
3277 let mut entries: Vec<_> = map.iter().collect();
3278 entries.sort_by_key(|(k1, _)| *k1);
3279 write!(f, "{{")?;
3280 for (i, (k, v)) in entries.iter().enumerate() {
3281 if i > 0 {
3282 write!(f, " ")?;
3283 }
3284 write!(f, "{k} {v}")?;
3285 }
3286 write!(f, "}}")
3287 }
3288 ValueViewRef::Lambda(l) => {
3289 if let Some(name) = &l.name {
3290 with_resolved(*name, |n| write!(f, "<lambda {n}>"))
3291 } else {
3292 write!(f, "<lambda>")
3293 }
3294 }
3295 ValueViewRef::Macro(m) => with_resolved(m.name, |n| write!(f, "<macro {n}>")),
3296 ValueViewRef::NativeFn(n) => write!(f, "<native-fn {}>", n.name),
3297 ValueViewRef::Prompt(p) => write!(f, "<prompt {} messages>", p.messages.len()),
3298 ValueViewRef::Message(m) => {
3299 write!(f, "<message {} \"{}\">", m.role, truncate(&m.content, 40))
3300 }
3301 ValueViewRef::Conversation(c) => {
3302 write!(f, "<conversation {} messages>", c.messages.len())
3303 }
3304 ValueViewRef::ToolDef(t) => write!(f, "<tool {}>", t.name),
3305 ValueViewRef::Agent(a) => write!(f, "<agent {}>", a.name),
3306 ValueViewRef::Thunk(t) => {
3307 if t.forced.borrow().is_some() {
3308 write!(f, "<promise (forced)>")
3309 } else {
3310 write!(f, "<promise>")
3311 }
3312 }
3313 ValueViewRef::Record(r) => {
3314 with_resolved(r.type_tag, |tag| write!(f, "#<record {tag}"))?;
3315 for field in &r.fields {
3316 write!(f, " {field}")?;
3317 }
3318 write!(f, ">")
3319 }
3320 ValueViewRef::Bytevector(bv) => {
3321 write!(f, "#u8(")?;
3322 for (i, byte) in bv.iter().enumerate() {
3323 if i > 0 {
3324 write!(f, " ")?;
3325 }
3326 write!(f, "{byte}")?;
3327 }
3328 write!(f, ")")
3329 }
3330 ValueViewRef::F64Array(arr) => {
3331 write!(f, "#f64(")?;
3332 for (i, v) in arr.iter().enumerate() {
3333 if i > 0 {
3334 write!(f, " ")?;
3335 }
3336 write!(f, "{v}")?;
3337 }
3338 write!(f, ")")
3339 }
3340 ValueViewRef::I64Array(arr) => {
3341 write!(f, "#i64(")?;
3342 for (i, v) in arr.iter().enumerate() {
3343 if i > 0 {
3344 write!(f, " ")?;
3345 }
3346 write!(f, "{v}")?;
3347 }
3348 write!(f, ")")
3349 }
3350 ValueViewRef::MultiMethod(m) => {
3351 with_resolved(m.name, |n| write!(f, "<multimethod {n}>"))
3352 }
3353 ValueViewRef::Stream(s) => write!(f, "<stream:{}>", s.stream_type()),
3354 ValueViewRef::AsyncPromise(_) => write!(f, "<async-promise>"),
3355 ValueViewRef::Channel(_) => write!(f, "<channel>"),
3356 ValueViewRef::MutableArray(a) => match a.items.try_borrow() {
3360 Ok(items) => write!(f, "<mutable-array {}>", items.len()),
3361 Err(_) => write!(f, "<mutable-array (borrowed)>"),
3362 },
3363 ValueViewRef::MutableCell(_) => write!(f, "<mutable-cell>"),
3364 })
3365 }
3366}
3367
3368pub fn pretty_print(value: &Value, max_width: usize) -> String {
3374 let mut buf = String::new();
3375 pp_value(value, 0, max_width, &mut buf);
3376 buf
3377}
3378
3379fn pp_value(value: &Value, indent: usize, max_width: usize, buf: &mut String) {
3383 let compact = format!("{value}");
3384 let remaining = max_width.saturating_sub(indent);
3385 if compact.len() <= remaining {
3386 buf.push_str(&compact);
3387 return;
3388 }
3389
3390 crate::stack::maybe_grow(|| match value.view_ref() {
3393 ValueViewRef::List(items) => {
3394 pp_seq(items.iter(), '(', ')', indent, max_width, buf);
3395 }
3396 ValueViewRef::Vector(items) => {
3397 pp_seq(items.iter(), '[', ']', indent, max_width, buf);
3398 }
3399 ValueViewRef::Map(map) => {
3400 pp_map(
3401 map.iter().map(|(k, v)| (k.clone(), v.clone())),
3402 indent,
3403 max_width,
3404 buf,
3405 );
3406 }
3407 ValueViewRef::HashMap(map) => {
3408 let mut entries: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
3409 entries.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
3410 pp_map(entries.into_iter(), indent, max_width, buf);
3411 }
3412 _ => buf.push_str(&compact),
3413 })
3414}
3415
3416fn pp_seq<'a>(
3418 items: impl Iterator<Item = &'a Value>,
3419 open: char,
3420 close: char,
3421 indent: usize,
3422 max_width: usize,
3423 buf: &mut String,
3424) {
3425 buf.push(open);
3426 let child_indent = indent + 1;
3427 let pad = " ".repeat(child_indent);
3428 for (i, item) in items.enumerate() {
3429 if i > 0 {
3430 buf.push('\n');
3431 buf.push_str(&pad);
3432 }
3433 pp_value(item, child_indent, max_width, buf);
3434 }
3435 buf.push(close);
3436}
3437
3438fn pp_map(
3440 entries: impl Iterator<Item = (Value, Value)>,
3441 indent: usize,
3442 max_width: usize,
3443 buf: &mut String,
3444) {
3445 buf.push('{');
3446 let child_indent = indent + 1;
3447 let pad = " ".repeat(child_indent);
3448 for (i, (k, v)) in entries.enumerate() {
3449 if i > 0 {
3450 buf.push('\n');
3451 buf.push_str(&pad);
3452 }
3453 let key_str = format!("{k}");
3455 buf.push_str(&key_str);
3456
3457 let inline_indent = child_indent + key_str.len() + 1;
3459 let compact_val = format!("{v}");
3460 let remaining = max_width.saturating_sub(inline_indent);
3461
3462 if compact_val.len() <= remaining {
3463 buf.push(' ');
3465 buf.push_str(&compact_val);
3466 } else if is_compound(&v) {
3467 let nested_indent = child_indent + 2;
3469 let nested_pad = " ".repeat(nested_indent);
3470 buf.push('\n');
3471 buf.push_str(&nested_pad);
3472 pp_value(&v, nested_indent, max_width, buf);
3473 } else {
3474 buf.push(' ');
3476 buf.push_str(&compact_val);
3477 }
3478 }
3479 buf.push('}');
3480}
3481
3482fn is_compound(value: &Value) -> bool {
3484 matches!(
3485 value.view(),
3486 ValueView::List(_) | ValueView::Vector(_) | ValueView::Map(_) | ValueView::HashMap(_)
3487 )
3488}
3489
3490impl fmt::Debug for Value {
3493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3494 match self.view() {
3495 ValueView::Nil => write!(f, "Nil"),
3496 ValueView::Bool(b) => write!(f, "Bool({b})"),
3497 ValueView::Int(n) => write!(f, "Int({n})"),
3498 ValueView::BigInt(n) => write!(f, "Int({n})"),
3499 ValueView::Rational(r) => write!(f, "Rational({}/{})", r.numer(), r.denom()),
3500 ValueView::Complex(c) => write!(
3501 f,
3502 "Complex({})",
3503 SemaNumber::Complex(Box::new((*c).clone()))
3504 ),
3505 ValueView::Float(n) => write!(f, "Float({n})"),
3506 ValueView::String(s) => write!(f, "String({:?})", &**s),
3507 ValueView::Symbol(s) => write!(f, "Symbol({})", resolve(s)),
3508 ValueView::Keyword(s) => write!(f, "Keyword({})", resolve(s)),
3509 ValueView::Char(c) => write!(f, "Char({c:?})"),
3510 ValueView::List(items) => write!(f, "List({items:?})"),
3511 ValueView::Vector(items) => write!(f, "Vector({items:?})"),
3512 ValueView::Map(map) => write!(f, "Map({map:?})"),
3513 ValueView::HashMap(map) => write!(f, "HashMap({map:?})"),
3514 ValueView::Lambda(l) => write!(f, "{l:?}"),
3515 ValueView::Macro(m) => write!(f, "{m:?}"),
3516 ValueView::NativeFn(n) => write!(f, "{n:?}"),
3517 ValueView::Prompt(p) => write!(f, "{p:?}"),
3518 ValueView::Message(m) => write!(f, "{m:?}"),
3519 ValueView::Conversation(c) => write!(f, "{c:?}"),
3520 ValueView::ToolDef(t) => write!(f, "{t:?}"),
3521 ValueView::Agent(a) => write!(f, "{a:?}"),
3522 ValueView::Thunk(t) => write!(f, "{t:?}"),
3523 ValueView::Record(r) => write!(f, "{r:?}"),
3524 ValueView::Bytevector(bv) => write!(f, "Bytevector({bv:?})"),
3525 ValueView::F64Array(arr) => write!(f, "F64Array({arr:?})"),
3526 ValueView::I64Array(arr) => write!(f, "I64Array({arr:?})"),
3527 ValueView::MultiMethod(m) => write!(f, "{m:?}"),
3528 ValueView::Stream(s) => write!(f, "Stream({:?})", s.stream_type()),
3529 ValueView::AsyncPromise(p) => write!(f, "{p:?}"),
3530 ValueView::Channel(c) => write!(f, "{c:?}"),
3531 ValueView::MutableArray(a) => write!(f, "{a:?}"),
3532 ValueView::MutableCell(c) => write!(f, "{c:?}"),
3533 }
3534 }
3535}
3536
3537#[derive(Debug, Clone)]
3541pub struct Env {
3542 pub bindings: Rc<RefCell<SpurMap<Spur, Value>>>,
3543 pub parent: Option<Rc<Env>>,
3544 pub version: Rc<Cell<u64>>,
3554}
3555
3556impl Env {
3557 pub fn new() -> Self {
3558 Env {
3559 bindings: Rc::new(RefCell::new(SpurMap::new())),
3560 parent: None,
3561 version: Rc::new(Cell::new(0)),
3562 }
3563 }
3564
3565 pub fn with_parent(parent: Rc<Env>) -> Self {
3566 Env {
3567 bindings: Rc::new(RefCell::new(SpurMap::new())),
3568 parent: Some(parent),
3569 version: Rc::new(Cell::new(0)),
3570 }
3571 }
3572
3573 pub fn bump_version(&self) {
3578 self.version.set(self.version.get().wrapping_add(1));
3579 }
3580
3581 pub fn get(&self, name: Spur) -> Option<Value> {
3582 if let Some(val) = self.bindings.borrow().get(&name) {
3583 Some(val.clone())
3584 } else if let Some(parent) = &self.parent {
3585 parent.get(name)
3586 } else {
3587 None
3588 }
3589 }
3590
3591 pub fn get_str(&self, name: &str) -> Option<Value> {
3592 self.get(intern(name))
3593 }
3594
3595 pub fn set(&self, name: Spur, val: Value) {
3596 self.bindings.borrow_mut().insert(name, val);
3597 self.bump_version();
3598 }
3599
3600 pub fn set_str(&self, name: &str, val: Value) {
3601 self.set(intern(name), val);
3602 }
3603
3604 pub fn update(&self, name: Spur, val: Value) {
3606 let mut bindings = self.bindings.borrow_mut();
3607 if let Some(entry) = bindings.get_mut(&name) {
3608 *entry = val;
3609 } else {
3610 bindings.insert(name, val);
3611 }
3612 drop(bindings);
3613 self.bump_version();
3614 }
3615
3616 pub fn take(&self, name: Spur) -> Option<Value> {
3618 let result = self.bindings.borrow_mut().remove(&name);
3619 if result.is_some() {
3620 self.bump_version();
3621 }
3622 result
3623 }
3624
3625 pub fn take_anywhere(&self, name: Spur) -> Option<Value> {
3627 if let Some(val) = self.bindings.borrow_mut().remove(&name) {
3628 self.bump_version();
3629 Some(val)
3630 } else if let Some(parent) = &self.parent {
3631 parent.take_anywhere(name)
3632 } else {
3633 None
3634 }
3635 }
3636
3637 pub fn set_existing(&self, name: Spur, val: Value) -> bool {
3639 let mut bindings = self.bindings.borrow_mut();
3640 if let Some(entry) = bindings.get_mut(&name) {
3641 *entry = val;
3642 drop(bindings);
3643 self.bump_version();
3644 true
3645 } else {
3646 drop(bindings);
3647 if let Some(parent) = &self.parent {
3648 parent.set_existing(name, val)
3649 } else {
3650 false
3651 }
3652 }
3653 }
3654
3655 pub fn all_names(&self) -> Vec<Spur> {
3657 let mut names: Vec<Spur> = self.bindings.borrow().keys().copied().collect();
3658 if let Some(parent) = &self.parent {
3659 names.extend(parent.all_names());
3660 }
3661 names.sort_unstable();
3662 names.dedup();
3663 names
3664 }
3665
3666 pub fn iter_bindings(&self, mut f: impl FnMut(Spur, &Value)) {
3668 let bindings = self.bindings.borrow();
3669 for (&spur, value) in bindings.iter() {
3670 f(spur, value);
3671 }
3672 }
3673
3674 pub fn get_local(&self, name: Spur) -> Option<Value> {
3676 self.bindings.borrow().get(&name).cloned()
3677 }
3678
3679 pub fn replace_bindings(&self, new_bindings: impl IntoIterator<Item = (Spur, Value)>) {
3682 let mut bindings = self.bindings.borrow_mut();
3683 bindings.clear();
3684 for (spur, value) in new_bindings {
3685 bindings.insert(spur, value);
3686 }
3687 drop(bindings);
3688 self.bump_version();
3689 }
3690}
3691
3692impl Default for Env {
3693 fn default() -> Self {
3694 Self::new()
3695 }
3696}
3697
3698#[cfg(test)]
3701#[allow(clippy::approx_constant)]
3702mod tests {
3703 use super::*;
3704
3705 #[test]
3706 fn test_size_of_value() {
3707 assert_eq!(std::mem::size_of::<Value>(), 8);
3708 }
3709
3710 #[test]
3711 fn rc_header_matches_std_layout() {
3712 let rc = Rc::new(String::from("layout probe"));
3716 let extra = Rc::clone(&rc);
3717 let raw = Rc::into_raw(rc);
3718 unsafe {
3719 assert_eq!(rc_strong_cell(raw as *const u8).get(), 2);
3720 drop(Rc::from_raw(raw));
3721 }
3722 drop(extra);
3723 }
3724
3725 #[test]
3726 fn drop_mixed_shallow_and_deep_nesting_does_not_double_free_or_leak() {
3727 fn deep_list(depth: usize, leaf: Value) -> Value {
3741 let mut v = leaf;
3742 for _ in 0..depth {
3743 v = Value::list(vec![v]);
3744 }
3745 v
3746 }
3747
3748 let shallow_leaf = Rc::new(String::from("shallow leaf"));
3749 let deep_leaf = Rc::new(String::from("deep leaf"));
3750 let shallow_weak = Rc::downgrade(&shallow_leaf);
3751 let deep_weak = Rc::downgrade(&deep_leaf);
3752
3753 let shallow_value = Value::string_from_rc(shallow_leaf);
3754 let deep_value = Value::string_from_rc(deep_leaf);
3755
3756 let inner_deep_list = deep_list(100, deep_value);
3758 let mut inner_map = BTreeMap::new();
3759 inner_map.insert(Value::keyword("payload"), inner_deep_list);
3760 let map_value = Value::map(inner_map);
3761
3762 let below = deep_list(2500, map_value);
3765 let spine_bottom = Value::list(vec![shallow_value, below]);
3766 let full = deep_list(2500, spine_bottom);
3767
3768 drop(full);
3769
3770 assert_eq!(
3771 shallow_weak.strong_count(),
3772 0,
3773 "shallow leaf freed exactly once"
3774 );
3775 assert_eq!(deep_weak.strong_count(), 0, "deep leaf freed exactly once");
3776 }
3777
3778 #[test]
3779 fn test_spur_bits_round_trip() {
3780 for s in ["x", "map", "string->symbol", "a-very-long-symbol-name", "λ"] {
3783 let spur = intern(s);
3784 assert_eq!(
3785 bits_to_spur(spur_to_bits(spur)),
3786 spur,
3787 "raw round-trip for {s:?}"
3788 );
3789
3790 let sym = Value::symbol_from_spur(spur);
3791 assert_eq!(sym.as_symbol_spur(), Some(spur), "symbol Value for {s:?}");
3792 assert_eq!(resolve(spur), s);
3793
3794 let kw = Value::keyword_from_spur(spur);
3795 assert_eq!(kw.as_keyword_spur(), Some(spur), "keyword Value for {s:?}");
3796 }
3797 }
3798
3799 #[test]
3800 fn as_index_rejects_negative() {
3801 let e = Value::int(-1).as_index("test").unwrap_err();
3802 assert!(
3803 matches!(e.inner(), SemaError::Eval(_)),
3804 "expected Eval error, got {e:?}"
3805 );
3806 assert!(e.to_string().contains("test"));
3807 }
3808
3809 #[test]
3810 fn as_index_accepts_non_negative() {
3811 assert_eq!(Value::int(0).as_index("test").unwrap(), 0);
3812 assert_eq!(Value::int(5).as_index("test").unwrap(), 5);
3813 }
3814
3815 #[test]
3816 fn as_index_rejects_non_int() {
3817 assert!(Value::string("x").as_index("test").is_err());
3818 }
3819
3820 #[test]
3821 fn test_nil() {
3822 let v = Value::nil();
3823 assert!(v.is_nil());
3824 assert!(!v.is_truthy());
3825 assert_eq!(v.type_name(), "nil");
3826 assert_eq!(format!("{v}"), "nil");
3827 }
3828
3829 #[test]
3830 fn test_bool() {
3831 let t = Value::bool(true);
3832 let f = Value::bool(false);
3833 assert!(t.is_truthy());
3834 assert!(!f.is_truthy());
3835 assert_eq!(t.as_bool(), Some(true));
3836 assert_eq!(f.as_bool(), Some(false));
3837 assert_eq!(format!("{t}"), "#t");
3838 assert_eq!(format!("{f}"), "#f");
3839 }
3840
3841 #[test]
3842 fn test_small_int() {
3843 let v = Value::int(42);
3844 assert_eq!(v.as_int(), Some(42));
3845 assert_eq!(v.type_name(), "int");
3846 assert_eq!(format!("{v}"), "42");
3847
3848 let neg = Value::int(-100);
3849 assert_eq!(neg.as_int(), Some(-100));
3850 assert_eq!(format!("{neg}"), "-100");
3851
3852 let zero = Value::int(0);
3853 assert_eq!(zero.as_int(), Some(0));
3854 }
3855
3856 #[test]
3857 fn test_small_int_boundaries() {
3858 let max = Value::int(SMALL_INT_MAX);
3859 assert_eq!(max.as_int(), Some(SMALL_INT_MAX));
3860
3861 let min = Value::int(SMALL_INT_MIN);
3862 assert_eq!(min.as_int(), Some(SMALL_INT_MIN));
3863 }
3864
3865 #[test]
3866 fn test_big_int() {
3867 let big = Value::int(i64::MAX);
3868 assert_eq!(big.as_int(), Some(i64::MAX));
3869 assert_eq!(big.type_name(), "int");
3870
3871 let big_neg = Value::int(i64::MIN);
3872 assert_eq!(big_neg.as_int(), Some(i64::MIN));
3873
3874 let just_over = Value::int(SMALL_INT_MAX + 1);
3876 assert_eq!(just_over.as_int(), Some(SMALL_INT_MAX + 1));
3877 }
3878
3879 #[test]
3880 fn test_float() {
3881 let v = Value::float(3.14);
3882 assert_eq!(v.as_float(), Some(3.14));
3883 assert_eq!(v.type_name(), "float");
3884
3885 let neg = Value::float(-0.5);
3886 assert_eq!(neg.as_float(), Some(-0.5));
3887
3888 let inf = Value::float(f64::INFINITY);
3889 assert_eq!(inf.as_float(), Some(f64::INFINITY));
3890
3891 let neg_inf = Value::float(f64::NEG_INFINITY);
3892 assert_eq!(neg_inf.as_float(), Some(f64::NEG_INFINITY));
3893 }
3894
3895 #[test]
3896 fn test_float_nan() {
3897 let nan = Value::float(f64::NAN);
3898 let f = nan.as_float().unwrap();
3899 assert!(f.is_nan());
3900 }
3901
3902 #[test]
3903 fn test_string() {
3904 let v = Value::string("hello");
3905 assert_eq!(v.as_str(), Some("hello"));
3906 assert_eq!(v.type_name(), "string");
3907 assert_eq!(format!("{v}"), "\"hello\"");
3908 }
3909
3910 #[test]
3911 fn test_symbol() {
3912 let v = Value::symbol("foo");
3913 assert!(v.as_symbol_spur().is_some());
3914 assert_eq!(v.as_symbol(), Some("foo".to_string()));
3915 assert_eq!(v.type_name(), "symbol");
3916 assert_eq!(format!("{v}"), "foo");
3917 }
3918
3919 #[test]
3920 fn test_keyword() {
3921 let v = Value::keyword("bar");
3922 assert!(v.as_keyword_spur().is_some());
3923 assert_eq!(v.as_keyword(), Some("bar".to_string()));
3924 assert_eq!(v.type_name(), "keyword");
3925 assert_eq!(format!("{v}"), ":bar");
3926 }
3927
3928 #[test]
3929 fn test_char() {
3930 let v = Value::char('λ');
3931 assert_eq!(v.as_char(), Some('λ'));
3932 assert_eq!(v.type_name(), "char");
3933 }
3934
3935 #[test]
3936 fn test_list() {
3937 let v = Value::list(vec![Value::int(1), Value::int(2), Value::int(3)]);
3938 assert_eq!(v.as_list().unwrap().len(), 3);
3939 assert_eq!(v.type_name(), "list");
3940 assert_eq!(format!("{v}"), "(1 2 3)");
3941 }
3942
3943 #[test]
3944 fn test_clone_immediate() {
3945 let v = Value::int(42);
3946 let v2 = v.clone();
3947 assert_eq!(v.as_int(), v2.as_int());
3948 }
3949
3950 #[test]
3951 fn test_clone_heap() {
3952 let v = Value::string("hello");
3953 let v2 = v.clone();
3954 assert_eq!(v.as_str(), v2.as_str());
3955 assert_eq!(format!("{v}"), format!("{v2}"));
3957 }
3958
3959 #[test]
3960 fn test_equality() {
3961 assert_eq!(Value::int(42), Value::int(42));
3962 assert_ne!(Value::int(42), Value::int(43));
3963 assert_eq!(Value::nil(), Value::nil());
3964 assert_eq!(Value::bool(true), Value::bool(true));
3965 assert_ne!(Value::bool(true), Value::bool(false));
3966 assert_eq!(Value::string("a"), Value::string("a"));
3967 assert_ne!(Value::string("a"), Value::string("b"));
3968 assert_eq!(Value::symbol("x"), Value::symbol("x"));
3969 }
3970
3971 #[test]
3972 fn record_field_names_do_not_affect_language_semantics() {
3973 use std::collections::hash_map::DefaultHasher;
3974 use std::hash::{Hash, Hasher};
3975
3976 let a = Value::record(Record {
3977 type_tag: intern("point"),
3978 field_names: vec![intern("x"), intern("y")],
3979 fields: vec![Value::int(1), Value::int(2)],
3980 });
3981 let b = Value::record(Record {
3982 type_tag: intern("point"),
3983 field_names: vec![intern("left"), intern("top")],
3984 fields: vec![Value::int(1), Value::int(2)],
3985 });
3986
3987 assert_eq!(a, b);
3988 assert_eq!(a.cmp(&b), std::cmp::Ordering::Equal);
3989 assert_eq!(format!("{a}"), "#<record point 1 2>");
3990 assert_eq!(format!("{a}"), format!("{b}"));
3991
3992 let mut a_hasher = DefaultHasher::new();
3993 a.hash(&mut a_hasher);
3994 let mut b_hasher = DefaultHasher::new();
3995 b.hash(&mut b_hasher);
3996 assert_eq!(a_hasher.finish(), b_hasher.finish());
3997 }
3998
3999 #[test]
4000 fn test_big_int_equality() {
4001 assert_eq!(Value::int(i64::MAX), Value::int(i64::MAX));
4002 assert_ne!(Value::int(i64::MAX), Value::int(i64::MIN));
4003 }
4004
4005 #[test]
4006 fn test_view_pattern_matching() {
4007 let v = Value::int(42);
4008 match v.view() {
4009 ValueView::Int(n) => assert_eq!(n, 42),
4010 _ => panic!("expected int"),
4011 }
4012
4013 let v = Value::string("hello");
4014 match v.view() {
4015 ValueView::String(s) => assert_eq!(&**s, "hello"),
4016 _ => panic!("expected string"),
4017 }
4018 }
4019
4020 #[test]
4021 fn test_env() {
4022 let env = Env::new();
4023 env.set_str("x", Value::int(42));
4024 assert_eq!(env.get_str("x"), Some(Value::int(42)));
4025 }
4026
4027 #[test]
4028 fn test_native_fn_simple() {
4029 let f = NativeFn::simple("add1", |args| Ok(args[0].clone()));
4030 let ctx = EvalContext::new();
4031 assert!((f.func)(&ctx, &[Value::int(42)]).is_ok());
4032 }
4033
4034 #[test]
4035 fn test_native_fn_with_ctx() {
4036 let f = NativeFn::with_ctx("get-depth", |ctx, _args| {
4037 Ok(Value::int(ctx.eval_depth.get() as i64))
4038 });
4039 let ctx = EvalContext::new();
4040 assert_eq!((f.func)(&ctx, &[]).unwrap(), Value::int(0));
4041 }
4042
4043 #[test]
4044 fn test_drop_doesnt_leak() {
4045 for _ in 0..10000 {
4047 let _ = Value::string("test");
4048 let _ = Value::list(vec![Value::int(1), Value::int(2)]);
4049 let _ = Value::int(i64::MAX); }
4051 }
4052
4053 #[test]
4054 fn test_is_truthy() {
4055 assert!(!Value::nil().is_truthy());
4056 assert!(!Value::bool(false).is_truthy());
4057 assert!(Value::bool(true).is_truthy());
4058 assert!(Value::int(0).is_truthy());
4059 assert!(Value::int(1).is_truthy());
4060 assert!(Value::string("").is_truthy());
4061 assert!(Value::list(vec![]).is_truthy());
4062 }
4063
4064 #[test]
4065 fn test_as_float_from_int() {
4066 assert_eq!(Value::int(42).as_float(), Some(42.0));
4067 assert_eq!(Value::float(3.14).as_float(), Some(3.14));
4068 }
4069
4070 #[test]
4071 fn test_next_gensym_unique() {
4072 let a = next_gensym("x");
4073 let b = next_gensym("x");
4074 let c = next_gensym("y");
4075 assert_ne!(a, b);
4076 assert_ne!(a, c);
4077 assert_ne!(b, c);
4078 assert!(a.starts_with("x__"));
4079 assert!(b.starts_with("x__"));
4080 assert!(c.starts_with("y__"));
4081 }
4082
4083 #[test]
4084 fn test_next_gensym_counter_does_not_panic_near_max() {
4085 GENSYM_COUNTER.with(|c| c.set(u64::MAX - 1));
4087 let a = next_gensym("z");
4088 assert!(a.contains(&(u64::MAX - 1).to_string()));
4089 let b = next_gensym("z");
4091 assert!(b.contains(&u64::MAX.to_string()));
4092 let c = next_gensym("z");
4094 assert!(c.contains("__0"));
4095 }
4096
4097 #[derive(Debug)]
4100 struct TestStream {
4101 data: RefCell<Vec<u8>>,
4102 readable: bool,
4103 writable: bool,
4104 }
4105
4106 impl TestStream {
4107 fn new(readable: bool, writable: bool) -> Self {
4108 TestStream {
4109 data: RefCell::new(Vec::new()),
4110 readable,
4111 writable,
4112 }
4113 }
4114 }
4115
4116 impl SemaStream for TestStream {
4117 fn read(&self, buf: &mut [u8]) -> Result<usize, SemaError> {
4118 let mut data = self.data.borrow_mut();
4119 let n = buf.len().min(data.len());
4120 buf[..n].copy_from_slice(&data[..n]);
4121 data.drain(..n);
4122 Ok(n)
4123 }
4124
4125 fn write(&self, data: &[u8]) -> Result<usize, SemaError> {
4126 self.data.borrow_mut().extend_from_slice(data);
4127 Ok(data.len())
4128 }
4129
4130 fn flush(&self) -> Result<(), SemaError> {
4131 Ok(())
4132 }
4133
4134 fn close(&self) -> Result<(), SemaError> {
4135 Ok(())
4136 }
4137
4138 fn available(&self) -> Result<bool, SemaError> {
4139 Ok(!self.data.borrow().is_empty())
4140 }
4141
4142 fn is_readable(&self) -> bool {
4143 self.readable
4144 }
4145
4146 fn is_writable(&self) -> bool {
4147 self.writable
4148 }
4149
4150 fn stream_type(&self) -> &'static str {
4151 "test"
4152 }
4153
4154 fn as_any(&self) -> &dyn std::any::Any {
4155 self
4156 }
4157 }
4158
4159 #[test]
4160 fn streambox_read_writes_data() {
4161 let sb = StreamBox::new(TestStream::new(true, true));
4162 sb.write(b"hello").unwrap();
4163 let mut buf = [0u8; 5];
4164 let n = sb.read(&mut buf).unwrap();
4165 assert_eq!(n, 5);
4166 assert_eq!(&buf, b"hello");
4167 }
4168
4169 #[test]
4170 fn streambox_close_prevents_read() {
4171 let sb = StreamBox::new(TestStream::new(true, true));
4172 sb.close().unwrap();
4173 let mut buf = [0u8; 5];
4174 let err = sb.read(&mut buf).unwrap_err();
4175 assert!(err.to_string().contains("closed"));
4176 }
4177
4178 #[test]
4179 fn streambox_close_prevents_write() {
4180 let sb = StreamBox::new(TestStream::new(true, true));
4181 sb.close().unwrap();
4182 let err = sb.write(b"data").unwrap_err();
4183 assert!(err.to_string().contains("closed"));
4184 }
4185
4186 #[test]
4187 fn streambox_close_prevents_flush() {
4188 let sb = StreamBox::new(TestStream::new(true, true));
4189 sb.close().unwrap();
4190 let err = sb.flush().unwrap_err();
4191 assert!(err.to_string().contains("closed"));
4192 }
4193
4194 #[test]
4195 fn streambox_double_close_is_noop() {
4196 let sb = StreamBox::new(TestStream::new(true, true));
4197 sb.close().unwrap();
4198 sb.close().unwrap(); }
4200
4201 #[test]
4202 fn streambox_is_closed() {
4203 let sb = StreamBox::new(TestStream::new(true, true));
4204 assert!(!sb.is_closed());
4205 sb.close().unwrap();
4206 assert!(sb.is_closed());
4207 }
4208
4209 #[test]
4210 fn streambox_is_readable() {
4211 let sb = StreamBox::new(TestStream::new(true, false));
4212 assert!(sb.is_readable());
4213 sb.close().unwrap();
4214 assert!(!sb.is_readable());
4215 }
4216
4217 #[test]
4218 fn streambox_is_writable() {
4219 let sb = StreamBox::new(TestStream::new(false, true));
4220 assert!(sb.is_writable());
4221 sb.close().unwrap();
4222 assert!(!sb.is_writable());
4223 }
4224
4225 #[test]
4226 fn streambox_available_when_closed() {
4227 let sb = StreamBox::new(TestStream::new(true, true));
4228 sb.close().unwrap();
4229 assert!(!sb.available().unwrap());
4230 }
4231
4232 #[test]
4233 fn streambox_stream_type() {
4234 let sb = StreamBox::new(TestStream::new(true, true));
4235 assert_eq!(sb.stream_type(), "test");
4236 }
4237
4238 #[test]
4239 fn bigint_roundtrip_and_normalize() {
4240 use num_bigint::BigInt;
4241 use std::str::FromStr;
4242 let big = BigInt::from_str("170141183460469231731687303715884105728").unwrap();
4244 let v = Value::from_bigint(big.clone());
4245 assert!(v.is_bigint());
4246 assert_eq!(v.to_string(), "170141183460469231731687303715884105728");
4247 assert_eq!(v.type_name(), "int");
4248 assert_eq!(v.as_int(), None); assert_eq!(v.as_bigint(), Some(big));
4250 let small = Value::from_bigint(BigInt::from(42));
4252 assert!(!small.is_bigint());
4253 assert_eq!(small.as_int(), Some(42));
4254 let v2 = v.clone();
4256 assert_eq!(v, v2);
4257 }
4258
4259 #[test]
4260 fn rational_roundtrip_and_normalize() {
4261 use num_bigint::BigInt;
4262 use num_rational::BigRational;
4263 use num_traits::One;
4264 let third = Value::rational(BigRational::new(BigInt::one(), BigInt::from(3)));
4265 assert!(third.is_rational());
4266 assert_eq!(third.to_string(), "1/3");
4267 assert_eq!(third.type_name(), "rational");
4268 let two = Value::rational(BigRational::new(BigInt::from(6), BigInt::from(3)));
4270 assert!(!two.is_rational());
4271 assert_eq!(two.as_int(), Some(2));
4272 assert_eq!(third.clone(), third);
4273 }
4274
4275 #[test]
4276 fn complex_roundtrip_and_normalize() {
4277 use crate::number::SemaNumber;
4278 let n = |v: i64| SemaNumber::from_i64(v);
4279 let c = Value::complex(n(3), n(4));
4280 assert!(c.is_complex());
4281 assert_eq!(c.to_string(), "3+4i");
4282 assert_eq!(c.type_name(), "complex");
4283 let comp = c.as_complex().unwrap();
4284 assert_eq!(comp.re, n(3));
4285 assert_eq!(comp.im, n(4));
4286 let c2 = c.clone();
4288 assert_eq!(c, c2);
4289 let real = Value::complex(n(5), n(0));
4291 assert!(!real.is_complex());
4292 assert_eq!(real.as_int(), Some(5));
4293 }
4294}