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