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