1use std::collections::{BTreeMap, 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,
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
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct StructLayout {
35 struct_name: String,
36 field_names: Vec<String>,
37 field_indexes: HashMap<String, usize>,
38}
39
40impl StructLayout {
41 pub fn new(struct_name: impl Into<String>, field_names: Vec<String>) -> Self {
42 let mut deduped = Vec::with_capacity(field_names.len());
43 let mut field_indexes = HashMap::with_capacity(field_names.len());
44 for field_name in field_names {
45 if field_indexes.contains_key(&field_name) {
46 continue;
47 }
48 let index = deduped.len();
49 field_indexes.insert(field_name.clone(), index);
50 deduped.push(field_name);
51 }
52
53 Self {
54 struct_name: struct_name.into(),
55 field_names: deduped,
56 field_indexes,
57 }
58 }
59
60 pub fn from_map(struct_name: impl Into<String>, fields: &BTreeMap<String, VmValue>) -> Self {
61 Self::new(struct_name, fields.keys().cloned().collect())
62 }
63
64 pub fn struct_name(&self) -> &str {
65 &self.struct_name
66 }
67
68 pub fn field_names(&self) -> &[String] {
69 &self.field_names
70 }
71
72 pub fn field_index(&self, field_name: &str) -> Option<usize> {
73 if self.field_names.len() <= 8 {
74 return self
75 .field_names
76 .iter()
77 .position(|candidate| candidate == field_name);
78 }
79 self.field_indexes.get(field_name).copied()
80 }
81
82 pub fn with_appended_field(&self, field_name: String) -> Self {
83 if self.field_indexes.contains_key(&field_name) {
84 return self.clone();
85 }
86 let mut field_names = self.field_names.clone();
87 field_names.push(field_name);
88 Self::new(self.struct_name.clone(), field_names)
89 }
90}
91
92#[derive(Debug, Clone)]
94pub struct VmEnumVariant {
95 pub enum_name: Shared<str>,
96 pub variant: Shared<str>,
97 pub fields: Shared<Vec<VmValue>>,
98}
99
100impl VmEnumVariant {
101 pub fn has_enum_name(&self, enum_name: &str) -> bool {
102 self.enum_name.as_ref() == enum_name
103 }
104
105 pub fn is_variant(&self, enum_name: &str, variant: &str) -> bool {
106 self.has_enum_name(enum_name) && self.variant.as_ref() == variant
107 }
108}
109
110#[derive(Debug, Clone)]
117pub enum VmValue {
118 Int(i64),
119 Float(f64),
120 String(Shared<str>),
121 Bytes(Shared<Vec<u8>>),
122 Bool(bool),
123 Nil,
124 List(Shared<Vec<VmValue>>),
125 Dict(Shared<BTreeMap<String, VmValue>>),
126 Closure(Shared<VmClosure>),
127 BuiltinRef(Shared<str>),
131 BuiltinRefId {
134 id: BuiltinId,
135 name: Shared<str>,
136 },
137 Duration(i64),
138 EnumVariant(Shared<VmEnumVariant>),
139 StructInstance {
140 layout: Shared<StructLayout>,
141 fields: Shared<Vec<Option<VmValue>>>,
142 },
143 TaskHandle(Shared<str>),
144 Channel(Shared<VmChannelHandle>),
145 Atomic(Shared<VmAtomicHandle>),
146 Rng(Shared<VmRngHandle>),
147 SyncPermit(Shared<VmSyncPermitHandle>),
148 McpClient(Shared<VmMcpClientHandle>),
149 Set(Shared<Vec<VmValue>>),
150 Generator(Shared<VmGenerator>),
151 Stream(Shared<VmStream>),
152 Range(VmRange),
153 Iter(crate::vm::iter::VmIterHandle),
155 Pair(Shared<(VmValue, VmValue)>),
160 Harness(Shared<VmHarness>),
165}
166
167static ASCII_CHAR_STRINGS: std::sync::LazyLock<[Arc<str>; 128]> = std::sync::LazyLock::new(|| {
176 std::array::from_fn(|byte| {
177 let mut buffer = [0u8; 4];
178 Arc::from((byte as u8 as char).encode_utf8(&mut buffer))
179 })
180});
181
182impl VmValue {
183 pub fn char_value(ch: char) -> Self {
187 if ch.is_ascii() {
188 return VmValue::String(Arc::clone(&ASCII_CHAR_STRINGS[ch as usize]));
189 }
190 let mut buffer = [0u8; 4];
191 VmValue::String(Arc::from(ch.encode_utf8(&mut buffer)))
192 }
193
194 pub fn chars_list(text: &str) -> Self {
199 VmValue::List(Shared::new(text.chars().map(VmValue::char_value).collect()))
200 }
201
202 pub fn enum_variant(
203 enum_name: impl Into<Shared<str>>,
204 variant: impl Into<Shared<str>>,
205 fields: Vec<VmValue>,
206 ) -> Self {
207 VmValue::EnumVariant(Shared::new(VmEnumVariant {
208 enum_name: enum_name.into(),
209 variant: variant.into(),
210 fields: Shared::new(fields),
211 }))
212 }
213
214 pub fn task_handle(id: impl Into<Shared<str>>) -> Self {
215 VmValue::TaskHandle(id.into())
216 }
217
218 pub fn channel(handle: VmChannelHandle) -> Self {
219 VmValue::Channel(Shared::new(handle))
220 }
221
222 pub fn atomic(handle: VmAtomicHandle) -> Self {
223 VmValue::Atomic(Shared::new(handle))
224 }
225
226 pub fn rng(handle: VmRngHandle) -> Self {
227 VmValue::Rng(Shared::new(handle))
228 }
229
230 pub fn sync_permit(handle: VmSyncPermitHandle) -> Self {
231 VmValue::SyncPermit(Shared::new(handle))
232 }
233
234 pub fn mcp_client(handle: VmMcpClientHandle) -> Self {
235 VmValue::McpClient(Shared::new(handle))
236 }
237
238 pub fn generator(generator: VmGenerator) -> Self {
239 VmValue::Generator(Shared::new(generator))
240 }
241
242 pub fn stream(stream: VmStream) -> Self {
243 VmValue::Stream(Shared::new(stream))
244 }
245
246 pub fn harness(handle: VmHarness) -> Self {
247 VmValue::Harness(Shared::new(handle))
248 }
249
250 pub fn struct_instance(
251 struct_name: impl Into<Shared<str>>,
252 fields: BTreeMap<String, VmValue>,
253 ) -> Self {
254 Self::struct_instance_from_map(struct_name.into().to_string(), fields)
255 }
256
257 pub fn is_truthy(&self) -> bool {
258 match self {
259 VmValue::Bool(b) => *b,
260 VmValue::Nil => false,
261 VmValue::Int(n) => *n != 0,
262 VmValue::Float(n) => *n != 0.0,
263 VmValue::String(s) => !s.is_empty(),
264 VmValue::Bytes(bytes) => !bytes.is_empty(),
265 VmValue::List(l) => !l.is_empty(),
266 VmValue::Dict(d) => !d.is_empty(),
267 VmValue::Closure(_) => true,
268 VmValue::BuiltinRef(_) => true,
269 VmValue::BuiltinRefId { .. } => true,
270 VmValue::Duration(ms) => *ms != 0,
271 VmValue::EnumVariant(_) => true,
272 VmValue::StructInstance { .. } => true,
273 VmValue::TaskHandle(_) => true,
274 VmValue::Channel(_) => true,
275 VmValue::Atomic(_) => true,
276 VmValue::Rng(_) => true,
277 VmValue::SyncPermit(_) => true,
278 VmValue::McpClient(_) => true,
279 VmValue::Set(s) => !s.is_empty(),
280 VmValue::Generator(_) => true,
281 VmValue::Stream(_) => true,
282 VmValue::Range(_) => true,
285 VmValue::Iter(_) => true,
286 VmValue::Pair(_) => true,
287 VmValue::Harness(_) => true,
288 }
289 }
290
291 pub fn type_name(&self) -> &'static str {
292 match self {
293 VmValue::String(_) => "string",
294 VmValue::Bytes(_) => "bytes",
295 VmValue::Int(_) => "int",
296 VmValue::Float(_) => "float",
297 VmValue::Bool(_) => "bool",
298 VmValue::Nil => "nil",
299 VmValue::List(_) => "list",
300 VmValue::Dict(_) => "dict",
301 VmValue::Closure(_) => "closure",
302 VmValue::BuiltinRef(_) => "builtin",
303 VmValue::BuiltinRefId { .. } => "builtin",
304 VmValue::Duration(_) => "duration",
305 VmValue::EnumVariant(_) => "enum",
306 VmValue::StructInstance { .. } => "struct",
307 VmValue::TaskHandle(_) => "task_handle",
308 VmValue::Channel(_) => "channel",
309 VmValue::Atomic(_) => "atomic",
310 VmValue::Rng(_) => "rng",
311 VmValue::SyncPermit(_) => "sync_permit",
312 VmValue::McpClient(_) => "mcp_client",
313 VmValue::Set(_) => "set",
314 VmValue::Generator(_) => "generator",
315 VmValue::Stream(_) => "stream",
316 VmValue::Range(_) => "range",
317 VmValue::Iter(_) => "iter",
318 VmValue::Pair(_) => "pair",
319 VmValue::Harness(h) => h.type_name(),
320 }
321 }
322
323 pub fn as_str_cow(&self) -> std::borrow::Cow<'_, str> {
329 match self {
330 VmValue::String(s) => std::borrow::Cow::Borrowed(s),
331 other => std::borrow::Cow::Owned(other.display()),
332 }
333 }
334
335 pub fn struct_name(&self) -> Option<&str> {
336 match self {
337 VmValue::StructInstance { layout, .. } => Some(layout.struct_name()),
338 _ => None,
339 }
340 }
341
342 pub fn struct_field(&self, field_name: &str) -> Option<&VmValue> {
343 match self {
344 VmValue::StructInstance { layout, fields } => layout
345 .field_index(field_name)
346 .and_then(|index| fields.get(index))
347 .and_then(Option::as_ref),
348 _ => None,
349 }
350 }
351
352 pub fn struct_fields_map(&self) -> Option<BTreeMap<String, VmValue>> {
353 match self {
354 VmValue::StructInstance { layout, fields } => {
355 Some(struct_fields_to_map(layout, fields))
356 }
357 _ => None,
358 }
359 }
360
361 pub fn struct_instance_from_map(
362 struct_name: impl Into<String>,
363 fields: BTreeMap<String, VmValue>,
364 ) -> Self {
365 let layout = Shared::new(StructLayout::from_map(struct_name, &fields));
366 let slots = layout
367 .field_names()
368 .iter()
369 .map(|name| fields.get(name).cloned())
370 .collect();
371 VmValue::StructInstance {
372 layout,
373 fields: Shared::new(slots),
374 }
375 }
376
377 pub fn struct_instance_with_layout(
378 struct_name: impl Into<String>,
379 field_names: Vec<String>,
380 field_values: BTreeMap<String, VmValue>,
381 ) -> Self {
382 let layout = Shared::new(StructLayout::new(struct_name, field_names));
383 let fields = layout
384 .field_names()
385 .iter()
386 .map(|name| field_values.get(name).cloned())
387 .collect();
388 VmValue::StructInstance {
389 layout,
390 fields: Shared::new(fields),
391 }
392 }
393
394 pub fn struct_instance_with_property(&self, field_name: &str, value: VmValue) -> Option<Self> {
395 let VmValue::StructInstance { layout, fields } = self else {
396 return None;
397 };
398
399 let mut new_fields = fields.as_ref().clone();
400 let layout = match layout.field_index(field_name) {
401 Some(index) => {
402 if index >= new_fields.len() {
403 new_fields.resize(index + 1, None);
404 }
405 new_fields[index] = Some(value);
406 Shared::clone(layout)
407 }
408 None => {
409 let new_layout = Shared::new(layout.with_appended_field(field_name.to_string()));
410 new_fields.push(Some(value));
411 new_layout
412 }
413 };
414
415 Some(VmValue::StructInstance {
416 layout,
417 fields: Shared::new(new_fields),
418 })
419 }
420
421 pub fn display(&self) -> String {
422 let mut out = String::new();
423 self.write_display(&mut out);
424 out
425 }
426
427 pub fn write_display(&self, out: &mut String) {
430 use std::fmt::Write;
431
432 match self {
433 VmValue::Int(n) => {
434 let _ = write!(out, "{n}");
435 }
436 VmValue::Float(n) => {
437 if *n == (*n as i64) as f64 && n.abs() < 1e15 {
438 let _ = write!(out, "{n:.1}");
439 } else {
440 let _ = write!(out, "{n}");
441 }
442 }
443 VmValue::String(s) => out.push_str(s),
444 VmValue::Bytes(bytes) => {
445 const MAX_PREVIEW_BYTES: usize = 32;
446
447 out.push_str("b\"");
448 for byte in bytes.iter().take(MAX_PREVIEW_BYTES) {
449 let _ = write!(out, "{byte:02x}");
450 }
451 if bytes.len() > MAX_PREVIEW_BYTES {
452 let _ = write!(out, "...+{}", bytes.len() - MAX_PREVIEW_BYTES);
453 }
454 out.push('"');
455 }
456 VmValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
457 VmValue::Nil => out.push_str("nil"),
458 VmValue::List(items) => {
459 out.push('[');
460 for (i, item) in items.iter().enumerate() {
461 if i > 0 {
462 out.push_str(", ");
463 }
464 item.write_display(out);
465 }
466 out.push(']');
467 }
468 VmValue::Dict(map) => {
469 out.push('{');
470 for (i, (k, v)) in map.iter().enumerate() {
471 if i > 0 {
472 out.push_str(", ");
473 }
474 out.push_str(k);
475 out.push_str(": ");
476 v.write_display(out);
477 }
478 out.push('}');
479 }
480 VmValue::Closure(c) => {
481 let names: Vec<&str> = c.func.param_names().collect();
482 let _ = write!(out, "<fn({})>", names.join(", "));
483 }
484 VmValue::BuiltinRef(name) => {
485 let _ = write!(out, "<builtin {name}>");
486 }
487 VmValue::BuiltinRefId { name, .. } => {
488 let _ = write!(out, "<builtin {name}>");
489 }
490 VmValue::Duration(ms) => {
491 let sign = if *ms < 0 { "-" } else { "" };
492 let abs_ms = ms.unsigned_abs();
493 if abs_ms >= 604_800_000 && abs_ms % 604_800_000 == 0 {
494 let _ = write!(out, "{}{}w", sign, abs_ms / 604_800_000);
495 } else if abs_ms >= 86_400_000 && abs_ms % 86_400_000 == 0 {
496 let _ = write!(out, "{}{}d", sign, abs_ms / 86_400_000);
497 } else if abs_ms >= 3_600_000 && abs_ms % 3_600_000 == 0 {
498 let _ = write!(out, "{}{}h", sign, abs_ms / 3_600_000);
499 } else if abs_ms >= 60_000 && abs_ms % 60_000 == 0 {
500 let _ = write!(out, "{}{}m", sign, abs_ms / 60_000);
501 } else if abs_ms >= 1000 && abs_ms % 1000 == 0 {
502 let _ = write!(out, "{}{}s", sign, abs_ms / 1000);
503 } else {
504 let _ = write!(out, "{sign}{abs_ms}ms");
505 }
506 }
507 VmValue::EnumVariant(enum_variant) => {
508 if enum_variant.fields.is_empty() {
509 let _ = write!(out, "{}.{}", enum_variant.enum_name, enum_variant.variant);
510 } else {
511 let _ = write!(out, "{}.{}(", enum_variant.enum_name, enum_variant.variant);
512 for (i, v) in enum_variant.fields.iter().enumerate() {
513 if i > 0 {
514 out.push_str(", ");
515 }
516 v.write_display(out);
517 }
518 out.push(')');
519 }
520 }
521 VmValue::StructInstance { layout, fields } => {
522 let _ = write!(out, "{} {{", layout.struct_name());
523 for (i, (k, v)) in struct_fields_to_map(layout, fields).iter().enumerate() {
524 if i > 0 {
525 out.push_str(", ");
526 }
527 out.push_str(k);
528 out.push_str(": ");
529 v.write_display(out);
530 }
531 out.push('}');
532 }
533 VmValue::TaskHandle(id) => {
534 let _ = write!(out, "<task:{id}>");
535 }
536 VmValue::Channel(ch) => {
537 let _ = write!(out, "<channel:{}>", ch.name);
538 }
539 VmValue::Atomic(a) => {
540 let _ = write!(out, "<atomic:{}>", a.value.load(Ordering::SeqCst));
541 }
542 VmValue::Rng(_) => {
543 out.push_str("<rng>");
544 }
545 VmValue::SyncPermit(p) => {
546 let _ = write!(out, "<sync_permit:{}:{}>", p.kind(), p.key());
547 }
548 VmValue::McpClient(c) => {
549 let _ = write!(out, "<mcp_client:{}>", c.name);
550 }
551 VmValue::Set(items) => {
552 out.push_str("set(");
553 for (i, item) in items.iter().enumerate() {
554 if i > 0 {
555 out.push_str(", ");
556 }
557 item.write_display(out);
558 }
559 out.push(')');
560 }
561 VmValue::Generator(g) => {
562 if g.is_done() {
563 out.push_str("<generator (done)>");
564 } else {
565 out.push_str("<generator>");
566 }
567 }
568 VmValue::Stream(s) => {
569 if s.is_done() {
570 out.push_str("<stream (done)>");
571 } else {
572 out.push_str("<stream>");
573 }
574 }
575 VmValue::Range(r) => {
578 let _ = write!(out, "{} to {}", r.start, r.end);
579 if !r.inclusive {
580 out.push_str(" exclusive");
581 }
582 }
583 VmValue::Iter(h) => {
584 if matches!(&*h.lock(), crate::vm::iter::VmIter::Exhausted) {
585 out.push_str("<iter (exhausted)>");
586 } else {
587 out.push_str("<iter>");
588 }
589 }
590 VmValue::Harness(h) => {
591 let _ = write!(out, "<{}>", h.type_name());
592 }
593 VmValue::Pair(p) => {
594 out.push('(');
595 p.0.write_display(out);
596 out.push_str(", ");
597 p.1.write_display(out);
598 out.push(')');
599 }
600 }
601 }
602
603 pub fn as_dict(&self) -> Option<&BTreeMap<String, VmValue>> {
605 if let VmValue::Dict(d) = self {
606 Some(d)
607 } else {
608 None
609 }
610 }
611
612 pub fn as_int(&self) -> Option<i64> {
613 if let VmValue::Int(n) = self {
614 Some(*n)
615 } else {
616 None
617 }
618 }
619
620 pub fn as_bytes(&self) -> Option<&[u8]> {
621 if let VmValue::Bytes(bytes) = self {
622 Some(bytes.as_slice())
623 } else {
624 None
625 }
626 }
627}
628
629pub fn struct_fields_to_map(
630 layout: &StructLayout,
631 fields: &[Option<VmValue>],
632) -> BTreeMap<String, VmValue> {
633 layout
634 .field_names()
635 .iter()
636 .enumerate()
637 .filter_map(|(index, name)| {
638 fields
639 .get(index)
640 .and_then(Option::as_ref)
641 .map(|value| (name.clone(), value.clone()))
642 })
643 .collect()
644}
645
646pub type VmBuiltinFn =
648 Arc<dyn Fn(&[VmValue], &mut String) -> Result<VmValue, VmError> + Send + Sync>;