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