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
167impl VmValue {
168 pub fn enum_variant(
169 enum_name: impl Into<Shared<str>>,
170 variant: impl Into<Shared<str>>,
171 fields: Vec<VmValue>,
172 ) -> Self {
173 VmValue::EnumVariant(Shared::new(VmEnumVariant {
174 enum_name: enum_name.into(),
175 variant: variant.into(),
176 fields: Shared::new(fields),
177 }))
178 }
179
180 pub fn task_handle(id: impl Into<Shared<str>>) -> Self {
181 VmValue::TaskHandle(id.into())
182 }
183
184 pub fn channel(handle: VmChannelHandle) -> Self {
185 VmValue::Channel(Shared::new(handle))
186 }
187
188 pub fn atomic(handle: VmAtomicHandle) -> Self {
189 VmValue::Atomic(Shared::new(handle))
190 }
191
192 pub fn rng(handle: VmRngHandle) -> Self {
193 VmValue::Rng(Shared::new(handle))
194 }
195
196 pub fn sync_permit(handle: VmSyncPermitHandle) -> Self {
197 VmValue::SyncPermit(Shared::new(handle))
198 }
199
200 pub fn mcp_client(handle: VmMcpClientHandle) -> Self {
201 VmValue::McpClient(Shared::new(handle))
202 }
203
204 pub fn generator(generator: VmGenerator) -> Self {
205 VmValue::Generator(Shared::new(generator))
206 }
207
208 pub fn stream(stream: VmStream) -> Self {
209 VmValue::Stream(Shared::new(stream))
210 }
211
212 pub fn harness(handle: VmHarness) -> Self {
213 VmValue::Harness(Shared::new(handle))
214 }
215
216 pub fn struct_instance(
217 struct_name: impl Into<Shared<str>>,
218 fields: BTreeMap<String, VmValue>,
219 ) -> Self {
220 Self::struct_instance_from_map(struct_name.into().to_string(), fields)
221 }
222
223 pub fn is_truthy(&self) -> bool {
224 match self {
225 VmValue::Bool(b) => *b,
226 VmValue::Nil => false,
227 VmValue::Int(n) => *n != 0,
228 VmValue::Float(n) => *n != 0.0,
229 VmValue::String(s) => !s.is_empty(),
230 VmValue::Bytes(bytes) => !bytes.is_empty(),
231 VmValue::List(l) => !l.is_empty(),
232 VmValue::Dict(d) => !d.is_empty(),
233 VmValue::Closure(_) => true,
234 VmValue::BuiltinRef(_) => true,
235 VmValue::BuiltinRefId { .. } => true,
236 VmValue::Duration(ms) => *ms != 0,
237 VmValue::EnumVariant(_) => true,
238 VmValue::StructInstance { .. } => true,
239 VmValue::TaskHandle(_) => true,
240 VmValue::Channel(_) => true,
241 VmValue::Atomic(_) => true,
242 VmValue::Rng(_) => true,
243 VmValue::SyncPermit(_) => true,
244 VmValue::McpClient(_) => true,
245 VmValue::Set(s) => !s.is_empty(),
246 VmValue::Generator(_) => true,
247 VmValue::Stream(_) => true,
248 VmValue::Range(_) => true,
251 VmValue::Iter(_) => true,
252 VmValue::Pair(_) => true,
253 VmValue::Harness(_) => true,
254 }
255 }
256
257 pub fn type_name(&self) -> &'static str {
258 match self {
259 VmValue::String(_) => "string",
260 VmValue::Bytes(_) => "bytes",
261 VmValue::Int(_) => "int",
262 VmValue::Float(_) => "float",
263 VmValue::Bool(_) => "bool",
264 VmValue::Nil => "nil",
265 VmValue::List(_) => "list",
266 VmValue::Dict(_) => "dict",
267 VmValue::Closure(_) => "closure",
268 VmValue::BuiltinRef(_) => "builtin",
269 VmValue::BuiltinRefId { .. } => "builtin",
270 VmValue::Duration(_) => "duration",
271 VmValue::EnumVariant(_) => "enum",
272 VmValue::StructInstance { .. } => "struct",
273 VmValue::TaskHandle(_) => "task_handle",
274 VmValue::Channel(_) => "channel",
275 VmValue::Atomic(_) => "atomic",
276 VmValue::Rng(_) => "rng",
277 VmValue::SyncPermit(_) => "sync_permit",
278 VmValue::McpClient(_) => "mcp_client",
279 VmValue::Set(_) => "set",
280 VmValue::Generator(_) => "generator",
281 VmValue::Stream(_) => "stream",
282 VmValue::Range(_) => "range",
283 VmValue::Iter(_) => "iter",
284 VmValue::Pair(_) => "pair",
285 VmValue::Harness(h) => h.type_name(),
286 }
287 }
288
289 pub fn struct_name(&self) -> Option<&str> {
290 match self {
291 VmValue::StructInstance { layout, .. } => Some(layout.struct_name()),
292 _ => None,
293 }
294 }
295
296 pub fn struct_field(&self, field_name: &str) -> Option<&VmValue> {
297 match self {
298 VmValue::StructInstance { layout, fields } => layout
299 .field_index(field_name)
300 .and_then(|index| fields.get(index))
301 .and_then(Option::as_ref),
302 _ => None,
303 }
304 }
305
306 pub fn struct_fields_map(&self) -> Option<BTreeMap<String, VmValue>> {
307 match self {
308 VmValue::StructInstance { layout, fields } => {
309 Some(struct_fields_to_map(layout, fields))
310 }
311 _ => None,
312 }
313 }
314
315 pub fn struct_instance_from_map(
316 struct_name: impl Into<String>,
317 fields: BTreeMap<String, VmValue>,
318 ) -> Self {
319 let layout = Shared::new(StructLayout::from_map(struct_name, &fields));
320 let slots = layout
321 .field_names()
322 .iter()
323 .map(|name| fields.get(name).cloned())
324 .collect();
325 VmValue::StructInstance {
326 layout,
327 fields: Shared::new(slots),
328 }
329 }
330
331 pub fn struct_instance_with_layout(
332 struct_name: impl Into<String>,
333 field_names: Vec<String>,
334 field_values: BTreeMap<String, VmValue>,
335 ) -> Self {
336 let layout = Shared::new(StructLayout::new(struct_name, field_names));
337 let fields = layout
338 .field_names()
339 .iter()
340 .map(|name| field_values.get(name).cloned())
341 .collect();
342 VmValue::StructInstance {
343 layout,
344 fields: Shared::new(fields),
345 }
346 }
347
348 pub fn struct_instance_with_property(&self, field_name: &str, value: VmValue) -> Option<Self> {
349 let VmValue::StructInstance { layout, fields } = self else {
350 return None;
351 };
352
353 let mut new_fields = fields.as_ref().clone();
354 let layout = match layout.field_index(field_name) {
355 Some(index) => {
356 if index >= new_fields.len() {
357 new_fields.resize(index + 1, None);
358 }
359 new_fields[index] = Some(value);
360 Shared::clone(layout)
361 }
362 None => {
363 let new_layout = Shared::new(layout.with_appended_field(field_name.to_string()));
364 new_fields.push(Some(value));
365 new_layout
366 }
367 };
368
369 Some(VmValue::StructInstance {
370 layout,
371 fields: Shared::new(new_fields),
372 })
373 }
374
375 pub fn display(&self) -> String {
376 let mut out = String::new();
377 self.write_display(&mut out);
378 out
379 }
380
381 pub fn write_display(&self, out: &mut String) {
384 use std::fmt::Write;
385
386 match self {
387 VmValue::Int(n) => {
388 let _ = write!(out, "{n}");
389 }
390 VmValue::Float(n) => {
391 if *n == (*n as i64) as f64 && n.abs() < 1e15 {
392 let _ = write!(out, "{n:.1}");
393 } else {
394 let _ = write!(out, "{n}");
395 }
396 }
397 VmValue::String(s) => out.push_str(s),
398 VmValue::Bytes(bytes) => {
399 const MAX_PREVIEW_BYTES: usize = 32;
400
401 out.push_str("b\"");
402 for byte in bytes.iter().take(MAX_PREVIEW_BYTES) {
403 let _ = write!(out, "{byte:02x}");
404 }
405 if bytes.len() > MAX_PREVIEW_BYTES {
406 let _ = write!(out, "...+{}", bytes.len() - MAX_PREVIEW_BYTES);
407 }
408 out.push('"');
409 }
410 VmValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
411 VmValue::Nil => out.push_str("nil"),
412 VmValue::List(items) => {
413 out.push('[');
414 for (i, item) in items.iter().enumerate() {
415 if i > 0 {
416 out.push_str(", ");
417 }
418 item.write_display(out);
419 }
420 out.push(']');
421 }
422 VmValue::Dict(map) => {
423 out.push('{');
424 for (i, (k, v)) in map.iter().enumerate() {
425 if i > 0 {
426 out.push_str(", ");
427 }
428 out.push_str(k);
429 out.push_str(": ");
430 v.write_display(out);
431 }
432 out.push('}');
433 }
434 VmValue::Closure(c) => {
435 let names: Vec<&str> = c.func.param_names().collect();
436 let _ = write!(out, "<fn({})>", names.join(", "));
437 }
438 VmValue::BuiltinRef(name) => {
439 let _ = write!(out, "<builtin {name}>");
440 }
441 VmValue::BuiltinRefId { name, .. } => {
442 let _ = write!(out, "<builtin {name}>");
443 }
444 VmValue::Duration(ms) => {
445 let sign = if *ms < 0 { "-" } else { "" };
446 let abs_ms = ms.unsigned_abs();
447 if abs_ms >= 604_800_000 && abs_ms % 604_800_000 == 0 {
448 let _ = write!(out, "{}{}w", sign, abs_ms / 604_800_000);
449 } else if abs_ms >= 86_400_000 && abs_ms % 86_400_000 == 0 {
450 let _ = write!(out, "{}{}d", sign, abs_ms / 86_400_000);
451 } else if abs_ms >= 3_600_000 && abs_ms % 3_600_000 == 0 {
452 let _ = write!(out, "{}{}h", sign, abs_ms / 3_600_000);
453 } else if abs_ms >= 60_000 && abs_ms % 60_000 == 0 {
454 let _ = write!(out, "{}{}m", sign, abs_ms / 60_000);
455 } else if abs_ms >= 1000 && abs_ms % 1000 == 0 {
456 let _ = write!(out, "{}{}s", sign, abs_ms / 1000);
457 } else {
458 let _ = write!(out, "{sign}{abs_ms}ms");
459 }
460 }
461 VmValue::EnumVariant(enum_variant) => {
462 if enum_variant.fields.is_empty() {
463 let _ = write!(out, "{}.{}", enum_variant.enum_name, enum_variant.variant);
464 } else {
465 let _ = write!(out, "{}.{}(", enum_variant.enum_name, enum_variant.variant);
466 for (i, v) in enum_variant.fields.iter().enumerate() {
467 if i > 0 {
468 out.push_str(", ");
469 }
470 v.write_display(out);
471 }
472 out.push(')');
473 }
474 }
475 VmValue::StructInstance { layout, fields } => {
476 let _ = write!(out, "{} {{", layout.struct_name());
477 for (i, (k, v)) in struct_fields_to_map(layout, fields).iter().enumerate() {
478 if i > 0 {
479 out.push_str(", ");
480 }
481 out.push_str(k);
482 out.push_str(": ");
483 v.write_display(out);
484 }
485 out.push('}');
486 }
487 VmValue::TaskHandle(id) => {
488 let _ = write!(out, "<task:{id}>");
489 }
490 VmValue::Channel(ch) => {
491 let _ = write!(out, "<channel:{}>", ch.name);
492 }
493 VmValue::Atomic(a) => {
494 let _ = write!(out, "<atomic:{}>", a.value.load(Ordering::SeqCst));
495 }
496 VmValue::Rng(_) => {
497 out.push_str("<rng>");
498 }
499 VmValue::SyncPermit(p) => {
500 let _ = write!(out, "<sync_permit:{}:{}>", p.kind(), p.key());
501 }
502 VmValue::McpClient(c) => {
503 let _ = write!(out, "<mcp_client:{}>", c.name);
504 }
505 VmValue::Set(items) => {
506 out.push_str("set(");
507 for (i, item) in items.iter().enumerate() {
508 if i > 0 {
509 out.push_str(", ");
510 }
511 item.write_display(out);
512 }
513 out.push(')');
514 }
515 VmValue::Generator(g) => {
516 if g.is_done() {
517 out.push_str("<generator (done)>");
518 } else {
519 out.push_str("<generator>");
520 }
521 }
522 VmValue::Stream(s) => {
523 if s.is_done() {
524 out.push_str("<stream (done)>");
525 } else {
526 out.push_str("<stream>");
527 }
528 }
529 VmValue::Range(r) => {
532 let _ = write!(out, "{} to {}", r.start, r.end);
533 if !r.inclusive {
534 out.push_str(" exclusive");
535 }
536 }
537 VmValue::Iter(h) => {
538 if matches!(&*h.lock(), crate::vm::iter::VmIter::Exhausted) {
539 out.push_str("<iter (exhausted)>");
540 } else {
541 out.push_str("<iter>");
542 }
543 }
544 VmValue::Harness(h) => {
545 let _ = write!(out, "<{}>", h.type_name());
546 }
547 VmValue::Pair(p) => {
548 out.push('(');
549 p.0.write_display(out);
550 out.push_str(", ");
551 p.1.write_display(out);
552 out.push(')');
553 }
554 }
555 }
556
557 pub fn as_dict(&self) -> Option<&BTreeMap<String, VmValue>> {
559 if let VmValue::Dict(d) = self {
560 Some(d)
561 } else {
562 None
563 }
564 }
565
566 pub fn as_int(&self) -> Option<i64> {
567 if let VmValue::Int(n) = self {
568 Some(*n)
569 } else {
570 None
571 }
572 }
573
574 pub fn as_bytes(&self) -> Option<&[u8]> {
575 if let VmValue::Bytes(bytes) = self {
576 Some(bytes.as_slice())
577 } else {
578 None
579 }
580 }
581}
582
583pub fn struct_fields_to_map(
584 layout: &StructLayout,
585 fields: &[Option<VmValue>],
586) -> BTreeMap<String, VmValue> {
587 layout
588 .field_names()
589 .iter()
590 .enumerate()
591 .filter_map(|(index, name)| {
592 fields
593 .get(index)
594 .and_then(Option::as_ref)
595 .map(|value| (name.clone(), value.clone()))
596 })
597 .collect()
598}
599
600pub type VmBuiltinFn =
602 Arc<dyn Fn(&[VmValue], &mut String) -> Result<VmValue, VmError> + Send + Sync>;