1use std::any::Any;
2use std::cell::{Cell, RefCell};
3use std::collections::BTreeMap;
4use std::fmt;
5use std::hash::{Hash, Hasher};
6use std::rc::Rc;
7
8use hashbrown::HashMap as SpurMap;
9use lasso::{Rodeo, Spur};
10
11use crate::error::SemaError;
12use crate::EvalContext;
13
14#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
18compile_error!("sema-core NaN-boxed Value requires a 64-bit platform (or wasm32)");
19
20thread_local! {
23 static INTERNER: RefCell<Rodeo> = RefCell::new(Rodeo::default());
24}
25
26pub fn intern(s: &str) -> Spur {
28 INTERNER.with(|r| r.borrow_mut().get_or_intern(s))
29}
30
31pub fn resolve(spur: Spur) -> String {
33 INTERNER.with(|r| r.borrow().resolve(&spur).to_string())
34}
35
36pub fn with_resolved<F, R>(spur: Spur, f: F) -> R
38where
39 F: FnOnce(&str) -> R,
40{
41 INTERNER.with(|r| {
42 let interner = r.borrow();
43 f(interner.resolve(&spur))
44 })
45}
46
47pub fn interner_stats() -> (usize, usize) {
49 INTERNER.with(|r| {
50 let interner = r.borrow();
51 let count = interner.len();
52 let bytes = count * 16; (count, bytes)
54 })
55}
56
57thread_local! {
60 static GENSYM_COUNTER: Cell<u64> = const { Cell::new(0) };
61}
62
63pub fn next_gensym(prefix: &str) -> String {
67 GENSYM_COUNTER.with(|c| {
68 let val = c.get();
69 c.set(val.wrapping_add(1));
70 format!("{prefix}__{val}")
71 })
72}
73
74pub fn compare_spurs(a: Spur, b: Spur) -> std::cmp::Ordering {
76 if a == b {
77 return std::cmp::Ordering::Equal;
78 }
79 INTERNER.with(|r| {
80 let interner = r.borrow();
81 interner.resolve(&a).cmp(interner.resolve(&b))
82 })
83}
84
85pub type NativeFnInner = dyn Fn(&EvalContext, &[Value]) -> Result<Value, SemaError>;
89
90pub struct NativeFn {
91 pub name: String,
92 pub func: Box<NativeFnInner>,
93 pub payload: Option<Rc<dyn Any>>,
94}
95
96impl NativeFn {
97 pub fn simple(
98 name: impl Into<String>,
99 f: impl Fn(&[Value]) -> Result<Value, SemaError> + 'static,
100 ) -> Self {
101 Self {
102 name: name.into(),
103 func: Box::new(move |_ctx, args| f(args)),
104 payload: None,
105 }
106 }
107
108 pub fn with_ctx(
109 name: impl Into<String>,
110 f: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
111 ) -> Self {
112 Self {
113 name: name.into(),
114 func: Box::new(f),
115 payload: None,
116 }
117 }
118
119 pub fn with_payload(
120 name: impl Into<String>,
121 payload: Rc<dyn Any>,
122 f: impl Fn(&EvalContext, &[Value]) -> Result<Value, SemaError> + 'static,
123 ) -> Self {
124 Self {
125 name: name.into(),
126 func: Box::new(f),
127 payload: Some(payload),
128 }
129 }
130}
131
132impl fmt::Debug for NativeFn {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "<native-fn {}>", self.name)
135 }
136}
137
138#[derive(Debug, Clone)]
140pub struct Lambda {
141 pub params: Vec<Spur>,
142 pub rest_param: Option<Spur>,
143 pub body: Vec<Value>,
144 pub env: Env,
145 pub name: Option<Spur>,
146}
147
148#[derive(Debug, Clone)]
150pub struct Macro {
151 pub params: Vec<Spur>,
152 pub rest_param: Option<Spur>,
153 pub body: Vec<Value>,
154 pub name: Spur,
155}
156
157pub struct Thunk {
159 pub body: Value,
160 pub forced: RefCell<Option<Value>>,
161}
162
163impl fmt::Debug for Thunk {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 if self.forced.borrow().is_some() {
166 write!(f, "<promise (forced)>")
167 } else {
168 write!(f, "<promise>")
169 }
170 }
171}
172
173impl Clone for Thunk {
174 fn clone(&self) -> Self {
175 Thunk {
176 body: self.body.clone(),
177 forced: RefCell::new(self.forced.borrow().clone()),
178 }
179 }
180}
181
182#[derive(Debug, Clone)]
184pub struct Record {
185 pub type_tag: Spur,
186 pub fields: Vec<Value>,
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
191pub enum Role {
192 System,
193 User,
194 Assistant,
195 Tool,
196}
197
198impl fmt::Display for Role {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 Role::System => write!(f, "system"),
202 Role::User => write!(f, "user"),
203 Role::Assistant => write!(f, "assistant"),
204 Role::Tool => write!(f, "tool"),
205 }
206 }
207}
208
209#[derive(Debug, Clone)]
211pub struct ImageAttachment {
212 pub data: String,
213 pub media_type: String,
214}
215
216#[derive(Debug, Clone)]
218pub struct Message {
219 pub role: Role,
220 pub content: String,
221 pub images: Vec<ImageAttachment>,
223}
224
225#[derive(Debug, Clone)]
227pub struct Prompt {
228 pub messages: Vec<Message>,
229}
230
231#[derive(Debug, Clone)]
233pub struct Conversation {
234 pub messages: Vec<Message>,
235 pub model: String,
236 pub metadata: BTreeMap<String, String>,
237}
238
239#[derive(Debug, Clone)]
241pub struct ToolDefinition {
242 pub name: String,
243 pub description: String,
244 pub parameters: Value,
245 pub handler: Value,
246}
247
248#[derive(Debug, Clone)]
250pub struct Agent {
251 pub name: String,
252 pub system: String,
253 pub tools: Vec<Value>,
254 pub max_turns: usize,
255 pub model: String,
256}
257
258pub struct MultiMethod {
261 pub name: Spur,
262 pub dispatch_fn: Value,
263 pub methods: RefCell<BTreeMap<Value, Value>>,
264 pub default: RefCell<Option<Value>>,
265}
266
267impl fmt::Debug for MultiMethod {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write!(f, "<multimethod {}>", resolve(self.name))
270 }
271}
272
273impl Clone for MultiMethod {
274 fn clone(&self) -> Self {
275 MultiMethod {
276 name: self.name,
277 dispatch_fn: self.dispatch_fn.clone(),
278 methods: RefCell::new(self.methods.borrow().clone()),
279 default: RefCell::new(self.default.borrow().clone()),
280 }
281 }
282}
283
284const BOX_MASK: u64 = 0xFFF8_0000_0000_0000;
296
297const PAYLOAD_MASK: u64 = (1u64 << 45) - 1; const INT_SIGN_BIT: u64 = 1u64 << 44;
302
303const TAG_MASK_6BIT: u64 = 0x3F;
305
306const CANONICAL_NAN: u64 = 0x7FF8_0000_0000_0000;
308
309const TAG_NIL: u64 = 0;
311const TAG_FALSE: u64 = 1;
312const TAG_TRUE: u64 = 2;
313const TAG_INT_SMALL: u64 = 3;
314const TAG_CHAR: u64 = 4;
315const TAG_SYMBOL: u64 = 5;
316const TAG_KEYWORD: u64 = 6;
317const TAG_INT_BIG: u64 = 7;
318const TAG_STRING: u64 = 8;
319const TAG_LIST: u64 = 9;
320const TAG_VECTOR: u64 = 10;
321const TAG_MAP: u64 = 11;
322const TAG_HASHMAP: u64 = 12;
323const TAG_LAMBDA: u64 = 13;
324const TAG_MACRO: u64 = 14;
325pub const TAG_NATIVE_FN: u64 = 15;
326const TAG_PROMPT: u64 = 16;
327const TAG_MESSAGE: u64 = 17;
328const TAG_CONVERSATION: u64 = 18;
329const TAG_TOOL_DEF: u64 = 19;
330const TAG_AGENT: u64 = 20;
331const TAG_THUNK: u64 = 21;
332const TAG_RECORD: u64 = 22;
333const TAG_BYTEVECTOR: u64 = 23;
334const TAG_MULTIMETHOD: u64 = 24;
335
336const SMALL_INT_MIN: i64 = -(1i64 << 44);
338const SMALL_INT_MAX: i64 = (1i64 << 44) - 1;
339
340pub const NAN_TAG_MASK: u64 = BOX_MASK | (TAG_MASK_6BIT << 45); pub const NAN_INT_SMALL_PATTERN: u64 = BOX_MASK | (TAG_INT_SMALL << 45);
347
348pub const NAN_PAYLOAD_MASK: u64 = PAYLOAD_MASK;
350
351pub const NAN_INT_SIGN_BIT: u64 = INT_SIGN_BIT;
353
354pub const NAN_PAYLOAD_BITS: u32 = 45;
356
357#[inline(always)]
360fn make_boxed(tag: u64, payload: u64) -> u64 {
361 BOX_MASK | (tag << 45) | (payload & PAYLOAD_MASK)
362}
363
364#[inline(always)]
365fn is_boxed(bits: u64) -> bool {
366 (bits & BOX_MASK) == BOX_MASK
367}
368
369#[inline(always)]
370fn get_tag(bits: u64) -> u64 {
371 (bits >> 45) & TAG_MASK_6BIT
372}
373
374#[inline(always)]
375fn get_payload(bits: u64) -> u64 {
376 bits & PAYLOAD_MASK
377}
378
379#[inline(always)]
380fn ptr_to_payload(ptr: *const u8) -> u64 {
381 let raw = ptr as u64;
382 debug_assert!(raw & 0x7 == 0, "pointer not 8-byte aligned: 0x{:x}", raw);
383 debug_assert!(
384 raw >> 48 == 0,
385 "pointer exceeds 48-bit VA space: 0x{:x}",
386 raw
387 );
388 raw >> 3
389}
390
391#[inline(always)]
392fn payload_to_ptr(payload: u64) -> *const u8 {
393 (payload << 3) as *const u8
394}
395
396pub enum ValueView {
401 Nil,
402 Bool(bool),
403 Int(i64),
404 Float(f64),
405 String(Rc<String>),
406 Symbol(Spur),
407 Keyword(Spur),
408 Char(char),
409 List(Rc<Vec<Value>>),
410 Vector(Rc<Vec<Value>>),
411 Map(Rc<BTreeMap<Value, Value>>),
412 HashMap(Rc<hashbrown::HashMap<Value, Value>>),
413 Lambda(Rc<Lambda>),
414 Macro(Rc<Macro>),
415 NativeFn(Rc<NativeFn>),
416 Prompt(Rc<Prompt>),
417 Message(Rc<Message>),
418 Conversation(Rc<Conversation>),
419 ToolDef(Rc<ToolDefinition>),
420 Agent(Rc<Agent>),
421 Thunk(Rc<Thunk>),
422 Record(Rc<Record>),
423 Bytevector(Rc<Vec<u8>>),
424 MultiMethod(Rc<MultiMethod>),
425}
426
427#[repr(transparent)]
433pub struct Value(u64);
434
435impl Value {
438 pub const NIL: Value = Value(make_boxed_const(TAG_NIL, 0));
441 pub const TRUE: Value = Value(make_boxed_const(TAG_TRUE, 0));
442 pub const FALSE: Value = Value(make_boxed_const(TAG_FALSE, 0));
443
444 #[inline(always)]
445 pub fn nil() -> Value {
446 Value::NIL
447 }
448
449 #[inline(always)]
450 pub fn bool(b: bool) -> Value {
451 if b {
452 Value::TRUE
453 } else {
454 Value::FALSE
455 }
456 }
457
458 #[inline(always)]
459 pub fn int(n: i64) -> Value {
460 if (SMALL_INT_MIN..=SMALL_INT_MAX).contains(&n) {
461 let payload = (n as u64) & PAYLOAD_MASK;
463 Value(make_boxed(TAG_INT_SMALL, payload))
464 } else {
465 let rc = Rc::new(n);
467 let ptr = Rc::into_raw(rc) as *const u8;
468 Value(make_boxed(TAG_INT_BIG, ptr_to_payload(ptr)))
469 }
470 }
471
472 #[inline(always)]
473 pub fn float(f: f64) -> Value {
474 let bits = f.to_bits();
475 if f.is_nan() {
476 Value(CANONICAL_NAN)
478 } else {
479 debug_assert!(
486 !is_boxed(bits),
487 "non-NaN float collides with boxed pattern: {:?} = 0x{:016x}",
488 f,
489 bits
490 );
491 Value(bits)
492 }
493 }
494
495 #[inline(always)]
496 pub fn char(c: char) -> Value {
497 Value(make_boxed(TAG_CHAR, c as u64))
498 }
499
500 #[inline(always)]
501 pub fn symbol_from_spur(spur: Spur) -> Value {
502 let bits: u32 = unsafe { std::mem::transmute(spur) };
503 Value(make_boxed(TAG_SYMBOL, bits as u64))
504 }
505
506 pub fn symbol(s: &str) -> Value {
507 Value::symbol_from_spur(intern(s))
508 }
509
510 #[inline(always)]
511 pub fn keyword_from_spur(spur: Spur) -> Value {
512 let bits: u32 = unsafe { std::mem::transmute(spur) };
513 Value(make_boxed(TAG_KEYWORD, bits as u64))
514 }
515
516 pub fn keyword(s: &str) -> Value {
517 Value::keyword_from_spur(intern(s))
518 }
519
520 fn from_rc_ptr<T>(tag: u64, rc: Rc<T>) -> Value {
523 let ptr = Rc::into_raw(rc) as *const u8;
524 Value(make_boxed(tag, ptr_to_payload(ptr)))
525 }
526
527 pub fn string(s: &str) -> Value {
528 Value::from_rc_ptr(TAG_STRING, Rc::new(s.to_string()))
529 }
530
531 pub fn string_from_rc(rc: Rc<String>) -> Value {
532 Value::from_rc_ptr(TAG_STRING, rc)
533 }
534
535 pub fn list(v: Vec<Value>) -> Value {
536 Value::from_rc_ptr(TAG_LIST, Rc::new(v))
537 }
538
539 pub fn list_from_rc(rc: Rc<Vec<Value>>) -> Value {
540 Value::from_rc_ptr(TAG_LIST, rc)
541 }
542
543 pub fn vector(v: Vec<Value>) -> Value {
544 Value::from_rc_ptr(TAG_VECTOR, Rc::new(v))
545 }
546
547 pub fn vector_from_rc(rc: Rc<Vec<Value>>) -> Value {
548 Value::from_rc_ptr(TAG_VECTOR, rc)
549 }
550
551 pub fn map(m: BTreeMap<Value, Value>) -> Value {
552 Value::from_rc_ptr(TAG_MAP, Rc::new(m))
553 }
554
555 pub fn map_from_rc(rc: Rc<BTreeMap<Value, Value>>) -> Value {
556 Value::from_rc_ptr(TAG_MAP, rc)
557 }
558
559 pub fn hashmap(entries: Vec<(Value, Value)>) -> Value {
560 let map: hashbrown::HashMap<Value, Value> = entries.into_iter().collect();
561 Value::from_rc_ptr(TAG_HASHMAP, Rc::new(map))
562 }
563
564 pub fn hashmap_from_rc(rc: Rc<hashbrown::HashMap<Value, Value>>) -> Value {
565 Value::from_rc_ptr(TAG_HASHMAP, rc)
566 }
567
568 pub fn lambda(l: Lambda) -> Value {
569 Value::from_rc_ptr(TAG_LAMBDA, Rc::new(l))
570 }
571
572 pub fn lambda_from_rc(rc: Rc<Lambda>) -> Value {
573 Value::from_rc_ptr(TAG_LAMBDA, rc)
574 }
575
576 pub fn macro_val(m: Macro) -> Value {
577 Value::from_rc_ptr(TAG_MACRO, Rc::new(m))
578 }
579
580 pub fn macro_from_rc(rc: Rc<Macro>) -> Value {
581 Value::from_rc_ptr(TAG_MACRO, rc)
582 }
583
584 pub fn native_fn(f: NativeFn) -> Value {
585 Value::from_rc_ptr(TAG_NATIVE_FN, Rc::new(f))
586 }
587
588 pub fn native_fn_from_rc(rc: Rc<NativeFn>) -> Value {
589 Value::from_rc_ptr(TAG_NATIVE_FN, rc)
590 }
591
592 pub fn prompt(p: Prompt) -> Value {
593 Value::from_rc_ptr(TAG_PROMPT, Rc::new(p))
594 }
595
596 pub fn prompt_from_rc(rc: Rc<Prompt>) -> Value {
597 Value::from_rc_ptr(TAG_PROMPT, rc)
598 }
599
600 pub fn message(m: Message) -> Value {
601 Value::from_rc_ptr(TAG_MESSAGE, Rc::new(m))
602 }
603
604 pub fn message_from_rc(rc: Rc<Message>) -> Value {
605 Value::from_rc_ptr(TAG_MESSAGE, rc)
606 }
607
608 pub fn conversation(c: Conversation) -> Value {
609 Value::from_rc_ptr(TAG_CONVERSATION, Rc::new(c))
610 }
611
612 pub fn conversation_from_rc(rc: Rc<Conversation>) -> Value {
613 Value::from_rc_ptr(TAG_CONVERSATION, rc)
614 }
615
616 pub fn tool_def(t: ToolDefinition) -> Value {
617 Value::from_rc_ptr(TAG_TOOL_DEF, Rc::new(t))
618 }
619
620 pub fn tool_def_from_rc(rc: Rc<ToolDefinition>) -> Value {
621 Value::from_rc_ptr(TAG_TOOL_DEF, rc)
622 }
623
624 pub fn agent(a: Agent) -> Value {
625 Value::from_rc_ptr(TAG_AGENT, Rc::new(a))
626 }
627
628 pub fn agent_from_rc(rc: Rc<Agent>) -> Value {
629 Value::from_rc_ptr(TAG_AGENT, rc)
630 }
631
632 pub fn thunk(t: Thunk) -> Value {
633 Value::from_rc_ptr(TAG_THUNK, Rc::new(t))
634 }
635
636 pub fn thunk_from_rc(rc: Rc<Thunk>) -> Value {
637 Value::from_rc_ptr(TAG_THUNK, rc)
638 }
639
640 pub fn record(r: Record) -> Value {
641 Value::from_rc_ptr(TAG_RECORD, Rc::new(r))
642 }
643
644 pub fn record_from_rc(rc: Rc<Record>) -> Value {
645 Value::from_rc_ptr(TAG_RECORD, rc)
646 }
647
648 pub fn bytevector(bytes: Vec<u8>) -> Value {
649 Value::from_rc_ptr(TAG_BYTEVECTOR, Rc::new(bytes))
650 }
651
652 pub fn bytevector_from_rc(rc: Rc<Vec<u8>>) -> Value {
653 Value::from_rc_ptr(TAG_BYTEVECTOR, rc)
654 }
655
656 pub fn multimethod(m: MultiMethod) -> Value {
657 Value::from_rc_ptr(TAG_MULTIMETHOD, Rc::new(m))
658 }
659
660 pub fn multimethod_from_rc(rc: Rc<MultiMethod>) -> Value {
661 Value::from_rc_ptr(TAG_MULTIMETHOD, rc)
662 }
663}
664
665const fn make_boxed_const(tag: u64, payload: u64) -> u64 {
667 BOX_MASK | (tag << 45) | (payload & PAYLOAD_MASK)
668}
669
670impl Value {
673 #[inline(always)]
675 pub fn raw_bits(&self) -> u64 {
676 self.0
677 }
678
679 #[inline(always)]
688 pub unsafe fn from_raw_bits(bits: u64) -> Value {
689 Value(bits)
690 }
691
692 #[inline(always)]
695 pub fn raw_tag(&self) -> Option<u64> {
696 if is_boxed(self.0) {
697 Some(get_tag(self.0))
698 } else {
699 None
700 }
701 }
702
703 #[inline(always)]
706 pub fn as_native_fn_ref(&self) -> Option<&NativeFn> {
707 if is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN {
708 Some(unsafe { self.borrow_ref::<NativeFn>() })
709 } else {
710 None
711 }
712 }
713
714 #[inline(always)]
716 pub fn is_float(&self) -> bool {
717 !is_boxed(self.0)
718 }
719
720 #[inline(always)]
723 unsafe fn get_rc<T>(&self) -> Rc<T> {
724 let payload = get_payload(self.0);
725 let ptr = payload_to_ptr(payload) as *const T;
726 Rc::increment_strong_count(ptr);
727 Rc::from_raw(ptr)
728 }
729
730 #[inline(always)]
733 unsafe fn borrow_ref<T>(&self) -> &T {
734 let payload = get_payload(self.0);
735 let ptr = payload_to_ptr(payload) as *const T;
736 &*ptr
737 }
738
739 pub fn view(&self) -> ValueView {
742 if !is_boxed(self.0) {
743 return ValueView::Float(f64::from_bits(self.0));
744 }
745 let tag = get_tag(self.0);
746 match tag {
747 TAG_NIL => ValueView::Nil,
748 TAG_FALSE => ValueView::Bool(false),
749 TAG_TRUE => ValueView::Bool(true),
750 TAG_INT_SMALL => {
751 let payload = get_payload(self.0);
752 let val = if payload & INT_SIGN_BIT != 0 {
753 (payload | !PAYLOAD_MASK) as i64
754 } else {
755 payload as i64
756 };
757 ValueView::Int(val)
758 }
759 TAG_CHAR => {
760 let payload = get_payload(self.0);
761 ValueView::Char(unsafe { char::from_u32_unchecked(payload as u32) })
762 }
763 TAG_SYMBOL => {
764 let payload = get_payload(self.0);
765 let spur: Spur = unsafe { std::mem::transmute(payload as u32) };
766 ValueView::Symbol(spur)
767 }
768 TAG_KEYWORD => {
769 let payload = get_payload(self.0);
770 let spur: Spur = unsafe { std::mem::transmute(payload as u32) };
771 ValueView::Keyword(spur)
772 }
773 TAG_INT_BIG => {
774 let val = unsafe { *self.borrow_ref::<i64>() };
775 ValueView::Int(val)
776 }
777 TAG_STRING => ValueView::String(unsafe { self.get_rc::<String>() }),
778 TAG_LIST => ValueView::List(unsafe { self.get_rc::<Vec<Value>>() }),
779 TAG_VECTOR => ValueView::Vector(unsafe { self.get_rc::<Vec<Value>>() }),
780 TAG_MAP => ValueView::Map(unsafe { self.get_rc::<BTreeMap<Value, Value>>() }),
781 TAG_HASHMAP => {
782 ValueView::HashMap(unsafe { self.get_rc::<hashbrown::HashMap<Value, Value>>() })
783 }
784 TAG_LAMBDA => ValueView::Lambda(unsafe { self.get_rc::<Lambda>() }),
785 TAG_MACRO => ValueView::Macro(unsafe { self.get_rc::<Macro>() }),
786 TAG_NATIVE_FN => ValueView::NativeFn(unsafe { self.get_rc::<NativeFn>() }),
787 TAG_PROMPT => ValueView::Prompt(unsafe { self.get_rc::<Prompt>() }),
788 TAG_MESSAGE => ValueView::Message(unsafe { self.get_rc::<Message>() }),
789 TAG_CONVERSATION => ValueView::Conversation(unsafe { self.get_rc::<Conversation>() }),
790 TAG_TOOL_DEF => ValueView::ToolDef(unsafe { self.get_rc::<ToolDefinition>() }),
791 TAG_AGENT => ValueView::Agent(unsafe { self.get_rc::<Agent>() }),
792 TAG_THUNK => ValueView::Thunk(unsafe { self.get_rc::<Thunk>() }),
793 TAG_RECORD => ValueView::Record(unsafe { self.get_rc::<Record>() }),
794 TAG_BYTEVECTOR => ValueView::Bytevector(unsafe { self.get_rc::<Vec<u8>>() }),
795 TAG_MULTIMETHOD => ValueView::MultiMethod(unsafe { self.get_rc::<MultiMethod>() }),
796 _ => unreachable!("invalid NaN-boxed tag: {}", tag),
797 }
798 }
799
800 pub fn type_name(&self) -> &'static str {
803 if !is_boxed(self.0) {
804 return "float";
805 }
806 match get_tag(self.0) {
807 TAG_NIL => "nil",
808 TAG_FALSE | TAG_TRUE => "bool",
809 TAG_INT_SMALL | TAG_INT_BIG => "int",
810 TAG_CHAR => "char",
811 TAG_SYMBOL => "symbol",
812 TAG_KEYWORD => "keyword",
813 TAG_STRING => "string",
814 TAG_LIST => "list",
815 TAG_VECTOR => "vector",
816 TAG_MAP => "map",
817 TAG_HASHMAP => "hashmap",
818 TAG_LAMBDA => "lambda",
819 TAG_MACRO => "macro",
820 TAG_NATIVE_FN => "native-fn",
821 TAG_PROMPT => "prompt",
822 TAG_MESSAGE => "message",
823 TAG_CONVERSATION => "conversation",
824 TAG_TOOL_DEF => "tool",
825 TAG_AGENT => "agent",
826 TAG_THUNK => "promise",
827 TAG_RECORD => "record",
828 TAG_BYTEVECTOR => "bytevector",
829 TAG_MULTIMETHOD => "multimethod",
830 _ => "unknown",
831 }
832 }
833
834 #[inline(always)]
835 pub fn is_nil(&self) -> bool {
836 self.0 == Value::NIL.0
837 }
838
839 #[inline(always)]
840 pub fn is_truthy(&self) -> bool {
841 self.0 != Value::NIL.0 && self.0 != Value::FALSE.0
842 }
843
844 #[inline(always)]
845 pub fn is_falsy(&self) -> bool {
846 !self.is_truthy()
847 }
848
849 #[inline(always)]
850 pub fn is_bool(&self) -> bool {
851 self.0 == Value::TRUE.0 || self.0 == Value::FALSE.0
852 }
853
854 #[inline(always)]
855 pub fn is_int(&self) -> bool {
856 is_boxed(self.0) && matches!(get_tag(self.0), TAG_INT_SMALL | TAG_INT_BIG)
857 }
858
859 #[inline(always)]
860 pub fn is_symbol(&self) -> bool {
861 is_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL
862 }
863
864 #[inline(always)]
865 pub fn is_keyword(&self) -> bool {
866 is_boxed(self.0) && get_tag(self.0) == TAG_KEYWORD
867 }
868
869 #[inline(always)]
870 pub fn is_string(&self) -> bool {
871 is_boxed(self.0) && get_tag(self.0) == TAG_STRING
872 }
873
874 #[inline(always)]
875 pub fn is_list(&self) -> bool {
876 is_boxed(self.0) && get_tag(self.0) == TAG_LIST
877 }
878
879 #[inline(always)]
880 pub fn is_pair(&self) -> bool {
881 if let Some(items) = self.as_list() {
882 !items.is_empty()
883 } else {
884 false
885 }
886 }
887
888 #[inline(always)]
889 pub fn is_vector(&self) -> bool {
890 is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR
891 }
892
893 #[inline(always)]
894 pub fn is_map(&self) -> bool {
895 is_boxed(self.0) && matches!(get_tag(self.0), TAG_MAP | TAG_HASHMAP)
896 }
897
898 #[inline(always)]
899 pub fn is_lambda(&self) -> bool {
900 is_boxed(self.0) && get_tag(self.0) == TAG_LAMBDA
901 }
902
903 #[inline(always)]
904 pub fn is_native_fn(&self) -> bool {
905 is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN
906 }
907
908 #[inline(always)]
909 pub fn is_thunk(&self) -> bool {
910 is_boxed(self.0) && get_tag(self.0) == TAG_THUNK
911 }
912
913 #[inline(always)]
914 pub fn is_record(&self) -> bool {
915 is_boxed(self.0) && get_tag(self.0) == TAG_RECORD
916 }
917
918 #[inline(always)]
919 pub fn as_int(&self) -> Option<i64> {
920 if !is_boxed(self.0) {
921 return None;
922 }
923 match get_tag(self.0) {
924 TAG_INT_SMALL => {
925 let payload = get_payload(self.0);
926 let val = if payload & INT_SIGN_BIT != 0 {
927 (payload | !PAYLOAD_MASK) as i64
928 } else {
929 payload as i64
930 };
931 Some(val)
932 }
933 TAG_INT_BIG => Some(unsafe { *self.borrow_ref::<i64>() }),
934 _ => None,
935 }
936 }
937
938 #[inline(always)]
939 pub fn as_float(&self) -> Option<f64> {
940 if !is_boxed(self.0) {
941 return Some(f64::from_bits(self.0));
942 }
943 match get_tag(self.0) {
944 TAG_INT_SMALL => {
945 let payload = get_payload(self.0);
946 let val = if payload & INT_SIGN_BIT != 0 {
947 (payload | !PAYLOAD_MASK) as i64
948 } else {
949 payload as i64
950 };
951 Some(val as f64)
952 }
953 TAG_INT_BIG => Some(unsafe { *self.borrow_ref::<i64>() } as f64),
954 _ => None,
955 }
956 }
957
958 #[inline(always)]
959 pub fn as_bool(&self) -> Option<bool> {
960 if self.0 == Value::TRUE.0 {
961 Some(true)
962 } else if self.0 == Value::FALSE.0 {
963 Some(false)
964 } else {
965 None
966 }
967 }
968
969 pub fn as_str(&self) -> Option<&str> {
970 if is_boxed(self.0) && get_tag(self.0) == TAG_STRING {
971 Some(unsafe { self.borrow_ref::<String>() })
972 } else {
973 None
974 }
975 }
976
977 pub fn as_string_rc(&self) -> Option<Rc<String>> {
978 if is_boxed(self.0) && get_tag(self.0) == TAG_STRING {
979 Some(unsafe { self.get_rc::<String>() })
980 } else {
981 None
982 }
983 }
984
985 pub fn as_symbol(&self) -> Option<String> {
986 self.as_symbol_spur().map(resolve)
987 }
988
989 pub fn as_symbol_spur(&self) -> Option<Spur> {
990 if is_boxed(self.0) && get_tag(self.0) == TAG_SYMBOL {
991 let payload = get_payload(self.0);
992 Some(unsafe { std::mem::transmute::<u32, Spur>(payload as u32) })
993 } else {
994 None
995 }
996 }
997
998 pub fn as_keyword(&self) -> Option<String> {
999 self.as_keyword_spur().map(resolve)
1000 }
1001
1002 pub fn as_keyword_spur(&self) -> Option<Spur> {
1003 if is_boxed(self.0) && get_tag(self.0) == TAG_KEYWORD {
1004 let payload = get_payload(self.0);
1005 Some(unsafe { std::mem::transmute::<u32, Spur>(payload as u32) })
1006 } else {
1007 None
1008 }
1009 }
1010
1011 pub fn as_char(&self) -> Option<char> {
1012 if is_boxed(self.0) && get_tag(self.0) == TAG_CHAR {
1013 let payload = get_payload(self.0);
1014 char::from_u32(payload as u32)
1015 } else {
1016 None
1017 }
1018 }
1019
1020 pub fn as_list(&self) -> Option<&[Value]> {
1021 if is_boxed(self.0) && get_tag(self.0) == TAG_LIST {
1022 Some(unsafe { self.borrow_ref::<Vec<Value>>() })
1023 } else {
1024 None
1025 }
1026 }
1027
1028 pub fn as_list_rc(&self) -> Option<Rc<Vec<Value>>> {
1029 if is_boxed(self.0) && get_tag(self.0) == TAG_LIST {
1030 Some(unsafe { self.get_rc::<Vec<Value>>() })
1031 } else {
1032 None
1033 }
1034 }
1035
1036 pub fn as_seq(&self) -> Option<&[Value]> {
1038 self.as_list().or_else(|| self.as_vector())
1039 }
1040
1041 pub fn as_vector(&self) -> Option<&[Value]> {
1042 if is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR {
1043 Some(unsafe { self.borrow_ref::<Vec<Value>>() })
1044 } else {
1045 None
1046 }
1047 }
1048
1049 pub fn as_vector_rc(&self) -> Option<Rc<Vec<Value>>> {
1050 if is_boxed(self.0) && get_tag(self.0) == TAG_VECTOR {
1051 Some(unsafe { self.get_rc::<Vec<Value>>() })
1052 } else {
1053 None
1054 }
1055 }
1056
1057 pub fn as_map_rc(&self) -> Option<Rc<BTreeMap<Value, Value>>> {
1058 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
1059 Some(unsafe { self.get_rc::<BTreeMap<Value, Value>>() })
1060 } else {
1061 None
1062 }
1063 }
1064
1065 pub fn as_hashmap_rc(&self) -> Option<Rc<hashbrown::HashMap<Value, Value>>> {
1066 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
1067 Some(unsafe { self.get_rc::<hashbrown::HashMap<Value, Value>>() })
1068 } else {
1069 None
1070 }
1071 }
1072
1073 #[inline(always)]
1075 pub fn as_hashmap_ref(&self) -> Option<&hashbrown::HashMap<Value, Value>> {
1076 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
1077 Some(unsafe { self.borrow_ref::<hashbrown::HashMap<Value, Value>>() })
1078 } else {
1079 None
1080 }
1081 }
1082
1083 #[inline(always)]
1085 pub fn as_map_ref(&self) -> Option<&BTreeMap<Value, Value>> {
1086 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
1087 Some(unsafe { self.borrow_ref::<BTreeMap<Value, Value>>() })
1088 } else {
1089 None
1090 }
1091 }
1092
1093 #[inline(always)]
1097 pub fn with_hashmap_mut_if_unique<R>(
1098 &self,
1099 f: impl FnOnce(&mut hashbrown::HashMap<Value, Value>) -> R,
1100 ) -> Option<R> {
1101 if !is_boxed(self.0) || get_tag(self.0) != TAG_HASHMAP {
1102 return None;
1103 }
1104 let payload = get_payload(self.0);
1105 let ptr = payload_to_ptr(payload) as *const hashbrown::HashMap<Value, Value>;
1106 let rc = std::mem::ManuallyDrop::new(unsafe { Rc::from_raw(ptr) });
1107 if Rc::strong_count(&rc) != 1 {
1108 return None;
1109 }
1110 let ptr_mut = ptr as *mut hashbrown::HashMap<Value, Value>;
1112 Some(f(unsafe { &mut *ptr_mut }))
1113 }
1114
1115 #[inline(always)]
1118 pub fn with_map_mut_if_unique<R>(
1119 &self,
1120 f: impl FnOnce(&mut BTreeMap<Value, Value>) -> R,
1121 ) -> Option<R> {
1122 if !is_boxed(self.0) || get_tag(self.0) != TAG_MAP {
1123 return None;
1124 }
1125 let payload = get_payload(self.0);
1126 let ptr = payload_to_ptr(payload) as *const BTreeMap<Value, Value>;
1127 let rc = std::mem::ManuallyDrop::new(unsafe { Rc::from_raw(ptr) });
1128 if Rc::strong_count(&rc) != 1 {
1129 return None;
1130 }
1131 let ptr_mut = ptr as *mut BTreeMap<Value, Value>;
1132 Some(f(unsafe { &mut *ptr_mut }))
1133 }
1134
1135 pub fn into_hashmap_rc(self) -> Result<Rc<hashbrown::HashMap<Value, Value>>, Value> {
1138 if is_boxed(self.0) && get_tag(self.0) == TAG_HASHMAP {
1139 let payload = get_payload(self.0);
1140 let ptr = payload_to_ptr(payload) as *const hashbrown::HashMap<Value, Value>;
1141 std::mem::forget(self);
1143 Ok(unsafe { Rc::from_raw(ptr) })
1144 } else {
1145 Err(self)
1146 }
1147 }
1148
1149 pub fn into_map_rc(self) -> Result<Rc<BTreeMap<Value, Value>>, Value> {
1152 if is_boxed(self.0) && get_tag(self.0) == TAG_MAP {
1153 let payload = get_payload(self.0);
1154 let ptr = payload_to_ptr(payload) as *const BTreeMap<Value, Value>;
1155 std::mem::forget(self);
1156 Ok(unsafe { Rc::from_raw(ptr) })
1157 } else {
1158 Err(self)
1159 }
1160 }
1161
1162 pub fn as_lambda_rc(&self) -> Option<Rc<Lambda>> {
1163 if is_boxed(self.0) && get_tag(self.0) == TAG_LAMBDA {
1164 Some(unsafe { self.get_rc::<Lambda>() })
1165 } else {
1166 None
1167 }
1168 }
1169
1170 pub fn as_macro_rc(&self) -> Option<Rc<Macro>> {
1171 if is_boxed(self.0) && get_tag(self.0) == TAG_MACRO {
1172 Some(unsafe { self.get_rc::<Macro>() })
1173 } else {
1174 None
1175 }
1176 }
1177
1178 pub fn as_native_fn_rc(&self) -> Option<Rc<NativeFn>> {
1179 if is_boxed(self.0) && get_tag(self.0) == TAG_NATIVE_FN {
1180 Some(unsafe { self.get_rc::<NativeFn>() })
1181 } else {
1182 None
1183 }
1184 }
1185
1186 pub fn as_thunk_rc(&self) -> Option<Rc<Thunk>> {
1187 if is_boxed(self.0) && get_tag(self.0) == TAG_THUNK {
1188 Some(unsafe { self.get_rc::<Thunk>() })
1189 } else {
1190 None
1191 }
1192 }
1193
1194 pub fn as_record(&self) -> Option<&Record> {
1195 if is_boxed(self.0) && get_tag(self.0) == TAG_RECORD {
1196 Some(unsafe { self.borrow_ref::<Record>() })
1197 } else {
1198 None
1199 }
1200 }
1201
1202 pub fn as_record_rc(&self) -> Option<Rc<Record>> {
1203 if is_boxed(self.0) && get_tag(self.0) == TAG_RECORD {
1204 Some(unsafe { self.get_rc::<Record>() })
1205 } else {
1206 None
1207 }
1208 }
1209
1210 pub fn as_bytevector(&self) -> Option<&[u8]> {
1211 if is_boxed(self.0) && get_tag(self.0) == TAG_BYTEVECTOR {
1212 Some(unsafe { self.borrow_ref::<Vec<u8>>() })
1213 } else {
1214 None
1215 }
1216 }
1217
1218 pub fn as_bytevector_rc(&self) -> Option<Rc<Vec<u8>>> {
1219 if is_boxed(self.0) && get_tag(self.0) == TAG_BYTEVECTOR {
1220 Some(unsafe { self.get_rc::<Vec<u8>>() })
1221 } else {
1222 None
1223 }
1224 }
1225
1226 pub fn as_prompt_rc(&self) -> Option<Rc<Prompt>> {
1227 if is_boxed(self.0) && get_tag(self.0) == TAG_PROMPT {
1228 Some(unsafe { self.get_rc::<Prompt>() })
1229 } else {
1230 None
1231 }
1232 }
1233
1234 pub fn as_message_rc(&self) -> Option<Rc<Message>> {
1235 if is_boxed(self.0) && get_tag(self.0) == TAG_MESSAGE {
1236 Some(unsafe { self.get_rc::<Message>() })
1237 } else {
1238 None
1239 }
1240 }
1241
1242 pub fn as_conversation_rc(&self) -> Option<Rc<Conversation>> {
1243 if is_boxed(self.0) && get_tag(self.0) == TAG_CONVERSATION {
1244 Some(unsafe { self.get_rc::<Conversation>() })
1245 } else {
1246 None
1247 }
1248 }
1249
1250 pub fn as_tool_def_rc(&self) -> Option<Rc<ToolDefinition>> {
1251 if is_boxed(self.0) && get_tag(self.0) == TAG_TOOL_DEF {
1252 Some(unsafe { self.get_rc::<ToolDefinition>() })
1253 } else {
1254 None
1255 }
1256 }
1257
1258 pub fn as_agent_rc(&self) -> Option<Rc<Agent>> {
1259 if is_boxed(self.0) && get_tag(self.0) == TAG_AGENT {
1260 Some(unsafe { self.get_rc::<Agent>() })
1261 } else {
1262 None
1263 }
1264 }
1265
1266 pub fn as_multimethod_rc(&self) -> Option<Rc<MultiMethod>> {
1267 if is_boxed(self.0) && get_tag(self.0) == TAG_MULTIMETHOD {
1268 Some(unsafe { self.get_rc::<MultiMethod>() })
1269 } else {
1270 None
1271 }
1272 }
1273}
1274
1275impl Clone for Value {
1278 #[inline(always)]
1279 fn clone(&self) -> Self {
1280 if !is_boxed(self.0) {
1281 return Value(self.0);
1283 }
1284 let tag = get_tag(self.0);
1285 match tag {
1286 TAG_NIL | TAG_FALSE | TAG_TRUE | TAG_INT_SMALL | TAG_CHAR | TAG_SYMBOL
1288 | TAG_KEYWORD => Value(self.0),
1289 _ => {
1291 let payload = get_payload(self.0);
1292 let ptr = payload_to_ptr(payload);
1293 unsafe {
1295 match tag {
1296 TAG_INT_BIG => Rc::increment_strong_count(ptr as *const i64),
1297 TAG_STRING => Rc::increment_strong_count(ptr as *const String),
1298 TAG_LIST | TAG_VECTOR => {
1299 Rc::increment_strong_count(ptr as *const Vec<Value>)
1300 }
1301 TAG_MAP => Rc::increment_strong_count(ptr as *const BTreeMap<Value, Value>),
1302 TAG_HASHMAP => Rc::increment_strong_count(
1303 ptr as *const hashbrown::HashMap<Value, Value>,
1304 ),
1305 TAG_LAMBDA => Rc::increment_strong_count(ptr as *const Lambda),
1306 TAG_MACRO => Rc::increment_strong_count(ptr as *const Macro),
1307 TAG_NATIVE_FN => Rc::increment_strong_count(ptr as *const NativeFn),
1308 TAG_PROMPT => Rc::increment_strong_count(ptr as *const Prompt),
1309 TAG_MESSAGE => Rc::increment_strong_count(ptr as *const Message),
1310 TAG_CONVERSATION => Rc::increment_strong_count(ptr as *const Conversation),
1311 TAG_TOOL_DEF => Rc::increment_strong_count(ptr as *const ToolDefinition),
1312 TAG_AGENT => Rc::increment_strong_count(ptr as *const Agent),
1313 TAG_THUNK => Rc::increment_strong_count(ptr as *const Thunk),
1314 TAG_RECORD => Rc::increment_strong_count(ptr as *const Record),
1315 TAG_BYTEVECTOR => Rc::increment_strong_count(ptr as *const Vec<u8>),
1316 TAG_MULTIMETHOD => Rc::increment_strong_count(ptr as *const MultiMethod),
1317 _ => unreachable!("invalid heap tag in clone: {}", tag),
1318 }
1319 }
1320 Value(self.0)
1321 }
1322 }
1323 }
1324}
1325
1326impl Drop for Value {
1329 #[inline(always)]
1330 fn drop(&mut self) {
1331 if !is_boxed(self.0) {
1332 return; }
1334 let tag = get_tag(self.0);
1335 match tag {
1336 TAG_NIL | TAG_FALSE | TAG_TRUE | TAG_INT_SMALL | TAG_CHAR | TAG_SYMBOL
1338 | TAG_KEYWORD => {}
1339 _ => {
1341 let payload = get_payload(self.0);
1342 let ptr = payload_to_ptr(payload);
1343 unsafe {
1344 match tag {
1345 TAG_INT_BIG => drop(Rc::from_raw(ptr as *const i64)),
1346 TAG_STRING => drop(Rc::from_raw(ptr as *const String)),
1347 TAG_LIST | TAG_VECTOR => drop(Rc::from_raw(ptr as *const Vec<Value>)),
1348 TAG_MAP => drop(Rc::from_raw(ptr as *const BTreeMap<Value, Value>)),
1349 TAG_HASHMAP => {
1350 drop(Rc::from_raw(ptr as *const hashbrown::HashMap<Value, Value>))
1351 }
1352 TAG_LAMBDA => drop(Rc::from_raw(ptr as *const Lambda)),
1353 TAG_MACRO => drop(Rc::from_raw(ptr as *const Macro)),
1354 TAG_NATIVE_FN => drop(Rc::from_raw(ptr as *const NativeFn)),
1355 TAG_PROMPT => drop(Rc::from_raw(ptr as *const Prompt)),
1356 TAG_MESSAGE => drop(Rc::from_raw(ptr as *const Message)),
1357 TAG_CONVERSATION => drop(Rc::from_raw(ptr as *const Conversation)),
1358 TAG_TOOL_DEF => drop(Rc::from_raw(ptr as *const ToolDefinition)),
1359 TAG_AGENT => drop(Rc::from_raw(ptr as *const Agent)),
1360 TAG_THUNK => drop(Rc::from_raw(ptr as *const Thunk)),
1361 TAG_RECORD => drop(Rc::from_raw(ptr as *const Record)),
1362 TAG_BYTEVECTOR => drop(Rc::from_raw(ptr as *const Vec<u8>)),
1363 TAG_MULTIMETHOD => drop(Rc::from_raw(ptr as *const MultiMethod)),
1364 _ => {} }
1366 }
1367 }
1368 }
1369 }
1370}
1371
1372impl PartialEq for Value {
1375 fn eq(&self, other: &Self) -> bool {
1376 if self.0 == other.0 {
1378 if !is_boxed(self.0) {
1382 let f = f64::from_bits(self.0);
1383 if f.is_nan() {
1385 return false;
1386 }
1387 return true;
1388 }
1389 return true;
1390 }
1391 match (self.view(), other.view()) {
1393 (ValueView::Nil, ValueView::Nil) => true,
1394 (ValueView::Bool(a), ValueView::Bool(b)) => a == b,
1395 (ValueView::Int(a), ValueView::Int(b)) => a == b,
1396 (ValueView::Float(a), ValueView::Float(b)) => a == b,
1397 (ValueView::String(a), ValueView::String(b)) => a == b,
1398 (ValueView::Symbol(a), ValueView::Symbol(b)) => a == b,
1399 (ValueView::Keyword(a), ValueView::Keyword(b)) => a == b,
1400 (ValueView::Char(a), ValueView::Char(b)) => a == b,
1401 (ValueView::List(a), ValueView::List(b)) => a == b,
1402 (ValueView::Vector(a), ValueView::Vector(b)) => a == b,
1403 (ValueView::Map(a), ValueView::Map(b)) => a == b,
1404 (ValueView::HashMap(a), ValueView::HashMap(b)) => a == b,
1405 (ValueView::Record(a), ValueView::Record(b)) => {
1406 a.type_tag == b.type_tag && a.fields == b.fields
1407 }
1408 (ValueView::Bytevector(a), ValueView::Bytevector(b)) => a == b,
1409 _ => false,
1410 }
1411 }
1412}
1413
1414impl Eq for Value {}
1415
1416impl Hash for Value {
1419 fn hash<H: Hasher>(&self, state: &mut H) {
1420 match self.view() {
1421 ValueView::Nil => 0u8.hash(state),
1422 ValueView::Bool(b) => {
1423 1u8.hash(state);
1424 b.hash(state);
1425 }
1426 ValueView::Int(n) => {
1427 2u8.hash(state);
1428 n.hash(state);
1429 }
1430 ValueView::Float(f) => {
1431 3u8.hash(state);
1432 let bits = if f == 0.0 { 0u64 } else { f.to_bits() };
1434 bits.hash(state);
1435 }
1436 ValueView::String(s) => {
1437 4u8.hash(state);
1438 s.hash(state);
1439 }
1440 ValueView::Symbol(s) => {
1441 5u8.hash(state);
1442 s.hash(state);
1443 }
1444 ValueView::Keyword(s) => {
1445 6u8.hash(state);
1446 s.hash(state);
1447 }
1448 ValueView::Char(c) => {
1449 7u8.hash(state);
1450 c.hash(state);
1451 }
1452 ValueView::List(l) => {
1453 8u8.hash(state);
1454 l.hash(state);
1455 }
1456 ValueView::Vector(v) => {
1457 9u8.hash(state);
1458 v.hash(state);
1459 }
1460 ValueView::Record(r) => {
1461 10u8.hash(state);
1462 r.type_tag.hash(state);
1463 r.fields.hash(state);
1464 }
1465 ValueView::Bytevector(bv) => {
1466 11u8.hash(state);
1467 bv.hash(state);
1468 }
1469 _ => {}
1470 }
1471 }
1472}
1473
1474impl PartialOrd for Value {
1477 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1478 Some(self.cmp(other))
1479 }
1480}
1481
1482impl Ord for Value {
1483 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1484 use std::cmp::Ordering;
1485 fn type_order(v: &Value) -> u8 {
1486 match v.view() {
1487 ValueView::Nil => 0,
1488 ValueView::Bool(_) => 1,
1489 ValueView::Int(_) => 2,
1490 ValueView::Float(_) => 3,
1491 ValueView::Char(_) => 4,
1492 ValueView::String(_) => 5,
1493 ValueView::Symbol(_) => 6,
1494 ValueView::Keyword(_) => 7,
1495 ValueView::List(_) => 8,
1496 ValueView::Vector(_) => 9,
1497 ValueView::Map(_) => 10,
1498 ValueView::HashMap(_) => 11,
1499 ValueView::Record(_) => 12,
1500 ValueView::Bytevector(_) => 13,
1501 _ => 14,
1502 }
1503 }
1504 match (self.view(), other.view()) {
1505 (ValueView::Nil, ValueView::Nil) => Ordering::Equal,
1506 (ValueView::Bool(a), ValueView::Bool(b)) => a.cmp(&b),
1507 (ValueView::Int(a), ValueView::Int(b)) => a.cmp(&b),
1508 (ValueView::Float(a), ValueView::Float(b)) => a.total_cmp(&b),
1509 (ValueView::String(a), ValueView::String(b)) => a.cmp(&b),
1510 (ValueView::Symbol(a), ValueView::Symbol(b)) => compare_spurs(a, b),
1511 (ValueView::Keyword(a), ValueView::Keyword(b)) => compare_spurs(a, b),
1512 (ValueView::Char(a), ValueView::Char(b)) => a.cmp(&b),
1513 (ValueView::List(a), ValueView::List(b)) => a.cmp(&b),
1514 (ValueView::Vector(a), ValueView::Vector(b)) => a.cmp(&b),
1515 (ValueView::Record(a), ValueView::Record(b)) => {
1516 compare_spurs(a.type_tag, b.type_tag).then_with(|| a.fields.cmp(&b.fields))
1517 }
1518 (ValueView::Bytevector(a), ValueView::Bytevector(b)) => a.cmp(&b),
1519 _ => type_order(self).cmp(&type_order(other)),
1520 }
1521 }
1522}
1523
1524fn truncate(s: &str, max: usize) -> String {
1527 let mut iter = s.chars();
1528 let prefix: String = iter.by_ref().take(max).collect();
1529 if iter.next().is_none() {
1530 prefix
1531 } else {
1532 format!("{prefix}...")
1533 }
1534}
1535
1536impl fmt::Display for Value {
1537 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1538 match self.view() {
1539 ValueView::Nil => write!(f, "nil"),
1540 ValueView::Bool(true) => write!(f, "#t"),
1541 ValueView::Bool(false) => write!(f, "#f"),
1542 ValueView::Int(n) => write!(f, "{n}"),
1543 ValueView::Float(n) => {
1544 if n.fract() == 0.0 {
1545 write!(f, "{n:.1}")
1546 } else {
1547 write!(f, "{n}")
1548 }
1549 }
1550 ValueView::String(s) => write!(f, "\"{s}\""),
1551 ValueView::Symbol(s) => with_resolved(s, |name| write!(f, "{name}")),
1552 ValueView::Keyword(s) => with_resolved(s, |name| write!(f, ":{name}")),
1553 ValueView::Char(c) => match c {
1554 ' ' => write!(f, "#\\space"),
1555 '\n' => write!(f, "#\\newline"),
1556 '\t' => write!(f, "#\\tab"),
1557 '\r' => write!(f, "#\\return"),
1558 '\0' => write!(f, "#\\nul"),
1559 _ => write!(f, "#\\{c}"),
1560 },
1561 ValueView::List(items) => {
1562 write!(f, "(")?;
1563 for (i, item) in items.iter().enumerate() {
1564 if i > 0 {
1565 write!(f, " ")?;
1566 }
1567 write!(f, "{item}")?;
1568 }
1569 write!(f, ")")
1570 }
1571 ValueView::Vector(items) => {
1572 write!(f, "[")?;
1573 for (i, item) in items.iter().enumerate() {
1574 if i > 0 {
1575 write!(f, " ")?;
1576 }
1577 write!(f, "{item}")?;
1578 }
1579 write!(f, "]")
1580 }
1581 ValueView::Map(map) => {
1582 write!(f, "{{")?;
1583 for (i, (k, v)) in map.iter().enumerate() {
1584 if i > 0 {
1585 write!(f, " ")?;
1586 }
1587 write!(f, "{k} {v}")?;
1588 }
1589 write!(f, "}}")
1590 }
1591 ValueView::HashMap(map) => {
1592 let mut entries: Vec<_> = map.iter().collect();
1593 entries.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
1594 write!(f, "{{")?;
1595 for (i, (k, v)) in entries.iter().enumerate() {
1596 if i > 0 {
1597 write!(f, " ")?;
1598 }
1599 write!(f, "{k} {v}")?;
1600 }
1601 write!(f, "}}")
1602 }
1603 ValueView::Lambda(l) => {
1604 if let Some(name) = &l.name {
1605 with_resolved(*name, |n| write!(f, "<lambda {n}>"))
1606 } else {
1607 write!(f, "<lambda>")
1608 }
1609 }
1610 ValueView::Macro(m) => with_resolved(m.name, |n| write!(f, "<macro {n}>")),
1611 ValueView::NativeFn(n) => write!(f, "<native-fn {}>", n.name),
1612 ValueView::Prompt(p) => write!(f, "<prompt {} messages>", p.messages.len()),
1613 ValueView::Message(m) => {
1614 write!(f, "<message {} \"{}\">", m.role, truncate(&m.content, 40))
1615 }
1616 ValueView::Conversation(c) => {
1617 write!(f, "<conversation {} messages>", c.messages.len())
1618 }
1619 ValueView::ToolDef(t) => write!(f, "<tool {}>", t.name),
1620 ValueView::Agent(a) => write!(f, "<agent {}>", a.name),
1621 ValueView::Thunk(t) => {
1622 if t.forced.borrow().is_some() {
1623 write!(f, "<promise (forced)>")
1624 } else {
1625 write!(f, "<promise>")
1626 }
1627 }
1628 ValueView::Record(r) => {
1629 with_resolved(r.type_tag, |tag| write!(f, "#<record {tag}"))?;
1630 for field in &r.fields {
1631 write!(f, " {field}")?;
1632 }
1633 write!(f, ">")
1634 }
1635 ValueView::Bytevector(bv) => {
1636 write!(f, "#u8(")?;
1637 for (i, byte) in bv.iter().enumerate() {
1638 if i > 0 {
1639 write!(f, " ")?;
1640 }
1641 write!(f, "{byte}")?;
1642 }
1643 write!(f, ")")
1644 }
1645 ValueView::MultiMethod(m) => with_resolved(m.name, |n| write!(f, "<multimethod {n}>")),
1646 }
1647 }
1648}
1649
1650pub fn pretty_print(value: &Value, max_width: usize) -> String {
1656 let compact = format!("{value}");
1657 if compact.len() <= max_width {
1658 return compact;
1659 }
1660 let mut buf = String::new();
1661 pp_value(value, 0, max_width, &mut buf);
1662 buf
1663}
1664
1665fn pp_value(value: &Value, indent: usize, max_width: usize, buf: &mut String) {
1669 let compact = format!("{value}");
1670 let remaining = max_width.saturating_sub(indent);
1671 if compact.len() <= remaining {
1672 buf.push_str(&compact);
1673 return;
1674 }
1675
1676 match value.view() {
1677 ValueView::List(items) => {
1678 pp_seq(items.iter(), '(', ')', indent, max_width, buf);
1679 }
1680 ValueView::Vector(items) => {
1681 pp_seq(items.iter(), '[', ']', indent, max_width, buf);
1682 }
1683 ValueView::Map(map) => {
1684 pp_map(
1685 map.iter().map(|(k, v)| (k.clone(), v.clone())),
1686 indent,
1687 max_width,
1688 buf,
1689 );
1690 }
1691 ValueView::HashMap(map) => {
1692 let mut entries: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
1693 entries.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
1694 pp_map(entries.into_iter(), indent, max_width, buf);
1695 }
1696 _ => buf.push_str(&compact),
1697 }
1698}
1699
1700fn pp_seq<'a>(
1702 items: impl Iterator<Item = &'a Value>,
1703 open: char,
1704 close: char,
1705 indent: usize,
1706 max_width: usize,
1707 buf: &mut String,
1708) {
1709 buf.push(open);
1710 let child_indent = indent + 1;
1711 let pad = " ".repeat(child_indent);
1712 for (i, item) in items.enumerate() {
1713 if i > 0 {
1714 buf.push('\n');
1715 buf.push_str(&pad);
1716 }
1717 pp_value(item, child_indent, max_width, buf);
1718 }
1719 buf.push(close);
1720}
1721
1722fn pp_map(
1724 entries: impl Iterator<Item = (Value, Value)>,
1725 indent: usize,
1726 max_width: usize,
1727 buf: &mut String,
1728) {
1729 buf.push('{');
1730 let child_indent = indent + 1;
1731 let pad = " ".repeat(child_indent);
1732 for (i, (k, v)) in entries.enumerate() {
1733 if i > 0 {
1734 buf.push('\n');
1735 buf.push_str(&pad);
1736 }
1737 let key_str = format!("{k}");
1739 buf.push_str(&key_str);
1740
1741 let inline_indent = child_indent + key_str.len() + 1;
1743 let compact_val = format!("{v}");
1744 let remaining = max_width.saturating_sub(inline_indent);
1745
1746 if compact_val.len() <= remaining {
1747 buf.push(' ');
1749 buf.push_str(&compact_val);
1750 } else if is_compound(&v) {
1751 let nested_indent = child_indent + 2;
1753 let nested_pad = " ".repeat(nested_indent);
1754 buf.push('\n');
1755 buf.push_str(&nested_pad);
1756 pp_value(&v, nested_indent, max_width, buf);
1757 } else {
1758 buf.push(' ');
1760 buf.push_str(&compact_val);
1761 }
1762 }
1763 buf.push('}');
1764}
1765
1766fn is_compound(value: &Value) -> bool {
1768 matches!(
1769 value.view(),
1770 ValueView::List(_) | ValueView::Vector(_) | ValueView::Map(_) | ValueView::HashMap(_)
1771 )
1772}
1773
1774impl fmt::Debug for Value {
1777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1778 match self.view() {
1779 ValueView::Nil => write!(f, "Nil"),
1780 ValueView::Bool(b) => write!(f, "Bool({b})"),
1781 ValueView::Int(n) => write!(f, "Int({n})"),
1782 ValueView::Float(n) => write!(f, "Float({n})"),
1783 ValueView::String(s) => write!(f, "String({:?})", &**s),
1784 ValueView::Symbol(s) => write!(f, "Symbol({})", resolve(s)),
1785 ValueView::Keyword(s) => write!(f, "Keyword({})", resolve(s)),
1786 ValueView::Char(c) => write!(f, "Char({c:?})"),
1787 ValueView::List(items) => write!(f, "List({items:?})"),
1788 ValueView::Vector(items) => write!(f, "Vector({items:?})"),
1789 ValueView::Map(map) => write!(f, "Map({map:?})"),
1790 ValueView::HashMap(map) => write!(f, "HashMap({map:?})"),
1791 ValueView::Lambda(l) => write!(f, "{l:?}"),
1792 ValueView::Macro(m) => write!(f, "{m:?}"),
1793 ValueView::NativeFn(n) => write!(f, "{n:?}"),
1794 ValueView::Prompt(p) => write!(f, "{p:?}"),
1795 ValueView::Message(m) => write!(f, "{m:?}"),
1796 ValueView::Conversation(c) => write!(f, "{c:?}"),
1797 ValueView::ToolDef(t) => write!(f, "{t:?}"),
1798 ValueView::Agent(a) => write!(f, "{a:?}"),
1799 ValueView::Thunk(t) => write!(f, "{t:?}"),
1800 ValueView::Record(r) => write!(f, "{r:?}"),
1801 ValueView::Bytevector(bv) => write!(f, "Bytevector({bv:?})"),
1802 ValueView::MultiMethod(m) => write!(f, "{m:?}"),
1803 }
1804 }
1805}
1806
1807#[derive(Debug, Clone)]
1811pub struct Env {
1812 pub bindings: Rc<RefCell<SpurMap<Spur, Value>>>,
1813 pub parent: Option<Rc<Env>>,
1814 pub version: Cell<u64>,
1815}
1816
1817impl Env {
1818 pub fn new() -> Self {
1819 Env {
1820 bindings: Rc::new(RefCell::new(SpurMap::new())),
1821 parent: None,
1822 version: Cell::new(0),
1823 }
1824 }
1825
1826 pub fn with_parent(parent: Rc<Env>) -> Self {
1827 Env {
1828 bindings: Rc::new(RefCell::new(SpurMap::new())),
1829 parent: Some(parent),
1830 version: Cell::new(0),
1831 }
1832 }
1833
1834 fn bump_version(&self) {
1835 self.version.set(self.version.get().wrapping_add(1));
1836 }
1837
1838 pub fn get(&self, name: Spur) -> Option<Value> {
1839 if let Some(val) = self.bindings.borrow().get(&name) {
1840 Some(val.clone())
1841 } else if let Some(parent) = &self.parent {
1842 parent.get(name)
1843 } else {
1844 None
1845 }
1846 }
1847
1848 pub fn get_str(&self, name: &str) -> Option<Value> {
1849 self.get(intern(name))
1850 }
1851
1852 pub fn set(&self, name: Spur, val: Value) {
1853 self.bindings.borrow_mut().insert(name, val);
1854 self.bump_version();
1855 }
1856
1857 pub fn set_str(&self, name: &str, val: Value) {
1858 self.set(intern(name), val);
1859 }
1860
1861 pub fn update(&self, name: Spur, val: Value) {
1863 let mut bindings = self.bindings.borrow_mut();
1864 if let Some(entry) = bindings.get_mut(&name) {
1865 *entry = val;
1866 } else {
1867 bindings.insert(name, val);
1868 }
1869 drop(bindings);
1870 self.bump_version();
1871 }
1872
1873 pub fn take(&self, name: Spur) -> Option<Value> {
1875 let result = self.bindings.borrow_mut().remove(&name);
1876 if result.is_some() {
1877 self.bump_version();
1878 }
1879 result
1880 }
1881
1882 pub fn take_anywhere(&self, name: Spur) -> Option<Value> {
1884 if let Some(val) = self.bindings.borrow_mut().remove(&name) {
1885 self.bump_version();
1886 Some(val)
1887 } else if let Some(parent) = &self.parent {
1888 parent.take_anywhere(name)
1889 } else {
1890 None
1891 }
1892 }
1893
1894 pub fn set_existing(&self, name: Spur, val: Value) -> bool {
1896 let mut bindings = self.bindings.borrow_mut();
1897 if let Some(entry) = bindings.get_mut(&name) {
1898 *entry = val;
1899 drop(bindings);
1900 self.bump_version();
1901 true
1902 } else {
1903 drop(bindings);
1904 if let Some(parent) = &self.parent {
1905 parent.set_existing(name, val)
1906 } else {
1907 false
1908 }
1909 }
1910 }
1911
1912 pub fn all_names(&self) -> Vec<Spur> {
1914 let mut names: Vec<Spur> = self.bindings.borrow().keys().copied().collect();
1915 if let Some(parent) = &self.parent {
1916 names.extend(parent.all_names());
1917 }
1918 names.sort_unstable();
1919 names.dedup();
1920 names
1921 }
1922}
1923
1924impl Default for Env {
1925 fn default() -> Self {
1926 Self::new()
1927 }
1928}
1929
1930#[cfg(test)]
1933mod tests {
1934 use super::*;
1935
1936 #[test]
1937 fn test_size_of_value() {
1938 assert_eq!(std::mem::size_of::<Value>(), 8);
1939 }
1940
1941 #[test]
1942 fn test_nil() {
1943 let v = Value::nil();
1944 assert!(v.is_nil());
1945 assert!(!v.is_truthy());
1946 assert_eq!(v.type_name(), "nil");
1947 assert_eq!(format!("{v}"), "nil");
1948 }
1949
1950 #[test]
1951 fn test_bool() {
1952 let t = Value::bool(true);
1953 let f = Value::bool(false);
1954 assert!(t.is_truthy());
1955 assert!(!f.is_truthy());
1956 assert_eq!(t.as_bool(), Some(true));
1957 assert_eq!(f.as_bool(), Some(false));
1958 assert_eq!(format!("{t}"), "#t");
1959 assert_eq!(format!("{f}"), "#f");
1960 }
1961
1962 #[test]
1963 fn test_small_int() {
1964 let v = Value::int(42);
1965 assert_eq!(v.as_int(), Some(42));
1966 assert_eq!(v.type_name(), "int");
1967 assert_eq!(format!("{v}"), "42");
1968
1969 let neg = Value::int(-100);
1970 assert_eq!(neg.as_int(), Some(-100));
1971 assert_eq!(format!("{neg}"), "-100");
1972
1973 let zero = Value::int(0);
1974 assert_eq!(zero.as_int(), Some(0));
1975 }
1976
1977 #[test]
1978 fn test_small_int_boundaries() {
1979 let max = Value::int(SMALL_INT_MAX);
1980 assert_eq!(max.as_int(), Some(SMALL_INT_MAX));
1981
1982 let min = Value::int(SMALL_INT_MIN);
1983 assert_eq!(min.as_int(), Some(SMALL_INT_MIN));
1984 }
1985
1986 #[test]
1987 fn test_big_int() {
1988 let big = Value::int(i64::MAX);
1989 assert_eq!(big.as_int(), Some(i64::MAX));
1990 assert_eq!(big.type_name(), "int");
1991
1992 let big_neg = Value::int(i64::MIN);
1993 assert_eq!(big_neg.as_int(), Some(i64::MIN));
1994
1995 let just_over = Value::int(SMALL_INT_MAX + 1);
1997 assert_eq!(just_over.as_int(), Some(SMALL_INT_MAX + 1));
1998 }
1999
2000 #[test]
2001 fn test_float() {
2002 let v = Value::float(3.14);
2003 assert_eq!(v.as_float(), Some(3.14));
2004 assert_eq!(v.type_name(), "float");
2005
2006 let neg = Value::float(-0.5);
2007 assert_eq!(neg.as_float(), Some(-0.5));
2008
2009 let inf = Value::float(f64::INFINITY);
2010 assert_eq!(inf.as_float(), Some(f64::INFINITY));
2011
2012 let neg_inf = Value::float(f64::NEG_INFINITY);
2013 assert_eq!(neg_inf.as_float(), Some(f64::NEG_INFINITY));
2014 }
2015
2016 #[test]
2017 fn test_float_nan() {
2018 let nan = Value::float(f64::NAN);
2019 let f = nan.as_float().unwrap();
2020 assert!(f.is_nan());
2021 }
2022
2023 #[test]
2024 fn test_string() {
2025 let v = Value::string("hello");
2026 assert_eq!(v.as_str(), Some("hello"));
2027 assert_eq!(v.type_name(), "string");
2028 assert_eq!(format!("{v}"), "\"hello\"");
2029 }
2030
2031 #[test]
2032 fn test_symbol() {
2033 let v = Value::symbol("foo");
2034 assert!(v.as_symbol_spur().is_some());
2035 assert_eq!(v.as_symbol(), Some("foo".to_string()));
2036 assert_eq!(v.type_name(), "symbol");
2037 assert_eq!(format!("{v}"), "foo");
2038 }
2039
2040 #[test]
2041 fn test_keyword() {
2042 let v = Value::keyword("bar");
2043 assert!(v.as_keyword_spur().is_some());
2044 assert_eq!(v.as_keyword(), Some("bar".to_string()));
2045 assert_eq!(v.type_name(), "keyword");
2046 assert_eq!(format!("{v}"), ":bar");
2047 }
2048
2049 #[test]
2050 fn test_char() {
2051 let v = Value::char('λ');
2052 assert_eq!(v.as_char(), Some('λ'));
2053 assert_eq!(v.type_name(), "char");
2054 }
2055
2056 #[test]
2057 fn test_list() {
2058 let v = Value::list(vec![Value::int(1), Value::int(2), Value::int(3)]);
2059 assert_eq!(v.as_list().unwrap().len(), 3);
2060 assert_eq!(v.type_name(), "list");
2061 assert_eq!(format!("{v}"), "(1 2 3)");
2062 }
2063
2064 #[test]
2065 fn test_clone_immediate() {
2066 let v = Value::int(42);
2067 let v2 = v.clone();
2068 assert_eq!(v.as_int(), v2.as_int());
2069 }
2070
2071 #[test]
2072 fn test_clone_heap() {
2073 let v = Value::string("hello");
2074 let v2 = v.clone();
2075 assert_eq!(v.as_str(), v2.as_str());
2076 assert_eq!(format!("{v}"), format!("{v2}"));
2078 }
2079
2080 #[test]
2081 fn test_equality() {
2082 assert_eq!(Value::int(42), Value::int(42));
2083 assert_ne!(Value::int(42), Value::int(43));
2084 assert_eq!(Value::nil(), Value::nil());
2085 assert_eq!(Value::bool(true), Value::bool(true));
2086 assert_ne!(Value::bool(true), Value::bool(false));
2087 assert_eq!(Value::string("a"), Value::string("a"));
2088 assert_ne!(Value::string("a"), Value::string("b"));
2089 assert_eq!(Value::symbol("x"), Value::symbol("x"));
2090 }
2091
2092 #[test]
2093 fn test_big_int_equality() {
2094 assert_eq!(Value::int(i64::MAX), Value::int(i64::MAX));
2095 assert_ne!(Value::int(i64::MAX), Value::int(i64::MIN));
2096 }
2097
2098 #[test]
2099 fn test_view_pattern_matching() {
2100 let v = Value::int(42);
2101 match v.view() {
2102 ValueView::Int(n) => assert_eq!(n, 42),
2103 _ => panic!("expected int"),
2104 }
2105
2106 let v = Value::string("hello");
2107 match v.view() {
2108 ValueView::String(s) => assert_eq!(&**s, "hello"),
2109 _ => panic!("expected string"),
2110 }
2111 }
2112
2113 #[test]
2114 fn test_env() {
2115 let env = Env::new();
2116 env.set_str("x", Value::int(42));
2117 assert_eq!(env.get_str("x"), Some(Value::int(42)));
2118 }
2119
2120 #[test]
2121 fn test_native_fn_simple() {
2122 let f = NativeFn::simple("add1", |args| Ok(args[0].clone()));
2123 let ctx = EvalContext::new();
2124 assert!((f.func)(&ctx, &[Value::int(42)]).is_ok());
2125 }
2126
2127 #[test]
2128 fn test_native_fn_with_ctx() {
2129 let f = NativeFn::with_ctx("get-depth", |ctx, _args| {
2130 Ok(Value::int(ctx.eval_depth.get() as i64))
2131 });
2132 let ctx = EvalContext::new();
2133 assert_eq!((f.func)(&ctx, &[]).unwrap(), Value::int(0));
2134 }
2135
2136 #[test]
2137 fn test_drop_doesnt_leak() {
2138 for _ in 0..10000 {
2140 let _ = Value::string("test");
2141 let _ = Value::list(vec![Value::int(1), Value::int(2)]);
2142 let _ = Value::int(i64::MAX); }
2144 }
2145
2146 #[test]
2147 fn test_is_truthy() {
2148 assert!(!Value::nil().is_truthy());
2149 assert!(!Value::bool(false).is_truthy());
2150 assert!(Value::bool(true).is_truthy());
2151 assert!(Value::int(0).is_truthy());
2152 assert!(Value::int(1).is_truthy());
2153 assert!(Value::string("").is_truthy());
2154 assert!(Value::list(vec![]).is_truthy());
2155 }
2156
2157 #[test]
2158 fn test_as_float_from_int() {
2159 assert_eq!(Value::int(42).as_float(), Some(42.0));
2160 assert_eq!(Value::float(3.14).as_float(), Some(3.14));
2161 }
2162
2163 #[test]
2164 fn test_next_gensym_unique() {
2165 let a = next_gensym("x");
2166 let b = next_gensym("x");
2167 let c = next_gensym("y");
2168 assert_ne!(a, b);
2169 assert_ne!(a, c);
2170 assert_ne!(b, c);
2171 assert!(a.starts_with("x__"));
2172 assert!(b.starts_with("x__"));
2173 assert!(c.starts_with("y__"));
2174 }
2175
2176 #[test]
2177 fn test_next_gensym_counter_does_not_panic_near_max() {
2178 GENSYM_COUNTER.with(|c| c.set(u64::MAX - 1));
2180 let a = next_gensym("z");
2181 assert!(a.contains(&(u64::MAX - 1).to_string()));
2182 let b = next_gensym("z");
2184 assert!(b.contains(&u64::MAX.to_string()));
2185 let c = next_gensym("z");
2187 assert!(c.contains("__0"));
2188 }
2189}