1use std::collections::HashMap;
2use std::sync::atomic::Ordering;
3use std::sync::Arc;
4use std::{future::Future, pin::Pin};
5
6use crate::harness::VmHarness;
7use crate::mcp::VmMcpClientHandle;
8use crate::BuiltinId;
9
10use super::{
11 VmAtomicHandle, VmChannelHandle, VmClosure, VmError, VmGenerator, VmRange, VmRngHandle, VmSet,
12 VmStream, VmSyncPermitHandle,
13};
14
15pub type VmAsyncBuiltinFn = Arc<
22 dyn Fn(
23 crate::vm::AsyncBuiltinCtx,
24 Vec<VmValue>,
25 ) -> Pin<Box<dyn Future<Output = Result<VmValue, VmError>> + Send>>
26 + Send
27 + Sync,
28>;
29
30type Shared<T> = Arc<T>;
31
32pub type DictMap = imbl::OrdMap<String, VmValue>;
44
45pub fn string_char_count(text: &str) -> usize {
51 if text.is_ascii() {
52 text.len()
53 } else {
54 text.chars().count()
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct StructLayout {
61 struct_name: String,
62 field_names: Vec<String>,
63 field_indexes: HashMap<String, usize>,
64}
65
66impl StructLayout {
67 pub fn new(struct_name: impl Into<String>, field_names: Vec<String>) -> Self {
68 let mut deduped = Vec::with_capacity(field_names.len());
69 let mut field_indexes = HashMap::with_capacity(field_names.len());
70 for field_name in field_names {
71 if field_indexes.contains_key(&field_name) {
72 continue;
73 }
74 let index = deduped.len();
75 field_indexes.insert(field_name.clone(), index);
76 deduped.push(field_name);
77 }
78
79 Self {
80 struct_name: struct_name.into(),
81 field_names: deduped,
82 field_indexes,
83 }
84 }
85
86 pub fn from_map(struct_name: impl Into<String>, fields: &crate::value::DictMap) -> Self {
87 Self::new(struct_name, fields.keys().cloned().collect())
88 }
89
90 pub fn struct_name(&self) -> &str {
91 &self.struct_name
92 }
93
94 pub fn field_names(&self) -> &[String] {
95 &self.field_names
96 }
97
98 pub fn field_index(&self, field_name: &str) -> Option<usize> {
99 if self.field_names.len() <= 8 {
100 return self
101 .field_names
102 .iter()
103 .position(|candidate| candidate == field_name);
104 }
105 self.field_indexes.get(field_name).copied()
106 }
107
108 pub fn with_appended_field(&self, field_name: String) -> Self {
109 if self.field_indexes.contains_key(&field_name) {
110 return self.clone();
111 }
112 let mut field_names = self.field_names.clone();
113 field_names.push(field_name);
114 Self::new(self.struct_name.clone(), field_names)
115 }
116}
117
118#[derive(Debug, Clone)]
120pub struct VmEnumVariant {
121 pub enum_name: Shared<str>,
122 pub variant: Shared<str>,
123 pub fields: Shared<Vec<VmValue>>,
124}
125
126impl VmEnumVariant {
127 pub fn has_enum_name(&self, enum_name: &str) -> bool {
128 self.enum_name.as_ref() == enum_name
129 }
130
131 pub fn is_variant(&self, enum_name: &str, variant: &str) -> bool {
132 self.has_enum_name(enum_name) && self.variant.as_ref() == variant
133 }
134}
135
136#[derive(Debug, Clone)]
143pub struct VmBuiltinRefId {
144 pub id: BuiltinId,
145 pub name: Shared<str>,
146}
147
148#[derive(Debug, Clone)]
161pub enum VmValue {
162 Int(i64),
163 Float(f64),
164 Decimal(rust_decimal::Decimal),
172 String(Shared<str>),
173 Bytes(Shared<Vec<u8>>),
174 Bool(bool),
175 Nil,
176 List(Shared<Vec<VmValue>>),
177 Dict(Shared<DictMap>),
178 Closure(Shared<VmClosure>),
179 BuiltinRef(Shared<str>),
183 BuiltinRefId(Shared<VmBuiltinRefId>),
188 Duration(i64),
189 EnumVariant(Shared<VmEnumVariant>),
190 StructInstance {
191 layout: Shared<StructLayout>,
192 fields: Shared<Vec<Option<VmValue>>>,
193 },
194 TaskHandle(Shared<str>),
195 Channel(Shared<VmChannelHandle>),
196 Atomic(Shared<VmAtomicHandle>),
197 Rng(Shared<VmRngHandle>),
198 SyncPermit(Shared<VmSyncPermitHandle>),
199 McpClient(Shared<VmMcpClientHandle>),
200 Set(Shared<VmSet>),
201 Generator(Shared<VmGenerator>),
202 Stream(Shared<VmStream>),
203 Range(Shared<VmRange>),
207 Iter(crate::vm::iter::VmIterHandle),
209 Pair(Shared<(VmValue, VmValue)>),
214 Harness(Shared<VmHarness>),
219}
220
221static ASCII_CHAR_STRINGS: std::sync::LazyLock<[Arc<str>; 128]> = std::sync::LazyLock::new(|| {
230 std::array::from_fn(|byte| {
231 let mut buffer = [0u8; 4];
232 Arc::from((byte as u8 as char).encode_utf8(&mut buffer))
233 })
234});
235
236impl VmValue {
237 pub fn string(value: impl AsRef<str>) -> Self {
245 VmValue::String(Arc::from(value.as_ref()))
246 }
247
248 pub fn char_value(ch: char) -> Self {
252 if ch.is_ascii() {
253 return VmValue::String(Arc::clone(&ASCII_CHAR_STRINGS[ch as usize]));
254 }
255 let mut buffer = [0u8; 4];
256 VmValue::String(Arc::from(ch.encode_utf8(&mut buffer)))
257 }
258
259 pub fn chars_list(text: &str) -> Self {
264 VmValue::List(Shared::new(text.chars().map(VmValue::char_value).collect()))
265 }
266
267 pub fn enum_variant(
268 enum_name: impl Into<Shared<str>>,
269 variant: impl Into<Shared<str>>,
270 fields: Vec<VmValue>,
271 ) -> Self {
272 VmValue::EnumVariant(Shared::new(VmEnumVariant {
273 enum_name: enum_name.into(),
274 variant: variant.into(),
275 fields: Shared::new(fields),
276 }))
277 }
278
279 pub fn task_handle(id: impl Into<Shared<str>>) -> Self {
280 VmValue::TaskHandle(id.into())
281 }
282
283 pub fn range(range: VmRange) -> Self {
285 VmValue::Range(Shared::new(range))
286 }
287
288 pub fn builtin_ref_id(id: BuiltinId, name: impl Into<Shared<str>>) -> Self {
290 VmValue::BuiltinRefId(Shared::new(VmBuiltinRefId {
291 id,
292 name: name.into(),
293 }))
294 }
295
296 pub fn dict(entries: impl IntoIterator<Item = (String, VmValue)>) -> Self {
302 VmValue::Dict(Shared::new(entries.into_iter().collect::<DictMap>()))
303 }
304
305 pub fn dict_map(map: DictMap) -> Self {
307 VmValue::Dict(Shared::new(map))
308 }
309
310 pub fn set(values: impl IntoIterator<Item = VmValue>) -> Self {
313 VmValue::Set(Shared::new(values.into_iter().collect::<VmSet>()))
314 }
315
316 pub fn set_value(set: VmSet) -> Self {
318 VmValue::Set(Shared::new(set))
319 }
320
321 pub fn channel(handle: VmChannelHandle) -> Self {
322 VmValue::Channel(Shared::new(handle))
323 }
324
325 pub fn atomic(handle: VmAtomicHandle) -> Self {
326 VmValue::Atomic(Shared::new(handle))
327 }
328
329 pub fn rng(handle: VmRngHandle) -> Self {
330 VmValue::Rng(Shared::new(handle))
331 }
332
333 pub fn sync_permit(handle: VmSyncPermitHandle) -> Self {
334 VmValue::SyncPermit(Shared::new(handle))
335 }
336
337 pub fn mcp_client(handle: VmMcpClientHandle) -> Self {
338 VmValue::McpClient(Shared::new(handle))
339 }
340
341 pub fn generator(generator: VmGenerator) -> Self {
342 VmValue::Generator(Shared::new(generator))
343 }
344
345 pub fn stream(stream: VmStream) -> Self {
346 VmValue::Stream(Shared::new(stream))
347 }
348
349 pub fn harness(handle: VmHarness) -> Self {
350 VmValue::Harness(Shared::new(handle))
351 }
352
353 pub fn struct_instance(
354 struct_name: impl Into<Shared<str>>,
355 fields: crate::value::DictMap,
356 ) -> Self {
357 Self::struct_instance_from_map(struct_name.into().to_string(), fields)
358 }
359
360 pub fn is_truthy(&self) -> bool {
361 match self {
362 VmValue::Bool(b) => *b,
363 VmValue::Nil => false,
364 VmValue::Int(n) => *n != 0,
365 VmValue::Float(n) => *n != 0.0,
366 VmValue::Decimal(d) => *d != rust_decimal::Decimal::ZERO,
367 VmValue::String(s) => !s.is_empty(),
368 VmValue::Bytes(bytes) => !bytes.is_empty(),
369 VmValue::List(l) => !l.is_empty(),
370 VmValue::Dict(d) => !d.is_empty(),
371 VmValue::Closure(_) => true,
372 VmValue::BuiltinRef(_) => true,
373 VmValue::BuiltinRefId(_) => true,
374 VmValue::Duration(ms) => *ms != 0,
375 VmValue::EnumVariant(_) => true,
376 VmValue::StructInstance { .. } => true,
377 VmValue::TaskHandle(_) => true,
378 VmValue::Channel(_) => true,
379 VmValue::Atomic(_) => true,
380 VmValue::Rng(_) => true,
381 VmValue::SyncPermit(_) => true,
382 VmValue::McpClient(_) => true,
383 VmValue::Set(s) => !s.is_empty(),
384 VmValue::Generator(_) => true,
385 VmValue::Stream(_) => true,
386 VmValue::Range(_) => true,
389 VmValue::Iter(_) => true,
390 VmValue::Pair(_) => true,
391 VmValue::Harness(_) => true,
392 }
393 }
394
395 pub fn type_name(&self) -> &'static str {
396 match self {
397 VmValue::String(_) => "string",
398 VmValue::Bytes(_) => "bytes",
399 VmValue::Int(_) => "int",
400 VmValue::Float(_) => "float",
401 VmValue::Decimal(_) => "decimal",
402 VmValue::Bool(_) => "bool",
403 VmValue::Nil => "nil",
404 VmValue::List(_) => "list",
405 VmValue::Dict(_) => "dict",
406 VmValue::Closure(_) => "closure",
407 VmValue::BuiltinRef(_) => "builtin",
408 VmValue::BuiltinRefId(_) => "builtin",
409 VmValue::Duration(_) => "duration",
410 VmValue::EnumVariant(_) => "enum",
411 VmValue::StructInstance { .. } => "struct",
412 VmValue::TaskHandle(_) => "task_handle",
413 VmValue::Channel(_) => "channel",
414 VmValue::Atomic(_) => "atomic",
415 VmValue::Rng(_) => "rng",
416 VmValue::SyncPermit(_) => "sync_permit",
417 VmValue::McpClient(_) => "mcp_client",
418 VmValue::Set(_) => "set",
419 VmValue::Generator(_) => "generator",
420 VmValue::Stream(_) => "stream",
421 VmValue::Range(_) => "range",
422 VmValue::Iter(_) => "iter",
423 VmValue::Pair(_) => "pair",
424 VmValue::Harness(h) => h.type_name(),
425 }
426 }
427
428 pub fn as_str_cow(&self) -> std::borrow::Cow<'_, str> {
434 match self {
435 VmValue::String(s) => std::borrow::Cow::Borrowed(s),
436 other => std::borrow::Cow::Owned(other.display()),
437 }
438 }
439
440 pub fn struct_name(&self) -> Option<&str> {
441 match self {
442 VmValue::StructInstance { layout, .. } => Some(layout.struct_name()),
443 _ => None,
444 }
445 }
446
447 pub fn struct_field(&self, field_name: &str) -> Option<&VmValue> {
448 match self {
449 VmValue::StructInstance { layout, fields } => layout
450 .field_index(field_name)
451 .and_then(|index| fields.get(index))
452 .and_then(Option::as_ref),
453 _ => None,
454 }
455 }
456
457 pub fn struct_fields_map(&self) -> Option<crate::value::DictMap> {
458 match self {
459 VmValue::StructInstance { layout, fields } => {
460 Some(struct_fields_to_map(layout, fields))
461 }
462 _ => None,
463 }
464 }
465
466 pub fn struct_instance_from_map(
467 struct_name: impl Into<String>,
468 fields: crate::value::DictMap,
469 ) -> Self {
470 let layout = Shared::new(StructLayout::from_map(struct_name, &fields));
471 let slots = layout
472 .field_names()
473 .iter()
474 .map(|name| fields.get(name).cloned())
475 .collect();
476 VmValue::StructInstance {
477 layout,
478 fields: Shared::new(slots),
479 }
480 }
481
482 pub fn struct_instance_with_layout(
483 struct_name: impl Into<String>,
484 field_names: Vec<String>,
485 field_values: crate::value::DictMap,
486 ) -> Self {
487 let layout = Shared::new(StructLayout::new(struct_name, field_names));
488 let fields = layout
489 .field_names()
490 .iter()
491 .map(|name| field_values.get(name).cloned())
492 .collect();
493 VmValue::StructInstance {
494 layout,
495 fields: Shared::new(fields),
496 }
497 }
498
499 pub fn struct_instance_with_property(&self, field_name: &str, value: VmValue) -> Option<Self> {
500 let VmValue::StructInstance { layout, fields } = self else {
501 return None;
502 };
503
504 let mut new_fields = fields.as_ref().clone();
505 let layout = match layout.field_index(field_name) {
506 Some(index) => {
507 if index >= new_fields.len() {
508 new_fields.resize(index + 1, None);
509 }
510 new_fields[index] = Some(value);
511 Shared::clone(layout)
512 }
513 None => {
514 let new_layout = Shared::new(layout.with_appended_field(field_name.to_string()));
515 new_fields.push(Some(value));
516 new_layout
517 }
518 };
519
520 Some(VmValue::StructInstance {
521 layout,
522 fields: Shared::new(new_fields),
523 })
524 }
525
526 pub fn display(&self) -> String {
527 let mut out = String::new();
528 self.write_display(&mut out);
529 out
530 }
531
532 pub fn write_display(&self, out: &mut String) {
535 use std::fmt::Write;
536
537 match self {
538 VmValue::Int(n) => {
539 let _ = write!(out, "{n}");
540 }
541 VmValue::Float(n) => {
542 if *n == (*n as i64) as f64 && n.abs() < 1e15 {
543 let _ = write!(out, "{n:.1}");
544 } else {
545 let _ = write!(out, "{n}");
546 }
547 }
548 VmValue::Decimal(d) => {
553 let _ = write!(out, "{d}");
554 }
555 VmValue::String(s) => out.push_str(s),
556 VmValue::Bytes(bytes) => {
557 const MAX_PREVIEW_BYTES: usize = 32;
558
559 out.push_str("b\"");
560 for byte in bytes.iter().take(MAX_PREVIEW_BYTES) {
561 let _ = write!(out, "{byte:02x}");
562 }
563 if bytes.len() > MAX_PREVIEW_BYTES {
564 let _ = write!(out, "...+{}", bytes.len() - MAX_PREVIEW_BYTES);
565 }
566 out.push('"');
567 }
568 VmValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
569 VmValue::Nil => out.push_str("nil"),
570 VmValue::List(items) => {
571 out.push('[');
572 crate::value::recursion::guard_recursion(|| {
573 for (i, item) in items.iter().enumerate() {
574 if i > 0 {
575 out.push_str(", ");
576 }
577 item.write_display(out);
578 }
579 });
580 out.push(']');
581 }
582 VmValue::Dict(map) => {
583 out.push('{');
584 crate::value::recursion::guard_recursion(|| {
585 for (i, (k, v)) in map.iter().enumerate() {
586 if i > 0 {
587 out.push_str(", ");
588 }
589 out.push_str(k);
590 out.push_str(": ");
591 v.write_display(out);
592 }
593 });
594 out.push('}');
595 }
596 VmValue::Closure(c) => {
597 let names: Vec<&str> = c.func.param_names().collect();
598 let _ = write!(out, "<fn({})>", names.join(", "));
599 }
600 VmValue::BuiltinRef(name) => {
601 let _ = write!(out, "<builtin {name}>");
602 }
603 VmValue::BuiltinRefId(r) => {
604 let _ = write!(out, "<builtin {}>", r.name);
605 }
606 VmValue::Duration(ms) => {
607 let sign = if *ms < 0 { "-" } else { "" };
608 let abs_ms = ms.unsigned_abs();
609 if abs_ms >= 604_800_000 && abs_ms % 604_800_000 == 0 {
610 let _ = write!(out, "{}{}w", sign, abs_ms / 604_800_000);
611 } else if abs_ms >= 86_400_000 && abs_ms % 86_400_000 == 0 {
612 let _ = write!(out, "{}{}d", sign, abs_ms / 86_400_000);
613 } else if abs_ms >= 3_600_000 && abs_ms % 3_600_000 == 0 {
614 let _ = write!(out, "{}{}h", sign, abs_ms / 3_600_000);
615 } else if abs_ms >= 60_000 && abs_ms % 60_000 == 0 {
616 let _ = write!(out, "{}{}m", sign, abs_ms / 60_000);
617 } else if abs_ms >= 1000 && abs_ms % 1000 == 0 {
618 let _ = write!(out, "{}{}s", sign, abs_ms / 1000);
619 } else {
620 let _ = write!(out, "{sign}{abs_ms}ms");
621 }
622 }
623 VmValue::EnumVariant(enum_variant) => {
624 if enum_variant.fields.is_empty() {
625 let _ = write!(out, "{}.{}", enum_variant.enum_name, enum_variant.variant);
626 } else {
627 let _ = write!(out, "{}.{}(", enum_variant.enum_name, enum_variant.variant);
628 crate::value::recursion::guard_recursion(|| {
629 for (i, v) in enum_variant.fields.iter().enumerate() {
630 if i > 0 {
631 out.push_str(", ");
632 }
633 v.write_display(out);
634 }
635 });
636 out.push(')');
637 }
638 }
639 VmValue::StructInstance { layout, fields } => {
640 let _ = write!(out, "{} {{", layout.struct_name());
641 crate::value::recursion::guard_recursion(|| {
642 for (i, (k, v)) in struct_fields_to_map(layout, fields).iter().enumerate() {
643 if i > 0 {
644 out.push_str(", ");
645 }
646 out.push_str(k);
647 out.push_str(": ");
648 v.write_display(out);
649 }
650 });
651 out.push('}');
652 }
653 VmValue::TaskHandle(id) => {
654 let _ = write!(out, "<task:{id}>");
655 }
656 VmValue::Channel(ch) => {
657 let _ = write!(out, "<channel:{}>", ch.name);
658 }
659 VmValue::Atomic(a) => {
660 let _ = write!(out, "<atomic:{}>", a.value.load(Ordering::SeqCst));
661 }
662 VmValue::Rng(_) => {
663 out.push_str("<rng>");
664 }
665 VmValue::SyncPermit(p) => {
666 let _ = write!(out, "<sync_permit:{}:{}>", p.kind(), p.key());
667 }
668 VmValue::McpClient(c) => {
669 let _ = write!(out, "<mcp_client:{}>", c.name);
670 }
671 VmValue::Set(items) => {
672 out.push_str("set(");
673 crate::value::recursion::guard_recursion(|| {
674 for (i, item) in items.iter().enumerate() {
675 if i > 0 {
676 out.push_str(", ");
677 }
678 item.write_display(out);
679 }
680 });
681 out.push(')');
682 }
683 VmValue::Generator(g) => {
684 if g.is_done() {
685 out.push_str("<generator (done)>");
686 } else {
687 out.push_str("<generator>");
688 }
689 }
690 VmValue::Stream(s) => {
691 if s.is_done() {
692 out.push_str("<stream (done)>");
693 } else {
694 out.push_str("<stream>");
695 }
696 }
697 VmValue::Range(r) => {
700 let _ = write!(out, "{} to {}", r.start, r.end);
701 if !r.inclusive {
702 out.push_str(" exclusive");
703 }
704 }
705 VmValue::Iter(h) => {
706 if matches!(&*h.lock(), crate::vm::iter::VmIter::Exhausted) {
707 out.push_str("<iter (exhausted)>");
708 } else {
709 out.push_str("<iter>");
710 }
711 }
712 VmValue::Harness(h) => {
713 let _ = write!(out, "<{}>", h.type_name());
714 }
715 VmValue::Pair(p) => {
716 out.push('(');
717 crate::value::recursion::guard_recursion(|| {
718 p.0.write_display(out);
719 out.push_str(", ");
720 p.1.write_display(out);
721 });
722 out.push(')');
723 }
724 }
725 }
726
727 pub fn as_dict(&self) -> Option<&DictMap> {
729 if let VmValue::Dict(d) = self {
730 Some(d)
731 } else {
732 None
733 }
734 }
735
736 pub fn as_int(&self) -> Option<i64> {
737 if let VmValue::Int(n) = self {
738 Some(*n)
739 } else {
740 None
741 }
742 }
743
744 pub fn as_bytes(&self) -> Option<&[u8]> {
745 if let VmValue::Bytes(bytes) = self {
746 Some(bytes.as_slice())
747 } else {
748 None
749 }
750 }
751}
752
753pub fn struct_fields_to_map(
754 layout: &StructLayout,
755 fields: &[Option<VmValue>],
756) -> crate::value::DictMap {
757 layout
758 .field_names()
759 .iter()
760 .enumerate()
761 .filter_map(|(index, name)| {
762 fields
763 .get(index)
764 .and_then(Option::as_ref)
765 .map(|value| (name.clone(), value.clone()))
766 })
767 .collect()
768}
769
770pub type VmBuiltinFn =
772 Arc<dyn Fn(&[VmValue], &mut String) -> Result<VmValue, VmError> + Send + Sync>;