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