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